diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000..c3d03a4f68 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,48 @@ +/*! + * Copyright 2020 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + plugins: [ + '@typescript-eslint', + ], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + ], + rules: { + // Following checks are temporarily disabled. We shall incrementally enable them in the + // future, fixing any violations as we go. + '@typescript-eslint/no-non-null-assertion': 0, + + // Disabled checks + '@typescript-eslint/no-explicit-any': 0, + '@typescript-eslint/no-use-before-define': 0, + + // Required checks + 'indent': ['error', 2], + '@typescript-eslint/explicit-function-return-type': [ + 'error', + { + 'allowExpressions': true, + 'allowTypedFunctionExpressions': true, + 'allowHigherOrderFunctions': true + } + ], + } +}; diff --git a/.github/actions/send-tweet/README.md b/.github/actions/send-tweet/README.md new file mode 100644 index 0000000000..eda484f608 --- /dev/null +++ b/.github/actions/send-tweet/README.md @@ -0,0 +1,49 @@ +# Send Tweet GitHub Action + +This is a minimalistic GitHub Action for posting Firebase release announcements +to Twitter. Simply specify the Twitter API keys along with the Tweet status to +be posted. + +## Inputs + +### `status` + +**Required** Text of the Tweet to send. + +### `consumer-key` + +**Required** Consumer API key from Twitter. + +### `consumer-secret` + +**Required** Consumer API secret key from Twitter. + +### `access-token` + +**Required** Twitter application access token. + +### `access-token-secret` + +**Required** Twitter application access token secret. + +## Example usage + +``` +- name: Send Tweet + uses: firebase/firebase-admin-node/.github/actions/send-tweet + with: + status: > + v1.2.3 of @Firebase Admin Node.js SDK is available. + Release notes at https://firebase.google.com. + consumer-key: ${{ secrets.TWITTER_CONSUMER_KEY }} + consumer-secret: ${{ secrets.TWITTER_CONSUMER_SECRET }} + access-token: ${{ secrets.TWITTER_ACCESS_TOKEN }} + access-token-secret: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} +``` + +## Implementation + +This Action uses the `twitter` NPM package to send Tweets. + +When making a code change remember to run `npm run pack` to rebuild the +`dist/index.js` file which is the executable of this Action. diff --git a/.github/actions/send-tweet/action.yml b/.github/actions/send-tweet/action.yml new file mode 100644 index 0000000000..4e44908179 --- /dev/null +++ b/.github/actions/send-tweet/action.yml @@ -0,0 +1,35 @@ +# Copyright 2020 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: 'Send Tweet Action' +description: 'Send Tweets from GitHub Actions workflows.' +inputs: + repo: + status: Status (Tweet) to be posted + required: true + consumer-key: + description: Consumer API key. + required: true + consumer-secret: + description: Consumer API secret key. + required: true + access-token: + description: Application access token. + required: true + access-token-secret: + description: Application access token secret. + required: true +runs: + using: 'node12' + main: 'dist/index.js' diff --git a/.github/actions/send-tweet/dist/index.js b/.github/actions/send-tweet/dist/index.js new file mode 100644 index 0000000000..e3bed9903a --- /dev/null +++ b/.github/actions/send-tweet/dist/index.js @@ -0,0 +1,33631 @@ +module.exports = +/******/ (function(modules, runtime) { // webpackBootstrap +/******/ "use strict"; +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ __webpack_require__.ab = __dirname + "/"; +/******/ +/******/ // the startup function +/******/ function startup() { +/******/ // Load entry module and return exports +/******/ return __webpack_require__(104); +/******/ }; +/******/ +/******/ // run startup +/******/ return startup(); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ 13: +/***/ (function(module) { + +"use strict"; + + +var replace = String.prototype.replace; +var percentTwenties = /%20/g; + +module.exports = { + 'default': 'RFC3986', + formatters: { + RFC1738: function (value) { + return replace.call(value, percentTwenties, '+'); + }, + RFC3986: function (value) { + return value; + } + }, + RFC1738: 'RFC1738', + RFC3986: 'RFC3986' +}; + + +/***/ }), + +/***/ 16: +/***/ (function(module) { + +module.exports = require("tls"); + +/***/ }), + +/***/ 28: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_comment(it, $keyword, $ruleType) { + var out = ' '; + var $schema = it.schema[$keyword]; + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $comment = it.util.toQuotedString($schema); + if (it.opts.$comment === true) { + out += ' console.log(' + ($comment) + ');'; + } else if (typeof it.opts.$comment == 'function') { + out += ' self._opts.$comment(' + ($comment) + ', ' + (it.util.toQuotedString($errSchemaPath)) + ', validate.root.schema);'; + } + return out; +} + + +/***/ }), + +/***/ 35: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_propertyNames(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + out += 'var ' + ($errs) + ' = errors;'; + if ((it.opts.strictKeywords ? typeof $schema == 'object' && Object.keys($schema).length > 0 : it.util.schemaHasRules($schema, it.RULES.all))) { + $it.schema = $schema; + $it.schemaPath = $schemaPath; + $it.errSchemaPath = $errSchemaPath; + var $key = 'key' + $lvl, + $idx = 'idx' + $lvl, + $i = 'i' + $lvl, + $invalidName = '\' + ' + $key + ' + \'', + $dataNxt = $it.dataLevel = it.dataLevel + 1, + $nextData = 'data' + $dataNxt, + $dataProperties = 'dataProperties' + $lvl, + $ownProperties = it.opts.ownProperties, + $currentBaseId = it.baseId; + if ($ownProperties) { + out += ' var ' + ($dataProperties) + ' = undefined; '; + } + if ($ownProperties) { + out += ' ' + ($dataProperties) + ' = ' + ($dataProperties) + ' || Object.keys(' + ($data) + '); for (var ' + ($idx) + '=0; ' + ($idx) + '<' + ($dataProperties) + '.length; ' + ($idx) + '++) { var ' + ($key) + ' = ' + ($dataProperties) + '[' + ($idx) + ']; '; + } else { + out += ' for (var ' + ($key) + ' in ' + ($data) + ') { '; + } + out += ' var startErrs' + ($lvl) + ' = errors; '; + var $passData = $key; + var $wasComposite = it.compositeRule; + it.compositeRule = $it.compositeRule = true; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + it.compositeRule = $it.compositeRule = $wasComposite; + out += ' if (!' + ($nextValid) + ') { for (var ' + ($i) + '=startErrs' + ($lvl) + '; ' + ($i) + ' 299) { + return reject(new Error('HTTP Error: ' + response.statusCode + ' ' + response.statusMessage)); + } + + // no errors + resolve(data); + }); + }); + } + + // Callback version + this.request(options, function(error, response, data) { + // request error + if (error) { + return callback(error, data, response); + } + + // JSON parse error or empty strings + try { + // An empty string is a valid response + if (data === '') { + data = {}; + } + else { + data = JSON.parse(data); + } + } + catch(parseError) { + return callback( + new Error('JSON parseError with HTTP Status: ' + response.statusCode + ' ' + response.statusMessage), + data, + response + ); + } + + + // response object errors + // This should return an error object not an array of errors + if (data.errors !== undefined) { + return callback(data.errors, data, response); + } + + // status code errors + if(response.statusCode < 200 || response.statusCode > 299) { + return callback( + new Error('HTTP Error: ' + response.statusCode + ' ' + response.statusMessage), + data, + response + ); + } + // no errors + callback(null, data, response); + }); + +}; + +/** + * GET + */ +Twitter.prototype.get = function(url, params, callback) { + return this.__request('get', url, params, callback); +}; + +/** + * POST + */ +Twitter.prototype.post = function(url, params, callback) { + return this.__request('post', url, params, callback); +}; + +/** + * STREAM + */ +Twitter.prototype.stream = function(method, params, callback) { + if (typeof params === 'function') { + callback = params; + params = {}; + } + + var base = 'stream'; + + if (method === 'user' || method === 'site') { + base = method + '_' + base; + } + + var url = this.__buildEndpoint(method, base); + var request = this.request({url: url, qs: params}); + var stream = new Streamparser(); + + stream.destroy = function() { + // FIXME: should we emit end/close on explicit destroy? + if ( typeof request.abort === 'function' ) { + request.abort(); // node v0.4.0 + } + else { + request.socket.destroy(); + } + }; + + request.on('response', function(response) { + if(response.statusCode !== 200) { + stream.emit('error', new Error('Status Code: ' + response.statusCode)); + } + else { + stream.emit('response', response); + } + + response.on('data', function(chunk) { + stream.receive(chunk); + }); + + response.on('error', function(error) { + stream.emit('error', error); + }); + + response.on('end', function() { + stream.emit('end', response); + }); + }); + + request.on('error', function(error) { + stream.emit('error', error); + }); + request.end(); + + if (typeof callback === 'function') { + callback(stream); + } + else { + return stream; + } +}; + + +module.exports = Twitter; + + +/***/ }), + +/***/ 54: +/***/ (function(__unusedmodule, exports) { + +"use strict"; +/*! + * Copyright (c) 2015, Salesforce.com, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Salesforce.com nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * "A request-path path-matches a given cookie-path if at least one of the + * following conditions holds:" + */ +function pathMatch (reqPath, cookiePath) { + // "o The cookie-path and the request-path are identical." + if (cookiePath === reqPath) { + return true; + } + + var idx = reqPath.indexOf(cookiePath); + if (idx === 0) { + // "o The cookie-path is a prefix of the request-path, and the last + // character of the cookie-path is %x2F ("/")." + if (cookiePath.substr(-1) === "/") { + return true; + } + + // " o The cookie-path is a prefix of the request-path, and the first + // character of the request-path that is not included in the cookie- path + // is a %x2F ("/") character." + if (reqPath.substr(cookiePath.length, 1) === "/") { + return true; + } + } + + return false; +} + +exports.pathMatch = pathMatch; + + +/***/ }), + +/***/ 62: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2011 Mark Cavage All rights reserved. + +// If you have no idea what ASN.1 or BER is, see this: +// ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc + +var Ber = __webpack_require__(249); + + + +// --- Exported API + +module.exports = { + + Ber: Ber, + + BerReader: Ber.Reader, + + BerWriter: Ber.Writer + +}; + + +/***/ }), + +/***/ 64: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2012 Joyent, Inc. All rights reserved. + +var assert = __webpack_require__(477); +var crypto = __webpack_require__(417); +var http = __webpack_require__(605); +var util = __webpack_require__(669); +var sshpk = __webpack_require__(650); +var jsprim = __webpack_require__(348); +var utils = __webpack_require__(909); + +var sprintf = __webpack_require__(669).format; + +var HASH_ALGOS = utils.HASH_ALGOS; +var PK_ALGOS = utils.PK_ALGOS; +var InvalidAlgorithmError = utils.InvalidAlgorithmError; +var HttpSignatureError = utils.HttpSignatureError; +var validateAlgorithm = utils.validateAlgorithm; + +///--- Globals + +var AUTHZ_FMT = + 'Signature keyId="%s",algorithm="%s",headers="%s",signature="%s"'; + +///--- Specific Errors + +function MissingHeaderError(message) { + HttpSignatureError.call(this, message, MissingHeaderError); +} +util.inherits(MissingHeaderError, HttpSignatureError); + +function StrictParsingError(message) { + HttpSignatureError.call(this, message, StrictParsingError); +} +util.inherits(StrictParsingError, HttpSignatureError); + +/* See createSigner() */ +function RequestSigner(options) { + assert.object(options, 'options'); + + var alg = []; + if (options.algorithm !== undefined) { + assert.string(options.algorithm, 'options.algorithm'); + alg = validateAlgorithm(options.algorithm); + } + this.rs_alg = alg; + + /* + * RequestSigners come in two varieties: ones with an rs_signFunc, and ones + * with an rs_signer. + * + * rs_signFunc-based RequestSigners have to build up their entire signing + * string within the rs_lines array and give it to rs_signFunc as a single + * concat'd blob. rs_signer-based RequestSigners can add a line at a time to + * their signing state by using rs_signer.update(), thus only needing to + * buffer the hash function state and one line at a time. + */ + if (options.sign !== undefined) { + assert.func(options.sign, 'options.sign'); + this.rs_signFunc = options.sign; + + } else if (alg[0] === 'hmac' && options.key !== undefined) { + assert.string(options.keyId, 'options.keyId'); + this.rs_keyId = options.keyId; + + if (typeof (options.key) !== 'string' && !Buffer.isBuffer(options.key)) + throw (new TypeError('options.key for HMAC must be a string or Buffer')); + + /* + * Make an rs_signer for HMACs, not a rs_signFunc -- HMACs digest their + * data in chunks rather than requiring it all to be given in one go + * at the end, so they are more similar to signers than signFuncs. + */ + this.rs_signer = crypto.createHmac(alg[1].toUpperCase(), options.key); + this.rs_signer.sign = function () { + var digest = this.digest('base64'); + return ({ + hashAlgorithm: alg[1], + toString: function () { return (digest); } + }); + }; + + } else if (options.key !== undefined) { + var key = options.key; + if (typeof (key) === 'string' || Buffer.isBuffer(key)) + key = sshpk.parsePrivateKey(key); + + assert.ok(sshpk.PrivateKey.isPrivateKey(key, [1, 2]), + 'options.key must be a sshpk.PrivateKey'); + this.rs_key = key; + + assert.string(options.keyId, 'options.keyId'); + this.rs_keyId = options.keyId; + + if (!PK_ALGOS[key.type]) { + throw (new InvalidAlgorithmError(key.type.toUpperCase() + ' type ' + + 'keys are not supported')); + } + + if (alg[0] !== undefined && key.type !== alg[0]) { + throw (new InvalidAlgorithmError('options.key must be a ' + + alg[0].toUpperCase() + ' key, was given a ' + + key.type.toUpperCase() + ' key instead')); + } + + this.rs_signer = key.createSign(alg[1]); + + } else { + throw (new TypeError('options.sign (func) or options.key is required')); + } + + this.rs_headers = []; + this.rs_lines = []; +} + +/** + * Adds a header to be signed, with its value, into this signer. + * + * @param {String} header + * @param {String} value + * @return {String} value written + */ +RequestSigner.prototype.writeHeader = function (header, value) { + assert.string(header, 'header'); + header = header.toLowerCase(); + assert.string(value, 'value'); + + this.rs_headers.push(header); + + if (this.rs_signFunc) { + this.rs_lines.push(header + ': ' + value); + + } else { + var line = header + ': ' + value; + if (this.rs_headers.length > 0) + line = '\n' + line; + this.rs_signer.update(line); + } + + return (value); +}; + +/** + * Adds a default Date header, returning its value. + * + * @return {String} + */ +RequestSigner.prototype.writeDateHeader = function () { + return (this.writeHeader('date', jsprim.rfc1123(new Date()))); +}; + +/** + * Adds the request target line to be signed. + * + * @param {String} method, HTTP method (e.g. 'get', 'post', 'put') + * @param {String} path + */ +RequestSigner.prototype.writeTarget = function (method, path) { + assert.string(method, 'method'); + assert.string(path, 'path'); + method = method.toLowerCase(); + this.writeHeader('(request-target)', method + ' ' + path); +}; + +/** + * Calculate the value for the Authorization header on this request + * asynchronously. + * + * @param {Func} callback (err, authz) + */ +RequestSigner.prototype.sign = function (cb) { + assert.func(cb, 'callback'); + + if (this.rs_headers.length < 1) + throw (new Error('At least one header must be signed')); + + var alg, authz; + if (this.rs_signFunc) { + var data = this.rs_lines.join('\n'); + var self = this; + this.rs_signFunc(data, function (err, sig) { + if (err) { + cb(err); + return; + } + try { + assert.object(sig, 'signature'); + assert.string(sig.keyId, 'signature.keyId'); + assert.string(sig.algorithm, 'signature.algorithm'); + assert.string(sig.signature, 'signature.signature'); + alg = validateAlgorithm(sig.algorithm); + + authz = sprintf(AUTHZ_FMT, + sig.keyId, + sig.algorithm, + self.rs_headers.join(' '), + sig.signature); + } catch (e) { + cb(e); + return; + } + cb(null, authz); + }); + + } else { + try { + var sigObj = this.rs_signer.sign(); + } catch (e) { + cb(e); + return; + } + alg = (this.rs_alg[0] || this.rs_key.type) + '-' + sigObj.hashAlgorithm; + var signature = sigObj.toString(); + authz = sprintf(AUTHZ_FMT, + this.rs_keyId, + alg, + this.rs_headers.join(' '), + signature); + cb(null, authz); + } +}; + +///--- Exported API + +module.exports = { + /** + * Identifies whether a given object is a request signer or not. + * + * @param {Object} object, the object to identify + * @returns {Boolean} + */ + isSigner: function (obj) { + if (typeof (obj) === 'object' && obj instanceof RequestSigner) + return (true); + return (false); + }, + + /** + * Creates a request signer, used to asynchronously build a signature + * for a request (does not have to be an http.ClientRequest). + * + * @param {Object} options, either: + * - {String} keyId + * - {String|Buffer} key + * - {String} algorithm (optional, required for HMAC) + * or: + * - {Func} sign (data, cb) + * @return {RequestSigner} + */ + createSigner: function createSigner(options) { + return (new RequestSigner(options)); + }, + + /** + * Adds an 'Authorization' header to an http.ClientRequest object. + * + * Note that this API will add a Date header if it's not already set. Any + * other headers in the options.headers array MUST be present, or this + * will throw. + * + * You shouldn't need to check the return type; it's just there if you want + * to be pedantic. + * + * The optional flag indicates whether parsing should use strict enforcement + * of the version draft-cavage-http-signatures-04 of the spec or beyond. + * The default is to be loose and support + * older versions for compatibility. + * + * @param {Object} request an instance of http.ClientRequest. + * @param {Object} options signing parameters object: + * - {String} keyId required. + * - {String} key required (either a PEM or HMAC key). + * - {Array} headers optional; defaults to ['date']. + * - {String} algorithm optional (unless key is HMAC); + * default is the same as the sshpk default + * signing algorithm for the type of key given + * - {String} httpVersion optional; defaults to '1.1'. + * - {Boolean} strict optional; defaults to 'false'. + * @return {Boolean} true if Authorization (and optionally Date) were added. + * @throws {TypeError} on bad parameter types (input). + * @throws {InvalidAlgorithmError} if algorithm was bad or incompatible with + * the given key. + * @throws {sshpk.KeyParseError} if key was bad. + * @throws {MissingHeaderError} if a header to be signed was specified but + * was not present. + */ + signRequest: function signRequest(request, options) { + assert.object(request, 'request'); + assert.object(options, 'options'); + assert.optionalString(options.algorithm, 'options.algorithm'); + assert.string(options.keyId, 'options.keyId'); + assert.optionalArrayOfString(options.headers, 'options.headers'); + assert.optionalString(options.httpVersion, 'options.httpVersion'); + + if (!request.getHeader('Date')) + request.setHeader('Date', jsprim.rfc1123(new Date())); + if (!options.headers) + options.headers = ['date']; + if (!options.httpVersion) + options.httpVersion = '1.1'; + + var alg = []; + if (options.algorithm) { + options.algorithm = options.algorithm.toLowerCase(); + alg = validateAlgorithm(options.algorithm); + } + + var i; + var stringToSign = ''; + for (i = 0; i < options.headers.length; i++) { + if (typeof (options.headers[i]) !== 'string') + throw new TypeError('options.headers must be an array of Strings'); + + var h = options.headers[i].toLowerCase(); + + if (h === 'request-line') { + if (!options.strict) { + /** + * We allow headers from the older spec drafts if strict parsing isn't + * specified in options. + */ + stringToSign += + request.method + ' ' + request.path + ' HTTP/' + + options.httpVersion; + } else { + /* Strict parsing doesn't allow older draft headers. */ + throw (new StrictParsingError('request-line is not a valid header ' + + 'with strict parsing enabled.')); + } + } else if (h === '(request-target)') { + stringToSign += + '(request-target): ' + request.method.toLowerCase() + ' ' + + request.path; + } else { + var value = request.getHeader(h); + if (value === undefined || value === '') { + throw new MissingHeaderError(h + ' was not in the request'); + } + stringToSign += h + ': ' + value; + } + + if ((i + 1) < options.headers.length) + stringToSign += '\n'; + } + + /* This is just for unit tests. */ + if (request.hasOwnProperty('_stringToSign')) { + request._stringToSign = stringToSign; + } + + var signature; + if (alg[0] === 'hmac') { + if (typeof (options.key) !== 'string' && !Buffer.isBuffer(options.key)) + throw (new TypeError('options.key must be a string or Buffer')); + + var hmac = crypto.createHmac(alg[1].toUpperCase(), options.key); + hmac.update(stringToSign); + signature = hmac.digest('base64'); + + } else { + var key = options.key; + if (typeof (key) === 'string' || Buffer.isBuffer(key)) + key = sshpk.parsePrivateKey(options.key); + + assert.ok(sshpk.PrivateKey.isPrivateKey(key, [1, 2]), + 'options.key must be a sshpk.PrivateKey'); + + if (!PK_ALGOS[key.type]) { + throw (new InvalidAlgorithmError(key.type.toUpperCase() + ' type ' + + 'keys are not supported')); + } + + if (alg[0] !== undefined && key.type !== alg[0]) { + throw (new InvalidAlgorithmError('options.key must be a ' + + alg[0].toUpperCase() + ' key, was given a ' + + key.type.toUpperCase() + ' key instead')); + } + + var signer = key.createSign(alg[1]); + signer.update(stringToSign); + var sigObj = signer.sign(); + if (!HASH_ALGOS[sigObj.hashAlgorithm]) { + throw (new InvalidAlgorithmError(sigObj.hashAlgorithm.toUpperCase() + + ' is not a supported hash algorithm')); + } + options.algorithm = key.type + '-' + sigObj.hashAlgorithm; + signature = sigObj.toString(); + assert.notStrictEqual(signature, '', 'empty signature produced'); + } + + var authzHeaderName = options.authorizationHeaderName || 'Authorization'; + + request.setHeader(authzHeaderName, sprintf(AUTHZ_FMT, + options.keyId, + options.algorithm, + options.headers.join(' '), + signature)); + + return true; + } + +}; + + +/***/ }), + +/***/ 69: +/***/ (function(module) { + +// populates missing values +module.exports = function(dst, src) { + + Object.keys(src).forEach(function(prop) + { + dst[prop] = dst[prop] || src[prop]; + }); + + return dst; +}; + + +/***/ }), + +/***/ 71: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +// glorious streaming json parser, built specifically for the twitter streaming api +// assumptions: +// 1) ninjas are mammals +// 2) tweets come in chunks of text, surrounded by {}'s, separated by line breaks +// 3) only one tweet per chunk +// +// p = new parser.instance() +// p.addListener('object', function...) +// p.receive(data) +// p.receive(data) +// ... + +var EventEmitter = __webpack_require__(614).EventEmitter; + +var Parser = module.exports = function Parser() { + // Make sure we call our parents constructor + EventEmitter.call(this); + this.buffer = ''; + return this; +}; + +// The parser emits events! +Parser.prototype = Object.create(EventEmitter.prototype); + +Parser.END = '\r\n'; +Parser.END_LENGTH = 2; + +Parser.prototype.receive = function receive(buffer) { + this.buffer += buffer.toString('utf8'); + var index, json; + + // We have END? + while ((index = this.buffer.indexOf(Parser.END)) > -1) { + json = this.buffer.slice(0, index); + this.buffer = this.buffer.slice(index + Parser.END_LENGTH); + if (json.length > 0) { + try { + json = JSON.parse(json); + // Event message + if (json.event !== undefined) { + // First emit specific event + this.emit(json.event, json); + // Now emit catch-all event + this.emit('event', json); + } + // Delete message + else if (json.delete !== undefined) { + this.emit('delete', json); + } + // Friends message (beginning of stream) + else if (json.friends !== undefined || json.friends_str !== undefined) { + this.emit('friends', json); + } + // Any other message + else { + this.emit('data', json); + } + } + catch (error) { + error.source = json; + this.emit('error', error); + } + } + else { + // Keep Alive + this.emit('ping'); + } + } +}; + + +/***/ }), + +/***/ 78: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2015 Joyent, Inc. + +module.exports = { + read: read, + readSSHPrivate: readSSHPrivate, + write: write +}; + +var assert = __webpack_require__(477); +var asn1 = __webpack_require__(62); +var Buffer = __webpack_require__(215).Buffer; +var algs = __webpack_require__(98); +var utils = __webpack_require__(270); +var crypto = __webpack_require__(417); + +var Key = __webpack_require__(852); +var PrivateKey = __webpack_require__(502); +var pem = __webpack_require__(268); +var rfc4253 = __webpack_require__(538); +var SSHBuffer = __webpack_require__(940); +var errors = __webpack_require__(753); + +var bcrypt; + +function read(buf, options) { + return (pem.read(buf, options)); +} + +var MAGIC = 'openssh-key-v1'; + +function readSSHPrivate(type, buf, options) { + buf = new SSHBuffer({buffer: buf}); + + var magic = buf.readCString(); + assert.strictEqual(magic, MAGIC, 'bad magic string'); + + var cipher = buf.readString(); + var kdf = buf.readString(); + var kdfOpts = buf.readBuffer(); + + var nkeys = buf.readInt(); + if (nkeys !== 1) { + throw (new Error('OpenSSH-format key file contains ' + + 'multiple keys: this is unsupported.')); + } + + var pubKey = buf.readBuffer(); + + if (type === 'public') { + assert.ok(buf.atEnd(), 'excess bytes left after key'); + return (rfc4253.read(pubKey)); + } + + var privKeyBlob = buf.readBuffer(); + assert.ok(buf.atEnd(), 'excess bytes left after key'); + + var kdfOptsBuf = new SSHBuffer({ buffer: kdfOpts }); + switch (kdf) { + case 'none': + if (cipher !== 'none') { + throw (new Error('OpenSSH-format key uses KDF "none" ' + + 'but specifies a cipher other than "none"')); + } + break; + case 'bcrypt': + var salt = kdfOptsBuf.readBuffer(); + var rounds = kdfOptsBuf.readInt(); + var cinf = utils.opensshCipherInfo(cipher); + if (bcrypt === undefined) { + bcrypt = __webpack_require__(641); + } + + if (typeof (options.passphrase) === 'string') { + options.passphrase = Buffer.from(options.passphrase, + 'utf-8'); + } + if (!Buffer.isBuffer(options.passphrase)) { + throw (new errors.KeyEncryptedError( + options.filename, 'OpenSSH')); + } + + var pass = new Uint8Array(options.passphrase); + var salti = new Uint8Array(salt); + /* Use the pbkdf to derive both the key and the IV. */ + var out = new Uint8Array(cinf.keySize + cinf.blockSize); + var res = bcrypt.pbkdf(pass, pass.length, salti, salti.length, + out, out.length, rounds); + if (res !== 0) { + throw (new Error('bcrypt_pbkdf function returned ' + + 'failure, parameters invalid')); + } + out = Buffer.from(out); + var ckey = out.slice(0, cinf.keySize); + var iv = out.slice(cinf.keySize, cinf.keySize + cinf.blockSize); + var cipherStream = crypto.createDecipheriv(cinf.opensslName, + ckey, iv); + cipherStream.setAutoPadding(false); + var chunk, chunks = []; + cipherStream.once('error', function (e) { + if (e.toString().indexOf('bad decrypt') !== -1) { + throw (new Error('Incorrect passphrase ' + + 'supplied, could not decrypt key')); + } + throw (e); + }); + cipherStream.write(privKeyBlob); + cipherStream.end(); + while ((chunk = cipherStream.read()) !== null) + chunks.push(chunk); + privKeyBlob = Buffer.concat(chunks); + break; + default: + throw (new Error( + 'OpenSSH-format key uses unknown KDF "' + kdf + '"')); + } + + buf = new SSHBuffer({buffer: privKeyBlob}); + + var checkInt1 = buf.readInt(); + var checkInt2 = buf.readInt(); + if (checkInt1 !== checkInt2) { + throw (new Error('Incorrect passphrase supplied, could not ' + + 'decrypt key')); + } + + var ret = {}; + var key = rfc4253.readInternal(ret, 'private', buf.remainder()); + + buf.skip(ret.consumed); + + var comment = buf.readString(); + key.comment = comment; + + return (key); +} + +function write(key, options) { + var pubKey; + if (PrivateKey.isPrivateKey(key)) + pubKey = key.toPublic(); + else + pubKey = key; + + var cipher = 'none'; + var kdf = 'none'; + var kdfopts = Buffer.alloc(0); + var cinf = { blockSize: 8 }; + var passphrase; + if (options !== undefined) { + passphrase = options.passphrase; + if (typeof (passphrase) === 'string') + passphrase = Buffer.from(passphrase, 'utf-8'); + if (passphrase !== undefined) { + assert.buffer(passphrase, 'options.passphrase'); + assert.optionalString(options.cipher, 'options.cipher'); + cipher = options.cipher; + if (cipher === undefined) + cipher = 'aes128-ctr'; + cinf = utils.opensshCipherInfo(cipher); + kdf = 'bcrypt'; + } + } + + var privBuf; + if (PrivateKey.isPrivateKey(key)) { + privBuf = new SSHBuffer({}); + var checkInt = crypto.randomBytes(4).readUInt32BE(0); + privBuf.writeInt(checkInt); + privBuf.writeInt(checkInt); + privBuf.write(key.toBuffer('rfc4253')); + privBuf.writeString(key.comment || ''); + + var n = 1; + while (privBuf._offset % cinf.blockSize !== 0) + privBuf.writeChar(n++); + privBuf = privBuf.toBuffer(); + } + + switch (kdf) { + case 'none': + break; + case 'bcrypt': + var salt = crypto.randomBytes(16); + var rounds = 16; + var kdfssh = new SSHBuffer({}); + kdfssh.writeBuffer(salt); + kdfssh.writeInt(rounds); + kdfopts = kdfssh.toBuffer(); + + if (bcrypt === undefined) { + bcrypt = __webpack_require__(641); + } + var pass = new Uint8Array(passphrase); + var salti = new Uint8Array(salt); + /* Use the pbkdf to derive both the key and the IV. */ + var out = new Uint8Array(cinf.keySize + cinf.blockSize); + var res = bcrypt.pbkdf(pass, pass.length, salti, salti.length, + out, out.length, rounds); + if (res !== 0) { + throw (new Error('bcrypt_pbkdf function returned ' + + 'failure, parameters invalid')); + } + out = Buffer.from(out); + var ckey = out.slice(0, cinf.keySize); + var iv = out.slice(cinf.keySize, cinf.keySize + cinf.blockSize); + + var cipherStream = crypto.createCipheriv(cinf.opensslName, + ckey, iv); + cipherStream.setAutoPadding(false); + var chunk, chunks = []; + cipherStream.once('error', function (e) { + throw (e); + }); + cipherStream.write(privBuf); + cipherStream.end(); + while ((chunk = cipherStream.read()) !== null) + chunks.push(chunk); + privBuf = Buffer.concat(chunks); + break; + default: + throw (new Error('Unsupported kdf ' + kdf)); + } + + var buf = new SSHBuffer({}); + + buf.writeCString(MAGIC); + buf.writeString(cipher); /* cipher */ + buf.writeString(kdf); /* kdf */ + buf.writeBuffer(kdfopts); /* kdfoptions */ + + buf.writeInt(1); /* nkeys */ + buf.writeBuffer(pubKey.toBuffer('rfc4253')); + + if (privBuf) + buf.writeBuffer(privBuf); + + buf = buf.toBuffer(); + + var header; + if (PrivateKey.isPrivateKey(key)) + header = 'OPENSSH PRIVATE KEY'; + else + header = 'OPENSSH PUBLIC KEY'; + + var tmp = buf.toString('base64'); + var len = tmp.length + (tmp.length / 70) + + 18 + 16 + header.length*2 + 10; + buf = Buffer.alloc(len); + var o = 0; + o += buf.write('-----BEGIN ' + header + '-----\n', o); + for (var i = 0; i < tmp.length; ) { + var limit = i + 70; + if (limit > tmp.length) + limit = tmp.length; + o += buf.write(tmp.slice(i, limit), o); + buf[o++] = 10; + i = limit; + } + o += buf.write('-----END ' + header + '-----\n', o); + + return (buf.slice(0, o)); +} + + +/***/ }), + +/***/ 85: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate__limitItems(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $errorKeyword; + var $data = 'data' + ($dataLvl || ''); + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + var $op = $keyword == 'maxItems' ? '>' : '<'; + out += 'if ( '; + if ($isData) { + out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || '; + } + out += ' ' + ($data) + '.length ' + ($op) + ' ' + ($schemaValue) + ') { '; + var $errorKeyword = $keyword; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ($errorKeyword || '_limitItems') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { limit: ' + ($schemaValue) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT have '; + if ($keyword == 'maxItems') { + out += 'more'; + } else { + out += 'fewer'; + } + out += ' than '; + if ($isData) { + out += '\' + ' + ($schemaValue) + ' + \''; + } else { + out += '' + ($schema); + } + out += ' items\' '; + } + if (it.opts.verbose) { + out += ' , schema: '; + if ($isData) { + out += 'validate.schema' + ($schemaPath); + } else { + out += '' + ($schema); + } + out += ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += '} '; + if ($breakOnError) { + out += ' else { '; + } + return out; +} + + +/***/ }), + +/***/ 87: +/***/ (function(module) { + +module.exports = require("os"); + +/***/ }), + +/***/ 91: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var serialOrdered = __webpack_require__(892); + +// Public API +module.exports = serial; + +/** + * Runs iterator over provided array elements in series + * + * @param {array|object} list - array or object (named list) to iterate over + * @param {function} iterator - iterator to run + * @param {function} callback - invoked when all elements processed + * @returns {function} - jobs terminator + */ +function serial(list, iterator, callback) +{ + return serialOrdered(list, iterator, null, callback); +} + + +/***/ }), + +/***/ 98: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2015 Joyent, Inc. + +var Buffer = __webpack_require__(215).Buffer; + +var algInfo = { + 'dsa': { + parts: ['p', 'q', 'g', 'y'], + sizePart: 'p' + }, + 'rsa': { + parts: ['e', 'n'], + sizePart: 'n' + }, + 'ecdsa': { + parts: ['curve', 'Q'], + sizePart: 'Q' + }, + 'ed25519': { + parts: ['A'], + sizePart: 'A' + } +}; +algInfo['curve25519'] = algInfo['ed25519']; + +var algPrivInfo = { + 'dsa': { + parts: ['p', 'q', 'g', 'y', 'x'] + }, + 'rsa': { + parts: ['n', 'e', 'd', 'iqmp', 'p', 'q'] + }, + 'ecdsa': { + parts: ['curve', 'Q', 'd'] + }, + 'ed25519': { + parts: ['A', 'k'] + } +}; +algPrivInfo['curve25519'] = algPrivInfo['ed25519']; + +var hashAlgs = { + 'md5': true, + 'sha1': true, + 'sha256': true, + 'sha384': true, + 'sha512': true +}; + +/* + * Taken from + * http://csrc.nist.gov/groups/ST/toolkit/documents/dss/NISTReCur.pdf + */ +var curves = { + 'nistp256': { + size: 256, + pkcs8oid: '1.2.840.10045.3.1.7', + p: Buffer.from(('00' + + 'ffffffff 00000001 00000000 00000000' + + '00000000 ffffffff ffffffff ffffffff'). + replace(/ /g, ''), 'hex'), + a: Buffer.from(('00' + + 'FFFFFFFF 00000001 00000000 00000000' + + '00000000 FFFFFFFF FFFFFFFF FFFFFFFC'). + replace(/ /g, ''), 'hex'), + b: Buffer.from(( + '5ac635d8 aa3a93e7 b3ebbd55 769886bc' + + '651d06b0 cc53b0f6 3bce3c3e 27d2604b'). + replace(/ /g, ''), 'hex'), + s: Buffer.from(('00' + + 'c49d3608 86e70493 6a6678e1 139d26b7' + + '819f7e90'). + replace(/ /g, ''), 'hex'), + n: Buffer.from(('00' + + 'ffffffff 00000000 ffffffff ffffffff' + + 'bce6faad a7179e84 f3b9cac2 fc632551'). + replace(/ /g, ''), 'hex'), + G: Buffer.from(('04' + + '6b17d1f2 e12c4247 f8bce6e5 63a440f2' + + '77037d81 2deb33a0 f4a13945 d898c296' + + '4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16' + + '2bce3357 6b315ece cbb64068 37bf51f5'). + replace(/ /g, ''), 'hex') + }, + 'nistp384': { + size: 384, + pkcs8oid: '1.3.132.0.34', + p: Buffer.from(('00' + + 'ffffffff ffffffff ffffffff ffffffff' + + 'ffffffff ffffffff ffffffff fffffffe' + + 'ffffffff 00000000 00000000 ffffffff'). + replace(/ /g, ''), 'hex'), + a: Buffer.from(('00' + + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF' + + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE' + + 'FFFFFFFF 00000000 00000000 FFFFFFFC'). + replace(/ /g, ''), 'hex'), + b: Buffer.from(( + 'b3312fa7 e23ee7e4 988e056b e3f82d19' + + '181d9c6e fe814112 0314088f 5013875a' + + 'c656398d 8a2ed19d 2a85c8ed d3ec2aef'). + replace(/ /g, ''), 'hex'), + s: Buffer.from(('00' + + 'a335926a a319a27a 1d00896a 6773a482' + + '7acdac73'). + replace(/ /g, ''), 'hex'), + n: Buffer.from(('00' + + 'ffffffff ffffffff ffffffff ffffffff' + + 'ffffffff ffffffff c7634d81 f4372ddf' + + '581a0db2 48b0a77a ecec196a ccc52973'). + replace(/ /g, ''), 'hex'), + G: Buffer.from(('04' + + 'aa87ca22 be8b0537 8eb1c71e f320ad74' + + '6e1d3b62 8ba79b98 59f741e0 82542a38' + + '5502f25d bf55296c 3a545e38 72760ab7' + + '3617de4a 96262c6f 5d9e98bf 9292dc29' + + 'f8f41dbd 289a147c e9da3113 b5f0b8c0' + + '0a60b1ce 1d7e819d 7a431d7c 90ea0e5f'). + replace(/ /g, ''), 'hex') + }, + 'nistp521': { + size: 521, + pkcs8oid: '1.3.132.0.35', + p: Buffer.from(( + '01ffffff ffffffff ffffffff ffffffff' + + 'ffffffff ffffffff ffffffff ffffffff' + + 'ffffffff ffffffff ffffffff ffffffff' + + 'ffffffff ffffffff ffffffff ffffffff' + + 'ffff').replace(/ /g, ''), 'hex'), + a: Buffer.from(('01FF' + + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF' + + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF' + + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF' + + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFC'). + replace(/ /g, ''), 'hex'), + b: Buffer.from(('51' + + '953eb961 8e1c9a1f 929a21a0 b68540ee' + + 'a2da725b 99b315f3 b8b48991 8ef109e1' + + '56193951 ec7e937b 1652c0bd 3bb1bf07' + + '3573df88 3d2c34f1 ef451fd4 6b503f00'). + replace(/ /g, ''), 'hex'), + s: Buffer.from(('00' + + 'd09e8800 291cb853 96cc6717 393284aa' + + 'a0da64ba').replace(/ /g, ''), 'hex'), + n: Buffer.from(('01ff' + + 'ffffffff ffffffff ffffffff ffffffff' + + 'ffffffff ffffffff ffffffff fffffffa' + + '51868783 bf2f966b 7fcc0148 f709a5d0' + + '3bb5c9b8 899c47ae bb6fb71e 91386409'). + replace(/ /g, ''), 'hex'), + G: Buffer.from(('04' + + '00c6 858e06b7 0404e9cd 9e3ecb66 2395b442' + + '9c648139 053fb521 f828af60 6b4d3dba' + + 'a14b5e77 efe75928 fe1dc127 a2ffa8de' + + '3348b3c1 856a429b f97e7e31 c2e5bd66' + + '0118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9' + + '98f54449 579b4468 17afbd17 273e662c' + + '97ee7299 5ef42640 c550b901 3fad0761' + + '353c7086 a272c240 88be9476 9fd16650'). + replace(/ /g, ''), 'hex') + } +}; + +module.exports = { + info: algInfo, + privInfo: algPrivInfo, + hashAlgs: hashAlgs, + curves: curves +}; + + +/***/ }), + +/***/ 104: +/***/ (function(__unusedmodule, __unusedexports, __webpack_require__) { + +/*! + * Copyright 2020 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const core = __webpack_require__(470); +const Twitter = __webpack_require__(50); + +function sendTweet() { + const twitter = new Twitter({ + consumer_key: core.getInput('consumer-key'), + consumer_secret: core.getInput('consumer-secret'), + access_token_key: core.getInput('access-token'), + access_token_secret: core.getInput('access-token-secret') + }); + + return twitter.post('/statuses/update', {status: core.getInput('status')}) + .then(() => { + return; + }) + .catch((err) => { + core.setFailed(err.message); + }); +} + +sendTweet(); + + +/***/ }), + +/***/ 107: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_allOf(it, $keyword, $ruleType) { + var out = ' '; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + var $currentBaseId = $it.baseId, + $allSchemasEmpty = true; + var arr1 = $schema; + if (arr1) { + var $sch, $i = -1, + l1 = arr1.length - 1; + while ($i < l1) { + $sch = arr1[$i += 1]; + if ((it.opts.strictKeywords ? typeof $sch == 'object' && Object.keys($sch).length > 0 : it.util.schemaHasRules($sch, it.RULES.all))) { + $allSchemasEmpty = false; + $it.schema = $sch; + $it.schemaPath = $schemaPath + '[' + $i + ']'; + $it.errSchemaPath = $errSchemaPath + '/' + $i; + out += ' ' + (it.validate($it)) + ' '; + $it.baseId = $currentBaseId; + if ($breakOnError) { + out += ' if (' + ($nextValid) + ') { '; + $closingBraces += '}'; + } + } + } + } + if ($breakOnError) { + if ($allSchemasEmpty) { + out += ' if (true) { '; + } else { + out += ' ' + ($closingBraces.slice(0, -1)) + ' '; + } + } + out = it.util.cleanUpCode(out); + return out; +} + + +/***/ }), + +/***/ 113: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +var crypto = __webpack_require__(417) + +function sha (key, body, algorithm) { + return crypto.createHmac(algorithm, key).update(body).digest('base64') +} + +function rsa (key, body) { + return crypto.createSign('RSA-SHA1').update(body).sign(key, 'base64') +} + +function rfc3986 (str) { + return encodeURIComponent(str) + .replace(/!/g,'%21') + .replace(/\*/g,'%2A') + .replace(/\(/g,'%28') + .replace(/\)/g,'%29') + .replace(/'/g,'%27') +} + +// Maps object to bi-dimensional array +// Converts { foo: 'A', bar: [ 'b', 'B' ]} to +// [ ['foo', 'A'], ['bar', 'b'], ['bar', 'B'] ] +function map (obj) { + var key, val, arr = [] + for (key in obj) { + val = obj[key] + if (Array.isArray(val)) + for (var i = 0; i < val.length; i++) + arr.push([key, val[i]]) + else if (typeof val === 'object') + for (var prop in val) + arr.push([key + '[' + prop + ']', val[prop]]) + else + arr.push([key, val]) + } + return arr +} + +// Compare function for sort +function compare (a, b) { + return a > b ? 1 : a < b ? -1 : 0 +} + +function generateBase (httpMethod, base_uri, params) { + // adapted from https://dev.twitter.com/docs/auth/oauth and + // https://dev.twitter.com/docs/auth/creating-signature + + // Parameter normalization + // http://tools.ietf.org/html/rfc5849#section-3.4.1.3.2 + var normalized = map(params) + // 1. First, the name and value of each parameter are encoded + .map(function (p) { + return [ rfc3986(p[0]), rfc3986(p[1] || '') ] + }) + // 2. The parameters are sorted by name, using ascending byte value + // ordering. If two or more parameters share the same name, they + // are sorted by their value. + .sort(function (a, b) { + return compare(a[0], b[0]) || compare(a[1], b[1]) + }) + // 3. The name of each parameter is concatenated to its corresponding + // value using an "=" character (ASCII code 61) as a separator, even + // if the value is empty. + .map(function (p) { return p.join('=') }) + // 4. The sorted name/value pairs are concatenated together into a + // single string by using an "&" character (ASCII code 38) as + // separator. + .join('&') + + var base = [ + rfc3986(httpMethod ? httpMethod.toUpperCase() : 'GET'), + rfc3986(base_uri), + rfc3986(normalized) + ].join('&') + + return base +} + +function hmacsign (httpMethod, base_uri, params, consumer_secret, token_secret) { + var base = generateBase(httpMethod, base_uri, params) + var key = [ + consumer_secret || '', + token_secret || '' + ].map(rfc3986).join('&') + + return sha(key, base, 'sha1') +} + +function hmacsign256 (httpMethod, base_uri, params, consumer_secret, token_secret) { + var base = generateBase(httpMethod, base_uri, params) + var key = [ + consumer_secret || '', + token_secret || '' + ].map(rfc3986).join('&') + + return sha(key, base, 'sha256') +} + +function rsasign (httpMethod, base_uri, params, private_key, token_secret) { + var base = generateBase(httpMethod, base_uri, params) + var key = private_key || '' + + return rsa(key, base) +} + +function plaintext (consumer_secret, token_secret) { + var key = [ + consumer_secret || '', + token_secret || '' + ].map(rfc3986).join('&') + + return key +} + +function sign (signMethod, httpMethod, base_uri, params, consumer_secret, token_secret) { + var method + var skipArgs = 1 + + switch (signMethod) { + case 'RSA-SHA1': + method = rsasign + break + case 'HMAC-SHA1': + method = hmacsign + break + case 'HMAC-SHA256': + method = hmacsign256 + break + case 'PLAINTEXT': + method = plaintext + skipArgs = 4 + break + default: + throw new Error('Signature method not supported: ' + signMethod) + } + + return method.apply(null, [].slice.call(arguments, skipArgs)) +} + +exports.hmacsign = hmacsign +exports.hmacsign256 = hmacsign256 +exports.rsasign = rsasign +exports.plaintext = plaintext +exports.sign = sign +exports.rfc3986 = rfc3986 +exports.generateBase = generateBase + +/***/ }), + +/***/ 139: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Unique ID creation requires a high quality random # generator. In node.js +// this is pretty straight-forward - we use the crypto API. + +var crypto = __webpack_require__(417); + +module.exports = function nodeRNG() { + return crypto.randomBytes(16); +}; + + +/***/ }), + +/***/ 147: +/***/ (function(module) { + +// API +module.exports = state; + +/** + * Creates initial state object + * for iteration over list + * + * @param {array|object} list - list to iterate over + * @param {function|null} sortMethod - function to use for keys sort, + * or `null` to keep them as is + * @returns {object} - initial state object + */ +function state(list, sortMethod) +{ + var isNamedList = !Array.isArray(list) + , initState = + { + index : 0, + keyedList: isNamedList || sortMethod ? Object.keys(list) : null, + jobs : {}, + results : isNamedList ? {} : [], + size : isNamedList ? Object.keys(list).length : list.length + } + ; + + if (sortMethod) + { + // sort array keys based on it's values + // sort object's keys just on own merit + initState.keyedList.sort(isNamedList ? sortMethod : function(a, b) + { + return sortMethod(list[a], list[b]); + }); + } + + return initState; +} + + +/***/ }), + +/***/ 149: +/***/ (function(module, exports, __webpack_require__) { + +/* eslint-disable node/no-deprecated-api */ +var buffer = __webpack_require__(293) +var Buffer = buffer.Buffer + +// alternative to using Object.keys for old browsers +function copyProps (src, dst) { + for (var key in src) { + dst[key] = src[key] + } +} +if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { + module.exports = buffer +} else { + // Copy properties from require('buffer') + copyProps(buffer, exports) + exports.Buffer = SafeBuffer +} + +function SafeBuffer (arg, encodingOrOffset, length) { + return Buffer(arg, encodingOrOffset, length) +} + +SafeBuffer.prototype = Object.create(Buffer.prototype) + +// Copy static methods from Buffer +copyProps(Buffer, SafeBuffer) + +SafeBuffer.from = function (arg, encodingOrOffset, length) { + if (typeof arg === 'number') { + throw new TypeError('Argument must not be a number') + } + return Buffer(arg, encodingOrOffset, length) +} + +SafeBuffer.alloc = function (size, fill, encoding) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + var buf = Buffer(size) + if (fill !== undefined) { + if (typeof encoding === 'string') { + buf.fill(fill, encoding) + } else { + buf.fill(fill) + } + } else { + buf.fill(0) + } + return buf +} + +SafeBuffer.allocUnsafe = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return Buffer(size) +} + +SafeBuffer.allocUnsafeSlow = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return buffer.SlowBuffer(size) +} + + +/***/ }), + +/***/ 152: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var Stream = __webpack_require__(413).Stream; +var util = __webpack_require__(669); + +module.exports = DelayedStream; +function DelayedStream() { + this.source = null; + this.dataSize = 0; + this.maxDataSize = 1024 * 1024; + this.pauseStream = true; + + this._maxDataSizeExceeded = false; + this._released = false; + this._bufferedEvents = []; +} +util.inherits(DelayedStream, Stream); + +DelayedStream.create = function(source, options) { + var delayedStream = new this(); + + options = options || {}; + for (var option in options) { + delayedStream[option] = options[option]; + } + + delayedStream.source = source; + + var realEmit = source.emit; + source.emit = function() { + delayedStream._handleEmit(arguments); + return realEmit.apply(source, arguments); + }; + + source.on('error', function() {}); + if (delayedStream.pauseStream) { + source.pause(); + } + + return delayedStream; +}; + +Object.defineProperty(DelayedStream.prototype, 'readable', { + configurable: true, + enumerable: true, + get: function() { + return this.source.readable; + } +}); + +DelayedStream.prototype.setEncoding = function() { + return this.source.setEncoding.apply(this.source, arguments); +}; + +DelayedStream.prototype.resume = function() { + if (!this._released) { + this.release(); + } + + this.source.resume(); +}; + +DelayedStream.prototype.pause = function() { + this.source.pause(); +}; + +DelayedStream.prototype.release = function() { + this._released = true; + + this._bufferedEvents.forEach(function(args) { + this.emit.apply(this, args); + }.bind(this)); + this._bufferedEvents = []; +}; + +DelayedStream.prototype.pipe = function() { + var r = Stream.prototype.pipe.apply(this, arguments); + this.resume(); + return r; +}; + +DelayedStream.prototype._handleEmit = function(args) { + if (this._released) { + this.emit.apply(this, args); + return; + } + + if (args[0] === 'data') { + this.dataSize += args[1].length; + this._checkIfMaxDataSizeExceeded(); + } + + this._bufferedEvents.push(args); +}; + +DelayedStream.prototype._checkIfMaxDataSizeExceeded = function() { + if (this._maxDataSizeExceeded) { + return; + } + + if (this.dataSize <= this.maxDataSize) { + return; + } + + this._maxDataSizeExceeded = true; + var message = + 'DelayedStream#maxDataSize of ' + this.maxDataSize + ' bytes exceeded.' + this.emit('error', new Error(message)); +}; + + +/***/ }), + +/***/ 154: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_contains(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + var $idx = 'i' + $lvl, + $dataNxt = $it.dataLevel = it.dataLevel + 1, + $nextData = 'data' + $dataNxt, + $currentBaseId = it.baseId, + $nonEmptySchema = (it.opts.strictKeywords ? typeof $schema == 'object' && Object.keys($schema).length > 0 : it.util.schemaHasRules($schema, it.RULES.all)); + out += 'var ' + ($errs) + ' = errors;var ' + ($valid) + ';'; + if ($nonEmptySchema) { + var $wasComposite = it.compositeRule; + it.compositeRule = $it.compositeRule = true; + $it.schema = $schema; + $it.schemaPath = $schemaPath; + $it.errSchemaPath = $errSchemaPath; + out += ' var ' + ($nextValid) + ' = false; for (var ' + ($idx) + ' = 0; ' + ($idx) + ' < ' + ($data) + '.length; ' + ($idx) + '++) { '; + $it.errorPath = it.util.getPathExpr(it.errorPath, $idx, it.opts.jsonPointers, true); + var $passData = $data + '[' + $idx + ']'; + $it.dataPathArr[$dataNxt] = $idx; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + out += ' if (' + ($nextValid) + ') break; } '; + it.compositeRule = $it.compositeRule = $wasComposite; + out += ' ' + ($closingBraces) + ' if (!' + ($nextValid) + ') {'; + } else { + out += ' if (' + ($data) + '.length == 0) {'; + } + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('contains') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} '; + if (it.opts.messages !== false) { + out += ' , message: \'should contain a valid item\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } else { '; + if ($nonEmptySchema) { + out += ' errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; } '; + } + if (it.opts.allErrors) { + out += ' } '; + } + out = it.util.cleanUpCode(out); + return out; +} + + +/***/ }), + +/***/ 157: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var async = __webpack_require__(751) + , abort = __webpack_require__(566) + ; + +// API +module.exports = iterate; + +/** + * Iterates over each job object + * + * @param {array|object} list - array or object (named list) to iterate over + * @param {function} iterator - iterator to run + * @param {object} state - current job status + * @param {function} callback - invoked when all elements processed + */ +function iterate(list, iterator, state, callback) +{ + // store current index + var key = state['keyedList'] ? state['keyedList'][state.index] : state.index; + + state.jobs[key] = runJob(iterator, key, list[key], function(error, output) + { + // don't repeat yourself + // skip secondary callbacks + if (!(key in state.jobs)) + { + return; + } + + // clean up jobs + delete state.jobs[key]; + + if (error) + { + // don't process rest of the results + // stop still active jobs + // and reset the list + abort(state); + } + else + { + state.results[key] = output; + } + + // return salvaged results + callback(error, state.results); + }); +} + +/** + * Runs iterator over provided job element + * + * @param {function} iterator - iterator to invoke + * @param {string|number} key - key/index of the element in the list of jobs + * @param {mixed} item - job description + * @param {function} callback - invoked after iterator is done with the job + * @returns {function|mixed} - job abort function or something else + */ +function runJob(iterator, key, item, callback) +{ + var aborter; + + // allow shortcut if iterator expects only two arguments + if (iterator.length == 2) + { + aborter = iterator(item, async(callback)); + } + // otherwise go with full three arguments + else + { + aborter = iterator(item, key, async(callback)); + } + + return aborter; +} + + +/***/ }), + +/***/ 162: +/***/ (function(module) { + +module.exports = {"$id":"content.json#","$schema":"http://json-schema.org/draft-06/schema#","type":"object","required":["size","mimeType"],"properties":{"size":{"type":"integer"},"compression":{"type":"integer"},"mimeType":{"type":"string"},"text":{"type":"string"},"encoding":{"type":"string"},"comment":{"type":"string"}}}; + +/***/ }), + +/***/ 181: +/***/ (function(module) { + +module.exports = {"$id":"pageTimings.json#","$schema":"http://json-schema.org/draft-06/schema#","type":"object","properties":{"onContentLoad":{"type":"number","min":-1},"onLoad":{"type":"number","min":-1},"comment":{"type":"string"}}}; + +/***/ }), + +/***/ 191: +/***/ (function(module) { + +module.exports = require("querystring"); + +/***/ }), + +/***/ 196: +/***/ (function(module, __unusedexports, __webpack_require__) { + +(function(nacl) { +'use strict'; + +// Ported in 2014 by Dmitry Chestnykh and Devi Mandiri. +// Public domain. +// +// Implementation derived from TweetNaCl version 20140427. +// See for details: http://tweetnacl.cr.yp.to/ + +var gf = function(init) { + var i, r = new Float64Array(16); + if (init) for (i = 0; i < init.length; i++) r[i] = init[i]; + return r; +}; + +// Pluggable, initialized in high-level API below. +var randombytes = function(/* x, n */) { throw new Error('no PRNG'); }; + +var _0 = new Uint8Array(16); +var _9 = new Uint8Array(32); _9[0] = 9; + +var gf0 = gf(), + gf1 = gf([1]), + _121665 = gf([0xdb41, 1]), + D = gf([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203]), + D2 = gf([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406]), + X = gf([0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169]), + Y = gf([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666]), + I = gf([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83]); + +function ts64(x, i, h, l) { + x[i] = (h >> 24) & 0xff; + x[i+1] = (h >> 16) & 0xff; + x[i+2] = (h >> 8) & 0xff; + x[i+3] = h & 0xff; + x[i+4] = (l >> 24) & 0xff; + x[i+5] = (l >> 16) & 0xff; + x[i+6] = (l >> 8) & 0xff; + x[i+7] = l & 0xff; +} + +function vn(x, xi, y, yi, n) { + var i,d = 0; + for (i = 0; i < n; i++) d |= x[xi+i]^y[yi+i]; + return (1 & ((d - 1) >>> 8)) - 1; +} + +function crypto_verify_16(x, xi, y, yi) { + return vn(x,xi,y,yi,16); +} + +function crypto_verify_32(x, xi, y, yi) { + return vn(x,xi,y,yi,32); +} + +function core_salsa20(o, p, k, c) { + var j0 = c[ 0] & 0xff | (c[ 1] & 0xff)<<8 | (c[ 2] & 0xff)<<16 | (c[ 3] & 0xff)<<24, + j1 = k[ 0] & 0xff | (k[ 1] & 0xff)<<8 | (k[ 2] & 0xff)<<16 | (k[ 3] & 0xff)<<24, + j2 = k[ 4] & 0xff | (k[ 5] & 0xff)<<8 | (k[ 6] & 0xff)<<16 | (k[ 7] & 0xff)<<24, + j3 = k[ 8] & 0xff | (k[ 9] & 0xff)<<8 | (k[10] & 0xff)<<16 | (k[11] & 0xff)<<24, + j4 = k[12] & 0xff | (k[13] & 0xff)<<8 | (k[14] & 0xff)<<16 | (k[15] & 0xff)<<24, + j5 = c[ 4] & 0xff | (c[ 5] & 0xff)<<8 | (c[ 6] & 0xff)<<16 | (c[ 7] & 0xff)<<24, + j6 = p[ 0] & 0xff | (p[ 1] & 0xff)<<8 | (p[ 2] & 0xff)<<16 | (p[ 3] & 0xff)<<24, + j7 = p[ 4] & 0xff | (p[ 5] & 0xff)<<8 | (p[ 6] & 0xff)<<16 | (p[ 7] & 0xff)<<24, + j8 = p[ 8] & 0xff | (p[ 9] & 0xff)<<8 | (p[10] & 0xff)<<16 | (p[11] & 0xff)<<24, + j9 = p[12] & 0xff | (p[13] & 0xff)<<8 | (p[14] & 0xff)<<16 | (p[15] & 0xff)<<24, + j10 = c[ 8] & 0xff | (c[ 9] & 0xff)<<8 | (c[10] & 0xff)<<16 | (c[11] & 0xff)<<24, + j11 = k[16] & 0xff | (k[17] & 0xff)<<8 | (k[18] & 0xff)<<16 | (k[19] & 0xff)<<24, + j12 = k[20] & 0xff | (k[21] & 0xff)<<8 | (k[22] & 0xff)<<16 | (k[23] & 0xff)<<24, + j13 = k[24] & 0xff | (k[25] & 0xff)<<8 | (k[26] & 0xff)<<16 | (k[27] & 0xff)<<24, + j14 = k[28] & 0xff | (k[29] & 0xff)<<8 | (k[30] & 0xff)<<16 | (k[31] & 0xff)<<24, + j15 = c[12] & 0xff | (c[13] & 0xff)<<8 | (c[14] & 0xff)<<16 | (c[15] & 0xff)<<24; + + var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7, + x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, + x15 = j15, u; + + for (var i = 0; i < 20; i += 2) { + u = x0 + x12 | 0; + x4 ^= u<<7 | u>>>(32-7); + u = x4 + x0 | 0; + x8 ^= u<<9 | u>>>(32-9); + u = x8 + x4 | 0; + x12 ^= u<<13 | u>>>(32-13); + u = x12 + x8 | 0; + x0 ^= u<<18 | u>>>(32-18); + + u = x5 + x1 | 0; + x9 ^= u<<7 | u>>>(32-7); + u = x9 + x5 | 0; + x13 ^= u<<9 | u>>>(32-9); + u = x13 + x9 | 0; + x1 ^= u<<13 | u>>>(32-13); + u = x1 + x13 | 0; + x5 ^= u<<18 | u>>>(32-18); + + u = x10 + x6 | 0; + x14 ^= u<<7 | u>>>(32-7); + u = x14 + x10 | 0; + x2 ^= u<<9 | u>>>(32-9); + u = x2 + x14 | 0; + x6 ^= u<<13 | u>>>(32-13); + u = x6 + x2 | 0; + x10 ^= u<<18 | u>>>(32-18); + + u = x15 + x11 | 0; + x3 ^= u<<7 | u>>>(32-7); + u = x3 + x15 | 0; + x7 ^= u<<9 | u>>>(32-9); + u = x7 + x3 | 0; + x11 ^= u<<13 | u>>>(32-13); + u = x11 + x7 | 0; + x15 ^= u<<18 | u>>>(32-18); + + u = x0 + x3 | 0; + x1 ^= u<<7 | u>>>(32-7); + u = x1 + x0 | 0; + x2 ^= u<<9 | u>>>(32-9); + u = x2 + x1 | 0; + x3 ^= u<<13 | u>>>(32-13); + u = x3 + x2 | 0; + x0 ^= u<<18 | u>>>(32-18); + + u = x5 + x4 | 0; + x6 ^= u<<7 | u>>>(32-7); + u = x6 + x5 | 0; + x7 ^= u<<9 | u>>>(32-9); + u = x7 + x6 | 0; + x4 ^= u<<13 | u>>>(32-13); + u = x4 + x7 | 0; + x5 ^= u<<18 | u>>>(32-18); + + u = x10 + x9 | 0; + x11 ^= u<<7 | u>>>(32-7); + u = x11 + x10 | 0; + x8 ^= u<<9 | u>>>(32-9); + u = x8 + x11 | 0; + x9 ^= u<<13 | u>>>(32-13); + u = x9 + x8 | 0; + x10 ^= u<<18 | u>>>(32-18); + + u = x15 + x14 | 0; + x12 ^= u<<7 | u>>>(32-7); + u = x12 + x15 | 0; + x13 ^= u<<9 | u>>>(32-9); + u = x13 + x12 | 0; + x14 ^= u<<13 | u>>>(32-13); + u = x14 + x13 | 0; + x15 ^= u<<18 | u>>>(32-18); + } + x0 = x0 + j0 | 0; + x1 = x1 + j1 | 0; + x2 = x2 + j2 | 0; + x3 = x3 + j3 | 0; + x4 = x4 + j4 | 0; + x5 = x5 + j5 | 0; + x6 = x6 + j6 | 0; + x7 = x7 + j7 | 0; + x8 = x8 + j8 | 0; + x9 = x9 + j9 | 0; + x10 = x10 + j10 | 0; + x11 = x11 + j11 | 0; + x12 = x12 + j12 | 0; + x13 = x13 + j13 | 0; + x14 = x14 + j14 | 0; + x15 = x15 + j15 | 0; + + o[ 0] = x0 >>> 0 & 0xff; + o[ 1] = x0 >>> 8 & 0xff; + o[ 2] = x0 >>> 16 & 0xff; + o[ 3] = x0 >>> 24 & 0xff; + + o[ 4] = x1 >>> 0 & 0xff; + o[ 5] = x1 >>> 8 & 0xff; + o[ 6] = x1 >>> 16 & 0xff; + o[ 7] = x1 >>> 24 & 0xff; + + o[ 8] = x2 >>> 0 & 0xff; + o[ 9] = x2 >>> 8 & 0xff; + o[10] = x2 >>> 16 & 0xff; + o[11] = x2 >>> 24 & 0xff; + + o[12] = x3 >>> 0 & 0xff; + o[13] = x3 >>> 8 & 0xff; + o[14] = x3 >>> 16 & 0xff; + o[15] = x3 >>> 24 & 0xff; + + o[16] = x4 >>> 0 & 0xff; + o[17] = x4 >>> 8 & 0xff; + o[18] = x4 >>> 16 & 0xff; + o[19] = x4 >>> 24 & 0xff; + + o[20] = x5 >>> 0 & 0xff; + o[21] = x5 >>> 8 & 0xff; + o[22] = x5 >>> 16 & 0xff; + o[23] = x5 >>> 24 & 0xff; + + o[24] = x6 >>> 0 & 0xff; + o[25] = x6 >>> 8 & 0xff; + o[26] = x6 >>> 16 & 0xff; + o[27] = x6 >>> 24 & 0xff; + + o[28] = x7 >>> 0 & 0xff; + o[29] = x7 >>> 8 & 0xff; + o[30] = x7 >>> 16 & 0xff; + o[31] = x7 >>> 24 & 0xff; + + o[32] = x8 >>> 0 & 0xff; + o[33] = x8 >>> 8 & 0xff; + o[34] = x8 >>> 16 & 0xff; + o[35] = x8 >>> 24 & 0xff; + + o[36] = x9 >>> 0 & 0xff; + o[37] = x9 >>> 8 & 0xff; + o[38] = x9 >>> 16 & 0xff; + o[39] = x9 >>> 24 & 0xff; + + o[40] = x10 >>> 0 & 0xff; + o[41] = x10 >>> 8 & 0xff; + o[42] = x10 >>> 16 & 0xff; + o[43] = x10 >>> 24 & 0xff; + + o[44] = x11 >>> 0 & 0xff; + o[45] = x11 >>> 8 & 0xff; + o[46] = x11 >>> 16 & 0xff; + o[47] = x11 >>> 24 & 0xff; + + o[48] = x12 >>> 0 & 0xff; + o[49] = x12 >>> 8 & 0xff; + o[50] = x12 >>> 16 & 0xff; + o[51] = x12 >>> 24 & 0xff; + + o[52] = x13 >>> 0 & 0xff; + o[53] = x13 >>> 8 & 0xff; + o[54] = x13 >>> 16 & 0xff; + o[55] = x13 >>> 24 & 0xff; + + o[56] = x14 >>> 0 & 0xff; + o[57] = x14 >>> 8 & 0xff; + o[58] = x14 >>> 16 & 0xff; + o[59] = x14 >>> 24 & 0xff; + + o[60] = x15 >>> 0 & 0xff; + o[61] = x15 >>> 8 & 0xff; + o[62] = x15 >>> 16 & 0xff; + o[63] = x15 >>> 24 & 0xff; +} + +function core_hsalsa20(o,p,k,c) { + var j0 = c[ 0] & 0xff | (c[ 1] & 0xff)<<8 | (c[ 2] & 0xff)<<16 | (c[ 3] & 0xff)<<24, + j1 = k[ 0] & 0xff | (k[ 1] & 0xff)<<8 | (k[ 2] & 0xff)<<16 | (k[ 3] & 0xff)<<24, + j2 = k[ 4] & 0xff | (k[ 5] & 0xff)<<8 | (k[ 6] & 0xff)<<16 | (k[ 7] & 0xff)<<24, + j3 = k[ 8] & 0xff | (k[ 9] & 0xff)<<8 | (k[10] & 0xff)<<16 | (k[11] & 0xff)<<24, + j4 = k[12] & 0xff | (k[13] & 0xff)<<8 | (k[14] & 0xff)<<16 | (k[15] & 0xff)<<24, + j5 = c[ 4] & 0xff | (c[ 5] & 0xff)<<8 | (c[ 6] & 0xff)<<16 | (c[ 7] & 0xff)<<24, + j6 = p[ 0] & 0xff | (p[ 1] & 0xff)<<8 | (p[ 2] & 0xff)<<16 | (p[ 3] & 0xff)<<24, + j7 = p[ 4] & 0xff | (p[ 5] & 0xff)<<8 | (p[ 6] & 0xff)<<16 | (p[ 7] & 0xff)<<24, + j8 = p[ 8] & 0xff | (p[ 9] & 0xff)<<8 | (p[10] & 0xff)<<16 | (p[11] & 0xff)<<24, + j9 = p[12] & 0xff | (p[13] & 0xff)<<8 | (p[14] & 0xff)<<16 | (p[15] & 0xff)<<24, + j10 = c[ 8] & 0xff | (c[ 9] & 0xff)<<8 | (c[10] & 0xff)<<16 | (c[11] & 0xff)<<24, + j11 = k[16] & 0xff | (k[17] & 0xff)<<8 | (k[18] & 0xff)<<16 | (k[19] & 0xff)<<24, + j12 = k[20] & 0xff | (k[21] & 0xff)<<8 | (k[22] & 0xff)<<16 | (k[23] & 0xff)<<24, + j13 = k[24] & 0xff | (k[25] & 0xff)<<8 | (k[26] & 0xff)<<16 | (k[27] & 0xff)<<24, + j14 = k[28] & 0xff | (k[29] & 0xff)<<8 | (k[30] & 0xff)<<16 | (k[31] & 0xff)<<24, + j15 = c[12] & 0xff | (c[13] & 0xff)<<8 | (c[14] & 0xff)<<16 | (c[15] & 0xff)<<24; + + var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7, + x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, + x15 = j15, u; + + for (var i = 0; i < 20; i += 2) { + u = x0 + x12 | 0; + x4 ^= u<<7 | u>>>(32-7); + u = x4 + x0 | 0; + x8 ^= u<<9 | u>>>(32-9); + u = x8 + x4 | 0; + x12 ^= u<<13 | u>>>(32-13); + u = x12 + x8 | 0; + x0 ^= u<<18 | u>>>(32-18); + + u = x5 + x1 | 0; + x9 ^= u<<7 | u>>>(32-7); + u = x9 + x5 | 0; + x13 ^= u<<9 | u>>>(32-9); + u = x13 + x9 | 0; + x1 ^= u<<13 | u>>>(32-13); + u = x1 + x13 | 0; + x5 ^= u<<18 | u>>>(32-18); + + u = x10 + x6 | 0; + x14 ^= u<<7 | u>>>(32-7); + u = x14 + x10 | 0; + x2 ^= u<<9 | u>>>(32-9); + u = x2 + x14 | 0; + x6 ^= u<<13 | u>>>(32-13); + u = x6 + x2 | 0; + x10 ^= u<<18 | u>>>(32-18); + + u = x15 + x11 | 0; + x3 ^= u<<7 | u>>>(32-7); + u = x3 + x15 | 0; + x7 ^= u<<9 | u>>>(32-9); + u = x7 + x3 | 0; + x11 ^= u<<13 | u>>>(32-13); + u = x11 + x7 | 0; + x15 ^= u<<18 | u>>>(32-18); + + u = x0 + x3 | 0; + x1 ^= u<<7 | u>>>(32-7); + u = x1 + x0 | 0; + x2 ^= u<<9 | u>>>(32-9); + u = x2 + x1 | 0; + x3 ^= u<<13 | u>>>(32-13); + u = x3 + x2 | 0; + x0 ^= u<<18 | u>>>(32-18); + + u = x5 + x4 | 0; + x6 ^= u<<7 | u>>>(32-7); + u = x6 + x5 | 0; + x7 ^= u<<9 | u>>>(32-9); + u = x7 + x6 | 0; + x4 ^= u<<13 | u>>>(32-13); + u = x4 + x7 | 0; + x5 ^= u<<18 | u>>>(32-18); + + u = x10 + x9 | 0; + x11 ^= u<<7 | u>>>(32-7); + u = x11 + x10 | 0; + x8 ^= u<<9 | u>>>(32-9); + u = x8 + x11 | 0; + x9 ^= u<<13 | u>>>(32-13); + u = x9 + x8 | 0; + x10 ^= u<<18 | u>>>(32-18); + + u = x15 + x14 | 0; + x12 ^= u<<7 | u>>>(32-7); + u = x12 + x15 | 0; + x13 ^= u<<9 | u>>>(32-9); + u = x13 + x12 | 0; + x14 ^= u<<13 | u>>>(32-13); + u = x14 + x13 | 0; + x15 ^= u<<18 | u>>>(32-18); + } + + o[ 0] = x0 >>> 0 & 0xff; + o[ 1] = x0 >>> 8 & 0xff; + o[ 2] = x0 >>> 16 & 0xff; + o[ 3] = x0 >>> 24 & 0xff; + + o[ 4] = x5 >>> 0 & 0xff; + o[ 5] = x5 >>> 8 & 0xff; + o[ 6] = x5 >>> 16 & 0xff; + o[ 7] = x5 >>> 24 & 0xff; + + o[ 8] = x10 >>> 0 & 0xff; + o[ 9] = x10 >>> 8 & 0xff; + o[10] = x10 >>> 16 & 0xff; + o[11] = x10 >>> 24 & 0xff; + + o[12] = x15 >>> 0 & 0xff; + o[13] = x15 >>> 8 & 0xff; + o[14] = x15 >>> 16 & 0xff; + o[15] = x15 >>> 24 & 0xff; + + o[16] = x6 >>> 0 & 0xff; + o[17] = x6 >>> 8 & 0xff; + o[18] = x6 >>> 16 & 0xff; + o[19] = x6 >>> 24 & 0xff; + + o[20] = x7 >>> 0 & 0xff; + o[21] = x7 >>> 8 & 0xff; + o[22] = x7 >>> 16 & 0xff; + o[23] = x7 >>> 24 & 0xff; + + o[24] = x8 >>> 0 & 0xff; + o[25] = x8 >>> 8 & 0xff; + o[26] = x8 >>> 16 & 0xff; + o[27] = x8 >>> 24 & 0xff; + + o[28] = x9 >>> 0 & 0xff; + o[29] = x9 >>> 8 & 0xff; + o[30] = x9 >>> 16 & 0xff; + o[31] = x9 >>> 24 & 0xff; +} + +function crypto_core_salsa20(out,inp,k,c) { + core_salsa20(out,inp,k,c); +} + +function crypto_core_hsalsa20(out,inp,k,c) { + core_hsalsa20(out,inp,k,c); +} + +var sigma = new Uint8Array([101, 120, 112, 97, 110, 100, 32, 51, 50, 45, 98, 121, 116, 101, 32, 107]); + // "expand 32-byte k" + +function crypto_stream_salsa20_xor(c,cpos,m,mpos,b,n,k) { + var z = new Uint8Array(16), x = new Uint8Array(64); + var u, i; + for (i = 0; i < 16; i++) z[i] = 0; + for (i = 0; i < 8; i++) z[i] = n[i]; + while (b >= 64) { + crypto_core_salsa20(x,z,k,sigma); + for (i = 0; i < 64; i++) c[cpos+i] = m[mpos+i] ^ x[i]; + u = 1; + for (i = 8; i < 16; i++) { + u = u + (z[i] & 0xff) | 0; + z[i] = u & 0xff; + u >>>= 8; + } + b -= 64; + cpos += 64; + mpos += 64; + } + if (b > 0) { + crypto_core_salsa20(x,z,k,sigma); + for (i = 0; i < b; i++) c[cpos+i] = m[mpos+i] ^ x[i]; + } + return 0; +} + +function crypto_stream_salsa20(c,cpos,b,n,k) { + var z = new Uint8Array(16), x = new Uint8Array(64); + var u, i; + for (i = 0; i < 16; i++) z[i] = 0; + for (i = 0; i < 8; i++) z[i] = n[i]; + while (b >= 64) { + crypto_core_salsa20(x,z,k,sigma); + for (i = 0; i < 64; i++) c[cpos+i] = x[i]; + u = 1; + for (i = 8; i < 16; i++) { + u = u + (z[i] & 0xff) | 0; + z[i] = u & 0xff; + u >>>= 8; + } + b -= 64; + cpos += 64; + } + if (b > 0) { + crypto_core_salsa20(x,z,k,sigma); + for (i = 0; i < b; i++) c[cpos+i] = x[i]; + } + return 0; +} + +function crypto_stream(c,cpos,d,n,k) { + var s = new Uint8Array(32); + crypto_core_hsalsa20(s,n,k,sigma); + var sn = new Uint8Array(8); + for (var i = 0; i < 8; i++) sn[i] = n[i+16]; + return crypto_stream_salsa20(c,cpos,d,sn,s); +} + +function crypto_stream_xor(c,cpos,m,mpos,d,n,k) { + var s = new Uint8Array(32); + crypto_core_hsalsa20(s,n,k,sigma); + var sn = new Uint8Array(8); + for (var i = 0; i < 8; i++) sn[i] = n[i+16]; + return crypto_stream_salsa20_xor(c,cpos,m,mpos,d,sn,s); +} + +/* +* Port of Andrew Moon's Poly1305-donna-16. Public domain. +* https://github.com/floodyberry/poly1305-donna +*/ + +var poly1305 = function(key) { + this.buffer = new Uint8Array(16); + this.r = new Uint16Array(10); + this.h = new Uint16Array(10); + this.pad = new Uint16Array(8); + this.leftover = 0; + this.fin = 0; + + var t0, t1, t2, t3, t4, t5, t6, t7; + + t0 = key[ 0] & 0xff | (key[ 1] & 0xff) << 8; this.r[0] = ( t0 ) & 0x1fff; + t1 = key[ 2] & 0xff | (key[ 3] & 0xff) << 8; this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff; + t2 = key[ 4] & 0xff | (key[ 5] & 0xff) << 8; this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03; + t3 = key[ 6] & 0xff | (key[ 7] & 0xff) << 8; this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff; + t4 = key[ 8] & 0xff | (key[ 9] & 0xff) << 8; this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff; + this.r[5] = ((t4 >>> 1)) & 0x1ffe; + t5 = key[10] & 0xff | (key[11] & 0xff) << 8; this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff; + t6 = key[12] & 0xff | (key[13] & 0xff) << 8; this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81; + t7 = key[14] & 0xff | (key[15] & 0xff) << 8; this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff; + this.r[9] = ((t7 >>> 5)) & 0x007f; + + this.pad[0] = key[16] & 0xff | (key[17] & 0xff) << 8; + this.pad[1] = key[18] & 0xff | (key[19] & 0xff) << 8; + this.pad[2] = key[20] & 0xff | (key[21] & 0xff) << 8; + this.pad[3] = key[22] & 0xff | (key[23] & 0xff) << 8; + this.pad[4] = key[24] & 0xff | (key[25] & 0xff) << 8; + this.pad[5] = key[26] & 0xff | (key[27] & 0xff) << 8; + this.pad[6] = key[28] & 0xff | (key[29] & 0xff) << 8; + this.pad[7] = key[30] & 0xff | (key[31] & 0xff) << 8; +}; + +poly1305.prototype.blocks = function(m, mpos, bytes) { + var hibit = this.fin ? 0 : (1 << 11); + var t0, t1, t2, t3, t4, t5, t6, t7, c; + var d0, d1, d2, d3, d4, d5, d6, d7, d8, d9; + + var h0 = this.h[0], + h1 = this.h[1], + h2 = this.h[2], + h3 = this.h[3], + h4 = this.h[4], + h5 = this.h[5], + h6 = this.h[6], + h7 = this.h[7], + h8 = this.h[8], + h9 = this.h[9]; + + var r0 = this.r[0], + r1 = this.r[1], + r2 = this.r[2], + r3 = this.r[3], + r4 = this.r[4], + r5 = this.r[5], + r6 = this.r[6], + r7 = this.r[7], + r8 = this.r[8], + r9 = this.r[9]; + + while (bytes >= 16) { + t0 = m[mpos+ 0] & 0xff | (m[mpos+ 1] & 0xff) << 8; h0 += ( t0 ) & 0x1fff; + t1 = m[mpos+ 2] & 0xff | (m[mpos+ 3] & 0xff) << 8; h1 += ((t0 >>> 13) | (t1 << 3)) & 0x1fff; + t2 = m[mpos+ 4] & 0xff | (m[mpos+ 5] & 0xff) << 8; h2 += ((t1 >>> 10) | (t2 << 6)) & 0x1fff; + t3 = m[mpos+ 6] & 0xff | (m[mpos+ 7] & 0xff) << 8; h3 += ((t2 >>> 7) | (t3 << 9)) & 0x1fff; + t4 = m[mpos+ 8] & 0xff | (m[mpos+ 9] & 0xff) << 8; h4 += ((t3 >>> 4) | (t4 << 12)) & 0x1fff; + h5 += ((t4 >>> 1)) & 0x1fff; + t5 = m[mpos+10] & 0xff | (m[mpos+11] & 0xff) << 8; h6 += ((t4 >>> 14) | (t5 << 2)) & 0x1fff; + t6 = m[mpos+12] & 0xff | (m[mpos+13] & 0xff) << 8; h7 += ((t5 >>> 11) | (t6 << 5)) & 0x1fff; + t7 = m[mpos+14] & 0xff | (m[mpos+15] & 0xff) << 8; h8 += ((t6 >>> 8) | (t7 << 8)) & 0x1fff; + h9 += ((t7 >>> 5)) | hibit; + + c = 0; + + d0 = c; + d0 += h0 * r0; + d0 += h1 * (5 * r9); + d0 += h2 * (5 * r8); + d0 += h3 * (5 * r7); + d0 += h4 * (5 * r6); + c = (d0 >>> 13); d0 &= 0x1fff; + d0 += h5 * (5 * r5); + d0 += h6 * (5 * r4); + d0 += h7 * (5 * r3); + d0 += h8 * (5 * r2); + d0 += h9 * (5 * r1); + c += (d0 >>> 13); d0 &= 0x1fff; + + d1 = c; + d1 += h0 * r1; + d1 += h1 * r0; + d1 += h2 * (5 * r9); + d1 += h3 * (5 * r8); + d1 += h4 * (5 * r7); + c = (d1 >>> 13); d1 &= 0x1fff; + d1 += h5 * (5 * r6); + d1 += h6 * (5 * r5); + d1 += h7 * (5 * r4); + d1 += h8 * (5 * r3); + d1 += h9 * (5 * r2); + c += (d1 >>> 13); d1 &= 0x1fff; + + d2 = c; + d2 += h0 * r2; + d2 += h1 * r1; + d2 += h2 * r0; + d2 += h3 * (5 * r9); + d2 += h4 * (5 * r8); + c = (d2 >>> 13); d2 &= 0x1fff; + d2 += h5 * (5 * r7); + d2 += h6 * (5 * r6); + d2 += h7 * (5 * r5); + d2 += h8 * (5 * r4); + d2 += h9 * (5 * r3); + c += (d2 >>> 13); d2 &= 0x1fff; + + d3 = c; + d3 += h0 * r3; + d3 += h1 * r2; + d3 += h2 * r1; + d3 += h3 * r0; + d3 += h4 * (5 * r9); + c = (d3 >>> 13); d3 &= 0x1fff; + d3 += h5 * (5 * r8); + d3 += h6 * (5 * r7); + d3 += h7 * (5 * r6); + d3 += h8 * (5 * r5); + d3 += h9 * (5 * r4); + c += (d3 >>> 13); d3 &= 0x1fff; + + d4 = c; + d4 += h0 * r4; + d4 += h1 * r3; + d4 += h2 * r2; + d4 += h3 * r1; + d4 += h4 * r0; + c = (d4 >>> 13); d4 &= 0x1fff; + d4 += h5 * (5 * r9); + d4 += h6 * (5 * r8); + d4 += h7 * (5 * r7); + d4 += h8 * (5 * r6); + d4 += h9 * (5 * r5); + c += (d4 >>> 13); d4 &= 0x1fff; + + d5 = c; + d5 += h0 * r5; + d5 += h1 * r4; + d5 += h2 * r3; + d5 += h3 * r2; + d5 += h4 * r1; + c = (d5 >>> 13); d5 &= 0x1fff; + d5 += h5 * r0; + d5 += h6 * (5 * r9); + d5 += h7 * (5 * r8); + d5 += h8 * (5 * r7); + d5 += h9 * (5 * r6); + c += (d5 >>> 13); d5 &= 0x1fff; + + d6 = c; + d6 += h0 * r6; + d6 += h1 * r5; + d6 += h2 * r4; + d6 += h3 * r3; + d6 += h4 * r2; + c = (d6 >>> 13); d6 &= 0x1fff; + d6 += h5 * r1; + d6 += h6 * r0; + d6 += h7 * (5 * r9); + d6 += h8 * (5 * r8); + d6 += h9 * (5 * r7); + c += (d6 >>> 13); d6 &= 0x1fff; + + d7 = c; + d7 += h0 * r7; + d7 += h1 * r6; + d7 += h2 * r5; + d7 += h3 * r4; + d7 += h4 * r3; + c = (d7 >>> 13); d7 &= 0x1fff; + d7 += h5 * r2; + d7 += h6 * r1; + d7 += h7 * r0; + d7 += h8 * (5 * r9); + d7 += h9 * (5 * r8); + c += (d7 >>> 13); d7 &= 0x1fff; + + d8 = c; + d8 += h0 * r8; + d8 += h1 * r7; + d8 += h2 * r6; + d8 += h3 * r5; + d8 += h4 * r4; + c = (d8 >>> 13); d8 &= 0x1fff; + d8 += h5 * r3; + d8 += h6 * r2; + d8 += h7 * r1; + d8 += h8 * r0; + d8 += h9 * (5 * r9); + c += (d8 >>> 13); d8 &= 0x1fff; + + d9 = c; + d9 += h0 * r9; + d9 += h1 * r8; + d9 += h2 * r7; + d9 += h3 * r6; + d9 += h4 * r5; + c = (d9 >>> 13); d9 &= 0x1fff; + d9 += h5 * r4; + d9 += h6 * r3; + d9 += h7 * r2; + d9 += h8 * r1; + d9 += h9 * r0; + c += (d9 >>> 13); d9 &= 0x1fff; + + c = (((c << 2) + c)) | 0; + c = (c + d0) | 0; + d0 = c & 0x1fff; + c = (c >>> 13); + d1 += c; + + h0 = d0; + h1 = d1; + h2 = d2; + h3 = d3; + h4 = d4; + h5 = d5; + h6 = d6; + h7 = d7; + h8 = d8; + h9 = d9; + + mpos += 16; + bytes -= 16; + } + this.h[0] = h0; + this.h[1] = h1; + this.h[2] = h2; + this.h[3] = h3; + this.h[4] = h4; + this.h[5] = h5; + this.h[6] = h6; + this.h[7] = h7; + this.h[8] = h8; + this.h[9] = h9; +}; + +poly1305.prototype.finish = function(mac, macpos) { + var g = new Uint16Array(10); + var c, mask, f, i; + + if (this.leftover) { + i = this.leftover; + this.buffer[i++] = 1; + for (; i < 16; i++) this.buffer[i] = 0; + this.fin = 1; + this.blocks(this.buffer, 0, 16); + } + + c = this.h[1] >>> 13; + this.h[1] &= 0x1fff; + for (i = 2; i < 10; i++) { + this.h[i] += c; + c = this.h[i] >>> 13; + this.h[i] &= 0x1fff; + } + this.h[0] += (c * 5); + c = this.h[0] >>> 13; + this.h[0] &= 0x1fff; + this.h[1] += c; + c = this.h[1] >>> 13; + this.h[1] &= 0x1fff; + this.h[2] += c; + + g[0] = this.h[0] + 5; + c = g[0] >>> 13; + g[0] &= 0x1fff; + for (i = 1; i < 10; i++) { + g[i] = this.h[i] + c; + c = g[i] >>> 13; + g[i] &= 0x1fff; + } + g[9] -= (1 << 13); + + mask = (c ^ 1) - 1; + for (i = 0; i < 10; i++) g[i] &= mask; + mask = ~mask; + for (i = 0; i < 10; i++) this.h[i] = (this.h[i] & mask) | g[i]; + + this.h[0] = ((this.h[0] ) | (this.h[1] << 13) ) & 0xffff; + this.h[1] = ((this.h[1] >>> 3) | (this.h[2] << 10) ) & 0xffff; + this.h[2] = ((this.h[2] >>> 6) | (this.h[3] << 7) ) & 0xffff; + this.h[3] = ((this.h[3] >>> 9) | (this.h[4] << 4) ) & 0xffff; + this.h[4] = ((this.h[4] >>> 12) | (this.h[5] << 1) | (this.h[6] << 14)) & 0xffff; + this.h[5] = ((this.h[6] >>> 2) | (this.h[7] << 11) ) & 0xffff; + this.h[6] = ((this.h[7] >>> 5) | (this.h[8] << 8) ) & 0xffff; + this.h[7] = ((this.h[8] >>> 8) | (this.h[9] << 5) ) & 0xffff; + + f = this.h[0] + this.pad[0]; + this.h[0] = f & 0xffff; + for (i = 1; i < 8; i++) { + f = (((this.h[i] + this.pad[i]) | 0) + (f >>> 16)) | 0; + this.h[i] = f & 0xffff; + } + + mac[macpos+ 0] = (this.h[0] >>> 0) & 0xff; + mac[macpos+ 1] = (this.h[0] >>> 8) & 0xff; + mac[macpos+ 2] = (this.h[1] >>> 0) & 0xff; + mac[macpos+ 3] = (this.h[1] >>> 8) & 0xff; + mac[macpos+ 4] = (this.h[2] >>> 0) & 0xff; + mac[macpos+ 5] = (this.h[2] >>> 8) & 0xff; + mac[macpos+ 6] = (this.h[3] >>> 0) & 0xff; + mac[macpos+ 7] = (this.h[3] >>> 8) & 0xff; + mac[macpos+ 8] = (this.h[4] >>> 0) & 0xff; + mac[macpos+ 9] = (this.h[4] >>> 8) & 0xff; + mac[macpos+10] = (this.h[5] >>> 0) & 0xff; + mac[macpos+11] = (this.h[5] >>> 8) & 0xff; + mac[macpos+12] = (this.h[6] >>> 0) & 0xff; + mac[macpos+13] = (this.h[6] >>> 8) & 0xff; + mac[macpos+14] = (this.h[7] >>> 0) & 0xff; + mac[macpos+15] = (this.h[7] >>> 8) & 0xff; +}; + +poly1305.prototype.update = function(m, mpos, bytes) { + var i, want; + + if (this.leftover) { + want = (16 - this.leftover); + if (want > bytes) + want = bytes; + for (i = 0; i < want; i++) + this.buffer[this.leftover + i] = m[mpos+i]; + bytes -= want; + mpos += want; + this.leftover += want; + if (this.leftover < 16) + return; + this.blocks(this.buffer, 0, 16); + this.leftover = 0; + } + + if (bytes >= 16) { + want = bytes - (bytes % 16); + this.blocks(m, mpos, want); + mpos += want; + bytes -= want; + } + + if (bytes) { + for (i = 0; i < bytes; i++) + this.buffer[this.leftover + i] = m[mpos+i]; + this.leftover += bytes; + } +}; + +function crypto_onetimeauth(out, outpos, m, mpos, n, k) { + var s = new poly1305(k); + s.update(m, mpos, n); + s.finish(out, outpos); + return 0; +} + +function crypto_onetimeauth_verify(h, hpos, m, mpos, n, k) { + var x = new Uint8Array(16); + crypto_onetimeauth(x,0,m,mpos,n,k); + return crypto_verify_16(h,hpos,x,0); +} + +function crypto_secretbox(c,m,d,n,k) { + var i; + if (d < 32) return -1; + crypto_stream_xor(c,0,m,0,d,n,k); + crypto_onetimeauth(c, 16, c, 32, d - 32, c); + for (i = 0; i < 16; i++) c[i] = 0; + return 0; +} + +function crypto_secretbox_open(m,c,d,n,k) { + var i; + var x = new Uint8Array(32); + if (d < 32) return -1; + crypto_stream(x,0,32,n,k); + if (crypto_onetimeauth_verify(c, 16,c, 32,d - 32,x) !== 0) return -1; + crypto_stream_xor(m,0,c,0,d,n,k); + for (i = 0; i < 32; i++) m[i] = 0; + return 0; +} + +function set25519(r, a) { + var i; + for (i = 0; i < 16; i++) r[i] = a[i]|0; +} + +function car25519(o) { + var i, v, c = 1; + for (i = 0; i < 16; i++) { + v = o[i] + c + 65535; + c = Math.floor(v / 65536); + o[i] = v - c * 65536; + } + o[0] += c-1 + 37 * (c-1); +} + +function sel25519(p, q, b) { + var t, c = ~(b-1); + for (var i = 0; i < 16; i++) { + t = c & (p[i] ^ q[i]); + p[i] ^= t; + q[i] ^= t; + } +} + +function pack25519(o, n) { + var i, j, b; + var m = gf(), t = gf(); + for (i = 0; i < 16; i++) t[i] = n[i]; + car25519(t); + car25519(t); + car25519(t); + for (j = 0; j < 2; j++) { + m[0] = t[0] - 0xffed; + for (i = 1; i < 15; i++) { + m[i] = t[i] - 0xffff - ((m[i-1]>>16) & 1); + m[i-1] &= 0xffff; + } + m[15] = t[15] - 0x7fff - ((m[14]>>16) & 1); + b = (m[15]>>16) & 1; + m[14] &= 0xffff; + sel25519(t, m, 1-b); + } + for (i = 0; i < 16; i++) { + o[2*i] = t[i] & 0xff; + o[2*i+1] = t[i]>>8; + } +} + +function neq25519(a, b) { + var c = new Uint8Array(32), d = new Uint8Array(32); + pack25519(c, a); + pack25519(d, b); + return crypto_verify_32(c, 0, d, 0); +} + +function par25519(a) { + var d = new Uint8Array(32); + pack25519(d, a); + return d[0] & 1; +} + +function unpack25519(o, n) { + var i; + for (i = 0; i < 16; i++) o[i] = n[2*i] + (n[2*i+1] << 8); + o[15] &= 0x7fff; +} + +function A(o, a, b) { + for (var i = 0; i < 16; i++) o[i] = a[i] + b[i]; +} + +function Z(o, a, b) { + for (var i = 0; i < 16; i++) o[i] = a[i] - b[i]; +} + +function M(o, a, b) { + var v, c, + t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, + t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0, + t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0, + t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0, + b0 = b[0], + b1 = b[1], + b2 = b[2], + b3 = b[3], + b4 = b[4], + b5 = b[5], + b6 = b[6], + b7 = b[7], + b8 = b[8], + b9 = b[9], + b10 = b[10], + b11 = b[11], + b12 = b[12], + b13 = b[13], + b14 = b[14], + b15 = b[15]; + + v = a[0]; + t0 += v * b0; + t1 += v * b1; + t2 += v * b2; + t3 += v * b3; + t4 += v * b4; + t5 += v * b5; + t6 += v * b6; + t7 += v * b7; + t8 += v * b8; + t9 += v * b9; + t10 += v * b10; + t11 += v * b11; + t12 += v * b12; + t13 += v * b13; + t14 += v * b14; + t15 += v * b15; + v = a[1]; + t1 += v * b0; + t2 += v * b1; + t3 += v * b2; + t4 += v * b3; + t5 += v * b4; + t6 += v * b5; + t7 += v * b6; + t8 += v * b7; + t9 += v * b8; + t10 += v * b9; + t11 += v * b10; + t12 += v * b11; + t13 += v * b12; + t14 += v * b13; + t15 += v * b14; + t16 += v * b15; + v = a[2]; + t2 += v * b0; + t3 += v * b1; + t4 += v * b2; + t5 += v * b3; + t6 += v * b4; + t7 += v * b5; + t8 += v * b6; + t9 += v * b7; + t10 += v * b8; + t11 += v * b9; + t12 += v * b10; + t13 += v * b11; + t14 += v * b12; + t15 += v * b13; + t16 += v * b14; + t17 += v * b15; + v = a[3]; + t3 += v * b0; + t4 += v * b1; + t5 += v * b2; + t6 += v * b3; + t7 += v * b4; + t8 += v * b5; + t9 += v * b6; + t10 += v * b7; + t11 += v * b8; + t12 += v * b9; + t13 += v * b10; + t14 += v * b11; + t15 += v * b12; + t16 += v * b13; + t17 += v * b14; + t18 += v * b15; + v = a[4]; + t4 += v * b0; + t5 += v * b1; + t6 += v * b2; + t7 += v * b3; + t8 += v * b4; + t9 += v * b5; + t10 += v * b6; + t11 += v * b7; + t12 += v * b8; + t13 += v * b9; + t14 += v * b10; + t15 += v * b11; + t16 += v * b12; + t17 += v * b13; + t18 += v * b14; + t19 += v * b15; + v = a[5]; + t5 += v * b0; + t6 += v * b1; + t7 += v * b2; + t8 += v * b3; + t9 += v * b4; + t10 += v * b5; + t11 += v * b6; + t12 += v * b7; + t13 += v * b8; + t14 += v * b9; + t15 += v * b10; + t16 += v * b11; + t17 += v * b12; + t18 += v * b13; + t19 += v * b14; + t20 += v * b15; + v = a[6]; + t6 += v * b0; + t7 += v * b1; + t8 += v * b2; + t9 += v * b3; + t10 += v * b4; + t11 += v * b5; + t12 += v * b6; + t13 += v * b7; + t14 += v * b8; + t15 += v * b9; + t16 += v * b10; + t17 += v * b11; + t18 += v * b12; + t19 += v * b13; + t20 += v * b14; + t21 += v * b15; + v = a[7]; + t7 += v * b0; + t8 += v * b1; + t9 += v * b2; + t10 += v * b3; + t11 += v * b4; + t12 += v * b5; + t13 += v * b6; + t14 += v * b7; + t15 += v * b8; + t16 += v * b9; + t17 += v * b10; + t18 += v * b11; + t19 += v * b12; + t20 += v * b13; + t21 += v * b14; + t22 += v * b15; + v = a[8]; + t8 += v * b0; + t9 += v * b1; + t10 += v * b2; + t11 += v * b3; + t12 += v * b4; + t13 += v * b5; + t14 += v * b6; + t15 += v * b7; + t16 += v * b8; + t17 += v * b9; + t18 += v * b10; + t19 += v * b11; + t20 += v * b12; + t21 += v * b13; + t22 += v * b14; + t23 += v * b15; + v = a[9]; + t9 += v * b0; + t10 += v * b1; + t11 += v * b2; + t12 += v * b3; + t13 += v * b4; + t14 += v * b5; + t15 += v * b6; + t16 += v * b7; + t17 += v * b8; + t18 += v * b9; + t19 += v * b10; + t20 += v * b11; + t21 += v * b12; + t22 += v * b13; + t23 += v * b14; + t24 += v * b15; + v = a[10]; + t10 += v * b0; + t11 += v * b1; + t12 += v * b2; + t13 += v * b3; + t14 += v * b4; + t15 += v * b5; + t16 += v * b6; + t17 += v * b7; + t18 += v * b8; + t19 += v * b9; + t20 += v * b10; + t21 += v * b11; + t22 += v * b12; + t23 += v * b13; + t24 += v * b14; + t25 += v * b15; + v = a[11]; + t11 += v * b0; + t12 += v * b1; + t13 += v * b2; + t14 += v * b3; + t15 += v * b4; + t16 += v * b5; + t17 += v * b6; + t18 += v * b7; + t19 += v * b8; + t20 += v * b9; + t21 += v * b10; + t22 += v * b11; + t23 += v * b12; + t24 += v * b13; + t25 += v * b14; + t26 += v * b15; + v = a[12]; + t12 += v * b0; + t13 += v * b1; + t14 += v * b2; + t15 += v * b3; + t16 += v * b4; + t17 += v * b5; + t18 += v * b6; + t19 += v * b7; + t20 += v * b8; + t21 += v * b9; + t22 += v * b10; + t23 += v * b11; + t24 += v * b12; + t25 += v * b13; + t26 += v * b14; + t27 += v * b15; + v = a[13]; + t13 += v * b0; + t14 += v * b1; + t15 += v * b2; + t16 += v * b3; + t17 += v * b4; + t18 += v * b5; + t19 += v * b6; + t20 += v * b7; + t21 += v * b8; + t22 += v * b9; + t23 += v * b10; + t24 += v * b11; + t25 += v * b12; + t26 += v * b13; + t27 += v * b14; + t28 += v * b15; + v = a[14]; + t14 += v * b0; + t15 += v * b1; + t16 += v * b2; + t17 += v * b3; + t18 += v * b4; + t19 += v * b5; + t20 += v * b6; + t21 += v * b7; + t22 += v * b8; + t23 += v * b9; + t24 += v * b10; + t25 += v * b11; + t26 += v * b12; + t27 += v * b13; + t28 += v * b14; + t29 += v * b15; + v = a[15]; + t15 += v * b0; + t16 += v * b1; + t17 += v * b2; + t18 += v * b3; + t19 += v * b4; + t20 += v * b5; + t21 += v * b6; + t22 += v * b7; + t23 += v * b8; + t24 += v * b9; + t25 += v * b10; + t26 += v * b11; + t27 += v * b12; + t28 += v * b13; + t29 += v * b14; + t30 += v * b15; + + t0 += 38 * t16; + t1 += 38 * t17; + t2 += 38 * t18; + t3 += 38 * t19; + t4 += 38 * t20; + t5 += 38 * t21; + t6 += 38 * t22; + t7 += 38 * t23; + t8 += 38 * t24; + t9 += 38 * t25; + t10 += 38 * t26; + t11 += 38 * t27; + t12 += 38 * t28; + t13 += 38 * t29; + t14 += 38 * t30; + // t15 left as is + + // first car + c = 1; + v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536; + v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536; + v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536; + v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536; + v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536; + v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536; + v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536; + v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536; + v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536; + v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536; + v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536; + v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536; + v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536; + v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536; + v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536; + v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536; + t0 += c-1 + 37 * (c-1); + + // second car + c = 1; + v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536; + v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536; + v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536; + v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536; + v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536; + v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536; + v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536; + v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536; + v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536; + v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536; + v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536; + v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536; + v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536; + v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536; + v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536; + v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536; + t0 += c-1 + 37 * (c-1); + + o[ 0] = t0; + o[ 1] = t1; + o[ 2] = t2; + o[ 3] = t3; + o[ 4] = t4; + o[ 5] = t5; + o[ 6] = t6; + o[ 7] = t7; + o[ 8] = t8; + o[ 9] = t9; + o[10] = t10; + o[11] = t11; + o[12] = t12; + o[13] = t13; + o[14] = t14; + o[15] = t15; +} + +function S(o, a) { + M(o, a, a); +} + +function inv25519(o, i) { + var c = gf(); + var a; + for (a = 0; a < 16; a++) c[a] = i[a]; + for (a = 253; a >= 0; a--) { + S(c, c); + if(a !== 2 && a !== 4) M(c, c, i); + } + for (a = 0; a < 16; a++) o[a] = c[a]; +} + +function pow2523(o, i) { + var c = gf(); + var a; + for (a = 0; a < 16; a++) c[a] = i[a]; + for (a = 250; a >= 0; a--) { + S(c, c); + if(a !== 1) M(c, c, i); + } + for (a = 0; a < 16; a++) o[a] = c[a]; +} + +function crypto_scalarmult(q, n, p) { + var z = new Uint8Array(32); + var x = new Float64Array(80), r, i; + var a = gf(), b = gf(), c = gf(), + d = gf(), e = gf(), f = gf(); + for (i = 0; i < 31; i++) z[i] = n[i]; + z[31]=(n[31]&127)|64; + z[0]&=248; + unpack25519(x,p); + for (i = 0; i < 16; i++) { + b[i]=x[i]; + d[i]=a[i]=c[i]=0; + } + a[0]=d[0]=1; + for (i=254; i>=0; --i) { + r=(z[i>>>3]>>>(i&7))&1; + sel25519(a,b,r); + sel25519(c,d,r); + A(e,a,c); + Z(a,a,c); + A(c,b,d); + Z(b,b,d); + S(d,e); + S(f,a); + M(a,c,a); + M(c,b,e); + A(e,a,c); + Z(a,a,c); + S(b,a); + Z(c,d,f); + M(a,c,_121665); + A(a,a,d); + M(c,c,a); + M(a,d,f); + M(d,b,x); + S(b,e); + sel25519(a,b,r); + sel25519(c,d,r); + } + for (i = 0; i < 16; i++) { + x[i+16]=a[i]; + x[i+32]=c[i]; + x[i+48]=b[i]; + x[i+64]=d[i]; + } + var x32 = x.subarray(32); + var x16 = x.subarray(16); + inv25519(x32,x32); + M(x16,x16,x32); + pack25519(q,x16); + return 0; +} + +function crypto_scalarmult_base(q, n) { + return crypto_scalarmult(q, n, _9); +} + +function crypto_box_keypair(y, x) { + randombytes(x, 32); + return crypto_scalarmult_base(y, x); +} + +function crypto_box_beforenm(k, y, x) { + var s = new Uint8Array(32); + crypto_scalarmult(s, x, y); + return crypto_core_hsalsa20(k, _0, s, sigma); +} + +var crypto_box_afternm = crypto_secretbox; +var crypto_box_open_afternm = crypto_secretbox_open; + +function crypto_box(c, m, d, n, y, x) { + var k = new Uint8Array(32); + crypto_box_beforenm(k, y, x); + return crypto_box_afternm(c, m, d, n, k); +} + +function crypto_box_open(m, c, d, n, y, x) { + var k = new Uint8Array(32); + crypto_box_beforenm(k, y, x); + return crypto_box_open_afternm(m, c, d, n, k); +} + +var K = [ + 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, + 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc, + 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, + 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118, + 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, + 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2, + 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, + 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694, + 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, + 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65, + 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, + 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5, + 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, + 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4, + 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, + 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70, + 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, + 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df, + 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, + 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b, + 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, + 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30, + 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, + 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8, + 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, + 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8, + 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, + 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3, + 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, + 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec, + 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, + 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b, + 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, + 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178, + 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, + 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b, + 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, + 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c, + 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, + 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817 +]; + +function crypto_hashblocks_hl(hh, hl, m, n) { + var wh = new Int32Array(16), wl = new Int32Array(16), + bh0, bh1, bh2, bh3, bh4, bh5, bh6, bh7, + bl0, bl1, bl2, bl3, bl4, bl5, bl6, bl7, + th, tl, i, j, h, l, a, b, c, d; + + var ah0 = hh[0], + ah1 = hh[1], + ah2 = hh[2], + ah3 = hh[3], + ah4 = hh[4], + ah5 = hh[5], + ah6 = hh[6], + ah7 = hh[7], + + al0 = hl[0], + al1 = hl[1], + al2 = hl[2], + al3 = hl[3], + al4 = hl[4], + al5 = hl[5], + al6 = hl[6], + al7 = hl[7]; + + var pos = 0; + while (n >= 128) { + for (i = 0; i < 16; i++) { + j = 8 * i + pos; + wh[i] = (m[j+0] << 24) | (m[j+1] << 16) | (m[j+2] << 8) | m[j+3]; + wl[i] = (m[j+4] << 24) | (m[j+5] << 16) | (m[j+6] << 8) | m[j+7]; + } + for (i = 0; i < 80; i++) { + bh0 = ah0; + bh1 = ah1; + bh2 = ah2; + bh3 = ah3; + bh4 = ah4; + bh5 = ah5; + bh6 = ah6; + bh7 = ah7; + + bl0 = al0; + bl1 = al1; + bl2 = al2; + bl3 = al3; + bl4 = al4; + bl5 = al5; + bl6 = al6; + bl7 = al7; + + // add + h = ah7; + l = al7; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + // Sigma1 + h = ((ah4 >>> 14) | (al4 << (32-14))) ^ ((ah4 >>> 18) | (al4 << (32-18))) ^ ((al4 >>> (41-32)) | (ah4 << (32-(41-32)))); + l = ((al4 >>> 14) | (ah4 << (32-14))) ^ ((al4 >>> 18) | (ah4 << (32-18))) ^ ((ah4 >>> (41-32)) | (al4 << (32-(41-32)))); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // Ch + h = (ah4 & ah5) ^ (~ah4 & ah6); + l = (al4 & al5) ^ (~al4 & al6); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // K + h = K[i*2]; + l = K[i*2+1]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // w + h = wh[i%16]; + l = wl[i%16]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + th = c & 0xffff | d << 16; + tl = a & 0xffff | b << 16; + + // add + h = th; + l = tl; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + // Sigma0 + h = ((ah0 >>> 28) | (al0 << (32-28))) ^ ((al0 >>> (34-32)) | (ah0 << (32-(34-32)))) ^ ((al0 >>> (39-32)) | (ah0 << (32-(39-32)))); + l = ((al0 >>> 28) | (ah0 << (32-28))) ^ ((ah0 >>> (34-32)) | (al0 << (32-(34-32)))) ^ ((ah0 >>> (39-32)) | (al0 << (32-(39-32)))); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // Maj + h = (ah0 & ah1) ^ (ah0 & ah2) ^ (ah1 & ah2); + l = (al0 & al1) ^ (al0 & al2) ^ (al1 & al2); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + bh7 = (c & 0xffff) | (d << 16); + bl7 = (a & 0xffff) | (b << 16); + + // add + h = bh3; + l = bl3; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = th; + l = tl; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + bh3 = (c & 0xffff) | (d << 16); + bl3 = (a & 0xffff) | (b << 16); + + ah1 = bh0; + ah2 = bh1; + ah3 = bh2; + ah4 = bh3; + ah5 = bh4; + ah6 = bh5; + ah7 = bh6; + ah0 = bh7; + + al1 = bl0; + al2 = bl1; + al3 = bl2; + al4 = bl3; + al5 = bl4; + al6 = bl5; + al7 = bl6; + al0 = bl7; + + if (i%16 === 15) { + for (j = 0; j < 16; j++) { + // add + h = wh[j]; + l = wl[j]; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = wh[(j+9)%16]; + l = wl[(j+9)%16]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // sigma0 + th = wh[(j+1)%16]; + tl = wl[(j+1)%16]; + h = ((th >>> 1) | (tl << (32-1))) ^ ((th >>> 8) | (tl << (32-8))) ^ (th >>> 7); + l = ((tl >>> 1) | (th << (32-1))) ^ ((tl >>> 8) | (th << (32-8))) ^ ((tl >>> 7) | (th << (32-7))); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + // sigma1 + th = wh[(j+14)%16]; + tl = wl[(j+14)%16]; + h = ((th >>> 19) | (tl << (32-19))) ^ ((tl >>> (61-32)) | (th << (32-(61-32)))) ^ (th >>> 6); + l = ((tl >>> 19) | (th << (32-19))) ^ ((th >>> (61-32)) | (tl << (32-(61-32)))) ^ ((tl >>> 6) | (th << (32-6))); + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + wh[j] = (c & 0xffff) | (d << 16); + wl[j] = (a & 0xffff) | (b << 16); + } + } + } + + // add + h = ah0; + l = al0; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[0]; + l = hl[0]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[0] = ah0 = (c & 0xffff) | (d << 16); + hl[0] = al0 = (a & 0xffff) | (b << 16); + + h = ah1; + l = al1; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[1]; + l = hl[1]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[1] = ah1 = (c & 0xffff) | (d << 16); + hl[1] = al1 = (a & 0xffff) | (b << 16); + + h = ah2; + l = al2; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[2]; + l = hl[2]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[2] = ah2 = (c & 0xffff) | (d << 16); + hl[2] = al2 = (a & 0xffff) | (b << 16); + + h = ah3; + l = al3; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[3]; + l = hl[3]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[3] = ah3 = (c & 0xffff) | (d << 16); + hl[3] = al3 = (a & 0xffff) | (b << 16); + + h = ah4; + l = al4; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[4]; + l = hl[4]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[4] = ah4 = (c & 0xffff) | (d << 16); + hl[4] = al4 = (a & 0xffff) | (b << 16); + + h = ah5; + l = al5; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[5]; + l = hl[5]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[5] = ah5 = (c & 0xffff) | (d << 16); + hl[5] = al5 = (a & 0xffff) | (b << 16); + + h = ah6; + l = al6; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[6]; + l = hl[6]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[6] = ah6 = (c & 0xffff) | (d << 16); + hl[6] = al6 = (a & 0xffff) | (b << 16); + + h = ah7; + l = al7; + + a = l & 0xffff; b = l >>> 16; + c = h & 0xffff; d = h >>> 16; + + h = hh[7]; + l = hl[7]; + + a += l & 0xffff; b += l >>> 16; + c += h & 0xffff; d += h >>> 16; + + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + + hh[7] = ah7 = (c & 0xffff) | (d << 16); + hl[7] = al7 = (a & 0xffff) | (b << 16); + + pos += 128; + n -= 128; + } + + return n; +} + +function crypto_hash(out, m, n) { + var hh = new Int32Array(8), + hl = new Int32Array(8), + x = new Uint8Array(256), + i, b = n; + + hh[0] = 0x6a09e667; + hh[1] = 0xbb67ae85; + hh[2] = 0x3c6ef372; + hh[3] = 0xa54ff53a; + hh[4] = 0x510e527f; + hh[5] = 0x9b05688c; + hh[6] = 0x1f83d9ab; + hh[7] = 0x5be0cd19; + + hl[0] = 0xf3bcc908; + hl[1] = 0x84caa73b; + hl[2] = 0xfe94f82b; + hl[3] = 0x5f1d36f1; + hl[4] = 0xade682d1; + hl[5] = 0x2b3e6c1f; + hl[6] = 0xfb41bd6b; + hl[7] = 0x137e2179; + + crypto_hashblocks_hl(hh, hl, m, n); + n %= 128; + + for (i = 0; i < n; i++) x[i] = m[b-n+i]; + x[n] = 128; + + n = 256-128*(n<112?1:0); + x[n-9] = 0; + ts64(x, n-8, (b / 0x20000000) | 0, b << 3); + crypto_hashblocks_hl(hh, hl, x, n); + + for (i = 0; i < 8; i++) ts64(out, 8*i, hh[i], hl[i]); + + return 0; +} + +function add(p, q) { + var a = gf(), b = gf(), c = gf(), + d = gf(), e = gf(), f = gf(), + g = gf(), h = gf(), t = gf(); + + Z(a, p[1], p[0]); + Z(t, q[1], q[0]); + M(a, a, t); + A(b, p[0], p[1]); + A(t, q[0], q[1]); + M(b, b, t); + M(c, p[3], q[3]); + M(c, c, D2); + M(d, p[2], q[2]); + A(d, d, d); + Z(e, b, a); + Z(f, d, c); + A(g, d, c); + A(h, b, a); + + M(p[0], e, f); + M(p[1], h, g); + M(p[2], g, f); + M(p[3], e, h); +} + +function cswap(p, q, b) { + var i; + for (i = 0; i < 4; i++) { + sel25519(p[i], q[i], b); + } +} + +function pack(r, p) { + var tx = gf(), ty = gf(), zi = gf(); + inv25519(zi, p[2]); + M(tx, p[0], zi); + M(ty, p[1], zi); + pack25519(r, ty); + r[31] ^= par25519(tx) << 7; +} + +function scalarmult(p, q, s) { + var b, i; + set25519(p[0], gf0); + set25519(p[1], gf1); + set25519(p[2], gf1); + set25519(p[3], gf0); + for (i = 255; i >= 0; --i) { + b = (s[(i/8)|0] >> (i&7)) & 1; + cswap(p, q, b); + add(q, p); + add(p, p); + cswap(p, q, b); + } +} + +function scalarbase(p, s) { + var q = [gf(), gf(), gf(), gf()]; + set25519(q[0], X); + set25519(q[1], Y); + set25519(q[2], gf1); + M(q[3], X, Y); + scalarmult(p, q, s); +} + +function crypto_sign_keypair(pk, sk, seeded) { + var d = new Uint8Array(64); + var p = [gf(), gf(), gf(), gf()]; + var i; + + if (!seeded) randombytes(sk, 32); + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + + scalarbase(p, d); + pack(pk, p); + + for (i = 0; i < 32; i++) sk[i+32] = pk[i]; + return 0; +} + +var L = new Float64Array([0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10]); + +function modL(r, x) { + var carry, i, j, k; + for (i = 63; i >= 32; --i) { + carry = 0; + for (j = i - 32, k = i - 12; j < k; ++j) { + x[j] += carry - 16 * x[i] * L[j - (i - 32)]; + carry = (x[j] + 128) >> 8; + x[j] -= carry * 256; + } + x[j] += carry; + x[i] = 0; + } + carry = 0; + for (j = 0; j < 32; j++) { + x[j] += carry - (x[31] >> 4) * L[j]; + carry = x[j] >> 8; + x[j] &= 255; + } + for (j = 0; j < 32; j++) x[j] -= carry * L[j]; + for (i = 0; i < 32; i++) { + x[i+1] += x[i] >> 8; + r[i] = x[i] & 255; + } +} + +function reduce(r) { + var x = new Float64Array(64), i; + for (i = 0; i < 64; i++) x[i] = r[i]; + for (i = 0; i < 64; i++) r[i] = 0; + modL(r, x); +} + +// Note: difference from C - smlen returned, not passed as argument. +function crypto_sign(sm, m, n, sk) { + var d = new Uint8Array(64), h = new Uint8Array(64), r = new Uint8Array(64); + var i, j, x = new Float64Array(64); + var p = [gf(), gf(), gf(), gf()]; + + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + + var smlen = n + 64; + for (i = 0; i < n; i++) sm[64 + i] = m[i]; + for (i = 0; i < 32; i++) sm[32 + i] = d[32 + i]; + + crypto_hash(r, sm.subarray(32), n+32); + reduce(r); + scalarbase(p, r); + pack(sm, p); + + for (i = 32; i < 64; i++) sm[i] = sk[i]; + crypto_hash(h, sm, n + 64); + reduce(h); + + for (i = 0; i < 64; i++) x[i] = 0; + for (i = 0; i < 32; i++) x[i] = r[i]; + for (i = 0; i < 32; i++) { + for (j = 0; j < 32; j++) { + x[i+j] += h[i] * d[j]; + } + } + + modL(sm.subarray(32), x); + return smlen; +} + +function unpackneg(r, p) { + var t = gf(), chk = gf(), num = gf(), + den = gf(), den2 = gf(), den4 = gf(), + den6 = gf(); + + set25519(r[2], gf1); + unpack25519(r[1], p); + S(num, r[1]); + M(den, num, D); + Z(num, num, r[2]); + A(den, r[2], den); + + S(den2, den); + S(den4, den2); + M(den6, den4, den2); + M(t, den6, num); + M(t, t, den); + + pow2523(t, t); + M(t, t, num); + M(t, t, den); + M(t, t, den); + M(r[0], t, den); + + S(chk, r[0]); + M(chk, chk, den); + if (neq25519(chk, num)) M(r[0], r[0], I); + + S(chk, r[0]); + M(chk, chk, den); + if (neq25519(chk, num)) return -1; + + if (par25519(r[0]) === (p[31]>>7)) Z(r[0], gf0, r[0]); + + M(r[3], r[0], r[1]); + return 0; +} + +function crypto_sign_open(m, sm, n, pk) { + var i, mlen; + var t = new Uint8Array(32), h = new Uint8Array(64); + var p = [gf(), gf(), gf(), gf()], + q = [gf(), gf(), gf(), gf()]; + + mlen = -1; + if (n < 64) return -1; + + if (unpackneg(q, pk)) return -1; + + for (i = 0; i < n; i++) m[i] = sm[i]; + for (i = 0; i < 32; i++) m[i+32] = pk[i]; + crypto_hash(h, m, n); + reduce(h); + scalarmult(p, q, h); + + scalarbase(q, sm.subarray(32)); + add(p, q); + pack(t, p); + + n -= 64; + if (crypto_verify_32(sm, 0, t, 0)) { + for (i = 0; i < n; i++) m[i] = 0; + return -1; + } + + for (i = 0; i < n; i++) m[i] = sm[i + 64]; + mlen = n; + return mlen; +} + +var crypto_secretbox_KEYBYTES = 32, + crypto_secretbox_NONCEBYTES = 24, + crypto_secretbox_ZEROBYTES = 32, + crypto_secretbox_BOXZEROBYTES = 16, + crypto_scalarmult_BYTES = 32, + crypto_scalarmult_SCALARBYTES = 32, + crypto_box_PUBLICKEYBYTES = 32, + crypto_box_SECRETKEYBYTES = 32, + crypto_box_BEFORENMBYTES = 32, + crypto_box_NONCEBYTES = crypto_secretbox_NONCEBYTES, + crypto_box_ZEROBYTES = crypto_secretbox_ZEROBYTES, + crypto_box_BOXZEROBYTES = crypto_secretbox_BOXZEROBYTES, + crypto_sign_BYTES = 64, + crypto_sign_PUBLICKEYBYTES = 32, + crypto_sign_SECRETKEYBYTES = 64, + crypto_sign_SEEDBYTES = 32, + crypto_hash_BYTES = 64; + +nacl.lowlevel = { + crypto_core_hsalsa20: crypto_core_hsalsa20, + crypto_stream_xor: crypto_stream_xor, + crypto_stream: crypto_stream, + crypto_stream_salsa20_xor: crypto_stream_salsa20_xor, + crypto_stream_salsa20: crypto_stream_salsa20, + crypto_onetimeauth: crypto_onetimeauth, + crypto_onetimeauth_verify: crypto_onetimeauth_verify, + crypto_verify_16: crypto_verify_16, + crypto_verify_32: crypto_verify_32, + crypto_secretbox: crypto_secretbox, + crypto_secretbox_open: crypto_secretbox_open, + crypto_scalarmult: crypto_scalarmult, + crypto_scalarmult_base: crypto_scalarmult_base, + crypto_box_beforenm: crypto_box_beforenm, + crypto_box_afternm: crypto_box_afternm, + crypto_box: crypto_box, + crypto_box_open: crypto_box_open, + crypto_box_keypair: crypto_box_keypair, + crypto_hash: crypto_hash, + crypto_sign: crypto_sign, + crypto_sign_keypair: crypto_sign_keypair, + crypto_sign_open: crypto_sign_open, + + crypto_secretbox_KEYBYTES: crypto_secretbox_KEYBYTES, + crypto_secretbox_NONCEBYTES: crypto_secretbox_NONCEBYTES, + crypto_secretbox_ZEROBYTES: crypto_secretbox_ZEROBYTES, + crypto_secretbox_BOXZEROBYTES: crypto_secretbox_BOXZEROBYTES, + crypto_scalarmult_BYTES: crypto_scalarmult_BYTES, + crypto_scalarmult_SCALARBYTES: crypto_scalarmult_SCALARBYTES, + crypto_box_PUBLICKEYBYTES: crypto_box_PUBLICKEYBYTES, + crypto_box_SECRETKEYBYTES: crypto_box_SECRETKEYBYTES, + crypto_box_BEFORENMBYTES: crypto_box_BEFORENMBYTES, + crypto_box_NONCEBYTES: crypto_box_NONCEBYTES, + crypto_box_ZEROBYTES: crypto_box_ZEROBYTES, + crypto_box_BOXZEROBYTES: crypto_box_BOXZEROBYTES, + crypto_sign_BYTES: crypto_sign_BYTES, + crypto_sign_PUBLICKEYBYTES: crypto_sign_PUBLICKEYBYTES, + crypto_sign_SECRETKEYBYTES: crypto_sign_SECRETKEYBYTES, + crypto_sign_SEEDBYTES: crypto_sign_SEEDBYTES, + crypto_hash_BYTES: crypto_hash_BYTES +}; + +/* High-level API */ + +function checkLengths(k, n) { + if (k.length !== crypto_secretbox_KEYBYTES) throw new Error('bad key size'); + if (n.length !== crypto_secretbox_NONCEBYTES) throw new Error('bad nonce size'); +} + +function checkBoxLengths(pk, sk) { + if (pk.length !== crypto_box_PUBLICKEYBYTES) throw new Error('bad public key size'); + if (sk.length !== crypto_box_SECRETKEYBYTES) throw new Error('bad secret key size'); +} + +function checkArrayTypes() { + var t, i; + for (i = 0; i < arguments.length; i++) { + if ((t = Object.prototype.toString.call(arguments[i])) !== '[object Uint8Array]') + throw new TypeError('unexpected type ' + t + ', use Uint8Array'); + } +} + +function cleanup(arr) { + for (var i = 0; i < arr.length; i++) arr[i] = 0; +} + +// TODO: Completely remove this in v0.15. +if (!nacl.util) { + nacl.util = {}; + nacl.util.decodeUTF8 = nacl.util.encodeUTF8 = nacl.util.encodeBase64 = nacl.util.decodeBase64 = function() { + throw new Error('nacl.util moved into separate package: https://github.com/dchest/tweetnacl-util-js'); + }; +} + +nacl.randomBytes = function(n) { + var b = new Uint8Array(n); + randombytes(b, n); + return b; +}; + +nacl.secretbox = function(msg, nonce, key) { + checkArrayTypes(msg, nonce, key); + checkLengths(key, nonce); + var m = new Uint8Array(crypto_secretbox_ZEROBYTES + msg.length); + var c = new Uint8Array(m.length); + for (var i = 0; i < msg.length; i++) m[i+crypto_secretbox_ZEROBYTES] = msg[i]; + crypto_secretbox(c, m, m.length, nonce, key); + return c.subarray(crypto_secretbox_BOXZEROBYTES); +}; + +nacl.secretbox.open = function(box, nonce, key) { + checkArrayTypes(box, nonce, key); + checkLengths(key, nonce); + var c = new Uint8Array(crypto_secretbox_BOXZEROBYTES + box.length); + var m = new Uint8Array(c.length); + for (var i = 0; i < box.length; i++) c[i+crypto_secretbox_BOXZEROBYTES] = box[i]; + if (c.length < 32) return false; + if (crypto_secretbox_open(m, c, c.length, nonce, key) !== 0) return false; + return m.subarray(crypto_secretbox_ZEROBYTES); +}; + +nacl.secretbox.keyLength = crypto_secretbox_KEYBYTES; +nacl.secretbox.nonceLength = crypto_secretbox_NONCEBYTES; +nacl.secretbox.overheadLength = crypto_secretbox_BOXZEROBYTES; + +nacl.scalarMult = function(n, p) { + checkArrayTypes(n, p); + if (n.length !== crypto_scalarmult_SCALARBYTES) throw new Error('bad n size'); + if (p.length !== crypto_scalarmult_BYTES) throw new Error('bad p size'); + var q = new Uint8Array(crypto_scalarmult_BYTES); + crypto_scalarmult(q, n, p); + return q; +}; + +nacl.scalarMult.base = function(n) { + checkArrayTypes(n); + if (n.length !== crypto_scalarmult_SCALARBYTES) throw new Error('bad n size'); + var q = new Uint8Array(crypto_scalarmult_BYTES); + crypto_scalarmult_base(q, n); + return q; +}; + +nacl.scalarMult.scalarLength = crypto_scalarmult_SCALARBYTES; +nacl.scalarMult.groupElementLength = crypto_scalarmult_BYTES; + +nacl.box = function(msg, nonce, publicKey, secretKey) { + var k = nacl.box.before(publicKey, secretKey); + return nacl.secretbox(msg, nonce, k); +}; + +nacl.box.before = function(publicKey, secretKey) { + checkArrayTypes(publicKey, secretKey); + checkBoxLengths(publicKey, secretKey); + var k = new Uint8Array(crypto_box_BEFORENMBYTES); + crypto_box_beforenm(k, publicKey, secretKey); + return k; +}; + +nacl.box.after = nacl.secretbox; + +nacl.box.open = function(msg, nonce, publicKey, secretKey) { + var k = nacl.box.before(publicKey, secretKey); + return nacl.secretbox.open(msg, nonce, k); +}; + +nacl.box.open.after = nacl.secretbox.open; + +nacl.box.keyPair = function() { + var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES); + var sk = new Uint8Array(crypto_box_SECRETKEYBYTES); + crypto_box_keypair(pk, sk); + return {publicKey: pk, secretKey: sk}; +}; + +nacl.box.keyPair.fromSecretKey = function(secretKey) { + checkArrayTypes(secretKey); + if (secretKey.length !== crypto_box_SECRETKEYBYTES) + throw new Error('bad secret key size'); + var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES); + crypto_scalarmult_base(pk, secretKey); + return {publicKey: pk, secretKey: new Uint8Array(secretKey)}; +}; + +nacl.box.publicKeyLength = crypto_box_PUBLICKEYBYTES; +nacl.box.secretKeyLength = crypto_box_SECRETKEYBYTES; +nacl.box.sharedKeyLength = crypto_box_BEFORENMBYTES; +nacl.box.nonceLength = crypto_box_NONCEBYTES; +nacl.box.overheadLength = nacl.secretbox.overheadLength; + +nacl.sign = function(msg, secretKey) { + checkArrayTypes(msg, secretKey); + if (secretKey.length !== crypto_sign_SECRETKEYBYTES) + throw new Error('bad secret key size'); + var signedMsg = new Uint8Array(crypto_sign_BYTES+msg.length); + crypto_sign(signedMsg, msg, msg.length, secretKey); + return signedMsg; +}; + +nacl.sign.open = function(signedMsg, publicKey) { + if (arguments.length !== 2) + throw new Error('nacl.sign.open accepts 2 arguments; did you mean to use nacl.sign.detached.verify?'); + checkArrayTypes(signedMsg, publicKey); + if (publicKey.length !== crypto_sign_PUBLICKEYBYTES) + throw new Error('bad public key size'); + var tmp = new Uint8Array(signedMsg.length); + var mlen = crypto_sign_open(tmp, signedMsg, signedMsg.length, publicKey); + if (mlen < 0) return null; + var m = new Uint8Array(mlen); + for (var i = 0; i < m.length; i++) m[i] = tmp[i]; + return m; +}; + +nacl.sign.detached = function(msg, secretKey) { + var signedMsg = nacl.sign(msg, secretKey); + var sig = new Uint8Array(crypto_sign_BYTES); + for (var i = 0; i < sig.length; i++) sig[i] = signedMsg[i]; + return sig; +}; + +nacl.sign.detached.verify = function(msg, sig, publicKey) { + checkArrayTypes(msg, sig, publicKey); + if (sig.length !== crypto_sign_BYTES) + throw new Error('bad signature size'); + if (publicKey.length !== crypto_sign_PUBLICKEYBYTES) + throw new Error('bad public key size'); + var sm = new Uint8Array(crypto_sign_BYTES + msg.length); + var m = new Uint8Array(crypto_sign_BYTES + msg.length); + var i; + for (i = 0; i < crypto_sign_BYTES; i++) sm[i] = sig[i]; + for (i = 0; i < msg.length; i++) sm[i+crypto_sign_BYTES] = msg[i]; + return (crypto_sign_open(m, sm, sm.length, publicKey) >= 0); +}; + +nacl.sign.keyPair = function() { + var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES); + crypto_sign_keypair(pk, sk); + return {publicKey: pk, secretKey: sk}; +}; + +nacl.sign.keyPair.fromSecretKey = function(secretKey) { + checkArrayTypes(secretKey); + if (secretKey.length !== crypto_sign_SECRETKEYBYTES) + throw new Error('bad secret key size'); + var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + for (var i = 0; i < pk.length; i++) pk[i] = secretKey[32+i]; + return {publicKey: pk, secretKey: new Uint8Array(secretKey)}; +}; + +nacl.sign.keyPair.fromSeed = function(seed) { + checkArrayTypes(seed); + if (seed.length !== crypto_sign_SEEDBYTES) + throw new Error('bad seed size'); + var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES); + for (var i = 0; i < 32; i++) sk[i] = seed[i]; + crypto_sign_keypair(pk, sk, true); + return {publicKey: pk, secretKey: sk}; +}; + +nacl.sign.publicKeyLength = crypto_sign_PUBLICKEYBYTES; +nacl.sign.secretKeyLength = crypto_sign_SECRETKEYBYTES; +nacl.sign.seedLength = crypto_sign_SEEDBYTES; +nacl.sign.signatureLength = crypto_sign_BYTES; + +nacl.hash = function(msg) { + checkArrayTypes(msg); + var h = new Uint8Array(crypto_hash_BYTES); + crypto_hash(h, msg, msg.length); + return h; +}; + +nacl.hash.hashLength = crypto_hash_BYTES; + +nacl.verify = function(x, y) { + checkArrayTypes(x, y); + // Zero length arguments are considered not equal. + if (x.length === 0 || y.length === 0) return false; + if (x.length !== y.length) return false; + return (vn(x, 0, y, 0, x.length) === 0) ? true : false; +}; + +nacl.setPRNG = function(fn) { + randombytes = fn; +}; + +(function() { + // Initialize PRNG if environment provides CSPRNG. + // If not, methods calling randombytes will throw. + var crypto = typeof self !== 'undefined' ? (self.crypto || self.msCrypto) : null; + if (crypto && crypto.getRandomValues) { + // Browsers. + var QUOTA = 65536; + nacl.setPRNG(function(x, n) { + var i, v = new Uint8Array(n); + for (i = 0; i < n; i += QUOTA) { + crypto.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA))); + } + for (i = 0; i < n; i++) x[i] = v[i]; + cleanup(v); + }); + } else if (true) { + // Node.js. + crypto = __webpack_require__(417); + if (crypto && crypto.randomBytes) { + nacl.setPRNG(function(x, n) { + var i, v = crypto.randomBytes(n); + for (i = 0; i < n; i++) x[i] = v[i]; + cleanup(v); + }); + } + } +})(); + +})( true && module.exports ? module.exports : (self.nacl = self.nacl || {})); + + +/***/ }), + +/***/ 203: +/***/ (function(module) { + +"use strict"; +/*! + * @description Recursive object extending + * @author Viacheslav Lotsmanov + * @license MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2018 Viacheslav Lotsmanov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + + +function isSpecificValue(val) { + return ( + val instanceof Buffer + || val instanceof Date + || val instanceof RegExp + ) ? true : false; +} + +function cloneSpecificValue(val) { + if (val instanceof Buffer) { + var x = Buffer.alloc + ? Buffer.alloc(val.length) + : new Buffer(val.length); + val.copy(x); + return x; + } else if (val instanceof Date) { + return new Date(val.getTime()); + } else if (val instanceof RegExp) { + return new RegExp(val); + } else { + throw new Error('Unexpected situation'); + } +} + +/** + * Recursive cloning array. + */ +function deepCloneArray(arr) { + var clone = []; + arr.forEach(function (item, index) { + if (typeof item === 'object' && item !== null) { + if (Array.isArray(item)) { + clone[index] = deepCloneArray(item); + } else if (isSpecificValue(item)) { + clone[index] = cloneSpecificValue(item); + } else { + clone[index] = deepExtend({}, item); + } + } else { + clone[index] = item; + } + }); + return clone; +} + +function safeGetProperty(object, property) { + return property === '__proto__' ? undefined : object[property]; +} + +/** + * Extening object that entered in first argument. + * + * Returns extended object or false if have no target object or incorrect type. + * + * If you wish to clone source object (without modify it), just use empty new + * object as first argument, like this: + * deepExtend({}, yourObj_1, [yourObj_N]); + */ +var deepExtend = module.exports = function (/*obj_1, [obj_2], [obj_N]*/) { + if (arguments.length < 1 || typeof arguments[0] !== 'object') { + return false; + } + + if (arguments.length < 2) { + return arguments[0]; + } + + var target = arguments[0]; + + // convert arguments to array and cut off target object + var args = Array.prototype.slice.call(arguments, 1); + + var val, src, clone; + + args.forEach(function (obj) { + // skip argument if isn't an object, is null, or is an array + if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) { + return; + } + + Object.keys(obj).forEach(function (key) { + src = safeGetProperty(target, key); // source value + val = safeGetProperty(obj, key); // new value + + // recursion prevention + if (val === target) { + return; + + /** + * if new value isn't object then just overwrite by new value + * instead of extending. + */ + } else if (typeof val !== 'object' || val === null) { + target[key] = val; + return; + + // just clone arrays (and recursive clone objects inside) + } else if (Array.isArray(val)) { + target[key] = deepCloneArray(val); + return; + + // custom cloning and overwrite for specific objects + } else if (isSpecificValue(val)) { + target[key] = cloneSpecificValue(val); + return; + + // overwrite by new value if source isn't object or array + } else if (typeof src !== 'object' || src === null || Array.isArray(src)) { + target[key] = deepExtend({}, val); + return; + + // source value and new value is objects both, extending... + } else { + target[key] = deepExtend(src, val); + return; + } + }); + }); + + return target; +}; + + +/***/ }), + +/***/ 211: +/***/ (function(module) { + +module.exports = require("https"); + +/***/ }), + +/***/ 213: +/***/ (function(module) { + +module.exports = require("punycode"); + +/***/ }), + +/***/ 215: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; +/* eslint-disable node/no-deprecated-api */ + + + +var buffer = __webpack_require__(293) +var Buffer = buffer.Buffer + +var safer = {} + +var key + +for (key in buffer) { + if (!buffer.hasOwnProperty(key)) continue + if (key === 'SlowBuffer' || key === 'Buffer') continue + safer[key] = buffer[key] +} + +var Safer = safer.Buffer = {} +for (key in Buffer) { + if (!Buffer.hasOwnProperty(key)) continue + if (key === 'allocUnsafe' || key === 'allocUnsafeSlow') continue + Safer[key] = Buffer[key] +} + +safer.Buffer.prototype = Buffer.prototype + +if (!Safer.from || Safer.from === Uint8Array.from) { + Safer.from = function (value, encodingOrOffset, length) { + if (typeof value === 'number') { + throw new TypeError('The "value" argument must not be of type number. Received type ' + typeof value) + } + if (value && typeof value.length === 'undefined') { + throw new TypeError('The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type ' + typeof value) + } + return Buffer(value, encodingOrOffset, length) + } +} + +if (!Safer.alloc) { + Safer.alloc = function (size, fill, encoding) { + if (typeof size !== 'number') { + throw new TypeError('The "size" argument must be of type number. Received type ' + typeof size) + } + if (size < 0 || size >= 2 * (1 << 30)) { + throw new RangeError('The value "' + size + '" is invalid for option "size"') + } + var buf = Buffer(size) + if (!fill || fill.length === 0) { + buf.fill(0) + } else if (typeof encoding === 'string') { + buf.fill(fill, encoding) + } else { + buf.fill(fill) + } + return buf + } +} + +if (!safer.kStringMaxLength) { + try { + safer.kStringMaxLength = process.binding('buffer').kStringMaxLength + } catch (e) { + // we can't determine kStringMaxLength in environments where process.binding + // is unsupported, so let's not set it + } +} + +if (!safer.constants) { + safer.constants = { + MAX_LENGTH: safer.kMaxLength + } + if (safer.kStringMaxLength) { + safer.constants.MAX_STRING_LENGTH = safer.kStringMaxLength + } +} + +module.exports = safer + + +/***/ }), + +/***/ 222: +/***/ (function(module) { + +module.exports = {"$id":"browser.json#","$schema":"http://json-schema.org/draft-06/schema#","type":"object","required":["name","version"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"comment":{"type":"string"}}}; + +/***/ }), + +/***/ 226: +/***/ (function(module) { + +module.exports = {"$id":"response.json#","$schema":"http://json-schema.org/draft-06/schema#","type":"object","required":["status","statusText","httpVersion","cookies","headers","content","redirectURL","headersSize","bodySize"],"properties":{"status":{"type":"integer"},"statusText":{"type":"string"},"httpVersion":{"type":"string"},"cookies":{"type":"array","items":{"$ref":"cookie.json#"}},"headers":{"type":"array","items":{"$ref":"header.json#"}},"content":{"$ref":"content.json#"},"redirectURL":{"type":"string"},"headersSize":{"type":"integer"},"bodySize":{"type":"integer"},"comment":{"type":"string"}}}; + +/***/ }), + +/***/ 233: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_dependencies(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + var $schemaDeps = {}, + $propertyDeps = {}, + $ownProperties = it.opts.ownProperties; + for ($property in $schema) { + var $sch = $schema[$property]; + var $deps = Array.isArray($sch) ? $propertyDeps : $schemaDeps; + $deps[$property] = $sch; + } + out += 'var ' + ($errs) + ' = errors;'; + var $currentErrorPath = it.errorPath; + out += 'var missing' + ($lvl) + ';'; + for (var $property in $propertyDeps) { + $deps = $propertyDeps[$property]; + if ($deps.length) { + out += ' if ( ' + ($data) + (it.util.getProperty($property)) + ' !== undefined '; + if ($ownProperties) { + out += ' && Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($property)) + '\') '; + } + if ($breakOnError) { + out += ' && ( '; + var arr1 = $deps; + if (arr1) { + var $propertyKey, $i = -1, + l1 = arr1.length - 1; + while ($i < l1) { + $propertyKey = arr1[$i += 1]; + if ($i) { + out += ' || '; + } + var $prop = it.util.getProperty($propertyKey), + $useData = $data + $prop; + out += ' ( ( ' + ($useData) + ' === undefined '; + if ($ownProperties) { + out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') '; + } + out += ') && (missing' + ($lvl) + ' = ' + (it.util.toQuotedString(it.opts.jsonPointers ? $propertyKey : $prop)) + ') ) '; + } + } + out += ')) { '; + var $propertyPath = 'missing' + $lvl, + $missingProperty = '\' + ' + $propertyPath + ' + \''; + if (it.opts._errorDataPathProperty) { + it.errorPath = it.opts.jsonPointers ? it.util.getPathExpr($currentErrorPath, $propertyPath, true) : $currentErrorPath + ' + ' + $propertyPath; + } + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('dependencies') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { property: \'' + (it.util.escapeQuotes($property)) + '\', missingProperty: \'' + ($missingProperty) + '\', depsCount: ' + ($deps.length) + ', deps: \'' + (it.util.escapeQuotes($deps.length == 1 ? $deps[0] : $deps.join(", "))) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should have '; + if ($deps.length == 1) { + out += 'property ' + (it.util.escapeQuotes($deps[0])); + } else { + out += 'properties ' + (it.util.escapeQuotes($deps.join(", "))); + } + out += ' when property ' + (it.util.escapeQuotes($property)) + ' is present\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + } else { + out += ' ) { '; + var arr2 = $deps; + if (arr2) { + var $propertyKey, i2 = -1, + l2 = arr2.length - 1; + while (i2 < l2) { + $propertyKey = arr2[i2 += 1]; + var $prop = it.util.getProperty($propertyKey), + $missingProperty = it.util.escapeQuotes($propertyKey), + $useData = $data + $prop; + if (it.opts._errorDataPathProperty) { + it.errorPath = it.util.getPath($currentErrorPath, $propertyKey, it.opts.jsonPointers); + } + out += ' if ( ' + ($useData) + ' === undefined '; + if ($ownProperties) { + out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') '; + } + out += ') { var err = '; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('dependencies') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { property: \'' + (it.util.escapeQuotes($property)) + '\', missingProperty: \'' + ($missingProperty) + '\', depsCount: ' + ($deps.length) + ', deps: \'' + (it.util.escapeQuotes($deps.length == 1 ? $deps[0] : $deps.join(", "))) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should have '; + if ($deps.length == 1) { + out += 'property ' + (it.util.escapeQuotes($deps[0])); + } else { + out += 'properties ' + (it.util.escapeQuotes($deps.join(", "))); + } + out += ' when property ' + (it.util.escapeQuotes($property)) + ' is present\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + out += '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } '; + } + } + } + out += ' } '; + if ($breakOnError) { + $closingBraces += '}'; + out += ' else { '; + } + } + } + it.errorPath = $currentErrorPath; + var $currentBaseId = $it.baseId; + for (var $property in $schemaDeps) { + var $sch = $schemaDeps[$property]; + if ((it.opts.strictKeywords ? typeof $sch == 'object' && Object.keys($sch).length > 0 : it.util.schemaHasRules($sch, it.RULES.all))) { + out += ' ' + ($nextValid) + ' = true; if ( ' + ($data) + (it.util.getProperty($property)) + ' !== undefined '; + if ($ownProperties) { + out += ' && Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($property)) + '\') '; + } + out += ') { '; + $it.schema = $sch; + $it.schemaPath = $schemaPath + it.util.getProperty($property); + $it.errSchemaPath = $errSchemaPath + '/' + it.util.escapeFragment($property); + out += ' ' + (it.validate($it)) + ' '; + $it.baseId = $currentBaseId; + out += ' } '; + if ($breakOnError) { + out += ' if (' + ($nextValid) + ') { '; + $closingBraces += '}'; + } + } + } + if ($breakOnError) { + out += ' ' + ($closingBraces) + ' if (' + ($errs) + ' == errors) {'; + } + out = it.util.cleanUpCode(out); + return out; +} + + +/***/ }), + +/***/ 241: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2018 Joyent, Inc. + +module.exports = { + read: read, + write: write +}; + +var assert = __webpack_require__(477); +var Buffer = __webpack_require__(215).Buffer; +var utils = __webpack_require__(270); +var Key = __webpack_require__(852); +var PrivateKey = __webpack_require__(502); + +var pem = __webpack_require__(268); +var ssh = __webpack_require__(603); +var rfc4253 = __webpack_require__(538); +var dnssec = __webpack_require__(982); +var putty = __webpack_require__(624); + +var DNSSEC_PRIVKEY_HEADER_PREFIX = 'Private-key-format: v1'; + +function read(buf, options) { + if (typeof (buf) === 'string') { + if (buf.trim().match(/^[-]+[ ]*BEGIN/)) + return (pem.read(buf, options)); + if (buf.match(/^\s*ssh-[a-z]/)) + return (ssh.read(buf, options)); + if (buf.match(/^\s*ecdsa-/)) + return (ssh.read(buf, options)); + if (buf.match(/^putty-user-key-file-2:/i)) + return (putty.read(buf, options)); + if (findDNSSECHeader(buf)) + return (dnssec.read(buf, options)); + buf = Buffer.from(buf, 'binary'); + } else { + assert.buffer(buf); + if (findPEMHeader(buf)) + return (pem.read(buf, options)); + if (findSSHHeader(buf)) + return (ssh.read(buf, options)); + if (findPuTTYHeader(buf)) + return (putty.read(buf, options)); + if (findDNSSECHeader(buf)) + return (dnssec.read(buf, options)); + } + if (buf.readUInt32BE(0) < buf.length) + return (rfc4253.read(buf, options)); + throw (new Error('Failed to auto-detect format of key')); +} + +function findPuTTYHeader(buf) { + var offset = 0; + while (offset < buf.length && + (buf[offset] === 32 || buf[offset] === 10 || buf[offset] === 9)) + ++offset; + if (offset + 22 <= buf.length && + buf.slice(offset, offset + 22).toString('ascii').toLowerCase() === + 'putty-user-key-file-2:') + return (true); + return (false); +} + +function findSSHHeader(buf) { + var offset = 0; + while (offset < buf.length && + (buf[offset] === 32 || buf[offset] === 10 || buf[offset] === 9)) + ++offset; + if (offset + 4 <= buf.length && + buf.slice(offset, offset + 4).toString('ascii') === 'ssh-') + return (true); + if (offset + 6 <= buf.length && + buf.slice(offset, offset + 6).toString('ascii') === 'ecdsa-') + return (true); + return (false); +} + +function findPEMHeader(buf) { + var offset = 0; + while (offset < buf.length && + (buf[offset] === 32 || buf[offset] === 10)) + ++offset; + if (buf[offset] !== 45) + return (false); + while (offset < buf.length && + (buf[offset] === 45)) + ++offset; + while (offset < buf.length && + (buf[offset] === 32)) + ++offset; + if (offset + 5 > buf.length || + buf.slice(offset, offset + 5).toString('ascii') !== 'BEGIN') + return (false); + return (true); +} + +function findDNSSECHeader(buf) { + // private case first + if (buf.length <= DNSSEC_PRIVKEY_HEADER_PREFIX.length) + return (false); + var headerCheck = buf.slice(0, DNSSEC_PRIVKEY_HEADER_PREFIX.length); + if (headerCheck.toString('ascii') === DNSSEC_PRIVKEY_HEADER_PREFIX) + return (true); + + // public-key RFC3110 ? + // 'domain.com. IN KEY ...' or 'domain.com. IN DNSKEY ...' + // skip any comment-lines + if (typeof (buf) !== 'string') { + buf = buf.toString('ascii'); + } + var lines = buf.split('\n'); + var line = 0; + /* JSSTYLED */ + while (lines[line].match(/^\;/)) + line++; + if (lines[line].toString('ascii').match(/\. IN KEY /)) + return (true); + if (lines[line].toString('ascii').match(/\. IN DNSKEY /)) + return (true); + return (false); +} + +function write(key, options) { + throw (new Error('"auto" format cannot be used for writing')); +} + + +/***/ }), + +/***/ 242: +/***/ (function(module, exports) { + +(function(){ + + // Copyright (c) 2005 Tom Wu + // All Rights Reserved. + // See "LICENSE" for details. + + // Basic JavaScript BN library - subset useful for RSA encryption. + + // Bits per digit + var dbits; + + // JavaScript engine analysis + var canary = 0xdeadbeefcafe; + var j_lm = ((canary&0xffffff)==0xefcafe); + + // (public) Constructor + function BigInteger(a,b,c) { + if(a != null) + if("number" == typeof a) this.fromNumber(a,b,c); + else if(b == null && "string" != typeof a) this.fromString(a,256); + else this.fromString(a,b); + } + + // return new, unset BigInteger + function nbi() { return new BigInteger(null); } + + // am: Compute w_j += (x*this_i), propagate carries, + // c is initial carry, returns final carry. + // c < 3*dvalue, x < 2*dvalue, this_i < dvalue + // We need to select the fastest one that works in this environment. + + // am1: use a single mult and divide to get the high bits, + // max digit bits should be 26 because + // max internal value = 2*dvalue^2-2*dvalue (< 2^53) + function am1(i,x,w,j,c,n) { + while(--n >= 0) { + var v = x*this[i++]+w[j]+c; + c = Math.floor(v/0x4000000); + w[j++] = v&0x3ffffff; + } + return c; + } + // am2 avoids a big mult-and-extract completely. + // Max digit bits should be <= 30 because we do bitwise ops + // on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) + function am2(i,x,w,j,c,n) { + var xl = x&0x7fff, xh = x>>15; + while(--n >= 0) { + var l = this[i]&0x7fff; + var h = this[i++]>>15; + var m = xh*l+h*xl; + l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff); + c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); + w[j++] = l&0x3fffffff; + } + return c; + } + // Alternately, set max digit bits to 28 since some + // browsers slow down when dealing with 32-bit numbers. + function am3(i,x,w,j,c,n) { + var xl = x&0x3fff, xh = x>>14; + while(--n >= 0) { + var l = this[i]&0x3fff; + var h = this[i++]>>14; + var m = xh*l+h*xl; + l = xl*l+((m&0x3fff)<<14)+w[j]+c; + c = (l>>28)+(m>>14)+xh*h; + w[j++] = l&0xfffffff; + } + return c; + } + var inBrowser = typeof navigator !== "undefined"; + if(inBrowser && j_lm && (navigator.appName == "Microsoft Internet Explorer")) { + BigInteger.prototype.am = am2; + dbits = 30; + } + else if(inBrowser && j_lm && (navigator.appName != "Netscape")) { + BigInteger.prototype.am = am1; + dbits = 26; + } + else { // Mozilla/Netscape seems to prefer am3 + BigInteger.prototype.am = am3; + dbits = 28; + } + + BigInteger.prototype.DB = dbits; + BigInteger.prototype.DM = ((1<= 0; --i) r[i] = this[i]; + r.t = this.t; + r.s = this.s; + } + + // (protected) set from integer value x, -DV <= x < DV + function bnpFromInt(x) { + this.t = 1; + this.s = (x<0)?-1:0; + if(x > 0) this[0] = x; + else if(x < -1) this[0] = x+this.DV; + else this.t = 0; + } + + // return bigint initialized to value + function nbv(i) { var r = nbi(); r.fromInt(i); return r; } + + // (protected) set from string and radix + function bnpFromString(s,b) { + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 256) k = 8; // byte array + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else { this.fromRadix(s,b); return; } + this.t = 0; + this.s = 0; + var i = s.length, mi = false, sh = 0; + while(--i >= 0) { + var x = (k==8)?s[i]&0xff:intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-") mi = true; + continue; + } + mi = false; + if(sh == 0) + this[this.t++] = x; + else if(sh+k > this.DB) { + this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<>(this.DB-sh)); + } + else + this[this.t-1] |= x<= this.DB) sh -= this.DB; + } + if(k == 8 && (s[0]&0x80) != 0) { + this.s = -1; + if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)< 0 && this[this.t-1] == c) --this.t; + } + + // (public) return string representation in given radix + function bnToString(b) { + if(this.s < 0) return "-"+this.negate().toString(b); + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else return this.toRadix(b); + var km = (1< 0) { + if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); } + while(i >= 0) { + if(p < k) { + d = (this[i]&((1<>(p+=this.DB-k); + } + else { + d = (this[i]>>(p-=k))&km; + if(p <= 0) { p += this.DB; --i; } + } + if(d > 0) m = true; + if(m) r += int2char(d); + } + } + return m?r:"0"; + } + + // (public) -this + function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } + + // (public) |this| + function bnAbs() { return (this.s<0)?this.negate():this; } + + // (public) return + if this > a, - if this < a, 0 if equal + function bnCompareTo(a) { + var r = this.s-a.s; + if(r != 0) return r; + var i = this.t; + r = i-a.t; + if(r != 0) return (this.s<0)?-r:r; + while(--i >= 0) if((r=this[i]-a[i]) != 0) return r; + return 0; + } + + // returns bit length of the integer x + function nbits(x) { + var r = 1, t; + if((t=x>>>16) != 0) { x = t; r += 16; } + if((t=x>>8) != 0) { x = t; r += 8; } + if((t=x>>4) != 0) { x = t; r += 4; } + if((t=x>>2) != 0) { x = t; r += 2; } + if((t=x>>1) != 0) { x = t; r += 1; } + return r; + } + + // (public) return the number of bits in "this" + function bnBitLength() { + if(this.t <= 0) return 0; + return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM)); + } + + // (protected) r = this << n*DB + function bnpDLShiftTo(n,r) { + var i; + for(i = this.t-1; i >= 0; --i) r[i+n] = this[i]; + for(i = n-1; i >= 0; --i) r[i] = 0; + r.t = this.t+n; + r.s = this.s; + } + + // (protected) r = this >> n*DB + function bnpDRShiftTo(n,r) { + for(var i = n; i < this.t; ++i) r[i-n] = this[i]; + r.t = Math.max(this.t-n,0); + r.s = this.s; + } + + // (protected) r = this << n + function bnpLShiftTo(n,r) { + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<= 0; --i) { + r[i+ds+1] = (this[i]>>cbs)|c; + c = (this[i]&bm)<= 0; --i) r[i] = 0; + r[ds] = c; + r.t = this.t+ds+1; + r.s = this.s; + r.clamp(); + } + + // (protected) r = this >> n + function bnpRShiftTo(n,r) { + r.s = this.s; + var ds = Math.floor(n/this.DB); + if(ds >= this.t) { r.t = 0; return; } + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<>bs; + for(var i = ds+1; i < this.t; ++i) { + r[i-ds-1] |= (this[i]&bm)<>bs; + } + if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<>= this.DB; + } + if(a.t < this.t) { + c -= a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c -= a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c -= a.s; + } + r.s = (c<0)?-1:0; + if(c < -1) r[i++] = this.DV+c; + else if(c > 0) r[i++] = c; + r.t = i; + r.clamp(); + } + + // (protected) r = this * a, r != this,a (HAC 14.12) + // "this" should be the larger one if appropriate. + function bnpMultiplyTo(a,r) { + var x = this.abs(), y = a.abs(); + var i = x.t; + r.t = i+y.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t); + r.s = 0; + r.clamp(); + if(this.s != a.s) BigInteger.ZERO.subTo(r,r); + } + + // (protected) r = this^2, r != this (HAC 14.16) + function bnpSquareTo(r) { + var x = this.abs(); + var i = r.t = 2*x.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < x.t-1; ++i) { + var c = x.am(i,x[i],r,2*i,0,1); + if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) { + r[i+x.t] -= x.DV; + r[i+x.t+1] = 1; + } + } + if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1); + r.s = 0; + r.clamp(); + } + + // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) + // r != q, this != m. q or r may be null. + function bnpDivRemTo(m,q,r) { + var pm = m.abs(); + if(pm.t <= 0) return; + var pt = this.abs(); + if(pt.t < pm.t) { + if(q != null) q.fromInt(0); + if(r != null) this.copyTo(r); + return; + } + if(r == null) r = nbi(); + var y = nbi(), ts = this.s, ms = m.s; + var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus + if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } + else { pm.copyTo(y); pt.copyTo(r); } + var ys = y.t; + var y0 = y[ys-1]; + if(y0 == 0) return; + var yt = y0*(1<1)?y[ys-2]>>this.F2:0); + var d1 = this.FV/yt, d2 = (1<= 0) { + r[r.t++] = 1; + r.subTo(t,r); + } + BigInteger.ONE.dlShiftTo(ys,t); + t.subTo(y,y); // "negative" y so we can replace sub with am later + while(y.t < ys) y[y.t++] = 0; + while(--j >= 0) { + // Estimate quotient digit + var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); + if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out + y.dlShiftTo(j,t); + r.subTo(t,r); + while(r[i] < --qd) r.subTo(t,r); + } + } + if(q != null) { + r.drShiftTo(ys,q); + if(ts != ms) BigInteger.ZERO.subTo(q,q); + } + r.t = ys; + r.clamp(); + if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder + if(ts < 0) BigInteger.ZERO.subTo(r,r); + } + + // (public) this mod a + function bnMod(a) { + var r = nbi(); + this.abs().divRemTo(a,null,r); + if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); + return r; + } + + // Modular reduction using "classic" algorithm + function Classic(m) { this.m = m; } + function cConvert(x) { + if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); + else return x; + } + function cRevert(x) { return x; } + function cReduce(x) { x.divRemTo(this.m,null,x); } + function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + + Classic.prototype.convert = cConvert; + Classic.prototype.revert = cRevert; + Classic.prototype.reduce = cReduce; + Classic.prototype.mulTo = cMulTo; + Classic.prototype.sqrTo = cSqrTo; + + // (protected) return "-1/this % 2^DB"; useful for Mont. reduction + // justification: + // xy == 1 (mod m) + // xy = 1+km + // xy(2-xy) = (1+km)(1-km) + // x[y(2-xy)] = 1-k^2m^2 + // x[y(2-xy)] == 1 (mod m^2) + // if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 + // should reduce x and y(2-xy) by m^2 at each step to keep size bounded. + // JS multiply "overflows" differently from C/C++, so care is needed here. + function bnpInvDigit() { + if(this.t < 1) return 0; + var x = this[0]; + if((x&1) == 0) return 0; + var y = x&3; // y == 1/x mod 2^2 + y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 + y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 + y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 + // last step - calculate inverse mod DV directly; + // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints + y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits + // we really want the negative inverse, and -DV < y < DV + return (y>0)?this.DV-y:-y; + } + + // Montgomery reduction + function Montgomery(m) { + this.m = m; + this.mp = m.invDigit(); + this.mpl = this.mp&0x7fff; + this.mph = this.mp>>15; + this.um = (1<<(m.DB-15))-1; + this.mt2 = 2*m.t; + } + + // xR mod m + function montConvert(x) { + var r = nbi(); + x.abs().dlShiftTo(this.m.t,r); + r.divRemTo(this.m,null,r); + if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); + return r; + } + + // x/R mod m + function montRevert(x) { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; + } + + // x = x/R mod m (HAC 14.32) + function montReduce(x) { + while(x.t <= this.mt2) // pad x so am has enough room later + x[x.t++] = 0; + for(var i = 0; i < this.m.t; ++i) { + // faster way of calculating u0 = x[i]*mp mod DV + var j = x[i]&0x7fff; + var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM; + // use am to combine the multiply-shift-add into one call + j = i+this.m.t; + x[j] += this.m.am(0,u0,x,i,0,this.m.t); + // propagate carry + while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; } + } + x.clamp(); + x.drShiftTo(this.m.t,x); + if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); + } + + // r = "x^2/R mod m"; x != r + function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + + // r = "xy/R mod m"; x,y != r + function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + + Montgomery.prototype.convert = montConvert; + Montgomery.prototype.revert = montRevert; + Montgomery.prototype.reduce = montReduce; + Montgomery.prototype.mulTo = montMulTo; + Montgomery.prototype.sqrTo = montSqrTo; + + // (protected) true iff this is even + function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; } + + // (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) + function bnpExp(e,z) { + if(e > 0xffffffff || e < 1) return BigInteger.ONE; + var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; + g.copyTo(r); + while(--i >= 0) { + z.sqrTo(r,r2); + if((e&(1< 0) z.mulTo(r2,g,r); + else { var t = r; r = r2; r2 = t; } + } + return z.revert(r); + } + + // (public) this^e % m, 0 <= e < 2^32 + function bnModPowInt(e,m) { + var z; + if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); + return this.exp(e,z); + } + + // protected + BigInteger.prototype.copyTo = bnpCopyTo; + BigInteger.prototype.fromInt = bnpFromInt; + BigInteger.prototype.fromString = bnpFromString; + BigInteger.prototype.clamp = bnpClamp; + BigInteger.prototype.dlShiftTo = bnpDLShiftTo; + BigInteger.prototype.drShiftTo = bnpDRShiftTo; + BigInteger.prototype.lShiftTo = bnpLShiftTo; + BigInteger.prototype.rShiftTo = bnpRShiftTo; + BigInteger.prototype.subTo = bnpSubTo; + BigInteger.prototype.multiplyTo = bnpMultiplyTo; + BigInteger.prototype.squareTo = bnpSquareTo; + BigInteger.prototype.divRemTo = bnpDivRemTo; + BigInteger.prototype.invDigit = bnpInvDigit; + BigInteger.prototype.isEven = bnpIsEven; + BigInteger.prototype.exp = bnpExp; + + // public + BigInteger.prototype.toString = bnToString; + BigInteger.prototype.negate = bnNegate; + BigInteger.prototype.abs = bnAbs; + BigInteger.prototype.compareTo = bnCompareTo; + BigInteger.prototype.bitLength = bnBitLength; + BigInteger.prototype.mod = bnMod; + BigInteger.prototype.modPowInt = bnModPowInt; + + // "constants" + BigInteger.ZERO = nbv(0); + BigInteger.ONE = nbv(1); + + // Copyright (c) 2005-2009 Tom Wu + // All Rights Reserved. + // See "LICENSE" for details. + + // Extended JavaScript BN functions, required for RSA private ops. + + // Version 1.1: new BigInteger("0", 10) returns "proper" zero + // Version 1.2: square() API, isProbablePrime fix + + // (public) + function bnClone() { var r = nbi(); this.copyTo(r); return r; } + + // (public) return value as integer + function bnIntValue() { + if(this.s < 0) { + if(this.t == 1) return this[0]-this.DV; + else if(this.t == 0) return -1; + } + else if(this.t == 1) return this[0]; + else if(this.t == 0) return 0; + // assumes 16 < DB < 32 + return ((this[1]&((1<<(32-this.DB))-1))<>24; } + + // (public) return value as short (assumes DB>=16) + function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } + + // (protected) return x s.t. r^x < DV + function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); } + + // (public) 0 if this == 0, 1 if this > 0 + function bnSigNum() { + if(this.s < 0) return -1; + else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; + else return 1; + } + + // (protected) convert to radix string + function bnpToRadix(b) { + if(b == null) b = 10; + if(this.signum() == 0 || b < 2 || b > 36) return "0"; + var cs = this.chunkSize(b); + var a = Math.pow(b,cs); + var d = nbv(a), y = nbi(), z = nbi(), r = ""; + this.divRemTo(d,y,z); + while(y.signum() > 0) { + r = (a+z.intValue()).toString(b).substr(1) + r; + y.divRemTo(d,y,z); + } + return z.intValue().toString(b) + r; + } + + // (protected) convert from radix string + function bnpFromRadix(s,b) { + this.fromInt(0); + if(b == null) b = 10; + var cs = this.chunkSize(b); + var d = Math.pow(b,cs), mi = false, j = 0, w = 0; + for(var i = 0; i < s.length; ++i) { + var x = intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-" && this.signum() == 0) mi = true; + continue; + } + w = b*w+x; + if(++j >= cs) { + this.dMultiply(d); + this.dAddOffset(w,0); + j = 0; + w = 0; + } + } + if(j > 0) { + this.dMultiply(Math.pow(b,j)); + this.dAddOffset(w,0); + } + if(mi) BigInteger.ZERO.subTo(this,this); + } + + // (protected) alternate constructor + function bnpFromNumber(a,b,c) { + if("number" == typeof b) { + // new BigInteger(int,int,RNG) + if(a < 2) this.fromInt(1); + else { + this.fromNumber(a,c); + if(!this.testBit(a-1)) // force MSB set + this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); + if(this.isEven()) this.dAddOffset(1,0); // force odd + while(!this.isProbablePrime(b)) { + this.dAddOffset(2,0); + if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); + } + } + } + else { + // new BigInteger(int,RNG) + var x = new Array(), t = a&7; + x.length = (a>>3)+1; + b.nextBytes(x); + if(t > 0) x[0] &= ((1< 0) { + if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p) + r[k++] = d|(this.s<<(this.DB-p)); + while(i >= 0) { + if(p < 8) { + d = (this[i]&((1<>(p+=this.DB-8); + } + else { + d = (this[i]>>(p-=8))&0xff; + if(p <= 0) { p += this.DB; --i; } + } + if((d&0x80) != 0) d |= -256; + if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; + if(k > 0 || d != this.s) r[k++] = d; + } + } + return r; + } + + function bnEquals(a) { return(this.compareTo(a)==0); } + function bnMin(a) { return(this.compareTo(a)<0)?this:a; } + function bnMax(a) { return(this.compareTo(a)>0)?this:a; } + + // (protected) r = this op a (bitwise) + function bnpBitwiseTo(a,op,r) { + var i, f, m = Math.min(a.t,this.t); + for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]); + if(a.t < this.t) { + f = a.s&this.DM; + for(i = m; i < this.t; ++i) r[i] = op(this[i],f); + r.t = this.t; + } + else { + f = this.s&this.DM; + for(i = m; i < a.t; ++i) r[i] = op(f,a[i]); + r.t = a.t; + } + r.s = op(this.s,a.s); + r.clamp(); + } + + // (public) this & a + function op_and(x,y) { return x&y; } + function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } + + // (public) this | a + function op_or(x,y) { return x|y; } + function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } + + // (public) this ^ a + function op_xor(x,y) { return x^y; } + function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } + + // (public) this & ~a + function op_andnot(x,y) { return x&~y; } + function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } + + // (public) ~this + function bnNot() { + var r = nbi(); + for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i]; + r.t = this.t; + r.s = ~this.s; + return r; + } + + // (public) this << n + function bnShiftLeft(n) { + var r = nbi(); + if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); + return r; + } + + // (public) this >> n + function bnShiftRight(n) { + var r = nbi(); + if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); + return r; + } + + // return index of lowest 1-bit in x, x < 2^31 + function lbit(x) { + if(x == 0) return -1; + var r = 0; + if((x&0xffff) == 0) { x >>= 16; r += 16; } + if((x&0xff) == 0) { x >>= 8; r += 8; } + if((x&0xf) == 0) { x >>= 4; r += 4; } + if((x&3) == 0) { x >>= 2; r += 2; } + if((x&1) == 0) ++r; + return r; + } + + // (public) returns index of lowest 1-bit (or -1 if none) + function bnGetLowestSetBit() { + for(var i = 0; i < this.t; ++i) + if(this[i] != 0) return i*this.DB+lbit(this[i]); + if(this.s < 0) return this.t*this.DB; + return -1; + } + + // return number of 1 bits in x + function cbit(x) { + var r = 0; + while(x != 0) { x &= x-1; ++r; } + return r; + } + + // (public) return number of set bits + function bnBitCount() { + var r = 0, x = this.s&this.DM; + for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x); + return r; + } + + // (public) true iff nth bit is set + function bnTestBit(n) { + var j = Math.floor(n/this.DB); + if(j >= this.t) return(this.s!=0); + return((this[j]&(1<<(n%this.DB)))!=0); + } + + // (protected) this op (1<>= this.DB; + } + if(a.t < this.t) { + c += a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c += a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += a.s; + } + r.s = (c<0)?-1:0; + if(c > 0) r[i++] = c; + else if(c < -1) r[i++] = this.DV+c; + r.t = i; + r.clamp(); + } + + // (public) this + a + function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } + + // (public) this - a + function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } + + // (public) this * a + function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } + + // (public) this^2 + function bnSquare() { var r = nbi(); this.squareTo(r); return r; } + + // (public) this / a + function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } + + // (public) this % a + function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } + + // (public) [this/a,this%a] + function bnDivideAndRemainder(a) { + var q = nbi(), r = nbi(); + this.divRemTo(a,q,r); + return new Array(q,r); + } + + // (protected) this *= n, this >= 0, 1 < n < DV + function bnpDMultiply(n) { + this[this.t] = this.am(0,n-1,this,0,0,this.t); + ++this.t; + this.clamp(); + } + + // (protected) this += n << w words, this >= 0 + function bnpDAddOffset(n,w) { + if(n == 0) return; + while(this.t <= w) this[this.t++] = 0; + this[w] += n; + while(this[w] >= this.DV) { + this[w] -= this.DV; + if(++w >= this.t) this[this.t++] = 0; + ++this[w]; + } + } + + // A "null" reducer + function NullExp() {} + function nNop(x) { return x; } + function nMulTo(x,y,r) { x.multiplyTo(y,r); } + function nSqrTo(x,r) { x.squareTo(r); } + + NullExp.prototype.convert = nNop; + NullExp.prototype.revert = nNop; + NullExp.prototype.mulTo = nMulTo; + NullExp.prototype.sqrTo = nSqrTo; + + // (public) this^e + function bnPow(e) { return this.exp(e,new NullExp()); } + + // (protected) r = lower n words of "this * a", a.t <= n + // "this" should be the larger one if appropriate. + function bnpMultiplyLowerTo(a,n,r) { + var i = Math.min(this.t+a.t,n); + r.s = 0; // assumes a,this >= 0 + r.t = i; + while(i > 0) r[--i] = 0; + var j; + for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t); + for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i); + r.clamp(); + } + + // (protected) r = "this * a" without lower n words, n > 0 + // "this" should be the larger one if appropriate. + function bnpMultiplyUpperTo(a,n,r) { + --n; + var i = r.t = this.t+a.t-n; + r.s = 0; // assumes a,this >= 0 + while(--i >= 0) r[i] = 0; + for(i = Math.max(n-this.t,0); i < a.t; ++i) + r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n); + r.clamp(); + r.drShiftTo(1,r); + } + + // Barrett modular reduction + function Barrett(m) { + // setup Barrett + this.r2 = nbi(); + this.q3 = nbi(); + BigInteger.ONE.dlShiftTo(2*m.t,this.r2); + this.mu = this.r2.divide(m); + this.m = m; + } + + function barrettConvert(x) { + if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); + else if(x.compareTo(this.m) < 0) return x; + else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } + } + + function barrettRevert(x) { return x; } + + // x = x mod m (HAC 14.42) + function barrettReduce(x) { + x.drShiftTo(this.m.t-1,this.r2); + if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } + this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); + this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); + while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); + x.subTo(this.r2,x); + while(x.compareTo(this.m) >= 0) x.subTo(this.m,x); + } + + // r = x^2 mod m; x != r + function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + + // r = x*y mod m; x,y != r + function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + + Barrett.prototype.convert = barrettConvert; + Barrett.prototype.revert = barrettRevert; + Barrett.prototype.reduce = barrettReduce; + Barrett.prototype.mulTo = barrettMulTo; + Barrett.prototype.sqrTo = barrettSqrTo; + + // (public) this^e % m (HAC 14.85) + function bnModPow(e,m) { + var i = e.bitLength(), k, r = nbv(1), z; + if(i <= 0) return r; + else if(i < 18) k = 1; + else if(i < 48) k = 3; + else if(i < 144) k = 4; + else if(i < 768) k = 5; + else k = 6; + if(i < 8) + z = new Classic(m); + else if(m.isEven()) + z = new Barrett(m); + else + z = new Montgomery(m); + + // precomputation + var g = new Array(), n = 3, k1 = k-1, km = (1< 1) { + var g2 = nbi(); + z.sqrTo(g[1],g2); + while(n <= km) { + g[n] = nbi(); + z.mulTo(g2,g[n-2],g[n]); + n += 2; + } + } + + var j = e.t-1, w, is1 = true, r2 = nbi(), t; + i = nbits(e[j])-1; + while(j >= 0) { + if(i >= k1) w = (e[j]>>(i-k1))&km; + else { + w = (e[j]&((1<<(i+1))-1))<<(k1-i); + if(j > 0) w |= e[j-1]>>(this.DB+i-k1); + } + + n = k; + while((w&1) == 0) { w >>= 1; --n; } + if((i -= n) < 0) { i += this.DB; --j; } + if(is1) { // ret == 1, don't bother squaring or multiplying it + g[w].copyTo(r); + is1 = false; + } + else { + while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; } + if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; } + z.mulTo(r2,g[w],r); + } + + while(j >= 0 && (e[j]&(1< 0) { + x.rShiftTo(g,x); + y.rShiftTo(g,y); + } + while(x.signum() > 0) { + if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); + if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); + if(x.compareTo(y) >= 0) { + x.subTo(y,x); + x.rShiftTo(1,x); + } + else { + y.subTo(x,y); + y.rShiftTo(1,y); + } + } + if(g > 0) y.lShiftTo(g,y); + return y; + } + + // (protected) this % n, n < 2^26 + function bnpModInt(n) { + if(n <= 0) return 0; + var d = this.DV%n, r = (this.s<0)?n-1:0; + if(this.t > 0) + if(d == 0) r = this[0]%n; + else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n; + return r; + } + + // (public) 1/this % m (HAC 14.61) + function bnModInverse(m) { + var ac = m.isEven(); + if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; + var u = m.clone(), v = this.clone(); + var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); + while(u.signum() != 0) { + while(u.isEven()) { + u.rShiftTo(1,u); + if(ac) { + if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } + a.rShiftTo(1,a); + } + else if(!b.isEven()) b.subTo(m,b); + b.rShiftTo(1,b); + } + while(v.isEven()) { + v.rShiftTo(1,v); + if(ac) { + if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } + c.rShiftTo(1,c); + } + else if(!d.isEven()) d.subTo(m,d); + d.rShiftTo(1,d); + } + if(u.compareTo(v) >= 0) { + u.subTo(v,u); + if(ac) a.subTo(c,a); + b.subTo(d,b); + } + else { + v.subTo(u,v); + if(ac) c.subTo(a,c); + d.subTo(b,d); + } + } + if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; + if(d.compareTo(m) >= 0) return d.subtract(m); + if(d.signum() < 0) d.addTo(m,d); else return d; + if(d.signum() < 0) return d.add(m); else return d; + } + + var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997]; + var lplim = (1<<26)/lowprimes[lowprimes.length-1]; + + // (public) test primality with certainty >= 1-.5^t + function bnIsProbablePrime(t) { + var i, x = this.abs(); + if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) { + for(i = 0; i < lowprimes.length; ++i) + if(x[0] == lowprimes[i]) return true; + return false; + } + if(x.isEven()) return false; + i = 1; + while(i < lowprimes.length) { + var m = lowprimes[i], j = i+1; + while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; + m = x.modInt(m); + while(i < j) if(m%lowprimes[i++] == 0) return false; + } + return x.millerRabin(t); + } + + // (protected) true if probably prime (HAC 4.24, Miller-Rabin) + function bnpMillerRabin(t) { + var n1 = this.subtract(BigInteger.ONE); + var k = n1.getLowestSetBit(); + if(k <= 0) return false; + var r = n1.shiftRight(k); + t = (t+1)>>1; + if(t > lowprimes.length) t = lowprimes.length; + var a = nbi(); + for(var i = 0; i < t; ++i) { + //Pick bases at random, instead of starting at 2 + a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]); + var y = a.modPow(r,this); + if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { + var j = 1; + while(j++ < k && y.compareTo(n1) != 0) { + y = y.modPowInt(2,this); + if(y.compareTo(BigInteger.ONE) == 0) return false; + } + if(y.compareTo(n1) != 0) return false; + } + } + return true; + } + + // protected + BigInteger.prototype.chunkSize = bnpChunkSize; + BigInteger.prototype.toRadix = bnpToRadix; + BigInteger.prototype.fromRadix = bnpFromRadix; + BigInteger.prototype.fromNumber = bnpFromNumber; + BigInteger.prototype.bitwiseTo = bnpBitwiseTo; + BigInteger.prototype.changeBit = bnpChangeBit; + BigInteger.prototype.addTo = bnpAddTo; + BigInteger.prototype.dMultiply = bnpDMultiply; + BigInteger.prototype.dAddOffset = bnpDAddOffset; + BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; + BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; + BigInteger.prototype.modInt = bnpModInt; + BigInteger.prototype.millerRabin = bnpMillerRabin; + + // public + BigInteger.prototype.clone = bnClone; + BigInteger.prototype.intValue = bnIntValue; + BigInteger.prototype.byteValue = bnByteValue; + BigInteger.prototype.shortValue = bnShortValue; + BigInteger.prototype.signum = bnSigNum; + BigInteger.prototype.toByteArray = bnToByteArray; + BigInteger.prototype.equals = bnEquals; + BigInteger.prototype.min = bnMin; + BigInteger.prototype.max = bnMax; + BigInteger.prototype.and = bnAnd; + BigInteger.prototype.or = bnOr; + BigInteger.prototype.xor = bnXor; + BigInteger.prototype.andNot = bnAndNot; + BigInteger.prototype.not = bnNot; + BigInteger.prototype.shiftLeft = bnShiftLeft; + BigInteger.prototype.shiftRight = bnShiftRight; + BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; + BigInteger.prototype.bitCount = bnBitCount; + BigInteger.prototype.testBit = bnTestBit; + BigInteger.prototype.setBit = bnSetBit; + BigInteger.prototype.clearBit = bnClearBit; + BigInteger.prototype.flipBit = bnFlipBit; + BigInteger.prototype.add = bnAdd; + BigInteger.prototype.subtract = bnSubtract; + BigInteger.prototype.multiply = bnMultiply; + BigInteger.prototype.divide = bnDivide; + BigInteger.prototype.remainder = bnRemainder; + BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; + BigInteger.prototype.modPow = bnModPow; + BigInteger.prototype.modInverse = bnModInverse; + BigInteger.prototype.pow = bnPow; + BigInteger.prototype.gcd = bnGCD; + BigInteger.prototype.isProbablePrime = bnIsProbablePrime; + + // JSBN-specific extension + BigInteger.prototype.square = bnSquare; + + // Expose the Barrett function + BigInteger.prototype.Barrett = Barrett + + // BigInteger interfaces not implemented in jsbn: + + // BigInteger(int signum, byte[] magnitude) + // double doubleValue() + // float floatValue() + // int hashCode() + // long longValue() + // static BigInteger valueOf(long val) + + // Random number generator - requires a PRNG backend, e.g. prng4.js + + // For best results, put code like + // + // in your main HTML document. + + var rng_state; + var rng_pool; + var rng_pptr; + + // Mix in a 32-bit integer into the pool + function rng_seed_int(x) { + rng_pool[rng_pptr++] ^= x & 255; + rng_pool[rng_pptr++] ^= (x >> 8) & 255; + rng_pool[rng_pptr++] ^= (x >> 16) & 255; + rng_pool[rng_pptr++] ^= (x >> 24) & 255; + if(rng_pptr >= rng_psize) rng_pptr -= rng_psize; + } + + // Mix in the current time (w/milliseconds) into the pool + function rng_seed_time() { + rng_seed_int(new Date().getTime()); + } + + // Initialize the pool with junk if needed. + if(rng_pool == null) { + rng_pool = new Array(); + rng_pptr = 0; + var t; + if(typeof window !== "undefined" && window.crypto) { + if (window.crypto.getRandomValues) { + // Use webcrypto if available + var ua = new Uint8Array(32); + window.crypto.getRandomValues(ua); + for(t = 0; t < 32; ++t) + rng_pool[rng_pptr++] = ua[t]; + } + else if(navigator.appName == "Netscape" && navigator.appVersion < "5") { + // Extract entropy (256 bits) from NS4 RNG if available + var z = window.crypto.random(32); + for(t = 0; t < z.length; ++t) + rng_pool[rng_pptr++] = z.charCodeAt(t) & 255; + } + } + while(rng_pptr < rng_psize) { // extract some randomness from Math.random() + t = Math.floor(65536 * Math.random()); + rng_pool[rng_pptr++] = t >>> 8; + rng_pool[rng_pptr++] = t & 255; + } + rng_pptr = 0; + rng_seed_time(); + //rng_seed_int(window.screenX); + //rng_seed_int(window.screenY); + } + + function rng_get_byte() { + if(rng_state == null) { + rng_seed_time(); + rng_state = prng_newstate(); + rng_state.init(rng_pool); + for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) + rng_pool[rng_pptr] = 0; + rng_pptr = 0; + //rng_pool = null; + } + // TODO: allow reseeding after first request + return rng_state.next(); + } + + function rng_get_bytes(ba) { + var i; + for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte(); + } + + function SecureRandom() {} + + SecureRandom.prototype.nextBytes = rng_get_bytes; + + // prng4.js - uses Arcfour as a PRNG + + function Arcfour() { + this.i = 0; + this.j = 0; + this.S = new Array(); + } + + // Initialize arcfour context from key, an array of ints, each from [0..255] + function ARC4init(key) { + var i, j, t; + for(i = 0; i < 256; ++i) + this.S[i] = i; + j = 0; + for(i = 0; i < 256; ++i) { + j = (j + this.S[i] + key[i % key.length]) & 255; + t = this.S[i]; + this.S[i] = this.S[j]; + this.S[j] = t; + } + this.i = 0; + this.j = 0; + } + + function ARC4next() { + var t; + this.i = (this.i + 1) & 255; + this.j = (this.j + this.S[this.i]) & 255; + t = this.S[this.i]; + this.S[this.i] = this.S[this.j]; + this.S[this.j] = t; + return this.S[(t + this.S[this.i]) & 255]; + } + + Arcfour.prototype.init = ARC4init; + Arcfour.prototype.next = ARC4next; + + // Plug in your RNG constructor here + function prng_newstate() { + return new Arcfour(); + } + + // Pool size must be a multiple of 4 and greater than 32. + // An array of bytes the size of the pool will be passed to init() + var rng_psize = 256; + + BigInteger.SecureRandom = SecureRandom; + BigInteger.BigInteger = BigInteger; + if (true) { + exports = module.exports = BigInteger; + } else {} + +}).call(this); + + +/***/ }), + +/***/ 243: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + + +var net = __webpack_require__(631) + , tls = __webpack_require__(16) + , http = __webpack_require__(605) + , https = __webpack_require__(211) + , events = __webpack_require__(614) + , assert = __webpack_require__(357) + , util = __webpack_require__(669) + , Buffer = __webpack_require__(149).Buffer + ; + +exports.httpOverHttp = httpOverHttp +exports.httpsOverHttp = httpsOverHttp +exports.httpOverHttps = httpOverHttps +exports.httpsOverHttps = httpsOverHttps + + +function httpOverHttp(options) { + var agent = new TunnelingAgent(options) + agent.request = http.request + return agent +} + +function httpsOverHttp(options) { + var agent = new TunnelingAgent(options) + agent.request = http.request + agent.createSocket = createSecureSocket + agent.defaultPort = 443 + return agent +} + +function httpOverHttps(options) { + var agent = new TunnelingAgent(options) + agent.request = https.request + return agent +} + +function httpsOverHttps(options) { + var agent = new TunnelingAgent(options) + agent.request = https.request + agent.createSocket = createSecureSocket + agent.defaultPort = 443 + return agent +} + + +function TunnelingAgent(options) { + var self = this + self.options = options || {} + self.proxyOptions = self.options.proxy || {} + self.maxSockets = self.options.maxSockets || http.Agent.defaultMaxSockets + self.requests = [] + self.sockets = [] + + self.on('free', function onFree(socket, host, port) { + for (var i = 0, len = self.requests.length; i < len; ++i) { + var pending = self.requests[i] + if (pending.host === host && pending.port === port) { + // Detect the request to connect same origin server, + // reuse the connection. + self.requests.splice(i, 1) + pending.request.onSocket(socket) + return + } + } + socket.destroy() + self.removeSocket(socket) + }) +} +util.inherits(TunnelingAgent, events.EventEmitter) + +TunnelingAgent.prototype.addRequest = function addRequest(req, options) { + var self = this + + // Legacy API: addRequest(req, host, port, path) + if (typeof options === 'string') { + options = { + host: options, + port: arguments[2], + path: arguments[3] + }; + } + + if (self.sockets.length >= this.maxSockets) { + // We are over limit so we'll add it to the queue. + self.requests.push({host: options.host, port: options.port, request: req}) + return + } + + // If we are under maxSockets create a new one. + self.createConnection({host: options.host, port: options.port, request: req}) +} + +TunnelingAgent.prototype.createConnection = function createConnection(pending) { + var self = this + + self.createSocket(pending, function(socket) { + socket.on('free', onFree) + socket.on('close', onCloseOrRemove) + socket.on('agentRemove', onCloseOrRemove) + pending.request.onSocket(socket) + + function onFree() { + self.emit('free', socket, pending.host, pending.port) + } + + function onCloseOrRemove(err) { + self.removeSocket(socket) + socket.removeListener('free', onFree) + socket.removeListener('close', onCloseOrRemove) + socket.removeListener('agentRemove', onCloseOrRemove) + } + }) +} + +TunnelingAgent.prototype.createSocket = function createSocket(options, cb) { + var self = this + var placeholder = {} + self.sockets.push(placeholder) + + var connectOptions = mergeOptions({}, self.proxyOptions, + { method: 'CONNECT' + , path: options.host + ':' + options.port + , agent: false + } + ) + if (connectOptions.proxyAuth) { + connectOptions.headers = connectOptions.headers || {} + connectOptions.headers['Proxy-Authorization'] = 'Basic ' + + Buffer.from(connectOptions.proxyAuth).toString('base64') + } + + debug('making CONNECT request') + var connectReq = self.request(connectOptions) + connectReq.useChunkedEncodingByDefault = false // for v0.6 + connectReq.once('response', onResponse) // for v0.6 + connectReq.once('upgrade', onUpgrade) // for v0.6 + connectReq.once('connect', onConnect) // for v0.7 or later + connectReq.once('error', onError) + connectReq.end() + + function onResponse(res) { + // Very hacky. This is necessary to avoid http-parser leaks. + res.upgrade = true + } + + function onUpgrade(res, socket, head) { + // Hacky. + process.nextTick(function() { + onConnect(res, socket, head) + }) + } + + function onConnect(res, socket, head) { + connectReq.removeAllListeners() + socket.removeAllListeners() + + if (res.statusCode === 200) { + assert.equal(head.length, 0) + debug('tunneling connection has established') + self.sockets[self.sockets.indexOf(placeholder)] = socket + cb(socket) + } else { + debug('tunneling socket could not be established, statusCode=%d', res.statusCode) + var error = new Error('tunneling socket could not be established, ' + 'statusCode=' + res.statusCode) + error.code = 'ECONNRESET' + options.request.emit('error', error) + self.removeSocket(placeholder) + } + } + + function onError(cause) { + connectReq.removeAllListeners() + + debug('tunneling socket could not be established, cause=%s\n', cause.message, cause.stack) + var error = new Error('tunneling socket could not be established, ' + 'cause=' + cause.message) + error.code = 'ECONNRESET' + options.request.emit('error', error) + self.removeSocket(placeholder) + } +} + +TunnelingAgent.prototype.removeSocket = function removeSocket(socket) { + var pos = this.sockets.indexOf(socket) + if (pos === -1) return + + this.sockets.splice(pos, 1) + + var pending = this.requests.shift() + if (pending) { + // If we have pending requests and a socket gets closed a new one + // needs to be created to take over in the pool for the one that closed. + this.createConnection(pending) + } +} + +function createSecureSocket(options, cb) { + var self = this + TunnelingAgent.prototype.createSocket.call(self, options, function(socket) { + // 0 is dummy port for v0.6 + var secureSocket = tls.connect(0, mergeOptions({}, self.options, + { servername: options.host + , socket: socket + } + )) + self.sockets[self.sockets.indexOf(socket)] = secureSocket + cb(secureSocket) + }) +} + + +function mergeOptions(target) { + for (var i = 1, len = arguments.length; i < len; ++i) { + var overrides = arguments[i] + if (typeof overrides === 'object') { + var keys = Object.keys(overrides) + for (var j = 0, keyLen = keys.length; j < keyLen; ++j) { + var k = keys[j] + if (overrides[k] !== undefined) { + target[k] = overrides[k] + } + } + } + } + return target +} + + +var debug +if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) { + debug = function() { + var args = Array.prototype.slice.call(arguments) + if (typeof args[0] === 'string') { + args[0] = 'TUNNEL: ' + args[0] + } else { + args.unshift('TUNNEL:') + } + console.error.apply(console, args) + } +} else { + debug = function() {} +} +exports.debug = debug // for test + + +/***/ }), + +/***/ 249: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2011 Mark Cavage All rights reserved. + +var errors = __webpack_require__(584); +var types = __webpack_require__(362); + +var Reader = __webpack_require__(733); +var Writer = __webpack_require__(998); + + +// --- Exports + +module.exports = { + + Reader: Reader, + + Writer: Writer + +}; + +for (var t in types) { + if (types.hasOwnProperty(t)) + module.exports[t] = types[t]; +} +for (var e in errors) { + if (errors.hasOwnProperty(e)) + module.exports[e] = errors[e]; +} + + +/***/ }), + +/***/ 254: +/***/ (function(module) { + +function Caseless (dict) { + this.dict = dict || {} +} +Caseless.prototype.set = function (name, value, clobber) { + if (typeof name === 'object') { + for (var i in name) { + this.set(i, name[i], value) + } + } else { + if (typeof clobber === 'undefined') clobber = true + var has = this.has(name) + + if (!clobber && has) this.dict[has] = this.dict[has] + ',' + value + else this.dict[has || name] = value + return has + } +} +Caseless.prototype.has = function (name) { + var keys = Object.keys(this.dict) + , name = name.toLowerCase() + ; + for (var i=0;i 0) { + m2 = lines[--ei].match(/*JSSTYLED*/ + /[-]+[ ]*END ([A-Z0-9][A-Za-z0-9]+ )?(PUBLIC|PRIVATE) KEY[ ]*[-]+/); + } + assert.ok(m2, 'invalid PEM footer'); + + /* Begin and end banners must match key type */ + assert.equal(m[2], m2[2]); + var type = m[2].toLowerCase(); + + var alg; + if (m[1]) { + /* They also must match algorithms, if given */ + assert.equal(m[1], m2[1], 'PEM header and footer mismatch'); + alg = m[1].trim(); + } + + lines = lines.slice(si, ei + 1); + + var headers = {}; + while (true) { + lines = lines.slice(1); + m = lines[0].match(/*JSSTYLED*/ + /^([A-Za-z0-9-]+): (.+)$/); + if (!m) + break; + headers[m[1].toLowerCase()] = m[2]; + } + + /* Chop off the first and last lines */ + lines = lines.slice(0, -1).join(''); + buf = Buffer.from(lines, 'base64'); + + var cipher, key, iv; + if (headers['proc-type']) { + var parts = headers['proc-type'].split(','); + if (parts[0] === '4' && parts[1] === 'ENCRYPTED') { + if (typeof (options.passphrase) === 'string') { + options.passphrase = Buffer.from( + options.passphrase, 'utf-8'); + } + if (!Buffer.isBuffer(options.passphrase)) { + throw (new errors.KeyEncryptedError( + options.filename, 'PEM')); + } else { + parts = headers['dek-info'].split(','); + assert.ok(parts.length === 2); + cipher = parts[0].toLowerCase(); + iv = Buffer.from(parts[1], 'hex'); + key = utils.opensslKeyDeriv(cipher, iv, + options.passphrase, 1).key; + } + } + } + + if (alg && alg.toLowerCase() === 'encrypted') { + var eder = new asn1.BerReader(buf); + var pbesEnd; + eder.readSequence(); + + eder.readSequence(); + pbesEnd = eder.offset + eder.length; + + var method = eder.readOID(); + if (method !== OID_PBES2) { + throw (new Error('Unsupported PEM/PKCS8 encryption ' + + 'scheme: ' + method)); + } + + eder.readSequence(); /* PBES2-params */ + + eder.readSequence(); /* keyDerivationFunc */ + var kdfEnd = eder.offset + eder.length; + var kdfOid = eder.readOID(); + if (kdfOid !== OID_PBKDF2) + throw (new Error('Unsupported PBES2 KDF: ' + kdfOid)); + eder.readSequence(); + var salt = eder.readString(asn1.Ber.OctetString, true); + var iterations = eder.readInt(); + var hashAlg = 'sha1'; + if (eder.offset < kdfEnd) { + eder.readSequence(); + var hashAlgOid = eder.readOID(); + hashAlg = OID_TO_HASH[hashAlgOid]; + if (hashAlg === undefined) { + throw (new Error('Unsupported PBKDF2 hash: ' + + hashAlgOid)); + } + } + eder._offset = kdfEnd; + + eder.readSequence(); /* encryptionScheme */ + var cipherOid = eder.readOID(); + cipher = OID_TO_CIPHER[cipherOid]; + if (cipher === undefined) { + throw (new Error('Unsupported PBES2 cipher: ' + + cipherOid)); + } + iv = eder.readString(asn1.Ber.OctetString, true); + + eder._offset = pbesEnd; + buf = eder.readString(asn1.Ber.OctetString, true); + + if (typeof (options.passphrase) === 'string') { + options.passphrase = Buffer.from( + options.passphrase, 'utf-8'); + } + if (!Buffer.isBuffer(options.passphrase)) { + throw (new errors.KeyEncryptedError( + options.filename, 'PEM')); + } + + var cinfo = utils.opensshCipherInfo(cipher); + + cipher = cinfo.opensslName; + key = utils.pbkdf2(hashAlg, salt, iterations, cinfo.keySize, + options.passphrase); + alg = undefined; + } + + if (cipher && key && iv) { + var cipherStream = crypto.createDecipheriv(cipher, key, iv); + var chunk, chunks = []; + cipherStream.once('error', function (e) { + if (e.toString().indexOf('bad decrypt') !== -1) { + throw (new Error('Incorrect passphrase ' + + 'supplied, could not decrypt key')); + } + throw (e); + }); + cipherStream.write(buf); + cipherStream.end(); + while ((chunk = cipherStream.read()) !== null) + chunks.push(chunk); + buf = Buffer.concat(chunks); + } + + /* The new OpenSSH internal format abuses PEM headers */ + if (alg && alg.toLowerCase() === 'openssh') + return (sshpriv.readSSHPrivate(type, buf, options)); + if (alg && alg.toLowerCase() === 'ssh2') + return (rfc4253.readType(type, buf, options)); + + var der = new asn1.BerReader(buf); + der.originalInput = input; + + /* + * All of the PEM file types start with a sequence tag, so chop it + * off here + */ + der.readSequence(); + + /* PKCS#1 type keys name an algorithm in the banner explicitly */ + if (alg) { + if (forceType) + assert.strictEqual(forceType, 'pkcs1'); + return (pkcs1.readPkcs1(alg, type, der)); + } else { + if (forceType) + assert.strictEqual(forceType, 'pkcs8'); + return (pkcs8.readPkcs8(alg, type, der)); + } +} + +function write(key, options, type) { + assert.object(key); + + var alg = { + 'ecdsa': 'EC', + 'rsa': 'RSA', + 'dsa': 'DSA', + 'ed25519': 'EdDSA' + }[key.type]; + var header; + + var der = new asn1.BerWriter(); + + if (PrivateKey.isPrivateKey(key)) { + if (type && type === 'pkcs8') { + header = 'PRIVATE KEY'; + pkcs8.writePkcs8(der, key); + } else { + if (type) + assert.strictEqual(type, 'pkcs1'); + header = alg + ' PRIVATE KEY'; + pkcs1.writePkcs1(der, key); + } + + } else if (Key.isKey(key)) { + if (type && type === 'pkcs1') { + header = alg + ' PUBLIC KEY'; + pkcs1.writePkcs1(der, key); + } else { + if (type) + assert.strictEqual(type, 'pkcs8'); + header = 'PUBLIC KEY'; + pkcs8.writePkcs8(der, key); + } + + } else { + throw (new Error('key is not a Key or PrivateKey')); + } + + var tmp = der.buffer.toString('base64'); + var len = tmp.length + (tmp.length / 64) + + 18 + 16 + header.length*2 + 10; + var buf = Buffer.alloc(len); + var o = 0; + o += buf.write('-----BEGIN ' + header + '-----\n', o); + for (var i = 0; i < tmp.length; ) { + var limit = i + 64; + if (limit > tmp.length) + limit = tmp.length; + o += buf.write(tmp.slice(i, limit), o); + buf[o++] = 10; + i = limit; + } + o += buf.write('-----END ' + header + '-----\n', o); + + return (buf.slice(0, o)); +} + + +/***/ }), + +/***/ 270: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2015 Joyent, Inc. + +module.exports = { + bufferSplit: bufferSplit, + addRSAMissing: addRSAMissing, + calculateDSAPublic: calculateDSAPublic, + calculateED25519Public: calculateED25519Public, + calculateX25519Public: calculateX25519Public, + mpNormalize: mpNormalize, + mpDenormalize: mpDenormalize, + ecNormalize: ecNormalize, + countZeros: countZeros, + assertCompatible: assertCompatible, + isCompatible: isCompatible, + opensslKeyDeriv: opensslKeyDeriv, + opensshCipherInfo: opensshCipherInfo, + publicFromPrivateECDSA: publicFromPrivateECDSA, + zeroPadToLength: zeroPadToLength, + writeBitString: writeBitString, + readBitString: readBitString, + pbkdf2: pbkdf2 +}; + +var assert = __webpack_require__(477); +var Buffer = __webpack_require__(215).Buffer; +var PrivateKey = __webpack_require__(502); +var Key = __webpack_require__(852); +var crypto = __webpack_require__(417); +var algs = __webpack_require__(98); +var asn1 = __webpack_require__(62); + +var ec = __webpack_require__(729); +var jsbn = __webpack_require__(242).BigInteger; +var nacl = __webpack_require__(196); + +var MAX_CLASS_DEPTH = 3; + +function isCompatible(obj, klass, needVer) { + if (obj === null || typeof (obj) !== 'object') + return (false); + if (needVer === undefined) + needVer = klass.prototype._sshpkApiVersion; + if (obj instanceof klass && + klass.prototype._sshpkApiVersion[0] == needVer[0]) + return (true); + var proto = Object.getPrototypeOf(obj); + var depth = 0; + while (proto.constructor.name !== klass.name) { + proto = Object.getPrototypeOf(proto); + if (!proto || ++depth > MAX_CLASS_DEPTH) + return (false); + } + if (proto.constructor.name !== klass.name) + return (false); + var ver = proto._sshpkApiVersion; + if (ver === undefined) + ver = klass._oldVersionDetect(obj); + if (ver[0] != needVer[0] || ver[1] < needVer[1]) + return (false); + return (true); +} + +function assertCompatible(obj, klass, needVer, name) { + if (name === undefined) + name = 'object'; + assert.ok(obj, name + ' must not be null'); + assert.object(obj, name + ' must be an object'); + if (needVer === undefined) + needVer = klass.prototype._sshpkApiVersion; + if (obj instanceof klass && + klass.prototype._sshpkApiVersion[0] == needVer[0]) + return; + var proto = Object.getPrototypeOf(obj); + var depth = 0; + while (proto.constructor.name !== klass.name) { + proto = Object.getPrototypeOf(proto); + assert.ok(proto && ++depth <= MAX_CLASS_DEPTH, + name + ' must be a ' + klass.name + ' instance'); + } + assert.strictEqual(proto.constructor.name, klass.name, + name + ' must be a ' + klass.name + ' instance'); + var ver = proto._sshpkApiVersion; + if (ver === undefined) + ver = klass._oldVersionDetect(obj); + assert.ok(ver[0] == needVer[0] && ver[1] >= needVer[1], + name + ' must be compatible with ' + klass.name + ' klass ' + + 'version ' + needVer[0] + '.' + needVer[1]); +} + +var CIPHER_LEN = { + 'des-ede3-cbc': { key: 24, iv: 8 }, + 'aes-128-cbc': { key: 16, iv: 16 }, + 'aes-256-cbc': { key: 32, iv: 16 } +}; +var PKCS5_SALT_LEN = 8; + +function opensslKeyDeriv(cipher, salt, passphrase, count) { + assert.buffer(salt, 'salt'); + assert.buffer(passphrase, 'passphrase'); + assert.number(count, 'iteration count'); + + var clen = CIPHER_LEN[cipher]; + assert.object(clen, 'supported cipher'); + + salt = salt.slice(0, PKCS5_SALT_LEN); + + var D, D_prev, bufs; + var material = Buffer.alloc(0); + while (material.length < clen.key + clen.iv) { + bufs = []; + if (D_prev) + bufs.push(D_prev); + bufs.push(passphrase); + bufs.push(salt); + D = Buffer.concat(bufs); + for (var j = 0; j < count; ++j) + D = crypto.createHash('md5').update(D).digest(); + material = Buffer.concat([material, D]); + D_prev = D; + } + + return ({ + key: material.slice(0, clen.key), + iv: material.slice(clen.key, clen.key + clen.iv) + }); +} + +/* See: RFC2898 */ +function pbkdf2(hashAlg, salt, iterations, size, passphrase) { + var hkey = Buffer.alloc(salt.length + 4); + salt.copy(hkey); + + var gen = 0, ts = []; + var i = 1; + while (gen < size) { + var t = T(i++); + gen += t.length; + ts.push(t); + } + return (Buffer.concat(ts).slice(0, size)); + + function T(I) { + hkey.writeUInt32BE(I, hkey.length - 4); + + var hmac = crypto.createHmac(hashAlg, passphrase); + hmac.update(hkey); + + var Ti = hmac.digest(); + var Uc = Ti; + var c = 1; + while (c++ < iterations) { + hmac = crypto.createHmac(hashAlg, passphrase); + hmac.update(Uc); + Uc = hmac.digest(); + for (var x = 0; x < Ti.length; ++x) + Ti[x] ^= Uc[x]; + } + return (Ti); + } +} + +/* Count leading zero bits on a buffer */ +function countZeros(buf) { + var o = 0, obit = 8; + while (o < buf.length) { + var mask = (1 << obit); + if ((buf[o] & mask) === mask) + break; + obit--; + if (obit < 0) { + o++; + obit = 8; + } + } + return (o*8 + (8 - obit) - 1); +} + +function bufferSplit(buf, chr) { + assert.buffer(buf); + assert.string(chr); + + var parts = []; + var lastPart = 0; + var matches = 0; + for (var i = 0; i < buf.length; ++i) { + if (buf[i] === chr.charCodeAt(matches)) + ++matches; + else if (buf[i] === chr.charCodeAt(0)) + matches = 1; + else + matches = 0; + + if (matches >= chr.length) { + var newPart = i + 1; + parts.push(buf.slice(lastPart, newPart - matches)); + lastPart = newPart; + matches = 0; + } + } + if (lastPart <= buf.length) + parts.push(buf.slice(lastPart, buf.length)); + + return (parts); +} + +function ecNormalize(buf, addZero) { + assert.buffer(buf); + if (buf[0] === 0x00 && buf[1] === 0x04) { + if (addZero) + return (buf); + return (buf.slice(1)); + } else if (buf[0] === 0x04) { + if (!addZero) + return (buf); + } else { + while (buf[0] === 0x00) + buf = buf.slice(1); + if (buf[0] === 0x02 || buf[0] === 0x03) + throw (new Error('Compressed elliptic curve points ' + + 'are not supported')); + if (buf[0] !== 0x04) + throw (new Error('Not a valid elliptic curve point')); + if (!addZero) + return (buf); + } + var b = Buffer.alloc(buf.length + 1); + b[0] = 0x0; + buf.copy(b, 1); + return (b); +} + +function readBitString(der, tag) { + if (tag === undefined) + tag = asn1.Ber.BitString; + var buf = der.readString(tag, true); + assert.strictEqual(buf[0], 0x00, 'bit strings with unused bits are ' + + 'not supported (0x' + buf[0].toString(16) + ')'); + return (buf.slice(1)); +} + +function writeBitString(der, buf, tag) { + if (tag === undefined) + tag = asn1.Ber.BitString; + var b = Buffer.alloc(buf.length + 1); + b[0] = 0x00; + buf.copy(b, 1); + der.writeBuffer(b, tag); +} + +function mpNormalize(buf) { + assert.buffer(buf); + while (buf.length > 1 && buf[0] === 0x00 && (buf[1] & 0x80) === 0x00) + buf = buf.slice(1); + if ((buf[0] & 0x80) === 0x80) { + var b = Buffer.alloc(buf.length + 1); + b[0] = 0x00; + buf.copy(b, 1); + buf = b; + } + return (buf); +} + +function mpDenormalize(buf) { + assert.buffer(buf); + while (buf.length > 1 && buf[0] === 0x00) + buf = buf.slice(1); + return (buf); +} + +function zeroPadToLength(buf, len) { + assert.buffer(buf); + assert.number(len); + while (buf.length > len) { + assert.equal(buf[0], 0x00); + buf = buf.slice(1); + } + while (buf.length < len) { + var b = Buffer.alloc(buf.length + 1); + b[0] = 0x00; + buf.copy(b, 1); + buf = b; + } + return (buf); +} + +function bigintToMpBuf(bigint) { + var buf = Buffer.from(bigint.toByteArray()); + buf = mpNormalize(buf); + return (buf); +} + +function calculateDSAPublic(g, p, x) { + assert.buffer(g); + assert.buffer(p); + assert.buffer(x); + g = new jsbn(g); + p = new jsbn(p); + x = new jsbn(x); + var y = g.modPow(x, p); + var ybuf = bigintToMpBuf(y); + return (ybuf); +} + +function calculateED25519Public(k) { + assert.buffer(k); + + var kp = nacl.sign.keyPair.fromSeed(new Uint8Array(k)); + return (Buffer.from(kp.publicKey)); +} + +function calculateX25519Public(k) { + assert.buffer(k); + + var kp = nacl.box.keyPair.fromSeed(new Uint8Array(k)); + return (Buffer.from(kp.publicKey)); +} + +function addRSAMissing(key) { + assert.object(key); + assertCompatible(key, PrivateKey, [1, 1]); + + var d = new jsbn(key.part.d.data); + var buf; + + if (!key.part.dmodp) { + var p = new jsbn(key.part.p.data); + var dmodp = d.mod(p.subtract(1)); + + buf = bigintToMpBuf(dmodp); + key.part.dmodp = {name: 'dmodp', data: buf}; + key.parts.push(key.part.dmodp); + } + if (!key.part.dmodq) { + var q = new jsbn(key.part.q.data); + var dmodq = d.mod(q.subtract(1)); + + buf = bigintToMpBuf(dmodq); + key.part.dmodq = {name: 'dmodq', data: buf}; + key.parts.push(key.part.dmodq); + } +} + +function publicFromPrivateECDSA(curveName, priv) { + assert.string(curveName, 'curveName'); + assert.buffer(priv); + var params = algs.curves[curveName]; + var p = new jsbn(params.p); + var a = new jsbn(params.a); + var b = new jsbn(params.b); + var curve = new ec.ECCurveFp(p, a, b); + var G = curve.decodePointHex(params.G.toString('hex')); + + var d = new jsbn(mpNormalize(priv)); + var pub = G.multiply(d); + pub = Buffer.from(curve.encodePointHex(pub), 'hex'); + + var parts = []; + parts.push({name: 'curve', data: Buffer.from(curveName)}); + parts.push({name: 'Q', data: pub}); + + var key = new Key({type: 'ecdsa', curve: curve, parts: parts}); + return (key); +} + +function opensshCipherInfo(cipher) { + var inf = {}; + switch (cipher) { + case '3des-cbc': + inf.keySize = 24; + inf.blockSize = 8; + inf.opensslName = 'des-ede3-cbc'; + break; + case 'blowfish-cbc': + inf.keySize = 16; + inf.blockSize = 8; + inf.opensslName = 'bf-cbc'; + break; + case 'aes128-cbc': + case 'aes128-ctr': + case 'aes128-gcm@openssh.com': + inf.keySize = 16; + inf.blockSize = 16; + inf.opensslName = 'aes-128-' + cipher.slice(7, 10); + break; + case 'aes192-cbc': + case 'aes192-ctr': + case 'aes192-gcm@openssh.com': + inf.keySize = 24; + inf.blockSize = 16; + inf.opensslName = 'aes-192-' + cipher.slice(7, 10); + break; + case 'aes256-cbc': + case 'aes256-ctr': + case 'aes256-gcm@openssh.com': + inf.keySize = 32; + inf.blockSize = 16; + inf.opensslName = 'aes-256-' + cipher.slice(7, 10); + break; + default: + throw (new Error( + 'Unsupported openssl cipher "' + cipher + '"')); + } + return (inf); +} + + +/***/ }), + +/***/ 281: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_enum(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + var $i = 'i' + $lvl, + $vSchema = 'schema' + $lvl; + if (!$isData) { + out += ' var ' + ($vSchema) + ' = validate.schema' + ($schemaPath) + ';'; + } + out += 'var ' + ($valid) + ';'; + if ($isData) { + out += ' if (schema' + ($lvl) + ' === undefined) ' + ($valid) + ' = true; else if (!Array.isArray(schema' + ($lvl) + ')) ' + ($valid) + ' = false; else {'; + } + out += '' + ($valid) + ' = false;for (var ' + ($i) + '=0; ' + ($i) + '<' + ($vSchema) + '.length; ' + ($i) + '++) if (equal(' + ($data) + ', ' + ($vSchema) + '[' + ($i) + '])) { ' + ($valid) + ' = true; break; }'; + if ($isData) { + out += ' } '; + } + out += ' if (!' + ($valid) + ') { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('enum') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { allowedValues: schema' + ($lvl) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should be equal to one of the allowed values\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' }'; + if ($breakOnError) { + out += ' else { '; + } + return out; +} + + +/***/ }), + +/***/ 286: +/***/ (function(__unusedmodule, exports) { + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. + +function isArray(arg) { + if (Array.isArray) { + return Array.isArray(arg); + } + return objectToString(arg) === '[object Array]'; +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = Buffer.isBuffer; + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +/***/ }), + +/***/ 287: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + + +var url = __webpack_require__(835) +var qs = __webpack_require__(386) +var caseless = __webpack_require__(254) +var uuid = __webpack_require__(826) +var oauth = __webpack_require__(113) +var crypto = __webpack_require__(417) +var Buffer = __webpack_require__(149).Buffer + +function OAuth (request) { + this.request = request + this.params = null +} + +OAuth.prototype.buildParams = function (_oauth, uri, method, query, form, qsLib) { + var oa = {} + for (var i in _oauth) { + oa['oauth_' + i] = _oauth[i] + } + if (!oa.oauth_version) { + oa.oauth_version = '1.0' + } + if (!oa.oauth_timestamp) { + oa.oauth_timestamp = Math.floor(Date.now() / 1000).toString() + } + if (!oa.oauth_nonce) { + oa.oauth_nonce = uuid().replace(/-/g, '') + } + if (!oa.oauth_signature_method) { + oa.oauth_signature_method = 'HMAC-SHA1' + } + + var consumer_secret_or_private_key = oa.oauth_consumer_secret || oa.oauth_private_key // eslint-disable-line camelcase + delete oa.oauth_consumer_secret + delete oa.oauth_private_key + + var token_secret = oa.oauth_token_secret // eslint-disable-line camelcase + delete oa.oauth_token_secret + + var realm = oa.oauth_realm + delete oa.oauth_realm + delete oa.oauth_transport_method + + var baseurl = uri.protocol + '//' + uri.host + uri.pathname + var params = qsLib.parse([].concat(query, form, qsLib.stringify(oa)).join('&')) + + oa.oauth_signature = oauth.sign( + oa.oauth_signature_method, + method, + baseurl, + params, + consumer_secret_or_private_key, // eslint-disable-line camelcase + token_secret // eslint-disable-line camelcase + ) + + if (realm) { + oa.realm = realm + } + + return oa +} + +OAuth.prototype.buildBodyHash = function (_oauth, body) { + if (['HMAC-SHA1', 'RSA-SHA1'].indexOf(_oauth.signature_method || 'HMAC-SHA1') < 0) { + this.request.emit('error', new Error('oauth: ' + _oauth.signature_method + + ' signature_method not supported with body_hash signing.')) + } + + var shasum = crypto.createHash('sha1') + shasum.update(body || '') + var sha1 = shasum.digest('hex') + + return Buffer.from(sha1, 'hex').toString('base64') +} + +OAuth.prototype.concatParams = function (oa, sep, wrap) { + wrap = wrap || '' + + var params = Object.keys(oa).filter(function (i) { + return i !== 'realm' && i !== 'oauth_signature' + }).sort() + + if (oa.realm) { + params.splice(0, 0, 'realm') + } + params.push('oauth_signature') + + return params.map(function (i) { + return i + '=' + wrap + oauth.rfc3986(oa[i]) + wrap + }).join(sep) +} + +OAuth.prototype.onRequest = function (_oauth) { + var self = this + self.params = _oauth + + var uri = self.request.uri || {} + var method = self.request.method || '' + var headers = caseless(self.request.headers) + var body = self.request.body || '' + var qsLib = self.request.qsLib || qs + + var form + var query + var contentType = headers.get('content-type') || '' + var formContentType = 'application/x-www-form-urlencoded' + var transport = _oauth.transport_method || 'header' + + if (contentType.slice(0, formContentType.length) === formContentType) { + contentType = formContentType + form = body + } + if (uri.query) { + query = uri.query + } + if (transport === 'body' && (method !== 'POST' || contentType !== formContentType)) { + self.request.emit('error', new Error('oauth: transport_method of body requires POST ' + + 'and content-type ' + formContentType)) + } + + if (!form && typeof _oauth.body_hash === 'boolean') { + _oauth.body_hash = self.buildBodyHash(_oauth, self.request.body.toString()) + } + + var oa = self.buildParams(_oauth, uri, method, query, form, qsLib) + + switch (transport) { + case 'header': + self.request.setHeader('Authorization', 'OAuth ' + self.concatParams(oa, ',', '"')) + break + + case 'query': + var href = self.request.uri.href += (query ? '&' : '?') + self.concatParams(oa, '&') + self.request.uri = url.parse(href) + self.request.path = self.request.uri.path + break + + case 'body': + self.request.body = (form ? form + '&' : '') + self.concatParams(oa, '&') + break + + default: + self.request.emit('error', new Error('oauth: transport_method invalid')) + } +} + +exports.OAuth = OAuth + + +/***/ }), + +/***/ 290: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2017 Joyent, Inc. + +module.exports = { + DiffieHellman: DiffieHellman, + generateECDSA: generateECDSA, + generateED25519: generateED25519 +}; + +var assert = __webpack_require__(477); +var crypto = __webpack_require__(417); +var Buffer = __webpack_require__(215).Buffer; +var algs = __webpack_require__(98); +var utils = __webpack_require__(270); +var nacl = __webpack_require__(196); + +var Key = __webpack_require__(852); +var PrivateKey = __webpack_require__(502); + +var CRYPTO_HAVE_ECDH = (crypto.createECDH !== undefined); + +var ecdh = __webpack_require__(886); +var ec = __webpack_require__(729); +var jsbn = __webpack_require__(242).BigInteger; + +function DiffieHellman(key) { + utils.assertCompatible(key, Key, [1, 4], 'key'); + this._isPriv = PrivateKey.isPrivateKey(key, [1, 3]); + this._algo = key.type; + this._curve = key.curve; + this._key = key; + if (key.type === 'dsa') { + if (!CRYPTO_HAVE_ECDH) { + throw (new Error('Due to bugs in the node 0.10 ' + + 'crypto API, node 0.12.x or later is required ' + + 'to use DH')); + } + this._dh = crypto.createDiffieHellman( + key.part.p.data, undefined, + key.part.g.data, undefined); + this._p = key.part.p; + this._g = key.part.g; + if (this._isPriv) + this._dh.setPrivateKey(key.part.x.data); + this._dh.setPublicKey(key.part.y.data); + + } else if (key.type === 'ecdsa') { + if (!CRYPTO_HAVE_ECDH) { + this._ecParams = new X9ECParameters(this._curve); + + if (this._isPriv) { + this._priv = new ECPrivate( + this._ecParams, key.part.d.data); + } + return; + } + + var curve = { + 'nistp256': 'prime256v1', + 'nistp384': 'secp384r1', + 'nistp521': 'secp521r1' + }[key.curve]; + this._dh = crypto.createECDH(curve); + if (typeof (this._dh) !== 'object' || + typeof (this._dh.setPrivateKey) !== 'function') { + CRYPTO_HAVE_ECDH = false; + DiffieHellman.call(this, key); + return; + } + if (this._isPriv) + this._dh.setPrivateKey(key.part.d.data); + this._dh.setPublicKey(key.part.Q.data); + + } else if (key.type === 'curve25519') { + if (this._isPriv) { + utils.assertCompatible(key, PrivateKey, [1, 5], 'key'); + this._priv = key.part.k.data; + } + + } else { + throw (new Error('DH not supported for ' + key.type + ' keys')); + } +} + +DiffieHellman.prototype.getPublicKey = function () { + if (this._isPriv) + return (this._key.toPublic()); + return (this._key); +}; + +DiffieHellman.prototype.getPrivateKey = function () { + if (this._isPriv) + return (this._key); + else + return (undefined); +}; +DiffieHellman.prototype.getKey = DiffieHellman.prototype.getPrivateKey; + +DiffieHellman.prototype._keyCheck = function (pk, isPub) { + assert.object(pk, 'key'); + if (!isPub) + utils.assertCompatible(pk, PrivateKey, [1, 3], 'key'); + utils.assertCompatible(pk, Key, [1, 4], 'key'); + + if (pk.type !== this._algo) { + throw (new Error('A ' + pk.type + ' key cannot be used in ' + + this._algo + ' Diffie-Hellman')); + } + + if (pk.curve !== this._curve) { + throw (new Error('A key from the ' + pk.curve + ' curve ' + + 'cannot be used with a ' + this._curve + + ' Diffie-Hellman')); + } + + if (pk.type === 'dsa') { + assert.deepEqual(pk.part.p, this._p, + 'DSA key prime does not match'); + assert.deepEqual(pk.part.g, this._g, + 'DSA key generator does not match'); + } +}; + +DiffieHellman.prototype.setKey = function (pk) { + this._keyCheck(pk); + + if (pk.type === 'dsa') { + this._dh.setPrivateKey(pk.part.x.data); + this._dh.setPublicKey(pk.part.y.data); + + } else if (pk.type === 'ecdsa') { + if (CRYPTO_HAVE_ECDH) { + this._dh.setPrivateKey(pk.part.d.data); + this._dh.setPublicKey(pk.part.Q.data); + } else { + this._priv = new ECPrivate( + this._ecParams, pk.part.d.data); + } + + } else if (pk.type === 'curve25519') { + var k = pk.part.k; + if (!pk.part.k) + k = pk.part.r; + this._priv = k.data; + if (this._priv[0] === 0x00) + this._priv = this._priv.slice(1); + this._priv = this._priv.slice(0, 32); + } + this._key = pk; + this._isPriv = true; +}; +DiffieHellman.prototype.setPrivateKey = DiffieHellman.prototype.setKey; + +DiffieHellman.prototype.computeSecret = function (otherpk) { + this._keyCheck(otherpk, true); + if (!this._isPriv) + throw (new Error('DH exchange has not been initialized with ' + + 'a private key yet')); + + var pub; + if (this._algo === 'dsa') { + return (this._dh.computeSecret( + otherpk.part.y.data)); + + } else if (this._algo === 'ecdsa') { + if (CRYPTO_HAVE_ECDH) { + return (this._dh.computeSecret( + otherpk.part.Q.data)); + } else { + pub = new ECPublic( + this._ecParams, otherpk.part.Q.data); + return (this._priv.deriveSharedSecret(pub)); + } + + } else if (this._algo === 'curve25519') { + pub = otherpk.part.A.data; + while (pub[0] === 0x00 && pub.length > 32) + pub = pub.slice(1); + var priv = this._priv; + assert.strictEqual(pub.length, 32); + assert.strictEqual(priv.length, 32); + + var secret = nacl.box.before(new Uint8Array(pub), + new Uint8Array(priv)); + + return (Buffer.from(secret)); + } + + throw (new Error('Invalid algorithm: ' + this._algo)); +}; + +DiffieHellman.prototype.generateKey = function () { + var parts = []; + var priv, pub; + if (this._algo === 'dsa') { + this._dh.generateKeys(); + + parts.push({name: 'p', data: this._p.data}); + parts.push({name: 'q', data: this._key.part.q.data}); + parts.push({name: 'g', data: this._g.data}); + parts.push({name: 'y', data: this._dh.getPublicKey()}); + parts.push({name: 'x', data: this._dh.getPrivateKey()}); + this._key = new PrivateKey({ + type: 'dsa', + parts: parts + }); + this._isPriv = true; + return (this._key); + + } else if (this._algo === 'ecdsa') { + if (CRYPTO_HAVE_ECDH) { + this._dh.generateKeys(); + + parts.push({name: 'curve', + data: Buffer.from(this._curve)}); + parts.push({name: 'Q', data: this._dh.getPublicKey()}); + parts.push({name: 'd', data: this._dh.getPrivateKey()}); + this._key = new PrivateKey({ + type: 'ecdsa', + curve: this._curve, + parts: parts + }); + this._isPriv = true; + return (this._key); + + } else { + var n = this._ecParams.getN(); + var r = new jsbn(crypto.randomBytes(n.bitLength())); + var n1 = n.subtract(jsbn.ONE); + priv = r.mod(n1).add(jsbn.ONE); + pub = this._ecParams.getG().multiply(priv); + + priv = Buffer.from(priv.toByteArray()); + pub = Buffer.from(this._ecParams.getCurve(). + encodePointHex(pub), 'hex'); + + this._priv = new ECPrivate(this._ecParams, priv); + + parts.push({name: 'curve', + data: Buffer.from(this._curve)}); + parts.push({name: 'Q', data: pub}); + parts.push({name: 'd', data: priv}); + + this._key = new PrivateKey({ + type: 'ecdsa', + curve: this._curve, + parts: parts + }); + this._isPriv = true; + return (this._key); + } + + } else if (this._algo === 'curve25519') { + var pair = nacl.box.keyPair(); + priv = Buffer.from(pair.secretKey); + pub = Buffer.from(pair.publicKey); + priv = Buffer.concat([priv, pub]); + assert.strictEqual(priv.length, 64); + assert.strictEqual(pub.length, 32); + + parts.push({name: 'A', data: pub}); + parts.push({name: 'k', data: priv}); + this._key = new PrivateKey({ + type: 'curve25519', + parts: parts + }); + this._isPriv = true; + return (this._key); + } + + throw (new Error('Invalid algorithm: ' + this._algo)); +}; +DiffieHellman.prototype.generateKeys = DiffieHellman.prototype.generateKey; + +/* These are helpers for using ecc-jsbn (for node 0.10 compatibility). */ + +function X9ECParameters(name) { + var params = algs.curves[name]; + assert.object(params); + + var p = new jsbn(params.p); + var a = new jsbn(params.a); + var b = new jsbn(params.b); + var n = new jsbn(params.n); + var h = jsbn.ONE; + var curve = new ec.ECCurveFp(p, a, b); + var G = curve.decodePointHex(params.G.toString('hex')); + + this.curve = curve; + this.g = G; + this.n = n; + this.h = h; +} +X9ECParameters.prototype.getCurve = function () { return (this.curve); }; +X9ECParameters.prototype.getG = function () { return (this.g); }; +X9ECParameters.prototype.getN = function () { return (this.n); }; +X9ECParameters.prototype.getH = function () { return (this.h); }; + +function ECPublic(params, buffer) { + this._params = params; + if (buffer[0] === 0x00) + buffer = buffer.slice(1); + this._pub = params.getCurve().decodePointHex(buffer.toString('hex')); +} + +function ECPrivate(params, buffer) { + this._params = params; + this._priv = new jsbn(utils.mpNormalize(buffer)); +} +ECPrivate.prototype.deriveSharedSecret = function (pubKey) { + assert.ok(pubKey instanceof ECPublic); + var S = pubKey._pub.multiply(this._priv); + return (Buffer.from(S.getX().toBigInteger().toByteArray())); +}; + +function generateED25519() { + var pair = nacl.sign.keyPair(); + var priv = Buffer.from(pair.secretKey); + var pub = Buffer.from(pair.publicKey); + assert.strictEqual(priv.length, 64); + assert.strictEqual(pub.length, 32); + + var parts = []; + parts.push({name: 'A', data: pub}); + parts.push({name: 'k', data: priv.slice(0, 32)}); + var key = new PrivateKey({ + type: 'ed25519', + parts: parts + }); + return (key); +} + +/* Generates a new ECDSA private key on a given curve. */ +function generateECDSA(curve) { + var parts = []; + var key; + + if (CRYPTO_HAVE_ECDH) { + /* + * Node crypto doesn't expose key generation directly, but the + * ECDH instances can generate keys. It turns out this just + * calls into the OpenSSL generic key generator, and we can + * read its output happily without doing an actual DH. So we + * use that here. + */ + var osCurve = { + 'nistp256': 'prime256v1', + 'nistp384': 'secp384r1', + 'nistp521': 'secp521r1' + }[curve]; + + var dh = crypto.createECDH(osCurve); + dh.generateKeys(); + + parts.push({name: 'curve', + data: Buffer.from(curve)}); + parts.push({name: 'Q', data: dh.getPublicKey()}); + parts.push({name: 'd', data: dh.getPrivateKey()}); + + key = new PrivateKey({ + type: 'ecdsa', + curve: curve, + parts: parts + }); + return (key); + } else { + + var ecParams = new X9ECParameters(curve); + + /* This algorithm taken from FIPS PUB 186-4 (section B.4.1) */ + var n = ecParams.getN(); + /* + * The crypto.randomBytes() function can only give us whole + * bytes, so taking a nod from X9.62, we round up. + */ + var cByteLen = Math.ceil((n.bitLength() + 64) / 8); + var c = new jsbn(crypto.randomBytes(cByteLen)); + + var n1 = n.subtract(jsbn.ONE); + var priv = c.mod(n1).add(jsbn.ONE); + var pub = ecParams.getG().multiply(priv); + + priv = Buffer.from(priv.toByteArray()); + pub = Buffer.from(ecParams.getCurve(). + encodePointHex(pub), 'hex'); + + parts.push({name: 'curve', data: Buffer.from(curve)}); + parts.push({name: 'Q', data: pub}); + parts.push({name: 'd', data: priv}); + + key = new PrivateKey({ + type: 'ecdsa', + curve: curve, + parts: parts + }); + return (key); + } +} + + +/***/ }), + +/***/ 293: +/***/ (function(module) { + +module.exports = require("buffer"); + +/***/ }), + +/***/ 314: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_custom(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $errorKeyword; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $errs = 'errs__' + $lvl; + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + var $rule = this, + $definition = 'definition' + $lvl, + $rDef = $rule.definition, + $closingBraces = ''; + var $compile, $inline, $macro, $ruleValidate, $validateCode; + if ($isData && $rDef.$data) { + $validateCode = 'keywordValidate' + $lvl; + var $validateSchema = $rDef.validateSchema; + out += ' var ' + ($definition) + ' = RULES.custom[\'' + ($keyword) + '\'].definition; var ' + ($validateCode) + ' = ' + ($definition) + '.validate;'; + } else { + $ruleValidate = it.useCustomRule($rule, $schema, it.schema, it); + if (!$ruleValidate) return; + $schemaValue = 'validate.schema' + $schemaPath; + $validateCode = $ruleValidate.code; + $compile = $rDef.compile; + $inline = $rDef.inline; + $macro = $rDef.macro; + } + var $ruleErrs = $validateCode + '.errors', + $i = 'i' + $lvl, + $ruleErr = 'ruleErr' + $lvl, + $asyncKeyword = $rDef.async; + if ($asyncKeyword && !it.async) throw new Error('async keyword in sync schema'); + if (!($inline || $macro)) { + out += '' + ($ruleErrs) + ' = null;'; + } + out += 'var ' + ($errs) + ' = errors;var ' + ($valid) + ';'; + if ($isData && $rDef.$data) { + $closingBraces += '}'; + out += ' if (' + ($schemaValue) + ' === undefined) { ' + ($valid) + ' = true; } else { '; + if ($validateSchema) { + $closingBraces += '}'; + out += ' ' + ($valid) + ' = ' + ($definition) + '.validateSchema(' + ($schemaValue) + '); if (' + ($valid) + ') { '; + } + } + if ($inline) { + if ($rDef.statements) { + out += ' ' + ($ruleValidate.validate) + ' '; + } else { + out += ' ' + ($valid) + ' = ' + ($ruleValidate.validate) + '; '; + } + } else if ($macro) { + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + $it.schema = $ruleValidate.validate; + $it.schemaPath = ''; + var $wasComposite = it.compositeRule; + it.compositeRule = $it.compositeRule = true; + var $code = it.validate($it).replace(/validate\.schema/g, $validateCode); + it.compositeRule = $it.compositeRule = $wasComposite; + out += ' ' + ($code); + } else { + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; + out += ' ' + ($validateCode) + '.call( '; + if (it.opts.passContext) { + out += 'this'; + } else { + out += 'self'; + } + if ($compile || $rDef.schema === false) { + out += ' , ' + ($data) + ' '; + } else { + out += ' , ' + ($schemaValue) + ' , ' + ($data) + ' , validate.schema' + (it.schemaPath) + ' '; + } + out += ' , (dataPath || \'\')'; + if (it.errorPath != '""') { + out += ' + ' + (it.errorPath); + } + var $parentData = $dataLvl ? 'data' + (($dataLvl - 1) || '') : 'parentData', + $parentDataProperty = $dataLvl ? it.dataPathArr[$dataLvl] : 'parentDataProperty'; + out += ' , ' + ($parentData) + ' , ' + ($parentDataProperty) + ' , rootData ) '; + var def_callRuleValidate = out; + out = $$outStack.pop(); + if ($rDef.errors === false) { + out += ' ' + ($valid) + ' = '; + if ($asyncKeyword) { + out += 'await '; + } + out += '' + (def_callRuleValidate) + '; '; + } else { + if ($asyncKeyword) { + $ruleErrs = 'customErrors' + $lvl; + out += ' var ' + ($ruleErrs) + ' = null; try { ' + ($valid) + ' = await ' + (def_callRuleValidate) + '; } catch (e) { ' + ($valid) + ' = false; if (e instanceof ValidationError) ' + ($ruleErrs) + ' = e.errors; else throw e; } '; + } else { + out += ' ' + ($ruleErrs) + ' = null; ' + ($valid) + ' = ' + (def_callRuleValidate) + '; '; + } + } + } + if ($rDef.modifying) { + out += ' if (' + ($parentData) + ') ' + ($data) + ' = ' + ($parentData) + '[' + ($parentDataProperty) + '];'; + } + out += '' + ($closingBraces); + if ($rDef.valid) { + if ($breakOnError) { + out += ' if (true) { '; + } + } else { + out += ' if ( '; + if ($rDef.valid === undefined) { + out += ' !'; + if ($macro) { + out += '' + ($nextValid); + } else { + out += '' + ($valid); + } + } else { + out += ' ' + (!$rDef.valid) + ' '; + } + out += ') { '; + $errorKeyword = $rule.keyword; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ($errorKeyword || 'custom') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { keyword: \'' + ($rule.keyword) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should pass "' + ($rule.keyword) + '" keyword validation\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + var def_customError = out; + out = $$outStack.pop(); + if ($inline) { + if ($rDef.errors) { + if ($rDef.errors != 'full') { + out += ' for (var ' + ($i) + '=' + ($errs) + '; ' + ($i) + '', + $notOp = $isMax ? '>' : '<', + $errorKeyword = undefined; + if ($isDataExcl) { + var $schemaValueExcl = it.util.getData($schemaExcl.$data, $dataLvl, it.dataPathArr), + $exclusive = 'exclusive' + $lvl, + $exclType = 'exclType' + $lvl, + $exclIsNumber = 'exclIsNumber' + $lvl, + $opExpr = 'op' + $lvl, + $opStr = '\' + ' + $opExpr + ' + \''; + out += ' var schemaExcl' + ($lvl) + ' = ' + ($schemaValueExcl) + '; '; + $schemaValueExcl = 'schemaExcl' + $lvl; + out += ' var ' + ($exclusive) + '; var ' + ($exclType) + ' = typeof ' + ($schemaValueExcl) + '; if (' + ($exclType) + ' != \'boolean\' && ' + ($exclType) + ' != \'undefined\' && ' + ($exclType) + ' != \'number\') { '; + var $errorKeyword = $exclusiveKeyword; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ($errorKeyword || '_exclusiveLimit') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} '; + if (it.opts.messages !== false) { + out += ' , message: \'' + ($exclusiveKeyword) + ' should be boolean\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } else if ( '; + if ($isData) { + out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || '; + } + out += ' ' + ($exclType) + ' == \'number\' ? ( (' + ($exclusive) + ' = ' + ($schemaValue) + ' === undefined || ' + ($schemaValueExcl) + ' ' + ($op) + '= ' + ($schemaValue) + ') ? ' + ($data) + ' ' + ($notOp) + '= ' + ($schemaValueExcl) + ' : ' + ($data) + ' ' + ($notOp) + ' ' + ($schemaValue) + ' ) : ( (' + ($exclusive) + ' = ' + ($schemaValueExcl) + ' === true) ? ' + ($data) + ' ' + ($notOp) + '= ' + ($schemaValue) + ' : ' + ($data) + ' ' + ($notOp) + ' ' + ($schemaValue) + ' ) || ' + ($data) + ' !== ' + ($data) + ') { var op' + ($lvl) + ' = ' + ($exclusive) + ' ? \'' + ($op) + '\' : \'' + ($op) + '=\'; '; + if ($schema === undefined) { + $errorKeyword = $exclusiveKeyword; + $errSchemaPath = it.errSchemaPath + '/' + $exclusiveKeyword; + $schemaValue = $schemaValueExcl; + $isData = $isDataExcl; + } + } else { + var $exclIsNumber = typeof $schemaExcl == 'number', + $opStr = $op; + if ($exclIsNumber && $isData) { + var $opExpr = '\'' + $opStr + '\''; + out += ' if ( '; + if ($isData) { + out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || '; + } + out += ' ( ' + ($schemaValue) + ' === undefined || ' + ($schemaExcl) + ' ' + ($op) + '= ' + ($schemaValue) + ' ? ' + ($data) + ' ' + ($notOp) + '= ' + ($schemaExcl) + ' : ' + ($data) + ' ' + ($notOp) + ' ' + ($schemaValue) + ' ) || ' + ($data) + ' !== ' + ($data) + ') { '; + } else { + if ($exclIsNumber && $schema === undefined) { + $exclusive = true; + $errorKeyword = $exclusiveKeyword; + $errSchemaPath = it.errSchemaPath + '/' + $exclusiveKeyword; + $schemaValue = $schemaExcl; + $notOp += '='; + } else { + if ($exclIsNumber) $schemaValue = Math[$isMax ? 'min' : 'max']($schemaExcl, $schema); + if ($schemaExcl === ($exclIsNumber ? $schemaValue : true)) { + $exclusive = true; + $errorKeyword = $exclusiveKeyword; + $errSchemaPath = it.errSchemaPath + '/' + $exclusiveKeyword; + $notOp += '='; + } else { + $exclusive = false; + $opStr += '='; + } + } + var $opExpr = '\'' + $opStr + '\''; + out += ' if ( '; + if ($isData) { + out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || '; + } + out += ' ' + ($data) + ' ' + ($notOp) + ' ' + ($schemaValue) + ' || ' + ($data) + ' !== ' + ($data) + ') { '; + } + } + $errorKeyword = $errorKeyword || $keyword; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ($errorKeyword || '_limit') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { comparison: ' + ($opExpr) + ', limit: ' + ($schemaValue) + ', exclusive: ' + ($exclusive) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should be ' + ($opStr) + ' '; + if ($isData) { + out += '\' + ' + ($schemaValue); + } else { + out += '' + ($schemaValue) + '\''; + } + } + if (it.opts.verbose) { + out += ' , schema: '; + if ($isData) { + out += 'validate.schema' + ($schemaPath); + } else { + out += '' + ($schema); + } + out += ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } '; + if ($breakOnError) { + out += ' else { '; + } + return out; +} + + +/***/ }), + +/***/ 342: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2012 Joyent, Inc. All rights reserved. + +var assert = __webpack_require__(477); +var util = __webpack_require__(669); +var utils = __webpack_require__(909); + + + +///--- Globals + +var HASH_ALGOS = utils.HASH_ALGOS; +var PK_ALGOS = utils.PK_ALGOS; +var HttpSignatureError = utils.HttpSignatureError; +var InvalidAlgorithmError = utils.InvalidAlgorithmError; +var validateAlgorithm = utils.validateAlgorithm; + +var State = { + New: 0, + Params: 1 +}; + +var ParamsState = { + Name: 0, + Quote: 1, + Value: 2, + Comma: 3 +}; + + +///--- Specific Errors + + +function ExpiredRequestError(message) { + HttpSignatureError.call(this, message, ExpiredRequestError); +} +util.inherits(ExpiredRequestError, HttpSignatureError); + + +function InvalidHeaderError(message) { + HttpSignatureError.call(this, message, InvalidHeaderError); +} +util.inherits(InvalidHeaderError, HttpSignatureError); + + +function InvalidParamsError(message) { + HttpSignatureError.call(this, message, InvalidParamsError); +} +util.inherits(InvalidParamsError, HttpSignatureError); + + +function MissingHeaderError(message) { + HttpSignatureError.call(this, message, MissingHeaderError); +} +util.inherits(MissingHeaderError, HttpSignatureError); + +function StrictParsingError(message) { + HttpSignatureError.call(this, message, StrictParsingError); +} +util.inherits(StrictParsingError, HttpSignatureError); + +///--- Exported API + +module.exports = { + + /** + * Parses the 'Authorization' header out of an http.ServerRequest object. + * + * Note that this API will fully validate the Authorization header, and throw + * on any error. It will not however check the signature, or the keyId format + * as those are specific to your environment. You can use the options object + * to pass in extra constraints. + * + * As a response object you can expect this: + * + * { + * "scheme": "Signature", + * "params": { + * "keyId": "foo", + * "algorithm": "rsa-sha256", + * "headers": [ + * "date" or "x-date", + * "digest" + * ], + * "signature": "base64" + * }, + * "signingString": "ready to be passed to crypto.verify()" + * } + * + * @param {Object} request an http.ServerRequest. + * @param {Object} options an optional options object with: + * - clockSkew: allowed clock skew in seconds (default 300). + * - headers: required header names (def: date or x-date) + * - algorithms: algorithms to support (default: all). + * - strict: should enforce latest spec parsing + * (default: false). + * @return {Object} parsed out object (see above). + * @throws {TypeError} on invalid input. + * @throws {InvalidHeaderError} on an invalid Authorization header error. + * @throws {InvalidParamsError} if the params in the scheme are invalid. + * @throws {MissingHeaderError} if the params indicate a header not present, + * either in the request headers from the params, + * or not in the params from a required header + * in options. + * @throws {StrictParsingError} if old attributes are used in strict parsing + * mode. + * @throws {ExpiredRequestError} if the value of date or x-date exceeds skew. + */ + parseRequest: function parseRequest(request, options) { + assert.object(request, 'request'); + assert.object(request.headers, 'request.headers'); + if (options === undefined) { + options = {}; + } + if (options.headers === undefined) { + options.headers = [request.headers['x-date'] ? 'x-date' : 'date']; + } + assert.object(options, 'options'); + assert.arrayOfString(options.headers, 'options.headers'); + assert.optionalFinite(options.clockSkew, 'options.clockSkew'); + + var authzHeaderName = options.authorizationHeaderName || 'authorization'; + + if (!request.headers[authzHeaderName]) { + throw new MissingHeaderError('no ' + authzHeaderName + ' header ' + + 'present in the request'); + } + + options.clockSkew = options.clockSkew || 300; + + + var i = 0; + var state = State.New; + var substate = ParamsState.Name; + var tmpName = ''; + var tmpValue = ''; + + var parsed = { + scheme: '', + params: {}, + signingString: '' + }; + + var authz = request.headers[authzHeaderName]; + for (i = 0; i < authz.length; i++) { + var c = authz.charAt(i); + + switch (Number(state)) { + + case State.New: + if (c !== ' ') parsed.scheme += c; + else state = State.Params; + break; + + case State.Params: + switch (Number(substate)) { + + case ParamsState.Name: + var code = c.charCodeAt(0); + // restricted name of A-Z / a-z + if ((code >= 0x41 && code <= 0x5a) || // A-Z + (code >= 0x61 && code <= 0x7a)) { // a-z + tmpName += c; + } else if (c === '=') { + if (tmpName.length === 0) + throw new InvalidHeaderError('bad param format'); + substate = ParamsState.Quote; + } else { + throw new InvalidHeaderError('bad param format'); + } + break; + + case ParamsState.Quote: + if (c === '"') { + tmpValue = ''; + substate = ParamsState.Value; + } else { + throw new InvalidHeaderError('bad param format'); + } + break; + + case ParamsState.Value: + if (c === '"') { + parsed.params[tmpName] = tmpValue; + substate = ParamsState.Comma; + } else { + tmpValue += c; + } + break; + + case ParamsState.Comma: + if (c === ',') { + tmpName = ''; + substate = ParamsState.Name; + } else { + throw new InvalidHeaderError('bad param format'); + } + break; + + default: + throw new Error('Invalid substate'); + } + break; + + default: + throw new Error('Invalid substate'); + } + + } + + if (!parsed.params.headers || parsed.params.headers === '') { + if (request.headers['x-date']) { + parsed.params.headers = ['x-date']; + } else { + parsed.params.headers = ['date']; + } + } else { + parsed.params.headers = parsed.params.headers.split(' '); + } + + // Minimally validate the parsed object + if (!parsed.scheme || parsed.scheme !== 'Signature') + throw new InvalidHeaderError('scheme was not "Signature"'); + + if (!parsed.params.keyId) + throw new InvalidHeaderError('keyId was not specified'); + + if (!parsed.params.algorithm) + throw new InvalidHeaderError('algorithm was not specified'); + + if (!parsed.params.signature) + throw new InvalidHeaderError('signature was not specified'); + + // Check the algorithm against the official list + parsed.params.algorithm = parsed.params.algorithm.toLowerCase(); + try { + validateAlgorithm(parsed.params.algorithm); + } catch (e) { + if (e instanceof InvalidAlgorithmError) + throw (new InvalidParamsError(parsed.params.algorithm + ' is not ' + + 'supported')); + else + throw (e); + } + + // Build the signingString + for (i = 0; i < parsed.params.headers.length; i++) { + var h = parsed.params.headers[i].toLowerCase(); + parsed.params.headers[i] = h; + + if (h === 'request-line') { + if (!options.strict) { + /* + * We allow headers from the older spec drafts if strict parsing isn't + * specified in options. + */ + parsed.signingString += + request.method + ' ' + request.url + ' HTTP/' + request.httpVersion; + } else { + /* Strict parsing doesn't allow older draft headers. */ + throw (new StrictParsingError('request-line is not a valid header ' + + 'with strict parsing enabled.')); + } + } else if (h === '(request-target)') { + parsed.signingString += + '(request-target): ' + request.method.toLowerCase() + ' ' + + request.url; + } else { + var value = request.headers[h]; + if (value === undefined) + throw new MissingHeaderError(h + ' was not in the request'); + parsed.signingString += h + ': ' + value; + } + + if ((i + 1) < parsed.params.headers.length) + parsed.signingString += '\n'; + } + + // Check against the constraints + var date; + if (request.headers.date || request.headers['x-date']) { + if (request.headers['x-date']) { + date = new Date(request.headers['x-date']); + } else { + date = new Date(request.headers.date); + } + var now = new Date(); + var skew = Math.abs(now.getTime() - date.getTime()); + + if (skew > options.clockSkew * 1000) { + throw new ExpiredRequestError('clock skew of ' + + (skew / 1000) + + 's was greater than ' + + options.clockSkew + 's'); + } + } + + options.headers.forEach(function (hdr) { + // Remember that we already checked any headers in the params + // were in the request, so if this passes we're good. + if (parsed.params.headers.indexOf(hdr.toLowerCase()) < 0) + throw new MissingHeaderError(hdr + ' was not a signed header'); + }); + + if (options.algorithms) { + if (options.algorithms.indexOf(parsed.params.algorithm) === -1) + throw new InvalidParamsError(parsed.params.algorithm + + ' is not a supported algorithm'); + } + + parsed.algorithm = parsed.params.algorithm.toUpperCase(); + parsed.keyId = parsed.params.keyId; + return parsed; + } + +}; + + +/***/ }), + +/***/ 343: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_properties(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + var $key = 'key' + $lvl, + $idx = 'idx' + $lvl, + $dataNxt = $it.dataLevel = it.dataLevel + 1, + $nextData = 'data' + $dataNxt, + $dataProperties = 'dataProperties' + $lvl; + var $schemaKeys = Object.keys($schema || {}), + $pProperties = it.schema.patternProperties || {}, + $pPropertyKeys = Object.keys($pProperties), + $aProperties = it.schema.additionalProperties, + $someProperties = $schemaKeys.length || $pPropertyKeys.length, + $noAdditional = $aProperties === false, + $additionalIsSchema = typeof $aProperties == 'object' && Object.keys($aProperties).length, + $removeAdditional = it.opts.removeAdditional, + $checkAdditional = $noAdditional || $additionalIsSchema || $removeAdditional, + $ownProperties = it.opts.ownProperties, + $currentBaseId = it.baseId; + var $required = it.schema.required; + if ($required && !(it.opts.$data && $required.$data) && $required.length < it.opts.loopRequired) var $requiredHash = it.util.toHash($required); + out += 'var ' + ($errs) + ' = errors;var ' + ($nextValid) + ' = true;'; + if ($ownProperties) { + out += ' var ' + ($dataProperties) + ' = undefined;'; + } + if ($checkAdditional) { + if ($ownProperties) { + out += ' ' + ($dataProperties) + ' = ' + ($dataProperties) + ' || Object.keys(' + ($data) + '); for (var ' + ($idx) + '=0; ' + ($idx) + '<' + ($dataProperties) + '.length; ' + ($idx) + '++) { var ' + ($key) + ' = ' + ($dataProperties) + '[' + ($idx) + ']; '; + } else { + out += ' for (var ' + ($key) + ' in ' + ($data) + ') { '; + } + if ($someProperties) { + out += ' var isAdditional' + ($lvl) + ' = !(false '; + if ($schemaKeys.length) { + if ($schemaKeys.length > 8) { + out += ' || validate.schema' + ($schemaPath) + '.hasOwnProperty(' + ($key) + ') '; + } else { + var arr1 = $schemaKeys; + if (arr1) { + var $propertyKey, i1 = -1, + l1 = arr1.length - 1; + while (i1 < l1) { + $propertyKey = arr1[i1 += 1]; + out += ' || ' + ($key) + ' == ' + (it.util.toQuotedString($propertyKey)) + ' '; + } + } + } + } + if ($pPropertyKeys.length) { + var arr2 = $pPropertyKeys; + if (arr2) { + var $pProperty, $i = -1, + l2 = arr2.length - 1; + while ($i < l2) { + $pProperty = arr2[$i += 1]; + out += ' || ' + (it.usePattern($pProperty)) + '.test(' + ($key) + ') '; + } + } + } + out += ' ); if (isAdditional' + ($lvl) + ') { '; + } + if ($removeAdditional == 'all') { + out += ' delete ' + ($data) + '[' + ($key) + ']; '; + } else { + var $currentErrorPath = it.errorPath; + var $additionalProperty = '\' + ' + $key + ' + \''; + if (it.opts._errorDataPathProperty) { + it.errorPath = it.util.getPathExpr(it.errorPath, $key, it.opts.jsonPointers); + } + if ($noAdditional) { + if ($removeAdditional) { + out += ' delete ' + ($data) + '[' + ($key) + ']; '; + } else { + out += ' ' + ($nextValid) + ' = false; '; + var $currErrSchemaPath = $errSchemaPath; + $errSchemaPath = it.errSchemaPath + '/additionalProperties'; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('additionalProperties') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { additionalProperty: \'' + ($additionalProperty) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \''; + if (it.opts._errorDataPathProperty) { + out += 'is an invalid additional property'; + } else { + out += 'should NOT have additional properties'; + } + out += '\' '; + } + if (it.opts.verbose) { + out += ' , schema: false , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + $errSchemaPath = $currErrSchemaPath; + if ($breakOnError) { + out += ' break; '; + } + } + } else if ($additionalIsSchema) { + if ($removeAdditional == 'failing') { + out += ' var ' + ($errs) + ' = errors; '; + var $wasComposite = it.compositeRule; + it.compositeRule = $it.compositeRule = true; + $it.schema = $aProperties; + $it.schemaPath = it.schemaPath + '.additionalProperties'; + $it.errSchemaPath = it.errSchemaPath + '/additionalProperties'; + $it.errorPath = it.opts._errorDataPathProperty ? it.errorPath : it.util.getPathExpr(it.errorPath, $key, it.opts.jsonPointers); + var $passData = $data + '[' + $key + ']'; + $it.dataPathArr[$dataNxt] = $key; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + out += ' if (!' + ($nextValid) + ') { errors = ' + ($errs) + '; if (validate.errors !== null) { if (errors) validate.errors.length = errors; else validate.errors = null; } delete ' + ($data) + '[' + ($key) + ']; } '; + it.compositeRule = $it.compositeRule = $wasComposite; + } else { + $it.schema = $aProperties; + $it.schemaPath = it.schemaPath + '.additionalProperties'; + $it.errSchemaPath = it.errSchemaPath + '/additionalProperties'; + $it.errorPath = it.opts._errorDataPathProperty ? it.errorPath : it.util.getPathExpr(it.errorPath, $key, it.opts.jsonPointers); + var $passData = $data + '[' + $key + ']'; + $it.dataPathArr[$dataNxt] = $key; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + if ($breakOnError) { + out += ' if (!' + ($nextValid) + ') break; '; + } + } + } + it.errorPath = $currentErrorPath; + } + if ($someProperties) { + out += ' } '; + } + out += ' } '; + if ($breakOnError) { + out += ' if (' + ($nextValid) + ') { '; + $closingBraces += '}'; + } + } + var $useDefaults = it.opts.useDefaults && !it.compositeRule; + if ($schemaKeys.length) { + var arr3 = $schemaKeys; + if (arr3) { + var $propertyKey, i3 = -1, + l3 = arr3.length - 1; + while (i3 < l3) { + $propertyKey = arr3[i3 += 1]; + var $sch = $schema[$propertyKey]; + if ((it.opts.strictKeywords ? typeof $sch == 'object' && Object.keys($sch).length > 0 : it.util.schemaHasRules($sch, it.RULES.all))) { + var $prop = it.util.getProperty($propertyKey), + $passData = $data + $prop, + $hasDefault = $useDefaults && $sch.default !== undefined; + $it.schema = $sch; + $it.schemaPath = $schemaPath + $prop; + $it.errSchemaPath = $errSchemaPath + '/' + it.util.escapeFragment($propertyKey); + $it.errorPath = it.util.getPath(it.errorPath, $propertyKey, it.opts.jsonPointers); + $it.dataPathArr[$dataNxt] = it.util.toQuotedString($propertyKey); + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + $code = it.util.varReplace($code, $nextData, $passData); + var $useData = $passData; + } else { + var $useData = $nextData; + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; '; + } + if ($hasDefault) { + out += ' ' + ($code) + ' '; + } else { + if ($requiredHash && $requiredHash[$propertyKey]) { + out += ' if ( ' + ($useData) + ' === undefined '; + if ($ownProperties) { + out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') '; + } + out += ') { ' + ($nextValid) + ' = false; '; + var $currentErrorPath = it.errorPath, + $currErrSchemaPath = $errSchemaPath, + $missingProperty = it.util.escapeQuotes($propertyKey); + if (it.opts._errorDataPathProperty) { + it.errorPath = it.util.getPath($currentErrorPath, $propertyKey, it.opts.jsonPointers); + } + $errSchemaPath = it.errSchemaPath + '/required'; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \''; + if (it.opts._errorDataPathProperty) { + out += 'is a required property'; + } else { + out += 'should have required property \\\'' + ($missingProperty) + '\\\''; + } + out += '\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + $errSchemaPath = $currErrSchemaPath; + it.errorPath = $currentErrorPath; + out += ' } else { '; + } else { + if ($breakOnError) { + out += ' if ( ' + ($useData) + ' === undefined '; + if ($ownProperties) { + out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') '; + } + out += ') { ' + ($nextValid) + ' = true; } else { '; + } else { + out += ' if (' + ($useData) + ' !== undefined '; + if ($ownProperties) { + out += ' && Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') '; + } + out += ' ) { '; + } + } + out += ' ' + ($code) + ' } '; + } + } + if ($breakOnError) { + out += ' if (' + ($nextValid) + ') { '; + $closingBraces += '}'; + } + } + } + } + if ($pPropertyKeys.length) { + var arr4 = $pPropertyKeys; + if (arr4) { + var $pProperty, i4 = -1, + l4 = arr4.length - 1; + while (i4 < l4) { + $pProperty = arr4[i4 += 1]; + var $sch = $pProperties[$pProperty]; + if ((it.opts.strictKeywords ? typeof $sch == 'object' && Object.keys($sch).length > 0 : it.util.schemaHasRules($sch, it.RULES.all))) { + $it.schema = $sch; + $it.schemaPath = it.schemaPath + '.patternProperties' + it.util.getProperty($pProperty); + $it.errSchemaPath = it.errSchemaPath + '/patternProperties/' + it.util.escapeFragment($pProperty); + if ($ownProperties) { + out += ' ' + ($dataProperties) + ' = ' + ($dataProperties) + ' || Object.keys(' + ($data) + '); for (var ' + ($idx) + '=0; ' + ($idx) + '<' + ($dataProperties) + '.length; ' + ($idx) + '++) { var ' + ($key) + ' = ' + ($dataProperties) + '[' + ($idx) + ']; '; + } else { + out += ' for (var ' + ($key) + ' in ' + ($data) + ') { '; + } + out += ' if (' + (it.usePattern($pProperty)) + '.test(' + ($key) + ')) { '; + $it.errorPath = it.util.getPathExpr(it.errorPath, $key, it.opts.jsonPointers); + var $passData = $data + '[' + $key + ']'; + $it.dataPathArr[$dataNxt] = $key; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + if ($breakOnError) { + out += ' if (!' + ($nextValid) + ') break; '; + } + out += ' } '; + if ($breakOnError) { + out += ' else ' + ($nextValid) + ' = true; '; + } + out += ' } '; + if ($breakOnError) { + out += ' if (' + ($nextValid) + ') { '; + $closingBraces += '}'; + } + } + } + } + } + if ($breakOnError) { + out += ' ' + ($closingBraces) + ' if (' + ($errs) + ' == errors) {'; + } + out = it.util.cleanUpCode(out); + return out; +} + + +/***/ }), + +/***/ 348: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +/* + * lib/jsprim.js: utilities for primitive JavaScript types + */ + +var mod_assert = __webpack_require__(477); +var mod_util = __webpack_require__(669); + +var mod_extsprintf = __webpack_require__(697); +var mod_verror = __webpack_require__(956); +var mod_jsonschema = __webpack_require__(703); + +/* + * Public interface + */ +exports.deepCopy = deepCopy; +exports.deepEqual = deepEqual; +exports.isEmpty = isEmpty; +exports.hasKey = hasKey; +exports.forEachKey = forEachKey; +exports.pluck = pluck; +exports.flattenObject = flattenObject; +exports.flattenIter = flattenIter; +exports.validateJsonObject = validateJsonObjectJS; +exports.validateJsonObjectJS = validateJsonObjectJS; +exports.randElt = randElt; +exports.extraProperties = extraProperties; +exports.mergeObjects = mergeObjects; + +exports.startsWith = startsWith; +exports.endsWith = endsWith; + +exports.parseInteger = parseInteger; + +exports.iso8601 = iso8601; +exports.rfc1123 = rfc1123; +exports.parseDateTime = parseDateTime; + +exports.hrtimediff = hrtimeDiff; +exports.hrtimeDiff = hrtimeDiff; +exports.hrtimeAccum = hrtimeAccum; +exports.hrtimeAdd = hrtimeAdd; +exports.hrtimeNanosec = hrtimeNanosec; +exports.hrtimeMicrosec = hrtimeMicrosec; +exports.hrtimeMillisec = hrtimeMillisec; + + +/* + * Deep copy an acyclic *basic* Javascript object. This only handles basic + * scalars (strings, numbers, booleans) and arbitrarily deep arrays and objects + * containing these. This does *not* handle instances of other classes. + */ +function deepCopy(obj) +{ + var ret, key; + var marker = '__deepCopy'; + + if (obj && obj[marker]) + throw (new Error('attempted deep copy of cyclic object')); + + if (obj && obj.constructor == Object) { + ret = {}; + obj[marker] = true; + + for (key in obj) { + if (key == marker) + continue; + + ret[key] = deepCopy(obj[key]); + } + + delete (obj[marker]); + return (ret); + } + + if (obj && obj.constructor == Array) { + ret = []; + obj[marker] = true; + + for (key = 0; key < obj.length; key++) + ret.push(deepCopy(obj[key])); + + delete (obj[marker]); + return (ret); + } + + /* + * It must be a primitive type -- just return it. + */ + return (obj); +} + +function deepEqual(obj1, obj2) +{ + if (typeof (obj1) != typeof (obj2)) + return (false); + + if (obj1 === null || obj2 === null || typeof (obj1) != 'object') + return (obj1 === obj2); + + if (obj1.constructor != obj2.constructor) + return (false); + + var k; + for (k in obj1) { + if (!obj2.hasOwnProperty(k)) + return (false); + + if (!deepEqual(obj1[k], obj2[k])) + return (false); + } + + for (k in obj2) { + if (!obj1.hasOwnProperty(k)) + return (false); + } + + return (true); +} + +function isEmpty(obj) +{ + var key; + for (key in obj) + return (false); + return (true); +} + +function hasKey(obj, key) +{ + mod_assert.equal(typeof (key), 'string'); + return (Object.prototype.hasOwnProperty.call(obj, key)); +} + +function forEachKey(obj, callback) +{ + for (var key in obj) { + if (hasKey(obj, key)) { + callback(key, obj[key]); + } + } +} + +function pluck(obj, key) +{ + mod_assert.equal(typeof (key), 'string'); + return (pluckv(obj, key)); +} + +function pluckv(obj, key) +{ + if (obj === null || typeof (obj) !== 'object') + return (undefined); + + if (obj.hasOwnProperty(key)) + return (obj[key]); + + var i = key.indexOf('.'); + if (i == -1) + return (undefined); + + var key1 = key.substr(0, i); + if (!obj.hasOwnProperty(key1)) + return (undefined); + + return (pluckv(obj[key1], key.substr(i + 1))); +} + +/* + * Invoke callback(row) for each entry in the array that would be returned by + * flattenObject(data, depth). This is just like flattenObject(data, + * depth).forEach(callback), except that the intermediate array is never + * created. + */ +function flattenIter(data, depth, callback) +{ + doFlattenIter(data, depth, [], callback); +} + +function doFlattenIter(data, depth, accum, callback) +{ + var each; + var key; + + if (depth === 0) { + each = accum.slice(0); + each.push(data); + callback(each); + return; + } + + mod_assert.ok(data !== null); + mod_assert.equal(typeof (data), 'object'); + mod_assert.equal(typeof (depth), 'number'); + mod_assert.ok(depth >= 0); + + for (key in data) { + each = accum.slice(0); + each.push(key); + doFlattenIter(data[key], depth - 1, each, callback); + } +} + +function flattenObject(data, depth) +{ + if (depth === 0) + return ([ data ]); + + mod_assert.ok(data !== null); + mod_assert.equal(typeof (data), 'object'); + mod_assert.equal(typeof (depth), 'number'); + mod_assert.ok(depth >= 0); + + var rv = []; + var key; + + for (key in data) { + flattenObject(data[key], depth - 1).forEach(function (p) { + rv.push([ key ].concat(p)); + }); + } + + return (rv); +} + +function startsWith(str, prefix) +{ + return (str.substr(0, prefix.length) == prefix); +} + +function endsWith(str, suffix) +{ + return (str.substr( + str.length - suffix.length, suffix.length) == suffix); +} + +function iso8601(d) +{ + if (typeof (d) == 'number') + d = new Date(d); + mod_assert.ok(d.constructor === Date); + return (mod_extsprintf.sprintf('%4d-%02d-%02dT%02d:%02d:%02d.%03dZ', + d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate(), + d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), + d.getUTCMilliseconds())); +} + +var RFC1123_MONTHS = [ + 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; +var RFC1123_DAYS = [ + 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + +function rfc1123(date) { + return (mod_extsprintf.sprintf('%s, %02d %s %04d %02d:%02d:%02d GMT', + RFC1123_DAYS[date.getUTCDay()], date.getUTCDate(), + RFC1123_MONTHS[date.getUTCMonth()], date.getUTCFullYear(), + date.getUTCHours(), date.getUTCMinutes(), + date.getUTCSeconds())); +} + +/* + * Parses a date expressed as a string, as either a number of milliseconds since + * the epoch or any string format that Date accepts, giving preference to the + * former where these two sets overlap (e.g., small numbers). + */ +function parseDateTime(str) +{ + /* + * This is irritatingly implicit, but significantly more concise than + * alternatives. The "+str" will convert a string containing only a + * number directly to a Number, or NaN for other strings. Thus, if the + * conversion succeeds, we use it (this is the milliseconds-since-epoch + * case). Otherwise, we pass the string directly to the Date + * constructor to parse. + */ + var numeric = +str; + if (!isNaN(numeric)) { + return (new Date(numeric)); + } else { + return (new Date(str)); + } +} + + +/* + * Number.*_SAFE_INTEGER isn't present before node v0.12, so we hardcode + * the ES6 definitions here, while allowing for them to someday be higher. + */ +var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; +var MIN_SAFE_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991; + + +/* + * Default options for parseInteger(). + */ +var PI_DEFAULTS = { + base: 10, + allowSign: true, + allowPrefix: false, + allowTrailing: false, + allowImprecise: false, + trimWhitespace: false, + leadingZeroIsOctal: false +}; + +var CP_0 = 0x30; +var CP_9 = 0x39; + +var CP_A = 0x41; +var CP_B = 0x42; +var CP_O = 0x4f; +var CP_T = 0x54; +var CP_X = 0x58; +var CP_Z = 0x5a; + +var CP_a = 0x61; +var CP_b = 0x62; +var CP_o = 0x6f; +var CP_t = 0x74; +var CP_x = 0x78; +var CP_z = 0x7a; + +var PI_CONV_DEC = 0x30; +var PI_CONV_UC = 0x37; +var PI_CONV_LC = 0x57; + + +/* + * A stricter version of parseInt() that provides options for changing what + * is an acceptable string (for example, disallowing trailing characters). + */ +function parseInteger(str, uopts) +{ + mod_assert.string(str, 'str'); + mod_assert.optionalObject(uopts, 'options'); + + var baseOverride = false; + var options = PI_DEFAULTS; + + if (uopts) { + baseOverride = hasKey(uopts, 'base'); + options = mergeObjects(options, uopts); + mod_assert.number(options.base, 'options.base'); + mod_assert.ok(options.base >= 2, 'options.base >= 2'); + mod_assert.ok(options.base <= 36, 'options.base <= 36'); + mod_assert.bool(options.allowSign, 'options.allowSign'); + mod_assert.bool(options.allowPrefix, 'options.allowPrefix'); + mod_assert.bool(options.allowTrailing, + 'options.allowTrailing'); + mod_assert.bool(options.allowImprecise, + 'options.allowImprecise'); + mod_assert.bool(options.trimWhitespace, + 'options.trimWhitespace'); + mod_assert.bool(options.leadingZeroIsOctal, + 'options.leadingZeroIsOctal'); + + if (options.leadingZeroIsOctal) { + mod_assert.ok(!baseOverride, + '"base" and "leadingZeroIsOctal" are ' + + 'mutually exclusive'); + } + } + + var c; + var pbase = -1; + var base = options.base; + var start; + var mult = 1; + var value = 0; + var idx = 0; + var len = str.length; + + /* Trim any whitespace on the left side. */ + if (options.trimWhitespace) { + while (idx < len && isSpace(str.charCodeAt(idx))) { + ++idx; + } + } + + /* Check the number for a leading sign. */ + if (options.allowSign) { + if (str[idx] === '-') { + idx += 1; + mult = -1; + } else if (str[idx] === '+') { + idx += 1; + } + } + + /* Parse the base-indicating prefix if there is one. */ + if (str[idx] === '0') { + if (options.allowPrefix) { + pbase = prefixToBase(str.charCodeAt(idx + 1)); + if (pbase !== -1 && (!baseOverride || pbase === base)) { + base = pbase; + idx += 2; + } + } + + if (pbase === -1 && options.leadingZeroIsOctal) { + base = 8; + } + } + + /* Parse the actual digits. */ + for (start = idx; idx < len; ++idx) { + c = translateDigit(str.charCodeAt(idx)); + if (c !== -1 && c < base) { + value *= base; + value += c; + } else { + break; + } + } + + /* If we didn't parse any digits, we have an invalid number. */ + if (start === idx) { + return (new Error('invalid number: ' + JSON.stringify(str))); + } + + /* Trim any whitespace on the right side. */ + if (options.trimWhitespace) { + while (idx < len && isSpace(str.charCodeAt(idx))) { + ++idx; + } + } + + /* Check for trailing characters. */ + if (idx < len && !options.allowTrailing) { + return (new Error('trailing characters after number: ' + + JSON.stringify(str.slice(idx)))); + } + + /* If our value is 0, we return now, to avoid returning -0. */ + if (value === 0) { + return (0); + } + + /* Calculate our final value. */ + var result = value * mult; + + /* + * If the string represents a value that cannot be precisely represented + * by JavaScript, then we want to check that: + * + * - We never increased the value past MAX_SAFE_INTEGER + * - We don't make the result negative and below MIN_SAFE_INTEGER + * + * Because we only ever increment the value during parsing, there's no + * chance of moving past MAX_SAFE_INTEGER and then dropping below it + * again, losing precision in the process. This means that we only need + * to do our checks here, at the end. + */ + if (!options.allowImprecise && + (value > MAX_SAFE_INTEGER || result < MIN_SAFE_INTEGER)) { + return (new Error('number is outside of the supported range: ' + + JSON.stringify(str.slice(start, idx)))); + } + + return (result); +} + + +/* + * Interpret a character code as a base-36 digit. + */ +function translateDigit(d) +{ + if (d >= CP_0 && d <= CP_9) { + /* '0' to '9' -> 0 to 9 */ + return (d - PI_CONV_DEC); + } else if (d >= CP_A && d <= CP_Z) { + /* 'A' - 'Z' -> 10 to 35 */ + return (d - PI_CONV_UC); + } else if (d >= CP_a && d <= CP_z) { + /* 'a' - 'z' -> 10 to 35 */ + return (d - PI_CONV_LC); + } else { + /* Invalid character code */ + return (-1); + } +} + + +/* + * Test if a value matches the ECMAScript definition of trimmable whitespace. + */ +function isSpace(c) +{ + return (c === 0x20) || + (c >= 0x0009 && c <= 0x000d) || + (c === 0x00a0) || + (c === 0x1680) || + (c === 0x180e) || + (c >= 0x2000 && c <= 0x200a) || + (c === 0x2028) || + (c === 0x2029) || + (c === 0x202f) || + (c === 0x205f) || + (c === 0x3000) || + (c === 0xfeff); +} + + +/* + * Determine which base a character indicates (e.g., 'x' indicates hex). + */ +function prefixToBase(c) +{ + if (c === CP_b || c === CP_B) { + /* 0b/0B (binary) */ + return (2); + } else if (c === CP_o || c === CP_O) { + /* 0o/0O (octal) */ + return (8); + } else if (c === CP_t || c === CP_T) { + /* 0t/0T (decimal) */ + return (10); + } else if (c === CP_x || c === CP_X) { + /* 0x/0X (hexadecimal) */ + return (16); + } else { + /* Not a meaningful character */ + return (-1); + } +} + + +function validateJsonObjectJS(schema, input) +{ + var report = mod_jsonschema.validate(input, schema); + + if (report.errors.length === 0) + return (null); + + /* Currently, we only do anything useful with the first error. */ + var error = report.errors[0]; + + /* The failed property is given by a URI with an irrelevant prefix. */ + var propname = error['property']; + var reason = error['message'].toLowerCase(); + var i, j; + + /* + * There's at least one case where the property error message is + * confusing at best. We work around this here. + */ + if ((i = reason.indexOf('the property ')) != -1 && + (j = reason.indexOf(' is not defined in the schema and the ' + + 'schema does not allow additional properties')) != -1) { + i += 'the property '.length; + if (propname === '') + propname = reason.substr(i, j - i); + else + propname = propname + '.' + reason.substr(i, j - i); + + reason = 'unsupported property'; + } + + var rv = new mod_verror.VError('property "%s": %s', propname, reason); + rv.jsv_details = error; + return (rv); +} + +function randElt(arr) +{ + mod_assert.ok(Array.isArray(arr) && arr.length > 0, + 'randElt argument must be a non-empty array'); + + return (arr[Math.floor(Math.random() * arr.length)]); +} + +function assertHrtime(a) +{ + mod_assert.ok(a[0] >= 0 && a[1] >= 0, + 'negative numbers not allowed in hrtimes'); + mod_assert.ok(a[1] < 1e9, 'nanoseconds column overflow'); +} + +/* + * Compute the time elapsed between hrtime readings A and B, where A is later + * than B. hrtime readings come from Node's process.hrtime(). There is no + * defined way to represent negative deltas, so it's illegal to diff B from A + * where the time denoted by B is later than the time denoted by A. If this + * becomes valuable, we can define a representation and extend the + * implementation to support it. + */ +function hrtimeDiff(a, b) +{ + assertHrtime(a); + assertHrtime(b); + mod_assert.ok(a[0] > b[0] || (a[0] == b[0] && a[1] >= b[1]), + 'negative differences not allowed'); + + var rv = [ a[0] - b[0], 0 ]; + + if (a[1] >= b[1]) { + rv[1] = a[1] - b[1]; + } else { + rv[0]--; + rv[1] = 1e9 - (b[1] - a[1]); + } + + return (rv); +} + +/* + * Convert a hrtime reading from the array format returned by Node's + * process.hrtime() into a scalar number of nanoseconds. + */ +function hrtimeNanosec(a) +{ + assertHrtime(a); + + return (Math.floor(a[0] * 1e9 + a[1])); +} + +/* + * Convert a hrtime reading from the array format returned by Node's + * process.hrtime() into a scalar number of microseconds. + */ +function hrtimeMicrosec(a) +{ + assertHrtime(a); + + return (Math.floor(a[0] * 1e6 + a[1] / 1e3)); +} + +/* + * Convert a hrtime reading from the array format returned by Node's + * process.hrtime() into a scalar number of milliseconds. + */ +function hrtimeMillisec(a) +{ + assertHrtime(a); + + return (Math.floor(a[0] * 1e3 + a[1] / 1e6)); +} + +/* + * Add two hrtime readings A and B, overwriting A with the result of the + * addition. This function is useful for accumulating several hrtime intervals + * into a counter. Returns A. + */ +function hrtimeAccum(a, b) +{ + assertHrtime(a); + assertHrtime(b); + + /* + * Accumulate the nanosecond component. + */ + a[1] += b[1]; + if (a[1] >= 1e9) { + /* + * The nanosecond component overflowed, so carry to the seconds + * field. + */ + a[0]++; + a[1] -= 1e9; + } + + /* + * Accumulate the seconds component. + */ + a[0] += b[0]; + + return (a); +} + +/* + * Add two hrtime readings A and B, returning the result as a new hrtime array. + * Does not modify either input argument. + */ +function hrtimeAdd(a, b) +{ + assertHrtime(a); + + var rv = [ a[0], a[1] ]; + + return (hrtimeAccum(rv, b)); +} + + +/* + * Check an object for unexpected properties. Accepts the object to check, and + * an array of allowed property names (strings). Returns an array of key names + * that were found on the object, but did not appear in the list of allowed + * properties. If no properties were found, the returned array will be of + * zero length. + */ +function extraProperties(obj, allowed) +{ + mod_assert.ok(typeof (obj) === 'object' && obj !== null, + 'obj argument must be a non-null object'); + mod_assert.ok(Array.isArray(allowed), + 'allowed argument must be an array of strings'); + for (var i = 0; i < allowed.length; i++) { + mod_assert.ok(typeof (allowed[i]) === 'string', + 'allowed argument must be an array of strings'); + } + + return (Object.keys(obj).filter(function (key) { + return (allowed.indexOf(key) === -1); + })); +} + +/* + * Given three sets of properties "provided" (may be undefined), "overrides" + * (required), and "defaults" (may be undefined), construct an object containing + * the union of these sets with "overrides" overriding "provided", and + * "provided" overriding "defaults". None of the input objects are modified. + */ +function mergeObjects(provided, overrides, defaults) +{ + var rv, k; + + rv = {}; + if (defaults) { + for (k in defaults) + rv[k] = defaults[k]; + } + + if (provided) { + for (k in provided) + rv[k] = provided[k]; + } + + if (overrides) { + for (k in overrides) + rv[k] = overrides[k]; + } + + return (rv); +} + + +/***/ }), + +/***/ 349: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; +/*! + * Copyright (c) 2015, Salesforce.com, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Salesforce.com nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +var Store = __webpack_require__(627).Store; +var permuteDomain = __webpack_require__(383).permuteDomain; +var pathMatch = __webpack_require__(54).pathMatch; +var util = __webpack_require__(669); + +function MemoryCookieStore() { + Store.call(this); + this.idx = {}; +} +util.inherits(MemoryCookieStore, Store); +exports.MemoryCookieStore = MemoryCookieStore; +MemoryCookieStore.prototype.idx = null; + +// Since it's just a struct in RAM, this Store is synchronous +MemoryCookieStore.prototype.synchronous = true; + +// force a default depth: +MemoryCookieStore.prototype.inspect = function() { + return "{ idx: "+util.inspect(this.idx, false, 2)+' }'; +}; + +// Use the new custom inspection symbol to add the custom inspect function if +// available. +if (util.inspect.custom) { + MemoryCookieStore.prototype[util.inspect.custom] = MemoryCookieStore.prototype.inspect; +} + +MemoryCookieStore.prototype.findCookie = function(domain, path, key, cb) { + if (!this.idx[domain]) { + return cb(null,undefined); + } + if (!this.idx[domain][path]) { + return cb(null,undefined); + } + return cb(null,this.idx[domain][path][key]||null); +}; + +MemoryCookieStore.prototype.findCookies = function(domain, path, cb) { + var results = []; + if (!domain) { + return cb(null,[]); + } + + var pathMatcher; + if (!path) { + // null means "all paths" + pathMatcher = function matchAll(domainIndex) { + for (var curPath in domainIndex) { + var pathIndex = domainIndex[curPath]; + for (var key in pathIndex) { + results.push(pathIndex[key]); + } + } + }; + + } else { + pathMatcher = function matchRFC(domainIndex) { + //NOTE: we should use path-match algorithm from S5.1.4 here + //(see : https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/canonical_cookie.cc#L299) + Object.keys(domainIndex).forEach(function (cookiePath) { + if (pathMatch(path, cookiePath)) { + var pathIndex = domainIndex[cookiePath]; + + for (var key in pathIndex) { + results.push(pathIndex[key]); + } + } + }); + }; + } + + var domains = permuteDomain(domain) || [domain]; + var idx = this.idx; + domains.forEach(function(curDomain) { + var domainIndex = idx[curDomain]; + if (!domainIndex) { + return; + } + pathMatcher(domainIndex); + }); + + cb(null,results); +}; + +MemoryCookieStore.prototype.putCookie = function(cookie, cb) { + if (!this.idx[cookie.domain]) { + this.idx[cookie.domain] = {}; + } + if (!this.idx[cookie.domain][cookie.path]) { + this.idx[cookie.domain][cookie.path] = {}; + } + this.idx[cookie.domain][cookie.path][cookie.key] = cookie; + cb(null); +}; + +MemoryCookieStore.prototype.updateCookie = function(oldCookie, newCookie, cb) { + // updateCookie() may avoid updating cookies that are identical. For example, + // lastAccessed may not be important to some stores and an equality + // comparison could exclude that field. + this.putCookie(newCookie,cb); +}; + +MemoryCookieStore.prototype.removeCookie = function(domain, path, key, cb) { + if (this.idx[domain] && this.idx[domain][path] && this.idx[domain][path][key]) { + delete this.idx[domain][path][key]; + } + cb(null); +}; + +MemoryCookieStore.prototype.removeCookies = function(domain, path, cb) { + if (this.idx[domain]) { + if (path) { + delete this.idx[domain][path]; + } else { + delete this.idx[domain]; + } + } + return cb(null); +}; + +MemoryCookieStore.prototype.removeAllCookies = function(cb) { + this.idx = {}; + return cb(null); +} + +MemoryCookieStore.prototype.getAllCookies = function(cb) { + var cookies = []; + var idx = this.idx; + + var domains = Object.keys(idx); + domains.forEach(function(domain) { + var paths = Object.keys(idx[domain]); + paths.forEach(function(path) { + var keys = Object.keys(idx[domain][path]); + keys.forEach(function(key) { + if (key !== null) { + cookies.push(idx[domain][path][key]); + } + }); + }); + }); + + // Sort by creationIndex so deserializing retains the creation order. + // When implementing your own store, this SHOULD retain the order too + cookies.sort(function(a,b) { + return (a.creationIndex||0) - (b.creationIndex||0); + }); + + cb(null, cookies); +}; + + +/***/ }), + +/***/ 357: +/***/ (function(module) { + +module.exports = require("assert"); + +/***/ }), + +/***/ 362: +/***/ (function(module) { + +// Copyright 2011 Mark Cavage All rights reserved. + + +module.exports = { + EOC: 0, + Boolean: 1, + Integer: 2, + BitString: 3, + OctetString: 4, + Null: 5, + OID: 6, + ObjectDescriptor: 7, + External: 8, + Real: 9, // float + Enumeration: 10, + PDV: 11, + Utf8String: 12, + RelativeOID: 13, + Sequence: 16, + Set: 17, + NumericString: 18, + PrintableString: 19, + T61String: 20, + VideotexString: 21, + IA5String: 22, + UTCTime: 23, + GeneralizedTime: 24, + GraphicString: 25, + VisibleString: 26, + GeneralString: 28, + UniversalString: 29, + CharacterString: 30, + BMPString: 31, + Constructor: 32, + Context: 128 +}; + + +/***/ }), + +/***/ 363: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2015 Joyent, Inc. + +module.exports = { + Verifier: Verifier, + Signer: Signer +}; + +var nacl = __webpack_require__(196); +var stream = __webpack_require__(413); +var util = __webpack_require__(669); +var assert = __webpack_require__(477); +var Buffer = __webpack_require__(215).Buffer; +var Signature = __webpack_require__(575); + +function Verifier(key, hashAlgo) { + if (hashAlgo.toLowerCase() !== 'sha512') + throw (new Error('ED25519 only supports the use of ' + + 'SHA-512 hashes')); + + this.key = key; + this.chunks = []; + + stream.Writable.call(this, {}); +} +util.inherits(Verifier, stream.Writable); + +Verifier.prototype._write = function (chunk, enc, cb) { + this.chunks.push(chunk); + cb(); +}; + +Verifier.prototype.update = function (chunk) { + if (typeof (chunk) === 'string') + chunk = Buffer.from(chunk, 'binary'); + this.chunks.push(chunk); +}; + +Verifier.prototype.verify = function (signature, fmt) { + var sig; + if (Signature.isSignature(signature, [2, 0])) { + if (signature.type !== 'ed25519') + return (false); + sig = signature.toBuffer('raw'); + + } else if (typeof (signature) === 'string') { + sig = Buffer.from(signature, 'base64'); + + } else if (Signature.isSignature(signature, [1, 0])) { + throw (new Error('signature was created by too old ' + + 'a version of sshpk and cannot be verified')); + } + + assert.buffer(sig); + return (nacl.sign.detached.verify( + new Uint8Array(Buffer.concat(this.chunks)), + new Uint8Array(sig), + new Uint8Array(this.key.part.A.data))); +}; + +function Signer(key, hashAlgo) { + if (hashAlgo.toLowerCase() !== 'sha512') + throw (new Error('ED25519 only supports the use of ' + + 'SHA-512 hashes')); + + this.key = key; + this.chunks = []; + + stream.Writable.call(this, {}); +} +util.inherits(Signer, stream.Writable); + +Signer.prototype._write = function (chunk, enc, cb) { + this.chunks.push(chunk); + cb(); +}; + +Signer.prototype.update = function (chunk) { + if (typeof (chunk) === 'string') + chunk = Buffer.from(chunk, 'binary'); + this.chunks.push(chunk); +}; + +Signer.prototype.sign = function () { + var sig = nacl.sign.detached( + new Uint8Array(Buffer.concat(this.chunks)), + new Uint8Array(Buffer.concat([ + this.key.part.k.data, this.key.part.A.data]))); + var sigBuf = Buffer.from(sig); + var sigObj = Signature.parse(sigBuf, 'ed25519', 'raw'); + sigObj.hashAlgorithm = 'sha512'; + return (sigObj); +}; + + +/***/ }), + +/***/ 374: +/***/ (function(module) { + +"use strict"; + + +var hasOwn = Object.prototype.hasOwnProperty; +var toStr = Object.prototype.toString; +var defineProperty = Object.defineProperty; +var gOPD = Object.getOwnPropertyDescriptor; + +var isArray = function isArray(arr) { + if (typeof Array.isArray === 'function') { + return Array.isArray(arr); + } + + return toStr.call(arr) === '[object Array]'; +}; + +var isPlainObject = function isPlainObject(obj) { + if (!obj || toStr.call(obj) !== '[object Object]') { + return false; + } + + var hasOwnConstructor = hasOwn.call(obj, 'constructor'); + var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf'); + // Not own constructor property must be Object + if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + var key; + for (key in obj) { /**/ } + + return typeof key === 'undefined' || hasOwn.call(obj, key); +}; + +// If name is '__proto__', and Object.defineProperty is available, define __proto__ as an own property on target +var setProperty = function setProperty(target, options) { + if (defineProperty && options.name === '__proto__') { + defineProperty(target, options.name, { + enumerable: true, + configurable: true, + value: options.newValue, + writable: true + }); + } else { + target[options.name] = options.newValue; + } +}; + +// Return undefined instead of __proto__ if '__proto__' is not an own property +var getProperty = function getProperty(obj, name) { + if (name === '__proto__') { + if (!hasOwn.call(obj, name)) { + return void 0; + } else if (gOPD) { + // In early versions of node, obj['__proto__'] is buggy when obj has + // __proto__ as an own property. Object.getOwnPropertyDescriptor() works. + return gOPD(obj, name).value; + } + } + + return obj[name]; +}; + +module.exports = function extend() { + var options, name, src, copy, copyIsArray, clone; + var target = arguments[0]; + var i = 1; + var length = arguments.length; + var deep = false; + + // Handle a deep copy situation + if (typeof target === 'boolean') { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + if (target == null || (typeof target !== 'object' && typeof target !== 'function')) { + target = {}; + } + + for (; i < length; ++i) { + options = arguments[i]; + // Only deal with non-null/undefined values + if (options != null) { + // Extend the base object + for (name in options) { + src = getProperty(target, name); + copy = getProperty(options, name); + + // Prevent never-ending loop + if (target !== copy) { + // Recurse if we're merging plain objects or arrays + if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) { + if (copyIsArray) { + copyIsArray = false; + clone = src && isArray(src) ? src : []; + } else { + clone = src && isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + setProperty(target, { name: name, newValue: extend(deep, clone, copy) }); + + // Don't bring in undefined values + } else if (typeof copy !== 'undefined') { + setProperty(target, { name: name, newValue: copy }); + } + } + } + } + } + + // Return the modified object + return target; +}; + + +/***/ }), + +/***/ 378: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2017 Joyent, Inc. + +module.exports = Identity; + +var assert = __webpack_require__(477); +var algs = __webpack_require__(98); +var crypto = __webpack_require__(417); +var Fingerprint = __webpack_require__(400); +var Signature = __webpack_require__(575); +var errs = __webpack_require__(753); +var util = __webpack_require__(669); +var utils = __webpack_require__(270); +var asn1 = __webpack_require__(62); +var Buffer = __webpack_require__(215).Buffer; + +/*JSSTYLED*/ +var DNS_NAME_RE = /^([*]|[a-z0-9][a-z0-9\-]{0,62})(?:\.([*]|[a-z0-9][a-z0-9\-]{0,62}))*$/i; + +var oids = {}; +oids.cn = '2.5.4.3'; +oids.o = '2.5.4.10'; +oids.ou = '2.5.4.11'; +oids.l = '2.5.4.7'; +oids.s = '2.5.4.8'; +oids.c = '2.5.4.6'; +oids.sn = '2.5.4.4'; +oids.postalCode = '2.5.4.17'; +oids.serialNumber = '2.5.4.5'; +oids.street = '2.5.4.9'; +oids.x500UniqueIdentifier = '2.5.4.45'; +oids.role = '2.5.4.72'; +oids.telephoneNumber = '2.5.4.20'; +oids.description = '2.5.4.13'; +oids.dc = '0.9.2342.19200300.100.1.25'; +oids.uid = '0.9.2342.19200300.100.1.1'; +oids.mail = '0.9.2342.19200300.100.1.3'; +oids.title = '2.5.4.12'; +oids.gn = '2.5.4.42'; +oids.initials = '2.5.4.43'; +oids.pseudonym = '2.5.4.65'; +oids.emailAddress = '1.2.840.113549.1.9.1'; + +var unoids = {}; +Object.keys(oids).forEach(function (k) { + unoids[oids[k]] = k; +}); + +function Identity(opts) { + var self = this; + assert.object(opts, 'options'); + assert.arrayOfObject(opts.components, 'options.components'); + this.components = opts.components; + this.componentLookup = {}; + this.components.forEach(function (c) { + if (c.name && !c.oid) + c.oid = oids[c.name]; + if (c.oid && !c.name) + c.name = unoids[c.oid]; + if (self.componentLookup[c.name] === undefined) + self.componentLookup[c.name] = []; + self.componentLookup[c.name].push(c); + }); + if (this.componentLookup.cn && this.componentLookup.cn.length > 0) { + this.cn = this.componentLookup.cn[0].value; + } + assert.optionalString(opts.type, 'options.type'); + if (opts.type === undefined) { + if (this.components.length === 1 && + this.componentLookup.cn && + this.componentLookup.cn.length === 1 && + this.componentLookup.cn[0].value.match(DNS_NAME_RE)) { + this.type = 'host'; + this.hostname = this.componentLookup.cn[0].value; + + } else if (this.componentLookup.dc && + this.components.length === this.componentLookup.dc.length) { + this.type = 'host'; + this.hostname = this.componentLookup.dc.map( + function (c) { + return (c.value); + }).join('.'); + + } else if (this.componentLookup.uid && + this.components.length === + this.componentLookup.uid.length) { + this.type = 'user'; + this.uid = this.componentLookup.uid[0].value; + + } else if (this.componentLookup.cn && + this.componentLookup.cn.length === 1 && + this.componentLookup.cn[0].value.match(DNS_NAME_RE)) { + this.type = 'host'; + this.hostname = this.componentLookup.cn[0].value; + + } else if (this.componentLookup.uid && + this.componentLookup.uid.length === 1) { + this.type = 'user'; + this.uid = this.componentLookup.uid[0].value; + + } else if (this.componentLookup.mail && + this.componentLookup.mail.length === 1) { + this.type = 'email'; + this.email = this.componentLookup.mail[0].value; + + } else if (this.componentLookup.cn && + this.componentLookup.cn.length === 1) { + this.type = 'user'; + this.uid = this.componentLookup.cn[0].value; + + } else { + this.type = 'unknown'; + } + } else { + this.type = opts.type; + if (this.type === 'host') + this.hostname = opts.hostname; + else if (this.type === 'user') + this.uid = opts.uid; + else if (this.type === 'email') + this.email = opts.email; + else + throw (new Error('Unknown type ' + this.type)); + } +} + +Identity.prototype.toString = function () { + return (this.components.map(function (c) { + var n = c.name.toUpperCase(); + /*JSSTYLED*/ + n = n.replace(/=/g, '\\='); + var v = c.value; + /*JSSTYLED*/ + v = v.replace(/,/g, '\\,'); + return (n + '=' + v); + }).join(', ')); +}; + +Identity.prototype.get = function (name, asArray) { + assert.string(name, 'name'); + var arr = this.componentLookup[name]; + if (arr === undefined || arr.length === 0) + return (undefined); + if (!asArray && arr.length > 1) + throw (new Error('Multiple values for attribute ' + name)); + if (!asArray) + return (arr[0].value); + return (arr.map(function (c) { + return (c.value); + })); +}; + +Identity.prototype.toArray = function (idx) { + return (this.components.map(function (c) { + return ({ + name: c.name, + value: c.value + }); + })); +}; + +/* + * These are from X.680 -- PrintableString allowed chars are in section 37.4 + * table 8. Spec for IA5Strings is "1,6 + SPACE + DEL" where 1 refers to + * ISO IR #001 (standard ASCII control characters) and 6 refers to ISO IR #006 + * (the basic ASCII character set). + */ +/* JSSTYLED */ +var NOT_PRINTABLE = /[^a-zA-Z0-9 '(),+.\/:=?-]/; +/* JSSTYLED */ +var NOT_IA5 = /[^\x00-\x7f]/; + +Identity.prototype.toAsn1 = function (der, tag) { + der.startSequence(tag); + this.components.forEach(function (c) { + der.startSequence(asn1.Ber.Constructor | asn1.Ber.Set); + der.startSequence(); + der.writeOID(c.oid); + /* + * If we fit in a PrintableString, use that. Otherwise use an + * IA5String or UTF8String. + * + * If this identity was parsed from a DN, use the ASN.1 types + * from the original representation (otherwise this might not + * be a full match for the original in some validators). + */ + if (c.asn1type === asn1.Ber.Utf8String || + c.value.match(NOT_IA5)) { + var v = Buffer.from(c.value, 'utf8'); + der.writeBuffer(v, asn1.Ber.Utf8String); + + } else if (c.asn1type === asn1.Ber.IA5String || + c.value.match(NOT_PRINTABLE)) { + der.writeString(c.value, asn1.Ber.IA5String); + + } else { + var type = asn1.Ber.PrintableString; + if (c.asn1type !== undefined) + type = c.asn1type; + der.writeString(c.value, type); + } + der.endSequence(); + der.endSequence(); + }); + der.endSequence(); +}; + +function globMatch(a, b) { + if (a === '**' || b === '**') + return (true); + var aParts = a.split('.'); + var bParts = b.split('.'); + if (aParts.length !== bParts.length) + return (false); + for (var i = 0; i < aParts.length; ++i) { + if (aParts[i] === '*' || bParts[i] === '*') + continue; + if (aParts[i] !== bParts[i]) + return (false); + } + return (true); +} + +Identity.prototype.equals = function (other) { + if (!Identity.isIdentity(other, [1, 0])) + return (false); + if (other.components.length !== this.components.length) + return (false); + for (var i = 0; i < this.components.length; ++i) { + if (this.components[i].oid !== other.components[i].oid) + return (false); + if (!globMatch(this.components[i].value, + other.components[i].value)) { + return (false); + } + } + return (true); +}; + +Identity.forHost = function (hostname) { + assert.string(hostname, 'hostname'); + return (new Identity({ + type: 'host', + hostname: hostname, + components: [ { name: 'cn', value: hostname } ] + })); +}; + +Identity.forUser = function (uid) { + assert.string(uid, 'uid'); + return (new Identity({ + type: 'user', + uid: uid, + components: [ { name: 'uid', value: uid } ] + })); +}; + +Identity.forEmail = function (email) { + assert.string(email, 'email'); + return (new Identity({ + type: 'email', + email: email, + components: [ { name: 'mail', value: email } ] + })); +}; + +Identity.parseDN = function (dn) { + assert.string(dn, 'dn'); + var parts = ['']; + var idx = 0; + var rem = dn; + while (rem.length > 0) { + var m; + /*JSSTYLED*/ + if ((m = /^,/.exec(rem)) !== null) { + parts[++idx] = ''; + rem = rem.slice(m[0].length); + /*JSSTYLED*/ + } else if ((m = /^\\,/.exec(rem)) !== null) { + parts[idx] += ','; + rem = rem.slice(m[0].length); + /*JSSTYLED*/ + } else if ((m = /^\\./.exec(rem)) !== null) { + parts[idx] += m[0]; + rem = rem.slice(m[0].length); + /*JSSTYLED*/ + } else if ((m = /^[^\\,]+/.exec(rem)) !== null) { + parts[idx] += m[0]; + rem = rem.slice(m[0].length); + } else { + throw (new Error('Failed to parse DN')); + } + } + var cmps = parts.map(function (c) { + c = c.trim(); + var eqPos = c.indexOf('='); + while (eqPos > 0 && c.charAt(eqPos - 1) === '\\') + eqPos = c.indexOf('=', eqPos + 1); + if (eqPos === -1) { + throw (new Error('Failed to parse DN')); + } + /*JSSTYLED*/ + var name = c.slice(0, eqPos).toLowerCase().replace(/\\=/g, '='); + var value = c.slice(eqPos + 1); + return ({ name: name, value: value }); + }); + return (new Identity({ components: cmps })); +}; + +Identity.fromArray = function (components) { + assert.arrayOfObject(components, 'components'); + components.forEach(function (cmp) { + assert.object(cmp, 'component'); + assert.string(cmp.name, 'component.name'); + if (!Buffer.isBuffer(cmp.value) && + !(typeof (cmp.value) === 'string')) { + throw (new Error('Invalid component value')); + } + }); + return (new Identity({ components: components })); +}; + +Identity.parseAsn1 = function (der, top) { + var components = []; + der.readSequence(top); + var end = der.offset + der.length; + while (der.offset < end) { + der.readSequence(asn1.Ber.Constructor | asn1.Ber.Set); + var after = der.offset + der.length; + der.readSequence(); + var oid = der.readOID(); + var type = der.peek(); + var value; + switch (type) { + case asn1.Ber.PrintableString: + case asn1.Ber.IA5String: + case asn1.Ber.OctetString: + case asn1.Ber.T61String: + value = der.readString(type); + break; + case asn1.Ber.Utf8String: + value = der.readString(type, true); + value = value.toString('utf8'); + break; + case asn1.Ber.CharacterString: + case asn1.Ber.BMPString: + value = der.readString(type, true); + value = value.toString('utf16le'); + break; + default: + throw (new Error('Unknown asn1 type ' + type)); + } + components.push({ oid: oid, asn1type: type, value: value }); + der._offset = after; + } + der._offset = end; + return (new Identity({ + components: components + })); +}; + +Identity.isIdentity = function (obj, ver) { + return (utils.isCompatible(obj, Identity, ver)); +}; + +/* + * API versions for Identity: + * [1,0] -- initial ver + */ +Identity.prototype._sshpkApiVersion = [1, 0]; + +Identity._oldVersionDetect = function (obj) { + return ([1, 0]); +}; + + +/***/ }), + +/***/ 380: +/***/ (function(module) { + +module.exports = {"$id":"request.json#","$schema":"http://json-schema.org/draft-06/schema#","type":"object","required":["method","url","httpVersion","cookies","headers","queryString","headersSize","bodySize"],"properties":{"method":{"type":"string"},"url":{"type":"string","format":"uri"},"httpVersion":{"type":"string"},"cookies":{"type":"array","items":{"$ref":"cookie.json#"}},"headers":{"type":"array","items":{"$ref":"header.json#"}},"queryString":{"type":"array","items":{"$ref":"query.json#"}},"postData":{"$ref":"postData.json#"},"headersSize":{"type":"integer"},"bodySize":{"type":"integer"},"comment":{"type":"string"}}}; + +/***/ }), + +/***/ 382: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var stream = __webpack_require__(413) + + +function isStream (obj) { + return obj instanceof stream.Stream +} + + +function isReadable (obj) { + return isStream(obj) && typeof obj._read == 'function' && typeof obj._readableState == 'object' +} + + +function isWritable (obj) { + return isStream(obj) && typeof obj._write == 'function' && typeof obj._writableState == 'object' +} + + +function isDuplex (obj) { + return isReadable(obj) && isWritable(obj) +} + + +module.exports = isStream +module.exports.isReadable = isReadable +module.exports.isWritable = isWritable +module.exports.isDuplex = isDuplex + + +/***/ }), + +/***/ 383: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; +/*! + * Copyright (c) 2015, Salesforce.com, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Salesforce.com nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +var pubsuffix = __webpack_require__(519); + +// Gives the permutation of all possible domainMatch()es of a given domain. The +// array is in shortest-to-longest order. Handy for indexing. +function permuteDomain (domain) { + var pubSuf = pubsuffix.getPublicSuffix(domain); + if (!pubSuf) { + return null; + } + if (pubSuf == domain) { + return [domain]; + } + + var prefix = domain.slice(0, -(pubSuf.length + 1)); // ".example.com" + var parts = prefix.split('.').reverse(); + var cur = pubSuf; + var permutations = [cur]; + while (parts.length) { + cur = parts.shift() + '.' + cur; + permutations.push(cur); + } + return permutations; +} + +exports.permuteDomain = permuteDomain; + + +/***/ }), + +/***/ 386: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +var stringify = __webpack_require__(897); +var parse = __webpack_require__(755); +var formats = __webpack_require__(13); + +module.exports = { + formats: formats, + parse: parse, + stringify: stringify +}; + + +/***/ }), + +/***/ 397: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_multipleOf(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + out += 'var division' + ($lvl) + ';if ('; + if ($isData) { + out += ' ' + ($schemaValue) + ' !== undefined && ( typeof ' + ($schemaValue) + ' != \'number\' || '; + } + out += ' (division' + ($lvl) + ' = ' + ($data) + ' / ' + ($schemaValue) + ', '; + if (it.opts.multipleOfPrecision) { + out += ' Math.abs(Math.round(division' + ($lvl) + ') - division' + ($lvl) + ') > 1e-' + (it.opts.multipleOfPrecision) + ' '; + } else { + out += ' division' + ($lvl) + ' !== parseInt(division' + ($lvl) + ') '; + } + out += ' ) '; + if ($isData) { + out += ' ) '; + } + out += ' ) { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('multipleOf') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { multipleOf: ' + ($schemaValue) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should be multiple of '; + if ($isData) { + out += '\' + ' + ($schemaValue); + } else { + out += '' + ($schemaValue) + '\''; + } + } + if (it.opts.verbose) { + out += ' , schema: '; + if ($isData) { + out += 'validate.schema' + ($schemaPath); + } else { + out += '' + ($schema); + } + out += ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += '} '; + if ($breakOnError) { + out += ' else { '; + } + return out; +} + + +/***/ }), + +/***/ 400: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2018 Joyent, Inc. + +module.exports = Fingerprint; + +var assert = __webpack_require__(477); +var Buffer = __webpack_require__(215).Buffer; +var algs = __webpack_require__(98); +var crypto = __webpack_require__(417); +var errs = __webpack_require__(753); +var Key = __webpack_require__(852); +var PrivateKey = __webpack_require__(502); +var Certificate = __webpack_require__(752); +var utils = __webpack_require__(270); + +var FingerprintFormatError = errs.FingerprintFormatError; +var InvalidAlgorithmError = errs.InvalidAlgorithmError; + +function Fingerprint(opts) { + assert.object(opts, 'options'); + assert.string(opts.type, 'options.type'); + assert.buffer(opts.hash, 'options.hash'); + assert.string(opts.algorithm, 'options.algorithm'); + + this.algorithm = opts.algorithm.toLowerCase(); + if (algs.hashAlgs[this.algorithm] !== true) + throw (new InvalidAlgorithmError(this.algorithm)); + + this.hash = opts.hash; + this.type = opts.type; + this.hashType = opts.hashType; +} + +Fingerprint.prototype.toString = function (format) { + if (format === undefined) { + if (this.algorithm === 'md5' || this.hashType === 'spki') + format = 'hex'; + else + format = 'base64'; + } + assert.string(format); + + switch (format) { + case 'hex': + if (this.hashType === 'spki') + return (this.hash.toString('hex')); + return (addColons(this.hash.toString('hex'))); + case 'base64': + if (this.hashType === 'spki') + return (this.hash.toString('base64')); + return (sshBase64Format(this.algorithm, + this.hash.toString('base64'))); + default: + throw (new FingerprintFormatError(undefined, format)); + } +}; + +Fingerprint.prototype.matches = function (other) { + assert.object(other, 'key or certificate'); + if (this.type === 'key' && this.hashType !== 'ssh') { + utils.assertCompatible(other, Key, [1, 7], 'key with spki'); + if (PrivateKey.isPrivateKey(other)) { + utils.assertCompatible(other, PrivateKey, [1, 6], + 'privatekey with spki support'); + } + } else if (this.type === 'key') { + utils.assertCompatible(other, Key, [1, 0], 'key'); + } else { + utils.assertCompatible(other, Certificate, [1, 0], + 'certificate'); + } + + var theirHash = other.hash(this.algorithm, this.hashType); + var theirHash2 = crypto.createHash(this.algorithm). + update(theirHash).digest('base64'); + + if (this.hash2 === undefined) + this.hash2 = crypto.createHash(this.algorithm). + update(this.hash).digest('base64'); + + return (this.hash2 === theirHash2); +}; + +/*JSSTYLED*/ +var base64RE = /^[A-Za-z0-9+\/=]+$/; +/*JSSTYLED*/ +var hexRE = /^[a-fA-F0-9]+$/; + +Fingerprint.parse = function (fp, options) { + assert.string(fp, 'fingerprint'); + + var alg, hash, enAlgs; + if (Array.isArray(options)) { + enAlgs = options; + options = {}; + } + assert.optionalObject(options, 'options'); + if (options === undefined) + options = {}; + if (options.enAlgs !== undefined) + enAlgs = options.enAlgs; + if (options.algorithms !== undefined) + enAlgs = options.algorithms; + assert.optionalArrayOfString(enAlgs, 'algorithms'); + + var hashType = 'ssh'; + if (options.hashType !== undefined) + hashType = options.hashType; + assert.string(hashType, 'options.hashType'); + + var parts = fp.split(':'); + if (parts.length == 2) { + alg = parts[0].toLowerCase(); + if (!base64RE.test(parts[1])) + throw (new FingerprintFormatError(fp)); + try { + hash = Buffer.from(parts[1], 'base64'); + } catch (e) { + throw (new FingerprintFormatError(fp)); + } + } else if (parts.length > 2) { + alg = 'md5'; + if (parts[0].toLowerCase() === 'md5') + parts = parts.slice(1); + parts = parts.map(function (p) { + while (p.length < 2) + p = '0' + p; + if (p.length > 2) + throw (new FingerprintFormatError(fp)); + return (p); + }); + parts = parts.join(''); + if (!hexRE.test(parts) || parts.length % 2 !== 0) + throw (new FingerprintFormatError(fp)); + try { + hash = Buffer.from(parts, 'hex'); + } catch (e) { + throw (new FingerprintFormatError(fp)); + } + } else { + if (hexRE.test(fp)) { + hash = Buffer.from(fp, 'hex'); + } else if (base64RE.test(fp)) { + hash = Buffer.from(fp, 'base64'); + } else { + throw (new FingerprintFormatError(fp)); + } + + switch (hash.length) { + case 32: + alg = 'sha256'; + break; + case 16: + alg = 'md5'; + break; + case 20: + alg = 'sha1'; + break; + case 64: + alg = 'sha512'; + break; + default: + throw (new FingerprintFormatError(fp)); + } + + /* Plain hex/base64: guess it's probably SPKI unless told. */ + if (options.hashType === undefined) + hashType = 'spki'; + } + + if (alg === undefined) + throw (new FingerprintFormatError(fp)); + + if (algs.hashAlgs[alg] === undefined) + throw (new InvalidAlgorithmError(alg)); + + if (enAlgs !== undefined) { + enAlgs = enAlgs.map(function (a) { return a.toLowerCase(); }); + if (enAlgs.indexOf(alg) === -1) + throw (new InvalidAlgorithmError(alg)); + } + + return (new Fingerprint({ + algorithm: alg, + hash: hash, + type: options.type || 'key', + hashType: hashType + })); +}; + +function addColons(s) { + /*JSSTYLED*/ + return (s.replace(/(.{2})(?=.)/g, '$1:')); +} + +function base64Strip(s) { + /*JSSTYLED*/ + return (s.replace(/=*$/, '')); +} + +function sshBase64Format(alg, h) { + return (alg.toUpperCase() + ':' + base64Strip(h)); +} + +Fingerprint.isFingerprint = function (obj, ver) { + return (utils.isCompatible(obj, Fingerprint, ver)); +}; + +/* + * API versions for Fingerprint: + * [1,0] -- initial ver + * [1,1] -- first tagged ver + * [1,2] -- hashType and spki support + */ +Fingerprint.prototype._sshpkApiVersion = [1, 2]; + +Fingerprint._oldVersionDetect = function (obj) { + assert.func(obj.toString); + assert.func(obj.matches); + return ([1, 0]); +}; + + +/***/ }), + +/***/ 413: +/***/ (function(module) { + +module.exports = require("stream"); + +/***/ }), + +/***/ 416: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + + +var fs = __webpack_require__(747) +var qs = __webpack_require__(191) +var validate = __webpack_require__(846) +var extend = __webpack_require__(374) + +function Har (request) { + this.request = request +} + +Har.prototype.reducer = function (obj, pair) { + // new property ? + if (obj[pair.name] === undefined) { + obj[pair.name] = pair.value + return obj + } + + // existing? convert to array + var arr = [ + obj[pair.name], + pair.value + ] + + obj[pair.name] = arr + + return obj +} + +Har.prototype.prep = function (data) { + // construct utility properties + data.queryObj = {} + data.headersObj = {} + data.postData.jsonObj = false + data.postData.paramsObj = false + + // construct query objects + if (data.queryString && data.queryString.length) { + data.queryObj = data.queryString.reduce(this.reducer, {}) + } + + // construct headers objects + if (data.headers && data.headers.length) { + // loweCase header keys + data.headersObj = data.headers.reduceRight(function (headers, header) { + headers[header.name] = header.value + return headers + }, {}) + } + + // construct Cookie header + if (data.cookies && data.cookies.length) { + var cookies = data.cookies.map(function (cookie) { + return cookie.name + '=' + cookie.value + }) + + if (cookies.length) { + data.headersObj.cookie = cookies.join('; ') + } + } + + // prep body + function some (arr) { + return arr.some(function (type) { + return data.postData.mimeType.indexOf(type) === 0 + }) + } + + if (some([ + 'multipart/mixed', + 'multipart/related', + 'multipart/form-data', + 'multipart/alternative'])) { + // reset values + data.postData.mimeType = 'multipart/form-data' + } else if (some([ + 'application/x-www-form-urlencoded'])) { + if (!data.postData.params) { + data.postData.text = '' + } else { + data.postData.paramsObj = data.postData.params.reduce(this.reducer, {}) + + // always overwrite + data.postData.text = qs.stringify(data.postData.paramsObj) + } + } else if (some([ + 'text/json', + 'text/x-json', + 'application/json', + 'application/x-json'])) { + data.postData.mimeType = 'application/json' + + if (data.postData.text) { + try { + data.postData.jsonObj = JSON.parse(data.postData.text) + } catch (e) { + this.request.debug(e) + + // force back to text/plain + data.postData.mimeType = 'text/plain' + } + } + } + + return data +} + +Har.prototype.options = function (options) { + // skip if no har property defined + if (!options.har) { + return options + } + + var har = {} + extend(har, options.har) + + // only process the first entry + if (har.log && har.log.entries) { + har = har.log.entries[0] + } + + // add optional properties to make validation successful + har.url = har.url || options.url || options.uri || options.baseUrl || '/' + har.httpVersion = har.httpVersion || 'HTTP/1.1' + har.queryString = har.queryString || [] + har.headers = har.headers || [] + har.cookies = har.cookies || [] + har.postData = har.postData || {} + har.postData.mimeType = har.postData.mimeType || 'application/octet-stream' + + har.bodySize = 0 + har.headersSize = 0 + har.postData.size = 0 + + if (!validate.request(har)) { + return options + } + + // clean up and get some utility properties + var req = this.prep(har) + + // construct new options + if (req.url) { + options.url = req.url + } + + if (req.method) { + options.method = req.method + } + + if (Object.keys(req.queryObj).length) { + options.qs = req.queryObj + } + + if (Object.keys(req.headersObj).length) { + options.headers = req.headersObj + } + + function test (type) { + return req.postData.mimeType.indexOf(type) === 0 + } + if (test('application/x-www-form-urlencoded')) { + options.form = req.postData.paramsObj + } else if (test('application/json')) { + if (req.postData.jsonObj) { + options.body = req.postData.jsonObj + options.json = true + } + } else if (test('multipart/form-data')) { + options.formData = {} + + req.postData.params.forEach(function (param) { + var attachment = {} + + if (!param.fileName && !param.contentType) { + options.formData[param.name] = param.value + return + } + + // attempt to read from disk! + if (param.fileName && !param.value) { + attachment.value = fs.createReadStream(param.fileName) + } else if (param.value) { + attachment.value = param.value + } + + if (param.fileName) { + attachment.options = { + filename: param.fileName, + contentType: param.contentType ? param.contentType : null + } + } + + options.formData[param.name] = attachment + }) + } else { + if (req.postData.text) { + options.body = req.postData.text + } + } + + return options +} + +exports.Har = Har + + +/***/ }), + +/***/ 417: +/***/ (function(module) { + +module.exports = require("crypto"); + +/***/ }), + +/***/ 424: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var iterate = __webpack_require__(157) + , initState = __webpack_require__(147) + , terminator = __webpack_require__(939) + ; + +// Public API +module.exports = parallel; + +/** + * Runs iterator over provided array elements in parallel + * + * @param {array|object} list - array or object (named list) to iterate over + * @param {function} iterator - iterator to run + * @param {function} callback - invoked when all elements processed + * @returns {function} - jobs terminator + */ +function parallel(list, iterator, callback) +{ + var state = initState(list); + + while (state.index < (state['keyedList'] || list).length) + { + iterate(list, iterator, state, function(error, result) + { + if (error) + { + callback(error, result); + return; + } + + // looks like it's the last one + if (Object.keys(state.jobs).length === 0) + { + callback(null, state.results); + return; + } + }); + + state.index++; + } + + return terminator.bind(state, callback); +} + + +/***/ }), + +/***/ 428: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2015 Joyent, Inc. + +var assert = __webpack_require__(477); +var crypto = __webpack_require__(417); +var sshpk = __webpack_require__(650); +var utils = __webpack_require__(909); + +var HASH_ALGOS = utils.HASH_ALGOS; +var PK_ALGOS = utils.PK_ALGOS; +var InvalidAlgorithmError = utils.InvalidAlgorithmError; +var HttpSignatureError = utils.HttpSignatureError; +var validateAlgorithm = utils.validateAlgorithm; + +///--- Exported API + +module.exports = { + /** + * Verify RSA/DSA signature against public key. You are expected to pass in + * an object that was returned from `parse()`. + * + * @param {Object} parsedSignature the object you got from `parse`. + * @param {String} pubkey RSA/DSA private key PEM. + * @return {Boolean} true if valid, false otherwise. + * @throws {TypeError} if you pass in bad arguments. + * @throws {InvalidAlgorithmError} + */ + verifySignature: function verifySignature(parsedSignature, pubkey) { + assert.object(parsedSignature, 'parsedSignature'); + if (typeof (pubkey) === 'string' || Buffer.isBuffer(pubkey)) + pubkey = sshpk.parseKey(pubkey); + assert.ok(sshpk.Key.isKey(pubkey, [1, 1]), 'pubkey must be a sshpk.Key'); + + var alg = validateAlgorithm(parsedSignature.algorithm); + if (alg[0] === 'hmac' || alg[0] !== pubkey.type) + return (false); + + var v = pubkey.createVerify(alg[1]); + v.update(parsedSignature.signingString); + return (v.verify(parsedSignature.params.signature, 'base64')); + }, + + /** + * Verify HMAC against shared secret. You are expected to pass in an object + * that was returned from `parse()`. + * + * @param {Object} parsedSignature the object you got from `parse`. + * @param {String} secret HMAC shared secret. + * @return {Boolean} true if valid, false otherwise. + * @throws {TypeError} if you pass in bad arguments. + * @throws {InvalidAlgorithmError} + */ + verifyHMAC: function verifyHMAC(parsedSignature, secret) { + assert.object(parsedSignature, 'parsedHMAC'); + assert.string(secret, 'secret'); + + var alg = validateAlgorithm(parsedSignature.algorithm); + if (alg[0] !== 'hmac') + return (false); + + var hashAlg = alg[1].toUpperCase(); + + var hmac = crypto.createHmac(hashAlg, secret); + hmac.update(parsedSignature.signingString); + + /* + * Now double-hash to avoid leaking timing information - there's + * no easy constant-time compare in JS, so we use this approach + * instead. See for more info: + * https://www.isecpartners.com/blog/2011/february/double-hmac- + * verification.aspx + */ + var h1 = crypto.createHmac(hashAlg, secret); + h1.update(hmac.digest()); + h1 = h1.digest(); + var h2 = crypto.createHmac(hashAlg, secret); + h2.update(new Buffer(parsedSignature.params.signature, 'base64')); + h2 = h2.digest(); + + /* Node 0.8 returns strings from .digest(). */ + if (typeof (h1) === 'string') + return (h1 === h2); + /* And node 0.10 lacks the .equals() method on Buffers. */ + if (Buffer.isBuffer(h1) && !h1.equals) + return (h1.toString('binary') === h2.toString('binary')); + + return (h1.equals(h2)); + } +}; + + +/***/ }), + +/***/ 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 + +/***/ }), + +/***/ 449: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2015 Joyent, Inc. + +module.exports = { + read: read, + readPkcs1: readPkcs1, + write: write, + writePkcs1: writePkcs1 +}; + +var assert = __webpack_require__(477); +var asn1 = __webpack_require__(62); +var Buffer = __webpack_require__(215).Buffer; +var algs = __webpack_require__(98); +var utils = __webpack_require__(270); + +var Key = __webpack_require__(852); +var PrivateKey = __webpack_require__(502); +var pem = __webpack_require__(268); + +var pkcs8 = __webpack_require__(707); +var readECDSACurve = pkcs8.readECDSACurve; + +function read(buf, options) { + return (pem.read(buf, options, 'pkcs1')); +} + +function write(key, options) { + return (pem.write(key, options, 'pkcs1')); +} + +/* Helper to read in a single mpint */ +function readMPInt(der, nm) { + assert.strictEqual(der.peek(), asn1.Ber.Integer, + nm + ' is not an Integer'); + return (utils.mpNormalize(der.readString(asn1.Ber.Integer, true))); +} + +function readPkcs1(alg, type, der) { + switch (alg) { + case 'RSA': + if (type === 'public') + return (readPkcs1RSAPublic(der)); + else if (type === 'private') + return (readPkcs1RSAPrivate(der)); + throw (new Error('Unknown key type: ' + type)); + case 'DSA': + if (type === 'public') + return (readPkcs1DSAPublic(der)); + else if (type === 'private') + return (readPkcs1DSAPrivate(der)); + throw (new Error('Unknown key type: ' + type)); + case 'EC': + case 'ECDSA': + if (type === 'private') + return (readPkcs1ECDSAPrivate(der)); + else if (type === 'public') + return (readPkcs1ECDSAPublic(der)); + throw (new Error('Unknown key type: ' + type)); + case 'EDDSA': + case 'EdDSA': + if (type === 'private') + return (readPkcs1EdDSAPrivate(der)); + throw (new Error(type + ' keys not supported with EdDSA')); + default: + throw (new Error('Unknown key algo: ' + alg)); + } +} + +function readPkcs1RSAPublic(der) { + // modulus and exponent + var n = readMPInt(der, 'modulus'); + var e = readMPInt(der, 'exponent'); + + // now, make the key + var key = { + type: 'rsa', + parts: [ + { name: 'e', data: e }, + { name: 'n', data: n } + ] + }; + + return (new Key(key)); +} + +function readPkcs1RSAPrivate(der) { + var version = readMPInt(der, 'version'); + assert.strictEqual(version[0], 0); + + // modulus then public exponent + var n = readMPInt(der, 'modulus'); + var e = readMPInt(der, 'public exponent'); + var d = readMPInt(der, 'private exponent'); + var p = readMPInt(der, 'prime1'); + var q = readMPInt(der, 'prime2'); + var dmodp = readMPInt(der, 'exponent1'); + var dmodq = readMPInt(der, 'exponent2'); + var iqmp = readMPInt(der, 'iqmp'); + + // now, make the key + var key = { + type: 'rsa', + parts: [ + { name: 'n', data: n }, + { name: 'e', data: e }, + { name: 'd', data: d }, + { name: 'iqmp', data: iqmp }, + { name: 'p', data: p }, + { name: 'q', data: q }, + { name: 'dmodp', data: dmodp }, + { name: 'dmodq', data: dmodq } + ] + }; + + return (new PrivateKey(key)); +} + +function readPkcs1DSAPrivate(der) { + var version = readMPInt(der, 'version'); + assert.strictEqual(version.readUInt8(0), 0); + + var p = readMPInt(der, 'p'); + var q = readMPInt(der, 'q'); + var g = readMPInt(der, 'g'); + var y = readMPInt(der, 'y'); + var x = readMPInt(der, 'x'); + + // now, make the key + var key = { + type: 'dsa', + parts: [ + { name: 'p', data: p }, + { name: 'q', data: q }, + { name: 'g', data: g }, + { name: 'y', data: y }, + { name: 'x', data: x } + ] + }; + + return (new PrivateKey(key)); +} + +function readPkcs1EdDSAPrivate(der) { + var version = readMPInt(der, 'version'); + assert.strictEqual(version.readUInt8(0), 1); + + // private key + var k = der.readString(asn1.Ber.OctetString, true); + + der.readSequence(0xa0); + var oid = der.readOID(); + assert.strictEqual(oid, '1.3.101.112', 'the ed25519 curve identifier'); + + der.readSequence(0xa1); + var A = utils.readBitString(der); + + var key = { + type: 'ed25519', + parts: [ + { name: 'A', data: utils.zeroPadToLength(A, 32) }, + { name: 'k', data: k } + ] + }; + + return (new PrivateKey(key)); +} + +function readPkcs1DSAPublic(der) { + var y = readMPInt(der, 'y'); + var p = readMPInt(der, 'p'); + var q = readMPInt(der, 'q'); + var g = readMPInt(der, 'g'); + + var key = { + type: 'dsa', + parts: [ + { name: 'y', data: y }, + { name: 'p', data: p }, + { name: 'q', data: q }, + { name: 'g', data: g } + ] + }; + + return (new Key(key)); +} + +function readPkcs1ECDSAPublic(der) { + der.readSequence(); + + var oid = der.readOID(); + assert.strictEqual(oid, '1.2.840.10045.2.1', 'must be ecPublicKey'); + + var curveOid = der.readOID(); + + var curve; + var curves = Object.keys(algs.curves); + for (var j = 0; j < curves.length; ++j) { + var c = curves[j]; + var cd = algs.curves[c]; + if (cd.pkcs8oid === curveOid) { + curve = c; + break; + } + } + assert.string(curve, 'a known ECDSA named curve'); + + var Q = der.readString(asn1.Ber.BitString, true); + Q = utils.ecNormalize(Q); + + var key = { + type: 'ecdsa', + parts: [ + { name: 'curve', data: Buffer.from(curve) }, + { name: 'Q', data: Q } + ] + }; + + return (new Key(key)); +} + +function readPkcs1ECDSAPrivate(der) { + var version = readMPInt(der, 'version'); + assert.strictEqual(version.readUInt8(0), 1); + + // private key + var d = der.readString(asn1.Ber.OctetString, true); + + der.readSequence(0xa0); + var curve = readECDSACurve(der); + assert.string(curve, 'a known elliptic curve'); + + der.readSequence(0xa1); + var Q = der.readString(asn1.Ber.BitString, true); + Q = utils.ecNormalize(Q); + + var key = { + type: 'ecdsa', + parts: [ + { name: 'curve', data: Buffer.from(curve) }, + { name: 'Q', data: Q }, + { name: 'd', data: d } + ] + }; + + return (new PrivateKey(key)); +} + +function writePkcs1(der, key) { + der.startSequence(); + + switch (key.type) { + case 'rsa': + if (PrivateKey.isPrivateKey(key)) + writePkcs1RSAPrivate(der, key); + else + writePkcs1RSAPublic(der, key); + break; + case 'dsa': + if (PrivateKey.isPrivateKey(key)) + writePkcs1DSAPrivate(der, key); + else + writePkcs1DSAPublic(der, key); + break; + case 'ecdsa': + if (PrivateKey.isPrivateKey(key)) + writePkcs1ECDSAPrivate(der, key); + else + writePkcs1ECDSAPublic(der, key); + break; + case 'ed25519': + if (PrivateKey.isPrivateKey(key)) + writePkcs1EdDSAPrivate(der, key); + else + writePkcs1EdDSAPublic(der, key); + break; + default: + throw (new Error('Unknown key algo: ' + key.type)); + } + + der.endSequence(); +} + +function writePkcs1RSAPublic(der, key) { + der.writeBuffer(key.part.n.data, asn1.Ber.Integer); + der.writeBuffer(key.part.e.data, asn1.Ber.Integer); +} + +function writePkcs1RSAPrivate(der, key) { + var ver = Buffer.from([0]); + der.writeBuffer(ver, asn1.Ber.Integer); + + der.writeBuffer(key.part.n.data, asn1.Ber.Integer); + der.writeBuffer(key.part.e.data, asn1.Ber.Integer); + der.writeBuffer(key.part.d.data, asn1.Ber.Integer); + der.writeBuffer(key.part.p.data, asn1.Ber.Integer); + der.writeBuffer(key.part.q.data, asn1.Ber.Integer); + if (!key.part.dmodp || !key.part.dmodq) + utils.addRSAMissing(key); + der.writeBuffer(key.part.dmodp.data, asn1.Ber.Integer); + der.writeBuffer(key.part.dmodq.data, asn1.Ber.Integer); + der.writeBuffer(key.part.iqmp.data, asn1.Ber.Integer); +} + +function writePkcs1DSAPrivate(der, key) { + var ver = Buffer.from([0]); + der.writeBuffer(ver, asn1.Ber.Integer); + + der.writeBuffer(key.part.p.data, asn1.Ber.Integer); + der.writeBuffer(key.part.q.data, asn1.Ber.Integer); + der.writeBuffer(key.part.g.data, asn1.Ber.Integer); + der.writeBuffer(key.part.y.data, asn1.Ber.Integer); + der.writeBuffer(key.part.x.data, asn1.Ber.Integer); +} + +function writePkcs1DSAPublic(der, key) { + der.writeBuffer(key.part.y.data, asn1.Ber.Integer); + der.writeBuffer(key.part.p.data, asn1.Ber.Integer); + der.writeBuffer(key.part.q.data, asn1.Ber.Integer); + der.writeBuffer(key.part.g.data, asn1.Ber.Integer); +} + +function writePkcs1ECDSAPublic(der, key) { + der.startSequence(); + + der.writeOID('1.2.840.10045.2.1'); /* ecPublicKey */ + var curve = key.part.curve.data.toString(); + var curveOid = algs.curves[curve].pkcs8oid; + assert.string(curveOid, 'a known ECDSA named curve'); + der.writeOID(curveOid); + + der.endSequence(); + + var Q = utils.ecNormalize(key.part.Q.data, true); + der.writeBuffer(Q, asn1.Ber.BitString); +} + +function writePkcs1ECDSAPrivate(der, key) { + var ver = Buffer.from([1]); + der.writeBuffer(ver, asn1.Ber.Integer); + + der.writeBuffer(key.part.d.data, asn1.Ber.OctetString); + + der.startSequence(0xa0); + var curve = key.part.curve.data.toString(); + var curveOid = algs.curves[curve].pkcs8oid; + assert.string(curveOid, 'a known ECDSA named curve'); + der.writeOID(curveOid); + der.endSequence(); + + der.startSequence(0xa1); + var Q = utils.ecNormalize(key.part.Q.data, true); + der.writeBuffer(Q, asn1.Ber.BitString); + der.endSequence(); +} + +function writePkcs1EdDSAPrivate(der, key) { + var ver = Buffer.from([1]); + der.writeBuffer(ver, asn1.Ber.Integer); + + der.writeBuffer(key.part.k.data, asn1.Ber.OctetString); + + der.startSequence(0xa0); + der.writeOID('1.3.101.112'); + der.endSequence(); + + der.startSequence(0xa1); + utils.writeBitString(der, key.part.A.data); + der.endSequence(); +} + +function writePkcs1EdDSAPublic(der, key) { + throw (new Error('Public keys are not supported for EdDSA PKCS#1')); +} + + +/***/ }), + +/***/ 455: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +var http = __webpack_require__(605) +var https = __webpack_require__(211) +var url = __webpack_require__(835) +var util = __webpack_require__(669) +var stream = __webpack_require__(413) +var zlib = __webpack_require__(761) +var aws2 = __webpack_require__(942) +var aws4 = __webpack_require__(658) +var httpSignature = __webpack_require__(789) +var mime = __webpack_require__(779) +var caseless = __webpack_require__(254) +var ForeverAgent = __webpack_require__(792) +var FormData = __webpack_require__(928) +var extend = __webpack_require__(374) +var isstream = __webpack_require__(382) +var isTypedArray = __webpack_require__(944).strict +var helpers = __webpack_require__(810) +var cookies = __webpack_require__(602) +var getProxyFromURI = __webpack_require__(721) +var Querystring = __webpack_require__(629).Querystring +var Har = __webpack_require__(416).Har +var Auth = __webpack_require__(554).Auth +var OAuth = __webpack_require__(287).OAuth +var hawk = __webpack_require__(964) +var Multipart = __webpack_require__(469).Multipart +var Redirect = __webpack_require__(552).Redirect +var Tunnel = __webpack_require__(461).Tunnel +var now = __webpack_require__(742) +var Buffer = __webpack_require__(149).Buffer + +var safeStringify = helpers.safeStringify +var isReadStream = helpers.isReadStream +var toBase64 = helpers.toBase64 +var defer = helpers.defer +var copy = helpers.copy +var version = helpers.version +var globalCookieJar = cookies.jar() + +var globalPool = {} + +function filterForNonReserved (reserved, options) { + // Filter out properties that are not reserved. + // Reserved values are passed in at call site. + + var object = {} + for (var i in options) { + var notReserved = (reserved.indexOf(i) === -1) + if (notReserved) { + object[i] = options[i] + } + } + return object +} + +function filterOutReservedFunctions (reserved, options) { + // Filter out properties that are functions and are reserved. + // Reserved values are passed in at call site. + + var object = {} + for (var i in options) { + var isReserved = !(reserved.indexOf(i) === -1) + var isFunction = (typeof options[i] === 'function') + if (!(isReserved && isFunction)) { + object[i] = options[i] + } + } + return object +} + +// Return a simpler request object to allow serialization +function requestToJSON () { + var self = this + return { + uri: self.uri, + method: self.method, + headers: self.headers + } +} + +// Return a simpler response object to allow serialization +function responseToJSON () { + var self = this + return { + statusCode: self.statusCode, + body: self.body, + headers: self.headers, + request: requestToJSON.call(self.request) + } +} + +function Request (options) { + // if given the method property in options, set property explicitMethod to true + + // extend the Request instance with any non-reserved properties + // remove any reserved functions from the options object + // set Request instance to be readable and writable + // call init + + var self = this + + // start with HAR, then override with additional options + if (options.har) { + self._har = new Har(self) + options = self._har.options(options) + } + + stream.Stream.call(self) + var reserved = Object.keys(Request.prototype) + var nonReserved = filterForNonReserved(reserved, options) + + extend(self, nonReserved) + options = filterOutReservedFunctions(reserved, options) + + self.readable = true + self.writable = true + if (options.method) { + self.explicitMethod = true + } + self._qs = new Querystring(self) + self._auth = new Auth(self) + self._oauth = new OAuth(self) + self._multipart = new Multipart(self) + self._redirect = new Redirect(self) + self._tunnel = new Tunnel(self) + self.init(options) +} + +util.inherits(Request, stream.Stream) + +// Debugging +Request.debug = process.env.NODE_DEBUG && /\brequest\b/.test(process.env.NODE_DEBUG) +function debug () { + if (Request.debug) { + console.error('REQUEST %s', util.format.apply(util, arguments)) + } +} +Request.prototype.debug = debug + +Request.prototype.init = function (options) { + // init() contains all the code to setup the request object. + // the actual outgoing request is not started until start() is called + // this function is called from both the constructor and on redirect. + var self = this + if (!options) { + options = {} + } + self.headers = self.headers ? copy(self.headers) : {} + + // Delete headers with value undefined since they break + // ClientRequest.OutgoingMessage.setHeader in node 0.12 + for (var headerName in self.headers) { + if (typeof self.headers[headerName] === 'undefined') { + delete self.headers[headerName] + } + } + + caseless.httpify(self, self.headers) + + if (!self.method) { + self.method = options.method || 'GET' + } + if (!self.localAddress) { + self.localAddress = options.localAddress + } + + self._qs.init(options) + + debug(options) + if (!self.pool && self.pool !== false) { + self.pool = globalPool + } + self.dests = self.dests || [] + self.__isRequestRequest = true + + // Protect against double callback + if (!self._callback && self.callback) { + self._callback = self.callback + self.callback = function () { + if (self._callbackCalled) { + return // Print a warning maybe? + } + self._callbackCalled = true + self._callback.apply(self, arguments) + } + self.on('error', self.callback.bind()) + self.on('complete', self.callback.bind(self, null)) + } + + // People use this property instead all the time, so support it + if (!self.uri && self.url) { + self.uri = self.url + delete self.url + } + + // If there's a baseUrl, then use it as the base URL (i.e. uri must be + // specified as a relative path and is appended to baseUrl). + if (self.baseUrl) { + if (typeof self.baseUrl !== 'string') { + return self.emit('error', new Error('options.baseUrl must be a string')) + } + + if (typeof self.uri !== 'string') { + return self.emit('error', new Error('options.uri must be a string when using options.baseUrl')) + } + + if (self.uri.indexOf('//') === 0 || self.uri.indexOf('://') !== -1) { + return self.emit('error', new Error('options.uri must be a path when using options.baseUrl')) + } + + // Handle all cases to make sure that there's only one slash between + // baseUrl and uri. + var baseUrlEndsWithSlash = self.baseUrl.lastIndexOf('/') === self.baseUrl.length - 1 + var uriStartsWithSlash = self.uri.indexOf('/') === 0 + + if (baseUrlEndsWithSlash && uriStartsWithSlash) { + self.uri = self.baseUrl + self.uri.slice(1) + } else if (baseUrlEndsWithSlash || uriStartsWithSlash) { + self.uri = self.baseUrl + self.uri + } else if (self.uri === '') { + self.uri = self.baseUrl + } else { + self.uri = self.baseUrl + '/' + self.uri + } + delete self.baseUrl + } + + // A URI is needed by this point, emit error if we haven't been able to get one + if (!self.uri) { + return self.emit('error', new Error('options.uri is a required argument')) + } + + // If a string URI/URL was given, parse it into a URL object + if (typeof self.uri === 'string') { + self.uri = url.parse(self.uri) + } + + // Some URL objects are not from a URL parsed string and need href added + if (!self.uri.href) { + self.uri.href = url.format(self.uri) + } + + // DEPRECATED: Warning for users of the old Unix Sockets URL Scheme + if (self.uri.protocol === 'unix:') { + return self.emit('error', new Error('`unix://` URL scheme is no longer supported. Please use the format `http://unix:SOCKET:PATH`')) + } + + // Support Unix Sockets + if (self.uri.host === 'unix') { + self.enableUnixSocket() + } + + if (self.strictSSL === false) { + self.rejectUnauthorized = false + } + + if (!self.uri.pathname) { self.uri.pathname = '/' } + + if (!(self.uri.host || (self.uri.hostname && self.uri.port)) && !self.uri.isUnix) { + // Invalid URI: it may generate lot of bad errors, like 'TypeError: Cannot call method `indexOf` of undefined' in CookieJar + // Detect and reject it as soon as possible + var faultyUri = url.format(self.uri) + var message = 'Invalid URI "' + faultyUri + '"' + if (Object.keys(options).length === 0) { + // No option ? This can be the sign of a redirect + // As this is a case where the user cannot do anything (they didn't call request directly with this URL) + // they should be warned that it can be caused by a redirection (can save some hair) + message += '. This can be caused by a crappy redirection.' + } + // This error was fatal + self.abort() + return self.emit('error', new Error(message)) + } + + if (!self.hasOwnProperty('proxy')) { + self.proxy = getProxyFromURI(self.uri) + } + + self.tunnel = self._tunnel.isEnabled() + if (self.proxy) { + self._tunnel.setup(options) + } + + self._redirect.onRequest(options) + + self.setHost = false + if (!self.hasHeader('host')) { + var hostHeaderName = self.originalHostHeaderName || 'host' + self.setHeader(hostHeaderName, self.uri.host) + // Drop :port suffix from Host header if known protocol. + if (self.uri.port) { + if ((self.uri.port === '80' && self.uri.protocol === 'http:') || + (self.uri.port === '443' && self.uri.protocol === 'https:')) { + self.setHeader(hostHeaderName, self.uri.hostname) + } + } + self.setHost = true + } + + self.jar(self._jar || options.jar) + + if (!self.uri.port) { + if (self.uri.protocol === 'http:') { self.uri.port = 80 } else if (self.uri.protocol === 'https:') { self.uri.port = 443 } + } + + if (self.proxy && !self.tunnel) { + self.port = self.proxy.port + self.host = self.proxy.hostname + } else { + self.port = self.uri.port + self.host = self.uri.hostname + } + + if (options.form) { + self.form(options.form) + } + + if (options.formData) { + var formData = options.formData + var requestForm = self.form() + var appendFormValue = function (key, value) { + if (value && value.hasOwnProperty('value') && value.hasOwnProperty('options')) { + requestForm.append(key, value.value, value.options) + } else { + requestForm.append(key, value) + } + } + for (var formKey in formData) { + if (formData.hasOwnProperty(formKey)) { + var formValue = formData[formKey] + if (formValue instanceof Array) { + for (var j = 0; j < formValue.length; j++) { + appendFormValue(formKey, formValue[j]) + } + } else { + appendFormValue(formKey, formValue) + } + } + } + } + + if (options.qs) { + self.qs(options.qs) + } + + if (self.uri.path) { + self.path = self.uri.path + } else { + self.path = self.uri.pathname + (self.uri.search || '') + } + + if (self.path.length === 0) { + self.path = '/' + } + + // Auth must happen last in case signing is dependent on other headers + if (options.aws) { + self.aws(options.aws) + } + + if (options.hawk) { + self.hawk(options.hawk) + } + + if (options.httpSignature) { + self.httpSignature(options.httpSignature) + } + + if (options.auth) { + if (Object.prototype.hasOwnProperty.call(options.auth, 'username')) { + options.auth.user = options.auth.username + } + if (Object.prototype.hasOwnProperty.call(options.auth, 'password')) { + options.auth.pass = options.auth.password + } + + self.auth( + options.auth.user, + options.auth.pass, + options.auth.sendImmediately, + options.auth.bearer + ) + } + + if (self.gzip && !self.hasHeader('accept-encoding')) { + self.setHeader('accept-encoding', 'gzip, deflate') + } + + if (self.uri.auth && !self.hasHeader('authorization')) { + var uriAuthPieces = self.uri.auth.split(':').map(function (item) { return self._qs.unescape(item) }) + self.auth(uriAuthPieces[0], uriAuthPieces.slice(1).join(':'), true) + } + + if (!self.tunnel && self.proxy && self.proxy.auth && !self.hasHeader('proxy-authorization')) { + var proxyAuthPieces = self.proxy.auth.split(':').map(function (item) { return self._qs.unescape(item) }) + var authHeader = 'Basic ' + toBase64(proxyAuthPieces.join(':')) + self.setHeader('proxy-authorization', authHeader) + } + + if (self.proxy && !self.tunnel) { + self.path = (self.uri.protocol + '//' + self.uri.host + self.path) + } + + if (options.json) { + self.json(options.json) + } + if (options.multipart) { + self.multipart(options.multipart) + } + + if (options.time) { + self.timing = true + + // NOTE: elapsedTime is deprecated in favor of .timings + self.elapsedTime = self.elapsedTime || 0 + } + + function setContentLength () { + if (isTypedArray(self.body)) { + self.body = Buffer.from(self.body) + } + + if (!self.hasHeader('content-length')) { + var length + if (typeof self.body === 'string') { + length = Buffer.byteLength(self.body) + } else if (Array.isArray(self.body)) { + length = self.body.reduce(function (a, b) { return a + b.length }, 0) + } else { + length = self.body.length + } + + if (length) { + self.setHeader('content-length', length) + } else { + self.emit('error', new Error('Argument error, options.body.')) + } + } + } + if (self.body && !isstream(self.body)) { + setContentLength() + } + + if (options.oauth) { + self.oauth(options.oauth) + } else if (self._oauth.params && self.hasHeader('authorization')) { + self.oauth(self._oauth.params) + } + + var protocol = self.proxy && !self.tunnel ? self.proxy.protocol : self.uri.protocol + var defaultModules = {'http:': http, 'https:': https} + var httpModules = self.httpModules || {} + + self.httpModule = httpModules[protocol] || defaultModules[protocol] + + if (!self.httpModule) { + return self.emit('error', new Error('Invalid protocol: ' + protocol)) + } + + if (options.ca) { + self.ca = options.ca + } + + if (!self.agent) { + if (options.agentOptions) { + self.agentOptions = options.agentOptions + } + + if (options.agentClass) { + self.agentClass = options.agentClass + } else if (options.forever) { + var v = version() + // use ForeverAgent in node 0.10- only + if (v.major === 0 && v.minor <= 10) { + self.agentClass = protocol === 'http:' ? ForeverAgent : ForeverAgent.SSL + } else { + self.agentClass = self.httpModule.Agent + self.agentOptions = self.agentOptions || {} + self.agentOptions.keepAlive = true + } + } else { + self.agentClass = self.httpModule.Agent + } + } + + if (self.pool === false) { + self.agent = false + } else { + self.agent = self.agent || self.getNewAgent() + } + + self.on('pipe', function (src) { + if (self.ntick && self._started) { + self.emit('error', new Error('You cannot pipe to this stream after the outbound request has started.')) + } + self.src = src + if (isReadStream(src)) { + if (!self.hasHeader('content-type')) { + self.setHeader('content-type', mime.lookup(src.path)) + } + } else { + if (src.headers) { + for (var i in src.headers) { + if (!self.hasHeader(i)) { + self.setHeader(i, src.headers[i]) + } + } + } + if (self._json && !self.hasHeader('content-type')) { + self.setHeader('content-type', 'application/json') + } + if (src.method && !self.explicitMethod) { + self.method = src.method + } + } + + // self.on('pipe', function () { + // console.error('You have already piped to this stream. Pipeing twice is likely to break the request.') + // }) + }) + + defer(function () { + if (self._aborted) { + return + } + + var end = function () { + if (self._form) { + if (!self._auth.hasAuth) { + self._form.pipe(self) + } else if (self._auth.hasAuth && self._auth.sentAuth) { + self._form.pipe(self) + } + } + if (self._multipart && self._multipart.chunked) { + self._multipart.body.pipe(self) + } + if (self.body) { + if (isstream(self.body)) { + self.body.pipe(self) + } else { + setContentLength() + if (Array.isArray(self.body)) { + self.body.forEach(function (part) { + self.write(part) + }) + } else { + self.write(self.body) + } + self.end() + } + } else if (self.requestBodyStream) { + console.warn('options.requestBodyStream is deprecated, please pass the request object to stream.pipe.') + self.requestBodyStream.pipe(self) + } else if (!self.src) { + if (self._auth.hasAuth && !self._auth.sentAuth) { + self.end() + return + } + if (self.method !== 'GET' && typeof self.method !== 'undefined') { + self.setHeader('content-length', 0) + } + self.end() + } + } + + if (self._form && !self.hasHeader('content-length')) { + // Before ending the request, we had to compute the length of the whole form, asyncly + self.setHeader(self._form.getHeaders(), true) + self._form.getLength(function (err, length) { + if (!err && !isNaN(length)) { + self.setHeader('content-length', length) + } + end() + }) + } else { + end() + } + + self.ntick = true + }) +} + +Request.prototype.getNewAgent = function () { + var self = this + var Agent = self.agentClass + var options = {} + if (self.agentOptions) { + for (var i in self.agentOptions) { + options[i] = self.agentOptions[i] + } + } + if (self.ca) { + options.ca = self.ca + } + if (self.ciphers) { + options.ciphers = self.ciphers + } + if (self.secureProtocol) { + options.secureProtocol = self.secureProtocol + } + if (self.secureOptions) { + options.secureOptions = self.secureOptions + } + if (typeof self.rejectUnauthorized !== 'undefined') { + options.rejectUnauthorized = self.rejectUnauthorized + } + + if (self.cert && self.key) { + options.key = self.key + options.cert = self.cert + } + + if (self.pfx) { + options.pfx = self.pfx + } + + if (self.passphrase) { + options.passphrase = self.passphrase + } + + var poolKey = '' + + // different types of agents are in different pools + if (Agent !== self.httpModule.Agent) { + poolKey += Agent.name + } + + // ca option is only relevant if proxy or destination are https + var proxy = self.proxy + if (typeof proxy === 'string') { + proxy = url.parse(proxy) + } + var isHttps = (proxy && proxy.protocol === 'https:') || this.uri.protocol === 'https:' + + if (isHttps) { + if (options.ca) { + if (poolKey) { + poolKey += ':' + } + poolKey += options.ca + } + + if (typeof options.rejectUnauthorized !== 'undefined') { + if (poolKey) { + poolKey += ':' + } + poolKey += options.rejectUnauthorized + } + + if (options.cert) { + if (poolKey) { + poolKey += ':' + } + poolKey += options.cert.toString('ascii') + options.key.toString('ascii') + } + + if (options.pfx) { + if (poolKey) { + poolKey += ':' + } + poolKey += options.pfx.toString('ascii') + } + + if (options.ciphers) { + if (poolKey) { + poolKey += ':' + } + poolKey += options.ciphers + } + + if (options.secureProtocol) { + if (poolKey) { + poolKey += ':' + } + poolKey += options.secureProtocol + } + + if (options.secureOptions) { + if (poolKey) { + poolKey += ':' + } + poolKey += options.secureOptions + } + } + + if (self.pool === globalPool && !poolKey && Object.keys(options).length === 0 && self.httpModule.globalAgent) { + // not doing anything special. Use the globalAgent + return self.httpModule.globalAgent + } + + // we're using a stored agent. Make sure it's protocol-specific + poolKey = self.uri.protocol + poolKey + + // generate a new agent for this setting if none yet exists + if (!self.pool[poolKey]) { + self.pool[poolKey] = new Agent(options) + // properly set maxSockets on new agents + if (self.pool.maxSockets) { + self.pool[poolKey].maxSockets = self.pool.maxSockets + } + } + + return self.pool[poolKey] +} + +Request.prototype.start = function () { + // start() is called once we are ready to send the outgoing HTTP request. + // this is usually called on the first write(), end() or on nextTick() + var self = this + + if (self.timing) { + // All timings will be relative to this request's startTime. In order to do this, + // we need to capture the wall-clock start time (via Date), immediately followed + // by the high-resolution timer (via now()). While these two won't be set + // at the _exact_ same time, they should be close enough to be able to calculate + // high-resolution, monotonically non-decreasing timestamps relative to startTime. + var startTime = new Date().getTime() + var startTimeNow = now() + } + + if (self._aborted) { + return + } + + self._started = true + self.method = self.method || 'GET' + self.href = self.uri.href + + if (self.src && self.src.stat && self.src.stat.size && !self.hasHeader('content-length')) { + self.setHeader('content-length', self.src.stat.size) + } + if (self._aws) { + self.aws(self._aws, true) + } + + // We have a method named auth, which is completely different from the http.request + // auth option. If we don't remove it, we're gonna have a bad time. + var reqOptions = copy(self) + delete reqOptions.auth + + debug('make request', self.uri.href) + + // node v6.8.0 now supports a `timeout` value in `http.request()`, but we + // should delete it for now since we handle timeouts manually for better + // consistency with node versions before v6.8.0 + delete reqOptions.timeout + + try { + self.req = self.httpModule.request(reqOptions) + } catch (err) { + self.emit('error', err) + return + } + + if (self.timing) { + self.startTime = startTime + self.startTimeNow = startTimeNow + + // Timing values will all be relative to startTime (by comparing to startTimeNow + // so we have an accurate clock) + self.timings = {} + } + + var timeout + if (self.timeout && !self.timeoutTimer) { + if (self.timeout < 0) { + timeout = 0 + } else if (typeof self.timeout === 'number' && isFinite(self.timeout)) { + timeout = self.timeout + } + } + + self.req.on('response', self.onRequestResponse.bind(self)) + self.req.on('error', self.onRequestError.bind(self)) + self.req.on('drain', function () { + self.emit('drain') + }) + + self.req.on('socket', function (socket) { + // `._connecting` was the old property which was made public in node v6.1.0 + var isConnecting = socket._connecting || socket.connecting + if (self.timing) { + self.timings.socket = now() - self.startTimeNow + + if (isConnecting) { + var onLookupTiming = function () { + self.timings.lookup = now() - self.startTimeNow + } + + var onConnectTiming = function () { + self.timings.connect = now() - self.startTimeNow + } + + socket.once('lookup', onLookupTiming) + socket.once('connect', onConnectTiming) + + // clean up timing event listeners if needed on error + self.req.once('error', function () { + socket.removeListener('lookup', onLookupTiming) + socket.removeListener('connect', onConnectTiming) + }) + } + } + + var setReqTimeout = function () { + // This timeout sets the amount of time to wait *between* bytes sent + // from the server once connected. + // + // In particular, it's useful for erroring if the server fails to send + // data halfway through streaming a response. + self.req.setTimeout(timeout, function () { + if (self.req) { + self.abort() + var e = new Error('ESOCKETTIMEDOUT') + e.code = 'ESOCKETTIMEDOUT' + e.connect = false + self.emit('error', e) + } + }) + } + if (timeout !== undefined) { + // Only start the connection timer if we're actually connecting a new + // socket, otherwise if we're already connected (because this is a + // keep-alive connection) do not bother. This is important since we won't + // get a 'connect' event for an already connected socket. + if (isConnecting) { + var onReqSockConnect = function () { + socket.removeListener('connect', onReqSockConnect) + self.clearTimeout() + setReqTimeout() + } + + socket.on('connect', onReqSockConnect) + + self.req.on('error', function (err) { // eslint-disable-line handle-callback-err + socket.removeListener('connect', onReqSockConnect) + }) + + // Set a timeout in memory - this block will throw if the server takes more + // than `timeout` to write the HTTP status and headers (corresponding to + // the on('response') event on the client). NB: this measures wall-clock + // time, not the time between bytes sent by the server. + self.timeoutTimer = setTimeout(function () { + socket.removeListener('connect', onReqSockConnect) + self.abort() + var e = new Error('ETIMEDOUT') + e.code = 'ETIMEDOUT' + e.connect = true + self.emit('error', e) + }, timeout) + } else { + // We're already connected + setReqTimeout() + } + } + self.emit('socket', socket) + }) + + self.emit('request', self.req) +} + +Request.prototype.onRequestError = function (error) { + var self = this + if (self._aborted) { + return + } + if (self.req && self.req._reusedSocket && error.code === 'ECONNRESET' && + self.agent.addRequestNoreuse) { + self.agent = { addRequest: self.agent.addRequestNoreuse.bind(self.agent) } + self.start() + self.req.end() + return + } + self.clearTimeout() + self.emit('error', error) +} + +Request.prototype.onRequestResponse = function (response) { + var self = this + + if (self.timing) { + self.timings.response = now() - self.startTimeNow + } + + debug('onRequestResponse', self.uri.href, response.statusCode, response.headers) + response.on('end', function () { + if (self.timing) { + self.timings.end = now() - self.startTimeNow + response.timingStart = self.startTime + + // fill in the blanks for any periods that didn't trigger, such as + // no lookup or connect due to keep alive + if (!self.timings.socket) { + self.timings.socket = 0 + } + if (!self.timings.lookup) { + self.timings.lookup = self.timings.socket + } + if (!self.timings.connect) { + self.timings.connect = self.timings.lookup + } + if (!self.timings.response) { + self.timings.response = self.timings.connect + } + + debug('elapsed time', self.timings.end) + + // elapsedTime includes all redirects + self.elapsedTime += Math.round(self.timings.end) + + // NOTE: elapsedTime is deprecated in favor of .timings + response.elapsedTime = self.elapsedTime + + // timings is just for the final fetch + response.timings = self.timings + + // pre-calculate phase timings as well + response.timingPhases = { + wait: self.timings.socket, + dns: self.timings.lookup - self.timings.socket, + tcp: self.timings.connect - self.timings.lookup, + firstByte: self.timings.response - self.timings.connect, + download: self.timings.end - self.timings.response, + total: self.timings.end + } + } + debug('response end', self.uri.href, response.statusCode, response.headers) + }) + + if (self._aborted) { + debug('aborted', self.uri.href) + response.resume() + return + } + + self.response = response + response.request = self + response.toJSON = responseToJSON + + // XXX This is different on 0.10, because SSL is strict by default + if (self.httpModule === https && + self.strictSSL && (!response.hasOwnProperty('socket') || + !response.socket.authorized)) { + debug('strict ssl error', self.uri.href) + var sslErr = response.hasOwnProperty('socket') ? response.socket.authorizationError : self.uri.href + ' does not support SSL' + self.emit('error', new Error('SSL Error: ' + sslErr)) + return + } + + // Save the original host before any redirect (if it changes, we need to + // remove any authorization headers). Also remember the case of the header + // name because lots of broken servers expect Host instead of host and we + // want the caller to be able to specify this. + self.originalHost = self.getHeader('host') + if (!self.originalHostHeaderName) { + self.originalHostHeaderName = self.hasHeader('host') + } + if (self.setHost) { + self.removeHeader('host') + } + self.clearTimeout() + + var targetCookieJar = (self._jar && self._jar.setCookie) ? self._jar : globalCookieJar + var addCookie = function (cookie) { + // set the cookie if it's domain in the href's domain. + try { + targetCookieJar.setCookie(cookie, self.uri.href, {ignoreError: true}) + } catch (e) { + self.emit('error', e) + } + } + + response.caseless = caseless(response.headers) + + if (response.caseless.has('set-cookie') && (!self._disableCookies)) { + var headerName = response.caseless.has('set-cookie') + if (Array.isArray(response.headers[headerName])) { + response.headers[headerName].forEach(addCookie) + } else { + addCookie(response.headers[headerName]) + } + } + + if (self._redirect.onResponse(response)) { + return // Ignore the rest of the response + } else { + // Be a good stream and emit end when the response is finished. + // Hack to emit end on close because of a core bug that never fires end + response.on('close', function () { + if (!self._ended) { + self.response.emit('end') + } + }) + + response.once('end', function () { + self._ended = true + }) + + var noBody = function (code) { + return ( + self.method === 'HEAD' || + // Informational + (code >= 100 && code < 200) || + // No Content + code === 204 || + // Not Modified + code === 304 + ) + } + + var responseContent + if (self.gzip && !noBody(response.statusCode)) { + var contentEncoding = response.headers['content-encoding'] || 'identity' + contentEncoding = contentEncoding.trim().toLowerCase() + + // Be more lenient with decoding compressed responses, since (very rarely) + // servers send slightly invalid gzip responses that are still accepted + // by common browsers. + // Always using Z_SYNC_FLUSH is what cURL does. + var zlibOptions = { + flush: zlib.Z_SYNC_FLUSH, + finishFlush: zlib.Z_SYNC_FLUSH + } + + if (contentEncoding === 'gzip') { + responseContent = zlib.createGunzip(zlibOptions) + response.pipe(responseContent) + } else if (contentEncoding === 'deflate') { + responseContent = zlib.createInflate(zlibOptions) + response.pipe(responseContent) + } else { + // Since previous versions didn't check for Content-Encoding header, + // ignore any invalid values to preserve backwards-compatibility + if (contentEncoding !== 'identity') { + debug('ignoring unrecognized Content-Encoding ' + contentEncoding) + } + responseContent = response + } + } else { + responseContent = response + } + + if (self.encoding) { + if (self.dests.length !== 0) { + console.error('Ignoring encoding parameter as this stream is being piped to another stream which makes the encoding option invalid.') + } else { + responseContent.setEncoding(self.encoding) + } + } + + if (self._paused) { + responseContent.pause() + } + + self.responseContent = responseContent + + self.emit('response', response) + + self.dests.forEach(function (dest) { + self.pipeDest(dest) + }) + + responseContent.on('data', function (chunk) { + if (self.timing && !self.responseStarted) { + self.responseStartTime = (new Date()).getTime() + + // NOTE: responseStartTime is deprecated in favor of .timings + response.responseStartTime = self.responseStartTime + } + self._destdata = true + self.emit('data', chunk) + }) + responseContent.once('end', function (chunk) { + self.emit('end', chunk) + }) + responseContent.on('error', function (error) { + self.emit('error', error) + }) + responseContent.on('close', function () { self.emit('close') }) + + if (self.callback) { + self.readResponseBody(response) + } else { // if no callback + self.on('end', function () { + if (self._aborted) { + debug('aborted', self.uri.href) + return + } + self.emit('complete', response) + }) + } + } + debug('finish init function', self.uri.href) +} + +Request.prototype.readResponseBody = function (response) { + var self = this + debug("reading response's body") + var buffers = [] + var bufferLength = 0 + var strings = [] + + self.on('data', function (chunk) { + if (!Buffer.isBuffer(chunk)) { + strings.push(chunk) + } else if (chunk.length) { + bufferLength += chunk.length + buffers.push(chunk) + } + }) + self.on('end', function () { + debug('end event', self.uri.href) + if (self._aborted) { + debug('aborted', self.uri.href) + // `buffer` is defined in the parent scope and used in a closure it exists for the life of the request. + // This can lead to leaky behavior if the user retains a reference to the request object. + buffers = [] + bufferLength = 0 + return + } + + if (bufferLength) { + debug('has body', self.uri.href, bufferLength) + response.body = Buffer.concat(buffers, bufferLength) + if (self.encoding !== null) { + response.body = response.body.toString(self.encoding) + } + // `buffer` is defined in the parent scope and used in a closure it exists for the life of the Request. + // This can lead to leaky behavior if the user retains a reference to the request object. + buffers = [] + bufferLength = 0 + } else if (strings.length) { + // The UTF8 BOM [0xEF,0xBB,0xBF] is converted to [0xFE,0xFF] in the JS UTC16/UCS2 representation. + // Strip this value out when the encoding is set to 'utf8', as upstream consumers won't expect it and it breaks JSON.parse(). + if (self.encoding === 'utf8' && strings[0].length > 0 && strings[0][0] === '\uFEFF') { + strings[0] = strings[0].substring(1) + } + response.body = strings.join('') + } + + if (self._json) { + try { + response.body = JSON.parse(response.body, self._jsonReviver) + } catch (e) { + debug('invalid JSON received', self.uri.href) + } + } + debug('emitting complete', self.uri.href) + if (typeof response.body === 'undefined' && !self._json) { + response.body = self.encoding === null ? Buffer.alloc(0) : '' + } + self.emit('complete', response, response.body) + }) +} + +Request.prototype.abort = function () { + var self = this + self._aborted = true + + if (self.req) { + self.req.abort() + } else if (self.response) { + self.response.destroy() + } + + self.clearTimeout() + self.emit('abort') +} + +Request.prototype.pipeDest = function (dest) { + var self = this + var response = self.response + // Called after the response is received + if (dest.headers && !dest.headersSent) { + if (response.caseless.has('content-type')) { + var ctname = response.caseless.has('content-type') + if (dest.setHeader) { + dest.setHeader(ctname, response.headers[ctname]) + } else { + dest.headers[ctname] = response.headers[ctname] + } + } + + if (response.caseless.has('content-length')) { + var clname = response.caseless.has('content-length') + if (dest.setHeader) { + dest.setHeader(clname, response.headers[clname]) + } else { + dest.headers[clname] = response.headers[clname] + } + } + } + if (dest.setHeader && !dest.headersSent) { + for (var i in response.headers) { + // If the response content is being decoded, the Content-Encoding header + // of the response doesn't represent the piped content, so don't pass it. + if (!self.gzip || i !== 'content-encoding') { + dest.setHeader(i, response.headers[i]) + } + } + dest.statusCode = response.statusCode + } + if (self.pipefilter) { + self.pipefilter(response, dest) + } +} + +Request.prototype.qs = function (q, clobber) { + var self = this + var base + if (!clobber && self.uri.query) { + base = self._qs.parse(self.uri.query) + } else { + base = {} + } + + for (var i in q) { + base[i] = q[i] + } + + var qs = self._qs.stringify(base) + + if (qs === '') { + return self + } + + self.uri = url.parse(self.uri.href.split('?')[0] + '?' + qs) + self.url = self.uri + self.path = self.uri.path + + if (self.uri.host === 'unix') { + self.enableUnixSocket() + } + + return self +} +Request.prototype.form = function (form) { + var self = this + if (form) { + if (!/^application\/x-www-form-urlencoded\b/.test(self.getHeader('content-type'))) { + self.setHeader('content-type', 'application/x-www-form-urlencoded') + } + self.body = (typeof form === 'string') + ? self._qs.rfc3986(form.toString('utf8')) + : self._qs.stringify(form).toString('utf8') + return self + } + // create form-data object + self._form = new FormData() + self._form.on('error', function (err) { + err.message = 'form-data: ' + err.message + self.emit('error', err) + self.abort() + }) + return self._form +} +Request.prototype.multipart = function (multipart) { + var self = this + + self._multipart.onRequest(multipart) + + if (!self._multipart.chunked) { + self.body = self._multipart.body + } + + return self +} +Request.prototype.json = function (val) { + var self = this + + if (!self.hasHeader('accept')) { + self.setHeader('accept', 'application/json') + } + + if (typeof self.jsonReplacer === 'function') { + self._jsonReplacer = self.jsonReplacer + } + + self._json = true + if (typeof val === 'boolean') { + if (self.body !== undefined) { + if (!/^application\/x-www-form-urlencoded\b/.test(self.getHeader('content-type'))) { + self.body = safeStringify(self.body, self._jsonReplacer) + } else { + self.body = self._qs.rfc3986(self.body) + } + if (!self.hasHeader('content-type')) { + self.setHeader('content-type', 'application/json') + } + } + } else { + self.body = safeStringify(val, self._jsonReplacer) + if (!self.hasHeader('content-type')) { + self.setHeader('content-type', 'application/json') + } + } + + if (typeof self.jsonReviver === 'function') { + self._jsonReviver = self.jsonReviver + } + + return self +} +Request.prototype.getHeader = function (name, headers) { + var self = this + var result, re, match + if (!headers) { + headers = self.headers + } + Object.keys(headers).forEach(function (key) { + if (key.length !== name.length) { + return + } + re = new RegExp(name, 'i') + match = key.match(re) + if (match) { + result = headers[key] + } + }) + return result +} +Request.prototype.enableUnixSocket = function () { + // Get the socket & request paths from the URL + var unixParts = this.uri.path.split(':') + var host = unixParts[0] + var path = unixParts[1] + // Apply unix properties to request + this.socketPath = host + this.uri.pathname = path + this.uri.path = path + this.uri.host = host + this.uri.hostname = host + this.uri.isUnix = true +} + +Request.prototype.auth = function (user, pass, sendImmediately, bearer) { + var self = this + + self._auth.onRequest(user, pass, sendImmediately, bearer) + + return self +} +Request.prototype.aws = function (opts, now) { + var self = this + + if (!now) { + self._aws = opts + return self + } + + if (opts.sign_version === 4 || opts.sign_version === '4') { + // use aws4 + var options = { + host: self.uri.host, + path: self.uri.path, + method: self.method, + headers: self.headers, + body: self.body + } + if (opts.service) { + options.service = opts.service + } + var signRes = aws4.sign(options, { + accessKeyId: opts.key, + secretAccessKey: opts.secret, + sessionToken: opts.session + }) + self.setHeader('authorization', signRes.headers.Authorization) + self.setHeader('x-amz-date', signRes.headers['X-Amz-Date']) + if (signRes.headers['X-Amz-Security-Token']) { + self.setHeader('x-amz-security-token', signRes.headers['X-Amz-Security-Token']) + } + } else { + // default: use aws-sign2 + var date = new Date() + self.setHeader('date', date.toUTCString()) + var auth = { + key: opts.key, + secret: opts.secret, + verb: self.method.toUpperCase(), + date: date, + contentType: self.getHeader('content-type') || '', + md5: self.getHeader('content-md5') || '', + amazonHeaders: aws2.canonicalizeHeaders(self.headers) + } + var path = self.uri.path + if (opts.bucket && path) { + auth.resource = '/' + opts.bucket + path + } else if (opts.bucket && !path) { + auth.resource = '/' + opts.bucket + } else if (!opts.bucket && path) { + auth.resource = path + } else if (!opts.bucket && !path) { + auth.resource = '/' + } + auth.resource = aws2.canonicalizeResource(auth.resource) + self.setHeader('authorization', aws2.authorization(auth)) + } + + return self +} +Request.prototype.httpSignature = function (opts) { + var self = this + httpSignature.signRequest({ + getHeader: function (header) { + return self.getHeader(header, self.headers) + }, + setHeader: function (header, value) { + self.setHeader(header, value) + }, + method: self.method, + path: self.path + }, opts) + debug('httpSignature authorization', self.getHeader('authorization')) + + return self +} +Request.prototype.hawk = function (opts) { + var self = this + self.setHeader('Authorization', hawk.header(self.uri, self.method, opts)) +} +Request.prototype.oauth = function (_oauth) { + var self = this + + self._oauth.onRequest(_oauth) + + return self +} + +Request.prototype.jar = function (jar) { + var self = this + var cookies + + if (self._redirect.redirectsFollowed === 0) { + self.originalCookieHeader = self.getHeader('cookie') + } + + if (!jar) { + // disable cookies + cookies = false + self._disableCookies = true + } else { + var targetCookieJar = jar.getCookieString ? jar : globalCookieJar + var urihref = self.uri.href + // fetch cookie in the Specified host + if (targetCookieJar) { + cookies = targetCookieJar.getCookieString(urihref) + } + } + + // if need cookie and cookie is not empty + if (cookies && cookies.length) { + if (self.originalCookieHeader) { + // Don't overwrite existing Cookie header + self.setHeader('cookie', self.originalCookieHeader + '; ' + cookies) + } else { + self.setHeader('cookie', cookies) + } + } + self._jar = jar + return self +} + +// Stream API +Request.prototype.pipe = function (dest, opts) { + var self = this + + if (self.response) { + if (self._destdata) { + self.emit('error', new Error('You cannot pipe after data has been emitted from the response.')) + } else if (self._ended) { + self.emit('error', new Error('You cannot pipe after the response has been ended.')) + } else { + stream.Stream.prototype.pipe.call(self, dest, opts) + self.pipeDest(dest) + return dest + } + } else { + self.dests.push(dest) + stream.Stream.prototype.pipe.call(self, dest, opts) + return dest + } +} +Request.prototype.write = function () { + var self = this + if (self._aborted) { return } + + if (!self._started) { + self.start() + } + if (self.req) { + return self.req.write.apply(self.req, arguments) + } +} +Request.prototype.end = function (chunk) { + var self = this + if (self._aborted) { return } + + if (chunk) { + self.write(chunk) + } + if (!self._started) { + self.start() + } + if (self.req) { + self.req.end() + } +} +Request.prototype.pause = function () { + var self = this + if (!self.responseContent) { + self._paused = true + } else { + self.responseContent.pause.apply(self.responseContent, arguments) + } +} +Request.prototype.resume = function () { + var self = this + if (!self.responseContent) { + self._paused = false + } else { + self.responseContent.resume.apply(self.responseContent, arguments) + } +} +Request.prototype.destroy = function () { + var self = this + this.clearTimeout() + if (!self._ended) { + self.end() + } else if (self.response) { + self.response.destroy() + } +} + +Request.prototype.clearTimeout = function () { + if (this.timeoutTimer) { + clearTimeout(this.timeoutTimer) + this.timeoutTimer = null + } +} + +Request.defaultProxyHeaderWhiteList = + Tunnel.defaultProxyHeaderWhiteList.slice() + +Request.defaultProxyHeaderExclusiveList = + Tunnel.defaultProxyHeaderExclusiveList.slice() + +// Exports + +Request.prototype.toJSON = requestToJSON +module.exports = Request + + +/***/ }), + +/***/ 459: +/***/ (function(module) { + +// generated by genversion +module.exports = '2.5.0' + + +/***/ }), + +/***/ 461: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + + +var url = __webpack_require__(835) +var tunnel = __webpack_require__(243) + +var defaultProxyHeaderWhiteList = [ + 'accept', + 'accept-charset', + 'accept-encoding', + 'accept-language', + 'accept-ranges', + 'cache-control', + 'content-encoding', + 'content-language', + 'content-location', + 'content-md5', + 'content-range', + 'content-type', + 'connection', + 'date', + 'expect', + 'max-forwards', + 'pragma', + 'referer', + 'te', + 'user-agent', + 'via' +] + +var defaultProxyHeaderExclusiveList = [ + 'proxy-authorization' +] + +function constructProxyHost (uriObject) { + var port = uriObject.port + var protocol = uriObject.protocol + var proxyHost = uriObject.hostname + ':' + + if (port) { + proxyHost += port + } else if (protocol === 'https:') { + proxyHost += '443' + } else { + proxyHost += '80' + } + + return proxyHost +} + +function constructProxyHeaderWhiteList (headers, proxyHeaderWhiteList) { + var whiteList = proxyHeaderWhiteList + .reduce(function (set, header) { + set[header.toLowerCase()] = true + return set + }, {}) + + return Object.keys(headers) + .filter(function (header) { + return whiteList[header.toLowerCase()] + }) + .reduce(function (set, header) { + set[header] = headers[header] + return set + }, {}) +} + +function constructTunnelOptions (request, proxyHeaders) { + var proxy = request.proxy + + var tunnelOptions = { + proxy: { + host: proxy.hostname, + port: +proxy.port, + proxyAuth: proxy.auth, + headers: proxyHeaders + }, + headers: request.headers, + ca: request.ca, + cert: request.cert, + key: request.key, + passphrase: request.passphrase, + pfx: request.pfx, + ciphers: request.ciphers, + rejectUnauthorized: request.rejectUnauthorized, + secureOptions: request.secureOptions, + secureProtocol: request.secureProtocol + } + + return tunnelOptions +} + +function constructTunnelFnName (uri, proxy) { + var uriProtocol = (uri.protocol === 'https:' ? 'https' : 'http') + var proxyProtocol = (proxy.protocol === 'https:' ? 'Https' : 'Http') + return [uriProtocol, proxyProtocol].join('Over') +} + +function getTunnelFn (request) { + var uri = request.uri + var proxy = request.proxy + var tunnelFnName = constructTunnelFnName(uri, proxy) + return tunnel[tunnelFnName] +} + +function Tunnel (request) { + this.request = request + this.proxyHeaderWhiteList = defaultProxyHeaderWhiteList + this.proxyHeaderExclusiveList = [] + if (typeof request.tunnel !== 'undefined') { + this.tunnelOverride = request.tunnel + } +} + +Tunnel.prototype.isEnabled = function () { + var self = this + var request = self.request + // Tunnel HTTPS by default. Allow the user to override this setting. + + // If self.tunnelOverride is set (the user specified a value), use it. + if (typeof self.tunnelOverride !== 'undefined') { + return self.tunnelOverride + } + + // If the destination is HTTPS, tunnel. + if (request.uri.protocol === 'https:') { + return true + } + + // Otherwise, do not use tunnel. + return false +} + +Tunnel.prototype.setup = function (options) { + var self = this + var request = self.request + + options = options || {} + + if (typeof request.proxy === 'string') { + request.proxy = url.parse(request.proxy) + } + + if (!request.proxy || !request.tunnel) { + return false + } + + // Setup Proxy Header Exclusive List and White List + if (options.proxyHeaderWhiteList) { + self.proxyHeaderWhiteList = options.proxyHeaderWhiteList + } + if (options.proxyHeaderExclusiveList) { + self.proxyHeaderExclusiveList = options.proxyHeaderExclusiveList + } + + var proxyHeaderExclusiveList = self.proxyHeaderExclusiveList.concat(defaultProxyHeaderExclusiveList) + var proxyHeaderWhiteList = self.proxyHeaderWhiteList.concat(proxyHeaderExclusiveList) + + // Setup Proxy Headers and Proxy Headers Host + // Only send the Proxy White Listed Header names + var proxyHeaders = constructProxyHeaderWhiteList(request.headers, proxyHeaderWhiteList) + proxyHeaders.host = constructProxyHost(request.uri) + + proxyHeaderExclusiveList.forEach(request.removeHeader, request) + + // Set Agent from Tunnel Data + var tunnelFn = getTunnelFn(request) + var tunnelOptions = constructTunnelOptions(request, proxyHeaders) + request.agent = tunnelFn(tunnelOptions) + + return true +} + +Tunnel.defaultProxyHeaderWhiteList = defaultProxyHeaderWhiteList +Tunnel.defaultProxyHeaderExclusiveList = defaultProxyHeaderExclusiveList +exports.Tunnel = Tunnel + + +/***/ }), + +/***/ 469: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + + +var uuid = __webpack_require__(826) +var CombinedStream = __webpack_require__(547) +var isstream = __webpack_require__(382) +var Buffer = __webpack_require__(149).Buffer + +function Multipart (request) { + this.request = request + this.boundary = uuid() + this.chunked = false + this.body = null +} + +Multipart.prototype.isChunked = function (options) { + var self = this + var chunked = false + var parts = options.data || options + + if (!parts.forEach) { + self.request.emit('error', new Error('Argument error, options.multipart.')) + } + + if (options.chunked !== undefined) { + chunked = options.chunked + } + + if (self.request.getHeader('transfer-encoding') === 'chunked') { + chunked = true + } + + if (!chunked) { + parts.forEach(function (part) { + if (typeof part.body === 'undefined') { + self.request.emit('error', new Error('Body attribute missing in multipart.')) + } + if (isstream(part.body)) { + chunked = true + } + }) + } + + return chunked +} + +Multipart.prototype.setHeaders = function (chunked) { + var self = this + + if (chunked && !self.request.hasHeader('transfer-encoding')) { + self.request.setHeader('transfer-encoding', 'chunked') + } + + var header = self.request.getHeader('content-type') + + if (!header || header.indexOf('multipart') === -1) { + self.request.setHeader('content-type', 'multipart/related; boundary=' + self.boundary) + } else { + if (header.indexOf('boundary') !== -1) { + self.boundary = header.replace(/.*boundary=([^\s;]+).*/, '$1') + } else { + self.request.setHeader('content-type', header + '; boundary=' + self.boundary) + } + } +} + +Multipart.prototype.build = function (parts, chunked) { + var self = this + var body = chunked ? new CombinedStream() : [] + + function add (part) { + if (typeof part === 'number') { + part = part.toString() + } + return chunked ? body.append(part) : body.push(Buffer.from(part)) + } + + if (self.request.preambleCRLF) { + add('\r\n') + } + + parts.forEach(function (part) { + var preamble = '--' + self.boundary + '\r\n' + Object.keys(part).forEach(function (key) { + if (key === 'body') { return } + preamble += key + ': ' + part[key] + '\r\n' + }) + preamble += '\r\n' + add(preamble) + add(part.body) + add('\r\n') + }) + add('--' + self.boundary + '--') + + if (self.request.postambleCRLF) { + add('\r\n') + } + + return body +} + +Multipart.prototype.onRequest = function (options) { + var self = this + + var chunked = self.isChunked(options) + var parts = options.data || options + + self.setHeaders(chunked) + self.chunked = chunked + self.body = self.build(parts, chunked) +} + +exports.Multipart = Multipart + + +/***/ }), + +/***/ 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 +//----------------------------------------------------------------------- +/** + * 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 + +/***/ }), + +/***/ 477: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright (c) 2012, Mark Cavage. All rights reserved. +// Copyright 2015 Joyent, Inc. + +var assert = __webpack_require__(357); +var Stream = __webpack_require__(413).Stream; +var util = __webpack_require__(669); + + +///--- Globals + +/* JSSTYLED */ +var UUID_REGEXP = /^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/; + + +///--- Internal + +function _capitalize(str) { + return (str.charAt(0).toUpperCase() + str.slice(1)); +} + +function _toss(name, expected, oper, arg, actual) { + throw new assert.AssertionError({ + message: util.format('%s (%s) is required', name, expected), + actual: (actual === undefined) ? typeof (arg) : actual(arg), + expected: expected, + operator: oper || '===', + stackStartFunction: _toss.caller + }); +} + +function _getClass(arg) { + return (Object.prototype.toString.call(arg).slice(8, -1)); +} + +function noop() { + // Why even bother with asserts? +} + + +///--- Exports + +var types = { + bool: { + check: function (arg) { return typeof (arg) === 'boolean'; } + }, + func: { + check: function (arg) { return typeof (arg) === 'function'; } + }, + string: { + check: function (arg) { return typeof (arg) === 'string'; } + }, + object: { + check: function (arg) { + return typeof (arg) === 'object' && arg !== null; + } + }, + number: { + check: function (arg) { + return typeof (arg) === 'number' && !isNaN(arg); + } + }, + finite: { + check: function (arg) { + return typeof (arg) === 'number' && !isNaN(arg) && isFinite(arg); + } + }, + buffer: { + check: function (arg) { return Buffer.isBuffer(arg); }, + operator: 'Buffer.isBuffer' + }, + array: { + check: function (arg) { return Array.isArray(arg); }, + operator: 'Array.isArray' + }, + stream: { + check: function (arg) { return arg instanceof Stream; }, + operator: 'instanceof', + actual: _getClass + }, + date: { + check: function (arg) { return arg instanceof Date; }, + operator: 'instanceof', + actual: _getClass + }, + regexp: { + check: function (arg) { return arg instanceof RegExp; }, + operator: 'instanceof', + actual: _getClass + }, + uuid: { + check: function (arg) { + return typeof (arg) === 'string' && UUID_REGEXP.test(arg); + }, + operator: 'isUUID' + } +}; + +function _setExports(ndebug) { + var keys = Object.keys(types); + var out; + + /* re-export standard assert */ + if (process.env.NODE_NDEBUG) { + out = noop; + } else { + out = function (arg, msg) { + if (!arg) { + _toss(msg, 'true', arg); + } + }; + } + + /* standard checks */ + keys.forEach(function (k) { + if (ndebug) { + out[k] = noop; + return; + } + var type = types[k]; + out[k] = function (arg, msg) { + if (!type.check(arg)) { + _toss(msg, k, type.operator, arg, type.actual); + } + }; + }); + + /* optional checks */ + keys.forEach(function (k) { + var name = 'optional' + _capitalize(k); + if (ndebug) { + out[name] = noop; + return; + } + var type = types[k]; + out[name] = function (arg, msg) { + if (arg === undefined || arg === null) { + return; + } + if (!type.check(arg)) { + _toss(msg, k, type.operator, arg, type.actual); + } + }; + }); + + /* arrayOf checks */ + keys.forEach(function (k) { + var name = 'arrayOf' + _capitalize(k); + if (ndebug) { + out[name] = noop; + return; + } + var type = types[k]; + var expected = '[' + k + ']'; + out[name] = function (arg, msg) { + if (!Array.isArray(arg)) { + _toss(msg, expected, type.operator, arg, type.actual); + } + var i; + for (i = 0; i < arg.length; i++) { + if (!type.check(arg[i])) { + _toss(msg, expected, type.operator, arg, type.actual); + } + } + }; + }); + + /* optionalArrayOf checks */ + keys.forEach(function (k) { + var name = 'optionalArrayOf' + _capitalize(k); + if (ndebug) { + out[name] = noop; + return; + } + var type = types[k]; + var expected = '[' + k + ']'; + out[name] = function (arg, msg) { + if (arg === undefined || arg === null) { + return; + } + if (!Array.isArray(arg)) { + _toss(msg, expected, type.operator, arg, type.actual); + } + var i; + for (i = 0; i < arg.length; i++) { + if (!type.check(arg[i])) { + _toss(msg, expected, type.operator, arg, type.actual); + } + } + }; + }); + + /* re-export built-in assertions */ + Object.keys(assert).forEach(function (k) { + if (k === 'AssertionError') { + out[k] = assert[k]; + return; + } + if (ndebug) { + out[k] = noop; + return; + } + out[k] = assert[k]; + }); + + /* export ourselves (for unit tests _only_) */ + out._setExports = _setExports; + + return out; +} + +module.exports = _setExports(process.env.NODE_NDEBUG); + + +/***/ }), + +/***/ 479: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_if(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + $it.level++; + var $nextValid = 'valid' + $it.level; + var $thenSch = it.schema['then'], + $elseSch = it.schema['else'], + $thenPresent = $thenSch !== undefined && (it.opts.strictKeywords ? typeof $thenSch == 'object' && Object.keys($thenSch).length > 0 : it.util.schemaHasRules($thenSch, it.RULES.all)), + $elsePresent = $elseSch !== undefined && (it.opts.strictKeywords ? typeof $elseSch == 'object' && Object.keys($elseSch).length > 0 : it.util.schemaHasRules($elseSch, it.RULES.all)), + $currentBaseId = $it.baseId; + if ($thenPresent || $elsePresent) { + var $ifClause; + $it.createErrors = false; + $it.schema = $schema; + $it.schemaPath = $schemaPath; + $it.errSchemaPath = $errSchemaPath; + out += ' var ' + ($errs) + ' = errors; var ' + ($valid) + ' = true; '; + var $wasComposite = it.compositeRule; + it.compositeRule = $it.compositeRule = true; + out += ' ' + (it.validate($it)) + ' '; + $it.baseId = $currentBaseId; + $it.createErrors = true; + out += ' errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; } '; + it.compositeRule = $it.compositeRule = $wasComposite; + if ($thenPresent) { + out += ' if (' + ($nextValid) + ') { '; + $it.schema = it.schema['then']; + $it.schemaPath = it.schemaPath + '.then'; + $it.errSchemaPath = it.errSchemaPath + '/then'; + out += ' ' + (it.validate($it)) + ' '; + $it.baseId = $currentBaseId; + out += ' ' + ($valid) + ' = ' + ($nextValid) + '; '; + if ($thenPresent && $elsePresent) { + $ifClause = 'ifClause' + $lvl; + out += ' var ' + ($ifClause) + ' = \'then\'; '; + } else { + $ifClause = '\'then\''; + } + out += ' } '; + if ($elsePresent) { + out += ' else { '; + } + } else { + out += ' if (!' + ($nextValid) + ') { '; + } + if ($elsePresent) { + $it.schema = it.schema['else']; + $it.schemaPath = it.schemaPath + '.else'; + $it.errSchemaPath = it.errSchemaPath + '/else'; + out += ' ' + (it.validate($it)) + ' '; + $it.baseId = $currentBaseId; + out += ' ' + ($valid) + ' = ' + ($nextValid) + '; '; + if ($thenPresent && $elsePresent) { + $ifClause = 'ifClause' + $lvl; + out += ' var ' + ($ifClause) + ' = \'else\'; '; + } else { + $ifClause = '\'else\''; + } + out += ' } '; + } + out += ' if (!' + ($valid) + ') { var err = '; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('if') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { failingKeyword: ' + ($ifClause) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should match "\' + ' + ($ifClause) + ' + \'" schema\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + out += '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError(vErrors); '; + } else { + out += ' validate.errors = vErrors; return false; '; + } + } + out += ' } '; + if ($breakOnError) { + out += ' else { '; + } + out = it.util.cleanUpCode(out); + } else { + if ($breakOnError) { + out += ' if (true) { '; + } + } + return out; +} + + +/***/ }), + +/***/ 496: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +var ruleModules = __webpack_require__(894) + , toHash = __webpack_require__(855).toHash; + +module.exports = function rules() { + var RULES = [ + { type: 'number', + rules: [ { 'maximum': ['exclusiveMaximum'] }, + { 'minimum': ['exclusiveMinimum'] }, 'multipleOf', 'format'] }, + { type: 'string', + rules: [ 'maxLength', 'minLength', 'pattern', 'format' ] }, + { type: 'array', + rules: [ 'maxItems', 'minItems', 'items', 'contains', 'uniqueItems' ] }, + { type: 'object', + rules: [ 'maxProperties', 'minProperties', 'required', 'dependencies', 'propertyNames', + { 'properties': ['additionalProperties', 'patternProperties'] } ] }, + { rules: [ '$ref', 'const', 'enum', 'not', 'anyOf', 'oneOf', 'allOf', 'if' ] } + ]; + + var ALL = [ 'type', '$comment' ]; + var KEYWORDS = [ + '$schema', '$id', 'id', '$data', '$async', 'title', + 'description', 'default', 'definitions', + 'examples', 'readOnly', 'writeOnly', + 'contentMediaType', 'contentEncoding', + 'additionalItems', 'then', 'else' + ]; + var TYPES = [ 'number', 'integer', 'string', 'array', 'object', 'boolean', 'null' ]; + RULES.all = toHash(ALL); + RULES.types = toHash(TYPES); + + RULES.forEach(function (group) { + group.rules = group.rules.map(function (keyword) { + var implKeywords; + if (typeof keyword == 'object') { + var key = Object.keys(keyword)[0]; + implKeywords = keyword[key]; + keyword = key; + implKeywords.forEach(function (k) { + ALL.push(k); + RULES.all[k] = true; + }); + } + ALL.push(keyword); + var rule = RULES.all[keyword] = { + keyword: keyword, + code: ruleModules[keyword], + implements: implKeywords + }; + return rule; + }); + + RULES.all.$comment = { + keyword: '$comment', + code: ruleModules.$comment + }; + + if (group.type) RULES.types[group.type] = group; + }); + + RULES.keywords = toHash(ALL.concat(KEYWORDS)); + RULES.custom = {}; + + return RULES; +}; + + +/***/ }), + +/***/ 500: +/***/ (function(module) { + +module.exports = defer; + +/** + * Runs provided function on next iteration of the event loop + * + * @param {function} fn - function to run + */ +function defer(fn) +{ + var nextTick = typeof setImmediate == 'function' + ? setImmediate + : ( + typeof process == 'object' && typeof process.nextTick == 'function' + ? process.nextTick + : null + ); + + if (nextTick) + { + nextTick(fn); + } + else + { + setTimeout(fn, 0); + } +} + + +/***/ }), + +/***/ 502: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2017 Joyent, Inc. + +module.exports = PrivateKey; + +var assert = __webpack_require__(477); +var Buffer = __webpack_require__(215).Buffer; +var algs = __webpack_require__(98); +var crypto = __webpack_require__(417); +var Fingerprint = __webpack_require__(400); +var Signature = __webpack_require__(575); +var errs = __webpack_require__(753); +var util = __webpack_require__(669); +var utils = __webpack_require__(270); +var dhe = __webpack_require__(290); +var generateECDSA = dhe.generateECDSA; +var generateED25519 = dhe.generateED25519; +var edCompat = __webpack_require__(363); +var nacl = __webpack_require__(196); + +var Key = __webpack_require__(852); + +var InvalidAlgorithmError = errs.InvalidAlgorithmError; +var KeyParseError = errs.KeyParseError; +var KeyEncryptedError = errs.KeyEncryptedError; + +var formats = {}; +formats['auto'] = __webpack_require__(241); +formats['pem'] = __webpack_require__(268); +formats['pkcs1'] = __webpack_require__(449); +formats['pkcs8'] = __webpack_require__(707); +formats['rfc4253'] = __webpack_require__(538); +formats['ssh-private'] = __webpack_require__(78); +formats['openssh'] = formats['ssh-private']; +formats['ssh'] = formats['ssh-private']; +formats['dnssec'] = __webpack_require__(982); + +function PrivateKey(opts) { + assert.object(opts, 'options'); + Key.call(this, opts); + + this._pubCache = undefined; +} +util.inherits(PrivateKey, Key); + +PrivateKey.formats = formats; + +PrivateKey.prototype.toBuffer = function (format, options) { + if (format === undefined) + format = 'pkcs1'; + assert.string(format, 'format'); + assert.object(formats[format], 'formats[format]'); + assert.optionalObject(options, 'options'); + + return (formats[format].write(this, options)); +}; + +PrivateKey.prototype.hash = function (algo, type) { + return (this.toPublic().hash(algo, type)); +}; + +PrivateKey.prototype.fingerprint = function (algo, type) { + return (this.toPublic().fingerprint(algo, type)); +}; + +PrivateKey.prototype.toPublic = function () { + if (this._pubCache) + return (this._pubCache); + + var algInfo = algs.info[this.type]; + var pubParts = []; + for (var i = 0; i < algInfo.parts.length; ++i) { + var p = algInfo.parts[i]; + pubParts.push(this.part[p]); + } + + this._pubCache = new Key({ + type: this.type, + source: this, + parts: pubParts + }); + if (this.comment) + this._pubCache.comment = this.comment; + return (this._pubCache); +}; + +PrivateKey.prototype.derive = function (newType) { + assert.string(newType, 'type'); + var priv, pub, pair; + + if (this.type === 'ed25519' && newType === 'curve25519') { + priv = this.part.k.data; + if (priv[0] === 0x00) + priv = priv.slice(1); + + pair = nacl.box.keyPair.fromSecretKey(new Uint8Array(priv)); + pub = Buffer.from(pair.publicKey); + + return (new PrivateKey({ + type: 'curve25519', + parts: [ + { name: 'A', data: utils.mpNormalize(pub) }, + { name: 'k', data: utils.mpNormalize(priv) } + ] + })); + } else if (this.type === 'curve25519' && newType === 'ed25519') { + priv = this.part.k.data; + if (priv[0] === 0x00) + priv = priv.slice(1); + + pair = nacl.sign.keyPair.fromSeed(new Uint8Array(priv)); + pub = Buffer.from(pair.publicKey); + + return (new PrivateKey({ + type: 'ed25519', + parts: [ + { name: 'A', data: utils.mpNormalize(pub) }, + { name: 'k', data: utils.mpNormalize(priv) } + ] + })); + } + throw (new Error('Key derivation not supported from ' + this.type + + ' to ' + newType)); +}; + +PrivateKey.prototype.createVerify = function (hashAlgo) { + return (this.toPublic().createVerify(hashAlgo)); +}; + +PrivateKey.prototype.createSign = function (hashAlgo) { + if (hashAlgo === undefined) + hashAlgo = this.defaultHashAlgorithm(); + assert.string(hashAlgo, 'hash algorithm'); + + /* ED25519 is not supported by OpenSSL, use a javascript impl. */ + if (this.type === 'ed25519' && edCompat !== undefined) + return (new edCompat.Signer(this, hashAlgo)); + if (this.type === 'curve25519') + throw (new Error('Curve25519 keys are not suitable for ' + + 'signing or verification')); + + var v, nm, err; + try { + nm = hashAlgo.toUpperCase(); + v = crypto.createSign(nm); + } catch (e) { + err = e; + } + if (v === undefined || (err instanceof Error && + err.message.match(/Unknown message digest/))) { + nm = 'RSA-'; + nm += hashAlgo.toUpperCase(); + v = crypto.createSign(nm); + } + assert.ok(v, 'failed to create verifier'); + var oldSign = v.sign.bind(v); + var key = this.toBuffer('pkcs1'); + var type = this.type; + var curve = this.curve; + v.sign = function () { + var sig = oldSign(key); + if (typeof (sig) === 'string') + sig = Buffer.from(sig, 'binary'); + sig = Signature.parse(sig, type, 'asn1'); + sig.hashAlgorithm = hashAlgo; + sig.curve = curve; + return (sig); + }; + return (v); +}; + +PrivateKey.parse = function (data, format, options) { + if (typeof (data) !== 'string') + assert.buffer(data, 'data'); + if (format === undefined) + format = 'auto'; + assert.string(format, 'format'); + if (typeof (options) === 'string') + options = { filename: options }; + assert.optionalObject(options, 'options'); + if (options === undefined) + options = {}; + assert.optionalString(options.filename, 'options.filename'); + if (options.filename === undefined) + options.filename = '(unnamed)'; + + assert.object(formats[format], 'formats[format]'); + + try { + var k = formats[format].read(data, options); + assert.ok(k instanceof PrivateKey, 'key is not a private key'); + if (!k.comment) + k.comment = options.filename; + return (k); + } catch (e) { + if (e.name === 'KeyEncryptedError') + throw (e); + throw (new KeyParseError(options.filename, format, e)); + } +}; + +PrivateKey.isPrivateKey = function (obj, ver) { + return (utils.isCompatible(obj, PrivateKey, ver)); +}; + +PrivateKey.generate = function (type, options) { + if (options === undefined) + options = {}; + assert.object(options, 'options'); + + switch (type) { + case 'ecdsa': + if (options.curve === undefined) + options.curve = 'nistp256'; + assert.string(options.curve, 'options.curve'); + return (generateECDSA(options.curve)); + case 'ed25519': + return (generateED25519()); + default: + throw (new Error('Key generation not supported with key ' + + 'type "' + type + '"')); + } +}; + +/* + * API versions for PrivateKey: + * [1,0] -- initial ver + * [1,1] -- added auto, pkcs[18], openssh/ssh-private formats + * [1,2] -- added defaultHashAlgorithm + * [1,3] -- added derive, ed, createDH + * [1,4] -- first tagged version + * [1,5] -- changed ed25519 part names and format + * [1,6] -- type arguments for hash() and fingerprint() + */ +PrivateKey.prototype._sshpkApiVersion = [1, 6]; + +PrivateKey._oldVersionDetect = function (obj) { + assert.func(obj.toPublic); + assert.func(obj.createSign); + if (obj.derive) + return ([1, 3]); + if (obj.defaultHashAlgorithm) + return ([1, 2]); + if (obj.formats['auto']) + return ([1, 1]); + return ([1, 0]); +}; + + +/***/ }), + +/***/ 512: +/***/ (function(module) { + +module.exports = {"application/1d-interleaved-parityfec":{"source":"iana"},"application/3gpdash-qoe-report+xml":{"source":"iana","compressible":true},"application/3gpp-ims+xml":{"source":"iana","compressible":true},"application/a2l":{"source":"iana"},"application/activemessage":{"source":"iana"},"application/activity+json":{"source":"iana","compressible":true},"application/alto-costmap+json":{"source":"iana","compressible":true},"application/alto-costmapfilter+json":{"source":"iana","compressible":true},"application/alto-directory+json":{"source":"iana","compressible":true},"application/alto-endpointcost+json":{"source":"iana","compressible":true},"application/alto-endpointcostparams+json":{"source":"iana","compressible":true},"application/alto-endpointprop+json":{"source":"iana","compressible":true},"application/alto-endpointpropparams+json":{"source":"iana","compressible":true},"application/alto-error+json":{"source":"iana","compressible":true},"application/alto-networkmap+json":{"source":"iana","compressible":true},"application/alto-networkmapfilter+json":{"source":"iana","compressible":true},"application/aml":{"source":"iana"},"application/andrew-inset":{"source":"iana","extensions":["ez"]},"application/applefile":{"source":"iana"},"application/applixware":{"source":"apache","extensions":["aw"]},"application/atf":{"source":"iana"},"application/atfx":{"source":"iana"},"application/atom+xml":{"source":"iana","compressible":true,"extensions":["atom"]},"application/atomcat+xml":{"source":"iana","compressible":true,"extensions":["atomcat"]},"application/atomdeleted+xml":{"source":"iana","compressible":true,"extensions":["atomdeleted"]},"application/atomicmail":{"source":"iana"},"application/atomsvc+xml":{"source":"iana","compressible":true,"extensions":["atomsvc"]},"application/atsc-dwd+xml":{"source":"iana","compressible":true,"extensions":["dwd"]},"application/atsc-held+xml":{"source":"iana","compressible":true,"extensions":["held"]},"application/atsc-rdt+json":{"source":"iana","compressible":true},"application/atsc-rsat+xml":{"source":"iana","compressible":true,"extensions":["rsat"]},"application/atxml":{"source":"iana"},"application/auth-policy+xml":{"source":"iana","compressible":true},"application/bacnet-xdd+zip":{"source":"iana","compressible":false},"application/batch-smtp":{"source":"iana"},"application/bdoc":{"compressible":false,"extensions":["bdoc"]},"application/beep+xml":{"source":"iana","compressible":true},"application/calendar+json":{"source":"iana","compressible":true},"application/calendar+xml":{"source":"iana","compressible":true,"extensions":["xcs"]},"application/call-completion":{"source":"iana"},"application/cals-1840":{"source":"iana"},"application/cbor":{"source":"iana"},"application/cbor-seq":{"source":"iana"},"application/cccex":{"source":"iana"},"application/ccmp+xml":{"source":"iana","compressible":true},"application/ccxml+xml":{"source":"iana","compressible":true,"extensions":["ccxml"]},"application/cdfx+xml":{"source":"iana","compressible":true,"extensions":["cdfx"]},"application/cdmi-capability":{"source":"iana","extensions":["cdmia"]},"application/cdmi-container":{"source":"iana","extensions":["cdmic"]},"application/cdmi-domain":{"source":"iana","extensions":["cdmid"]},"application/cdmi-object":{"source":"iana","extensions":["cdmio"]},"application/cdmi-queue":{"source":"iana","extensions":["cdmiq"]},"application/cdni":{"source":"iana"},"application/cea":{"source":"iana"},"application/cea-2018+xml":{"source":"iana","compressible":true},"application/cellml+xml":{"source":"iana","compressible":true},"application/cfw":{"source":"iana"},"application/clue+xml":{"source":"iana","compressible":true},"application/clue_info+xml":{"source":"iana","compressible":true},"application/cms":{"source":"iana"},"application/cnrp+xml":{"source":"iana","compressible":true},"application/coap-group+json":{"source":"iana","compressible":true},"application/coap-payload":{"source":"iana"},"application/commonground":{"source":"iana"},"application/conference-info+xml":{"source":"iana","compressible":true},"application/cose":{"source":"iana"},"application/cose-key":{"source":"iana"},"application/cose-key-set":{"source":"iana"},"application/cpl+xml":{"source":"iana","compressible":true},"application/csrattrs":{"source":"iana"},"application/csta+xml":{"source":"iana","compressible":true},"application/cstadata+xml":{"source":"iana","compressible":true},"application/csvm+json":{"source":"iana","compressible":true},"application/cu-seeme":{"source":"apache","extensions":["cu"]},"application/cwt":{"source":"iana"},"application/cybercash":{"source":"iana"},"application/dart":{"compressible":true},"application/dash+xml":{"source":"iana","compressible":true,"extensions":["mpd"]},"application/dashdelta":{"source":"iana"},"application/davmount+xml":{"source":"iana","compressible":true,"extensions":["davmount"]},"application/dca-rft":{"source":"iana"},"application/dcd":{"source":"iana"},"application/dec-dx":{"source":"iana"},"application/dialog-info+xml":{"source":"iana","compressible":true},"application/dicom":{"source":"iana"},"application/dicom+json":{"source":"iana","compressible":true},"application/dicom+xml":{"source":"iana","compressible":true},"application/dii":{"source":"iana"},"application/dit":{"source":"iana"},"application/dns":{"source":"iana"},"application/dns+json":{"source":"iana","compressible":true},"application/dns-message":{"source":"iana"},"application/docbook+xml":{"source":"apache","compressible":true,"extensions":["dbk"]},"application/dskpp+xml":{"source":"iana","compressible":true},"application/dssc+der":{"source":"iana","extensions":["dssc"]},"application/dssc+xml":{"source":"iana","compressible":true,"extensions":["xdssc"]},"application/dvcs":{"source":"iana"},"application/ecmascript":{"source":"iana","compressible":true,"extensions":["ecma","es"]},"application/edi-consent":{"source":"iana"},"application/edi-x12":{"source":"iana","compressible":false},"application/edifact":{"source":"iana","compressible":false},"application/efi":{"source":"iana"},"application/emergencycalldata.comment+xml":{"source":"iana","compressible":true},"application/emergencycalldata.control+xml":{"source":"iana","compressible":true},"application/emergencycalldata.deviceinfo+xml":{"source":"iana","compressible":true},"application/emergencycalldata.ecall.msd":{"source":"iana"},"application/emergencycalldata.providerinfo+xml":{"source":"iana","compressible":true},"application/emergencycalldata.serviceinfo+xml":{"source":"iana","compressible":true},"application/emergencycalldata.subscriberinfo+xml":{"source":"iana","compressible":true},"application/emergencycalldata.veds+xml":{"source":"iana","compressible":true},"application/emma+xml":{"source":"iana","compressible":true,"extensions":["emma"]},"application/emotionml+xml":{"source":"iana","compressible":true,"extensions":["emotionml"]},"application/encaprtp":{"source":"iana"},"application/epp+xml":{"source":"iana","compressible":true},"application/epub+zip":{"source":"iana","compressible":false,"extensions":["epub"]},"application/eshop":{"source":"iana"},"application/exi":{"source":"iana","extensions":["exi"]},"application/expect-ct-report+json":{"source":"iana","compressible":true},"application/fastinfoset":{"source":"iana"},"application/fastsoap":{"source":"iana"},"application/fdt+xml":{"source":"iana","compressible":true,"extensions":["fdt"]},"application/fhir+json":{"source":"iana","compressible":true},"application/fhir+xml":{"source":"iana","compressible":true},"application/fido.trusted-apps+json":{"compressible":true},"application/fits":{"source":"iana"},"application/flexfec":{"source":"iana"},"application/font-sfnt":{"source":"iana"},"application/font-tdpfr":{"source":"iana","extensions":["pfr"]},"application/font-woff":{"source":"iana","compressible":false},"application/framework-attributes+xml":{"source":"iana","compressible":true},"application/geo+json":{"source":"iana","compressible":true,"extensions":["geojson"]},"application/geo+json-seq":{"source":"iana"},"application/geopackage+sqlite3":{"source":"iana"},"application/geoxacml+xml":{"source":"iana","compressible":true},"application/gltf-buffer":{"source":"iana"},"application/gml+xml":{"source":"iana","compressible":true,"extensions":["gml"]},"application/gpx+xml":{"source":"apache","compressible":true,"extensions":["gpx"]},"application/gxf":{"source":"apache","extensions":["gxf"]},"application/gzip":{"source":"iana","compressible":false,"extensions":["gz"]},"application/h224":{"source":"iana"},"application/held+xml":{"source":"iana","compressible":true},"application/hjson":{"extensions":["hjson"]},"application/http":{"source":"iana"},"application/hyperstudio":{"source":"iana","extensions":["stk"]},"application/ibe-key-request+xml":{"source":"iana","compressible":true},"application/ibe-pkg-reply+xml":{"source":"iana","compressible":true},"application/ibe-pp-data":{"source":"iana"},"application/iges":{"source":"iana"},"application/im-iscomposing+xml":{"source":"iana","compressible":true},"application/index":{"source":"iana"},"application/index.cmd":{"source":"iana"},"application/index.obj":{"source":"iana"},"application/index.response":{"source":"iana"},"application/index.vnd":{"source":"iana"},"application/inkml+xml":{"source":"iana","compressible":true,"extensions":["ink","inkml"]},"application/iotp":{"source":"iana"},"application/ipfix":{"source":"iana","extensions":["ipfix"]},"application/ipp":{"source":"iana"},"application/isup":{"source":"iana"},"application/its+xml":{"source":"iana","compressible":true,"extensions":["its"]},"application/java-archive":{"source":"apache","compressible":false,"extensions":["jar","war","ear"]},"application/java-serialized-object":{"source":"apache","compressible":false,"extensions":["ser"]},"application/java-vm":{"source":"apache","compressible":false,"extensions":["class"]},"application/javascript":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["js","mjs"]},"application/jf2feed+json":{"source":"iana","compressible":true},"application/jose":{"source":"iana"},"application/jose+json":{"source":"iana","compressible":true},"application/jrd+json":{"source":"iana","compressible":true},"application/json":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["json","map"]},"application/json-patch+json":{"source":"iana","compressible":true},"application/json-seq":{"source":"iana"},"application/json5":{"extensions":["json5"]},"application/jsonml+json":{"source":"apache","compressible":true,"extensions":["jsonml"]},"application/jwk+json":{"source":"iana","compressible":true},"application/jwk-set+json":{"source":"iana","compressible":true},"application/jwt":{"source":"iana"},"application/kpml-request+xml":{"source":"iana","compressible":true},"application/kpml-response+xml":{"source":"iana","compressible":true},"application/ld+json":{"source":"iana","compressible":true,"extensions":["jsonld"]},"application/lgr+xml":{"source":"iana","compressible":true,"extensions":["lgr"]},"application/link-format":{"source":"iana"},"application/load-control+xml":{"source":"iana","compressible":true},"application/lost+xml":{"source":"iana","compressible":true,"extensions":["lostxml"]},"application/lostsync+xml":{"source":"iana","compressible":true},"application/lxf":{"source":"iana"},"application/mac-binhex40":{"source":"iana","extensions":["hqx"]},"application/mac-compactpro":{"source":"apache","extensions":["cpt"]},"application/macwriteii":{"source":"iana"},"application/mads+xml":{"source":"iana","compressible":true,"extensions":["mads"]},"application/manifest+json":{"charset":"UTF-8","compressible":true,"extensions":["webmanifest"]},"application/marc":{"source":"iana","extensions":["mrc"]},"application/marcxml+xml":{"source":"iana","compressible":true,"extensions":["mrcx"]},"application/mathematica":{"source":"iana","extensions":["ma","nb","mb"]},"application/mathml+xml":{"source":"iana","compressible":true,"extensions":["mathml"]},"application/mathml-content+xml":{"source":"iana","compressible":true},"application/mathml-presentation+xml":{"source":"iana","compressible":true},"application/mbms-associated-procedure-description+xml":{"source":"iana","compressible":true},"application/mbms-deregister+xml":{"source":"iana","compressible":true},"application/mbms-envelope+xml":{"source":"iana","compressible":true},"application/mbms-msk+xml":{"source":"iana","compressible":true},"application/mbms-msk-response+xml":{"source":"iana","compressible":true},"application/mbms-protection-description+xml":{"source":"iana","compressible":true},"application/mbms-reception-report+xml":{"source":"iana","compressible":true},"application/mbms-register+xml":{"source":"iana","compressible":true},"application/mbms-register-response+xml":{"source":"iana","compressible":true},"application/mbms-schedule+xml":{"source":"iana","compressible":true},"application/mbms-user-service-description+xml":{"source":"iana","compressible":true},"application/mbox":{"source":"iana","extensions":["mbox"]},"application/media-policy-dataset+xml":{"source":"iana","compressible":true},"application/media_control+xml":{"source":"iana","compressible":true},"application/mediaservercontrol+xml":{"source":"iana","compressible":true,"extensions":["mscml"]},"application/merge-patch+json":{"source":"iana","compressible":true},"application/metalink+xml":{"source":"apache","compressible":true,"extensions":["metalink"]},"application/metalink4+xml":{"source":"iana","compressible":true,"extensions":["meta4"]},"application/mets+xml":{"source":"iana","compressible":true,"extensions":["mets"]},"application/mf4":{"source":"iana"},"application/mikey":{"source":"iana"},"application/mipc":{"source":"iana"},"application/mmt-aei+xml":{"source":"iana","compressible":true,"extensions":["maei"]},"application/mmt-usd+xml":{"source":"iana","compressible":true,"extensions":["musd"]},"application/mods+xml":{"source":"iana","compressible":true,"extensions":["mods"]},"application/moss-keys":{"source":"iana"},"application/moss-signature":{"source":"iana"},"application/mosskey-data":{"source":"iana"},"application/mosskey-request":{"source":"iana"},"application/mp21":{"source":"iana","extensions":["m21","mp21"]},"application/mp4":{"source":"iana","extensions":["mp4s","m4p"]},"application/mpeg4-generic":{"source":"iana"},"application/mpeg4-iod":{"source":"iana"},"application/mpeg4-iod-xmt":{"source":"iana"},"application/mrb-consumer+xml":{"source":"iana","compressible":true,"extensions":["xdf"]},"application/mrb-publish+xml":{"source":"iana","compressible":true,"extensions":["xdf"]},"application/msc-ivr+xml":{"source":"iana","compressible":true},"application/msc-mixer+xml":{"source":"iana","compressible":true},"application/msword":{"source":"iana","compressible":false,"extensions":["doc","dot"]},"application/mud+json":{"source":"iana","compressible":true},"application/multipart-core":{"source":"iana"},"application/mxf":{"source":"iana","extensions":["mxf"]},"application/n-quads":{"source":"iana","extensions":["nq"]},"application/n-triples":{"source":"iana","extensions":["nt"]},"application/nasdata":{"source":"iana"},"application/news-checkgroups":{"source":"iana"},"application/news-groupinfo":{"source":"iana"},"application/news-transmission":{"source":"iana"},"application/nlsml+xml":{"source":"iana","compressible":true},"application/node":{"source":"iana"},"application/nss":{"source":"iana"},"application/ocsp-request":{"source":"iana"},"application/ocsp-response":{"source":"iana"},"application/octet-stream":{"source":"iana","compressible":false,"extensions":["bin","dms","lrf","mar","so","dist","distz","pkg","bpk","dump","elc","deploy","exe","dll","deb","dmg","iso","img","msi","msp","msm","buffer"]},"application/oda":{"source":"iana","extensions":["oda"]},"application/odm+xml":{"source":"iana","compressible":true},"application/odx":{"source":"iana"},"application/oebps-package+xml":{"source":"iana","compressible":true,"extensions":["opf"]},"application/ogg":{"source":"iana","compressible":false,"extensions":["ogx"]},"application/omdoc+xml":{"source":"apache","compressible":true,"extensions":["omdoc"]},"application/onenote":{"source":"apache","extensions":["onetoc","onetoc2","onetmp","onepkg"]},"application/oscore":{"source":"iana"},"application/oxps":{"source":"iana","extensions":["oxps"]},"application/p2p-overlay+xml":{"source":"iana","compressible":true,"extensions":["relo"]},"application/parityfec":{"source":"iana"},"application/passport":{"source":"iana"},"application/patch-ops-error+xml":{"source":"iana","compressible":true,"extensions":["xer"]},"application/pdf":{"source":"iana","compressible":false,"extensions":["pdf"]},"application/pdx":{"source":"iana"},"application/pem-certificate-chain":{"source":"iana"},"application/pgp-encrypted":{"source":"iana","compressible":false,"extensions":["pgp"]},"application/pgp-keys":{"source":"iana"},"application/pgp-signature":{"source":"iana","extensions":["asc","sig"]},"application/pics-rules":{"source":"apache","extensions":["prf"]},"application/pidf+xml":{"source":"iana","compressible":true},"application/pidf-diff+xml":{"source":"iana","compressible":true},"application/pkcs10":{"source":"iana","extensions":["p10"]},"application/pkcs12":{"source":"iana"},"application/pkcs7-mime":{"source":"iana","extensions":["p7m","p7c"]},"application/pkcs7-signature":{"source":"iana","extensions":["p7s"]},"application/pkcs8":{"source":"iana","extensions":["p8"]},"application/pkcs8-encrypted":{"source":"iana"},"application/pkix-attr-cert":{"source":"iana","extensions":["ac"]},"application/pkix-cert":{"source":"iana","extensions":["cer"]},"application/pkix-crl":{"source":"iana","extensions":["crl"]},"application/pkix-pkipath":{"source":"iana","extensions":["pkipath"]},"application/pkixcmp":{"source":"iana","extensions":["pki"]},"application/pls+xml":{"source":"iana","compressible":true,"extensions":["pls"]},"application/poc-settings+xml":{"source":"iana","compressible":true},"application/postscript":{"source":"iana","compressible":true,"extensions":["ai","eps","ps"]},"application/ppsp-tracker+json":{"source":"iana","compressible":true},"application/problem+json":{"source":"iana","compressible":true},"application/problem+xml":{"source":"iana","compressible":true},"application/provenance+xml":{"source":"iana","compressible":true,"extensions":["provx"]},"application/prs.alvestrand.titrax-sheet":{"source":"iana"},"application/prs.cww":{"source":"iana","extensions":["cww"]},"application/prs.hpub+zip":{"source":"iana","compressible":false},"application/prs.nprend":{"source":"iana"},"application/prs.plucker":{"source":"iana"},"application/prs.rdf-xml-crypt":{"source":"iana"},"application/prs.xsf+xml":{"source":"iana","compressible":true},"application/pskc+xml":{"source":"iana","compressible":true,"extensions":["pskcxml"]},"application/qsig":{"source":"iana"},"application/raml+yaml":{"compressible":true,"extensions":["raml"]},"application/raptorfec":{"source":"iana"},"application/rdap+json":{"source":"iana","compressible":true},"application/rdf+xml":{"source":"iana","compressible":true,"extensions":["rdf","owl"]},"application/reginfo+xml":{"source":"iana","compressible":true,"extensions":["rif"]},"application/relax-ng-compact-syntax":{"source":"iana","extensions":["rnc"]},"application/remote-printing":{"source":"iana"},"application/reputon+json":{"source":"iana","compressible":true},"application/resource-lists+xml":{"source":"iana","compressible":true,"extensions":["rl"]},"application/resource-lists-diff+xml":{"source":"iana","compressible":true,"extensions":["rld"]},"application/rfc+xml":{"source":"iana","compressible":true},"application/riscos":{"source":"iana"},"application/rlmi+xml":{"source":"iana","compressible":true},"application/rls-services+xml":{"source":"iana","compressible":true,"extensions":["rs"]},"application/route-apd+xml":{"source":"iana","compressible":true,"extensions":["rapd"]},"application/route-s-tsid+xml":{"source":"iana","compressible":true,"extensions":["sls"]},"application/route-usd+xml":{"source":"iana","compressible":true,"extensions":["rusd"]},"application/rpki-ghostbusters":{"source":"iana","extensions":["gbr"]},"application/rpki-manifest":{"source":"iana","extensions":["mft"]},"application/rpki-publication":{"source":"iana"},"application/rpki-roa":{"source":"iana","extensions":["roa"]},"application/rpki-updown":{"source":"iana"},"application/rsd+xml":{"source":"apache","compressible":true,"extensions":["rsd"]},"application/rss+xml":{"source":"apache","compressible":true,"extensions":["rss"]},"application/rtf":{"source":"iana","compressible":true,"extensions":["rtf"]},"application/rtploopback":{"source":"iana"},"application/rtx":{"source":"iana"},"application/samlassertion+xml":{"source":"iana","compressible":true},"application/samlmetadata+xml":{"source":"iana","compressible":true},"application/sbml+xml":{"source":"iana","compressible":true,"extensions":["sbml"]},"application/scaip+xml":{"source":"iana","compressible":true},"application/scim+json":{"source":"iana","compressible":true},"application/scvp-cv-request":{"source":"iana","extensions":["scq"]},"application/scvp-cv-response":{"source":"iana","extensions":["scs"]},"application/scvp-vp-request":{"source":"iana","extensions":["spq"]},"application/scvp-vp-response":{"source":"iana","extensions":["spp"]},"application/sdp":{"source":"iana","extensions":["sdp"]},"application/secevent+jwt":{"source":"iana"},"application/senml+cbor":{"source":"iana"},"application/senml+json":{"source":"iana","compressible":true},"application/senml+xml":{"source":"iana","compressible":true,"extensions":["senmlx"]},"application/senml-exi":{"source":"iana"},"application/sensml+cbor":{"source":"iana"},"application/sensml+json":{"source":"iana","compressible":true},"application/sensml+xml":{"source":"iana","compressible":true,"extensions":["sensmlx"]},"application/sensml-exi":{"source":"iana"},"application/sep+xml":{"source":"iana","compressible":true},"application/sep-exi":{"source":"iana"},"application/session-info":{"source":"iana"},"application/set-payment":{"source":"iana"},"application/set-payment-initiation":{"source":"iana","extensions":["setpay"]},"application/set-registration":{"source":"iana"},"application/set-registration-initiation":{"source":"iana","extensions":["setreg"]},"application/sgml":{"source":"iana"},"application/sgml-open-catalog":{"source":"iana"},"application/shf+xml":{"source":"iana","compressible":true,"extensions":["shf"]},"application/sieve":{"source":"iana","extensions":["siv","sieve"]},"application/simple-filter+xml":{"source":"iana","compressible":true},"application/simple-message-summary":{"source":"iana"},"application/simplesymbolcontainer":{"source":"iana"},"application/sipc":{"source":"iana"},"application/slate":{"source":"iana"},"application/smil":{"source":"iana"},"application/smil+xml":{"source":"iana","compressible":true,"extensions":["smi","smil"]},"application/smpte336m":{"source":"iana"},"application/soap+fastinfoset":{"source":"iana"},"application/soap+xml":{"source":"iana","compressible":true},"application/sparql-query":{"source":"iana","extensions":["rq"]},"application/sparql-results+xml":{"source":"iana","compressible":true,"extensions":["srx"]},"application/spirits-event+xml":{"source":"iana","compressible":true},"application/sql":{"source":"iana"},"application/srgs":{"source":"iana","extensions":["gram"]},"application/srgs+xml":{"source":"iana","compressible":true,"extensions":["grxml"]},"application/sru+xml":{"source":"iana","compressible":true,"extensions":["sru"]},"application/ssdl+xml":{"source":"apache","compressible":true,"extensions":["ssdl"]},"application/ssml+xml":{"source":"iana","compressible":true,"extensions":["ssml"]},"application/stix+json":{"source":"iana","compressible":true},"application/swid+xml":{"source":"iana","compressible":true,"extensions":["swidtag"]},"application/tamp-apex-update":{"source":"iana"},"application/tamp-apex-update-confirm":{"source":"iana"},"application/tamp-community-update":{"source":"iana"},"application/tamp-community-update-confirm":{"source":"iana"},"application/tamp-error":{"source":"iana"},"application/tamp-sequence-adjust":{"source":"iana"},"application/tamp-sequence-adjust-confirm":{"source":"iana"},"application/tamp-status-query":{"source":"iana"},"application/tamp-status-response":{"source":"iana"},"application/tamp-update":{"source":"iana"},"application/tamp-update-confirm":{"source":"iana"},"application/tar":{"compressible":true},"application/taxii+json":{"source":"iana","compressible":true},"application/tei+xml":{"source":"iana","compressible":true,"extensions":["tei","teicorpus"]},"application/tetra_isi":{"source":"iana"},"application/thraud+xml":{"source":"iana","compressible":true,"extensions":["tfi"]},"application/timestamp-query":{"source":"iana"},"application/timestamp-reply":{"source":"iana"},"application/timestamped-data":{"source":"iana","extensions":["tsd"]},"application/tlsrpt+gzip":{"source":"iana"},"application/tlsrpt+json":{"source":"iana","compressible":true},"application/tnauthlist":{"source":"iana"},"application/toml":{"compressible":true,"extensions":["toml"]},"application/trickle-ice-sdpfrag":{"source":"iana"},"application/trig":{"source":"iana"},"application/ttml+xml":{"source":"iana","compressible":true,"extensions":["ttml"]},"application/tve-trigger":{"source":"iana"},"application/tzif":{"source":"iana"},"application/tzif-leap":{"source":"iana"},"application/ulpfec":{"source":"iana"},"application/urc-grpsheet+xml":{"source":"iana","compressible":true},"application/urc-ressheet+xml":{"source":"iana","compressible":true,"extensions":["rsheet"]},"application/urc-targetdesc+xml":{"source":"iana","compressible":true},"application/urc-uisocketdesc+xml":{"source":"iana","compressible":true},"application/vcard+json":{"source":"iana","compressible":true},"application/vcard+xml":{"source":"iana","compressible":true},"application/vemmi":{"source":"iana"},"application/vividence.scriptfile":{"source":"apache"},"application/vnd.1000minds.decision-model+xml":{"source":"iana","compressible":true,"extensions":["1km"]},"application/vnd.3gpp-prose+xml":{"source":"iana","compressible":true},"application/vnd.3gpp-prose-pc3ch+xml":{"source":"iana","compressible":true},"application/vnd.3gpp-v2x-local-service-information":{"source":"iana"},"application/vnd.3gpp.access-transfer-events+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.bsf+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.gmop+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mc-signalling-ear":{"source":"iana"},"application/vnd.3gpp.mcdata-affiliation-command+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcdata-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcdata-payload":{"source":"iana"},"application/vnd.3gpp.mcdata-service-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcdata-signalling":{"source":"iana"},"application/vnd.3gpp.mcdata-ue-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcdata-user-profile+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-affiliation-command+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-floor-request+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-location-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-mbms-usage-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-service-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-signed+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-ue-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-ue-init-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-user-profile+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-affiliation-command+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-affiliation-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-location-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-mbms-usage-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-service-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-transmission-request+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-ue-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-user-profile+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mid-call+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.pic-bw-large":{"source":"iana","extensions":["plb"]},"application/vnd.3gpp.pic-bw-small":{"source":"iana","extensions":["psb"]},"application/vnd.3gpp.pic-bw-var":{"source":"iana","extensions":["pvb"]},"application/vnd.3gpp.sms":{"source":"iana"},"application/vnd.3gpp.sms+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.srvcc-ext+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.srvcc-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.state-and-event-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.ussd+xml":{"source":"iana","compressible":true},"application/vnd.3gpp2.bcmcsinfo+xml":{"source":"iana","compressible":true},"application/vnd.3gpp2.sms":{"source":"iana"},"application/vnd.3gpp2.tcap":{"source":"iana","extensions":["tcap"]},"application/vnd.3lightssoftware.imagescal":{"source":"iana"},"application/vnd.3m.post-it-notes":{"source":"iana","extensions":["pwn"]},"application/vnd.accpac.simply.aso":{"source":"iana","extensions":["aso"]},"application/vnd.accpac.simply.imp":{"source":"iana","extensions":["imp"]},"application/vnd.acucobol":{"source":"iana","extensions":["acu"]},"application/vnd.acucorp":{"source":"iana","extensions":["atc","acutc"]},"application/vnd.adobe.air-application-installer-package+zip":{"source":"apache","compressible":false,"extensions":["air"]},"application/vnd.adobe.flash.movie":{"source":"iana"},"application/vnd.adobe.formscentral.fcdt":{"source":"iana","extensions":["fcdt"]},"application/vnd.adobe.fxp":{"source":"iana","extensions":["fxp","fxpl"]},"application/vnd.adobe.partial-upload":{"source":"iana"},"application/vnd.adobe.xdp+xml":{"source":"iana","compressible":true,"extensions":["xdp"]},"application/vnd.adobe.xfdf":{"source":"iana","extensions":["xfdf"]},"application/vnd.aether.imp":{"source":"iana"},"application/vnd.afpc.afplinedata":{"source":"iana"},"application/vnd.afpc.afplinedata-pagedef":{"source":"iana"},"application/vnd.afpc.foca-charset":{"source":"iana"},"application/vnd.afpc.foca-codedfont":{"source":"iana"},"application/vnd.afpc.foca-codepage":{"source":"iana"},"application/vnd.afpc.modca":{"source":"iana"},"application/vnd.afpc.modca-formdef":{"source":"iana"},"application/vnd.afpc.modca-mediummap":{"source":"iana"},"application/vnd.afpc.modca-objectcontainer":{"source":"iana"},"application/vnd.afpc.modca-overlay":{"source":"iana"},"application/vnd.afpc.modca-pagesegment":{"source":"iana"},"application/vnd.ah-barcode":{"source":"iana"},"application/vnd.ahead.space":{"source":"iana","extensions":["ahead"]},"application/vnd.airzip.filesecure.azf":{"source":"iana","extensions":["azf"]},"application/vnd.airzip.filesecure.azs":{"source":"iana","extensions":["azs"]},"application/vnd.amadeus+json":{"source":"iana","compressible":true},"application/vnd.amazon.ebook":{"source":"apache","extensions":["azw"]},"application/vnd.amazon.mobi8-ebook":{"source":"iana"},"application/vnd.americandynamics.acc":{"source":"iana","extensions":["acc"]},"application/vnd.amiga.ami":{"source":"iana","extensions":["ami"]},"application/vnd.amundsen.maze+xml":{"source":"iana","compressible":true},"application/vnd.android.ota":{"source":"iana"},"application/vnd.android.package-archive":{"source":"apache","compressible":false,"extensions":["apk"]},"application/vnd.anki":{"source":"iana"},"application/vnd.anser-web-certificate-issue-initiation":{"source":"iana","extensions":["cii"]},"application/vnd.anser-web-funds-transfer-initiation":{"source":"apache","extensions":["fti"]},"application/vnd.antix.game-component":{"source":"iana","extensions":["atx"]},"application/vnd.apache.thrift.binary":{"source":"iana"},"application/vnd.apache.thrift.compact":{"source":"iana"},"application/vnd.apache.thrift.json":{"source":"iana"},"application/vnd.api+json":{"source":"iana","compressible":true},"application/vnd.aplextor.warrp+json":{"source":"iana","compressible":true},"application/vnd.apothekende.reservation+json":{"source":"iana","compressible":true},"application/vnd.apple.installer+xml":{"source":"iana","compressible":true,"extensions":["mpkg"]},"application/vnd.apple.keynote":{"source":"iana","extensions":["keynote"]},"application/vnd.apple.mpegurl":{"source":"iana","extensions":["m3u8"]},"application/vnd.apple.numbers":{"source":"iana","extensions":["numbers"]},"application/vnd.apple.pages":{"source":"iana","extensions":["pages"]},"application/vnd.apple.pkpass":{"compressible":false,"extensions":["pkpass"]},"application/vnd.arastra.swi":{"source":"iana"},"application/vnd.aristanetworks.swi":{"source":"iana","extensions":["swi"]},"application/vnd.artisan+json":{"source":"iana","compressible":true},"application/vnd.artsquare":{"source":"iana"},"application/vnd.astraea-software.iota":{"source":"iana","extensions":["iota"]},"application/vnd.audiograph":{"source":"iana","extensions":["aep"]},"application/vnd.autopackage":{"source":"iana"},"application/vnd.avalon+json":{"source":"iana","compressible":true},"application/vnd.avistar+xml":{"source":"iana","compressible":true},"application/vnd.balsamiq.bmml+xml":{"source":"iana","compressible":true,"extensions":["bmml"]},"application/vnd.balsamiq.bmpr":{"source":"iana"},"application/vnd.banana-accounting":{"source":"iana"},"application/vnd.bbf.usp.error":{"source":"iana"},"application/vnd.bbf.usp.msg":{"source":"iana"},"application/vnd.bbf.usp.msg+json":{"source":"iana","compressible":true},"application/vnd.bekitzur-stech+json":{"source":"iana","compressible":true},"application/vnd.bint.med-content":{"source":"iana"},"application/vnd.biopax.rdf+xml":{"source":"iana","compressible":true},"application/vnd.blink-idb-value-wrapper":{"source":"iana"},"application/vnd.blueice.multipass":{"source":"iana","extensions":["mpm"]},"application/vnd.bluetooth.ep.oob":{"source":"iana"},"application/vnd.bluetooth.le.oob":{"source":"iana"},"application/vnd.bmi":{"source":"iana","extensions":["bmi"]},"application/vnd.bpf":{"source":"iana"},"application/vnd.bpf3":{"source":"iana"},"application/vnd.businessobjects":{"source":"iana","extensions":["rep"]},"application/vnd.byu.uapi+json":{"source":"iana","compressible":true},"application/vnd.cab-jscript":{"source":"iana"},"application/vnd.canon-cpdl":{"source":"iana"},"application/vnd.canon-lips":{"source":"iana"},"application/vnd.capasystems-pg+json":{"source":"iana","compressible":true},"application/vnd.cendio.thinlinc.clientconf":{"source":"iana"},"application/vnd.century-systems.tcp_stream":{"source":"iana"},"application/vnd.chemdraw+xml":{"source":"iana","compressible":true,"extensions":["cdxml"]},"application/vnd.chess-pgn":{"source":"iana"},"application/vnd.chipnuts.karaoke-mmd":{"source":"iana","extensions":["mmd"]},"application/vnd.ciedi":{"source":"iana"},"application/vnd.cinderella":{"source":"iana","extensions":["cdy"]},"application/vnd.cirpack.isdn-ext":{"source":"iana"},"application/vnd.citationstyles.style+xml":{"source":"iana","compressible":true,"extensions":["csl"]},"application/vnd.claymore":{"source":"iana","extensions":["cla"]},"application/vnd.cloanto.rp9":{"source":"iana","extensions":["rp9"]},"application/vnd.clonk.c4group":{"source":"iana","extensions":["c4g","c4d","c4f","c4p","c4u"]},"application/vnd.cluetrust.cartomobile-config":{"source":"iana","extensions":["c11amc"]},"application/vnd.cluetrust.cartomobile-config-pkg":{"source":"iana","extensions":["c11amz"]},"application/vnd.coffeescript":{"source":"iana"},"application/vnd.collabio.xodocuments.document":{"source":"iana"},"application/vnd.collabio.xodocuments.document-template":{"source":"iana"},"application/vnd.collabio.xodocuments.presentation":{"source":"iana"},"application/vnd.collabio.xodocuments.presentation-template":{"source":"iana"},"application/vnd.collabio.xodocuments.spreadsheet":{"source":"iana"},"application/vnd.collabio.xodocuments.spreadsheet-template":{"source":"iana"},"application/vnd.collection+json":{"source":"iana","compressible":true},"application/vnd.collection.doc+json":{"source":"iana","compressible":true},"application/vnd.collection.next+json":{"source":"iana","compressible":true},"application/vnd.comicbook+zip":{"source":"iana","compressible":false},"application/vnd.comicbook-rar":{"source":"iana"},"application/vnd.commerce-battelle":{"source":"iana"},"application/vnd.commonspace":{"source":"iana","extensions":["csp"]},"application/vnd.contact.cmsg":{"source":"iana","extensions":["cdbcmsg"]},"application/vnd.coreos.ignition+json":{"source":"iana","compressible":true},"application/vnd.cosmocaller":{"source":"iana","extensions":["cmc"]},"application/vnd.crick.clicker":{"source":"iana","extensions":["clkx"]},"application/vnd.crick.clicker.keyboard":{"source":"iana","extensions":["clkk"]},"application/vnd.crick.clicker.palette":{"source":"iana","extensions":["clkp"]},"application/vnd.crick.clicker.template":{"source":"iana","extensions":["clkt"]},"application/vnd.crick.clicker.wordbank":{"source":"iana","extensions":["clkw"]},"application/vnd.criticaltools.wbs+xml":{"source":"iana","compressible":true,"extensions":["wbs"]},"application/vnd.cryptii.pipe+json":{"source":"iana","compressible":true},"application/vnd.crypto-shade-file":{"source":"iana"},"application/vnd.ctc-posml":{"source":"iana","extensions":["pml"]},"application/vnd.ctct.ws+xml":{"source":"iana","compressible":true},"application/vnd.cups-pdf":{"source":"iana"},"application/vnd.cups-postscript":{"source":"iana"},"application/vnd.cups-ppd":{"source":"iana","extensions":["ppd"]},"application/vnd.cups-raster":{"source":"iana"},"application/vnd.cups-raw":{"source":"iana"},"application/vnd.curl":{"source":"iana"},"application/vnd.curl.car":{"source":"apache","extensions":["car"]},"application/vnd.curl.pcurl":{"source":"apache","extensions":["pcurl"]},"application/vnd.cyan.dean.root+xml":{"source":"iana","compressible":true},"application/vnd.cybank":{"source":"iana"},"application/vnd.d2l.coursepackage1p0+zip":{"source":"iana","compressible":false},"application/vnd.dart":{"source":"iana","compressible":true,"extensions":["dart"]},"application/vnd.data-vision.rdz":{"source":"iana","extensions":["rdz"]},"application/vnd.datapackage+json":{"source":"iana","compressible":true},"application/vnd.dataresource+json":{"source":"iana","compressible":true},"application/vnd.debian.binary-package":{"source":"iana"},"application/vnd.dece.data":{"source":"iana","extensions":["uvf","uvvf","uvd","uvvd"]},"application/vnd.dece.ttml+xml":{"source":"iana","compressible":true,"extensions":["uvt","uvvt"]},"application/vnd.dece.unspecified":{"source":"iana","extensions":["uvx","uvvx"]},"application/vnd.dece.zip":{"source":"iana","extensions":["uvz","uvvz"]},"application/vnd.denovo.fcselayout-link":{"source":"iana","extensions":["fe_launch"]},"application/vnd.desmume.movie":{"source":"iana"},"application/vnd.dir-bi.plate-dl-nosuffix":{"source":"iana"},"application/vnd.dm.delegation+xml":{"source":"iana","compressible":true},"application/vnd.dna":{"source":"iana","extensions":["dna"]},"application/vnd.document+json":{"source":"iana","compressible":true},"application/vnd.dolby.mlp":{"source":"apache","extensions":["mlp"]},"application/vnd.dolby.mobile.1":{"source":"iana"},"application/vnd.dolby.mobile.2":{"source":"iana"},"application/vnd.doremir.scorecloud-binary-document":{"source":"iana"},"application/vnd.dpgraph":{"source":"iana","extensions":["dpg"]},"application/vnd.dreamfactory":{"source":"iana","extensions":["dfac"]},"application/vnd.drive+json":{"source":"iana","compressible":true},"application/vnd.ds-keypoint":{"source":"apache","extensions":["kpxx"]},"application/vnd.dtg.local":{"source":"iana"},"application/vnd.dtg.local.flash":{"source":"iana"},"application/vnd.dtg.local.html":{"source":"iana"},"application/vnd.dvb.ait":{"source":"iana","extensions":["ait"]},"application/vnd.dvb.dvbj":{"source":"iana"},"application/vnd.dvb.esgcontainer":{"source":"iana"},"application/vnd.dvb.ipdcdftnotifaccess":{"source":"iana"},"application/vnd.dvb.ipdcesgaccess":{"source":"iana"},"application/vnd.dvb.ipdcesgaccess2":{"source":"iana"},"application/vnd.dvb.ipdcesgpdd":{"source":"iana"},"application/vnd.dvb.ipdcroaming":{"source":"iana"},"application/vnd.dvb.iptv.alfec-base":{"source":"iana"},"application/vnd.dvb.iptv.alfec-enhancement":{"source":"iana"},"application/vnd.dvb.notif-aggregate-root+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-container+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-generic+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-ia-msglist+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-ia-registration-request+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-ia-registration-response+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-init+xml":{"source":"iana","compressible":true},"application/vnd.dvb.pfr":{"source":"iana"},"application/vnd.dvb.service":{"source":"iana","extensions":["svc"]},"application/vnd.dxr":{"source":"iana"},"application/vnd.dynageo":{"source":"iana","extensions":["geo"]},"application/vnd.dzr":{"source":"iana"},"application/vnd.easykaraoke.cdgdownload":{"source":"iana"},"application/vnd.ecdis-update":{"source":"iana"},"application/vnd.ecip.rlp":{"source":"iana"},"application/vnd.ecowin.chart":{"source":"iana","extensions":["mag"]},"application/vnd.ecowin.filerequest":{"source":"iana"},"application/vnd.ecowin.fileupdate":{"source":"iana"},"application/vnd.ecowin.series":{"source":"iana"},"application/vnd.ecowin.seriesrequest":{"source":"iana"},"application/vnd.ecowin.seriesupdate":{"source":"iana"},"application/vnd.efi.img":{"source":"iana"},"application/vnd.efi.iso":{"source":"iana"},"application/vnd.emclient.accessrequest+xml":{"source":"iana","compressible":true},"application/vnd.enliven":{"source":"iana","extensions":["nml"]},"application/vnd.enphase.envoy":{"source":"iana"},"application/vnd.eprints.data+xml":{"source":"iana","compressible":true},"application/vnd.epson.esf":{"source":"iana","extensions":["esf"]},"application/vnd.epson.msf":{"source":"iana","extensions":["msf"]},"application/vnd.epson.quickanime":{"source":"iana","extensions":["qam"]},"application/vnd.epson.salt":{"source":"iana","extensions":["slt"]},"application/vnd.epson.ssf":{"source":"iana","extensions":["ssf"]},"application/vnd.ericsson.quickcall":{"source":"iana"},"application/vnd.espass-espass+zip":{"source":"iana","compressible":false},"application/vnd.eszigno3+xml":{"source":"iana","compressible":true,"extensions":["es3","et3"]},"application/vnd.etsi.aoc+xml":{"source":"iana","compressible":true},"application/vnd.etsi.asic-e+zip":{"source":"iana","compressible":false},"application/vnd.etsi.asic-s+zip":{"source":"iana","compressible":false},"application/vnd.etsi.cug+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvcommand+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvdiscovery+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvprofile+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvsad-bc+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvsad-cod+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvsad-npvr+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvservice+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvsync+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvueprofile+xml":{"source":"iana","compressible":true},"application/vnd.etsi.mcid+xml":{"source":"iana","compressible":true},"application/vnd.etsi.mheg5":{"source":"iana"},"application/vnd.etsi.overload-control-policy-dataset+xml":{"source":"iana","compressible":true},"application/vnd.etsi.pstn+xml":{"source":"iana","compressible":true},"application/vnd.etsi.sci+xml":{"source":"iana","compressible":true},"application/vnd.etsi.simservs+xml":{"source":"iana","compressible":true},"application/vnd.etsi.timestamp-token":{"source":"iana"},"application/vnd.etsi.tsl+xml":{"source":"iana","compressible":true},"application/vnd.etsi.tsl.der":{"source":"iana"},"application/vnd.eudora.data":{"source":"iana"},"application/vnd.evolv.ecig.profile":{"source":"iana"},"application/vnd.evolv.ecig.settings":{"source":"iana"},"application/vnd.evolv.ecig.theme":{"source":"iana"},"application/vnd.exstream-empower+zip":{"source":"iana","compressible":false},"application/vnd.exstream-package":{"source":"iana"},"application/vnd.ezpix-album":{"source":"iana","extensions":["ez2"]},"application/vnd.ezpix-package":{"source":"iana","extensions":["ez3"]},"application/vnd.f-secure.mobile":{"source":"iana"},"application/vnd.fastcopy-disk-image":{"source":"iana"},"application/vnd.fdf":{"source":"iana","extensions":["fdf"]},"application/vnd.fdsn.mseed":{"source":"iana","extensions":["mseed"]},"application/vnd.fdsn.seed":{"source":"iana","extensions":["seed","dataless"]},"application/vnd.ffsns":{"source":"iana"},"application/vnd.ficlab.flb+zip":{"source":"iana","compressible":false},"application/vnd.filmit.zfc":{"source":"iana"},"application/vnd.fints":{"source":"iana"},"application/vnd.firemonkeys.cloudcell":{"source":"iana"},"application/vnd.flographit":{"source":"iana","extensions":["gph"]},"application/vnd.fluxtime.clip":{"source":"iana","extensions":["ftc"]},"application/vnd.font-fontforge-sfd":{"source":"iana"},"application/vnd.framemaker":{"source":"iana","extensions":["fm","frame","maker","book"]},"application/vnd.frogans.fnc":{"source":"iana","extensions":["fnc"]},"application/vnd.frogans.ltf":{"source":"iana","extensions":["ltf"]},"application/vnd.fsc.weblaunch":{"source":"iana","extensions":["fsc"]},"application/vnd.fujitsu.oasys":{"source":"iana","extensions":["oas"]},"application/vnd.fujitsu.oasys2":{"source":"iana","extensions":["oa2"]},"application/vnd.fujitsu.oasys3":{"source":"iana","extensions":["oa3"]},"application/vnd.fujitsu.oasysgp":{"source":"iana","extensions":["fg5"]},"application/vnd.fujitsu.oasysprs":{"source":"iana","extensions":["bh2"]},"application/vnd.fujixerox.art-ex":{"source":"iana"},"application/vnd.fujixerox.art4":{"source":"iana"},"application/vnd.fujixerox.ddd":{"source":"iana","extensions":["ddd"]},"application/vnd.fujixerox.docuworks":{"source":"iana","extensions":["xdw"]},"application/vnd.fujixerox.docuworks.binder":{"source":"iana","extensions":["xbd"]},"application/vnd.fujixerox.docuworks.container":{"source":"iana"},"application/vnd.fujixerox.hbpl":{"source":"iana"},"application/vnd.fut-misnet":{"source":"iana"},"application/vnd.futoin+cbor":{"source":"iana"},"application/vnd.futoin+json":{"source":"iana","compressible":true},"application/vnd.fuzzysheet":{"source":"iana","extensions":["fzs"]},"application/vnd.genomatix.tuxedo":{"source":"iana","extensions":["txd"]},"application/vnd.gentics.grd+json":{"source":"iana","compressible":true},"application/vnd.geo+json":{"source":"iana","compressible":true},"application/vnd.geocube+xml":{"source":"iana","compressible":true},"application/vnd.geogebra.file":{"source":"iana","extensions":["ggb"]},"application/vnd.geogebra.tool":{"source":"iana","extensions":["ggt"]},"application/vnd.geometry-explorer":{"source":"iana","extensions":["gex","gre"]},"application/vnd.geonext":{"source":"iana","extensions":["gxt"]},"application/vnd.geoplan":{"source":"iana","extensions":["g2w"]},"application/vnd.geospace":{"source":"iana","extensions":["g3w"]},"application/vnd.gerber":{"source":"iana"},"application/vnd.globalplatform.card-content-mgt":{"source":"iana"},"application/vnd.globalplatform.card-content-mgt-response":{"source":"iana"},"application/vnd.gmx":{"source":"iana","extensions":["gmx"]},"application/vnd.google-apps.document":{"compressible":false,"extensions":["gdoc"]},"application/vnd.google-apps.presentation":{"compressible":false,"extensions":["gslides"]},"application/vnd.google-apps.spreadsheet":{"compressible":false,"extensions":["gsheet"]},"application/vnd.google-earth.kml+xml":{"source":"iana","compressible":true,"extensions":["kml"]},"application/vnd.google-earth.kmz":{"source":"iana","compressible":false,"extensions":["kmz"]},"application/vnd.gov.sk.e-form+xml":{"source":"iana","compressible":true},"application/vnd.gov.sk.e-form+zip":{"source":"iana","compressible":false},"application/vnd.gov.sk.xmldatacontainer+xml":{"source":"iana","compressible":true},"application/vnd.grafeq":{"source":"iana","extensions":["gqf","gqs"]},"application/vnd.gridmp":{"source":"iana"},"application/vnd.groove-account":{"source":"iana","extensions":["gac"]},"application/vnd.groove-help":{"source":"iana","extensions":["ghf"]},"application/vnd.groove-identity-message":{"source":"iana","extensions":["gim"]},"application/vnd.groove-injector":{"source":"iana","extensions":["grv"]},"application/vnd.groove-tool-message":{"source":"iana","extensions":["gtm"]},"application/vnd.groove-tool-template":{"source":"iana","extensions":["tpl"]},"application/vnd.groove-vcard":{"source":"iana","extensions":["vcg"]},"application/vnd.hal+json":{"source":"iana","compressible":true},"application/vnd.hal+xml":{"source":"iana","compressible":true,"extensions":["hal"]},"application/vnd.handheld-entertainment+xml":{"source":"iana","compressible":true,"extensions":["zmm"]},"application/vnd.hbci":{"source":"iana","extensions":["hbci"]},"application/vnd.hc+json":{"source":"iana","compressible":true},"application/vnd.hcl-bireports":{"source":"iana"},"application/vnd.hdt":{"source":"iana"},"application/vnd.heroku+json":{"source":"iana","compressible":true},"application/vnd.hhe.lesson-player":{"source":"iana","extensions":["les"]},"application/vnd.hp-hpgl":{"source":"iana","extensions":["hpgl"]},"application/vnd.hp-hpid":{"source":"iana","extensions":["hpid"]},"application/vnd.hp-hps":{"source":"iana","extensions":["hps"]},"application/vnd.hp-jlyt":{"source":"iana","extensions":["jlt"]},"application/vnd.hp-pcl":{"source":"iana","extensions":["pcl"]},"application/vnd.hp-pclxl":{"source":"iana","extensions":["pclxl"]},"application/vnd.httphone":{"source":"iana"},"application/vnd.hydrostatix.sof-data":{"source":"iana","extensions":["sfd-hdstx"]},"application/vnd.hyper+json":{"source":"iana","compressible":true},"application/vnd.hyper-item+json":{"source":"iana","compressible":true},"application/vnd.hyperdrive+json":{"source":"iana","compressible":true},"application/vnd.hzn-3d-crossword":{"source":"iana"},"application/vnd.ibm.afplinedata":{"source":"iana"},"application/vnd.ibm.electronic-media":{"source":"iana"},"application/vnd.ibm.minipay":{"source":"iana","extensions":["mpy"]},"application/vnd.ibm.modcap":{"source":"iana","extensions":["afp","listafp","list3820"]},"application/vnd.ibm.rights-management":{"source":"iana","extensions":["irm"]},"application/vnd.ibm.secure-container":{"source":"iana","extensions":["sc"]},"application/vnd.iccprofile":{"source":"iana","extensions":["icc","icm"]},"application/vnd.ieee.1905":{"source":"iana"},"application/vnd.igloader":{"source":"iana","extensions":["igl"]},"application/vnd.imagemeter.folder+zip":{"source":"iana","compressible":false},"application/vnd.imagemeter.image+zip":{"source":"iana","compressible":false},"application/vnd.immervision-ivp":{"source":"iana","extensions":["ivp"]},"application/vnd.immervision-ivu":{"source":"iana","extensions":["ivu"]},"application/vnd.ims.imsccv1p1":{"source":"iana"},"application/vnd.ims.imsccv1p2":{"source":"iana"},"application/vnd.ims.imsccv1p3":{"source":"iana"},"application/vnd.ims.lis.v2.result+json":{"source":"iana","compressible":true},"application/vnd.ims.lti.v2.toolconsumerprofile+json":{"source":"iana","compressible":true},"application/vnd.ims.lti.v2.toolproxy+json":{"source":"iana","compressible":true},"application/vnd.ims.lti.v2.toolproxy.id+json":{"source":"iana","compressible":true},"application/vnd.ims.lti.v2.toolsettings+json":{"source":"iana","compressible":true},"application/vnd.ims.lti.v2.toolsettings.simple+json":{"source":"iana","compressible":true},"application/vnd.informedcontrol.rms+xml":{"source":"iana","compressible":true},"application/vnd.informix-visionary":{"source":"iana"},"application/vnd.infotech.project":{"source":"iana"},"application/vnd.infotech.project+xml":{"source":"iana","compressible":true},"application/vnd.innopath.wamp.notification":{"source":"iana"},"application/vnd.insors.igm":{"source":"iana","extensions":["igm"]},"application/vnd.intercon.formnet":{"source":"iana","extensions":["xpw","xpx"]},"application/vnd.intergeo":{"source":"iana","extensions":["i2g"]},"application/vnd.intertrust.digibox":{"source":"iana"},"application/vnd.intertrust.nncp":{"source":"iana"},"application/vnd.intu.qbo":{"source":"iana","extensions":["qbo"]},"application/vnd.intu.qfx":{"source":"iana","extensions":["qfx"]},"application/vnd.iptc.g2.catalogitem+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.conceptitem+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.knowledgeitem+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.newsitem+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.newsmessage+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.packageitem+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.planningitem+xml":{"source":"iana","compressible":true},"application/vnd.ipunplugged.rcprofile":{"source":"iana","extensions":["rcprofile"]},"application/vnd.irepository.package+xml":{"source":"iana","compressible":true,"extensions":["irp"]},"application/vnd.is-xpr":{"source":"iana","extensions":["xpr"]},"application/vnd.isac.fcs":{"source":"iana","extensions":["fcs"]},"application/vnd.iso11783-10+zip":{"source":"iana","compressible":false},"application/vnd.jam":{"source":"iana","extensions":["jam"]},"application/vnd.japannet-directory-service":{"source":"iana"},"application/vnd.japannet-jpnstore-wakeup":{"source":"iana"},"application/vnd.japannet-payment-wakeup":{"source":"iana"},"application/vnd.japannet-registration":{"source":"iana"},"application/vnd.japannet-registration-wakeup":{"source":"iana"},"application/vnd.japannet-setstore-wakeup":{"source":"iana"},"application/vnd.japannet-verification":{"source":"iana"},"application/vnd.japannet-verification-wakeup":{"source":"iana"},"application/vnd.jcp.javame.midlet-rms":{"source":"iana","extensions":["rms"]},"application/vnd.jisp":{"source":"iana","extensions":["jisp"]},"application/vnd.joost.joda-archive":{"source":"iana","extensions":["joda"]},"application/vnd.jsk.isdn-ngn":{"source":"iana"},"application/vnd.kahootz":{"source":"iana","extensions":["ktz","ktr"]},"application/vnd.kde.karbon":{"source":"iana","extensions":["karbon"]},"application/vnd.kde.kchart":{"source":"iana","extensions":["chrt"]},"application/vnd.kde.kformula":{"source":"iana","extensions":["kfo"]},"application/vnd.kde.kivio":{"source":"iana","extensions":["flw"]},"application/vnd.kde.kontour":{"source":"iana","extensions":["kon"]},"application/vnd.kde.kpresenter":{"source":"iana","extensions":["kpr","kpt"]},"application/vnd.kde.kspread":{"source":"iana","extensions":["ksp"]},"application/vnd.kde.kword":{"source":"iana","extensions":["kwd","kwt"]},"application/vnd.kenameaapp":{"source":"iana","extensions":["htke"]},"application/vnd.kidspiration":{"source":"iana","extensions":["kia"]},"application/vnd.kinar":{"source":"iana","extensions":["kne","knp"]},"application/vnd.koan":{"source":"iana","extensions":["skp","skd","skt","skm"]},"application/vnd.kodak-descriptor":{"source":"iana","extensions":["sse"]},"application/vnd.las":{"source":"iana"},"application/vnd.las.las+json":{"source":"iana","compressible":true},"application/vnd.las.las+xml":{"source":"iana","compressible":true,"extensions":["lasxml"]},"application/vnd.laszip":{"source":"iana"},"application/vnd.leap+json":{"source":"iana","compressible":true},"application/vnd.liberty-request+xml":{"source":"iana","compressible":true},"application/vnd.llamagraphics.life-balance.desktop":{"source":"iana","extensions":["lbd"]},"application/vnd.llamagraphics.life-balance.exchange+xml":{"source":"iana","compressible":true,"extensions":["lbe"]},"application/vnd.logipipe.circuit+zip":{"source":"iana","compressible":false},"application/vnd.loom":{"source":"iana"},"application/vnd.lotus-1-2-3":{"source":"iana","extensions":["123"]},"application/vnd.lotus-approach":{"source":"iana","extensions":["apr"]},"application/vnd.lotus-freelance":{"source":"iana","extensions":["pre"]},"application/vnd.lotus-notes":{"source":"iana","extensions":["nsf"]},"application/vnd.lotus-organizer":{"source":"iana","extensions":["org"]},"application/vnd.lotus-screencam":{"source":"iana","extensions":["scm"]},"application/vnd.lotus-wordpro":{"source":"iana","extensions":["lwp"]},"application/vnd.macports.portpkg":{"source":"iana","extensions":["portpkg"]},"application/vnd.mapbox-vector-tile":{"source":"iana"},"application/vnd.marlin.drm.actiontoken+xml":{"source":"iana","compressible":true},"application/vnd.marlin.drm.conftoken+xml":{"source":"iana","compressible":true},"application/vnd.marlin.drm.license+xml":{"source":"iana","compressible":true},"application/vnd.marlin.drm.mdcf":{"source":"iana"},"application/vnd.mason+json":{"source":"iana","compressible":true},"application/vnd.maxmind.maxmind-db":{"source":"iana"},"application/vnd.mcd":{"source":"iana","extensions":["mcd"]},"application/vnd.medcalcdata":{"source":"iana","extensions":["mc1"]},"application/vnd.mediastation.cdkey":{"source":"iana","extensions":["cdkey"]},"application/vnd.meridian-slingshot":{"source":"iana"},"application/vnd.mfer":{"source":"iana","extensions":["mwf"]},"application/vnd.mfmp":{"source":"iana","extensions":["mfm"]},"application/vnd.micro+json":{"source":"iana","compressible":true},"application/vnd.micrografx.flo":{"source":"iana","extensions":["flo"]},"application/vnd.micrografx.igx":{"source":"iana","extensions":["igx"]},"application/vnd.microsoft.portable-executable":{"source":"iana"},"application/vnd.microsoft.windows.thumbnail-cache":{"source":"iana"},"application/vnd.miele+json":{"source":"iana","compressible":true},"application/vnd.mif":{"source":"iana","extensions":["mif"]},"application/vnd.minisoft-hp3000-save":{"source":"iana"},"application/vnd.mitsubishi.misty-guard.trustweb":{"source":"iana"},"application/vnd.mobius.daf":{"source":"iana","extensions":["daf"]},"application/vnd.mobius.dis":{"source":"iana","extensions":["dis"]},"application/vnd.mobius.mbk":{"source":"iana","extensions":["mbk"]},"application/vnd.mobius.mqy":{"source":"iana","extensions":["mqy"]},"application/vnd.mobius.msl":{"source":"iana","extensions":["msl"]},"application/vnd.mobius.plc":{"source":"iana","extensions":["plc"]},"application/vnd.mobius.txf":{"source":"iana","extensions":["txf"]},"application/vnd.mophun.application":{"source":"iana","extensions":["mpn"]},"application/vnd.mophun.certificate":{"source":"iana","extensions":["mpc"]},"application/vnd.motorola.flexsuite":{"source":"iana"},"application/vnd.motorola.flexsuite.adsi":{"source":"iana"},"application/vnd.motorola.flexsuite.fis":{"source":"iana"},"application/vnd.motorola.flexsuite.gotap":{"source":"iana"},"application/vnd.motorola.flexsuite.kmr":{"source":"iana"},"application/vnd.motorola.flexsuite.ttc":{"source":"iana"},"application/vnd.motorola.flexsuite.wem":{"source":"iana"},"application/vnd.motorola.iprm":{"source":"iana"},"application/vnd.mozilla.xul+xml":{"source":"iana","compressible":true,"extensions":["xul"]},"application/vnd.ms-3mfdocument":{"source":"iana"},"application/vnd.ms-artgalry":{"source":"iana","extensions":["cil"]},"application/vnd.ms-asf":{"source":"iana"},"application/vnd.ms-cab-compressed":{"source":"iana","extensions":["cab"]},"application/vnd.ms-color.iccprofile":{"source":"apache"},"application/vnd.ms-excel":{"source":"iana","compressible":false,"extensions":["xls","xlm","xla","xlc","xlt","xlw"]},"application/vnd.ms-excel.addin.macroenabled.12":{"source":"iana","extensions":["xlam"]},"application/vnd.ms-excel.sheet.binary.macroenabled.12":{"source":"iana","extensions":["xlsb"]},"application/vnd.ms-excel.sheet.macroenabled.12":{"source":"iana","extensions":["xlsm"]},"application/vnd.ms-excel.template.macroenabled.12":{"source":"iana","extensions":["xltm"]},"application/vnd.ms-fontobject":{"source":"iana","compressible":true,"extensions":["eot"]},"application/vnd.ms-htmlhelp":{"source":"iana","extensions":["chm"]},"application/vnd.ms-ims":{"source":"iana","extensions":["ims"]},"application/vnd.ms-lrm":{"source":"iana","extensions":["lrm"]},"application/vnd.ms-office.activex+xml":{"source":"iana","compressible":true},"application/vnd.ms-officetheme":{"source":"iana","extensions":["thmx"]},"application/vnd.ms-opentype":{"source":"apache","compressible":true},"application/vnd.ms-outlook":{"compressible":false,"extensions":["msg"]},"application/vnd.ms-package.obfuscated-opentype":{"source":"apache"},"application/vnd.ms-pki.seccat":{"source":"apache","extensions":["cat"]},"application/vnd.ms-pki.stl":{"source":"apache","extensions":["stl"]},"application/vnd.ms-playready.initiator+xml":{"source":"iana","compressible":true},"application/vnd.ms-powerpoint":{"source":"iana","compressible":false,"extensions":["ppt","pps","pot"]},"application/vnd.ms-powerpoint.addin.macroenabled.12":{"source":"iana","extensions":["ppam"]},"application/vnd.ms-powerpoint.presentation.macroenabled.12":{"source":"iana","extensions":["pptm"]},"application/vnd.ms-powerpoint.slide.macroenabled.12":{"source":"iana","extensions":["sldm"]},"application/vnd.ms-powerpoint.slideshow.macroenabled.12":{"source":"iana","extensions":["ppsm"]},"application/vnd.ms-powerpoint.template.macroenabled.12":{"source":"iana","extensions":["potm"]},"application/vnd.ms-printdevicecapabilities+xml":{"source":"iana","compressible":true},"application/vnd.ms-printing.printticket+xml":{"source":"apache","compressible":true},"application/vnd.ms-printschematicket+xml":{"source":"iana","compressible":true},"application/vnd.ms-project":{"source":"iana","extensions":["mpp","mpt"]},"application/vnd.ms-tnef":{"source":"iana"},"application/vnd.ms-windows.devicepairing":{"source":"iana"},"application/vnd.ms-windows.nwprinting.oob":{"source":"iana"},"application/vnd.ms-windows.printerpairing":{"source":"iana"},"application/vnd.ms-windows.wsd.oob":{"source":"iana"},"application/vnd.ms-wmdrm.lic-chlg-req":{"source":"iana"},"application/vnd.ms-wmdrm.lic-resp":{"source":"iana"},"application/vnd.ms-wmdrm.meter-chlg-req":{"source":"iana"},"application/vnd.ms-wmdrm.meter-resp":{"source":"iana"},"application/vnd.ms-word.document.macroenabled.12":{"source":"iana","extensions":["docm"]},"application/vnd.ms-word.template.macroenabled.12":{"source":"iana","extensions":["dotm"]},"application/vnd.ms-works":{"source":"iana","extensions":["wps","wks","wcm","wdb"]},"application/vnd.ms-wpl":{"source":"iana","extensions":["wpl"]},"application/vnd.ms-xpsdocument":{"source":"iana","compressible":false,"extensions":["xps"]},"application/vnd.msa-disk-image":{"source":"iana"},"application/vnd.mseq":{"source":"iana","extensions":["mseq"]},"application/vnd.msign":{"source":"iana"},"application/vnd.multiad.creator":{"source":"iana"},"application/vnd.multiad.creator.cif":{"source":"iana"},"application/vnd.music-niff":{"source":"iana"},"application/vnd.musician":{"source":"iana","extensions":["mus"]},"application/vnd.muvee.style":{"source":"iana","extensions":["msty"]},"application/vnd.mynfc":{"source":"iana","extensions":["taglet"]},"application/vnd.ncd.control":{"source":"iana"},"application/vnd.ncd.reference":{"source":"iana"},"application/vnd.nearst.inv+json":{"source":"iana","compressible":true},"application/vnd.nervana":{"source":"iana"},"application/vnd.netfpx":{"source":"iana"},"application/vnd.neurolanguage.nlu":{"source":"iana","extensions":["nlu"]},"application/vnd.nimn":{"source":"iana"},"application/vnd.nintendo.nitro.rom":{"source":"iana"},"application/vnd.nintendo.snes.rom":{"source":"iana"},"application/vnd.nitf":{"source":"iana","extensions":["ntf","nitf"]},"application/vnd.noblenet-directory":{"source":"iana","extensions":["nnd"]},"application/vnd.noblenet-sealer":{"source":"iana","extensions":["nns"]},"application/vnd.noblenet-web":{"source":"iana","extensions":["nnw"]},"application/vnd.nokia.catalogs":{"source":"iana"},"application/vnd.nokia.conml+wbxml":{"source":"iana"},"application/vnd.nokia.conml+xml":{"source":"iana","compressible":true},"application/vnd.nokia.iptv.config+xml":{"source":"iana","compressible":true},"application/vnd.nokia.isds-radio-presets":{"source":"iana"},"application/vnd.nokia.landmark+wbxml":{"source":"iana"},"application/vnd.nokia.landmark+xml":{"source":"iana","compressible":true},"application/vnd.nokia.landmarkcollection+xml":{"source":"iana","compressible":true},"application/vnd.nokia.n-gage.ac+xml":{"source":"iana","compressible":true,"extensions":["ac"]},"application/vnd.nokia.n-gage.data":{"source":"iana","extensions":["ngdat"]},"application/vnd.nokia.n-gage.symbian.install":{"source":"iana","extensions":["n-gage"]},"application/vnd.nokia.ncd":{"source":"iana"},"application/vnd.nokia.pcd+wbxml":{"source":"iana"},"application/vnd.nokia.pcd+xml":{"source":"iana","compressible":true},"application/vnd.nokia.radio-preset":{"source":"iana","extensions":["rpst"]},"application/vnd.nokia.radio-presets":{"source":"iana","extensions":["rpss"]},"application/vnd.novadigm.edm":{"source":"iana","extensions":["edm"]},"application/vnd.novadigm.edx":{"source":"iana","extensions":["edx"]},"application/vnd.novadigm.ext":{"source":"iana","extensions":["ext"]},"application/vnd.ntt-local.content-share":{"source":"iana"},"application/vnd.ntt-local.file-transfer":{"source":"iana"},"application/vnd.ntt-local.ogw_remote-access":{"source":"iana"},"application/vnd.ntt-local.sip-ta_remote":{"source":"iana"},"application/vnd.ntt-local.sip-ta_tcp_stream":{"source":"iana"},"application/vnd.oasis.opendocument.chart":{"source":"iana","extensions":["odc"]},"application/vnd.oasis.opendocument.chart-template":{"source":"iana","extensions":["otc"]},"application/vnd.oasis.opendocument.database":{"source":"iana","extensions":["odb"]},"application/vnd.oasis.opendocument.formula":{"source":"iana","extensions":["odf"]},"application/vnd.oasis.opendocument.formula-template":{"source":"iana","extensions":["odft"]},"application/vnd.oasis.opendocument.graphics":{"source":"iana","compressible":false,"extensions":["odg"]},"application/vnd.oasis.opendocument.graphics-template":{"source":"iana","extensions":["otg"]},"application/vnd.oasis.opendocument.image":{"source":"iana","extensions":["odi"]},"application/vnd.oasis.opendocument.image-template":{"source":"iana","extensions":["oti"]},"application/vnd.oasis.opendocument.presentation":{"source":"iana","compressible":false,"extensions":["odp"]},"application/vnd.oasis.opendocument.presentation-template":{"source":"iana","extensions":["otp"]},"application/vnd.oasis.opendocument.spreadsheet":{"source":"iana","compressible":false,"extensions":["ods"]},"application/vnd.oasis.opendocument.spreadsheet-template":{"source":"iana","extensions":["ots"]},"application/vnd.oasis.opendocument.text":{"source":"iana","compressible":false,"extensions":["odt"]},"application/vnd.oasis.opendocument.text-master":{"source":"iana","extensions":["odm"]},"application/vnd.oasis.opendocument.text-template":{"source":"iana","extensions":["ott"]},"application/vnd.oasis.opendocument.text-web":{"source":"iana","extensions":["oth"]},"application/vnd.obn":{"source":"iana"},"application/vnd.ocf+cbor":{"source":"iana"},"application/vnd.oftn.l10n+json":{"source":"iana","compressible":true},"application/vnd.oipf.contentaccessdownload+xml":{"source":"iana","compressible":true},"application/vnd.oipf.contentaccessstreaming+xml":{"source":"iana","compressible":true},"application/vnd.oipf.cspg-hexbinary":{"source":"iana"},"application/vnd.oipf.dae.svg+xml":{"source":"iana","compressible":true},"application/vnd.oipf.dae.xhtml+xml":{"source":"iana","compressible":true},"application/vnd.oipf.mippvcontrolmessage+xml":{"source":"iana","compressible":true},"application/vnd.oipf.pae.gem":{"source":"iana"},"application/vnd.oipf.spdiscovery+xml":{"source":"iana","compressible":true},"application/vnd.oipf.spdlist+xml":{"source":"iana","compressible":true},"application/vnd.oipf.ueprofile+xml":{"source":"iana","compressible":true},"application/vnd.oipf.userprofile+xml":{"source":"iana","compressible":true},"application/vnd.olpc-sugar":{"source":"iana","extensions":["xo"]},"application/vnd.oma-scws-config":{"source":"iana"},"application/vnd.oma-scws-http-request":{"source":"iana"},"application/vnd.oma-scws-http-response":{"source":"iana"},"application/vnd.oma.bcast.associated-procedure-parameter+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.drm-trigger+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.imd+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.ltkm":{"source":"iana"},"application/vnd.oma.bcast.notification+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.provisioningtrigger":{"source":"iana"},"application/vnd.oma.bcast.sgboot":{"source":"iana"},"application/vnd.oma.bcast.sgdd+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.sgdu":{"source":"iana"},"application/vnd.oma.bcast.simple-symbol-container":{"source":"iana"},"application/vnd.oma.bcast.smartcard-trigger+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.sprov+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.stkm":{"source":"iana"},"application/vnd.oma.cab-address-book+xml":{"source":"iana","compressible":true},"application/vnd.oma.cab-feature-handler+xml":{"source":"iana","compressible":true},"application/vnd.oma.cab-pcc+xml":{"source":"iana","compressible":true},"application/vnd.oma.cab-subs-invite+xml":{"source":"iana","compressible":true},"application/vnd.oma.cab-user-prefs+xml":{"source":"iana","compressible":true},"application/vnd.oma.dcd":{"source":"iana"},"application/vnd.oma.dcdc":{"source":"iana"},"application/vnd.oma.dd2+xml":{"source":"iana","compressible":true,"extensions":["dd2"]},"application/vnd.oma.drm.risd+xml":{"source":"iana","compressible":true},"application/vnd.oma.group-usage-list+xml":{"source":"iana","compressible":true},"application/vnd.oma.lwm2m+json":{"source":"iana","compressible":true},"application/vnd.oma.lwm2m+tlv":{"source":"iana"},"application/vnd.oma.pal+xml":{"source":"iana","compressible":true},"application/vnd.oma.poc.detailed-progress-report+xml":{"source":"iana","compressible":true},"application/vnd.oma.poc.final-report+xml":{"source":"iana","compressible":true},"application/vnd.oma.poc.groups+xml":{"source":"iana","compressible":true},"application/vnd.oma.poc.invocation-descriptor+xml":{"source":"iana","compressible":true},"application/vnd.oma.poc.optimized-progress-report+xml":{"source":"iana","compressible":true},"application/vnd.oma.push":{"source":"iana"},"application/vnd.oma.scidm.messages+xml":{"source":"iana","compressible":true},"application/vnd.oma.xcap-directory+xml":{"source":"iana","compressible":true},"application/vnd.omads-email+xml":{"source":"iana","compressible":true},"application/vnd.omads-file+xml":{"source":"iana","compressible":true},"application/vnd.omads-folder+xml":{"source":"iana","compressible":true},"application/vnd.omaloc-supl-init":{"source":"iana"},"application/vnd.onepager":{"source":"iana"},"application/vnd.onepagertamp":{"source":"iana"},"application/vnd.onepagertamx":{"source":"iana"},"application/vnd.onepagertat":{"source":"iana"},"application/vnd.onepagertatp":{"source":"iana"},"application/vnd.onepagertatx":{"source":"iana"},"application/vnd.openblox.game+xml":{"source":"iana","compressible":true,"extensions":["obgx"]},"application/vnd.openblox.game-binary":{"source":"iana"},"application/vnd.openeye.oeb":{"source":"iana"},"application/vnd.openofficeorg.extension":{"source":"apache","extensions":["oxt"]},"application/vnd.openstreetmap.data+xml":{"source":"iana","compressible":true,"extensions":["osm"]},"application/vnd.openxmlformats-officedocument.custom-properties+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.customxmlproperties+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawing+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.chart+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.diagramdata+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.extended-properties+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.commentauthors+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.comments+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.notesmaster+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.notesslide+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.presentation":{"source":"iana","compressible":false,"extensions":["pptx"]},"application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.presprops+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.slide":{"source":"iana","extensions":["sldx"]},"application/vnd.openxmlformats-officedocument.presentationml.slide+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.slidelayout+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.slidemaster+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.slideshow":{"source":"iana","extensions":["ppsx"]},"application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.tablestyles+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.tags+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.template":{"source":"iana","extensions":["potx"]},"application/vnd.openxmlformats-officedocument.presentationml.template.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.viewprops+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":{"source":"iana","compressible":false,"extensions":["xlsx"]},"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.template":{"source":"iana","extensions":["xltx"]},"application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.theme+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.themeoverride+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.vmldrawing":{"source":"iana"},"application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.document":{"source":"iana","compressible":false,"extensions":["docx"]},"application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.template":{"source":"iana","extensions":["dotx"]},"application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-package.core-properties+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-package.relationships+xml":{"source":"iana","compressible":true},"application/vnd.oracle.resource+json":{"source":"iana","compressible":true},"application/vnd.orange.indata":{"source":"iana"},"application/vnd.osa.netdeploy":{"source":"iana"},"application/vnd.osgeo.mapguide.package":{"source":"iana","extensions":["mgp"]},"application/vnd.osgi.bundle":{"source":"iana"},"application/vnd.osgi.dp":{"source":"iana","extensions":["dp"]},"application/vnd.osgi.subsystem":{"source":"iana","extensions":["esa"]},"application/vnd.otps.ct-kip+xml":{"source":"iana","compressible":true},"application/vnd.oxli.countgraph":{"source":"iana"},"application/vnd.pagerduty+json":{"source":"iana","compressible":true},"application/vnd.palm":{"source":"iana","extensions":["pdb","pqa","oprc"]},"application/vnd.panoply":{"source":"iana"},"application/vnd.paos.xml":{"source":"iana"},"application/vnd.patentdive":{"source":"iana"},"application/vnd.patientecommsdoc":{"source":"iana"},"application/vnd.pawaafile":{"source":"iana","extensions":["paw"]},"application/vnd.pcos":{"source":"iana"},"application/vnd.pg.format":{"source":"iana","extensions":["str"]},"application/vnd.pg.osasli":{"source":"iana","extensions":["ei6"]},"application/vnd.piaccess.application-licence":{"source":"iana"},"application/vnd.picsel":{"source":"iana","extensions":["efif"]},"application/vnd.pmi.widget":{"source":"iana","extensions":["wg"]},"application/vnd.poc.group-advertisement+xml":{"source":"iana","compressible":true},"application/vnd.pocketlearn":{"source":"iana","extensions":["plf"]},"application/vnd.powerbuilder6":{"source":"iana","extensions":["pbd"]},"application/vnd.powerbuilder6-s":{"source":"iana"},"application/vnd.powerbuilder7":{"source":"iana"},"application/vnd.powerbuilder7-s":{"source":"iana"},"application/vnd.powerbuilder75":{"source":"iana"},"application/vnd.powerbuilder75-s":{"source":"iana"},"application/vnd.preminet":{"source":"iana"},"application/vnd.previewsystems.box":{"source":"iana","extensions":["box"]},"application/vnd.proteus.magazine":{"source":"iana","extensions":["mgz"]},"application/vnd.psfs":{"source":"iana"},"application/vnd.publishare-delta-tree":{"source":"iana","extensions":["qps"]},"application/vnd.pvi.ptid1":{"source":"iana","extensions":["ptid"]},"application/vnd.pwg-multiplexed":{"source":"iana"},"application/vnd.pwg-xhtml-print+xml":{"source":"iana","compressible":true},"application/vnd.qualcomm.brew-app-res":{"source":"iana"},"application/vnd.quarantainenet":{"source":"iana"},"application/vnd.quark.quarkxpress":{"source":"iana","extensions":["qxd","qxt","qwd","qwt","qxl","qxb"]},"application/vnd.quobject-quoxdocument":{"source":"iana"},"application/vnd.radisys.moml+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-audit+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-audit-conf+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-audit-conn+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-audit-dialog+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-audit-stream+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-conf+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-base+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-fax-detect+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-fax-sendrecv+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-group+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-speech+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-transform+xml":{"source":"iana","compressible":true},"application/vnd.rainstor.data":{"source":"iana"},"application/vnd.rapid":{"source":"iana"},"application/vnd.rar":{"source":"iana"},"application/vnd.realvnc.bed":{"source":"iana","extensions":["bed"]},"application/vnd.recordare.musicxml":{"source":"iana","extensions":["mxl"]},"application/vnd.recordare.musicxml+xml":{"source":"iana","compressible":true,"extensions":["musicxml"]},"application/vnd.renlearn.rlprint":{"source":"iana"},"application/vnd.restful+json":{"source":"iana","compressible":true},"application/vnd.rig.cryptonote":{"source":"iana","extensions":["cryptonote"]},"application/vnd.rim.cod":{"source":"apache","extensions":["cod"]},"application/vnd.rn-realmedia":{"source":"apache","extensions":["rm"]},"application/vnd.rn-realmedia-vbr":{"source":"apache","extensions":["rmvb"]},"application/vnd.route66.link66+xml":{"source":"iana","compressible":true,"extensions":["link66"]},"application/vnd.rs-274x":{"source":"iana"},"application/vnd.ruckus.download":{"source":"iana"},"application/vnd.s3sms":{"source":"iana"},"application/vnd.sailingtracker.track":{"source":"iana","extensions":["st"]},"application/vnd.sbm.cid":{"source":"iana"},"application/vnd.sbm.mid2":{"source":"iana"},"application/vnd.scribus":{"source":"iana"},"application/vnd.sealed.3df":{"source":"iana"},"application/vnd.sealed.csf":{"source":"iana"},"application/vnd.sealed.doc":{"source":"iana"},"application/vnd.sealed.eml":{"source":"iana"},"application/vnd.sealed.mht":{"source":"iana"},"application/vnd.sealed.net":{"source":"iana"},"application/vnd.sealed.ppt":{"source":"iana"},"application/vnd.sealed.tiff":{"source":"iana"},"application/vnd.sealed.xls":{"source":"iana"},"application/vnd.sealedmedia.softseal.html":{"source":"iana"},"application/vnd.sealedmedia.softseal.pdf":{"source":"iana"},"application/vnd.seemail":{"source":"iana","extensions":["see"]},"application/vnd.sema":{"source":"iana","extensions":["sema"]},"application/vnd.semd":{"source":"iana","extensions":["semd"]},"application/vnd.semf":{"source":"iana","extensions":["semf"]},"application/vnd.shade-save-file":{"source":"iana"},"application/vnd.shana.informed.formdata":{"source":"iana","extensions":["ifm"]},"application/vnd.shana.informed.formtemplate":{"source":"iana","extensions":["itp"]},"application/vnd.shana.informed.interchange":{"source":"iana","extensions":["iif"]},"application/vnd.shana.informed.package":{"source":"iana","extensions":["ipk"]},"application/vnd.shootproof+json":{"source":"iana","compressible":true},"application/vnd.shopkick+json":{"source":"iana","compressible":true},"application/vnd.sigrok.session":{"source":"iana"},"application/vnd.simtech-mindmapper":{"source":"iana","extensions":["twd","twds"]},"application/vnd.siren+json":{"source":"iana","compressible":true},"application/vnd.smaf":{"source":"iana","extensions":["mmf"]},"application/vnd.smart.notebook":{"source":"iana"},"application/vnd.smart.teacher":{"source":"iana","extensions":["teacher"]},"application/vnd.software602.filler.form+xml":{"source":"iana","compressible":true,"extensions":["fo"]},"application/vnd.software602.filler.form-xml-zip":{"source":"iana"},"application/vnd.solent.sdkm+xml":{"source":"iana","compressible":true,"extensions":["sdkm","sdkd"]},"application/vnd.spotfire.dxp":{"source":"iana","extensions":["dxp"]},"application/vnd.spotfire.sfs":{"source":"iana","extensions":["sfs"]},"application/vnd.sqlite3":{"source":"iana"},"application/vnd.sss-cod":{"source":"iana"},"application/vnd.sss-dtf":{"source":"iana"},"application/vnd.sss-ntf":{"source":"iana"},"application/vnd.stardivision.calc":{"source":"apache","extensions":["sdc"]},"application/vnd.stardivision.draw":{"source":"apache","extensions":["sda"]},"application/vnd.stardivision.impress":{"source":"apache","extensions":["sdd"]},"application/vnd.stardivision.math":{"source":"apache","extensions":["smf"]},"application/vnd.stardivision.writer":{"source":"apache","extensions":["sdw","vor"]},"application/vnd.stardivision.writer-global":{"source":"apache","extensions":["sgl"]},"application/vnd.stepmania.package":{"source":"iana","extensions":["smzip"]},"application/vnd.stepmania.stepchart":{"source":"iana","extensions":["sm"]},"application/vnd.street-stream":{"source":"iana"},"application/vnd.sun.wadl+xml":{"source":"iana","compressible":true,"extensions":["wadl"]},"application/vnd.sun.xml.calc":{"source":"apache","extensions":["sxc"]},"application/vnd.sun.xml.calc.template":{"source":"apache","extensions":["stc"]},"application/vnd.sun.xml.draw":{"source":"apache","extensions":["sxd"]},"application/vnd.sun.xml.draw.template":{"source":"apache","extensions":["std"]},"application/vnd.sun.xml.impress":{"source":"apache","extensions":["sxi"]},"application/vnd.sun.xml.impress.template":{"source":"apache","extensions":["sti"]},"application/vnd.sun.xml.math":{"source":"apache","extensions":["sxm"]},"application/vnd.sun.xml.writer":{"source":"apache","extensions":["sxw"]},"application/vnd.sun.xml.writer.global":{"source":"apache","extensions":["sxg"]},"application/vnd.sun.xml.writer.template":{"source":"apache","extensions":["stw"]},"application/vnd.sus-calendar":{"source":"iana","extensions":["sus","susp"]},"application/vnd.svd":{"source":"iana","extensions":["svd"]},"application/vnd.swiftview-ics":{"source":"iana"},"application/vnd.symbian.install":{"source":"apache","extensions":["sis","sisx"]},"application/vnd.syncml+xml":{"source":"iana","compressible":true,"extensions":["xsm"]},"application/vnd.syncml.dm+wbxml":{"source":"iana","extensions":["bdm"]},"application/vnd.syncml.dm+xml":{"source":"iana","compressible":true,"extensions":["xdm"]},"application/vnd.syncml.dm.notification":{"source":"iana"},"application/vnd.syncml.dmddf+wbxml":{"source":"iana"},"application/vnd.syncml.dmddf+xml":{"source":"iana","compressible":true,"extensions":["ddf"]},"application/vnd.syncml.dmtnds+wbxml":{"source":"iana"},"application/vnd.syncml.dmtnds+xml":{"source":"iana","compressible":true},"application/vnd.syncml.ds.notification":{"source":"iana"},"application/vnd.tableschema+json":{"source":"iana","compressible":true},"application/vnd.tao.intent-module-archive":{"source":"iana","extensions":["tao"]},"application/vnd.tcpdump.pcap":{"source":"iana","extensions":["pcap","cap","dmp"]},"application/vnd.think-cell.ppttc+json":{"source":"iana","compressible":true},"application/vnd.tmd.mediaflex.api+xml":{"source":"iana","compressible":true},"application/vnd.tml":{"source":"iana"},"application/vnd.tmobile-livetv":{"source":"iana","extensions":["tmo"]},"application/vnd.tri.onesource":{"source":"iana"},"application/vnd.trid.tpt":{"source":"iana","extensions":["tpt"]},"application/vnd.triscape.mxs":{"source":"iana","extensions":["mxs"]},"application/vnd.trueapp":{"source":"iana","extensions":["tra"]},"application/vnd.truedoc":{"source":"iana"},"application/vnd.ubisoft.webplayer":{"source":"iana"},"application/vnd.ufdl":{"source":"iana","extensions":["ufd","ufdl"]},"application/vnd.uiq.theme":{"source":"iana","extensions":["utz"]},"application/vnd.umajin":{"source":"iana","extensions":["umj"]},"application/vnd.unity":{"source":"iana","extensions":["unityweb"]},"application/vnd.uoml+xml":{"source":"iana","compressible":true,"extensions":["uoml"]},"application/vnd.uplanet.alert":{"source":"iana"},"application/vnd.uplanet.alert-wbxml":{"source":"iana"},"application/vnd.uplanet.bearer-choice":{"source":"iana"},"application/vnd.uplanet.bearer-choice-wbxml":{"source":"iana"},"application/vnd.uplanet.cacheop":{"source":"iana"},"application/vnd.uplanet.cacheop-wbxml":{"source":"iana"},"application/vnd.uplanet.channel":{"source":"iana"},"application/vnd.uplanet.channel-wbxml":{"source":"iana"},"application/vnd.uplanet.list":{"source":"iana"},"application/vnd.uplanet.list-wbxml":{"source":"iana"},"application/vnd.uplanet.listcmd":{"source":"iana"},"application/vnd.uplanet.listcmd-wbxml":{"source":"iana"},"application/vnd.uplanet.signal":{"source":"iana"},"application/vnd.uri-map":{"source":"iana"},"application/vnd.valve.source.material":{"source":"iana"},"application/vnd.vcx":{"source":"iana","extensions":["vcx"]},"application/vnd.vd-study":{"source":"iana"},"application/vnd.vectorworks":{"source":"iana"},"application/vnd.vel+json":{"source":"iana","compressible":true},"application/vnd.verimatrix.vcas":{"source":"iana"},"application/vnd.veryant.thin":{"source":"iana"},"application/vnd.ves.encrypted":{"source":"iana"},"application/vnd.vidsoft.vidconference":{"source":"iana"},"application/vnd.visio":{"source":"iana","extensions":["vsd","vst","vss","vsw"]},"application/vnd.visionary":{"source":"iana","extensions":["vis"]},"application/vnd.vividence.scriptfile":{"source":"iana"},"application/vnd.vsf":{"source":"iana","extensions":["vsf"]},"application/vnd.wap.sic":{"source":"iana"},"application/vnd.wap.slc":{"source":"iana"},"application/vnd.wap.wbxml":{"source":"iana","extensions":["wbxml"]},"application/vnd.wap.wmlc":{"source":"iana","extensions":["wmlc"]},"application/vnd.wap.wmlscriptc":{"source":"iana","extensions":["wmlsc"]},"application/vnd.webturbo":{"source":"iana","extensions":["wtb"]},"application/vnd.wfa.p2p":{"source":"iana"},"application/vnd.wfa.wsc":{"source":"iana"},"application/vnd.windows.devicepairing":{"source":"iana"},"application/vnd.wmc":{"source":"iana"},"application/vnd.wmf.bootstrap":{"source":"iana"},"application/vnd.wolfram.mathematica":{"source":"iana"},"application/vnd.wolfram.mathematica.package":{"source":"iana"},"application/vnd.wolfram.player":{"source":"iana","extensions":["nbp"]},"application/vnd.wordperfect":{"source":"iana","extensions":["wpd"]},"application/vnd.wqd":{"source":"iana","extensions":["wqd"]},"application/vnd.wrq-hp3000-labelled":{"source":"iana"},"application/vnd.wt.stf":{"source":"iana","extensions":["stf"]},"application/vnd.wv.csp+wbxml":{"source":"iana"},"application/vnd.wv.csp+xml":{"source":"iana","compressible":true},"application/vnd.wv.ssp+xml":{"source":"iana","compressible":true},"application/vnd.xacml+json":{"source":"iana","compressible":true},"application/vnd.xara":{"source":"iana","extensions":["xar"]},"application/vnd.xfdl":{"source":"iana","extensions":["xfdl"]},"application/vnd.xfdl.webform":{"source":"iana"},"application/vnd.xmi+xml":{"source":"iana","compressible":true},"application/vnd.xmpie.cpkg":{"source":"iana"},"application/vnd.xmpie.dpkg":{"source":"iana"},"application/vnd.xmpie.plan":{"source":"iana"},"application/vnd.xmpie.ppkg":{"source":"iana"},"application/vnd.xmpie.xlim":{"source":"iana"},"application/vnd.yamaha.hv-dic":{"source":"iana","extensions":["hvd"]},"application/vnd.yamaha.hv-script":{"source":"iana","extensions":["hvs"]},"application/vnd.yamaha.hv-voice":{"source":"iana","extensions":["hvp"]},"application/vnd.yamaha.openscoreformat":{"source":"iana","extensions":["osf"]},"application/vnd.yamaha.openscoreformat.osfpvg+xml":{"source":"iana","compressible":true,"extensions":["osfpvg"]},"application/vnd.yamaha.remote-setup":{"source":"iana"},"application/vnd.yamaha.smaf-audio":{"source":"iana","extensions":["saf"]},"application/vnd.yamaha.smaf-phrase":{"source":"iana","extensions":["spf"]},"application/vnd.yamaha.through-ngn":{"source":"iana"},"application/vnd.yamaha.tunnel-udpencap":{"source":"iana"},"application/vnd.yaoweme":{"source":"iana"},"application/vnd.yellowriver-custom-menu":{"source":"iana","extensions":["cmp"]},"application/vnd.youtube.yt":{"source":"iana"},"application/vnd.zul":{"source":"iana","extensions":["zir","zirz"]},"application/vnd.zzazz.deck+xml":{"source":"iana","compressible":true,"extensions":["zaz"]},"application/voicexml+xml":{"source":"iana","compressible":true,"extensions":["vxml"]},"application/voucher-cms+json":{"source":"iana","compressible":true},"application/vq-rtcpxr":{"source":"iana"},"application/wasm":{"compressible":true,"extensions":["wasm"]},"application/watcherinfo+xml":{"source":"iana","compressible":true},"application/webpush-options+json":{"source":"iana","compressible":true},"application/whoispp-query":{"source":"iana"},"application/whoispp-response":{"source":"iana"},"application/widget":{"source":"iana","extensions":["wgt"]},"application/winhlp":{"source":"apache","extensions":["hlp"]},"application/wita":{"source":"iana"},"application/wordperfect5.1":{"source":"iana"},"application/wsdl+xml":{"source":"iana","compressible":true,"extensions":["wsdl"]},"application/wspolicy+xml":{"source":"iana","compressible":true,"extensions":["wspolicy"]},"application/x-7z-compressed":{"source":"apache","compressible":false,"extensions":["7z"]},"application/x-abiword":{"source":"apache","extensions":["abw"]},"application/x-ace-compressed":{"source":"apache","extensions":["ace"]},"application/x-amf":{"source":"apache"},"application/x-apple-diskimage":{"source":"apache","extensions":["dmg"]},"application/x-arj":{"compressible":false,"extensions":["arj"]},"application/x-authorware-bin":{"source":"apache","extensions":["aab","x32","u32","vox"]},"application/x-authorware-map":{"source":"apache","extensions":["aam"]},"application/x-authorware-seg":{"source":"apache","extensions":["aas"]},"application/x-bcpio":{"source":"apache","extensions":["bcpio"]},"application/x-bdoc":{"compressible":false,"extensions":["bdoc"]},"application/x-bittorrent":{"source":"apache","extensions":["torrent"]},"application/x-blorb":{"source":"apache","extensions":["blb","blorb"]},"application/x-bzip":{"source":"apache","compressible":false,"extensions":["bz"]},"application/x-bzip2":{"source":"apache","compressible":false,"extensions":["bz2","boz"]},"application/x-cbr":{"source":"apache","extensions":["cbr","cba","cbt","cbz","cb7"]},"application/x-cdlink":{"source":"apache","extensions":["vcd"]},"application/x-cfs-compressed":{"source":"apache","extensions":["cfs"]},"application/x-chat":{"source":"apache","extensions":["chat"]},"application/x-chess-pgn":{"source":"apache","extensions":["pgn"]},"application/x-chrome-extension":{"extensions":["crx"]},"application/x-cocoa":{"source":"nginx","extensions":["cco"]},"application/x-compress":{"source":"apache"},"application/x-conference":{"source":"apache","extensions":["nsc"]},"application/x-cpio":{"source":"apache","extensions":["cpio"]},"application/x-csh":{"source":"apache","extensions":["csh"]},"application/x-deb":{"compressible":false},"application/x-debian-package":{"source":"apache","extensions":["deb","udeb"]},"application/x-dgc-compressed":{"source":"apache","extensions":["dgc"]},"application/x-director":{"source":"apache","extensions":["dir","dcr","dxr","cst","cct","cxt","w3d","fgd","swa"]},"application/x-doom":{"source":"apache","extensions":["wad"]},"application/x-dtbncx+xml":{"source":"apache","compressible":true,"extensions":["ncx"]},"application/x-dtbook+xml":{"source":"apache","compressible":true,"extensions":["dtb"]},"application/x-dtbresource+xml":{"source":"apache","compressible":true,"extensions":["res"]},"application/x-dvi":{"source":"apache","compressible":false,"extensions":["dvi"]},"application/x-envoy":{"source":"apache","extensions":["evy"]},"application/x-eva":{"source":"apache","extensions":["eva"]},"application/x-font-bdf":{"source":"apache","extensions":["bdf"]},"application/x-font-dos":{"source":"apache"},"application/x-font-framemaker":{"source":"apache"},"application/x-font-ghostscript":{"source":"apache","extensions":["gsf"]},"application/x-font-libgrx":{"source":"apache"},"application/x-font-linux-psf":{"source":"apache","extensions":["psf"]},"application/x-font-pcf":{"source":"apache","extensions":["pcf"]},"application/x-font-snf":{"source":"apache","extensions":["snf"]},"application/x-font-speedo":{"source":"apache"},"application/x-font-sunos-news":{"source":"apache"},"application/x-font-type1":{"source":"apache","extensions":["pfa","pfb","pfm","afm"]},"application/x-font-vfont":{"source":"apache"},"application/x-freearc":{"source":"apache","extensions":["arc"]},"application/x-futuresplash":{"source":"apache","extensions":["spl"]},"application/x-gca-compressed":{"source":"apache","extensions":["gca"]},"application/x-glulx":{"source":"apache","extensions":["ulx"]},"application/x-gnumeric":{"source":"apache","extensions":["gnumeric"]},"application/x-gramps-xml":{"source":"apache","extensions":["gramps"]},"application/x-gtar":{"source":"apache","extensions":["gtar"]},"application/x-gzip":{"source":"apache"},"application/x-hdf":{"source":"apache","extensions":["hdf"]},"application/x-httpd-php":{"compressible":true,"extensions":["php"]},"application/x-install-instructions":{"source":"apache","extensions":["install"]},"application/x-iso9660-image":{"source":"apache","extensions":["iso"]},"application/x-java-archive-diff":{"source":"nginx","extensions":["jardiff"]},"application/x-java-jnlp-file":{"source":"apache","compressible":false,"extensions":["jnlp"]},"application/x-javascript":{"compressible":true},"application/x-keepass2":{"extensions":["kdbx"]},"application/x-latex":{"source":"apache","compressible":false,"extensions":["latex"]},"application/x-lua-bytecode":{"extensions":["luac"]},"application/x-lzh-compressed":{"source":"apache","extensions":["lzh","lha"]},"application/x-makeself":{"source":"nginx","extensions":["run"]},"application/x-mie":{"source":"apache","extensions":["mie"]},"application/x-mobipocket-ebook":{"source":"apache","extensions":["prc","mobi"]},"application/x-mpegurl":{"compressible":false},"application/x-ms-application":{"source":"apache","extensions":["application"]},"application/x-ms-shortcut":{"source":"apache","extensions":["lnk"]},"application/x-ms-wmd":{"source":"apache","extensions":["wmd"]},"application/x-ms-wmz":{"source":"apache","extensions":["wmz"]},"application/x-ms-xbap":{"source":"apache","extensions":["xbap"]},"application/x-msaccess":{"source":"apache","extensions":["mdb"]},"application/x-msbinder":{"source":"apache","extensions":["obd"]},"application/x-mscardfile":{"source":"apache","extensions":["crd"]},"application/x-msclip":{"source":"apache","extensions":["clp"]},"application/x-msdos-program":{"extensions":["exe"]},"application/x-msdownload":{"source":"apache","extensions":["exe","dll","com","bat","msi"]},"application/x-msmediaview":{"source":"apache","extensions":["mvb","m13","m14"]},"application/x-msmetafile":{"source":"apache","extensions":["wmf","wmz","emf","emz"]},"application/x-msmoney":{"source":"apache","extensions":["mny"]},"application/x-mspublisher":{"source":"apache","extensions":["pub"]},"application/x-msschedule":{"source":"apache","extensions":["scd"]},"application/x-msterminal":{"source":"apache","extensions":["trm"]},"application/x-mswrite":{"source":"apache","extensions":["wri"]},"application/x-netcdf":{"source":"apache","extensions":["nc","cdf"]},"application/x-ns-proxy-autoconfig":{"compressible":true,"extensions":["pac"]},"application/x-nzb":{"source":"apache","extensions":["nzb"]},"application/x-perl":{"source":"nginx","extensions":["pl","pm"]},"application/x-pilot":{"source":"nginx","extensions":["prc","pdb"]},"application/x-pkcs12":{"source":"apache","compressible":false,"extensions":["p12","pfx"]},"application/x-pkcs7-certificates":{"source":"apache","extensions":["p7b","spc"]},"application/x-pkcs7-certreqresp":{"source":"apache","extensions":["p7r"]},"application/x-rar-compressed":{"source":"apache","compressible":false,"extensions":["rar"]},"application/x-redhat-package-manager":{"source":"nginx","extensions":["rpm"]},"application/x-research-info-systems":{"source":"apache","extensions":["ris"]},"application/x-sea":{"source":"nginx","extensions":["sea"]},"application/x-sh":{"source":"apache","compressible":true,"extensions":["sh"]},"application/x-shar":{"source":"apache","extensions":["shar"]},"application/x-shockwave-flash":{"source":"apache","compressible":false,"extensions":["swf"]},"application/x-silverlight-app":{"source":"apache","extensions":["xap"]},"application/x-sql":{"source":"apache","extensions":["sql"]},"application/x-stuffit":{"source":"apache","compressible":false,"extensions":["sit"]},"application/x-stuffitx":{"source":"apache","extensions":["sitx"]},"application/x-subrip":{"source":"apache","extensions":["srt"]},"application/x-sv4cpio":{"source":"apache","extensions":["sv4cpio"]},"application/x-sv4crc":{"source":"apache","extensions":["sv4crc"]},"application/x-t3vm-image":{"source":"apache","extensions":["t3"]},"application/x-tads":{"source":"apache","extensions":["gam"]},"application/x-tar":{"source":"apache","compressible":true,"extensions":["tar"]},"application/x-tcl":{"source":"apache","extensions":["tcl","tk"]},"application/x-tex":{"source":"apache","extensions":["tex"]},"application/x-tex-tfm":{"source":"apache","extensions":["tfm"]},"application/x-texinfo":{"source":"apache","extensions":["texinfo","texi"]},"application/x-tgif":{"source":"apache","extensions":["obj"]},"application/x-ustar":{"source":"apache","extensions":["ustar"]},"application/x-virtualbox-hdd":{"compressible":true,"extensions":["hdd"]},"application/x-virtualbox-ova":{"compressible":true,"extensions":["ova"]},"application/x-virtualbox-ovf":{"compressible":true,"extensions":["ovf"]},"application/x-virtualbox-vbox":{"compressible":true,"extensions":["vbox"]},"application/x-virtualbox-vbox-extpack":{"compressible":false,"extensions":["vbox-extpack"]},"application/x-virtualbox-vdi":{"compressible":true,"extensions":["vdi"]},"application/x-virtualbox-vhd":{"compressible":true,"extensions":["vhd"]},"application/x-virtualbox-vmdk":{"compressible":true,"extensions":["vmdk"]},"application/x-wais-source":{"source":"apache","extensions":["src"]},"application/x-web-app-manifest+json":{"compressible":true,"extensions":["webapp"]},"application/x-www-form-urlencoded":{"source":"iana","compressible":true},"application/x-x509-ca-cert":{"source":"apache","extensions":["der","crt","pem"]},"application/x-xfig":{"source":"apache","extensions":["fig"]},"application/x-xliff+xml":{"source":"apache","compressible":true,"extensions":["xlf"]},"application/x-xpinstall":{"source":"apache","compressible":false,"extensions":["xpi"]},"application/x-xz":{"source":"apache","extensions":["xz"]},"application/x-zmachine":{"source":"apache","extensions":["z1","z2","z3","z4","z5","z6","z7","z8"]},"application/x400-bp":{"source":"iana"},"application/xacml+xml":{"source":"iana","compressible":true},"application/xaml+xml":{"source":"apache","compressible":true,"extensions":["xaml"]},"application/xcap-att+xml":{"source":"iana","compressible":true,"extensions":["xav"]},"application/xcap-caps+xml":{"source":"iana","compressible":true,"extensions":["xca"]},"application/xcap-diff+xml":{"source":"iana","compressible":true,"extensions":["xdf"]},"application/xcap-el+xml":{"source":"iana","compressible":true,"extensions":["xel"]},"application/xcap-error+xml":{"source":"iana","compressible":true,"extensions":["xer"]},"application/xcap-ns+xml":{"source":"iana","compressible":true,"extensions":["xns"]},"application/xcon-conference-info+xml":{"source":"iana","compressible":true},"application/xcon-conference-info-diff+xml":{"source":"iana","compressible":true},"application/xenc+xml":{"source":"iana","compressible":true,"extensions":["xenc"]},"application/xhtml+xml":{"source":"iana","compressible":true,"extensions":["xhtml","xht"]},"application/xhtml-voice+xml":{"source":"apache","compressible":true},"application/xliff+xml":{"source":"iana","compressible":true,"extensions":["xlf"]},"application/xml":{"source":"iana","compressible":true,"extensions":["xml","xsl","xsd","rng"]},"application/xml-dtd":{"source":"iana","compressible":true,"extensions":["dtd"]},"application/xml-external-parsed-entity":{"source":"iana"},"application/xml-patch+xml":{"source":"iana","compressible":true},"application/xmpp+xml":{"source":"iana","compressible":true},"application/xop+xml":{"source":"iana","compressible":true,"extensions":["xop"]},"application/xproc+xml":{"source":"apache","compressible":true,"extensions":["xpl"]},"application/xslt+xml":{"source":"iana","compressible":true,"extensions":["xslt"]},"application/xspf+xml":{"source":"apache","compressible":true,"extensions":["xspf"]},"application/xv+xml":{"source":"iana","compressible":true,"extensions":["mxml","xhvml","xvml","xvm"]},"application/yang":{"source":"iana","extensions":["yang"]},"application/yang-data+json":{"source":"iana","compressible":true},"application/yang-data+xml":{"source":"iana","compressible":true},"application/yang-patch+json":{"source":"iana","compressible":true},"application/yang-patch+xml":{"source":"iana","compressible":true},"application/yin+xml":{"source":"iana","compressible":true,"extensions":["yin"]},"application/zip":{"source":"iana","compressible":false,"extensions":["zip"]},"application/zlib":{"source":"iana"},"application/zstd":{"source":"iana"},"audio/1d-interleaved-parityfec":{"source":"iana"},"audio/32kadpcm":{"source":"iana"},"audio/3gpp":{"source":"iana","compressible":false,"extensions":["3gpp"]},"audio/3gpp2":{"source":"iana"},"audio/aac":{"source":"iana"},"audio/ac3":{"source":"iana"},"audio/adpcm":{"source":"apache","extensions":["adp"]},"audio/amr":{"source":"iana"},"audio/amr-wb":{"source":"iana"},"audio/amr-wb+":{"source":"iana"},"audio/aptx":{"source":"iana"},"audio/asc":{"source":"iana"},"audio/atrac-advanced-lossless":{"source":"iana"},"audio/atrac-x":{"source":"iana"},"audio/atrac3":{"source":"iana"},"audio/basic":{"source":"iana","compressible":false,"extensions":["au","snd"]},"audio/bv16":{"source":"iana"},"audio/bv32":{"source":"iana"},"audio/clearmode":{"source":"iana"},"audio/cn":{"source":"iana"},"audio/dat12":{"source":"iana"},"audio/dls":{"source":"iana"},"audio/dsr-es201108":{"source":"iana"},"audio/dsr-es202050":{"source":"iana"},"audio/dsr-es202211":{"source":"iana"},"audio/dsr-es202212":{"source":"iana"},"audio/dv":{"source":"iana"},"audio/dvi4":{"source":"iana"},"audio/eac3":{"source":"iana"},"audio/encaprtp":{"source":"iana"},"audio/evrc":{"source":"iana"},"audio/evrc-qcp":{"source":"iana"},"audio/evrc0":{"source":"iana"},"audio/evrc1":{"source":"iana"},"audio/evrcb":{"source":"iana"},"audio/evrcb0":{"source":"iana"},"audio/evrcb1":{"source":"iana"},"audio/evrcnw":{"source":"iana"},"audio/evrcnw0":{"source":"iana"},"audio/evrcnw1":{"source":"iana"},"audio/evrcwb":{"source":"iana"},"audio/evrcwb0":{"source":"iana"},"audio/evrcwb1":{"source":"iana"},"audio/evs":{"source":"iana"},"audio/flexfec":{"source":"iana"},"audio/fwdred":{"source":"iana"},"audio/g711-0":{"source":"iana"},"audio/g719":{"source":"iana"},"audio/g722":{"source":"iana"},"audio/g7221":{"source":"iana"},"audio/g723":{"source":"iana"},"audio/g726-16":{"source":"iana"},"audio/g726-24":{"source":"iana"},"audio/g726-32":{"source":"iana"},"audio/g726-40":{"source":"iana"},"audio/g728":{"source":"iana"},"audio/g729":{"source":"iana"},"audio/g7291":{"source":"iana"},"audio/g729d":{"source":"iana"},"audio/g729e":{"source":"iana"},"audio/gsm":{"source":"iana"},"audio/gsm-efr":{"source":"iana"},"audio/gsm-hr-08":{"source":"iana"},"audio/ilbc":{"source":"iana"},"audio/ip-mr_v2.5":{"source":"iana"},"audio/isac":{"source":"apache"},"audio/l16":{"source":"iana"},"audio/l20":{"source":"iana"},"audio/l24":{"source":"iana","compressible":false},"audio/l8":{"source":"iana"},"audio/lpc":{"source":"iana"},"audio/melp":{"source":"iana"},"audio/melp1200":{"source":"iana"},"audio/melp2400":{"source":"iana"},"audio/melp600":{"source":"iana"},"audio/midi":{"source":"apache","extensions":["mid","midi","kar","rmi"]},"audio/mobile-xmf":{"source":"iana","extensions":["mxmf"]},"audio/mp3":{"compressible":false,"extensions":["mp3"]},"audio/mp4":{"source":"iana","compressible":false,"extensions":["m4a","mp4a"]},"audio/mp4a-latm":{"source":"iana"},"audio/mpa":{"source":"iana"},"audio/mpa-robust":{"source":"iana"},"audio/mpeg":{"source":"iana","compressible":false,"extensions":["mpga","mp2","mp2a","mp3","m2a","m3a"]},"audio/mpeg4-generic":{"source":"iana"},"audio/musepack":{"source":"apache"},"audio/ogg":{"source":"iana","compressible":false,"extensions":["oga","ogg","spx"]},"audio/opus":{"source":"iana"},"audio/parityfec":{"source":"iana"},"audio/pcma":{"source":"iana"},"audio/pcma-wb":{"source":"iana"},"audio/pcmu":{"source":"iana"},"audio/pcmu-wb":{"source":"iana"},"audio/prs.sid":{"source":"iana"},"audio/qcelp":{"source":"iana"},"audio/raptorfec":{"source":"iana"},"audio/red":{"source":"iana"},"audio/rtp-enc-aescm128":{"source":"iana"},"audio/rtp-midi":{"source":"iana"},"audio/rtploopback":{"source":"iana"},"audio/rtx":{"source":"iana"},"audio/s3m":{"source":"apache","extensions":["s3m"]},"audio/silk":{"source":"apache","extensions":["sil"]},"audio/smv":{"source":"iana"},"audio/smv-qcp":{"source":"iana"},"audio/smv0":{"source":"iana"},"audio/sp-midi":{"source":"iana"},"audio/speex":{"source":"iana"},"audio/t140c":{"source":"iana"},"audio/t38":{"source":"iana"},"audio/telephone-event":{"source":"iana"},"audio/tetra_acelp":{"source":"iana"},"audio/tone":{"source":"iana"},"audio/uemclip":{"source":"iana"},"audio/ulpfec":{"source":"iana"},"audio/usac":{"source":"iana"},"audio/vdvi":{"source":"iana"},"audio/vmr-wb":{"source":"iana"},"audio/vnd.3gpp.iufp":{"source":"iana"},"audio/vnd.4sb":{"source":"iana"},"audio/vnd.audiokoz":{"source":"iana"},"audio/vnd.celp":{"source":"iana"},"audio/vnd.cisco.nse":{"source":"iana"},"audio/vnd.cmles.radio-events":{"source":"iana"},"audio/vnd.cns.anp1":{"source":"iana"},"audio/vnd.cns.inf1":{"source":"iana"},"audio/vnd.dece.audio":{"source":"iana","extensions":["uva","uvva"]},"audio/vnd.digital-winds":{"source":"iana","extensions":["eol"]},"audio/vnd.dlna.adts":{"source":"iana"},"audio/vnd.dolby.heaac.1":{"source":"iana"},"audio/vnd.dolby.heaac.2":{"source":"iana"},"audio/vnd.dolby.mlp":{"source":"iana"},"audio/vnd.dolby.mps":{"source":"iana"},"audio/vnd.dolby.pl2":{"source":"iana"},"audio/vnd.dolby.pl2x":{"source":"iana"},"audio/vnd.dolby.pl2z":{"source":"iana"},"audio/vnd.dolby.pulse.1":{"source":"iana"},"audio/vnd.dra":{"source":"iana","extensions":["dra"]},"audio/vnd.dts":{"source":"iana","extensions":["dts"]},"audio/vnd.dts.hd":{"source":"iana","extensions":["dtshd"]},"audio/vnd.dts.uhd":{"source":"iana"},"audio/vnd.dvb.file":{"source":"iana"},"audio/vnd.everad.plj":{"source":"iana"},"audio/vnd.hns.audio":{"source":"iana"},"audio/vnd.lucent.voice":{"source":"iana","extensions":["lvp"]},"audio/vnd.ms-playready.media.pya":{"source":"iana","extensions":["pya"]},"audio/vnd.nokia.mobile-xmf":{"source":"iana"},"audio/vnd.nortel.vbk":{"source":"iana"},"audio/vnd.nuera.ecelp4800":{"source":"iana","extensions":["ecelp4800"]},"audio/vnd.nuera.ecelp7470":{"source":"iana","extensions":["ecelp7470"]},"audio/vnd.nuera.ecelp9600":{"source":"iana","extensions":["ecelp9600"]},"audio/vnd.octel.sbc":{"source":"iana"},"audio/vnd.presonus.multitrack":{"source":"iana"},"audio/vnd.qcelp":{"source":"iana"},"audio/vnd.rhetorex.32kadpcm":{"source":"iana"},"audio/vnd.rip":{"source":"iana","extensions":["rip"]},"audio/vnd.rn-realaudio":{"compressible":false},"audio/vnd.sealedmedia.softseal.mpeg":{"source":"iana"},"audio/vnd.vmx.cvsd":{"source":"iana"},"audio/vnd.wave":{"compressible":false},"audio/vorbis":{"source":"iana","compressible":false},"audio/vorbis-config":{"source":"iana"},"audio/wav":{"compressible":false,"extensions":["wav"]},"audio/wave":{"compressible":false,"extensions":["wav"]},"audio/webm":{"source":"apache","compressible":false,"extensions":["weba"]},"audio/x-aac":{"source":"apache","compressible":false,"extensions":["aac"]},"audio/x-aiff":{"source":"apache","extensions":["aif","aiff","aifc"]},"audio/x-caf":{"source":"apache","compressible":false,"extensions":["caf"]},"audio/x-flac":{"source":"apache","extensions":["flac"]},"audio/x-m4a":{"source":"nginx","extensions":["m4a"]},"audio/x-matroska":{"source":"apache","extensions":["mka"]},"audio/x-mpegurl":{"source":"apache","extensions":["m3u"]},"audio/x-ms-wax":{"source":"apache","extensions":["wax"]},"audio/x-ms-wma":{"source":"apache","extensions":["wma"]},"audio/x-pn-realaudio":{"source":"apache","extensions":["ram","ra"]},"audio/x-pn-realaudio-plugin":{"source":"apache","extensions":["rmp"]},"audio/x-realaudio":{"source":"nginx","extensions":["ra"]},"audio/x-tta":{"source":"apache"},"audio/x-wav":{"source":"apache","extensions":["wav"]},"audio/xm":{"source":"apache","extensions":["xm"]},"chemical/x-cdx":{"source":"apache","extensions":["cdx"]},"chemical/x-cif":{"source":"apache","extensions":["cif"]},"chemical/x-cmdf":{"source":"apache","extensions":["cmdf"]},"chemical/x-cml":{"source":"apache","extensions":["cml"]},"chemical/x-csml":{"source":"apache","extensions":["csml"]},"chemical/x-pdb":{"source":"apache"},"chemical/x-xyz":{"source":"apache","extensions":["xyz"]},"font/collection":{"source":"iana","extensions":["ttc"]},"font/otf":{"source":"iana","compressible":true,"extensions":["otf"]},"font/sfnt":{"source":"iana"},"font/ttf":{"source":"iana","compressible":true,"extensions":["ttf"]},"font/woff":{"source":"iana","extensions":["woff"]},"font/woff2":{"source":"iana","extensions":["woff2"]},"image/aces":{"source":"iana","extensions":["exr"]},"image/apng":{"compressible":false,"extensions":["apng"]},"image/avci":{"source":"iana"},"image/avcs":{"source":"iana"},"image/bmp":{"source":"iana","compressible":true,"extensions":["bmp"]},"image/cgm":{"source":"iana","extensions":["cgm"]},"image/dicom-rle":{"source":"iana","extensions":["drle"]},"image/emf":{"source":"iana","extensions":["emf"]},"image/fits":{"source":"iana","extensions":["fits"]},"image/g3fax":{"source":"iana","extensions":["g3"]},"image/gif":{"source":"iana","compressible":false,"extensions":["gif"]},"image/heic":{"source":"iana","extensions":["heic"]},"image/heic-sequence":{"source":"iana","extensions":["heics"]},"image/heif":{"source":"iana","extensions":["heif"]},"image/heif-sequence":{"source":"iana","extensions":["heifs"]},"image/hej2k":{"source":"iana","extensions":["hej2"]},"image/hsj2":{"source":"iana","extensions":["hsj2"]},"image/ief":{"source":"iana","extensions":["ief"]},"image/jls":{"source":"iana","extensions":["jls"]},"image/jp2":{"source":"iana","compressible":false,"extensions":["jp2","jpg2"]},"image/jpeg":{"source":"iana","compressible":false,"extensions":["jpeg","jpg","jpe"]},"image/jph":{"source":"iana","extensions":["jph"]},"image/jphc":{"source":"iana","extensions":["jhc"]},"image/jpm":{"source":"iana","compressible":false,"extensions":["jpm"]},"image/jpx":{"source":"iana","compressible":false,"extensions":["jpx","jpf"]},"image/jxr":{"source":"iana","extensions":["jxr"]},"image/jxra":{"source":"iana","extensions":["jxra"]},"image/jxrs":{"source":"iana","extensions":["jxrs"]},"image/jxs":{"source":"iana","extensions":["jxs"]},"image/jxsc":{"source":"iana","extensions":["jxsc"]},"image/jxsi":{"source":"iana","extensions":["jxsi"]},"image/jxss":{"source":"iana","extensions":["jxss"]},"image/ktx":{"source":"iana","extensions":["ktx"]},"image/naplps":{"source":"iana"},"image/pjpeg":{"compressible":false},"image/png":{"source":"iana","compressible":false,"extensions":["png"]},"image/prs.btif":{"source":"iana","extensions":["btif"]},"image/prs.pti":{"source":"iana","extensions":["pti"]},"image/pwg-raster":{"source":"iana"},"image/sgi":{"source":"apache","extensions":["sgi"]},"image/svg+xml":{"source":"iana","compressible":true,"extensions":["svg","svgz"]},"image/t38":{"source":"iana","extensions":["t38"]},"image/tiff":{"source":"iana","compressible":false,"extensions":["tif","tiff"]},"image/tiff-fx":{"source":"iana","extensions":["tfx"]},"image/vnd.adobe.photoshop":{"source":"iana","compressible":true,"extensions":["psd"]},"image/vnd.airzip.accelerator.azv":{"source":"iana","extensions":["azv"]},"image/vnd.cns.inf2":{"source":"iana"},"image/vnd.dece.graphic":{"source":"iana","extensions":["uvi","uvvi","uvg","uvvg"]},"image/vnd.djvu":{"source":"iana","extensions":["djvu","djv"]},"image/vnd.dvb.subtitle":{"source":"iana","extensions":["sub"]},"image/vnd.dwg":{"source":"iana","extensions":["dwg"]},"image/vnd.dxf":{"source":"iana","extensions":["dxf"]},"image/vnd.fastbidsheet":{"source":"iana","extensions":["fbs"]},"image/vnd.fpx":{"source":"iana","extensions":["fpx"]},"image/vnd.fst":{"source":"iana","extensions":["fst"]},"image/vnd.fujixerox.edmics-mmr":{"source":"iana","extensions":["mmr"]},"image/vnd.fujixerox.edmics-rlc":{"source":"iana","extensions":["rlc"]},"image/vnd.globalgraphics.pgb":{"source":"iana"},"image/vnd.microsoft.icon":{"source":"iana","extensions":["ico"]},"image/vnd.mix":{"source":"iana"},"image/vnd.mozilla.apng":{"source":"iana"},"image/vnd.ms-dds":{"extensions":["dds"]},"image/vnd.ms-modi":{"source":"iana","extensions":["mdi"]},"image/vnd.ms-photo":{"source":"apache","extensions":["wdp"]},"image/vnd.net-fpx":{"source":"iana","extensions":["npx"]},"image/vnd.radiance":{"source":"iana"},"image/vnd.sealed.png":{"source":"iana"},"image/vnd.sealedmedia.softseal.gif":{"source":"iana"},"image/vnd.sealedmedia.softseal.jpg":{"source":"iana"},"image/vnd.svf":{"source":"iana"},"image/vnd.tencent.tap":{"source":"iana","extensions":["tap"]},"image/vnd.valve.source.texture":{"source":"iana","extensions":["vtf"]},"image/vnd.wap.wbmp":{"source":"iana","extensions":["wbmp"]},"image/vnd.xiff":{"source":"iana","extensions":["xif"]},"image/vnd.zbrush.pcx":{"source":"iana","extensions":["pcx"]},"image/webp":{"source":"apache","extensions":["webp"]},"image/wmf":{"source":"iana","extensions":["wmf"]},"image/x-3ds":{"source":"apache","extensions":["3ds"]},"image/x-cmu-raster":{"source":"apache","extensions":["ras"]},"image/x-cmx":{"source":"apache","extensions":["cmx"]},"image/x-freehand":{"source":"apache","extensions":["fh","fhc","fh4","fh5","fh7"]},"image/x-icon":{"source":"apache","compressible":true,"extensions":["ico"]},"image/x-jng":{"source":"nginx","extensions":["jng"]},"image/x-mrsid-image":{"source":"apache","extensions":["sid"]},"image/x-ms-bmp":{"source":"nginx","compressible":true,"extensions":["bmp"]},"image/x-pcx":{"source":"apache","extensions":["pcx"]},"image/x-pict":{"source":"apache","extensions":["pic","pct"]},"image/x-portable-anymap":{"source":"apache","extensions":["pnm"]},"image/x-portable-bitmap":{"source":"apache","extensions":["pbm"]},"image/x-portable-graymap":{"source":"apache","extensions":["pgm"]},"image/x-portable-pixmap":{"source":"apache","extensions":["ppm"]},"image/x-rgb":{"source":"apache","extensions":["rgb"]},"image/x-tga":{"source":"apache","extensions":["tga"]},"image/x-xbitmap":{"source":"apache","extensions":["xbm"]},"image/x-xcf":{"compressible":false},"image/x-xpixmap":{"source":"apache","extensions":["xpm"]},"image/x-xwindowdump":{"source":"apache","extensions":["xwd"]},"message/cpim":{"source":"iana"},"message/delivery-status":{"source":"iana"},"message/disposition-notification":{"source":"iana","extensions":["disposition-notification"]},"message/external-body":{"source":"iana"},"message/feedback-report":{"source":"iana"},"message/global":{"source":"iana","extensions":["u8msg"]},"message/global-delivery-status":{"source":"iana","extensions":["u8dsn"]},"message/global-disposition-notification":{"source":"iana","extensions":["u8mdn"]},"message/global-headers":{"source":"iana","extensions":["u8hdr"]},"message/http":{"source":"iana","compressible":false},"message/imdn+xml":{"source":"iana","compressible":true},"message/news":{"source":"iana"},"message/partial":{"source":"iana","compressible":false},"message/rfc822":{"source":"iana","compressible":true,"extensions":["eml","mime"]},"message/s-http":{"source":"iana"},"message/sip":{"source":"iana"},"message/sipfrag":{"source":"iana"},"message/tracking-status":{"source":"iana"},"message/vnd.si.simp":{"source":"iana"},"message/vnd.wfa.wsc":{"source":"iana","extensions":["wsc"]},"model/3mf":{"source":"iana","extensions":["3mf"]},"model/gltf+json":{"source":"iana","compressible":true,"extensions":["gltf"]},"model/gltf-binary":{"source":"iana","compressible":true,"extensions":["glb"]},"model/iges":{"source":"iana","compressible":false,"extensions":["igs","iges"]},"model/mesh":{"source":"iana","compressible":false,"extensions":["msh","mesh","silo"]},"model/stl":{"source":"iana","extensions":["stl"]},"model/vnd.collada+xml":{"source":"iana","compressible":true,"extensions":["dae"]},"model/vnd.dwf":{"source":"iana","extensions":["dwf"]},"model/vnd.flatland.3dml":{"source":"iana"},"model/vnd.gdl":{"source":"iana","extensions":["gdl"]},"model/vnd.gs-gdl":{"source":"apache"},"model/vnd.gs.gdl":{"source":"iana"},"model/vnd.gtw":{"source":"iana","extensions":["gtw"]},"model/vnd.moml+xml":{"source":"iana","compressible":true},"model/vnd.mts":{"source":"iana","extensions":["mts"]},"model/vnd.opengex":{"source":"iana","extensions":["ogex"]},"model/vnd.parasolid.transmit.binary":{"source":"iana","extensions":["x_b"]},"model/vnd.parasolid.transmit.text":{"source":"iana","extensions":["x_t"]},"model/vnd.rosette.annotated-data-model":{"source":"iana"},"model/vnd.usdz+zip":{"source":"iana","compressible":false,"extensions":["usdz"]},"model/vnd.valve.source.compiled-map":{"source":"iana","extensions":["bsp"]},"model/vnd.vtu":{"source":"iana","extensions":["vtu"]},"model/vrml":{"source":"iana","compressible":false,"extensions":["wrl","vrml"]},"model/x3d+binary":{"source":"apache","compressible":false,"extensions":["x3db","x3dbz"]},"model/x3d+fastinfoset":{"source":"iana","extensions":["x3db"]},"model/x3d+vrml":{"source":"apache","compressible":false,"extensions":["x3dv","x3dvz"]},"model/x3d+xml":{"source":"iana","compressible":true,"extensions":["x3d","x3dz"]},"model/x3d-vrml":{"source":"iana","extensions":["x3dv"]},"multipart/alternative":{"source":"iana","compressible":false},"multipart/appledouble":{"source":"iana"},"multipart/byteranges":{"source":"iana"},"multipart/digest":{"source":"iana"},"multipart/encrypted":{"source":"iana","compressible":false},"multipart/form-data":{"source":"iana","compressible":false},"multipart/header-set":{"source":"iana"},"multipart/mixed":{"source":"iana"},"multipart/multilingual":{"source":"iana"},"multipart/parallel":{"source":"iana"},"multipart/related":{"source":"iana","compressible":false},"multipart/report":{"source":"iana"},"multipart/signed":{"source":"iana","compressible":false},"multipart/vnd.bint.med-plus":{"source":"iana"},"multipart/voice-message":{"source":"iana"},"multipart/x-mixed-replace":{"source":"iana"},"text/1d-interleaved-parityfec":{"source":"iana"},"text/cache-manifest":{"source":"iana","compressible":true,"extensions":["appcache","manifest"]},"text/calendar":{"source":"iana","extensions":["ics","ifb"]},"text/calender":{"compressible":true},"text/cmd":{"compressible":true},"text/coffeescript":{"extensions":["coffee","litcoffee"]},"text/css":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["css"]},"text/csv":{"source":"iana","compressible":true,"extensions":["csv"]},"text/csv-schema":{"source":"iana"},"text/directory":{"source":"iana"},"text/dns":{"source":"iana"},"text/ecmascript":{"source":"iana"},"text/encaprtp":{"source":"iana"},"text/enriched":{"source":"iana"},"text/flexfec":{"source":"iana"},"text/fwdred":{"source":"iana"},"text/grammar-ref-list":{"source":"iana"},"text/html":{"source":"iana","compressible":true,"extensions":["html","htm","shtml"]},"text/jade":{"extensions":["jade"]},"text/javascript":{"source":"iana","compressible":true},"text/jcr-cnd":{"source":"iana"},"text/jsx":{"compressible":true,"extensions":["jsx"]},"text/less":{"compressible":true,"extensions":["less"]},"text/markdown":{"source":"iana","compressible":true,"extensions":["markdown","md"]},"text/mathml":{"source":"nginx","extensions":["mml"]},"text/mdx":{"compressible":true,"extensions":["mdx"]},"text/mizar":{"source":"iana"},"text/n3":{"source":"iana","compressible":true,"extensions":["n3"]},"text/parameters":{"source":"iana"},"text/parityfec":{"source":"iana"},"text/plain":{"source":"iana","compressible":true,"extensions":["txt","text","conf","def","list","log","in","ini"]},"text/provenance-notation":{"source":"iana"},"text/prs.fallenstein.rst":{"source":"iana"},"text/prs.lines.tag":{"source":"iana","extensions":["dsc"]},"text/prs.prop.logic":{"source":"iana"},"text/raptorfec":{"source":"iana"},"text/red":{"source":"iana"},"text/rfc822-headers":{"source":"iana"},"text/richtext":{"source":"iana","compressible":true,"extensions":["rtx"]},"text/rtf":{"source":"iana","compressible":true,"extensions":["rtf"]},"text/rtp-enc-aescm128":{"source":"iana"},"text/rtploopback":{"source":"iana"},"text/rtx":{"source":"iana"},"text/sgml":{"source":"iana","extensions":["sgml","sgm"]},"text/shex":{"extensions":["shex"]},"text/slim":{"extensions":["slim","slm"]},"text/strings":{"source":"iana"},"text/stylus":{"extensions":["stylus","styl"]},"text/t140":{"source":"iana"},"text/tab-separated-values":{"source":"iana","compressible":true,"extensions":["tsv"]},"text/troff":{"source":"iana","extensions":["t","tr","roff","man","me","ms"]},"text/turtle":{"source":"iana","charset":"UTF-8","extensions":["ttl"]},"text/ulpfec":{"source":"iana"},"text/uri-list":{"source":"iana","compressible":true,"extensions":["uri","uris","urls"]},"text/vcard":{"source":"iana","compressible":true,"extensions":["vcard"]},"text/vnd.a":{"source":"iana"},"text/vnd.abc":{"source":"iana"},"text/vnd.ascii-art":{"source":"iana"},"text/vnd.curl":{"source":"iana","extensions":["curl"]},"text/vnd.curl.dcurl":{"source":"apache","extensions":["dcurl"]},"text/vnd.curl.mcurl":{"source":"apache","extensions":["mcurl"]},"text/vnd.curl.scurl":{"source":"apache","extensions":["scurl"]},"text/vnd.debian.copyright":{"source":"iana"},"text/vnd.dmclientscript":{"source":"iana"},"text/vnd.dvb.subtitle":{"source":"iana","extensions":["sub"]},"text/vnd.esmertec.theme-descriptor":{"source":"iana"},"text/vnd.ficlab.flt":{"source":"iana"},"text/vnd.fly":{"source":"iana","extensions":["fly"]},"text/vnd.fmi.flexstor":{"source":"iana","extensions":["flx"]},"text/vnd.gml":{"source":"iana"},"text/vnd.graphviz":{"source":"iana","extensions":["gv"]},"text/vnd.hgl":{"source":"iana"},"text/vnd.in3d.3dml":{"source":"iana","extensions":["3dml"]},"text/vnd.in3d.spot":{"source":"iana","extensions":["spot"]},"text/vnd.iptc.newsml":{"source":"iana"},"text/vnd.iptc.nitf":{"source":"iana"},"text/vnd.latex-z":{"source":"iana"},"text/vnd.motorola.reflex":{"source":"iana"},"text/vnd.ms-mediapackage":{"source":"iana"},"text/vnd.net2phone.commcenter.command":{"source":"iana"},"text/vnd.radisys.msml-basic-layout":{"source":"iana"},"text/vnd.senx.warpscript":{"source":"iana"},"text/vnd.si.uricatalogue":{"source":"iana"},"text/vnd.sosi":{"source":"iana"},"text/vnd.sun.j2me.app-descriptor":{"source":"iana","extensions":["jad"]},"text/vnd.trolltech.linguist":{"source":"iana"},"text/vnd.wap.si":{"source":"iana"},"text/vnd.wap.sl":{"source":"iana"},"text/vnd.wap.wml":{"source":"iana","extensions":["wml"]},"text/vnd.wap.wmlscript":{"source":"iana","extensions":["wmls"]},"text/vtt":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["vtt"]},"text/x-asm":{"source":"apache","extensions":["s","asm"]},"text/x-c":{"source":"apache","extensions":["c","cc","cxx","cpp","h","hh","dic"]},"text/x-component":{"source":"nginx","extensions":["htc"]},"text/x-fortran":{"source":"apache","extensions":["f","for","f77","f90"]},"text/x-gwt-rpc":{"compressible":true},"text/x-handlebars-template":{"extensions":["hbs"]},"text/x-java-source":{"source":"apache","extensions":["java"]},"text/x-jquery-tmpl":{"compressible":true},"text/x-lua":{"extensions":["lua"]},"text/x-markdown":{"compressible":true,"extensions":["mkd"]},"text/x-nfo":{"source":"apache","extensions":["nfo"]},"text/x-opml":{"source":"apache","extensions":["opml"]},"text/x-org":{"compressible":true,"extensions":["org"]},"text/x-pascal":{"source":"apache","extensions":["p","pas"]},"text/x-processing":{"compressible":true,"extensions":["pde"]},"text/x-sass":{"extensions":["sass"]},"text/x-scss":{"extensions":["scss"]},"text/x-setext":{"source":"apache","extensions":["etx"]},"text/x-sfv":{"source":"apache","extensions":["sfv"]},"text/x-suse-ymp":{"compressible":true,"extensions":["ymp"]},"text/x-uuencode":{"source":"apache","extensions":["uu"]},"text/x-vcalendar":{"source":"apache","extensions":["vcs"]},"text/x-vcard":{"source":"apache","extensions":["vcf"]},"text/xml":{"source":"iana","compressible":true,"extensions":["xml"]},"text/xml-external-parsed-entity":{"source":"iana"},"text/yaml":{"extensions":["yaml","yml"]},"video/1d-interleaved-parityfec":{"source":"iana"},"video/3gpp":{"source":"iana","extensions":["3gp","3gpp"]},"video/3gpp-tt":{"source":"iana"},"video/3gpp2":{"source":"iana","extensions":["3g2"]},"video/bmpeg":{"source":"iana"},"video/bt656":{"source":"iana"},"video/celb":{"source":"iana"},"video/dv":{"source":"iana"},"video/encaprtp":{"source":"iana"},"video/flexfec":{"source":"iana"},"video/h261":{"source":"iana","extensions":["h261"]},"video/h263":{"source":"iana","extensions":["h263"]},"video/h263-1998":{"source":"iana"},"video/h263-2000":{"source":"iana"},"video/h264":{"source":"iana","extensions":["h264"]},"video/h264-rcdo":{"source":"iana"},"video/h264-svc":{"source":"iana"},"video/h265":{"source":"iana"},"video/iso.segment":{"source":"iana"},"video/jpeg":{"source":"iana","extensions":["jpgv"]},"video/jpeg2000":{"source":"iana"},"video/jpm":{"source":"apache","extensions":["jpm","jpgm"]},"video/mj2":{"source":"iana","extensions":["mj2","mjp2"]},"video/mp1s":{"source":"iana"},"video/mp2p":{"source":"iana"},"video/mp2t":{"source":"iana","extensions":["ts"]},"video/mp4":{"source":"iana","compressible":false,"extensions":["mp4","mp4v","mpg4"]},"video/mp4v-es":{"source":"iana"},"video/mpeg":{"source":"iana","compressible":false,"extensions":["mpeg","mpg","mpe","m1v","m2v"]},"video/mpeg4-generic":{"source":"iana"},"video/mpv":{"source":"iana"},"video/nv":{"source":"iana"},"video/ogg":{"source":"iana","compressible":false,"extensions":["ogv"]},"video/parityfec":{"source":"iana"},"video/pointer":{"source":"iana"},"video/quicktime":{"source":"iana","compressible":false,"extensions":["qt","mov"]},"video/raptorfec":{"source":"iana"},"video/raw":{"source":"iana"},"video/rtp-enc-aescm128":{"source":"iana"},"video/rtploopback":{"source":"iana"},"video/rtx":{"source":"iana"},"video/smpte291":{"source":"iana"},"video/smpte292m":{"source":"iana"},"video/ulpfec":{"source":"iana"},"video/vc1":{"source":"iana"},"video/vc2":{"source":"iana"},"video/vnd.cctv":{"source":"iana"},"video/vnd.dece.hd":{"source":"iana","extensions":["uvh","uvvh"]},"video/vnd.dece.mobile":{"source":"iana","extensions":["uvm","uvvm"]},"video/vnd.dece.mp4":{"source":"iana"},"video/vnd.dece.pd":{"source":"iana","extensions":["uvp","uvvp"]},"video/vnd.dece.sd":{"source":"iana","extensions":["uvs","uvvs"]},"video/vnd.dece.video":{"source":"iana","extensions":["uvv","uvvv"]},"video/vnd.directv.mpeg":{"source":"iana"},"video/vnd.directv.mpeg-tts":{"source":"iana"},"video/vnd.dlna.mpeg-tts":{"source":"iana"},"video/vnd.dvb.file":{"source":"iana","extensions":["dvb"]},"video/vnd.fvt":{"source":"iana","extensions":["fvt"]},"video/vnd.hns.video":{"source":"iana"},"video/vnd.iptvforum.1dparityfec-1010":{"source":"iana"},"video/vnd.iptvforum.1dparityfec-2005":{"source":"iana"},"video/vnd.iptvforum.2dparityfec-1010":{"source":"iana"},"video/vnd.iptvforum.2dparityfec-2005":{"source":"iana"},"video/vnd.iptvforum.ttsavc":{"source":"iana"},"video/vnd.iptvforum.ttsmpeg2":{"source":"iana"},"video/vnd.motorola.video":{"source":"iana"},"video/vnd.motorola.videop":{"source":"iana"},"video/vnd.mpegurl":{"source":"iana","extensions":["mxu","m4u"]},"video/vnd.ms-playready.media.pyv":{"source":"iana","extensions":["pyv"]},"video/vnd.nokia.interleaved-multimedia":{"source":"iana"},"video/vnd.nokia.mp4vr":{"source":"iana"},"video/vnd.nokia.videovoip":{"source":"iana"},"video/vnd.objectvideo":{"source":"iana"},"video/vnd.radgamettools.bink":{"source":"iana"},"video/vnd.radgamettools.smacker":{"source":"iana"},"video/vnd.sealed.mpeg1":{"source":"iana"},"video/vnd.sealed.mpeg4":{"source":"iana"},"video/vnd.sealed.swf":{"source":"iana"},"video/vnd.sealedmedia.softseal.mov":{"source":"iana"},"video/vnd.uvvu.mp4":{"source":"iana","extensions":["uvu","uvvu"]},"video/vnd.vivo":{"source":"iana","extensions":["viv"]},"video/vnd.youtube.yt":{"source":"iana"},"video/vp8":{"source":"iana"},"video/webm":{"source":"apache","compressible":false,"extensions":["webm"]},"video/x-f4v":{"source":"apache","extensions":["f4v"]},"video/x-fli":{"source":"apache","extensions":["fli"]},"video/x-flv":{"source":"apache","compressible":false,"extensions":["flv"]},"video/x-m4v":{"source":"apache","extensions":["m4v"]},"video/x-matroska":{"source":"apache","compressible":false,"extensions":["mkv","mk3d","mks"]},"video/x-mng":{"source":"apache","extensions":["mng"]},"video/x-ms-asf":{"source":"apache","extensions":["asf","asx"]},"video/x-ms-vob":{"source":"apache","extensions":["vob"]},"video/x-ms-wm":{"source":"apache","extensions":["wm"]},"video/x-ms-wmv":{"source":"apache","compressible":false,"extensions":["wmv"]},"video/x-ms-wmx":{"source":"apache","extensions":["wmx"]},"video/x-ms-wvx":{"source":"apache","extensions":["wvx"]},"video/x-msvideo":{"source":"apache","extensions":["avi"]},"video/x-sgi-movie":{"source":"apache","extensions":["movie"]},"video/x-smv":{"source":"apache","extensions":["smv"]},"x-conference/x-cooltalk":{"source":"apache","extensions":["ice"]},"x-shader/x-fragment":{"compressible":true},"x-shader/x-vertex":{"compressible":true}}; + +/***/ }), + +/***/ 514: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +var compileSchema = __webpack_require__(805) + , resolve = __webpack_require__(867) + , Cache = __webpack_require__(921) + , SchemaObject = __webpack_require__(955) + , stableStringify = __webpack_require__(741) + , formats = __webpack_require__(881) + , rules = __webpack_require__(496) + , $dataMetaSchema = __webpack_require__(628) + , util = __webpack_require__(855); + +module.exports = Ajv; + +Ajv.prototype.validate = validate; +Ajv.prototype.compile = compile; +Ajv.prototype.addSchema = addSchema; +Ajv.prototype.addMetaSchema = addMetaSchema; +Ajv.prototype.validateSchema = validateSchema; +Ajv.prototype.getSchema = getSchema; +Ajv.prototype.removeSchema = removeSchema; +Ajv.prototype.addFormat = addFormat; +Ajv.prototype.errorsText = errorsText; + +Ajv.prototype._addSchema = _addSchema; +Ajv.prototype._compile = _compile; + +Ajv.prototype.compileAsync = __webpack_require__(890); +var customKeyword = __webpack_require__(45); +Ajv.prototype.addKeyword = customKeyword.add; +Ajv.prototype.getKeyword = customKeyword.get; +Ajv.prototype.removeKeyword = customKeyword.remove; +Ajv.prototype.validateKeyword = customKeyword.validate; + +var errorClasses = __webpack_require__(844); +Ajv.ValidationError = errorClasses.Validation; +Ajv.MissingRefError = errorClasses.MissingRef; +Ajv.$dataMetaSchema = $dataMetaSchema; + +var META_SCHEMA_ID = 'http://json-schema.org/draft-07/schema'; + +var META_IGNORE_OPTIONS = [ 'removeAdditional', 'useDefaults', 'coerceTypes', 'strictDefaults' ]; +var META_SUPPORT_DATA = ['/properties']; + +/** + * Creates validator instance. + * Usage: `Ajv(opts)` + * @param {Object} opts optional options + * @return {Object} ajv instance + */ +function Ajv(opts) { + if (!(this instanceof Ajv)) return new Ajv(opts); + opts = this._opts = util.copy(opts) || {}; + setLogger(this); + this._schemas = {}; + this._refs = {}; + this._fragments = {}; + this._formats = formats(opts.format); + + this._cache = opts.cache || new Cache; + this._loadingSchemas = {}; + this._compilations = []; + this.RULES = rules(); + this._getId = chooseGetId(opts); + + opts.loopRequired = opts.loopRequired || Infinity; + if (opts.errorDataPath == 'property') opts._errorDataPathProperty = true; + if (opts.serialize === undefined) opts.serialize = stableStringify; + this._metaOpts = getMetaSchemaOptions(this); + + if (opts.formats) addInitialFormats(this); + addDefaultMetaSchema(this); + if (typeof opts.meta == 'object') this.addMetaSchema(opts.meta); + if (opts.nullable) this.addKeyword('nullable', {metaSchema: {type: 'boolean'}}); + addInitialSchemas(this); +} + + + +/** + * Validate data using schema + * Schema will be compiled and cached (using serialized JSON as key. [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used to serialize. + * @this Ajv + * @param {String|Object} schemaKeyRef key, ref or schema object + * @param {Any} data to be validated + * @return {Boolean} validation result. Errors from the last validation will be available in `ajv.errors` (and also in compiled schema: `schema.errors`). + */ +function validate(schemaKeyRef, data) { + var v; + if (typeof schemaKeyRef == 'string') { + v = this.getSchema(schemaKeyRef); + if (!v) throw new Error('no schema with key or ref "' + schemaKeyRef + '"'); + } else { + var schemaObj = this._addSchema(schemaKeyRef); + v = schemaObj.validate || this._compile(schemaObj); + } + + var valid = v(data); + if (v.$async !== true) this.errors = v.errors; + return valid; +} + + +/** + * Create validating function for passed schema. + * @this Ajv + * @param {Object} schema schema object + * @param {Boolean} _meta true if schema is a meta-schema. Used internally to compile meta schemas of custom keywords. + * @return {Function} validating function + */ +function compile(schema, _meta) { + var schemaObj = this._addSchema(schema, undefined, _meta); + return schemaObj.validate || this._compile(schemaObj); +} + + +/** + * Adds schema to the instance. + * @this Ajv + * @param {Object|Array} schema schema or array of schemas. If array is passed, `key` and other parameters will be ignored. + * @param {String} key Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`. + * @param {Boolean} _skipValidation true to skip schema validation. Used internally, option validateSchema should be used instead. + * @param {Boolean} _meta true if schema is a meta-schema. Used internally, addMetaSchema should be used instead. + * @return {Ajv} this for method chaining + */ +function addSchema(schema, key, _skipValidation, _meta) { + if (Array.isArray(schema)){ + for (var i=0; i} errors optional array of validation errors, if not passed errors from the instance are used. + * @param {Object} options optional options with properties `separator` and `dataVar`. + * @return {String} human readable string with all errors descriptions + */ +function errorsText(errors, options) { + errors = errors || this.errors; + if (!errors) return 'No errors'; + options = options || {}; + var separator = options.separator === undefined ? ', ' : options.separator; + var dataVar = options.dataVar === undefined ? 'data' : options.dataVar; + + var text = ''; + for (var i=0; i= 1, + 'key must have at least one part'); + assert.ok(partial || sshbuf.atEnd(), + 'leftover bytes at end of key'); + + var Constructor = Key; + var algInfo = algs.info[key.type]; + if (type === 'private' || algInfo.parts.length !== parts.length) { + algInfo = algs.privInfo[key.type]; + Constructor = PrivateKey; + } + assert.strictEqual(algInfo.parts.length, parts.length); + + if (key.type === 'ecdsa') { + var res = /^ecdsa-sha2-(.+)$/.exec(alg); + assert.ok(res !== null); + assert.strictEqual(res[1], parts[0].data.toString()); + } + + var normalized = true; + for (var i = 0; i < algInfo.parts.length; ++i) { + var p = parts[i]; + p.name = algInfo.parts[i]; + /* + * OpenSSH stores ed25519 "private" keys as seed + public key + * concat'd together (k followed by A). We want to keep them + * separate for other formats that don't do this. + */ + if (key.type === 'ed25519' && p.name === 'k') + p.data = p.data.slice(0, 32); + + if (p.name !== 'curve' && algInfo.normalize !== false) { + var nd; + if (key.type === 'ed25519') { + nd = utils.zeroPadToLength(p.data, 32); + } else { + nd = utils.mpNormalize(p.data); + } + if (nd.toString('binary') !== + p.data.toString('binary')) { + p.data = nd; + normalized = false; + } + } + } + + if (normalized) + key._rfc4253Cache = sshbuf.toBuffer(); + + if (partial && typeof (partial) === 'object') { + partial.remainder = sshbuf.remainder(); + partial.consumed = sshbuf._offset; + } + + return (new Constructor(key)); +} + +function write(key, options) { + assert.object(key); + + var alg = keyTypeToAlg(key); + var i; + + var algInfo = algs.info[key.type]; + if (PrivateKey.isPrivateKey(key)) + algInfo = algs.privInfo[key.type]; + var parts = algInfo.parts; + + var buf = new SSHBuffer({}); + + buf.writeString(alg); + + for (i = 0; i < parts.length; ++i) { + var data = key.part[parts[i]].data; + if (algInfo.normalize !== false) { + if (key.type === 'ed25519') + data = utils.zeroPadToLength(data, 32); + else + data = utils.mpNormalize(data); + } + if (key.type === 'ed25519' && parts[i] === 'k') + data = Buffer.concat([data, key.part.A.data]); + buf.writeBuffer(data); + } + + return (buf.toBuffer()); +} + + +/***/ }), + +/***/ 540: +/***/ (function(module) { + +module.exports = {"_from":"twitter","_id":"twitter@1.7.1","_inBundle":false,"_integrity":"sha1-B2I3jx3BwFDkj2ZqypBOJLGpYvQ=","_location":"/twitter","_phantomChildren":{},"_requested":{"type":"tag","registry":true,"raw":"twitter","name":"twitter","escapedName":"twitter","rawSpec":"","saveSpec":null,"fetchSpec":"latest"},"_requiredBy":["#USER","/"],"_resolved":"https://registry.npmjs.org/twitter/-/twitter-1.7.1.tgz","_shasum":"0762378f1dc1c050e48f666aca904e24b1a962f4","_spec":"twitter","_where":"/usr/local/google/home/hkj/Projects/firebase-admin-node/public/.github/actions/send-tweet","author":{"name":"Desmond Morris","email":"hi@desmondmorris.com"},"bugs":{"url":"https://github.com/desmondmorris/node-twitter/issues"},"bundleDependencies":false,"dependencies":{"deep-extend":"^0.5.0","request":"^2.72.0"},"deprecated":false,"description":"Twitter API client library for node.js","devDependencies":{"eslint":"^3.12.0","mocha":"^3.2.0","nock":"^9.0.2"},"homepage":"https://github.com/desmondmorris/node-twitter","keywords":["twitter","streaming","oauth"],"license":"MIT","main":"./lib/twitter","name":"twitter","repository":{"type":"git","url":"git+https://github.com/desmondmorris/node-twitter.git"},"scripts":{"lint":"eslint test/*.js lib/*.js","test":"npm run lint && mocha"},"version":"1.7.1"}; + +/***/ }), + +/***/ 542: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_pattern(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + var $regexp = $isData ? '(new RegExp(' + $schemaValue + '))' : it.usePattern($schema); + out += 'if ( '; + if ($isData) { + out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'string\') || '; + } + out += ' !' + ($regexp) + '.test(' + ($data) + ') ) { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('pattern') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { pattern: '; + if ($isData) { + out += '' + ($schemaValue); + } else { + out += '' + (it.util.toQuotedString($schema)); + } + out += ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should match pattern "'; + if ($isData) { + out += '\' + ' + ($schemaValue) + ' + \''; + } else { + out += '' + (it.util.escapeQuotes($schema)); + } + out += '"\' '; + } + if (it.opts.verbose) { + out += ' , schema: '; + if ($isData) { + out += 'validate.schema' + ($schemaPath); + } else { + out += '' + (it.util.toQuotedString($schema)); + } + out += ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += '} '; + if ($breakOnError) { + out += ' else { '; + } + return out; +} + + +/***/ }), + +/***/ 547: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var util = __webpack_require__(669); +var Stream = __webpack_require__(413).Stream; +var DelayedStream = __webpack_require__(152); + +module.exports = CombinedStream; +function CombinedStream() { + this.writable = false; + this.readable = true; + this.dataSize = 0; + this.maxDataSize = 2 * 1024 * 1024; + this.pauseStreams = true; + + this._released = false; + this._streams = []; + this._currentStream = null; + this._insideLoop = false; + this._pendingNext = false; +} +util.inherits(CombinedStream, Stream); + +CombinedStream.create = function(options) { + var combinedStream = new this(); + + options = options || {}; + for (var option in options) { + combinedStream[option] = options[option]; + } + + return combinedStream; +}; + +CombinedStream.isStreamLike = function(stream) { + return (typeof stream !== 'function') + && (typeof stream !== 'string') + && (typeof stream !== 'boolean') + && (typeof stream !== 'number') + && (!Buffer.isBuffer(stream)); +}; + +CombinedStream.prototype.append = function(stream) { + var isStreamLike = CombinedStream.isStreamLike(stream); + + if (isStreamLike) { + if (!(stream instanceof DelayedStream)) { + var newStream = DelayedStream.create(stream, { + maxDataSize: Infinity, + pauseStream: this.pauseStreams, + }); + stream.on('data', this._checkDataSize.bind(this)); + stream = newStream; + } + + this._handleErrors(stream); + + if (this.pauseStreams) { + stream.pause(); + } + } + + this._streams.push(stream); + return this; +}; + +CombinedStream.prototype.pipe = function(dest, options) { + Stream.prototype.pipe.call(this, dest, options); + this.resume(); + return dest; +}; + +CombinedStream.prototype._getNext = function() { + this._currentStream = null; + + if (this._insideLoop) { + this._pendingNext = true; + return; // defer call + } + + this._insideLoop = true; + try { + do { + this._pendingNext = false; + this._realGetNext(); + } while (this._pendingNext); + } finally { + this._insideLoop = false; + } +}; + +CombinedStream.prototype._realGetNext = function() { + var stream = this._streams.shift(); + + + if (typeof stream == 'undefined') { + this.end(); + return; + } + + if (typeof stream !== 'function') { + this._pipeNext(stream); + return; + } + + var getStream = stream; + getStream(function(stream) { + var isStreamLike = CombinedStream.isStreamLike(stream); + if (isStreamLike) { + stream.on('data', this._checkDataSize.bind(this)); + this._handleErrors(stream); + } + + this._pipeNext(stream); + }.bind(this)); +}; + +CombinedStream.prototype._pipeNext = function(stream) { + this._currentStream = stream; + + var isStreamLike = CombinedStream.isStreamLike(stream); + if (isStreamLike) { + stream.on('end', this._getNext.bind(this)); + stream.pipe(this, {end: false}); + return; + } + + var value = stream; + this.write(value); + this._getNext(); +}; + +CombinedStream.prototype._handleErrors = function(stream) { + var self = this; + stream.on('error', function(err) { + self._emitError(err); + }); +}; + +CombinedStream.prototype.write = function(data) { + this.emit('data', data); +}; + +CombinedStream.prototype.pause = function() { + if (!this.pauseStreams) { + return; + } + + if(this.pauseStreams && this._currentStream && typeof(this._currentStream.pause) == 'function') this._currentStream.pause(); + this.emit('pause'); +}; + +CombinedStream.prototype.resume = function() { + if (!this._released) { + this._released = true; + this.writable = true; + this._getNext(); + } + + if(this.pauseStreams && this._currentStream && typeof(this._currentStream.resume) == 'function') this._currentStream.resume(); + this.emit('resume'); +}; + +CombinedStream.prototype.end = function() { + this._reset(); + this.emit('end'); +}; + +CombinedStream.prototype.destroy = function() { + this._reset(); + this.emit('close'); +}; + +CombinedStream.prototype._reset = function() { + this.writable = false; + this._streams = []; + this._currentStream = null; +}; + +CombinedStream.prototype._checkDataSize = function() { + this._updateDataSize(); + if (this.dataSize <= this.maxDataSize) { + return; + } + + var message = + 'DelayedStream#maxDataSize of ' + this.maxDataSize + ' bytes exceeded.'; + this._emitError(new Error(message)); +}; + +CombinedStream.prototype._updateDataSize = function() { + this.dataSize = 0; + + var self = this; + this._streams.forEach(function(stream) { + if (!stream.dataSize) { + return; + } + + self.dataSize += stream.dataSize; + }); + + if (this._currentStream && this._currentStream.dataSize) { + this.dataSize += this._currentStream.dataSize; + } +}; + +CombinedStream.prototype._emitError = function(err) { + this._reset(); + this.emit('error', err); +}; + + +/***/ }), + +/***/ 552: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + + +var url = __webpack_require__(835) +var isUrl = /^https?:/ + +function Redirect (request) { + this.request = request + this.followRedirect = true + this.followRedirects = true + this.followAllRedirects = false + this.followOriginalHttpMethod = false + this.allowRedirect = function () { return true } + this.maxRedirects = 10 + this.redirects = [] + this.redirectsFollowed = 0 + this.removeRefererHeader = false +} + +Redirect.prototype.onRequest = function (options) { + var self = this + + if (options.maxRedirects !== undefined) { + self.maxRedirects = options.maxRedirects + } + if (typeof options.followRedirect === 'function') { + self.allowRedirect = options.followRedirect + } + if (options.followRedirect !== undefined) { + self.followRedirects = !!options.followRedirect + } + if (options.followAllRedirects !== undefined) { + self.followAllRedirects = options.followAllRedirects + } + if (self.followRedirects || self.followAllRedirects) { + self.redirects = self.redirects || [] + } + if (options.removeRefererHeader !== undefined) { + self.removeRefererHeader = options.removeRefererHeader + } + if (options.followOriginalHttpMethod !== undefined) { + self.followOriginalHttpMethod = options.followOriginalHttpMethod + } +} + +Redirect.prototype.redirectTo = function (response) { + var self = this + var request = self.request + + var redirectTo = null + if (response.statusCode >= 300 && response.statusCode < 400 && response.caseless.has('location')) { + var location = response.caseless.get('location') + request.debug('redirect', location) + + if (self.followAllRedirects) { + redirectTo = location + } else if (self.followRedirects) { + switch (request.method) { + case 'PATCH': + case 'PUT': + case 'POST': + case 'DELETE': + // Do not follow redirects + break + default: + redirectTo = location + break + } + } + } else if (response.statusCode === 401) { + var authHeader = request._auth.onResponse(response) + if (authHeader) { + request.setHeader('authorization', authHeader) + redirectTo = request.uri + } + } + return redirectTo +} + +Redirect.prototype.onResponse = function (response) { + var self = this + var request = self.request + + var redirectTo = self.redirectTo(response) + if (!redirectTo || !self.allowRedirect.call(request, response)) { + return false + } + + request.debug('redirect to', redirectTo) + + // ignore any potential response body. it cannot possibly be useful + // to us at this point. + // response.resume should be defined, but check anyway before calling. Workaround for browserify. + if (response.resume) { + response.resume() + } + + if (self.redirectsFollowed >= self.maxRedirects) { + request.emit('error', new Error('Exceeded maxRedirects. Probably stuck in a redirect loop ' + request.uri.href)) + return false + } + self.redirectsFollowed += 1 + + if (!isUrl.test(redirectTo)) { + redirectTo = url.resolve(request.uri.href, redirectTo) + } + + var uriPrev = request.uri + request.uri = url.parse(redirectTo) + + // handle the case where we change protocol from https to http or vice versa + if (request.uri.protocol !== uriPrev.protocol) { + delete request.agent + } + + self.redirects.push({ statusCode: response.statusCode, redirectUri: redirectTo }) + + if (self.followAllRedirects && request.method !== 'HEAD' && + response.statusCode !== 401 && response.statusCode !== 307) { + request.method = self.followOriginalHttpMethod ? request.method : 'GET' + } + // request.method = 'GET' // Force all redirects to use GET || commented out fixes #215 + delete request.src + delete request.req + delete request._started + if (response.statusCode !== 401 && response.statusCode !== 307) { + // Remove parameters from the previous response, unless this is the second request + // for a server that requires digest authentication. + delete request.body + delete request._form + if (request.headers) { + request.removeHeader('host') + request.removeHeader('content-type') + request.removeHeader('content-length') + if (request.uri.hostname !== request.originalHost.split(':')[0]) { + // Remove authorization if changing hostnames (but not if just + // changing ports or protocols). This matches the behavior of curl: + // https://github.com/bagder/curl/blob/6beb0eee/lib/http.c#L710 + request.removeHeader('authorization') + } + } + } + + if (!self.removeRefererHeader) { + request.setHeader('referer', uriPrev.href) + } + + request.emit('redirect') + + request.init() + + return true +} + +exports.Redirect = Redirect + + +/***/ }), + +/***/ 554: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + + +var caseless = __webpack_require__(254) +var uuid = __webpack_require__(826) +var helpers = __webpack_require__(810) + +var md5 = helpers.md5 +var toBase64 = helpers.toBase64 + +function Auth (request) { + // define all public properties here + this.request = request + this.hasAuth = false + this.sentAuth = false + this.bearerToken = null + this.user = null + this.pass = null +} + +Auth.prototype.basic = function (user, pass, sendImmediately) { + var self = this + if (typeof user !== 'string' || (pass !== undefined && typeof pass !== 'string')) { + self.request.emit('error', new Error('auth() received invalid user or password')) + } + self.user = user + self.pass = pass + self.hasAuth = true + var header = user + ':' + (pass || '') + if (sendImmediately || typeof sendImmediately === 'undefined') { + var authHeader = 'Basic ' + toBase64(header) + self.sentAuth = true + return authHeader + } +} + +Auth.prototype.bearer = function (bearer, sendImmediately) { + var self = this + self.bearerToken = bearer + self.hasAuth = true + if (sendImmediately || typeof sendImmediately === 'undefined') { + if (typeof bearer === 'function') { + bearer = bearer() + } + var authHeader = 'Bearer ' + (bearer || '') + self.sentAuth = true + return authHeader + } +} + +Auth.prototype.digest = function (method, path, authHeader) { + // TODO: More complete implementation of RFC 2617. + // - handle challenge.domain + // - support qop="auth-int" only + // - handle Authentication-Info (not necessarily?) + // - check challenge.stale (not necessarily?) + // - increase nc (not necessarily?) + // For reference: + // http://tools.ietf.org/html/rfc2617#section-3 + // https://github.com/bagder/curl/blob/master/lib/http_digest.c + + var self = this + + var challenge = {} + var re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi + while (true) { + var match = re.exec(authHeader) + if (!match) { + break + } + challenge[match[1]] = match[2] || match[3] + } + + /** + * RFC 2617: handle both MD5 and MD5-sess algorithms. + * + * If the algorithm directive's value is "MD5" or unspecified, then HA1 is + * HA1=MD5(username:realm:password) + * If the algorithm directive's value is "MD5-sess", then HA1 is + * HA1=MD5(MD5(username:realm:password):nonce:cnonce) + */ + var ha1Compute = function (algorithm, user, realm, pass, nonce, cnonce) { + var ha1 = md5(user + ':' + realm + ':' + pass) + if (algorithm && algorithm.toLowerCase() === 'md5-sess') { + return md5(ha1 + ':' + nonce + ':' + cnonce) + } else { + return ha1 + } + } + + var qop = /(^|,)\s*auth\s*($|,)/.test(challenge.qop) && 'auth' + var nc = qop && '00000001' + var cnonce = qop && uuid().replace(/-/g, '') + var ha1 = ha1Compute(challenge.algorithm, self.user, challenge.realm, self.pass, challenge.nonce, cnonce) + var ha2 = md5(method + ':' + path) + var digestResponse = qop + ? md5(ha1 + ':' + challenge.nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2) + : md5(ha1 + ':' + challenge.nonce + ':' + ha2) + var authValues = { + username: self.user, + realm: challenge.realm, + nonce: challenge.nonce, + uri: path, + qop: qop, + response: digestResponse, + nc: nc, + cnonce: cnonce, + algorithm: challenge.algorithm, + opaque: challenge.opaque + } + + authHeader = [] + for (var k in authValues) { + if (authValues[k]) { + if (k === 'qop' || k === 'nc' || k === 'algorithm') { + authHeader.push(k + '=' + authValues[k]) + } else { + authHeader.push(k + '="' + authValues[k] + '"') + } + } + } + authHeader = 'Digest ' + authHeader.join(', ') + self.sentAuth = true + return authHeader +} + +Auth.prototype.onRequest = function (user, pass, sendImmediately, bearer) { + var self = this + var request = self.request + + var authHeader + if (bearer === undefined && user === undefined) { + self.request.emit('error', new Error('no auth mechanism defined')) + } else if (bearer !== undefined) { + authHeader = self.bearer(bearer, sendImmediately) + } else { + authHeader = self.basic(user, pass, sendImmediately) + } + if (authHeader) { + request.setHeader('authorization', authHeader) + } +} + +Auth.prototype.onResponse = function (response) { + var self = this + var request = self.request + + if (!self.hasAuth || self.sentAuth) { return null } + + var c = caseless(response.headers) + + var authHeader = c.get('www-authenticate') + var authVerb = authHeader && authHeader.split(' ')[0].toLowerCase() + request.debug('reauth', authVerb) + + switch (authVerb) { + case 'basic': + return self.basic(self.user, self.pass, true) + + case 'bearer': + return self.bearer(self.bearerToken, true) + + case 'digest': + return self.digest(request.method, request.path, authHeader) + } +} + +exports.Auth = Auth + + +/***/ }), + +/***/ 560: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate__limitProperties(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $errorKeyword; + var $data = 'data' + ($dataLvl || ''); + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + var $op = $keyword == 'maxProperties' ? '>' : '<'; + out += 'if ( '; + if ($isData) { + out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || '; + } + out += ' Object.keys(' + ($data) + ').length ' + ($op) + ' ' + ($schemaValue) + ') { '; + var $errorKeyword = $keyword; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ($errorKeyword || '_limitProperties') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { limit: ' + ($schemaValue) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT have '; + if ($keyword == 'maxProperties') { + out += 'more'; + } else { + out += 'fewer'; + } + out += ' than '; + if ($isData) { + out += '\' + ' + ($schemaValue) + ' + \''; + } else { + out += '' + ($schema); + } + out += ' properties\' '; + } + if (it.opts.verbose) { + out += ' , schema: '; + if ($isData) { + out += 'validate.schema' + ($schemaPath); + } else { + out += '' + ($schema); + } + out += ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += '} '; + if ($breakOnError) { + out += ' else { '; + } + return out; +} + + +/***/ }), + +/***/ 566: +/***/ (function(module) { + +// API +module.exports = abort; + +/** + * Aborts leftover active jobs + * + * @param {object} state - current state object + */ +function abort(state) +{ + Object.keys(state.jobs).forEach(clean.bind(state)); + + // reset leftover jobs + state.jobs = {}; +} + +/** + * Cleans up leftover job by invoking abort function for the provided job id + * + * @this state + * @param {string|number} key - job id to abort + */ +function clean(key) +{ + if (typeof this.jobs[key] == 'function') + { + this.jobs[key](); + } +} + + +/***/ }), + +/***/ 575: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2015 Joyent, Inc. + +module.exports = Signature; + +var assert = __webpack_require__(477); +var Buffer = __webpack_require__(215).Buffer; +var algs = __webpack_require__(98); +var crypto = __webpack_require__(417); +var errs = __webpack_require__(753); +var utils = __webpack_require__(270); +var asn1 = __webpack_require__(62); +var SSHBuffer = __webpack_require__(940); + +var InvalidAlgorithmError = errs.InvalidAlgorithmError; +var SignatureParseError = errs.SignatureParseError; + +function Signature(opts) { + assert.object(opts, 'options'); + assert.arrayOfObject(opts.parts, 'options.parts'); + assert.string(opts.type, 'options.type'); + + var partLookup = {}; + for (var i = 0; i < opts.parts.length; ++i) { + var part = opts.parts[i]; + partLookup[part.name] = part; + } + + this.type = opts.type; + this.hashAlgorithm = opts.hashAlgo; + this.curve = opts.curve; + this.parts = opts.parts; + this.part = partLookup; +} + +Signature.prototype.toBuffer = function (format) { + if (format === undefined) + format = 'asn1'; + assert.string(format, 'format'); + + var buf; + var stype = 'ssh-' + this.type; + + switch (this.type) { + case 'rsa': + switch (this.hashAlgorithm) { + case 'sha256': + stype = 'rsa-sha2-256'; + break; + case 'sha512': + stype = 'rsa-sha2-512'; + break; + case 'sha1': + case undefined: + break; + default: + throw (new Error('SSH signature ' + + 'format does not support hash ' + + 'algorithm ' + this.hashAlgorithm)); + } + if (format === 'ssh') { + buf = new SSHBuffer({}); + buf.writeString(stype); + buf.writePart(this.part.sig); + return (buf.toBuffer()); + } else { + return (this.part.sig.data); + } + break; + + case 'ed25519': + if (format === 'ssh') { + buf = new SSHBuffer({}); + buf.writeString(stype); + buf.writePart(this.part.sig); + return (buf.toBuffer()); + } else { + return (this.part.sig.data); + } + break; + + case 'dsa': + case 'ecdsa': + var r, s; + if (format === 'asn1') { + var der = new asn1.BerWriter(); + der.startSequence(); + r = utils.mpNormalize(this.part.r.data); + s = utils.mpNormalize(this.part.s.data); + der.writeBuffer(r, asn1.Ber.Integer); + der.writeBuffer(s, asn1.Ber.Integer); + der.endSequence(); + return (der.buffer); + } else if (format === 'ssh' && this.type === 'dsa') { + buf = new SSHBuffer({}); + buf.writeString('ssh-dss'); + r = this.part.r.data; + if (r.length > 20 && r[0] === 0x00) + r = r.slice(1); + s = this.part.s.data; + if (s.length > 20 && s[0] === 0x00) + s = s.slice(1); + if ((this.hashAlgorithm && + this.hashAlgorithm !== 'sha1') || + r.length + s.length !== 40) { + throw (new Error('OpenSSH only supports ' + + 'DSA signatures with SHA1 hash')); + } + buf.writeBuffer(Buffer.concat([r, s])); + return (buf.toBuffer()); + } else if (format === 'ssh' && this.type === 'ecdsa') { + var inner = new SSHBuffer({}); + r = this.part.r.data; + inner.writeBuffer(r); + inner.writePart(this.part.s); + + buf = new SSHBuffer({}); + /* XXX: find a more proper way to do this? */ + var curve; + if (r[0] === 0x00) + r = r.slice(1); + var sz = r.length * 8; + if (sz === 256) + curve = 'nistp256'; + else if (sz === 384) + curve = 'nistp384'; + else if (sz === 528) + curve = 'nistp521'; + buf.writeString('ecdsa-sha2-' + curve); + buf.writeBuffer(inner.toBuffer()); + return (buf.toBuffer()); + } + throw (new Error('Invalid signature format')); + default: + throw (new Error('Invalid signature data')); + } +}; + +Signature.prototype.toString = function (format) { + assert.optionalString(format, 'format'); + return (this.toBuffer(format).toString('base64')); +}; + +Signature.parse = function (data, type, format) { + if (typeof (data) === 'string') + data = Buffer.from(data, 'base64'); + assert.buffer(data, 'data'); + assert.string(format, 'format'); + assert.string(type, 'type'); + + var opts = {}; + opts.type = type.toLowerCase(); + opts.parts = []; + + try { + assert.ok(data.length > 0, 'signature must not be empty'); + switch (opts.type) { + case 'rsa': + return (parseOneNum(data, type, format, opts)); + case 'ed25519': + return (parseOneNum(data, type, format, opts)); + + case 'dsa': + case 'ecdsa': + if (format === 'asn1') + return (parseDSAasn1(data, type, format, opts)); + else if (opts.type === 'dsa') + return (parseDSA(data, type, format, opts)); + else + return (parseECDSA(data, type, format, opts)); + + default: + throw (new InvalidAlgorithmError(type)); + } + + } catch (e) { + if (e instanceof InvalidAlgorithmError) + throw (e); + throw (new SignatureParseError(type, format, e)); + } +}; + +function parseOneNum(data, type, format, opts) { + if (format === 'ssh') { + try { + var buf = new SSHBuffer({buffer: data}); + var head = buf.readString(); + } catch (e) { + /* fall through */ + } + if (buf !== undefined) { + var msg = 'SSH signature does not match expected ' + + 'type (expected ' + type + ', got ' + head + ')'; + switch (head) { + case 'ssh-rsa': + assert.strictEqual(type, 'rsa', msg); + opts.hashAlgo = 'sha1'; + break; + case 'rsa-sha2-256': + assert.strictEqual(type, 'rsa', msg); + opts.hashAlgo = 'sha256'; + break; + case 'rsa-sha2-512': + assert.strictEqual(type, 'rsa', msg); + opts.hashAlgo = 'sha512'; + break; + case 'ssh-ed25519': + assert.strictEqual(type, 'ed25519', msg); + opts.hashAlgo = 'sha512'; + break; + default: + throw (new Error('Unknown SSH signature ' + + 'type: ' + head)); + } + var sig = buf.readPart(); + assert.ok(buf.atEnd(), 'extra trailing bytes'); + sig.name = 'sig'; + opts.parts.push(sig); + return (new Signature(opts)); + } + } + opts.parts.push({name: 'sig', data: data}); + return (new Signature(opts)); +} + +function parseDSAasn1(data, type, format, opts) { + var der = new asn1.BerReader(data); + der.readSequence(); + var r = der.readString(asn1.Ber.Integer, true); + var s = der.readString(asn1.Ber.Integer, true); + + opts.parts.push({name: 'r', data: utils.mpNormalize(r)}); + opts.parts.push({name: 's', data: utils.mpNormalize(s)}); + + return (new Signature(opts)); +} + +function parseDSA(data, type, format, opts) { + if (data.length != 40) { + var buf = new SSHBuffer({buffer: data}); + var d = buf.readBuffer(); + if (d.toString('ascii') === 'ssh-dss') + d = buf.readBuffer(); + assert.ok(buf.atEnd(), 'extra trailing bytes'); + assert.strictEqual(d.length, 40, 'invalid inner length'); + data = d; + } + opts.parts.push({name: 'r', data: data.slice(0, 20)}); + opts.parts.push({name: 's', data: data.slice(20, 40)}); + return (new Signature(opts)); +} + +function parseECDSA(data, type, format, opts) { + var buf = new SSHBuffer({buffer: data}); + + var r, s; + var inner = buf.readBuffer(); + var stype = inner.toString('ascii'); + if (stype.slice(0, 6) === 'ecdsa-') { + var parts = stype.split('-'); + assert.strictEqual(parts[0], 'ecdsa'); + assert.strictEqual(parts[1], 'sha2'); + opts.curve = parts[2]; + switch (opts.curve) { + case 'nistp256': + opts.hashAlgo = 'sha256'; + break; + case 'nistp384': + opts.hashAlgo = 'sha384'; + break; + case 'nistp521': + opts.hashAlgo = 'sha512'; + break; + default: + throw (new Error('Unsupported ECDSA curve: ' + + opts.curve)); + } + inner = buf.readBuffer(); + assert.ok(buf.atEnd(), 'extra trailing bytes on outer'); + buf = new SSHBuffer({buffer: inner}); + r = buf.readPart(); + } else { + r = {data: inner}; + } + + s = buf.readPart(); + assert.ok(buf.atEnd(), 'extra trailing bytes'); + + r.name = 'r'; + s.name = 's'; + + opts.parts.push(r); + opts.parts.push(s); + return (new Signature(opts)); +} + +Signature.isSignature = function (obj, ver) { + return (utils.isCompatible(obj, Signature, ver)); +}; + +/* + * API versions for Signature: + * [1,0] -- initial ver + * [2,0] -- support for rsa in full ssh format, compat with sshpk-agent + * hashAlgorithm property + * [2,1] -- first tagged version + */ +Signature.prototype._sshpkApiVersion = [2, 1]; + +Signature._oldVersionDetect = function (obj) { + assert.func(obj.toBuffer); + if (obj.hasOwnProperty('hashAlgorithm')) + return ([2, 0]); + return ([1, 0]); +}; + + +/***/ }), + +/***/ 581: +/***/ (function(module) { + +"use strict"; + + +var has = Object.prototype.hasOwnProperty; + +var hexTable = (function () { + var array = []; + for (var i = 0; i < 256; ++i) { + array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase()); + } + + return array; +}()); + +var compactQueue = function compactQueue(queue) { + var obj; + + while (queue.length) { + var item = queue.pop(); + obj = item.obj[item.prop]; + + if (Array.isArray(obj)) { + var compacted = []; + + for (var j = 0; j < obj.length; ++j) { + if (typeof obj[j] !== 'undefined') { + compacted.push(obj[j]); + } + } + + item.obj[item.prop] = compacted; + } + } + + return obj; +}; + +var arrayToObject = function arrayToObject(source, options) { + var obj = options && options.plainObjects ? Object.create(null) : {}; + for (var i = 0; i < source.length; ++i) { + if (typeof source[i] !== 'undefined') { + obj[i] = source[i]; + } + } + + return obj; +}; + +var merge = function merge(target, source, options) { + if (!source) { + return target; + } + + if (typeof source !== 'object') { + if (Array.isArray(target)) { + target.push(source); + } else if (typeof target === 'object') { + if (options.plainObjects || options.allowPrototypes || !has.call(Object.prototype, source)) { + target[source] = true; + } + } else { + return [target, source]; + } + + return target; + } + + if (typeof target !== 'object') { + return [target].concat(source); + } + + var mergeTarget = target; + if (Array.isArray(target) && !Array.isArray(source)) { + mergeTarget = arrayToObject(target, options); + } + + if (Array.isArray(target) && Array.isArray(source)) { + source.forEach(function (item, i) { + if (has.call(target, i)) { + if (target[i] && typeof target[i] === 'object') { + target[i] = merge(target[i], item, options); + } else { + target.push(item); + } + } else { + target[i] = item; + } + }); + return target; + } + + return Object.keys(source).reduce(function (acc, key) { + var value = source[key]; + + if (has.call(acc, key)) { + acc[key] = merge(acc[key], value, options); + } else { + acc[key] = value; + } + return acc; + }, mergeTarget); +}; + +var assign = function assignSingleSource(target, source) { + return Object.keys(source).reduce(function (acc, key) { + acc[key] = source[key]; + return acc; + }, target); +}; + +var decode = function (str) { + try { + return decodeURIComponent(str.replace(/\+/g, ' ')); + } catch (e) { + return str; + } +}; + +var encode = function encode(str) { + // This code was originally written by Brian White (mscdex) for the io.js core querystring library. + // It has been adapted here for stricter adherence to RFC 3986 + if (str.length === 0) { + return str; + } + + var string = typeof str === 'string' ? str : String(str); + + var out = ''; + for (var i = 0; i < string.length; ++i) { + var c = string.charCodeAt(i); + + if ( + c === 0x2D // - + || c === 0x2E // . + || c === 0x5F // _ + || c === 0x7E // ~ + || (c >= 0x30 && c <= 0x39) // 0-9 + || (c >= 0x41 && c <= 0x5A) // a-z + || (c >= 0x61 && c <= 0x7A) // A-Z + ) { + out += string.charAt(i); + continue; + } + + if (c < 0x80) { + out = out + hexTable[c]; + continue; + } + + if (c < 0x800) { + out = out + (hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)]); + continue; + } + + if (c < 0xD800 || c >= 0xE000) { + out = out + (hexTable[0xE0 | (c >> 12)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]); + continue; + } + + i += 1; + c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF)); + out += hexTable[0xF0 | (c >> 18)] + + hexTable[0x80 | ((c >> 12) & 0x3F)] + + hexTable[0x80 | ((c >> 6) & 0x3F)] + + hexTable[0x80 | (c & 0x3F)]; + } + + return out; +}; + +var compact = function compact(value) { + var queue = [{ obj: { o: value }, prop: 'o' }]; + var refs = []; + + for (var i = 0; i < queue.length; ++i) { + var item = queue[i]; + var obj = item.obj[item.prop]; + + var keys = Object.keys(obj); + for (var j = 0; j < keys.length; ++j) { + var key = keys[j]; + var val = obj[key]; + if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) { + queue.push({ obj: obj, prop: key }); + refs.push(val); + } + } + } + + return compactQueue(queue); +}; + +var isRegExp = function isRegExp(obj) { + return Object.prototype.toString.call(obj) === '[object RegExp]'; +}; + +var isBuffer = function isBuffer(obj) { + if (obj === null || typeof obj === 'undefined') { + return false; + } + + return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj)); +}; + +module.exports = { + arrayToObject: arrayToObject, + assign: assign, + compact: compact, + decode: decode, + encode: encode, + isBuffer: isBuffer, + isRegExp: isRegExp, + merge: merge +}; + + +/***/ }), + +/***/ 584: +/***/ (function(module) { + +// Copyright 2011 Mark Cavage All rights reserved. + + +module.exports = { + + newInvalidAsn1Error: function (msg) { + var e = new Error(); + e.name = 'InvalidAsn1Error'; + e.message = msg || ''; + return e; + } + +}; + + +/***/ }), + +/***/ 602: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + + +var tough = __webpack_require__(701) + +var Cookie = tough.Cookie +var CookieJar = tough.CookieJar + +exports.parse = function (str) { + if (str && str.uri) { + str = str.uri + } + if (typeof str !== 'string') { + throw new Error('The cookie function only accepts STRING as param') + } + return Cookie.parse(str, {loose: true}) +} + +// Adapt the sometimes-Async api of tough.CookieJar to our requirements +function RequestJar (store) { + var self = this + self._jar = new CookieJar(store, {looseMode: true}) +} +RequestJar.prototype.setCookie = function (cookieOrStr, uri, options) { + var self = this + return self._jar.setCookieSync(cookieOrStr, uri, options || {}) +} +RequestJar.prototype.getCookieString = function (uri) { + var self = this + return self._jar.getCookieStringSync(uri) +} +RequestJar.prototype.getCookies = function (uri) { + var self = this + return self._jar.getCookiesSync(uri) +} + +exports.jar = function (store) { + return new RequestJar(store) +} + + +/***/ }), + +/***/ 603: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2015 Joyent, Inc. + +module.exports = { + read: read, + write: write +}; + +var assert = __webpack_require__(477); +var Buffer = __webpack_require__(215).Buffer; +var rfc4253 = __webpack_require__(538); +var utils = __webpack_require__(270); +var Key = __webpack_require__(852); +var PrivateKey = __webpack_require__(502); + +var sshpriv = __webpack_require__(78); + +/*JSSTYLED*/ +var SSHKEY_RE = /^([a-z0-9-]+)[ \t]+([a-zA-Z0-9+\/]+[=]*)([ \t]+([^ \t][^\n]*[\n]*)?)?$/; +/*JSSTYLED*/ +var SSHKEY_RE2 = /^([a-z0-9-]+)[ \t\n]+([a-zA-Z0-9+\/][a-zA-Z0-9+\/ \t\n=]*)([^a-zA-Z0-9+\/ \t\n=].*)?$/; + +function read(buf, options) { + if (typeof (buf) !== 'string') { + assert.buffer(buf, 'buf'); + buf = buf.toString('ascii'); + } + + var trimmed = buf.trim().replace(/[\\\r]/g, ''); + var m = trimmed.match(SSHKEY_RE); + if (!m) + m = trimmed.match(SSHKEY_RE2); + assert.ok(m, 'key must match regex'); + + var type = rfc4253.algToKeyType(m[1]); + var kbuf = Buffer.from(m[2], 'base64'); + + /* + * This is a bit tricky. If we managed to parse the key and locate the + * key comment with the regex, then do a non-partial read and assert + * that we have consumed all bytes. If we couldn't locate the key + * comment, though, there may be whitespace shenanigans going on that + * have conjoined the comment to the rest of the key. We do a partial + * read in this case to try to make the best out of a sorry situation. + */ + var key; + var ret = {}; + if (m[4]) { + try { + key = rfc4253.read(kbuf); + + } catch (e) { + m = trimmed.match(SSHKEY_RE2); + assert.ok(m, 'key must match regex'); + kbuf = Buffer.from(m[2], 'base64'); + key = rfc4253.readInternal(ret, 'public', kbuf); + } + } else { + key = rfc4253.readInternal(ret, 'public', kbuf); + } + + assert.strictEqual(type, key.type); + + if (m[4] && m[4].length > 0) { + key.comment = m[4]; + + } else if (ret.consumed) { + /* + * Now the magic: trying to recover the key comment when it's + * gotten conjoined to the key or otherwise shenanigan'd. + * + * Work out how much base64 we used, then drop all non-base64 + * chars from the beginning up to this point in the the string. + * Then offset in this and try to make up for missing = chars. + */ + var data = m[2] + (m[3] ? m[3] : ''); + var realOffset = Math.ceil(ret.consumed / 3) * 4; + data = data.slice(0, realOffset - 2). /*JSSTYLED*/ + replace(/[^a-zA-Z0-9+\/=]/g, '') + + data.slice(realOffset - 2); + + var padding = ret.consumed % 3; + if (padding > 0 && + data.slice(realOffset - 1, realOffset) !== '=') + realOffset--; + while (data.slice(realOffset, realOffset + 1) === '=') + realOffset++; + + /* Finally, grab what we think is the comment & clean it up. */ + var trailer = data.slice(realOffset); + trailer = trailer.replace(/[\r\n]/g, ' '). + replace(/^\s+/, ''); + if (trailer.match(/^[a-zA-Z0-9]/)) + key.comment = trailer; + } + + return (key); +} + +function write(key, options) { + assert.object(key); + if (!Key.isKey(key)) + throw (new Error('Must be a public key')); + + var parts = []; + var alg = rfc4253.keyTypeToAlg(key); + parts.push(alg); + + var buf = rfc4253.write(key); + parts.push(buf.toString('base64')); + + if (key.comment) + parts.push(key.comment); + + return (Buffer.from(parts.join(' '))); +} + + +/***/ }), + +/***/ 605: +/***/ (function(module) { + +module.exports = require("http"); + +/***/ }), + +/***/ 614: +/***/ (function(module) { + +module.exports = require("events"); + +/***/ }), + +/***/ 616: +/***/ (function(module) { + +module.exports = {"$id":"beforeRequest.json#","$schema":"http://json-schema.org/draft-06/schema#","type":"object","optional":true,"required":["lastAccess","eTag","hitCount"],"properties":{"expires":{"type":"string","pattern":"^(\\d{4})(-)?(\\d\\d)(-)?(\\d\\d)(T)?(\\d\\d)(:)?(\\d\\d)(:)?(\\d\\d)(\\.\\d+)?(Z|([+-])(\\d\\d)(:)?(\\d\\d))?"},"lastAccess":{"type":"string","pattern":"^(\\d{4})(-)?(\\d\\d)(-)?(\\d\\d)(T)?(\\d\\d)(:)?(\\d\\d)(:)?(\\d\\d)(\\.\\d+)?(Z|([+-])(\\d\\d)(:)?(\\d\\d))?"},"eTag":{"type":"string"},"hitCount":{"type":"integer"},"comment":{"type":"string"}}}; + +/***/ }), + +/***/ 622: +/***/ (function(module) { + +module.exports = require("path"); + +/***/ }), + +/***/ 624: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2018 Joyent, Inc. + +module.exports = { + read: read, + write: write +}; + +var assert = __webpack_require__(477); +var Buffer = __webpack_require__(215).Buffer; +var rfc4253 = __webpack_require__(538); +var Key = __webpack_require__(852); + +var errors = __webpack_require__(753); + +function read(buf, options) { + var lines = buf.toString('ascii').split(/[\r\n]+/); + var found = false; + var parts; + var si = 0; + while (si < lines.length) { + parts = splitHeader(lines[si++]); + if (parts && + parts[0].toLowerCase() === 'putty-user-key-file-2') { + found = true; + break; + } + } + if (!found) { + throw (new Error('No PuTTY format first line found')); + } + var alg = parts[1]; + + parts = splitHeader(lines[si++]); + assert.equal(parts[0].toLowerCase(), 'encryption'); + + parts = splitHeader(lines[si++]); + assert.equal(parts[0].toLowerCase(), 'comment'); + var comment = parts[1]; + + parts = splitHeader(lines[si++]); + assert.equal(parts[0].toLowerCase(), 'public-lines'); + var publicLines = parseInt(parts[1], 10); + if (!isFinite(publicLines) || publicLines < 0 || + publicLines > lines.length) { + throw (new Error('Invalid public-lines count')); + } + + var publicBuf = Buffer.from( + lines.slice(si, si + publicLines).join(''), 'base64'); + var keyType = rfc4253.algToKeyType(alg); + var key = rfc4253.read(publicBuf); + if (key.type !== keyType) { + throw (new Error('Outer key algorithm mismatch')); + } + key.comment = comment; + return (key); +} + +function splitHeader(line) { + var idx = line.indexOf(':'); + if (idx === -1) + return (null); + var header = line.slice(0, idx); + ++idx; + while (line[idx] === ' ') + ++idx; + var rest = line.slice(idx); + return ([header, rest]); +} + +function write(key, options) { + assert.object(key); + if (!Key.isKey(key)) + throw (new Error('Must be a public key')); + + var alg = rfc4253.keyTypeToAlg(key); + var buf = rfc4253.write(key); + var comment = key.comment || ''; + + var b64 = buf.toString('base64'); + var lines = wrap(b64, 64); + + lines.unshift('Public-Lines: ' + lines.length); + lines.unshift('Comment: ' + comment); + lines.unshift('Encryption: none'); + lines.unshift('PuTTY-User-Key-File-2: ' + alg); + + return (Buffer.from(lines.join('\n') + '\n')); +} + +function wrap(txt, len) { + var lines = []; + var pos = 0; + while (pos < txt.length) { + lines.push(txt.slice(pos, pos + 64)); + pos += 64; + } + return (lines); +} + + +/***/ }), + +/***/ 627: +/***/ (function(__unusedmodule, exports) { + +"use strict"; +/*! + * Copyright (c) 2015, Salesforce.com, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Salesforce.com nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/*jshint unused:false */ + +function Store() { +} +exports.Store = Store; + +// Stores may be synchronous, but are still required to use a +// Continuation-Passing Style API. The CookieJar itself will expose a "*Sync" +// API that converts from synchronous-callbacks to imperative style. +Store.prototype.synchronous = false; + +Store.prototype.findCookie = function(domain, path, key, cb) { + throw new Error('findCookie is not implemented'); +}; + +Store.prototype.findCookies = function(domain, path, cb) { + throw new Error('findCookies is not implemented'); +}; + +Store.prototype.putCookie = function(cookie, cb) { + throw new Error('putCookie is not implemented'); +}; + +Store.prototype.updateCookie = function(oldCookie, newCookie, cb) { + // recommended default implementation: + // return this.putCookie(newCookie, cb); + throw new Error('updateCookie is not implemented'); +}; + +Store.prototype.removeCookie = function(domain, path, key, cb) { + throw new Error('removeCookie is not implemented'); +}; + +Store.prototype.removeCookies = function(domain, path, cb) { + throw new Error('removeCookies is not implemented'); +}; + +Store.prototype.removeAllCookies = function(cb) { + throw new Error('removeAllCookies is not implemented'); +} + +Store.prototype.getAllCookies = function(cb) { + throw new Error('getAllCookies is not implemented (therefore jar cannot be serialized)'); +}; + + +/***/ }), + +/***/ 628: +/***/ (function(module) { + +"use strict"; + + +var KEYWORDS = [ + 'multipleOf', + 'maximum', + 'exclusiveMaximum', + 'minimum', + 'exclusiveMinimum', + 'maxLength', + 'minLength', + 'pattern', + 'additionalItems', + 'maxItems', + 'minItems', + 'uniqueItems', + 'maxProperties', + 'minProperties', + 'required', + 'additionalProperties', + 'enum', + 'format', + 'const' +]; + +module.exports = function (metaSchema, keywordsJsonPointers) { + for (var i=0; i + */ + +/* + * The Blowfish portions are under the following license: + * + * Blowfish block cipher for OpenBSD + * Copyright 1997 Niels Provos + * All rights reserved. + * + * Implementation advice by David Mazieres . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * The bcrypt_pbkdf portions are under the following license: + * + * Copyright (c) 2013 Ted Unangst + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Performance improvements (Javascript-specific): + * + * Copyright 2016, Joyent Inc + * Author: Alex Wilson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +// Ported from OpenBSD bcrypt_pbkdf.c v1.9 + +var BLF_J = 0; + +var Blowfish = function() { + this.S = [ + new Uint32Array([ + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a]), + new Uint32Array([ + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7]), + new Uint32Array([ + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0]), + new Uint32Array([ + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6]) + ]; + this.P = new Uint32Array([ + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b]); +}; + +function F(S, x8, i) { + return (((S[0][x8[i+3]] + + S[1][x8[i+2]]) ^ + S[2][x8[i+1]]) + + S[3][x8[i]]); +}; + +Blowfish.prototype.encipher = function(x, x8) { + if (x8 === undefined) { + x8 = new Uint8Array(x.buffer); + if (x.byteOffset !== 0) + x8 = x8.subarray(x.byteOffset); + } + x[0] ^= this.P[0]; + for (var i = 1; i < 16; i += 2) { + x[1] ^= F(this.S, x8, 0) ^ this.P[i]; + x[0] ^= F(this.S, x8, 4) ^ this.P[i+1]; + } + var t = x[0]; + x[0] = x[1] ^ this.P[17]; + x[1] = t; +}; + +Blowfish.prototype.decipher = function(x) { + var x8 = new Uint8Array(x.buffer); + if (x.byteOffset !== 0) + x8 = x8.subarray(x.byteOffset); + x[0] ^= this.P[17]; + for (var i = 16; i > 0; i -= 2) { + x[1] ^= F(this.S, x8, 0) ^ this.P[i]; + x[0] ^= F(this.S, x8, 4) ^ this.P[i-1]; + } + var t = x[0]; + x[0] = x[1] ^ this.P[0]; + x[1] = t; +}; + +function stream2word(data, databytes){ + var i, temp = 0; + for (i = 0; i < 4; i++, BLF_J++) { + if (BLF_J >= databytes) BLF_J = 0; + temp = (temp << 8) | data[BLF_J]; + } + return temp; +}; + +Blowfish.prototype.expand0state = function(key, keybytes) { + var d = new Uint32Array(2), i, k; + var d8 = new Uint8Array(d.buffer); + + for (i = 0, BLF_J = 0; i < 18; i++) { + this.P[i] ^= stream2word(key, keybytes); + } + BLF_J = 0; + + for (i = 0; i < 18; i += 2) { + this.encipher(d, d8); + this.P[i] = d[0]; + this.P[i+1] = d[1]; + } + + for (i = 0; i < 4; i++) { + for (k = 0; k < 256; k += 2) { + this.encipher(d, d8); + this.S[i][k] = d[0]; + this.S[i][k+1] = d[1]; + } + } +}; + +Blowfish.prototype.expandstate = function(data, databytes, key, keybytes) { + var d = new Uint32Array(2), i, k; + + for (i = 0, BLF_J = 0; i < 18; i++) { + this.P[i] ^= stream2word(key, keybytes); + } + + for (i = 0, BLF_J = 0; i < 18; i += 2) { + d[0] ^= stream2word(data, databytes); + d[1] ^= stream2word(data, databytes); + this.encipher(d); + this.P[i] = d[0]; + this.P[i+1] = d[1]; + } + + for (i = 0; i < 4; i++) { + for (k = 0; k < 256; k += 2) { + d[0] ^= stream2word(data, databytes); + d[1] ^= stream2word(data, databytes); + this.encipher(d); + this.S[i][k] = d[0]; + this.S[i][k+1] = d[1]; + } + } + BLF_J = 0; +}; + +Blowfish.prototype.enc = function(data, blocks) { + for (var i = 0; i < blocks; i++) { + this.encipher(data.subarray(i*2)); + } +}; + +Blowfish.prototype.dec = function(data, blocks) { + for (var i = 0; i < blocks; i++) { + this.decipher(data.subarray(i*2)); + } +}; + +var BCRYPT_BLOCKS = 8, + BCRYPT_HASHSIZE = 32; + +function bcrypt_hash(sha2pass, sha2salt, out) { + var state = new Blowfish(), + cdata = new Uint32Array(BCRYPT_BLOCKS), i, + ciphertext = new Uint8Array([79,120,121,99,104,114,111,109,97,116,105, + 99,66,108,111,119,102,105,115,104,83,119,97,116,68,121,110,97,109, + 105,116,101]); //"OxychromaticBlowfishSwatDynamite" + + state.expandstate(sha2salt, 64, sha2pass, 64); + for (i = 0; i < 64; i++) { + state.expand0state(sha2salt, 64); + state.expand0state(sha2pass, 64); + } + + for (i = 0; i < BCRYPT_BLOCKS; i++) + cdata[i] = stream2word(ciphertext, ciphertext.byteLength); + for (i = 0; i < 64; i++) + state.enc(cdata, cdata.byteLength / 8); + + for (i = 0; i < BCRYPT_BLOCKS; i++) { + out[4*i+3] = cdata[i] >>> 24; + out[4*i+2] = cdata[i] >>> 16; + out[4*i+1] = cdata[i] >>> 8; + out[4*i+0] = cdata[i]; + } +}; + +function bcrypt_pbkdf(pass, passlen, salt, saltlen, key, keylen, rounds) { + var sha2pass = new Uint8Array(64), + sha2salt = new Uint8Array(64), + out = new Uint8Array(BCRYPT_HASHSIZE), + tmpout = new Uint8Array(BCRYPT_HASHSIZE), + countsalt = new Uint8Array(saltlen+4), + i, j, amt, stride, dest, count, + origkeylen = keylen; + + if (rounds < 1) + return -1; + if (passlen === 0 || saltlen === 0 || keylen === 0 || + keylen > (out.byteLength * out.byteLength) || saltlen > (1<<20)) + return -1; + + stride = Math.floor((keylen + out.byteLength - 1) / out.byteLength); + amt = Math.floor((keylen + stride - 1) / stride); + + for (i = 0; i < saltlen; i++) + countsalt[i] = salt[i]; + + crypto_hash_sha512(sha2pass, pass, passlen); + + for (count = 1; keylen > 0; count++) { + countsalt[saltlen+0] = count >>> 24; + countsalt[saltlen+1] = count >>> 16; + countsalt[saltlen+2] = count >>> 8; + countsalt[saltlen+3] = count; + + crypto_hash_sha512(sha2salt, countsalt, saltlen + 4); + bcrypt_hash(sha2pass, sha2salt, tmpout); + for (i = out.byteLength; i--;) + out[i] = tmpout[i]; + + for (i = 1; i < rounds; i++) { + crypto_hash_sha512(sha2salt, tmpout, tmpout.byteLength); + bcrypt_hash(sha2pass, sha2salt, tmpout); + for (j = 0; j < out.byteLength; j++) + out[j] ^= tmpout[j]; + } + + amt = Math.min(amt, keylen); + for (i = 0; i < amt; i++) { + dest = i * stride + (count - 1); + if (dest >= origkeylen) + break; + key[dest] = out[i]; + } + keylen -= i; + } + + return 0; +}; + +module.exports = { + BLOCKS: BCRYPT_BLOCKS, + HASHSIZE: BCRYPT_HASHSIZE, + hash: bcrypt_hash, + pbkdf: bcrypt_pbkdf +}; + + +/***/ }), + +/***/ 643: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_items(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + var $idx = 'i' + $lvl, + $dataNxt = $it.dataLevel = it.dataLevel + 1, + $nextData = 'data' + $dataNxt, + $currentBaseId = it.baseId; + out += 'var ' + ($errs) + ' = errors;var ' + ($valid) + ';'; + if (Array.isArray($schema)) { + var $additionalItems = it.schema.additionalItems; + if ($additionalItems === false) { + out += ' ' + ($valid) + ' = ' + ($data) + '.length <= ' + ($schema.length) + '; '; + var $currErrSchemaPath = $errSchemaPath; + $errSchemaPath = it.errSchemaPath + '/additionalItems'; + out += ' if (!' + ($valid) + ') { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('additionalItems') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { limit: ' + ($schema.length) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT have more than ' + ($schema.length) + ' items\' '; + } + if (it.opts.verbose) { + out += ' , schema: false , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } '; + $errSchemaPath = $currErrSchemaPath; + if ($breakOnError) { + $closingBraces += '}'; + out += ' else { '; + } + } + var arr1 = $schema; + if (arr1) { + var $sch, $i = -1, + l1 = arr1.length - 1; + while ($i < l1) { + $sch = arr1[$i += 1]; + if ((it.opts.strictKeywords ? typeof $sch == 'object' && Object.keys($sch).length > 0 : it.util.schemaHasRules($sch, it.RULES.all))) { + out += ' ' + ($nextValid) + ' = true; if (' + ($data) + '.length > ' + ($i) + ') { '; + var $passData = $data + '[' + $i + ']'; + $it.schema = $sch; + $it.schemaPath = $schemaPath + '[' + $i + ']'; + $it.errSchemaPath = $errSchemaPath + '/' + $i; + $it.errorPath = it.util.getPathExpr(it.errorPath, $i, it.opts.jsonPointers, true); + $it.dataPathArr[$dataNxt] = $i; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + out += ' } '; + if ($breakOnError) { + out += ' if (' + ($nextValid) + ') { '; + $closingBraces += '}'; + } + } + } + } + if (typeof $additionalItems == 'object' && (it.opts.strictKeywords ? typeof $additionalItems == 'object' && Object.keys($additionalItems).length > 0 : it.util.schemaHasRules($additionalItems, it.RULES.all))) { + $it.schema = $additionalItems; + $it.schemaPath = it.schemaPath + '.additionalItems'; + $it.errSchemaPath = it.errSchemaPath + '/additionalItems'; + out += ' ' + ($nextValid) + ' = true; if (' + ($data) + '.length > ' + ($schema.length) + ') { for (var ' + ($idx) + ' = ' + ($schema.length) + '; ' + ($idx) + ' < ' + ($data) + '.length; ' + ($idx) + '++) { '; + $it.errorPath = it.util.getPathExpr(it.errorPath, $idx, it.opts.jsonPointers, true); + var $passData = $data + '[' + $idx + ']'; + $it.dataPathArr[$dataNxt] = $idx; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + if ($breakOnError) { + out += ' if (!' + ($nextValid) + ') break; '; + } + out += ' } } '; + if ($breakOnError) { + out += ' if (' + ($nextValid) + ') { '; + $closingBraces += '}'; + } + } + } else if ((it.opts.strictKeywords ? typeof $schema == 'object' && Object.keys($schema).length > 0 : it.util.schemaHasRules($schema, it.RULES.all))) { + $it.schema = $schema; + $it.schemaPath = $schemaPath; + $it.errSchemaPath = $errSchemaPath; + out += ' for (var ' + ($idx) + ' = ' + (0) + '; ' + ($idx) + ' < ' + ($data) + '.length; ' + ($idx) + '++) { '; + $it.errorPath = it.util.getPathExpr(it.errorPath, $idx, it.opts.jsonPointers, true); + var $passData = $data + '[' + $idx + ']'; + $it.dataPathArr[$dataNxt] = $idx; + var $code = it.validate($it); + $it.baseId = $currentBaseId; + if (it.util.varOccurences($code, $nextData) < 2) { + out += ' ' + (it.util.varReplace($code, $nextData, $passData)) + ' '; + } else { + out += ' var ' + ($nextData) + ' = ' + ($passData) + '; ' + ($code) + ' '; + } + if ($breakOnError) { + out += ' if (!' + ($nextValid) + ') break; '; + } + out += ' }'; + } + if ($breakOnError) { + out += ' ' + ($closingBraces) + ' if (' + ($errs) + ' == errors) {'; + } + out = it.util.cleanUpCode(out); + return out; +} + + +/***/ }), + +/***/ 650: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2015 Joyent, Inc. + +var Key = __webpack_require__(852); +var Fingerprint = __webpack_require__(400); +var Signature = __webpack_require__(575); +var PrivateKey = __webpack_require__(502); +var Certificate = __webpack_require__(752); +var Identity = __webpack_require__(378); +var errs = __webpack_require__(753); + +module.exports = { + /* top-level classes */ + Key: Key, + parseKey: Key.parse, + Fingerprint: Fingerprint, + parseFingerprint: Fingerprint.parse, + Signature: Signature, + parseSignature: Signature.parse, + PrivateKey: PrivateKey, + parsePrivateKey: PrivateKey.parse, + generatePrivateKey: PrivateKey.generate, + Certificate: Certificate, + parseCertificate: Certificate.parse, + createSelfSignedCertificate: Certificate.createSelfSigned, + createCertificate: Certificate.create, + Identity: Identity, + identityFromDN: Identity.parseDN, + identityForHost: Identity.forHost, + identityForUser: Identity.forUser, + identityForEmail: Identity.forEmail, + identityFromArray: Identity.fromArray, + + /* errors */ + FingerprintFormatError: errs.FingerprintFormatError, + InvalidAlgorithmError: errs.InvalidAlgorithmError, + KeyParseError: errs.KeyParseError, + SignatureParseError: errs.SignatureParseError, + KeyEncryptedError: errs.KeyEncryptedError, + CertificateParseError: errs.CertificateParseError +}; + + +/***/ }), + +/***/ 653: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_oneOf(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + var $currentBaseId = $it.baseId, + $prevValid = 'prevValid' + $lvl, + $passingSchemas = 'passingSchemas' + $lvl; + out += 'var ' + ($errs) + ' = errors , ' + ($prevValid) + ' = false , ' + ($valid) + ' = false , ' + ($passingSchemas) + ' = null; '; + var $wasComposite = it.compositeRule; + it.compositeRule = $it.compositeRule = true; + var arr1 = $schema; + if (arr1) { + var $sch, $i = -1, + l1 = arr1.length - 1; + while ($i < l1) { + $sch = arr1[$i += 1]; + if ((it.opts.strictKeywords ? typeof $sch == 'object' && Object.keys($sch).length > 0 : it.util.schemaHasRules($sch, it.RULES.all))) { + $it.schema = $sch; + $it.schemaPath = $schemaPath + '[' + $i + ']'; + $it.errSchemaPath = $errSchemaPath + '/' + $i; + out += ' ' + (it.validate($it)) + ' '; + $it.baseId = $currentBaseId; + } else { + out += ' var ' + ($nextValid) + ' = true; '; + } + if ($i) { + out += ' if (' + ($nextValid) + ' && ' + ($prevValid) + ') { ' + ($valid) + ' = false; ' + ($passingSchemas) + ' = [' + ($passingSchemas) + ', ' + ($i) + ']; } else { '; + $closingBraces += '}'; + } + out += ' if (' + ($nextValid) + ') { ' + ($valid) + ' = ' + ($prevValid) + ' = true; ' + ($passingSchemas) + ' = ' + ($i) + '; }'; + } + } + it.compositeRule = $it.compositeRule = $wasComposite; + out += '' + ($closingBraces) + 'if (!' + ($valid) + ') { var err = '; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('oneOf') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { passingSchemas: ' + ($passingSchemas) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should match exactly one schema in oneOf\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + out += '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError(vErrors); '; + } else { + out += ' validate.errors = vErrors; return false; '; + } + } + out += '} else { errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; }'; + if (it.opts.allErrors) { + out += ' } '; + } + return out; +} + + +/***/ }), + +/***/ 658: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +var aws4 = exports, + url = __webpack_require__(835), + querystring = __webpack_require__(191), + crypto = __webpack_require__(417), + lru = __webpack_require__(985), + credentialsCache = lru(1000) + +// http://docs.amazonwebservices.com/general/latest/gr/signature-version-4.html + +function hmac(key, string, encoding) { + return crypto.createHmac('sha256', key).update(string, 'utf8').digest(encoding) +} + +function hash(string, encoding) { + return crypto.createHash('sha256').update(string, 'utf8').digest(encoding) +} + +// This function assumes the string has already been percent encoded +function encodeRfc3986(urlEncodedString) { + return urlEncodedString.replace(/[!'()*]/g, function(c) { + return '%' + c.charCodeAt(0).toString(16).toUpperCase() + }) +} + +function encodeRfc3986Full(str) { + return encodeRfc3986(encodeURIComponent(str)) +} + +// request: { path | body, [host], [method], [headers], [service], [region] } +// credentials: { accessKeyId, secretAccessKey, [sessionToken] } +function RequestSigner(request, credentials) { + + if (typeof request === 'string') request = url.parse(request) + + var headers = request.headers = (request.headers || {}), + hostParts = this.matchHost(request.hostname || request.host || headers.Host || headers.host) + + this.request = request + this.credentials = credentials || this.defaultCredentials() + + this.service = request.service || hostParts[0] || '' + this.region = request.region || hostParts[1] || 'us-east-1' + + // SES uses a different domain from the service name + if (this.service === 'email') this.service = 'ses' + + if (!request.method && request.body) + request.method = 'POST' + + if (!headers.Host && !headers.host) { + headers.Host = request.hostname || request.host || this.createHost() + + // If a port is specified explicitly, use it as is + if (request.port) + headers.Host += ':' + request.port + } + if (!request.hostname && !request.host) + request.hostname = headers.Host || headers.host + + this.isCodeCommitGit = this.service === 'codecommit' && request.method === 'GIT' +} + +RequestSigner.prototype.matchHost = function(host) { + var match = (host || '').match(/([^\.]+)\.(?:([^\.]*)\.)?amazonaws\.com(\.cn)?$/) + var hostParts = (match || []).slice(1, 3) + + // ES's hostParts are sometimes the other way round, if the value that is expected + // to be region equals ‘es’ switch them back + // e.g. search-cluster-name-aaaa00aaaa0aaa0aaaaaaa0aaa.us-east-1.es.amazonaws.com + if (hostParts[1] === 'es') + hostParts = hostParts.reverse() + + return hostParts +} + +// http://docs.aws.amazon.com/general/latest/gr/rande.html +RequestSigner.prototype.isSingleRegion = function() { + // Special case for S3 and SimpleDB in us-east-1 + if (['s3', 'sdb'].indexOf(this.service) >= 0 && this.region === 'us-east-1') return true + + return ['cloudfront', 'ls', 'route53', 'iam', 'importexport', 'sts'] + .indexOf(this.service) >= 0 +} + +RequestSigner.prototype.createHost = function() { + var region = this.isSingleRegion() ? '' : + (this.service === 's3' && this.region !== 'us-east-1' ? '-' : '.') + this.region, + service = this.service === 'ses' ? 'email' : this.service + return service + region + '.amazonaws.com' +} + +RequestSigner.prototype.prepareRequest = function() { + this.parsePath() + + var request = this.request, headers = request.headers, query + + if (request.signQuery) { + + this.parsedPath.query = query = this.parsedPath.query || {} + + if (this.credentials.sessionToken) + query['X-Amz-Security-Token'] = this.credentials.sessionToken + + if (this.service === 's3' && !query['X-Amz-Expires']) + query['X-Amz-Expires'] = 86400 + + if (query['X-Amz-Date']) + this.datetime = query['X-Amz-Date'] + else + query['X-Amz-Date'] = this.getDateTime() + + query['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256' + query['X-Amz-Credential'] = this.credentials.accessKeyId + '/' + this.credentialString() + query['X-Amz-SignedHeaders'] = this.signedHeaders() + + } else { + + if (!request.doNotModifyHeaders && !this.isCodeCommitGit) { + if (request.body && !headers['Content-Type'] && !headers['content-type']) + headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8' + + if (request.body && !headers['Content-Length'] && !headers['content-length']) + headers['Content-Length'] = Buffer.byteLength(request.body) + + if (this.credentials.sessionToken && !headers['X-Amz-Security-Token'] && !headers['x-amz-security-token']) + headers['X-Amz-Security-Token'] = this.credentials.sessionToken + + if (this.service === 's3' && !headers['X-Amz-Content-Sha256'] && !headers['x-amz-content-sha256']) + headers['X-Amz-Content-Sha256'] = hash(this.request.body || '', 'hex') + + if (headers['X-Amz-Date'] || headers['x-amz-date']) + this.datetime = headers['X-Amz-Date'] || headers['x-amz-date'] + else + headers['X-Amz-Date'] = this.getDateTime() + } + + delete headers.Authorization + delete headers.authorization + } +} + +RequestSigner.prototype.sign = function() { + if (!this.parsedPath) this.prepareRequest() + + if (this.request.signQuery) { + this.parsedPath.query['X-Amz-Signature'] = this.signature() + } else { + this.request.headers.Authorization = this.authHeader() + } + + this.request.path = this.formatPath() + + return this.request +} + +RequestSigner.prototype.getDateTime = function() { + if (!this.datetime) { + var headers = this.request.headers, + date = new Date(headers.Date || headers.date || new Date) + + this.datetime = date.toISOString().replace(/[:\-]|\.\d{3}/g, '') + + // Remove the trailing 'Z' on the timestamp string for CodeCommit git access + if (this.isCodeCommitGit) this.datetime = this.datetime.slice(0, -1) + } + return this.datetime +} + +RequestSigner.prototype.getDate = function() { + return this.getDateTime().substr(0, 8) +} + +RequestSigner.prototype.authHeader = function() { + return [ + 'AWS4-HMAC-SHA256 Credential=' + this.credentials.accessKeyId + '/' + this.credentialString(), + 'SignedHeaders=' + this.signedHeaders(), + 'Signature=' + this.signature(), + ].join(', ') +} + +RequestSigner.prototype.signature = function() { + var date = this.getDate(), + cacheKey = [this.credentials.secretAccessKey, date, this.region, this.service].join(), + kDate, kRegion, kService, kCredentials = credentialsCache.get(cacheKey) + if (!kCredentials) { + kDate = hmac('AWS4' + this.credentials.secretAccessKey, date) + kRegion = hmac(kDate, this.region) + kService = hmac(kRegion, this.service) + kCredentials = hmac(kService, 'aws4_request') + credentialsCache.set(cacheKey, kCredentials) + } + return hmac(kCredentials, this.stringToSign(), 'hex') +} + +RequestSigner.prototype.stringToSign = function() { + return [ + 'AWS4-HMAC-SHA256', + this.getDateTime(), + this.credentialString(), + hash(this.canonicalString(), 'hex'), + ].join('\n') +} + +RequestSigner.prototype.canonicalString = function() { + if (!this.parsedPath) this.prepareRequest() + + var pathStr = this.parsedPath.path, + query = this.parsedPath.query, + headers = this.request.headers, + queryStr = '', + normalizePath = this.service !== 's3', + decodePath = this.service === 's3' || this.request.doNotEncodePath, + decodeSlashesInPath = this.service === 's3', + firstValOnly = this.service === 's3', + bodyHash + + if (this.service === 's3' && this.request.signQuery) { + bodyHash = 'UNSIGNED-PAYLOAD' + } else if (this.isCodeCommitGit) { + bodyHash = '' + } else { + bodyHash = headers['X-Amz-Content-Sha256'] || headers['x-amz-content-sha256'] || + hash(this.request.body || '', 'hex') + } + + if (query) { + var reducedQuery = Object.keys(query).reduce(function(obj, key) { + if (!key) return obj + obj[encodeRfc3986Full(key)] = !Array.isArray(query[key]) ? query[key] : + (firstValOnly ? query[key][0] : query[key]) + return obj + }, {}) + var encodedQueryPieces = [] + Object.keys(reducedQuery).sort().forEach(function(key) { + if (!Array.isArray(reducedQuery[key])) { + encodedQueryPieces.push(key + '=' + encodeRfc3986Full(reducedQuery[key])) + } else { + reducedQuery[key].map(encodeRfc3986Full).sort() + .forEach(function(val) { encodedQueryPieces.push(key + '=' + val) }) + } + }) + queryStr = encodedQueryPieces.join('&') + } + if (pathStr !== '/') { + if (normalizePath) pathStr = pathStr.replace(/\/{2,}/g, '/') + pathStr = pathStr.split('/').reduce(function(path, piece) { + if (normalizePath && piece === '..') { + path.pop() + } else if (!normalizePath || piece !== '.') { + if (decodePath) piece = decodeURIComponent(piece).replace(/\+/g, ' ') + path.push(encodeRfc3986Full(piece)) + } + return path + }, []).join('/') + if (pathStr[0] !== '/') pathStr = '/' + pathStr + if (decodeSlashesInPath) pathStr = pathStr.replace(/%2F/g, '/') + } + + return [ + this.request.method || 'GET', + pathStr, + queryStr, + this.canonicalHeaders() + '\n', + this.signedHeaders(), + bodyHash, + ].join('\n') +} + +RequestSigner.prototype.canonicalHeaders = function() { + var headers = this.request.headers + function trimAll(header) { + return header.toString().trim().replace(/\s+/g, ' ') + } + return Object.keys(headers) + .sort(function(a, b) { return a.toLowerCase() < b.toLowerCase() ? -1 : 1 }) + .map(function(key) { return key.toLowerCase() + ':' + trimAll(headers[key]) }) + .join('\n') +} + +RequestSigner.prototype.signedHeaders = function() { + return Object.keys(this.request.headers) + .map(function(key) { return key.toLowerCase() }) + .sort() + .join(';') +} + +RequestSigner.prototype.credentialString = function() { + return [ + this.getDate(), + this.region, + this.service, + 'aws4_request', + ].join('/') +} + +RequestSigner.prototype.defaultCredentials = function() { + var env = process.env + return { + accessKeyId: env.AWS_ACCESS_KEY_ID || env.AWS_ACCESS_KEY, + secretAccessKey: env.AWS_SECRET_ACCESS_KEY || env.AWS_SECRET_KEY, + sessionToken: env.AWS_SESSION_TOKEN, + } +} + +RequestSigner.prototype.parsePath = function() { + var path = this.request.path || '/' + + // S3 doesn't always encode characters > 127 correctly and + // all services don't encode characters > 255 correctly + // So if there are non-reserved chars (and it's not already all % encoded), just encode them all + if (/[^0-9A-Za-z;,/?:@&=+$\-_.!~*'()#%]/.test(path)) { + path = encodeURI(decodeURI(path)) + } + + var queryIx = path.indexOf('?'), + query = null + + if (queryIx >= 0) { + query = querystring.parse(path.slice(queryIx + 1)) + path = path.slice(0, queryIx) + } + + this.parsedPath = { + path: path, + query: query, + } +} + +RequestSigner.prototype.formatPath = function() { + var path = this.parsedPath.path, + query = this.parsedPath.query + + if (!query) return path + + // Services don't support empty query string keys + if (query[''] != null) delete query[''] + + return path + '?' + encodeRfc3986(querystring.stringify(query)) +} + +aws4.RequestSigner = RequestSigner + +aws4.sign = function(request, credentials) { + return new RequestSigner(request, credentials).sign() +} + + +/***/ }), + +/***/ 662: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_const(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + if (!$isData) { + out += ' var schema' + ($lvl) + ' = validate.schema' + ($schemaPath) + ';'; + } + out += 'var ' + ($valid) + ' = equal(' + ($data) + ', schema' + ($lvl) + '); if (!' + ($valid) + ') { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('const') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { allowedValue: schema' + ($lvl) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should be equal to constant\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' }'; + if ($breakOnError) { + out += ' else { '; + } + return out; +} + + +/***/ }), + +/***/ 669: +/***/ (function(module) { + +module.exports = require("util"); + +/***/ }), + +/***/ 671: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +module.exports = { + afterRequest: __webpack_require__(672), + beforeRequest: __webpack_require__(616), + browser: __webpack_require__(222), + cache: __webpack_require__(993), + content: __webpack_require__(162), + cookie: __webpack_require__(326), + creator: __webpack_require__(776), + entry: __webpack_require__(919), + har: __webpack_require__(41), + header: __webpack_require__(883), + log: __webpack_require__(319), + page: __webpack_require__(744), + pageTimings: __webpack_require__(181), + postData: __webpack_require__(740), + query: __webpack_require__(813), + request: __webpack_require__(380), + response: __webpack_require__(226), + timings: __webpack_require__(758) +} + + +/***/ }), + +/***/ 672: +/***/ (function(module) { + +module.exports = {"$id":"afterRequest.json#","$schema":"http://json-schema.org/draft-06/schema#","type":"object","optional":true,"required":["lastAccess","eTag","hitCount"],"properties":{"expires":{"type":"string","pattern":"^(\\d{4})(-)?(\\d\\d)(-)?(\\d\\d)(T)?(\\d\\d)(:)?(\\d\\d)(:)?(\\d\\d)(\\.\\d+)?(Z|([+-])(\\d\\d)(:)?(\\d\\d))?"},"lastAccess":{"type":"string","pattern":"^(\\d{4})(-)?(\\d\\d)(-)?(\\d\\d)(T)?(\\d\\d)(:)?(\\d\\d)(:)?(\\d\\d)(\\.\\d+)?(Z|([+-])(\\d\\d)(:)?(\\d\\d))?"},"eTag":{"type":"string"},"hitCount":{"type":"integer"},"comment":{"type":"string"}}}; + +/***/ }), + +/***/ 673: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_not(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + $it.level++; + var $nextValid = 'valid' + $it.level; + if ((it.opts.strictKeywords ? typeof $schema == 'object' && Object.keys($schema).length > 0 : it.util.schemaHasRules($schema, it.RULES.all))) { + $it.schema = $schema; + $it.schemaPath = $schemaPath; + $it.errSchemaPath = $errSchemaPath; + out += ' var ' + ($errs) + ' = errors; '; + var $wasComposite = it.compositeRule; + it.compositeRule = $it.compositeRule = true; + $it.createErrors = false; + var $allErrorsOption; + if ($it.opts.allErrors) { + $allErrorsOption = $it.opts.allErrors; + $it.opts.allErrors = false; + } + out += ' ' + (it.validate($it)) + ' '; + $it.createErrors = true; + if ($allErrorsOption) $it.opts.allErrors = $allErrorsOption; + it.compositeRule = $it.compositeRule = $wasComposite; + out += ' if (' + ($nextValid) + ') { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('not') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT be valid\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } else { errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; } '; + if (it.opts.allErrors) { + out += ' } '; + } + } else { + out += ' var err = '; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('not') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT be valid\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + out += '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + if ($breakOnError) { + out += ' if (false) { '; + } + } + return out; +} + + +/***/ }), + +/***/ 680: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2016 Joyent, Inc. + +var x509 = __webpack_require__(866); + +module.exports = { + read: read, + verify: x509.verify, + sign: x509.sign, + write: write +}; + +var assert = __webpack_require__(477); +var asn1 = __webpack_require__(62); +var Buffer = __webpack_require__(215).Buffer; +var algs = __webpack_require__(98); +var utils = __webpack_require__(270); +var Key = __webpack_require__(852); +var PrivateKey = __webpack_require__(502); +var pem = __webpack_require__(268); +var Identity = __webpack_require__(378); +var Signature = __webpack_require__(575); +var Certificate = __webpack_require__(752); + +function read(buf, options) { + if (typeof (buf) !== 'string') { + assert.buffer(buf, 'buf'); + buf = buf.toString('ascii'); + } + + var lines = buf.trim().split(/[\r\n]+/g); + + var m; + var si = -1; + while (!m && si < lines.length) { + m = lines[++si].match(/*JSSTYLED*/ + /[-]+[ ]*BEGIN CERTIFICATE[ ]*[-]+/); + } + assert.ok(m, 'invalid PEM header'); + + var m2; + var ei = lines.length; + while (!m2 && ei > 0) { + m2 = lines[--ei].match(/*JSSTYLED*/ + /[-]+[ ]*END CERTIFICATE[ ]*[-]+/); + } + assert.ok(m2, 'invalid PEM footer'); + + lines = lines.slice(si, ei + 1); + + var headers = {}; + while (true) { + lines = lines.slice(1); + m = lines[0].match(/*JSSTYLED*/ + /^([A-Za-z0-9-]+): (.+)$/); + if (!m) + break; + headers[m[1].toLowerCase()] = m[2]; + } + + /* Chop off the first and last lines */ + lines = lines.slice(0, -1).join(''); + buf = Buffer.from(lines, 'base64'); + + return (x509.read(buf, options)); +} + +function write(cert, options) { + var dbuf = x509.write(cert, options); + + var header = 'CERTIFICATE'; + var tmp = dbuf.toString('base64'); + var len = tmp.length + (tmp.length / 64) + + 18 + 16 + header.length*2 + 10; + var buf = Buffer.alloc(len); + var o = 0; + o += buf.write('-----BEGIN ' + header + '-----\n', o); + for (var i = 0; i < tmp.length; ) { + var limit = i + 64; + if (limit > tmp.length) + limit = tmp.length; + o += buf.write(tmp.slice(i, limit), o); + buf[o++] = 10; + i = limit; + } + o += buf.write('-----END ' + header + '-----\n', o); + + return (buf.slice(0, o)); +} + + +/***/ }), + +/***/ 687: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_format(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + if (it.opts.format === false) { + if ($breakOnError) { + out += ' if (true) { '; + } + return out; + } + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + var $unknownFormats = it.opts.unknownFormats, + $allowUnknown = Array.isArray($unknownFormats); + if ($isData) { + var $format = 'format' + $lvl, + $isObject = 'isObject' + $lvl, + $formatType = 'formatType' + $lvl; + out += ' var ' + ($format) + ' = formats[' + ($schemaValue) + ']; var ' + ($isObject) + ' = typeof ' + ($format) + ' == \'object\' && !(' + ($format) + ' instanceof RegExp) && ' + ($format) + '.validate; var ' + ($formatType) + ' = ' + ($isObject) + ' && ' + ($format) + '.type || \'string\'; if (' + ($isObject) + ') { '; + if (it.async) { + out += ' var async' + ($lvl) + ' = ' + ($format) + '.async; '; + } + out += ' ' + ($format) + ' = ' + ($format) + '.validate; } if ( '; + if ($isData) { + out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'string\') || '; + } + out += ' ('; + if ($unknownFormats != 'ignore') { + out += ' (' + ($schemaValue) + ' && !' + ($format) + ' '; + if ($allowUnknown) { + out += ' && self._opts.unknownFormats.indexOf(' + ($schemaValue) + ') == -1 '; + } + out += ') || '; + } + out += ' (' + ($format) + ' && ' + ($formatType) + ' == \'' + ($ruleType) + '\' && !(typeof ' + ($format) + ' == \'function\' ? '; + if (it.async) { + out += ' (async' + ($lvl) + ' ? await ' + ($format) + '(' + ($data) + ') : ' + ($format) + '(' + ($data) + ')) '; + } else { + out += ' ' + ($format) + '(' + ($data) + ') '; + } + out += ' : ' + ($format) + '.test(' + ($data) + '))))) {'; + } else { + var $format = it.formats[$schema]; + if (!$format) { + if ($unknownFormats == 'ignore') { + it.logger.warn('unknown format "' + $schema + '" ignored in schema at path "' + it.errSchemaPath + '"'); + if ($breakOnError) { + out += ' if (true) { '; + } + return out; + } else if ($allowUnknown && $unknownFormats.indexOf($schema) >= 0) { + if ($breakOnError) { + out += ' if (true) { '; + } + return out; + } else { + throw new Error('unknown format "' + $schema + '" is used in schema at path "' + it.errSchemaPath + '"'); + } + } + var $isObject = typeof $format == 'object' && !($format instanceof RegExp) && $format.validate; + var $formatType = $isObject && $format.type || 'string'; + if ($isObject) { + var $async = $format.async === true; + $format = $format.validate; + } + if ($formatType != $ruleType) { + if ($breakOnError) { + out += ' if (true) { '; + } + return out; + } + if ($async) { + if (!it.async) throw new Error('async format in sync schema'); + var $formatRef = 'formats' + it.util.getProperty($schema) + '.validate'; + out += ' if (!(await ' + ($formatRef) + '(' + ($data) + '))) { '; + } else { + out += ' if (! '; + var $formatRef = 'formats' + it.util.getProperty($schema); + if ($isObject) $formatRef += '.validate'; + if (typeof $format == 'function') { + out += ' ' + ($formatRef) + '(' + ($data) + ') '; + } else { + out += ' ' + ($formatRef) + '.test(' + ($data) + ') '; + } + out += ') { '; + } + } + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('format') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { format: '; + if ($isData) { + out += '' + ($schemaValue); + } else { + out += '' + (it.util.toQuotedString($schema)); + } + out += ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should match format "'; + if ($isData) { + out += '\' + ' + ($schemaValue) + ' + \''; + } else { + out += '' + (it.util.escapeQuotes($schema)); + } + out += '"\' '; + } + if (it.opts.verbose) { + out += ' , schema: '; + if ($isData) { + out += 'validate.schema' + ($schemaPath); + } else { + out += '' + (it.util.toQuotedString($schema)); + } + out += ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } '; + if ($breakOnError) { + out += ' else { '; + } + return out; +} + + +/***/ }), + +/***/ 691: +/***/ (function(module) { + +"use strict"; + + +// https://mathiasbynens.be/notes/javascript-encoding +// https://github.com/bestiejs/punycode.js - punycode.ucs2.decode +module.exports = function ucs2length(str) { + var length = 0 + , len = str.length + , pos = 0 + , value; + while (pos < len) { + length++; + value = str.charCodeAt(pos++); + if (value >= 0xD800 && value <= 0xDBFF && pos < len) { + // high surrogate, and there is a next character + value = str.charCodeAt(pos); + if ((value & 0xFC00) == 0xDC00) pos++; // low surrogate + } + } + return length; +}; + + +/***/ }), + +/***/ 697: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +/* + * extsprintf.js: extended POSIX-style sprintf + */ + +var mod_assert = __webpack_require__(357); +var mod_util = __webpack_require__(669); + +/* + * Public interface + */ +exports.sprintf = jsSprintf; +exports.printf = jsPrintf; +exports.fprintf = jsFprintf; + +/* + * Stripped down version of s[n]printf(3c). We make a best effort to throw an + * exception when given a format string we don't understand, rather than + * ignoring it, so that we won't break existing programs if/when we go implement + * the rest of this. + * + * This implementation currently supports specifying + * - field alignment ('-' flag), + * - zero-pad ('0' flag) + * - always show numeric sign ('+' flag), + * - field width + * - conversions for strings, decimal integers, and floats (numbers). + * - argument size specifiers. These are all accepted but ignored, since + * Javascript has no notion of the physical size of an argument. + * + * Everything else is currently unsupported, most notably precision, unsigned + * numbers, non-decimal numbers, and characters. + */ +function jsSprintf(fmt) +{ + var regex = [ + '([^%]*)', /* normal text */ + '%', /* start of format */ + '([\'\\-+ #0]*?)', /* flags (optional) */ + '([1-9]\\d*)?', /* width (optional) */ + '(\\.([1-9]\\d*))?', /* precision (optional) */ + '[lhjztL]*?', /* length mods (ignored) */ + '([diouxXfFeEgGaAcCsSp%jr])' /* conversion */ + ].join(''); + + var re = new RegExp(regex); + var args = Array.prototype.slice.call(arguments, 1); + var flags, width, precision, conversion; + var left, pad, sign, arg, match; + var ret = ''; + var argn = 1; + + mod_assert.equal('string', typeof (fmt)); + + while ((match = re.exec(fmt)) !== null) { + ret += match[1]; + fmt = fmt.substring(match[0].length); + + flags = match[2] || ''; + width = match[3] || 0; + precision = match[4] || ''; + conversion = match[6]; + left = false; + sign = false; + pad = ' '; + + if (conversion == '%') { + ret += '%'; + continue; + } + + if (args.length === 0) + throw (new Error('too few args to sprintf')); + + arg = args.shift(); + argn++; + + if (flags.match(/[\' #]/)) + throw (new Error( + 'unsupported flags: ' + flags)); + + if (precision.length > 0) + throw (new Error( + 'non-zero precision not supported')); + + if (flags.match(/-/)) + left = true; + + if (flags.match(/0/)) + pad = '0'; + + if (flags.match(/\+/)) + sign = true; + + switch (conversion) { + case 's': + if (arg === undefined || arg === null) + throw (new Error('argument ' + argn + + ': attempted to print undefined or null ' + + 'as a string')); + ret += doPad(pad, width, left, arg.toString()); + break; + + case 'd': + arg = Math.floor(arg); + /*jsl:fallthru*/ + case 'f': + sign = sign && arg > 0 ? '+' : ''; + ret += sign + doPad(pad, width, left, + arg.toString()); + break; + + case 'x': + ret += doPad(pad, width, left, arg.toString(16)); + break; + + case 'j': /* non-standard */ + if (width === 0) + width = 10; + ret += mod_util.inspect(arg, false, width); + break; + + case 'r': /* non-standard */ + ret += dumpException(arg); + break; + + default: + throw (new Error('unsupported conversion: ' + + conversion)); + } + } + + ret += fmt; + return (ret); +} + +function jsPrintf() { + var args = Array.prototype.slice.call(arguments); + args.unshift(process.stdout); + jsFprintf.apply(null, args); +} + +function jsFprintf(stream) { + var args = Array.prototype.slice.call(arguments, 1); + return (stream.write(jsSprintf.apply(this, args))); +} + +function doPad(chr, width, left, str) +{ + var ret = str; + + while (ret.length < width) { + if (left) + ret += chr; + else + ret = chr + ret; + } + + return (ret); +} + +/* + * This function dumps long stack traces for exceptions having a cause() method. + * See node-verror for an example. + */ +function dumpException(ex) +{ + var ret; + + if (!(ex instanceof Error)) + throw (new Error(jsSprintf('invalid type for %%r: %j', ex))); + + /* Note that V8 prepends "ex.stack" with ex.toString(). */ + ret = 'EXCEPTION: ' + ex.constructor.name + ': ' + ex.stack; + + if (ex.cause && typeof (ex.cause) === 'function') { + var cex = ex.cause(); + if (cex) { + ret += '\nCaused by: ' + dumpException(cex); + } + } + + return (ret); +} + + +/***/ }), + +/***/ 701: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; +/*! + * Copyright (c) 2015, Salesforce.com, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Salesforce.com nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +var net = __webpack_require__(631); +var urlParse = __webpack_require__(835).parse; +var util = __webpack_require__(669); +var pubsuffix = __webpack_require__(519); +var Store = __webpack_require__(627).Store; +var MemoryCookieStore = __webpack_require__(349).MemoryCookieStore; +var pathMatch = __webpack_require__(54).pathMatch; +var VERSION = __webpack_require__(459); + +var punycode; +try { + punycode = __webpack_require__(213); +} catch(e) { + console.warn("tough-cookie: can't load punycode; won't use punycode for domain normalization"); +} + +// From RFC6265 S4.1.1 +// note that it excludes \x3B ";" +var COOKIE_OCTETS = /^[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]+$/; + +var CONTROL_CHARS = /[\x00-\x1F]/; + +// From Chromium // '\r', '\n' and '\0' should be treated as a terminator in +// the "relaxed" mode, see: +// https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/parsed_cookie.cc#L60 +var TERMINATORS = ['\n', '\r', '\0']; + +// RFC6265 S4.1.1 defines path value as 'any CHAR except CTLs or ";"' +// Note ';' is \x3B +var PATH_VALUE = /[\x20-\x3A\x3C-\x7E]+/; + +// date-time parsing constants (RFC6265 S5.1.1) + +var DATE_DELIM = /[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]/; + +var MONTH_TO_NUM = { + jan:0, feb:1, mar:2, apr:3, may:4, jun:5, + jul:6, aug:7, sep:8, oct:9, nov:10, dec:11 +}; +var NUM_TO_MONTH = [ + 'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec' +]; +var NUM_TO_DAY = [ + 'Sun','Mon','Tue','Wed','Thu','Fri','Sat' +]; + +var MAX_TIME = 2147483647000; // 31-bit max +var MIN_TIME = 0; // 31-bit min + +/* + * Parses a Natural number (i.e., non-negative integer) with either the + * *DIGIT ( non-digit *OCTET ) + * or + * *DIGIT + * grammar (RFC6265 S5.1.1). + * + * The "trailingOK" boolean controls if the grammar accepts a + * "( non-digit *OCTET )" trailer. + */ +function parseDigits(token, minDigits, maxDigits, trailingOK) { + var count = 0; + while (count < token.length) { + var c = token.charCodeAt(count); + // "non-digit = %x00-2F / %x3A-FF" + if (c <= 0x2F || c >= 0x3A) { + break; + } + count++; + } + + // constrain to a minimum and maximum number of digits. + if (count < minDigits || count > maxDigits) { + return null; + } + + if (!trailingOK && count != token.length) { + return null; + } + + return parseInt(token.substr(0,count), 10); +} + +function parseTime(token) { + var parts = token.split(':'); + var result = [0,0,0]; + + /* RF6256 S5.1.1: + * time = hms-time ( non-digit *OCTET ) + * hms-time = time-field ":" time-field ":" time-field + * time-field = 1*2DIGIT + */ + + if (parts.length !== 3) { + return null; + } + + for (var i = 0; i < 3; i++) { + // "time-field" must be strictly "1*2DIGIT", HOWEVER, "hms-time" can be + // followed by "( non-digit *OCTET )" so therefore the last time-field can + // have a trailer + var trailingOK = (i == 2); + var num = parseDigits(parts[i], 1, 2, trailingOK); + if (num === null) { + return null; + } + result[i] = num; + } + + return result; +} + +function parseMonth(token) { + token = String(token).substr(0,3).toLowerCase(); + var num = MONTH_TO_NUM[token]; + return num >= 0 ? num : null; +} + +/* + * RFC6265 S5.1.1 date parser (see RFC for full grammar) + */ +function parseDate(str) { + if (!str) { + return; + } + + /* RFC6265 S5.1.1: + * 2. Process each date-token sequentially in the order the date-tokens + * appear in the cookie-date + */ + var tokens = str.split(DATE_DELIM); + if (!tokens) { + return; + } + + var hour = null; + var minute = null; + var second = null; + var dayOfMonth = null; + var month = null; + var year = null; + + for (var i=0; i= 70 && year <= 99) { + year += 1900; + } else if (year >= 0 && year <= 69) { + year += 2000; + } + } + } + } + + /* RFC 6265 S5.1.1 + * "5. Abort these steps and fail to parse the cookie-date if: + * * at least one of the found-day-of-month, found-month, found- + * year, or found-time flags is not set, + * * the day-of-month-value is less than 1 or greater than 31, + * * the year-value is less than 1601, + * * the hour-value is greater than 23, + * * the minute-value is greater than 59, or + * * the second-value is greater than 59. + * (Note that leap seconds cannot be represented in this syntax.)" + * + * So, in order as above: + */ + if ( + dayOfMonth === null || month === null || year === null || second === null || + dayOfMonth < 1 || dayOfMonth > 31 || + year < 1601 || + hour > 23 || + minute > 59 || + second > 59 + ) { + return; + } + + return new Date(Date.UTC(year, month, dayOfMonth, hour, minute, second)); +} + +function formatDate(date) { + var d = date.getUTCDate(); d = d >= 10 ? d : '0'+d; + var h = date.getUTCHours(); h = h >= 10 ? h : '0'+h; + var m = date.getUTCMinutes(); m = m >= 10 ? m : '0'+m; + var s = date.getUTCSeconds(); s = s >= 10 ? s : '0'+s; + return NUM_TO_DAY[date.getUTCDay()] + ', ' + + d+' '+ NUM_TO_MONTH[date.getUTCMonth()] +' '+ date.getUTCFullYear() +' '+ + h+':'+m+':'+s+' GMT'; +} + +// S5.1.2 Canonicalized Host Names +function canonicalDomain(str) { + if (str == null) { + return null; + } + str = str.trim().replace(/^\./,''); // S4.1.2.3 & S5.2.3: ignore leading . + + // convert to IDN if any non-ASCII characters + if (punycode && /[^\u0001-\u007f]/.test(str)) { + str = punycode.toASCII(str); + } + + return str.toLowerCase(); +} + +// S5.1.3 Domain Matching +function domainMatch(str, domStr, canonicalize) { + if (str == null || domStr == null) { + return null; + } + if (canonicalize !== false) { + str = canonicalDomain(str); + domStr = canonicalDomain(domStr); + } + + /* + * "The domain string and the string are identical. (Note that both the + * domain string and the string will have been canonicalized to lower case at + * this point)" + */ + if (str == domStr) { + return true; + } + + /* "All of the following [three] conditions hold:" (order adjusted from the RFC) */ + + /* "* The string is a host name (i.e., not an IP address)." */ + if (net.isIP(str)) { + return false; + } + + /* "* The domain string is a suffix of the string" */ + var idx = str.indexOf(domStr); + if (idx <= 0) { + return false; // it's a non-match (-1) or prefix (0) + } + + // e.g "a.b.c".indexOf("b.c") === 2 + // 5 === 3+2 + if (str.length !== domStr.length + idx) { // it's not a suffix + return false; + } + + /* "* The last character of the string that is not included in the domain + * string is a %x2E (".") character." */ + if (str.substr(idx-1,1) !== '.') { + return false; + } + + return true; +} + + +// RFC6265 S5.1.4 Paths and Path-Match + +/* + * "The user agent MUST use an algorithm equivalent to the following algorithm + * to compute the default-path of a cookie:" + * + * Assumption: the path (and not query part or absolute uri) is passed in. + */ +function defaultPath(path) { + // "2. If the uri-path is empty or if the first character of the uri-path is not + // a %x2F ("/") character, output %x2F ("/") and skip the remaining steps. + if (!path || path.substr(0,1) !== "/") { + return "/"; + } + + // "3. If the uri-path contains no more than one %x2F ("/") character, output + // %x2F ("/") and skip the remaining step." + if (path === "/") { + return path; + } + + var rightSlash = path.lastIndexOf("/"); + if (rightSlash === 0) { + return "/"; + } + + // "4. Output the characters of the uri-path from the first character up to, + // but not including, the right-most %x2F ("/")." + return path.slice(0, rightSlash); +} + +function trimTerminator(str) { + for (var t = 0; t < TERMINATORS.length; t++) { + var terminatorIdx = str.indexOf(TERMINATORS[t]); + if (terminatorIdx !== -1) { + str = str.substr(0,terminatorIdx); + } + } + + return str; +} + +function parseCookiePair(cookiePair, looseMode) { + cookiePair = trimTerminator(cookiePair); + + var firstEq = cookiePair.indexOf('='); + if (looseMode) { + if (firstEq === 0) { // '=' is immediately at start + cookiePair = cookiePair.substr(1); + firstEq = cookiePair.indexOf('='); // might still need to split on '=' + } + } else { // non-loose mode + if (firstEq <= 0) { // no '=' or is at start + return; // needs to have non-empty "cookie-name" + } + } + + var cookieName, cookieValue; + if (firstEq <= 0) { + cookieName = ""; + cookieValue = cookiePair.trim(); + } else { + cookieName = cookiePair.substr(0, firstEq).trim(); + cookieValue = cookiePair.substr(firstEq+1).trim(); + } + + if (CONTROL_CHARS.test(cookieName) || CONTROL_CHARS.test(cookieValue)) { + return; + } + + var c = new Cookie(); + c.key = cookieName; + c.value = cookieValue; + return c; +} + +function parse(str, options) { + if (!options || typeof options !== 'object') { + options = {}; + } + str = str.trim(); + + // We use a regex to parse the "name-value-pair" part of S5.2 + var firstSemi = str.indexOf(';'); // S5.2 step 1 + var cookiePair = (firstSemi === -1) ? str : str.substr(0, firstSemi); + var c = parseCookiePair(cookiePair, !!options.loose); + if (!c) { + return; + } + + if (firstSemi === -1) { + return c; + } + + // S5.2.3 "unparsed-attributes consist of the remainder of the set-cookie-string + // (including the %x3B (";") in question)." plus later on in the same section + // "discard the first ";" and trim". + var unparsed = str.slice(firstSemi + 1).trim(); + + // "If the unparsed-attributes string is empty, skip the rest of these + // steps." + if (unparsed.length === 0) { + return c; + } + + /* + * S5.2 says that when looping over the items "[p]rocess the attribute-name + * and attribute-value according to the requirements in the following + * subsections" for every item. Plus, for many of the individual attributes + * in S5.3 it says to use the "attribute-value of the last attribute in the + * cookie-attribute-list". Therefore, in this implementation, we overwrite + * the previous value. + */ + var cookie_avs = unparsed.split(';'); + while (cookie_avs.length) { + var av = cookie_avs.shift().trim(); + if (av.length === 0) { // happens if ";;" appears + continue; + } + var av_sep = av.indexOf('='); + var av_key, av_value; + + if (av_sep === -1) { + av_key = av; + av_value = null; + } else { + av_key = av.substr(0,av_sep); + av_value = av.substr(av_sep+1); + } + + av_key = av_key.trim().toLowerCase(); + + if (av_value) { + av_value = av_value.trim(); + } + + switch(av_key) { + case 'expires': // S5.2.1 + if (av_value) { + var exp = parseDate(av_value); + // "If the attribute-value failed to parse as a cookie date, ignore the + // cookie-av." + if (exp) { + // over and underflow not realistically a concern: V8's getTime() seems to + // store something larger than a 32-bit time_t (even with 32-bit node) + c.expires = exp; + } + } + break; + + case 'max-age': // S5.2.2 + if (av_value) { + // "If the first character of the attribute-value is not a DIGIT or a "-" + // character ...[or]... If the remainder of attribute-value contains a + // non-DIGIT character, ignore the cookie-av." + if (/^-?[0-9]+$/.test(av_value)) { + var delta = parseInt(av_value, 10); + // "If delta-seconds is less than or equal to zero (0), let expiry-time + // be the earliest representable date and time." + c.setMaxAge(delta); + } + } + break; + + case 'domain': // S5.2.3 + // "If the attribute-value is empty, the behavior is undefined. However, + // the user agent SHOULD ignore the cookie-av entirely." + if (av_value) { + // S5.2.3 "Let cookie-domain be the attribute-value without the leading %x2E + // (".") character." + var domain = av_value.trim().replace(/^\./, ''); + if (domain) { + // "Convert the cookie-domain to lower case." + c.domain = domain.toLowerCase(); + } + } + break; + + case 'path': // S5.2.4 + /* + * "If the attribute-value is empty or if the first character of the + * attribute-value is not %x2F ("/"): + * Let cookie-path be the default-path. + * Otherwise: + * Let cookie-path be the attribute-value." + * + * We'll represent the default-path as null since it depends on the + * context of the parsing. + */ + c.path = av_value && av_value[0] === "/" ? av_value : null; + break; + + case 'secure': // S5.2.5 + /* + * "If the attribute-name case-insensitively matches the string "Secure", + * the user agent MUST append an attribute to the cookie-attribute-list + * with an attribute-name of Secure and an empty attribute-value." + */ + c.secure = true; + break; + + case 'httponly': // S5.2.6 -- effectively the same as 'secure' + c.httpOnly = true; + break; + + default: + c.extensions = c.extensions || []; + c.extensions.push(av); + break; + } + } + + return c; +} + +// avoid the V8 deoptimization monster! +function jsonParse(str) { + var obj; + try { + obj = JSON.parse(str); + } catch (e) { + return e; + } + return obj; +} + +function fromJSON(str) { + if (!str) { + return null; + } + + var obj; + if (typeof str === 'string') { + obj = jsonParse(str); + if (obj instanceof Error) { + return null; + } + } else { + // assume it's an Object + obj = str; + } + + var c = new Cookie(); + for (var i=0; i 1) { + var lindex = path.lastIndexOf('/'); + if (lindex === 0) { + break; + } + path = path.substr(0,lindex); + permutations.push(path); + } + permutations.push('/'); + return permutations; +} + +function getCookieContext(url) { + if (url instanceof Object) { + return url; + } + // NOTE: decodeURI will throw on malformed URIs (see GH-32). + // Therefore, we will just skip decoding for such URIs. + try { + url = decodeURI(url); + } + catch(err) { + // Silently swallow error + } + + return urlParse(url); +} + +function Cookie(options) { + options = options || {}; + + Object.keys(options).forEach(function(prop) { + if (Cookie.prototype.hasOwnProperty(prop) && + Cookie.prototype[prop] !== options[prop] && + prop.substr(0,1) !== '_') + { + this[prop] = options[prop]; + } + }, this); + + this.creation = this.creation || new Date(); + + // used to break creation ties in cookieCompare(): + Object.defineProperty(this, 'creationIndex', { + configurable: false, + enumerable: false, // important for assert.deepEqual checks + writable: true, + value: ++Cookie.cookiesCreated + }); +} + +Cookie.cookiesCreated = 0; // incremented each time a cookie is created + +Cookie.parse = parse; +Cookie.fromJSON = fromJSON; + +Cookie.prototype.key = ""; +Cookie.prototype.value = ""; + +// the order in which the RFC has them: +Cookie.prototype.expires = "Infinity"; // coerces to literal Infinity +Cookie.prototype.maxAge = null; // takes precedence over expires for TTL +Cookie.prototype.domain = null; +Cookie.prototype.path = null; +Cookie.prototype.secure = false; +Cookie.prototype.httpOnly = false; +Cookie.prototype.extensions = null; + +// set by the CookieJar: +Cookie.prototype.hostOnly = null; // boolean when set +Cookie.prototype.pathIsDefault = null; // boolean when set +Cookie.prototype.creation = null; // Date when set; defaulted by Cookie.parse +Cookie.prototype.lastAccessed = null; // Date when set +Object.defineProperty(Cookie.prototype, 'creationIndex', { + configurable: true, + enumerable: false, + writable: true, + value: 0 +}); + +Cookie.serializableProperties = Object.keys(Cookie.prototype) + .filter(function(prop) { + return !( + Cookie.prototype[prop] instanceof Function || + prop === 'creationIndex' || + prop.substr(0,1) === '_' + ); + }); + +Cookie.prototype.inspect = function inspect() { + var now = Date.now(); + return 'Cookie="'+this.toString() + + '; hostOnly='+(this.hostOnly != null ? this.hostOnly : '?') + + '; aAge='+(this.lastAccessed ? (now-this.lastAccessed.getTime())+'ms' : '?') + + '; cAge='+(this.creation ? (now-this.creation.getTime())+'ms' : '?') + + '"'; +}; + +// Use the new custom inspection symbol to add the custom inspect function if +// available. +if (util.inspect.custom) { + Cookie.prototype[util.inspect.custom] = Cookie.prototype.inspect; +} + +Cookie.prototype.toJSON = function() { + var obj = {}; + + var props = Cookie.serializableProperties; + for (var i=0; i schema.maxItems){ + addError("There must be a maximum of " + schema.maxItems + " in the array"); + } + }else if(schema.properties || schema.additionalProperties){ + errors.concat(checkObj(value, schema.properties, path, schema.additionalProperties)); + } + if(schema.pattern && typeof value == 'string' && !value.match(schema.pattern)){ + addError("does not match the regex pattern " + schema.pattern); + } + if(schema.maxLength && typeof value == 'string' && value.length > schema.maxLength){ + addError("may only be " + schema.maxLength + " characters long"); + } + if(schema.minLength && typeof value == 'string' && value.length < schema.minLength){ + addError("must be at least " + schema.minLength + " characters long"); + } + if(typeof schema.minimum !== undefined && typeof value == typeof schema.minimum && + schema.minimum > value){ + addError("must have a minimum value of " + schema.minimum); + } + if(typeof schema.maximum !== undefined && typeof value == typeof schema.maximum && + schema.maximum < value){ + addError("must have a maximum value of " + schema.maximum); + } + if(schema['enum']){ + var enumer = schema['enum']; + l = enumer.length; + var found; + for(var j = 0; j < l; j++){ + if(enumer[j]===value){ + found=1; + break; + } + } + if(!found){ + addError("does not have a value in the enumeration " + enumer.join(", ")); + } + } + if(typeof schema.maxDecimal == 'number' && + (value.toString().match(new RegExp("\\.[0-9]{" + (schema.maxDecimal + 1) + ",}")))){ + addError("may only have " + schema.maxDecimal + " digits of decimal places"); + } + } + } + return null; + } + // validate an object against a schema + function checkObj(instance,objTypeDef,path,additionalProp){ + + if(typeof objTypeDef =='object'){ + if(typeof instance != 'object' || instance instanceof Array){ + errors.push({property:path,message:"an object is required"}); + } + + for(var i in objTypeDef){ + if(objTypeDef.hasOwnProperty(i)){ + var value = instance[i]; + // skip _not_ specified properties + if (value === undefined && options.existingOnly) continue; + var propDef = objTypeDef[i]; + // set default + if(value === undefined && propDef["default"]){ + value = instance[i] = propDef["default"]; + } + if(options.coerce && i in instance){ + value = instance[i] = options.coerce(value, propDef); + } + checkProp(value,propDef,path,i); + } + } + } + for(i in instance){ + if(instance.hasOwnProperty(i) && !(i.charAt(0) == '_' && i.charAt(1) == '_') && objTypeDef && !objTypeDef[i] && additionalProp===false){ + if (options.filter) { + delete instance[i]; + continue; + } else { + errors.push({property:path,message:(typeof value) + "The property " + i + + " is not defined in the schema and the schema does not allow additional properties"}); + } + } + var requires = objTypeDef && objTypeDef[i] && objTypeDef[i].requires; + if(requires && !(requires in instance)){ + errors.push({property:path,message:"the presence of the property " + i + " requires that " + requires + " also be present"}); + } + value = instance[i]; + if(additionalProp && (!(objTypeDef && typeof objTypeDef == 'object') || !(i in objTypeDef))){ + if(options.coerce){ + value = instance[i] = options.coerce(value, additionalProp); + } + checkProp(value,additionalProp,path,i); + } + if(!_changing && value && value.$schema){ + errors = errors.concat(checkProp(value,value.$schema,path,i)); + } + } + return errors; + } + if(schema){ + checkProp(instance,schema,'',_changing || ''); + } + if(!_changing && instance && instance.$schema){ + checkProp(instance,instance.$schema,'',''); + } + return {valid:!errors.length,errors:errors}; +}; +exports.mustBeValid = function(result){ + // summary: + // This checks to ensure that the result is valid and will throw an appropriate error message if it is not + // result: the result returned from checkPropertyChange or validate + if(!result.valid){ + throw new TypeError(result.errors.map(function(error){return "for property " + error.property + ': ' + error.message;}).join(", \n")); + } +} + +return exports; +})); + + +/***/ }), + +/***/ 704: +/***/ (function(module, exports) { + +exports = module.exports = stringify +exports.getSerialize = serializer + +function stringify(obj, replacer, spaces, cycleReplacer) { + return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces) +} + +function serializer(replacer, cycleReplacer) { + var stack = [], keys = [] + + if (cycleReplacer == null) cycleReplacer = function(key, value) { + if (stack[0] === value) return "[Circular ~]" + return "[Circular ~." + keys.slice(0, stack.indexOf(value)).join(".") + "]" + } + + return function(key, value) { + if (stack.length > 0) { + var thisPos = stack.indexOf(this) + ~thisPos ? stack.splice(thisPos + 1) : stack.push(this) + ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key) + if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value) + } + else stack.push(value) + + return replacer == null ? value : replacer.call(this, key, value) + } +} + + +/***/ }), + +/***/ 707: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2018 Joyent, Inc. + +module.exports = { + read: read, + readPkcs8: readPkcs8, + write: write, + writePkcs8: writePkcs8, + pkcs8ToBuffer: pkcs8ToBuffer, + + readECDSACurve: readECDSACurve, + writeECDSACurve: writeECDSACurve +}; + +var assert = __webpack_require__(477); +var asn1 = __webpack_require__(62); +var Buffer = __webpack_require__(215).Buffer; +var algs = __webpack_require__(98); +var utils = __webpack_require__(270); +var Key = __webpack_require__(852); +var PrivateKey = __webpack_require__(502); +var pem = __webpack_require__(268); + +function read(buf, options) { + return (pem.read(buf, options, 'pkcs8')); +} + +function write(key, options) { + return (pem.write(key, options, 'pkcs8')); +} + +/* Helper to read in a single mpint */ +function readMPInt(der, nm) { + assert.strictEqual(der.peek(), asn1.Ber.Integer, + nm + ' is not an Integer'); + return (utils.mpNormalize(der.readString(asn1.Ber.Integer, true))); +} + +function readPkcs8(alg, type, der) { + /* Private keys in pkcs#8 format have a weird extra int */ + if (der.peek() === asn1.Ber.Integer) { + assert.strictEqual(type, 'private', + 'unexpected Integer at start of public key'); + der.readString(asn1.Ber.Integer, true); + } + + der.readSequence(); + var next = der.offset + der.length; + + var oid = der.readOID(); + switch (oid) { + case '1.2.840.113549.1.1.1': + der._offset = next; + if (type === 'public') + return (readPkcs8RSAPublic(der)); + else + return (readPkcs8RSAPrivate(der)); + case '1.2.840.10040.4.1': + if (type === 'public') + return (readPkcs8DSAPublic(der)); + else + return (readPkcs8DSAPrivate(der)); + case '1.2.840.10045.2.1': + if (type === 'public') + return (readPkcs8ECDSAPublic(der)); + else + return (readPkcs8ECDSAPrivate(der)); + case '1.3.101.112': + if (type === 'public') { + return (readPkcs8EdDSAPublic(der)); + } else { + return (readPkcs8EdDSAPrivate(der)); + } + case '1.3.101.110': + if (type === 'public') { + return (readPkcs8X25519Public(der)); + } else { + return (readPkcs8X25519Private(der)); + } + default: + throw (new Error('Unknown key type OID ' + oid)); + } +} + +function readPkcs8RSAPublic(der) { + // bit string sequence + der.readSequence(asn1.Ber.BitString); + der.readByte(); + der.readSequence(); + + // modulus + var n = readMPInt(der, 'modulus'); + var e = readMPInt(der, 'exponent'); + + // now, make the key + var key = { + type: 'rsa', + source: der.originalInput, + parts: [ + { name: 'e', data: e }, + { name: 'n', data: n } + ] + }; + + return (new Key(key)); +} + +function readPkcs8RSAPrivate(der) { + der.readSequence(asn1.Ber.OctetString); + der.readSequence(); + + var ver = readMPInt(der, 'version'); + assert.equal(ver[0], 0x0, 'unknown RSA private key version'); + + // modulus then public exponent + var n = readMPInt(der, 'modulus'); + var e = readMPInt(der, 'public exponent'); + var d = readMPInt(der, 'private exponent'); + var p = readMPInt(der, 'prime1'); + var q = readMPInt(der, 'prime2'); + var dmodp = readMPInt(der, 'exponent1'); + var dmodq = readMPInt(der, 'exponent2'); + var iqmp = readMPInt(der, 'iqmp'); + + // now, make the key + var key = { + type: 'rsa', + parts: [ + { name: 'n', data: n }, + { name: 'e', data: e }, + { name: 'd', data: d }, + { name: 'iqmp', data: iqmp }, + { name: 'p', data: p }, + { name: 'q', data: q }, + { name: 'dmodp', data: dmodp }, + { name: 'dmodq', data: dmodq } + ] + }; + + return (new PrivateKey(key)); +} + +function readPkcs8DSAPublic(der) { + der.readSequence(); + + var p = readMPInt(der, 'p'); + var q = readMPInt(der, 'q'); + var g = readMPInt(der, 'g'); + + // bit string sequence + der.readSequence(asn1.Ber.BitString); + der.readByte(); + + var y = readMPInt(der, 'y'); + + // now, make the key + var key = { + type: 'dsa', + parts: [ + { name: 'p', data: p }, + { name: 'q', data: q }, + { name: 'g', data: g }, + { name: 'y', data: y } + ] + }; + + return (new Key(key)); +} + +function readPkcs8DSAPrivate(der) { + der.readSequence(); + + var p = readMPInt(der, 'p'); + var q = readMPInt(der, 'q'); + var g = readMPInt(der, 'g'); + + der.readSequence(asn1.Ber.OctetString); + var x = readMPInt(der, 'x'); + + /* The pkcs#8 format does not include the public key */ + var y = utils.calculateDSAPublic(g, p, x); + + var key = { + type: 'dsa', + parts: [ + { name: 'p', data: p }, + { name: 'q', data: q }, + { name: 'g', data: g }, + { name: 'y', data: y }, + { name: 'x', data: x } + ] + }; + + return (new PrivateKey(key)); +} + +function readECDSACurve(der) { + var curveName, curveNames; + var j, c, cd; + + if (der.peek() === asn1.Ber.OID) { + var oid = der.readOID(); + + curveNames = Object.keys(algs.curves); + for (j = 0; j < curveNames.length; ++j) { + c = curveNames[j]; + cd = algs.curves[c]; + if (cd.pkcs8oid === oid) { + curveName = c; + break; + } + } + + } else { + // ECParameters sequence + der.readSequence(); + var version = der.readString(asn1.Ber.Integer, true); + assert.strictEqual(version[0], 1, 'ECDSA key not version 1'); + + var curve = {}; + + // FieldID sequence + der.readSequence(); + var fieldTypeOid = der.readOID(); + assert.strictEqual(fieldTypeOid, '1.2.840.10045.1.1', + 'ECDSA key is not from a prime-field'); + var p = curve.p = utils.mpNormalize( + der.readString(asn1.Ber.Integer, true)); + /* + * p always starts with a 1 bit, so count the zeros to get its + * real size. + */ + curve.size = p.length * 8 - utils.countZeros(p); + + // Curve sequence + der.readSequence(); + curve.a = utils.mpNormalize( + der.readString(asn1.Ber.OctetString, true)); + curve.b = utils.mpNormalize( + der.readString(asn1.Ber.OctetString, true)); + if (der.peek() === asn1.Ber.BitString) + curve.s = der.readString(asn1.Ber.BitString, true); + + // Combined Gx and Gy + curve.G = der.readString(asn1.Ber.OctetString, true); + assert.strictEqual(curve.G[0], 0x4, + 'uncompressed G is required'); + + curve.n = utils.mpNormalize( + der.readString(asn1.Ber.Integer, true)); + curve.h = utils.mpNormalize( + der.readString(asn1.Ber.Integer, true)); + assert.strictEqual(curve.h[0], 0x1, 'a cofactor=1 curve is ' + + 'required'); + + curveNames = Object.keys(algs.curves); + var ks = Object.keys(curve); + for (j = 0; j < curveNames.length; ++j) { + c = curveNames[j]; + cd = algs.curves[c]; + var equal = true; + for (var i = 0; i < ks.length; ++i) { + var k = ks[i]; + if (cd[k] === undefined) + continue; + if (typeof (cd[k]) === 'object' && + cd[k].equals !== undefined) { + if (!cd[k].equals(curve[k])) { + equal = false; + break; + } + } else if (Buffer.isBuffer(cd[k])) { + if (cd[k].toString('binary') + !== curve[k].toString('binary')) { + equal = false; + break; + } + } else { + if (cd[k] !== curve[k]) { + equal = false; + break; + } + } + } + if (equal) { + curveName = c; + break; + } + } + } + return (curveName); +} + +function readPkcs8ECDSAPrivate(der) { + var curveName = readECDSACurve(der); + assert.string(curveName, 'a known elliptic curve'); + + der.readSequence(asn1.Ber.OctetString); + der.readSequence(); + + var version = readMPInt(der, 'version'); + assert.equal(version[0], 1, 'unknown version of ECDSA key'); + + var d = der.readString(asn1.Ber.OctetString, true); + var Q; + + if (der.peek() == 0xa0) { + der.readSequence(0xa0); + der._offset += der.length; + } + if (der.peek() == 0xa1) { + der.readSequence(0xa1); + Q = der.readString(asn1.Ber.BitString, true); + Q = utils.ecNormalize(Q); + } + + if (Q === undefined) { + var pub = utils.publicFromPrivateECDSA(curveName, d); + Q = pub.part.Q.data; + } + + var key = { + type: 'ecdsa', + parts: [ + { name: 'curve', data: Buffer.from(curveName) }, + { name: 'Q', data: Q }, + { name: 'd', data: d } + ] + }; + + return (new PrivateKey(key)); +} + +function readPkcs8ECDSAPublic(der) { + var curveName = readECDSACurve(der); + assert.string(curveName, 'a known elliptic curve'); + + var Q = der.readString(asn1.Ber.BitString, true); + Q = utils.ecNormalize(Q); + + var key = { + type: 'ecdsa', + parts: [ + { name: 'curve', data: Buffer.from(curveName) }, + { name: 'Q', data: Q } + ] + }; + + return (new Key(key)); +} + +function readPkcs8EdDSAPublic(der) { + if (der.peek() === 0x00) + der.readByte(); + + var A = utils.readBitString(der); + + var key = { + type: 'ed25519', + parts: [ + { name: 'A', data: utils.zeroPadToLength(A, 32) } + ] + }; + + return (new Key(key)); +} + +function readPkcs8X25519Public(der) { + var A = utils.readBitString(der); + + var key = { + type: 'curve25519', + parts: [ + { name: 'A', data: utils.zeroPadToLength(A, 32) } + ] + }; + + return (new Key(key)); +} + +function readPkcs8EdDSAPrivate(der) { + if (der.peek() === 0x00) + der.readByte(); + + der.readSequence(asn1.Ber.OctetString); + var k = der.readString(asn1.Ber.OctetString, true); + k = utils.zeroPadToLength(k, 32); + + var A; + if (der.peek() === asn1.Ber.BitString) { + A = utils.readBitString(der); + A = utils.zeroPadToLength(A, 32); + } else { + A = utils.calculateED25519Public(k); + } + + var key = { + type: 'ed25519', + parts: [ + { name: 'A', data: utils.zeroPadToLength(A, 32) }, + { name: 'k', data: utils.zeroPadToLength(k, 32) } + ] + }; + + return (new PrivateKey(key)); +} + +function readPkcs8X25519Private(der) { + if (der.peek() === 0x00) + der.readByte(); + + der.readSequence(asn1.Ber.OctetString); + var k = der.readString(asn1.Ber.OctetString, true); + k = utils.zeroPadToLength(k, 32); + + var A = utils.calculateX25519Public(k); + + var key = { + type: 'curve25519', + parts: [ + { name: 'A', data: utils.zeroPadToLength(A, 32) }, + { name: 'k', data: utils.zeroPadToLength(k, 32) } + ] + }; + + return (new PrivateKey(key)); +} + +function pkcs8ToBuffer(key) { + var der = new asn1.BerWriter(); + writePkcs8(der, key); + return (der.buffer); +} + +function writePkcs8(der, key) { + der.startSequence(); + + if (PrivateKey.isPrivateKey(key)) { + var sillyInt = Buffer.from([0]); + der.writeBuffer(sillyInt, asn1.Ber.Integer); + } + + der.startSequence(); + switch (key.type) { + case 'rsa': + der.writeOID('1.2.840.113549.1.1.1'); + if (PrivateKey.isPrivateKey(key)) + writePkcs8RSAPrivate(key, der); + else + writePkcs8RSAPublic(key, der); + break; + case 'dsa': + der.writeOID('1.2.840.10040.4.1'); + if (PrivateKey.isPrivateKey(key)) + writePkcs8DSAPrivate(key, der); + else + writePkcs8DSAPublic(key, der); + break; + case 'ecdsa': + der.writeOID('1.2.840.10045.2.1'); + if (PrivateKey.isPrivateKey(key)) + writePkcs8ECDSAPrivate(key, der); + else + writePkcs8ECDSAPublic(key, der); + break; + case 'ed25519': + der.writeOID('1.3.101.112'); + if (PrivateKey.isPrivateKey(key)) + throw (new Error('Ed25519 private keys in pkcs8 ' + + 'format are not supported')); + writePkcs8EdDSAPublic(key, der); + break; + default: + throw (new Error('Unsupported key type: ' + key.type)); + } + + der.endSequence(); +} + +function writePkcs8RSAPrivate(key, der) { + der.writeNull(); + der.endSequence(); + + der.startSequence(asn1.Ber.OctetString); + der.startSequence(); + + var version = Buffer.from([0]); + der.writeBuffer(version, asn1.Ber.Integer); + + der.writeBuffer(key.part.n.data, asn1.Ber.Integer); + der.writeBuffer(key.part.e.data, asn1.Ber.Integer); + der.writeBuffer(key.part.d.data, asn1.Ber.Integer); + der.writeBuffer(key.part.p.data, asn1.Ber.Integer); + der.writeBuffer(key.part.q.data, asn1.Ber.Integer); + if (!key.part.dmodp || !key.part.dmodq) + utils.addRSAMissing(key); + der.writeBuffer(key.part.dmodp.data, asn1.Ber.Integer); + der.writeBuffer(key.part.dmodq.data, asn1.Ber.Integer); + der.writeBuffer(key.part.iqmp.data, asn1.Ber.Integer); + + der.endSequence(); + der.endSequence(); +} + +function writePkcs8RSAPublic(key, der) { + der.writeNull(); + der.endSequence(); + + der.startSequence(asn1.Ber.BitString); + der.writeByte(0x00); + + der.startSequence(); + der.writeBuffer(key.part.n.data, asn1.Ber.Integer); + der.writeBuffer(key.part.e.data, asn1.Ber.Integer); + der.endSequence(); + + der.endSequence(); +} + +function writePkcs8DSAPrivate(key, der) { + der.startSequence(); + der.writeBuffer(key.part.p.data, asn1.Ber.Integer); + der.writeBuffer(key.part.q.data, asn1.Ber.Integer); + der.writeBuffer(key.part.g.data, asn1.Ber.Integer); + der.endSequence(); + + der.endSequence(); + + der.startSequence(asn1.Ber.OctetString); + der.writeBuffer(key.part.x.data, asn1.Ber.Integer); + der.endSequence(); +} + +function writePkcs8DSAPublic(key, der) { + der.startSequence(); + der.writeBuffer(key.part.p.data, asn1.Ber.Integer); + der.writeBuffer(key.part.q.data, asn1.Ber.Integer); + der.writeBuffer(key.part.g.data, asn1.Ber.Integer); + der.endSequence(); + der.endSequence(); + + der.startSequence(asn1.Ber.BitString); + der.writeByte(0x00); + der.writeBuffer(key.part.y.data, asn1.Ber.Integer); + der.endSequence(); +} + +function writeECDSACurve(key, der) { + var curve = algs.curves[key.curve]; + if (curve.pkcs8oid) { + /* This one has a name in pkcs#8, so just write the oid */ + der.writeOID(curve.pkcs8oid); + + } else { + // ECParameters sequence + der.startSequence(); + + var version = Buffer.from([1]); + der.writeBuffer(version, asn1.Ber.Integer); + + // FieldID sequence + der.startSequence(); + der.writeOID('1.2.840.10045.1.1'); // prime-field + der.writeBuffer(curve.p, asn1.Ber.Integer); + der.endSequence(); + + // Curve sequence + der.startSequence(); + var a = curve.p; + if (a[0] === 0x0) + a = a.slice(1); + der.writeBuffer(a, asn1.Ber.OctetString); + der.writeBuffer(curve.b, asn1.Ber.OctetString); + der.writeBuffer(curve.s, asn1.Ber.BitString); + der.endSequence(); + + der.writeBuffer(curve.G, asn1.Ber.OctetString); + der.writeBuffer(curve.n, asn1.Ber.Integer); + var h = curve.h; + if (!h) { + h = Buffer.from([1]); + } + der.writeBuffer(h, asn1.Ber.Integer); + + // ECParameters + der.endSequence(); + } +} + +function writePkcs8ECDSAPublic(key, der) { + writeECDSACurve(key, der); + der.endSequence(); + + var Q = utils.ecNormalize(key.part.Q.data, true); + der.writeBuffer(Q, asn1.Ber.BitString); +} + +function writePkcs8ECDSAPrivate(key, der) { + writeECDSACurve(key, der); + der.endSequence(); + + der.startSequence(asn1.Ber.OctetString); + der.startSequence(); + + var version = Buffer.from([1]); + der.writeBuffer(version, asn1.Ber.Integer); + + der.writeBuffer(key.part.d.data, asn1.Ber.OctetString); + + der.startSequence(0xa1); + var Q = utils.ecNormalize(key.part.Q.data, true); + der.writeBuffer(Q, asn1.Ber.BitString); + der.endSequence(); + + der.endSequence(); + der.endSequence(); +} + +function writePkcs8EdDSAPublic(key, der) { + der.endSequence(); + + utils.writeBitString(der, key.part.A.data); +} + +function writePkcs8EdDSAPrivate(key, der) { + der.endSequence(); + + var k = utils.mpNormalize(key.part.k.data, true); + der.startSequence(asn1.Ber.OctetString); + der.writeBuffer(k, asn1.Ber.OctetString); + der.endSequence(); +} + + +/***/ }), + +/***/ 721: +/***/ (function(module) { + +"use strict"; + + +function formatHostname (hostname) { + // canonicalize the hostname, so that 'oogle.com' won't match 'google.com' + return hostname.replace(/^\.*/, '.').toLowerCase() +} + +function parseNoProxyZone (zone) { + zone = zone.trim().toLowerCase() + + var zoneParts = zone.split(':', 2) + var zoneHost = formatHostname(zoneParts[0]) + var zonePort = zoneParts[1] + var hasPort = zone.indexOf(':') > -1 + + return {hostname: zoneHost, port: zonePort, hasPort: hasPort} +} + +function uriInNoProxy (uri, noProxy) { + var port = uri.port || (uri.protocol === 'https:' ? '443' : '80') + var hostname = formatHostname(uri.hostname) + var noProxyList = noProxy.split(',') + + // iterate through the noProxyList until it finds a match. + return noProxyList.map(parseNoProxyZone).some(function (noProxyZone) { + var isMatchedAt = hostname.indexOf(noProxyZone.hostname) + var hostnameMatched = ( + isMatchedAt > -1 && + (isMatchedAt === hostname.length - noProxyZone.hostname.length) + ) + + if (noProxyZone.hasPort) { + return (port === noProxyZone.port) && hostnameMatched + } + + return hostnameMatched + }) +} + +function getProxyFromURI (uri) { + // Decide the proper request proxy to use based on the request URI object and the + // environmental variables (NO_PROXY, HTTP_PROXY, etc.) + // respect NO_PROXY environment variables (see: https://lynx.invisible-island.net/lynx2.8.7/breakout/lynx_help/keystrokes/environments.html) + + var noProxy = process.env.NO_PROXY || process.env.no_proxy || '' + + // if the noProxy is a wildcard then return null + + if (noProxy === '*') { + return null + } + + // if the noProxy is not empty and the uri is found return null + + if (noProxy !== '' && uriInNoProxy(uri, noProxy)) { + return null + } + + // Check for HTTP or HTTPS Proxy in environment Else default to null + + if (uri.protocol === 'http:') { + return process.env.HTTP_PROXY || + process.env.http_proxy || null + } + + if (uri.protocol === 'https:') { + return process.env.HTTPS_PROXY || + process.env.https_proxy || + process.env.HTTP_PROXY || + process.env.http_proxy || null + } + + // if none of that works, return null + // (What uri protocol are you using then?) + + return null +} + +module.exports = getProxyFromURI + + +/***/ }), + +/***/ 722: +/***/ (function(module) { + +/** + * Convert array of 16 byte values to UUID string format of the form: + * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + */ +var byteToHex = []; +for (var i = 0; i < 256; ++i) { + byteToHex[i] = (i + 0x100).toString(16).substr(1); +} + +function bytesToUuid(buf, offset) { + var i = offset || 0; + var bth = byteToHex; + // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4 + return ([ + bth[buf[i++]], bth[buf[i++]], + bth[buf[i++]], bth[buf[i++]], '-', + bth[buf[i++]], bth[buf[i++]], '-', + bth[buf[i++]], bth[buf[i++]], '-', + bth[buf[i++]], bth[buf[i++]], '-', + bth[buf[i++]], bth[buf[i++]], + bth[buf[i++]], bth[buf[i++]], + bth[buf[i++]], bth[buf[i++]] + ]).join(''); +} + +module.exports = bytesToUuid; + + +/***/ }), + +/***/ 729: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Basic Javascript Elliptic Curve implementation +// Ported loosely from BouncyCastle's Java EC code +// Only Fp curves implemented for now + +// Requires jsbn.js and jsbn2.js +var BigInteger = __webpack_require__(242).BigInteger +var Barrett = BigInteger.prototype.Barrett + +// ---------------- +// ECFieldElementFp + +// constructor +function ECFieldElementFp(q,x) { + this.x = x; + // TODO if(x.compareTo(q) >= 0) error + this.q = q; +} + +function feFpEquals(other) { + if(other == this) return true; + return (this.q.equals(other.q) && this.x.equals(other.x)); +} + +function feFpToBigInteger() { + return this.x; +} + +function feFpNegate() { + return new ECFieldElementFp(this.q, this.x.negate().mod(this.q)); +} + +function feFpAdd(b) { + return new ECFieldElementFp(this.q, this.x.add(b.toBigInteger()).mod(this.q)); +} + +function feFpSubtract(b) { + return new ECFieldElementFp(this.q, this.x.subtract(b.toBigInteger()).mod(this.q)); +} + +function feFpMultiply(b) { + return new ECFieldElementFp(this.q, this.x.multiply(b.toBigInteger()).mod(this.q)); +} + +function feFpSquare() { + return new ECFieldElementFp(this.q, this.x.square().mod(this.q)); +} + +function feFpDivide(b) { + return new ECFieldElementFp(this.q, this.x.multiply(b.toBigInteger().modInverse(this.q)).mod(this.q)); +} + +ECFieldElementFp.prototype.equals = feFpEquals; +ECFieldElementFp.prototype.toBigInteger = feFpToBigInteger; +ECFieldElementFp.prototype.negate = feFpNegate; +ECFieldElementFp.prototype.add = feFpAdd; +ECFieldElementFp.prototype.subtract = feFpSubtract; +ECFieldElementFp.prototype.multiply = feFpMultiply; +ECFieldElementFp.prototype.square = feFpSquare; +ECFieldElementFp.prototype.divide = feFpDivide; + +// ---------------- +// ECPointFp + +// constructor +function ECPointFp(curve,x,y,z) { + this.curve = curve; + this.x = x; + this.y = y; + // Projective coordinates: either zinv == null or z * zinv == 1 + // z and zinv are just BigIntegers, not fieldElements + if(z == null) { + this.z = BigInteger.ONE; + } + else { + this.z = z; + } + this.zinv = null; + //TODO: compression flag +} + +function pointFpGetX() { + if(this.zinv == null) { + this.zinv = this.z.modInverse(this.curve.q); + } + var r = this.x.toBigInteger().multiply(this.zinv); + this.curve.reduce(r); + return this.curve.fromBigInteger(r); +} + +function pointFpGetY() { + if(this.zinv == null) { + this.zinv = this.z.modInverse(this.curve.q); + } + var r = this.y.toBigInteger().multiply(this.zinv); + this.curve.reduce(r); + return this.curve.fromBigInteger(r); +} + +function pointFpEquals(other) { + if(other == this) return true; + if(this.isInfinity()) return other.isInfinity(); + if(other.isInfinity()) return this.isInfinity(); + var u, v; + // u = Y2 * Z1 - Y1 * Z2 + u = other.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(other.z)).mod(this.curve.q); + if(!u.equals(BigInteger.ZERO)) return false; + // v = X2 * Z1 - X1 * Z2 + v = other.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(other.z)).mod(this.curve.q); + return v.equals(BigInteger.ZERO); +} + +function pointFpIsInfinity() { + if((this.x == null) && (this.y == null)) return true; + return this.z.equals(BigInteger.ZERO) && !this.y.toBigInteger().equals(BigInteger.ZERO); +} + +function pointFpNegate() { + return new ECPointFp(this.curve, this.x, this.y.negate(), this.z); +} + +function pointFpAdd(b) { + if(this.isInfinity()) return b; + if(b.isInfinity()) return this; + + // u = Y2 * Z1 - Y1 * Z2 + var u = b.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(b.z)).mod(this.curve.q); + // v = X2 * Z1 - X1 * Z2 + var v = b.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(b.z)).mod(this.curve.q); + + if(BigInteger.ZERO.equals(v)) { + if(BigInteger.ZERO.equals(u)) { + return this.twice(); // this == b, so double + } + return this.curve.getInfinity(); // this = -b, so infinity + } + + var THREE = new BigInteger("3"); + var x1 = this.x.toBigInteger(); + var y1 = this.y.toBigInteger(); + var x2 = b.x.toBigInteger(); + var y2 = b.y.toBigInteger(); + + var v2 = v.square(); + var v3 = v2.multiply(v); + var x1v2 = x1.multiply(v2); + var zu2 = u.square().multiply(this.z); + + // x3 = v * (z2 * (z1 * u^2 - 2 * x1 * v^2) - v^3) + var x3 = zu2.subtract(x1v2.shiftLeft(1)).multiply(b.z).subtract(v3).multiply(v).mod(this.curve.q); + // y3 = z2 * (3 * x1 * u * v^2 - y1 * v^3 - z1 * u^3) + u * v^3 + var y3 = x1v2.multiply(THREE).multiply(u).subtract(y1.multiply(v3)).subtract(zu2.multiply(u)).multiply(b.z).add(u.multiply(v3)).mod(this.curve.q); + // z3 = v^3 * z1 * z2 + var z3 = v3.multiply(this.z).multiply(b.z).mod(this.curve.q); + + return new ECPointFp(this.curve, this.curve.fromBigInteger(x3), this.curve.fromBigInteger(y3), z3); +} + +function pointFpTwice() { + if(this.isInfinity()) return this; + if(this.y.toBigInteger().signum() == 0) return this.curve.getInfinity(); + + // TODO: optimized handling of constants + var THREE = new BigInteger("3"); + var x1 = this.x.toBigInteger(); + var y1 = this.y.toBigInteger(); + + var y1z1 = y1.multiply(this.z); + var y1sqz1 = y1z1.multiply(y1).mod(this.curve.q); + var a = this.curve.a.toBigInteger(); + + // w = 3 * x1^2 + a * z1^2 + var w = x1.square().multiply(THREE); + if(!BigInteger.ZERO.equals(a)) { + w = w.add(this.z.square().multiply(a)); + } + w = w.mod(this.curve.q); + //this.curve.reduce(w); + // x3 = 2 * y1 * z1 * (w^2 - 8 * x1 * y1^2 * z1) + var x3 = w.square().subtract(x1.shiftLeft(3).multiply(y1sqz1)).shiftLeft(1).multiply(y1z1).mod(this.curve.q); + // y3 = 4 * y1^2 * z1 * (3 * w * x1 - 2 * y1^2 * z1) - w^3 + var y3 = w.multiply(THREE).multiply(x1).subtract(y1sqz1.shiftLeft(1)).shiftLeft(2).multiply(y1sqz1).subtract(w.square().multiply(w)).mod(this.curve.q); + // z3 = 8 * (y1 * z1)^3 + var z3 = y1z1.square().multiply(y1z1).shiftLeft(3).mod(this.curve.q); + + return new ECPointFp(this.curve, this.curve.fromBigInteger(x3), this.curve.fromBigInteger(y3), z3); +} + +// Simple NAF (Non-Adjacent Form) multiplication algorithm +// TODO: modularize the multiplication algorithm +function pointFpMultiply(k) { + if(this.isInfinity()) return this; + if(k.signum() == 0) return this.curve.getInfinity(); + + var e = k; + var h = e.multiply(new BigInteger("3")); + + var neg = this.negate(); + var R = this; + + var i; + for(i = h.bitLength() - 2; i > 0; --i) { + R = R.twice(); + + var hBit = h.testBit(i); + var eBit = e.testBit(i); + + if (hBit != eBit) { + R = R.add(hBit ? this : neg); + } + } + + return R; +} + +// Compute this*j + x*k (simultaneous multiplication) +function pointFpMultiplyTwo(j,x,k) { + var i; + if(j.bitLength() > k.bitLength()) + i = j.bitLength() - 1; + else + i = k.bitLength() - 1; + + var R = this.curve.getInfinity(); + var both = this.add(x); + while(i >= 0) { + R = R.twice(); + if(j.testBit(i)) { + if(k.testBit(i)) { + R = R.add(both); + } + else { + R = R.add(this); + } + } + else { + if(k.testBit(i)) { + R = R.add(x); + } + } + --i; + } + + return R; +} + +ECPointFp.prototype.getX = pointFpGetX; +ECPointFp.prototype.getY = pointFpGetY; +ECPointFp.prototype.equals = pointFpEquals; +ECPointFp.prototype.isInfinity = pointFpIsInfinity; +ECPointFp.prototype.negate = pointFpNegate; +ECPointFp.prototype.add = pointFpAdd; +ECPointFp.prototype.twice = pointFpTwice; +ECPointFp.prototype.multiply = pointFpMultiply; +ECPointFp.prototype.multiplyTwo = pointFpMultiplyTwo; + +// ---------------- +// ECCurveFp + +// constructor +function ECCurveFp(q,a,b) { + this.q = q; + this.a = this.fromBigInteger(a); + this.b = this.fromBigInteger(b); + this.infinity = new ECPointFp(this, null, null); + this.reducer = new Barrett(this.q); +} + +function curveFpGetQ() { + return this.q; +} + +function curveFpGetA() { + return this.a; +} + +function curveFpGetB() { + return this.b; +} + +function curveFpEquals(other) { + if(other == this) return true; + return(this.q.equals(other.q) && this.a.equals(other.a) && this.b.equals(other.b)); +} + +function curveFpGetInfinity() { + return this.infinity; +} + +function curveFpFromBigInteger(x) { + return new ECFieldElementFp(this.q, x); +} + +function curveReduce(x) { + this.reducer.reduce(x); +} + +// for now, work with hex strings because they're easier in JS +function curveFpDecodePointHex(s) { + switch(parseInt(s.substr(0,2), 16)) { // first byte + case 0: + return this.infinity; + case 2: + case 3: + // point compression not supported yet + return null; + case 4: + case 6: + case 7: + var len = (s.length - 2) / 2; + var xHex = s.substr(2, len); + var yHex = s.substr(len+2, len); + + return new ECPointFp(this, + this.fromBigInteger(new BigInteger(xHex, 16)), + this.fromBigInteger(new BigInteger(yHex, 16))); + + default: // unsupported + return null; + } +} + +function curveFpEncodePointHex(p) { + if (p.isInfinity()) return "00"; + var xHex = p.getX().toBigInteger().toString(16); + var yHex = p.getY().toBigInteger().toString(16); + var oLen = this.getQ().toString(16).length; + if ((oLen % 2) != 0) oLen++; + while (xHex.length < oLen) { + xHex = "0" + xHex; + } + while (yHex.length < oLen) { + yHex = "0" + yHex; + } + return "04" + xHex + yHex; +} + +ECCurveFp.prototype.getQ = curveFpGetQ; +ECCurveFp.prototype.getA = curveFpGetA; +ECCurveFp.prototype.getB = curveFpGetB; +ECCurveFp.prototype.equals = curveFpEquals; +ECCurveFp.prototype.getInfinity = curveFpGetInfinity; +ECCurveFp.prototype.fromBigInteger = curveFpFromBigInteger; +ECCurveFp.prototype.reduce = curveReduce; +//ECCurveFp.prototype.decodePointHex = curveFpDecodePointHex; +ECCurveFp.prototype.encodePointHex = curveFpEncodePointHex; + +// from: https://github.com/kaielvin/jsbn-ec-point-compression +ECCurveFp.prototype.decodePointHex = function(s) +{ + var yIsEven; + switch(parseInt(s.substr(0,2), 16)) { // first byte + case 0: + return this.infinity; + case 2: + yIsEven = false; + case 3: + if(yIsEven == undefined) yIsEven = true; + var len = s.length - 2; + var xHex = s.substr(2, len); + var x = this.fromBigInteger(new BigInteger(xHex,16)); + var alpha = x.multiply(x.square().add(this.getA())).add(this.getB()); + var beta = alpha.sqrt(); + + if (beta == null) throw "Invalid point compression"; + + var betaValue = beta.toBigInteger(); + if (betaValue.testBit(0) != yIsEven) + { + // Use the other root + beta = this.fromBigInteger(this.getQ().subtract(betaValue)); + } + return new ECPointFp(this,x,beta); + case 4: + case 6: + case 7: + var len = (s.length - 2) / 2; + var xHex = s.substr(2, len); + var yHex = s.substr(len+2, len); + + return new ECPointFp(this, + this.fromBigInteger(new BigInteger(xHex, 16)), + this.fromBigInteger(new BigInteger(yHex, 16))); + + default: // unsupported + return null; + } +} +ECCurveFp.prototype.encodeCompressedPointHex = function(p) +{ + if (p.isInfinity()) return "00"; + var xHex = p.getX().toBigInteger().toString(16); + var oLen = this.getQ().toString(16).length; + if ((oLen % 2) != 0) oLen++; + while (xHex.length < oLen) + xHex = "0" + xHex; + var yPrefix; + if(p.getY().toBigInteger().isEven()) yPrefix = "02"; + else yPrefix = "03"; + + return yPrefix + xHex; +} + + +ECFieldElementFp.prototype.getR = function() +{ + if(this.r != undefined) return this.r; + + this.r = null; + var bitLength = this.q.bitLength(); + if (bitLength > 128) + { + var firstWord = this.q.shiftRight(bitLength - 64); + if (firstWord.intValue() == -1) + { + this.r = BigInteger.ONE.shiftLeft(bitLength).subtract(this.q); + } + } + return this.r; +} +ECFieldElementFp.prototype.modMult = function(x1,x2) +{ + return this.modReduce(x1.multiply(x2)); +} +ECFieldElementFp.prototype.modReduce = function(x) +{ + if (this.getR() != null) + { + var qLen = q.bitLength(); + while (x.bitLength() > (qLen + 1)) + { + var u = x.shiftRight(qLen); + var v = x.subtract(u.shiftLeft(qLen)); + if (!this.getR().equals(BigInteger.ONE)) + { + u = u.multiply(this.getR()); + } + x = u.add(v); + } + while (x.compareTo(q) >= 0) + { + x = x.subtract(q); + } + } + else + { + x = x.mod(q); + } + return x; +} +ECFieldElementFp.prototype.sqrt = function() +{ + if (!this.q.testBit(0)) throw "unsupported"; + + // p mod 4 == 3 + if (this.q.testBit(1)) + { + var z = new ECFieldElementFp(this.q,this.x.modPow(this.q.shiftRight(2).add(BigInteger.ONE),this.q)); + return z.square().equals(this) ? z : null; + } + + // p mod 4 == 1 + var qMinusOne = this.q.subtract(BigInteger.ONE); + + var legendreExponent = qMinusOne.shiftRight(1); + if (!(this.x.modPow(legendreExponent, this.q).equals(BigInteger.ONE))) + { + return null; + } + + var u = qMinusOne.shiftRight(2); + var k = u.shiftLeft(1).add(BigInteger.ONE); + + var Q = this.x; + var fourQ = modDouble(modDouble(Q)); + + var U, V; + do + { + var P; + do + { + P = new BigInteger(this.q.bitLength(), new SecureRandom()); + } + while (P.compareTo(this.q) >= 0 + || !(P.multiply(P).subtract(fourQ).modPow(legendreExponent, this.q).equals(qMinusOne))); + + var result = this.lucasSequence(P, Q, k); + U = result[0]; + V = result[1]; + + if (this.modMult(V, V).equals(fourQ)) + { + // Integer division by 2, mod q + if (V.testBit(0)) + { + V = V.add(q); + } + + V = V.shiftRight(1); + + return new ECFieldElementFp(q,V); + } + } + while (U.equals(BigInteger.ONE) || U.equals(qMinusOne)); + + return null; +} +ECFieldElementFp.prototype.lucasSequence = function(P,Q,k) +{ + var n = k.bitLength(); + var s = k.getLowestSetBit(); + + var Uh = BigInteger.ONE; + var Vl = BigInteger.TWO; + var Vh = P; + var Ql = BigInteger.ONE; + var Qh = BigInteger.ONE; + + for (var j = n - 1; j >= s + 1; --j) + { + Ql = this.modMult(Ql, Qh); + + if (k.testBit(j)) + { + Qh = this.modMult(Ql, Q); + Uh = this.modMult(Uh, Vh); + Vl = this.modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql))); + Vh = this.modReduce(Vh.multiply(Vh).subtract(Qh.shiftLeft(1))); + } + else + { + Qh = Ql; + Uh = this.modReduce(Uh.multiply(Vl).subtract(Ql)); + Vh = this.modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql))); + Vl = this.modReduce(Vl.multiply(Vl).subtract(Ql.shiftLeft(1))); + } + } + + Ql = this.modMult(Ql, Qh); + Qh = this.modMult(Ql, Q); + Uh = this.modReduce(Uh.multiply(Vl).subtract(Ql)); + Vl = this.modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql))); + Ql = this.modMult(Ql, Qh); + + for (var j = 1; j <= s; ++j) + { + Uh = this.modMult(Uh, Vl); + Vl = this.modReduce(Vl.multiply(Vl).subtract(Ql.shiftLeft(1))); + Ql = this.modMult(Ql, Ql); + } + + return [ Uh, Vl ]; +} + +var exports = { + ECCurveFp: ECCurveFp, + ECPointFp: ECPointFp, + ECFieldElementFp: ECFieldElementFp +} + +module.exports = exports + + +/***/ }), + +/***/ 733: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2011 Mark Cavage All rights reserved. + +var assert = __webpack_require__(357); +var Buffer = __webpack_require__(215).Buffer; + +var ASN1 = __webpack_require__(362); +var errors = __webpack_require__(584); + + +// --- Globals + +var newInvalidAsn1Error = errors.newInvalidAsn1Error; + + + +// --- API + +function Reader(data) { + if (!data || !Buffer.isBuffer(data)) + throw new TypeError('data must be a node Buffer'); + + this._buf = data; + this._size = data.length; + + // These hold the "current" state + this._len = 0; + this._offset = 0; +} + +Object.defineProperty(Reader.prototype, 'length', { + enumerable: true, + get: function () { return (this._len); } +}); + +Object.defineProperty(Reader.prototype, 'offset', { + enumerable: true, + get: function () { return (this._offset); } +}); + +Object.defineProperty(Reader.prototype, 'remain', { + get: function () { return (this._size - this._offset); } +}); + +Object.defineProperty(Reader.prototype, 'buffer', { + get: function () { return (this._buf.slice(this._offset)); } +}); + + +/** + * Reads a single byte and advances offset; you can pass in `true` to make this + * a "peek" operation (i.e., get the byte, but don't advance the offset). + * + * @param {Boolean} peek true means don't move offset. + * @return {Number} the next byte, null if not enough data. + */ +Reader.prototype.readByte = function (peek) { + if (this._size - this._offset < 1) + return null; + + var b = this._buf[this._offset] & 0xff; + + if (!peek) + this._offset += 1; + + return b; +}; + + +Reader.prototype.peek = function () { + return this.readByte(true); +}; + + +/** + * Reads a (potentially) variable length off the BER buffer. This call is + * not really meant to be called directly, as callers have to manipulate + * the internal buffer afterwards. + * + * As a result of this call, you can call `Reader.length`, until the + * next thing called that does a readLength. + * + * @return {Number} the amount of offset to advance the buffer. + * @throws {InvalidAsn1Error} on bad ASN.1 + */ +Reader.prototype.readLength = function (offset) { + if (offset === undefined) + offset = this._offset; + + if (offset >= this._size) + return null; + + var lenB = this._buf[offset++] & 0xff; + if (lenB === null) + return null; + + if ((lenB & 0x80) === 0x80) { + lenB &= 0x7f; + + if (lenB === 0) + throw newInvalidAsn1Error('Indefinite length not supported'); + + if (lenB > 4) + throw newInvalidAsn1Error('encoding too long'); + + if (this._size - offset < lenB) + return null; + + this._len = 0; + for (var i = 0; i < lenB; i++) + this._len = (this._len << 8) + (this._buf[offset++] & 0xff); + + } else { + // Wasn't a variable length + this._len = lenB; + } + + return offset; +}; + + +/** + * Parses the next sequence in this BER buffer. + * + * To get the length of the sequence, call `Reader.length`. + * + * @return {Number} the sequence's tag. + */ +Reader.prototype.readSequence = function (tag) { + var seq = this.peek(); + if (seq === null) + return null; + if (tag !== undefined && tag !== seq) + throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + seq.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + if (o === null) + return null; + + this._offset = o; + return seq; +}; + + +Reader.prototype.readInt = function () { + return this._readTag(ASN1.Integer); +}; + + +Reader.prototype.readBoolean = function () { + return (this._readTag(ASN1.Boolean) === 0 ? false : true); +}; + + +Reader.prototype.readEnumeration = function () { + return this._readTag(ASN1.Enumeration); +}; + + +Reader.prototype.readString = function (tag, retbuf) { + if (!tag) + tag = ASN1.OctetString; + + var b = this.peek(); + if (b === null) + return null; + + if (b !== tag) + throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + b.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + + if (o === null) + return null; + + if (this.length > this._size - o) + return null; + + this._offset = o; + + if (this.length === 0) + return retbuf ? Buffer.alloc(0) : ''; + + var str = this._buf.slice(this._offset, this._offset + this.length); + this._offset += this.length; + + return retbuf ? str : str.toString('utf8'); +}; + +Reader.prototype.readOID = function (tag) { + if (!tag) + tag = ASN1.OID; + + var b = this.readString(tag, true); + if (b === null) + return null; + + var values = []; + var value = 0; + + for (var i = 0; i < b.length; i++) { + var byte = b[i] & 0xff; + + value <<= 7; + value += byte & 0x7f; + if ((byte & 0x80) === 0) { + values.push(value); + value = 0; + } + } + + value = values.shift(); + values.unshift(value % 40); + values.unshift((value / 40) >> 0); + + return values.join('.'); +}; + + +Reader.prototype._readTag = function (tag) { + assert.ok(tag !== undefined); + + var b = this.peek(); + + if (b === null) + return null; + + if (b !== tag) + throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + b.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + if (o === null) + return null; + + if (this.length > 4) + throw newInvalidAsn1Error('Integer too long: ' + this.length); + + if (this.length > this._size - o) + return null; + this._offset = o; + + var fb = this._buf[this._offset]; + var value = 0; + + for (var i = 0; i < this.length; i++) { + value <<= 8; + value |= (this._buf[this._offset++] & 0xff); + } + + if ((fb & 0x80) === 0x80 && i !== 4) + value -= (1 << (i * 8)); + + return value >> 0; +}; + + + +// --- Exported API + +module.exports = Reader; + + +/***/ }), + +/***/ 740: +/***/ (function(module) { + +module.exports = {"$id":"postData.json#","$schema":"http://json-schema.org/draft-06/schema#","type":"object","optional":true,"required":["mimeType"],"properties":{"mimeType":{"type":"string"},"text":{"type":"string"},"params":{"type":"array","required":["name"],"properties":{"name":{"type":"string"},"value":{"type":"string"},"fileName":{"type":"string"},"contentType":{"type":"string"},"comment":{"type":"string"}}},"comment":{"type":"string"}}}; + +/***/ }), + +/***/ 741: +/***/ (function(module) { + +"use strict"; + + +module.exports = function (data, opts) { + if (!opts) opts = {}; + if (typeof opts === 'function') opts = { cmp: opts }; + var cycles = (typeof opts.cycles === 'boolean') ? opts.cycles : false; + + var cmp = opts.cmp && (function (f) { + return function (node) { + return function (a, b) { + var aobj = { key: a, value: node[a] }; + var bobj = { key: b, value: node[b] }; + return f(aobj, bobj); + }; + }; + })(opts.cmp); + + var seen = []; + return (function stringify (node) { + if (node && node.toJSON && typeof node.toJSON === 'function') { + node = node.toJSON(); + } + + if (node === undefined) return; + if (typeof node == 'number') return isFinite(node) ? '' + node : 'null'; + if (typeof node !== 'object') return JSON.stringify(node); + + var i, out; + if (Array.isArray(node)) { + out = '['; + for (i = 0; i < node.length; i++) { + if (i) out += ','; + out += stringify(node[i]) || 'null'; + } + return out + ']'; + } + + if (node === null) return 'null'; + + if (seen.indexOf(node) !== -1) { + if (cycles) return JSON.stringify('__cycle__'); + throw new TypeError('Converting circular structure to JSON'); + } + + var seenIndex = seen.push(node) - 1; + var keys = Object.keys(node).sort(cmp && cmp(node)); + out = ''; + for (i = 0; i < keys.length; i++) { + var key = keys[i]; + var value = stringify(node[key]); + + if (!value) continue; + if (out) out += ','; + out += JSON.stringify(key) + ':' + value; + } + seen.splice(seenIndex, 1); + return '{' + out + '}'; + })(data); +}; + + +/***/ }), + +/***/ 742: +/***/ (function(module) { + +// Generated by CoffeeScript 1.12.2 +(function() { + var getNanoSeconds, hrtime, loadTime, moduleLoadTime, nodeLoadTime, upTime; + + if ((typeof performance !== "undefined" && performance !== null) && performance.now) { + module.exports = function() { + return performance.now(); + }; + } else if ((typeof process !== "undefined" && process !== null) && process.hrtime) { + module.exports = function() { + return (getNanoSeconds() - nodeLoadTime) / 1e6; + }; + hrtime = process.hrtime; + getNanoSeconds = function() { + var hr; + hr = hrtime(); + return hr[0] * 1e9 + hr[1]; + }; + moduleLoadTime = getNanoSeconds(); + upTime = process.uptime() * 1e9; + nodeLoadTime = moduleLoadTime - upTime; + } else if (Date.now) { + module.exports = function() { + return Date.now() - loadTime; + }; + loadTime = Date.now(); + } else { + module.exports = function() { + return new Date().getTime() - loadTime; + }; + loadTime = new Date().getTime(); + } + +}).call(this); + +//# sourceMappingURL=performance-now.js.map + + +/***/ }), + +/***/ 744: +/***/ (function(module) { + +module.exports = {"$id":"page.json#","$schema":"http://json-schema.org/draft-06/schema#","type":"object","optional":true,"required":["startedDateTime","id","title","pageTimings"],"properties":{"startedDateTime":{"type":"string","format":"date-time","pattern":"^(\\d{4})(-)?(\\d\\d)(-)?(\\d\\d)(T)?(\\d\\d)(:)?(\\d\\d)(:)?(\\d\\d)(\\.\\d+)?(Z|([+-])(\\d\\d)(:)?(\\d\\d))"},"id":{"type":"string","unique":true},"title":{"type":"string"},"pageTimings":{"$ref":"pageTimings.json#"},"comment":{"type":"string"}}}; + +/***/ }), + +/***/ 747: +/***/ (function(module) { + +module.exports = require("fs"); + +/***/ }), + +/***/ 750: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; +/*eslint no-var:0, prefer-arrow-callback: 0, object-shorthand: 0 */ + + + +var Punycode = __webpack_require__(213); + + +var internals = {}; + + +// +// Read rules from file. +// +internals.rules = __webpack_require__(820).map(function (rule) { + + return { + rule: rule, + suffix: rule.replace(/^(\*\.|\!)/, ''), + punySuffix: -1, + wildcard: rule.charAt(0) === '*', + exception: rule.charAt(0) === '!' + }; +}); + + +// +// Check is given string ends with `suffix`. +// +internals.endsWith = function (str, suffix) { + + return str.indexOf(suffix, str.length - suffix.length) !== -1; +}; + + +// +// Find rule for a given domain. +// +internals.findRule = function (domain) { + + var punyDomain = Punycode.toASCII(domain); + return internals.rules.reduce(function (memo, rule) { + + if (rule.punySuffix === -1){ + rule.punySuffix = Punycode.toASCII(rule.suffix); + } + if (!internals.endsWith(punyDomain, '.' + rule.punySuffix) && punyDomain !== rule.punySuffix) { + return memo; + } + // This has been commented out as it never seems to run. This is because + // sub tlds always appear after their parents and we never find a shorter + // match. + //if (memo) { + // var memoSuffix = Punycode.toASCII(memo.suffix); + // if (memoSuffix.length >= punySuffix.length) { + // return memo; + // } + //} + return rule; + }, null); +}; + + +// +// Error codes and messages. +// +exports.errorCodes = { + DOMAIN_TOO_SHORT: 'Domain name too short.', + DOMAIN_TOO_LONG: 'Domain name too long. It should be no more than 255 chars.', + LABEL_STARTS_WITH_DASH: 'Domain name label can not start with a dash.', + LABEL_ENDS_WITH_DASH: 'Domain name label can not end with a dash.', + LABEL_TOO_LONG: 'Domain name label should be at most 63 chars long.', + LABEL_TOO_SHORT: 'Domain name label should be at least 1 character long.', + LABEL_INVALID_CHARS: 'Domain name label can only contain alphanumeric characters or dashes.' +}; + + +// +// Validate domain name and throw if not valid. +// +// From wikipedia: +// +// Hostnames are composed of series of labels concatenated with dots, as are all +// domain names. Each label must be between 1 and 63 characters long, and the +// entire hostname (including the delimiting dots) has a maximum of 255 chars. +// +// Allowed chars: +// +// * `a-z` +// * `0-9` +// * `-` but not as a starting or ending character +// * `.` as a separator for the textual portions of a domain name +// +// * http://en.wikipedia.org/wiki/Domain_name +// * http://en.wikipedia.org/wiki/Hostname +// +internals.validate = function (input) { + + // Before we can validate we need to take care of IDNs with unicode chars. + var ascii = Punycode.toASCII(input); + + if (ascii.length < 1) { + return 'DOMAIN_TOO_SHORT'; + } + if (ascii.length > 255) { + return 'DOMAIN_TOO_LONG'; + } + + // Check each part's length and allowed chars. + var labels = ascii.split('.'); + var label; + + for (var i = 0; i < labels.length; ++i) { + label = labels[i]; + if (!label.length) { + return 'LABEL_TOO_SHORT'; + } + if (label.length > 63) { + return 'LABEL_TOO_LONG'; + } + if (label.charAt(0) === '-') { + return 'LABEL_STARTS_WITH_DASH'; + } + if (label.charAt(label.length - 1) === '-') { + return 'LABEL_ENDS_WITH_DASH'; + } + if (!/^[a-z0-9\-]+$/.test(label)) { + return 'LABEL_INVALID_CHARS'; + } + } +}; + + +// +// Public API +// + + +// +// Parse domain. +// +exports.parse = function (input) { + + if (typeof input !== 'string') { + throw new TypeError('Domain name must be a string.'); + } + + // Force domain to lowercase. + var domain = input.slice(0).toLowerCase(); + + // Handle FQDN. + // TODO: Simply remove trailing dot? + if (domain.charAt(domain.length - 1) === '.') { + domain = domain.slice(0, domain.length - 1); + } + + // Validate and sanitise input. + var error = internals.validate(domain); + if (error) { + return { + input: input, + error: { + message: exports.errorCodes[error], + code: error + } + }; + } + + var parsed = { + input: input, + tld: null, + sld: null, + domain: null, + subdomain: null, + listed: false + }; + + var domainParts = domain.split('.'); + + // Non-Internet TLD + if (domainParts[domainParts.length - 1] === 'local') { + return parsed; + } + + var handlePunycode = function () { + + if (!/xn--/.test(domain)) { + return parsed; + } + if (parsed.domain) { + parsed.domain = Punycode.toASCII(parsed.domain); + } + if (parsed.subdomain) { + parsed.subdomain = Punycode.toASCII(parsed.subdomain); + } + return parsed; + }; + + var rule = internals.findRule(domain); + + // Unlisted tld. + if (!rule) { + if (domainParts.length < 2) { + return parsed; + } + parsed.tld = domainParts.pop(); + parsed.sld = domainParts.pop(); + parsed.domain = [parsed.sld, parsed.tld].join('.'); + if (domainParts.length) { + parsed.subdomain = domainParts.pop(); + } + return handlePunycode(); + } + + // At this point we know the public suffix is listed. + parsed.listed = true; + + var tldParts = rule.suffix.split('.'); + var privateParts = domainParts.slice(0, domainParts.length - tldParts.length); + + if (rule.exception) { + privateParts.push(tldParts.shift()); + } + + parsed.tld = tldParts.join('.'); + + if (!privateParts.length) { + return handlePunycode(); + } + + if (rule.wildcard) { + tldParts.unshift(privateParts.pop()); + parsed.tld = tldParts.join('.'); + } + + if (!privateParts.length) { + return handlePunycode(); + } + + parsed.sld = privateParts.pop(); + parsed.domain = [parsed.sld, parsed.tld].join('.'); + + if (privateParts.length) { + parsed.subdomain = privateParts.join('.'); + } + + return handlePunycode(); +}; + + +// +// Get domain. +// +exports.get = function (domain) { + + if (!domain) { + return null; + } + return exports.parse(domain).domain || null; +}; + + +// +// Check whether domain belongs to a known public suffix. +// +exports.isValid = function (domain) { + + var parsed = exports.parse(domain); + return Boolean(parsed.domain && parsed.listed); +}; + + +/***/ }), + +/***/ 751: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var defer = __webpack_require__(500); + +// API +module.exports = async; + +/** + * Runs provided callback asynchronously + * even if callback itself is not + * + * @param {function} callback - callback to invoke + * @returns {function} - augmented callback + */ +function async(callback) +{ + var isAsync = false; + + // check if async happened + defer(function() { isAsync = true; }); + + return function async_callback(err, result) + { + if (isAsync) + { + callback(err, result); + } + else + { + defer(function nextTick_callback() + { + callback(err, result); + }); + } + }; +} + + +/***/ }), + +/***/ 752: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2016 Joyent, Inc. + +module.exports = Certificate; + +var assert = __webpack_require__(477); +var Buffer = __webpack_require__(215).Buffer; +var algs = __webpack_require__(98); +var crypto = __webpack_require__(417); +var Fingerprint = __webpack_require__(400); +var Signature = __webpack_require__(575); +var errs = __webpack_require__(753); +var util = __webpack_require__(669); +var utils = __webpack_require__(270); +var Key = __webpack_require__(852); +var PrivateKey = __webpack_require__(502); +var Identity = __webpack_require__(378); + +var formats = {}; +formats['openssh'] = __webpack_require__(893); +formats['x509'] = __webpack_require__(866); +formats['pem'] = __webpack_require__(680); + +var CertificateParseError = errs.CertificateParseError; +var InvalidAlgorithmError = errs.InvalidAlgorithmError; + +function Certificate(opts) { + assert.object(opts, 'options'); + assert.arrayOfObject(opts.subjects, 'options.subjects'); + utils.assertCompatible(opts.subjects[0], Identity, [1, 0], + 'options.subjects'); + utils.assertCompatible(opts.subjectKey, Key, [1, 0], + 'options.subjectKey'); + utils.assertCompatible(opts.issuer, Identity, [1, 0], 'options.issuer'); + if (opts.issuerKey !== undefined) { + utils.assertCompatible(opts.issuerKey, Key, [1, 0], + 'options.issuerKey'); + } + assert.object(opts.signatures, 'options.signatures'); + assert.buffer(opts.serial, 'options.serial'); + assert.date(opts.validFrom, 'options.validFrom'); + assert.date(opts.validUntil, 'optons.validUntil'); + + assert.optionalArrayOfString(opts.purposes, 'options.purposes'); + + this._hashCache = {}; + + this.subjects = opts.subjects; + this.issuer = opts.issuer; + this.subjectKey = opts.subjectKey; + this.issuerKey = opts.issuerKey; + this.signatures = opts.signatures; + this.serial = opts.serial; + this.validFrom = opts.validFrom; + this.validUntil = opts.validUntil; + this.purposes = opts.purposes; +} + +Certificate.formats = formats; + +Certificate.prototype.toBuffer = function (format, options) { + if (format === undefined) + format = 'x509'; + assert.string(format, 'format'); + assert.object(formats[format], 'formats[format]'); + assert.optionalObject(options, 'options'); + + return (formats[format].write(this, options)); +}; + +Certificate.prototype.toString = function (format, options) { + if (format === undefined) + format = 'pem'; + return (this.toBuffer(format, options).toString()); +}; + +Certificate.prototype.fingerprint = function (algo) { + if (algo === undefined) + algo = 'sha256'; + assert.string(algo, 'algorithm'); + var opts = { + type: 'certificate', + hash: this.hash(algo), + algorithm: algo + }; + return (new Fingerprint(opts)); +}; + +Certificate.prototype.hash = function (algo) { + assert.string(algo, 'algorithm'); + algo = algo.toLowerCase(); + if (algs.hashAlgs[algo] === undefined) + throw (new InvalidAlgorithmError(algo)); + + if (this._hashCache[algo]) + return (this._hashCache[algo]); + + var hash = crypto.createHash(algo). + update(this.toBuffer('x509')).digest(); + this._hashCache[algo] = hash; + return (hash); +}; + +Certificate.prototype.isExpired = function (when) { + if (when === undefined) + when = new Date(); + return (!((when.getTime() >= this.validFrom.getTime()) && + (when.getTime() < this.validUntil.getTime()))); +}; + +Certificate.prototype.isSignedBy = function (issuerCert) { + utils.assertCompatible(issuerCert, Certificate, [1, 0], 'issuer'); + + if (!this.issuer.equals(issuerCert.subjects[0])) + return (false); + if (this.issuer.purposes && this.issuer.purposes.length > 0 && + this.issuer.purposes.indexOf('ca') === -1) { + return (false); + } + + return (this.isSignedByKey(issuerCert.subjectKey)); +}; + +Certificate.prototype.getExtension = function (keyOrOid) { + assert.string(keyOrOid, 'keyOrOid'); + var ext = this.getExtensions().filter(function (maybeExt) { + if (maybeExt.format === 'x509') + return (maybeExt.oid === keyOrOid); + if (maybeExt.format === 'openssh') + return (maybeExt.name === keyOrOid); + return (false); + })[0]; + return (ext); +}; + +Certificate.prototype.getExtensions = function () { + var exts = []; + var x509 = this.signatures.x509; + if (x509 && x509.extras && x509.extras.exts) { + x509.extras.exts.forEach(function (ext) { + ext.format = 'x509'; + exts.push(ext); + }); + } + var openssh = this.signatures.openssh; + if (openssh && openssh.exts) { + openssh.exts.forEach(function (ext) { + ext.format = 'openssh'; + exts.push(ext); + }); + } + return (exts); +}; + +Certificate.prototype.isSignedByKey = function (issuerKey) { + utils.assertCompatible(issuerKey, Key, [1, 2], 'issuerKey'); + + if (this.issuerKey !== undefined) { + return (this.issuerKey. + fingerprint('sha512').matches(issuerKey)); + } + + var fmt = Object.keys(this.signatures)[0]; + var valid = formats[fmt].verify(this, issuerKey); + if (valid) + this.issuerKey = issuerKey; + return (valid); +}; + +Certificate.prototype.signWith = function (key) { + utils.assertCompatible(key, PrivateKey, [1, 2], 'key'); + var fmts = Object.keys(formats); + var didOne = false; + for (var i = 0; i < fmts.length; ++i) { + if (fmts[i] !== 'pem') { + var ret = formats[fmts[i]].sign(this, key); + if (ret === true) + didOne = true; + } + } + if (!didOne) { + throw (new Error('Failed to sign the certificate for any ' + + 'available certificate formats')); + } +}; + +Certificate.createSelfSigned = function (subjectOrSubjects, key, options) { + var subjects; + if (Array.isArray(subjectOrSubjects)) + subjects = subjectOrSubjects; + else + subjects = [subjectOrSubjects]; + + assert.arrayOfObject(subjects); + subjects.forEach(function (subject) { + utils.assertCompatible(subject, Identity, [1, 0], 'subject'); + }); + + utils.assertCompatible(key, PrivateKey, [1, 2], 'private key'); + + assert.optionalObject(options, 'options'); + if (options === undefined) + options = {}; + assert.optionalObject(options.validFrom, 'options.validFrom'); + assert.optionalObject(options.validUntil, 'options.validUntil'); + var validFrom = options.validFrom; + var validUntil = options.validUntil; + if (validFrom === undefined) + validFrom = new Date(); + if (validUntil === undefined) { + assert.optionalNumber(options.lifetime, 'options.lifetime'); + var lifetime = options.lifetime; + if (lifetime === undefined) + lifetime = 10*365*24*3600; + validUntil = new Date(); + validUntil.setTime(validUntil.getTime() + lifetime*1000); + } + assert.optionalBuffer(options.serial, 'options.serial'); + var serial = options.serial; + if (serial === undefined) + serial = Buffer.from('0000000000000001', 'hex'); + + var purposes = options.purposes; + if (purposes === undefined) + purposes = []; + + if (purposes.indexOf('signature') === -1) + purposes.push('signature'); + + /* Self-signed certs are always CAs. */ + if (purposes.indexOf('ca') === -1) + purposes.push('ca'); + if (purposes.indexOf('crl') === -1) + purposes.push('crl'); + + /* + * If we weren't explicitly given any other purposes, do the sensible + * thing and add some basic ones depending on the subject type. + */ + if (purposes.length <= 3) { + var hostSubjects = subjects.filter(function (subject) { + return (subject.type === 'host'); + }); + var userSubjects = subjects.filter(function (subject) { + return (subject.type === 'user'); + }); + if (hostSubjects.length > 0) { + if (purposes.indexOf('serverAuth') === -1) + purposes.push('serverAuth'); + } + if (userSubjects.length > 0) { + if (purposes.indexOf('clientAuth') === -1) + purposes.push('clientAuth'); + } + if (userSubjects.length > 0 || hostSubjects.length > 0) { + if (purposes.indexOf('keyAgreement') === -1) + purposes.push('keyAgreement'); + if (key.type === 'rsa' && + purposes.indexOf('encryption') === -1) + purposes.push('encryption'); + } + } + + var cert = new Certificate({ + subjects: subjects, + issuer: subjects[0], + subjectKey: key.toPublic(), + issuerKey: key.toPublic(), + signatures: {}, + serial: serial, + validFrom: validFrom, + validUntil: validUntil, + purposes: purposes + }); + cert.signWith(key); + + return (cert); +}; + +Certificate.create = + function (subjectOrSubjects, key, issuer, issuerKey, options) { + var subjects; + if (Array.isArray(subjectOrSubjects)) + subjects = subjectOrSubjects; + else + subjects = [subjectOrSubjects]; + + assert.arrayOfObject(subjects); + subjects.forEach(function (subject) { + utils.assertCompatible(subject, Identity, [1, 0], 'subject'); + }); + + utils.assertCompatible(key, Key, [1, 0], 'key'); + if (PrivateKey.isPrivateKey(key)) + key = key.toPublic(); + utils.assertCompatible(issuer, Identity, [1, 0], 'issuer'); + utils.assertCompatible(issuerKey, PrivateKey, [1, 2], 'issuer key'); + + assert.optionalObject(options, 'options'); + if (options === undefined) + options = {}; + assert.optionalObject(options.validFrom, 'options.validFrom'); + assert.optionalObject(options.validUntil, 'options.validUntil'); + var validFrom = options.validFrom; + var validUntil = options.validUntil; + if (validFrom === undefined) + validFrom = new Date(); + if (validUntil === undefined) { + assert.optionalNumber(options.lifetime, 'options.lifetime'); + var lifetime = options.lifetime; + if (lifetime === undefined) + lifetime = 10*365*24*3600; + validUntil = new Date(); + validUntil.setTime(validUntil.getTime() + lifetime*1000); + } + assert.optionalBuffer(options.serial, 'options.serial'); + var serial = options.serial; + if (serial === undefined) + serial = Buffer.from('0000000000000001', 'hex'); + + var purposes = options.purposes; + if (purposes === undefined) + purposes = []; + + if (purposes.indexOf('signature') === -1) + purposes.push('signature'); + + if (options.ca === true) { + if (purposes.indexOf('ca') === -1) + purposes.push('ca'); + if (purposes.indexOf('crl') === -1) + purposes.push('crl'); + } + + var hostSubjects = subjects.filter(function (subject) { + return (subject.type === 'host'); + }); + var userSubjects = subjects.filter(function (subject) { + return (subject.type === 'user'); + }); + if (hostSubjects.length > 0) { + if (purposes.indexOf('serverAuth') === -1) + purposes.push('serverAuth'); + } + if (userSubjects.length > 0) { + if (purposes.indexOf('clientAuth') === -1) + purposes.push('clientAuth'); + } + if (userSubjects.length > 0 || hostSubjects.length > 0) { + if (purposes.indexOf('keyAgreement') === -1) + purposes.push('keyAgreement'); + if (key.type === 'rsa' && + purposes.indexOf('encryption') === -1) + purposes.push('encryption'); + } + + var cert = new Certificate({ + subjects: subjects, + issuer: issuer, + subjectKey: key, + issuerKey: issuerKey.toPublic(), + signatures: {}, + serial: serial, + validFrom: validFrom, + validUntil: validUntil, + purposes: purposes + }); + cert.signWith(issuerKey); + + return (cert); +}; + +Certificate.parse = function (data, format, options) { + if (typeof (data) !== 'string') + assert.buffer(data, 'data'); + if (format === undefined) + format = 'auto'; + assert.string(format, 'format'); + if (typeof (options) === 'string') + options = { filename: options }; + assert.optionalObject(options, 'options'); + if (options === undefined) + options = {}; + assert.optionalString(options.filename, 'options.filename'); + if (options.filename === undefined) + options.filename = '(unnamed)'; + + assert.object(formats[format], 'formats[format]'); + + try { + var k = formats[format].read(data, options); + return (k); + } catch (e) { + throw (new CertificateParseError(options.filename, format, e)); + } +}; + +Certificate.isCertificate = function (obj, ver) { + return (utils.isCompatible(obj, Certificate, ver)); +}; + +/* + * API versions for Certificate: + * [1,0] -- initial ver + * [1,1] -- openssh format now unpacks extensions + */ +Certificate.prototype._sshpkApiVersion = [1, 1]; + +Certificate._oldVersionDetect = function (obj) { + return ([1, 0]); +}; + + +/***/ }), + +/***/ 753: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2015 Joyent, Inc. + +var assert = __webpack_require__(477); +var util = __webpack_require__(669); + +function FingerprintFormatError(fp, format) { + if (Error.captureStackTrace) + Error.captureStackTrace(this, FingerprintFormatError); + this.name = 'FingerprintFormatError'; + this.fingerprint = fp; + this.format = format; + this.message = 'Fingerprint format is not supported, or is invalid: '; + if (fp !== undefined) + this.message += ' fingerprint = ' + fp; + if (format !== undefined) + this.message += ' format = ' + format; +} +util.inherits(FingerprintFormatError, Error); + +function InvalidAlgorithmError(alg) { + if (Error.captureStackTrace) + Error.captureStackTrace(this, InvalidAlgorithmError); + this.name = 'InvalidAlgorithmError'; + this.algorithm = alg; + this.message = 'Algorithm "' + alg + '" is not supported'; +} +util.inherits(InvalidAlgorithmError, Error); + +function KeyParseError(name, format, innerErr) { + if (Error.captureStackTrace) + Error.captureStackTrace(this, KeyParseError); + this.name = 'KeyParseError'; + this.format = format; + this.keyName = name; + this.innerErr = innerErr; + this.message = 'Failed to parse ' + name + ' as a valid ' + format + + ' format key: ' + innerErr.message; +} +util.inherits(KeyParseError, Error); + +function SignatureParseError(type, format, innerErr) { + if (Error.captureStackTrace) + Error.captureStackTrace(this, SignatureParseError); + this.name = 'SignatureParseError'; + this.type = type; + this.format = format; + this.innerErr = innerErr; + this.message = 'Failed to parse the given data as a ' + type + + ' signature in ' + format + ' format: ' + innerErr.message; +} +util.inherits(SignatureParseError, Error); + +function CertificateParseError(name, format, innerErr) { + if (Error.captureStackTrace) + Error.captureStackTrace(this, CertificateParseError); + this.name = 'CertificateParseError'; + this.format = format; + this.certName = name; + this.innerErr = innerErr; + this.message = 'Failed to parse ' + name + ' as a valid ' + format + + ' format certificate: ' + innerErr.message; +} +util.inherits(CertificateParseError, Error); + +function KeyEncryptedError(name, format) { + if (Error.captureStackTrace) + Error.captureStackTrace(this, KeyEncryptedError); + this.name = 'KeyEncryptedError'; + this.format = format; + this.keyName = name; + this.message = 'The ' + format + ' format key ' + name + ' is ' + + 'encrypted (password-protected), and no passphrase was ' + + 'provided in `options`'; +} +util.inherits(KeyEncryptedError, Error); + +module.exports = { + FingerprintFormatError: FingerprintFormatError, + InvalidAlgorithmError: InvalidAlgorithmError, + KeyParseError: KeyParseError, + SignatureParseError: SignatureParseError, + KeyEncryptedError: KeyEncryptedError, + CertificateParseError: CertificateParseError +}; + + +/***/ }), + +/***/ 755: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +var utils = __webpack_require__(581); + +var has = Object.prototype.hasOwnProperty; + +var defaults = { + allowDots: false, + allowPrototypes: false, + arrayLimit: 20, + decoder: utils.decode, + delimiter: '&', + depth: 5, + parameterLimit: 1000, + plainObjects: false, + strictNullHandling: false +}; + +var parseValues = function parseQueryStringValues(str, options) { + var obj = {}; + var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str; + var limit = options.parameterLimit === Infinity ? undefined : options.parameterLimit; + var parts = cleanStr.split(options.delimiter, limit); + + for (var i = 0; i < parts.length; ++i) { + var part = parts[i]; + + var bracketEqualsPos = part.indexOf(']='); + var pos = bracketEqualsPos === -1 ? part.indexOf('=') : bracketEqualsPos + 1; + + var key, val; + if (pos === -1) { + key = options.decoder(part, defaults.decoder); + val = options.strictNullHandling ? null : ''; + } else { + key = options.decoder(part.slice(0, pos), defaults.decoder); + val = options.decoder(part.slice(pos + 1), defaults.decoder); + } + if (has.call(obj, key)) { + obj[key] = [].concat(obj[key]).concat(val); + } else { + obj[key] = val; + } + } + + return obj; +}; + +var parseObject = function (chain, val, options) { + var leaf = val; + + for (var i = chain.length - 1; i >= 0; --i) { + var obj; + var root = chain[i]; + + if (root === '[]') { + obj = []; + obj = obj.concat(leaf); + } else { + obj = options.plainObjects ? Object.create(null) : {}; + var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root; + var index = parseInt(cleanRoot, 10); + if ( + !isNaN(index) + && root !== cleanRoot + && String(index) === cleanRoot + && index >= 0 + && (options.parseArrays && index <= options.arrayLimit) + ) { + obj = []; + obj[index] = leaf; + } else { + obj[cleanRoot] = leaf; + } + } + + leaf = obj; + } + + return leaf; +}; + +var parseKeys = function parseQueryStringKeys(givenKey, val, options) { + if (!givenKey) { + return; + } + + // Transform dot notation to bracket notation + var key = options.allowDots ? givenKey.replace(/\.([^.[]+)/g, '[$1]') : givenKey; + + // The regex chunks + + var brackets = /(\[[^[\]]*])/; + var child = /(\[[^[\]]*])/g; + + // Get the parent + + var segment = brackets.exec(key); + var parent = segment ? key.slice(0, segment.index) : key; + + // Stash the parent if it exists + + var keys = []; + if (parent) { + // If we aren't using plain objects, optionally prefix keys + // that would overwrite object prototype properties + if (!options.plainObjects && has.call(Object.prototype, parent)) { + if (!options.allowPrototypes) { + return; + } + } + + keys.push(parent); + } + + // Loop through children appending to the array until we hit depth + + var i = 0; + while ((segment = child.exec(key)) !== null && i < options.depth) { + i += 1; + if (!options.plainObjects && has.call(Object.prototype, segment[1].slice(1, -1))) { + if (!options.allowPrototypes) { + return; + } + } + keys.push(segment[1]); + } + + // If there's a remainder, just add whatever is left + + if (segment) { + keys.push('[' + key.slice(segment.index) + ']'); + } + + return parseObject(keys, val, options); +}; + +module.exports = function (str, opts) { + var options = opts ? utils.assign({}, opts) : {}; + + if (options.decoder !== null && options.decoder !== undefined && typeof options.decoder !== 'function') { + throw new TypeError('Decoder has to be a function.'); + } + + options.ignoreQueryPrefix = options.ignoreQueryPrefix === true; + options.delimiter = typeof options.delimiter === 'string' || utils.isRegExp(options.delimiter) ? options.delimiter : defaults.delimiter; + options.depth = typeof options.depth === 'number' ? options.depth : defaults.depth; + options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : defaults.arrayLimit; + options.parseArrays = options.parseArrays !== false; + options.decoder = typeof options.decoder === 'function' ? options.decoder : defaults.decoder; + options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : defaults.allowDots; + options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : defaults.plainObjects; + options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : defaults.allowPrototypes; + options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : defaults.parameterLimit; + options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling; + + if (str === '' || str === null || typeof str === 'undefined') { + return options.plainObjects ? Object.create(null) : {}; + } + + var tempObj = typeof str === 'string' ? parseValues(str, options) : str; + var obj = options.plainObjects ? Object.create(null) : {}; + + // Iterate over the keys and setup the new object + + var keys = Object.keys(tempObj); + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + var newObj = parseKeys(key, tempObj[key], options); + obj = utils.merge(obj, newObj, options); + } + + return utils.compact(obj); +}; + + +/***/ }), + +/***/ 758: +/***/ (function(module) { + +module.exports = {"$id":"timings.json#","$schema":"http://json-schema.org/draft-06/schema#","required":["send","wait","receive"],"properties":{"dns":{"type":"number","min":-1},"connect":{"type":"number","min":-1},"blocked":{"type":"number","min":-1},"send":{"type":"number","min":-1},"wait":{"type":"number","min":-1},"receive":{"type":"number","min":-1},"ssl":{"type":"number","min":-1},"comment":{"type":"string"}}}; + +/***/ }), + +/***/ 761: +/***/ (function(module) { + +module.exports = require("zlib"); + +/***/ }), + +/***/ 772: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate__limitLength(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $errorKeyword; + var $data = 'data' + ($dataLvl || ''); + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + var $op = $keyword == 'maxLength' ? '>' : '<'; + out += 'if ( '; + if ($isData) { + out += ' (' + ($schemaValue) + ' !== undefined && typeof ' + ($schemaValue) + ' != \'number\') || '; + } + if (it.opts.unicode === false) { + out += ' ' + ($data) + '.length '; + } else { + out += ' ucs2length(' + ($data) + ') '; + } + out += ' ' + ($op) + ' ' + ($schemaValue) + ') { '; + var $errorKeyword = $keyword; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ($errorKeyword || '_limitLength') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { limit: ' + ($schemaValue) + ' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT be '; + if ($keyword == 'maxLength') { + out += 'longer'; + } else { + out += 'shorter'; + } + out += ' than '; + if ($isData) { + out += '\' + ' + ($schemaValue) + ' + \''; + } else { + out += '' + ($schema); + } + out += ' characters\' '; + } + if (it.opts.verbose) { + out += ' , schema: '; + if ($isData) { + out += 'validate.schema' + ($schemaPath); + } else { + out += '' + ($schema); + } + out += ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += '} '; + if ($breakOnError) { + out += ' else { '; + } + return out; +} + + +/***/ }), + +/***/ 776: +/***/ (function(module) { + +module.exports = {"$id":"creator.json#","$schema":"http://json-schema.org/draft-06/schema#","type":"object","required":["name","version"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"comment":{"type":"string"}}}; + +/***/ }), + +/***/ 779: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; +/*! + * mime-types + * Copyright(c) 2014 Jonathan Ong + * Copyright(c) 2015 Douglas Christopher Wilson + * MIT Licensed + */ + + + +/** + * Module dependencies. + * @private + */ + +var db = __webpack_require__(972) +var extname = __webpack_require__(622).extname + +/** + * Module variables. + * @private + */ + +var EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/ +var TEXT_TYPE_REGEXP = /^text\//i + +/** + * Module exports. + * @public + */ + +exports.charset = charset +exports.charsets = { lookup: charset } +exports.contentType = contentType +exports.extension = extension +exports.extensions = Object.create(null) +exports.lookup = lookup +exports.types = Object.create(null) + +// Populate the extensions/types maps +populateMaps(exports.extensions, exports.types) + +/** + * Get the default charset for a MIME type. + * + * @param {string} type + * @return {boolean|string} + */ + +function charset (type) { + if (!type || typeof type !== 'string') { + return false + } + + // TODO: use media-typer + var match = EXTRACT_TYPE_REGEXP.exec(type) + var mime = match && db[match[1].toLowerCase()] + + if (mime && mime.charset) { + return mime.charset + } + + // default text/* to utf-8 + if (match && TEXT_TYPE_REGEXP.test(match[1])) { + return 'UTF-8' + } + + return false +} + +/** + * Create a full Content-Type header given a MIME type or extension. + * + * @param {string} str + * @return {boolean|string} + */ + +function contentType (str) { + // TODO: should this even be in this module? + if (!str || typeof str !== 'string') { + return false + } + + var mime = str.indexOf('/') === -1 + ? exports.lookup(str) + : str + + if (!mime) { + return false + } + + // TODO: use content-type or other module + if (mime.indexOf('charset') === -1) { + var charset = exports.charset(mime) + if (charset) mime += '; charset=' + charset.toLowerCase() + } + + return mime +} + +/** + * Get the default extension for a MIME type. + * + * @param {string} type + * @return {boolean|string} + */ + +function extension (type) { + if (!type || typeof type !== 'string') { + return false + } + + // TODO: use media-typer + var match = EXTRACT_TYPE_REGEXP.exec(type) + + // get extensions + var exts = match && exports.extensions[match[1].toLowerCase()] + + if (!exts || !exts.length) { + return false + } + + return exts[0] +} + +/** + * Lookup the MIME type for a file path/extension. + * + * @param {string} path + * @return {boolean|string} + */ + +function lookup (path) { + if (!path || typeof path !== 'string') { + return false + } + + // get the extension ("ext" or ".ext" or full path) + var extension = extname('x.' + path) + .toLowerCase() + .substr(1) + + if (!extension) { + return false + } + + return exports.types[extension] || false +} + +/** + * Populate the extensions and types maps. + * @private + */ + +function populateMaps (extensions, types) { + // source preference (least -> most) + var preference = ['nginx', 'apache', undefined, 'iana'] + + Object.keys(db).forEach(function forEachMimeType (type) { + var mime = db[type] + var exts = mime.extensions + + if (!exts || !exts.length) { + return + } + + // mime -> extensions + extensions[type] = exts + + // extension -> mime + for (var i = 0; i < exts.length; i++) { + var extension = exts[i] + + if (types[extension]) { + var from = preference.indexOf(db[types[extension]].source) + var to = preference.indexOf(mime.source) + + if (types[extension] !== 'application/octet-stream' && + (from > to || (from === to && types[extension].substr(0, 12) === 'application/'))) { + // skip the remapping + continue + } + } + + // set the extension -> mime + types[extension] = type + } + }) +} + + +/***/ }), + +/***/ 789: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2015 Joyent, Inc. + +var parser = __webpack_require__(342); +var signer = __webpack_require__(64); +var verify = __webpack_require__(428); +var utils = __webpack_require__(909); + + + +///--- API + +module.exports = { + + parse: parser.parseRequest, + parseRequest: parser.parseRequest, + + sign: signer.signRequest, + signRequest: signer.signRequest, + createSigner: signer.createSigner, + isSigner: signer.isSigner, + + sshKeyToPEM: utils.sshKeyToPEM, + sshKeyFingerprint: utils.fingerprint, + pemToRsaSSHKey: utils.pemToRsaSSHKey, + + verify: verify.verifySignature, + verifySignature: verify.verifySignature, + verifyHMAC: verify.verifyHMAC +}; + + +/***/ }), + +/***/ 792: +/***/ (function(module, __unusedexports, __webpack_require__) { + +module.exports = ForeverAgent +ForeverAgent.SSL = ForeverAgentSSL + +var util = __webpack_require__(669) + , Agent = __webpack_require__(605).Agent + , net = __webpack_require__(631) + , tls = __webpack_require__(16) + , AgentSSL = __webpack_require__(211).Agent + +function getConnectionName(host, port) { + var name = '' + if (typeof host === 'string') { + name = host + ':' + port + } else { + // For node.js v012.0 and iojs-v1.5.1, host is an object. And any existing localAddress is part of the connection name. + name = host.host + ':' + host.port + ':' + (host.localAddress ? (host.localAddress + ':') : ':') + } + return name +} + +function ForeverAgent(options) { + var self = this + self.options = options || {} + self.requests = {} + self.sockets = {} + self.freeSockets = {} + self.maxSockets = self.options.maxSockets || Agent.defaultMaxSockets + self.minSockets = self.options.minSockets || ForeverAgent.defaultMinSockets + self.on('free', function(socket, host, port) { + var name = getConnectionName(host, port) + + if (self.requests[name] && self.requests[name].length) { + self.requests[name].shift().onSocket(socket) + } else if (self.sockets[name].length < self.minSockets) { + if (!self.freeSockets[name]) self.freeSockets[name] = [] + self.freeSockets[name].push(socket) + + // if an error happens while we don't use the socket anyway, meh, throw the socket away + var onIdleError = function() { + socket.destroy() + } + socket._onIdleError = onIdleError + socket.on('error', onIdleError) + } else { + // If there are no pending requests just destroy the + // socket and it will get removed from the pool. This + // gets us out of timeout issues and allows us to + // default to Connection:keep-alive. + socket.destroy() + } + }) + +} +util.inherits(ForeverAgent, Agent) + +ForeverAgent.defaultMinSockets = 5 + + +ForeverAgent.prototype.createConnection = net.createConnection +ForeverAgent.prototype.addRequestNoreuse = Agent.prototype.addRequest +ForeverAgent.prototype.addRequest = function(req, host, port) { + var name = getConnectionName(host, port) + + if (typeof host !== 'string') { + var options = host + port = options.port + host = options.host + } + + if (this.freeSockets[name] && this.freeSockets[name].length > 0 && !req.useChunkedEncodingByDefault) { + var idleSocket = this.freeSockets[name].pop() + idleSocket.removeListener('error', idleSocket._onIdleError) + delete idleSocket._onIdleError + req._reusedSocket = true + req.onSocket(idleSocket) + } else { + this.addRequestNoreuse(req, host, port) + } +} + +ForeverAgent.prototype.removeSocket = function(s, name, host, port) { + if (this.sockets[name]) { + var index = this.sockets[name].indexOf(s) + if (index !== -1) { + this.sockets[name].splice(index, 1) + } + } else if (this.sockets[name] && this.sockets[name].length === 0) { + // don't leak + delete this.sockets[name] + delete this.requests[name] + } + + if (this.freeSockets[name]) { + var index = this.freeSockets[name].indexOf(s) + if (index !== -1) { + this.freeSockets[name].splice(index, 1) + if (this.freeSockets[name].length === 0) { + delete this.freeSockets[name] + } + } + } + + if (this.requests[name] && this.requests[name].length) { + // If we have pending requests and a socket gets closed a new one + // needs to be created to take over in the pool for the one that closed. + this.createSocket(name, host, port).emit('free') + } +} + +function ForeverAgentSSL (options) { + ForeverAgent.call(this, options) +} +util.inherits(ForeverAgentSSL, ForeverAgent) + +ForeverAgentSSL.prototype.createConnection = createConnectionSSL +ForeverAgentSSL.prototype.addRequestNoreuse = AgentSSL.prototype.addRequest + +function createConnectionSSL (port, host, options) { + if (typeof port === 'object') { + options = port; + } else if (typeof host === 'object') { + options = host; + } else if (typeof options === 'object') { + options = options; + } else { + options = {}; + } + + if (typeof port === 'number') { + options.port = port; + } + + if (typeof host === 'string') { + options.host = host; + } + + return tls.connect(options); +} + + +/***/ }), + +/***/ 805: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +var resolve = __webpack_require__(867) + , util = __webpack_require__(855) + , errorClasses = __webpack_require__(844) + , stableStringify = __webpack_require__(741); + +var validateGenerator = __webpack_require__(967); + +/** + * Functions below are used inside compiled validations function + */ + +var ucs2length = util.ucs2length; +var equal = __webpack_require__(832); + +// this error is thrown by async schemas to return validation errors via exception +var ValidationError = errorClasses.Validation; + +module.exports = compile; + + +/** + * Compiles schema to validation function + * @this Ajv + * @param {Object} schema schema object + * @param {Object} root object with information about the root schema for this schema + * @param {Object} localRefs the hash of local references inside the schema (created by resolve.id), used for inline resolution + * @param {String} baseId base ID for IDs in the schema + * @return {Function} validation function + */ +function compile(schema, root, localRefs, baseId) { + /* jshint validthis: true, evil: true */ + /* eslint no-shadow: 0 */ + var self = this + , opts = this._opts + , refVal = [ undefined ] + , refs = {} + , patterns = [] + , patternsHash = {} + , defaults = [] + , defaultsHash = {} + , customRules = []; + + root = root || { schema: schema, refVal: refVal, refs: refs }; + + var c = checkCompiling.call(this, schema, root, baseId); + var compilation = this._compilations[c.index]; + if (c.compiling) return (compilation.callValidate = callValidate); + + var formats = this._formats; + var RULES = this.RULES; + + try { + var v = localCompile(schema, root, localRefs, baseId); + compilation.validate = v; + var cv = compilation.callValidate; + if (cv) { + cv.schema = v.schema; + cv.errors = null; + cv.refs = v.refs; + cv.refVal = v.refVal; + cv.root = v.root; + cv.$async = v.$async; + if (opts.sourceCode) cv.source = v.source; + } + return v; + } finally { + endCompiling.call(this, schema, root, baseId); + } + + /* @this {*} - custom context, see passContext option */ + function callValidate() { + /* jshint validthis: true */ + var validate = compilation.validate; + var result = validate.apply(this, arguments); + callValidate.errors = validate.errors; + return result; + } + + function localCompile(_schema, _root, localRefs, baseId) { + var isRoot = !_root || (_root && _root.schema == _schema); + if (_root.schema != root.schema) + return compile.call(self, _schema, _root, localRefs, baseId); + + var $async = _schema.$async === true; + + var sourceCode = validateGenerator({ + isTop: true, + schema: _schema, + isRoot: isRoot, + baseId: baseId, + root: _root, + schemaPath: '', + errSchemaPath: '#', + errorPath: '""', + MissingRefError: errorClasses.MissingRef, + RULES: RULES, + validate: validateGenerator, + util: util, + resolve: resolve, + resolveRef: resolveRef, + usePattern: usePattern, + useDefault: useDefault, + useCustomRule: useCustomRule, + opts: opts, + formats: formats, + logger: self.logger, + self: self + }); + + sourceCode = vars(refVal, refValCode) + vars(patterns, patternCode) + + vars(defaults, defaultCode) + vars(customRules, customRuleCode) + + sourceCode; + + if (opts.processCode) sourceCode = opts.processCode(sourceCode); + // console.log('\n\n\n *** \n', JSON.stringify(sourceCode)); + var validate; + try { + var makeValidate = new Function( + 'self', + 'RULES', + 'formats', + 'root', + 'refVal', + 'defaults', + 'customRules', + 'equal', + 'ucs2length', + 'ValidationError', + sourceCode + ); + + validate = makeValidate( + self, + RULES, + formats, + root, + refVal, + defaults, + customRules, + equal, + ucs2length, + ValidationError + ); + + refVal[0] = validate; + } catch(e) { + self.logger.error('Error compiling schema, function code:', sourceCode); + throw e; + } + + validate.schema = _schema; + validate.errors = null; + validate.refs = refs; + validate.refVal = refVal; + validate.root = isRoot ? validate : _root; + if ($async) validate.$async = true; + if (opts.sourceCode === true) { + validate.source = { + code: sourceCode, + patterns: patterns, + defaults: defaults + }; + } + + return validate; + } + + function resolveRef(baseId, ref, isRoot) { + ref = resolve.url(baseId, ref); + var refIndex = refs[ref]; + var _refVal, refCode; + if (refIndex !== undefined) { + _refVal = refVal[refIndex]; + refCode = 'refVal[' + refIndex + ']'; + return resolvedRef(_refVal, refCode); + } + if (!isRoot && root.refs) { + var rootRefId = root.refs[ref]; + if (rootRefId !== undefined) { + _refVal = root.refVal[rootRefId]; + refCode = addLocalRef(ref, _refVal); + return resolvedRef(_refVal, refCode); + } + } + + refCode = addLocalRef(ref); + var v = resolve.call(self, localCompile, root, ref); + if (v === undefined) { + var localSchema = localRefs && localRefs[ref]; + if (localSchema) { + v = resolve.inlineRef(localSchema, opts.inlineRefs) + ? localSchema + : compile.call(self, localSchema, root, localRefs, baseId); + } + } + + if (v === undefined) { + removeLocalRef(ref); + } else { + replaceLocalRef(ref, v); + return resolvedRef(v, refCode); + } + } + + function addLocalRef(ref, v) { + var refId = refVal.length; + refVal[refId] = v; + refs[ref] = refId; + return 'refVal' + refId; + } + + function removeLocalRef(ref) { + delete refs[ref]; + } + + function replaceLocalRef(ref, v) { + var refId = refs[ref]; + refVal[refId] = v; + } + + function resolvedRef(refVal, code) { + return typeof refVal == 'object' || typeof refVal == 'boolean' + ? { code: code, schema: refVal, inline: true } + : { code: code, $async: refVal && !!refVal.$async }; + } + + function usePattern(regexStr) { + var index = patternsHash[regexStr]; + if (index === undefined) { + index = patternsHash[regexStr] = patterns.length; + patterns[index] = regexStr; + } + return 'pattern' + index; + } + + function useDefault(value) { + switch (typeof value) { + case 'boolean': + case 'number': + return '' + value; + case 'string': + return util.toQuotedString(value); + case 'object': + if (value === null) return 'null'; + var valueStr = stableStringify(value); + var index = defaultsHash[valueStr]; + if (index === undefined) { + index = defaultsHash[valueStr] = defaults.length; + defaults[index] = value; + } + return 'default' + index; + } + } + + function useCustomRule(rule, schema, parentSchema, it) { + if (self._opts.validateSchema !== false) { + var deps = rule.definition.dependencies; + if (deps && !deps.every(function(keyword) { + return Object.prototype.hasOwnProperty.call(parentSchema, keyword); + })) + throw new Error('parent schema must have all required keywords: ' + deps.join(',')); + + var validateSchema = rule.definition.validateSchema; + if (validateSchema) { + var valid = validateSchema(schema); + if (!valid) { + var message = 'keyword schema is invalid: ' + self.errorsText(validateSchema.errors); + if (self._opts.validateSchema == 'log') self.logger.error(message); + else throw new Error(message); + } + } + } + + var compile = rule.definition.compile + , inline = rule.definition.inline + , macro = rule.definition.macro; + + var validate; + if (compile) { + validate = compile.call(self, schema, parentSchema, it); + } else if (macro) { + validate = macro.call(self, schema, parentSchema, it); + if (opts.validateSchema !== false) self.validateSchema(validate, true); + } else if (inline) { + validate = inline.call(self, it, rule.keyword, schema, parentSchema); + } else { + validate = rule.definition.validate; + if (!validate) return; + } + + if (validate === undefined) + throw new Error('custom keyword "' + rule.keyword + '"failed to compile'); + + var index = customRules.length; + customRules[index] = validate; + + return { + code: 'customRule' + index, + validate: validate + }; + } +} + + +/** + * Checks if the schema is currently compiled + * @this Ajv + * @param {Object} schema schema to compile + * @param {Object} root root object + * @param {String} baseId base schema ID + * @return {Object} object with properties "index" (compilation index) and "compiling" (boolean) + */ +function checkCompiling(schema, root, baseId) { + /* jshint validthis: true */ + var index = compIndex.call(this, schema, root, baseId); + if (index >= 0) return { index: index, compiling: true }; + index = this._compilations.length; + this._compilations[index] = { + schema: schema, + root: root, + baseId: baseId + }; + return { index: index, compiling: false }; +} + + +/** + * Removes the schema from the currently compiled list + * @this Ajv + * @param {Object} schema schema to compile + * @param {Object} root root object + * @param {String} baseId base schema ID + */ +function endCompiling(schema, root, baseId) { + /* jshint validthis: true */ + var i = compIndex.call(this, schema, root, baseId); + if (i >= 0) this._compilations.splice(i, 1); +} + + +/** + * Index of schema compilation in the currently compiled list + * @this Ajv + * @param {Object} schema schema to compile + * @param {Object} root root object + * @param {String} baseId base schema ID + * @return {Integer} compilation index + */ +function compIndex(schema, root, baseId) { + /* jshint validthis: true */ + for (var i=0; i 1024) + hashAlgo = 'sha256'; + if (this.type === 'ed25519') + hashAlgo = 'sha512'; + if (this.type === 'ecdsa') { + if (this.size <= 256) + hashAlgo = 'sha256'; + else if (this.size <= 384) + hashAlgo = 'sha384'; + else + hashAlgo = 'sha512'; + } + return (hashAlgo); +}; + +Key.prototype.createVerify = function (hashAlgo) { + if (hashAlgo === undefined) + hashAlgo = this.defaultHashAlgorithm(); + assert.string(hashAlgo, 'hash algorithm'); + + /* ED25519 is not supported by OpenSSL, use a javascript impl. */ + if (this.type === 'ed25519' && edCompat !== undefined) + return (new edCompat.Verifier(this, hashAlgo)); + if (this.type === 'curve25519') + throw (new Error('Curve25519 keys are not suitable for ' + + 'signing or verification')); + + var v, nm, err; + try { + nm = hashAlgo.toUpperCase(); + v = crypto.createVerify(nm); + } catch (e) { + err = e; + } + if (v === undefined || (err instanceof Error && + err.message.match(/Unknown message digest/))) { + nm = 'RSA-'; + nm += hashAlgo.toUpperCase(); + v = crypto.createVerify(nm); + } + assert.ok(v, 'failed to create verifier'); + var oldVerify = v.verify.bind(v); + var key = this.toBuffer('pkcs8'); + var curve = this.curve; + var self = this; + v.verify = function (signature, fmt) { + if (Signature.isSignature(signature, [2, 0])) { + if (signature.type !== self.type) + return (false); + if (signature.hashAlgorithm && + signature.hashAlgorithm !== hashAlgo) + return (false); + if (signature.curve && self.type === 'ecdsa' && + signature.curve !== curve) + return (false); + return (oldVerify(key, signature.toBuffer('asn1'))); + + } else if (typeof (signature) === 'string' || + Buffer.isBuffer(signature)) { + return (oldVerify(key, signature, fmt)); + + /* + * Avoid doing this on valid arguments, walking the prototype + * chain can be quite slow. + */ + } else if (Signature.isSignature(signature, [1, 0])) { + throw (new Error('signature was created by too old ' + + 'a version of sshpk and cannot be verified')); + + } else { + throw (new TypeError('signature must be a string, ' + + 'Buffer, or Signature object')); + } + }; + return (v); +}; + +Key.prototype.createDiffieHellman = function () { + if (this.type === 'rsa') + throw (new Error('RSA keys do not support Diffie-Hellman')); + + return (new DiffieHellman(this)); +}; +Key.prototype.createDH = Key.prototype.createDiffieHellman; + +Key.parse = function (data, format, options) { + if (typeof (data) !== 'string') + assert.buffer(data, 'data'); + if (format === undefined) + format = 'auto'; + assert.string(format, 'format'); + if (typeof (options) === 'string') + options = { filename: options }; + assert.optionalObject(options, 'options'); + if (options === undefined) + options = {}; + assert.optionalString(options.filename, 'options.filename'); + if (options.filename === undefined) + options.filename = '(unnamed)'; + + assert.object(formats[format], 'formats[format]'); + + try { + var k = formats[format].read(data, options); + if (k instanceof PrivateKey) + k = k.toPublic(); + if (!k.comment) + k.comment = options.filename; + return (k); + } catch (e) { + if (e.name === 'KeyEncryptedError') + throw (e); + throw (new KeyParseError(options.filename, format, e)); + } +}; + +Key.isKey = function (obj, ver) { + return (utils.isCompatible(obj, Key, ver)); +}; + +/* + * API versions for Key: + * [1,0] -- initial ver, may take Signature for createVerify or may not + * [1,1] -- added pkcs1, pkcs8 formats + * [1,2] -- added auto, ssh-private, openssh formats + * [1,3] -- added defaultHashAlgorithm + * [1,4] -- added ed support, createDH + * [1,5] -- first explicitly tagged version + * [1,6] -- changed ed25519 part names + * [1,7] -- spki hash types + */ +Key.prototype._sshpkApiVersion = [1, 7]; + +Key._oldVersionDetect = function (obj) { + assert.func(obj.toBuffer); + assert.func(obj.fingerprint); + if (obj.createDH) + return ([1, 4]); + if (obj.defaultHashAlgorithm) + return ([1, 3]); + if (obj.formats['auto']) + return ([1, 2]); + if (obj.formats['pkcs1']) + return ([1, 1]); + return ([1, 0]); +}; + + +/***/ }), + +/***/ 853: +/***/ (function(__unusedmodule, exports) { + +/** @license URI.js v4.2.1 (c) 2011 Gary Court. License: http://github.com/garycourt/uri-js */ +(function (global, factory) { + true ? factory(exports) : + undefined; +}(this, (function (exports) { 'use strict'; + +function merge() { + for (var _len = arguments.length, sets = Array(_len), _key = 0; _key < _len; _key++) { + sets[_key] = arguments[_key]; + } + + if (sets.length > 1) { + sets[0] = sets[0].slice(0, -1); + var xl = sets.length - 1; + for (var x = 1; x < xl; ++x) { + sets[x] = sets[x].slice(1, -1); + } + sets[xl] = sets[xl].slice(1); + return sets.join(''); + } else { + return sets[0]; + } +} +function subexp(str) { + return "(?:" + str + ")"; +} +function typeOf(o) { + return o === undefined ? "undefined" : o === null ? "null" : Object.prototype.toString.call(o).split(" ").pop().split("]").shift().toLowerCase(); +} +function toUpperCase(str) { + return str.toUpperCase(); +} +function toArray(obj) { + return obj !== undefined && obj !== null ? obj instanceof Array ? obj : typeof obj.length !== "number" || obj.split || obj.setInterval || obj.call ? [obj] : Array.prototype.slice.call(obj) : []; +} +function assign(target, source) { + var obj = target; + if (source) { + for (var key in source) { + obj[key] = source[key]; + } + } + return obj; +} + +function buildExps(isIRI) { + var ALPHA$$ = "[A-Za-z]", + CR$ = "[\\x0D]", + DIGIT$$ = "[0-9]", + DQUOTE$$ = "[\\x22]", + HEXDIG$$ = merge(DIGIT$$, "[A-Fa-f]"), + //case-insensitive + LF$$ = "[\\x0A]", + SP$$ = "[\\x20]", + PCT_ENCODED$ = subexp(subexp("%[EFef]" + HEXDIG$$ + "%" + HEXDIG$$ + HEXDIG$$ + "%" + HEXDIG$$ + HEXDIG$$) + "|" + subexp("%[89A-Fa-f]" + HEXDIG$$ + "%" + HEXDIG$$ + HEXDIG$$) + "|" + subexp("%" + HEXDIG$$ + HEXDIG$$)), + //expanded + GEN_DELIMS$$ = "[\\:\\/\\?\\#\\[\\]\\@]", + SUB_DELIMS$$ = "[\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\=]", + RESERVED$$ = merge(GEN_DELIMS$$, SUB_DELIMS$$), + UCSCHAR$$ = isIRI ? "[\\xA0-\\u200D\\u2010-\\u2029\\u202F-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]" : "[]", + //subset, excludes bidi control characters + IPRIVATE$$ = isIRI ? "[\\uE000-\\uF8FF]" : "[]", + //subset + UNRESERVED$$ = merge(ALPHA$$, DIGIT$$, "[\\-\\.\\_\\~]", UCSCHAR$$), + SCHEME$ = subexp(ALPHA$$ + merge(ALPHA$$, DIGIT$$, "[\\+\\-\\.]") + "*"), + USERINFO$ = subexp(subexp(PCT_ENCODED$ + "|" + merge(UNRESERVED$$, SUB_DELIMS$$, "[\\:]")) + "*"), + DEC_OCTET$ = subexp(subexp("25[0-5]") + "|" + subexp("2[0-4]" + DIGIT$$) + "|" + subexp("1" + DIGIT$$ + DIGIT$$) + "|" + subexp("[1-9]" + DIGIT$$) + "|" + DIGIT$$), + DEC_OCTET_RELAXED$ = subexp(subexp("25[0-5]") + "|" + subexp("2[0-4]" + DIGIT$$) + "|" + subexp("1" + DIGIT$$ + DIGIT$$) + "|" + subexp("0?[1-9]" + DIGIT$$) + "|0?0?" + DIGIT$$), + //relaxed parsing rules + IPV4ADDRESS$ = subexp(DEC_OCTET_RELAXED$ + "\\." + DEC_OCTET_RELAXED$ + "\\." + DEC_OCTET_RELAXED$ + "\\." + DEC_OCTET_RELAXED$), + H16$ = subexp(HEXDIG$$ + "{1,4}"), + LS32$ = subexp(subexp(H16$ + "\\:" + H16$) + "|" + IPV4ADDRESS$), + IPV6ADDRESS1$ = subexp(subexp(H16$ + "\\:") + "{6}" + LS32$), + // 6( h16 ":" ) ls32 + IPV6ADDRESS2$ = subexp("\\:\\:" + subexp(H16$ + "\\:") + "{5}" + LS32$), + // "::" 5( h16 ":" ) ls32 + IPV6ADDRESS3$ = subexp(subexp(H16$) + "?\\:\\:" + subexp(H16$ + "\\:") + "{4}" + LS32$), + //[ h16 ] "::" 4( h16 ":" ) ls32 + IPV6ADDRESS4$ = subexp(subexp(subexp(H16$ + "\\:") + "{0,1}" + H16$) + "?\\:\\:" + subexp(H16$ + "\\:") + "{3}" + LS32$), + //[ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + IPV6ADDRESS5$ = subexp(subexp(subexp(H16$ + "\\:") + "{0,2}" + H16$) + "?\\:\\:" + subexp(H16$ + "\\:") + "{2}" + LS32$), + //[ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + IPV6ADDRESS6$ = subexp(subexp(subexp(H16$ + "\\:") + "{0,3}" + H16$) + "?\\:\\:" + H16$ + "\\:" + LS32$), + //[ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + IPV6ADDRESS7$ = subexp(subexp(subexp(H16$ + "\\:") + "{0,4}" + H16$) + "?\\:\\:" + LS32$), + //[ *4( h16 ":" ) h16 ] "::" ls32 + IPV6ADDRESS8$ = subexp(subexp(subexp(H16$ + "\\:") + "{0,5}" + H16$) + "?\\:\\:" + H16$), + //[ *5( h16 ":" ) h16 ] "::" h16 + IPV6ADDRESS9$ = subexp(subexp(subexp(H16$ + "\\:") + "{0,6}" + H16$) + "?\\:\\:"), + //[ *6( h16 ":" ) h16 ] "::" + IPV6ADDRESS$ = subexp([IPV6ADDRESS1$, IPV6ADDRESS2$, IPV6ADDRESS3$, IPV6ADDRESS4$, IPV6ADDRESS5$, IPV6ADDRESS6$, IPV6ADDRESS7$, IPV6ADDRESS8$, IPV6ADDRESS9$].join("|")), + ZONEID$ = subexp(subexp(UNRESERVED$$ + "|" + PCT_ENCODED$) + "+"), + //RFC 6874 + IPV6ADDRZ$ = subexp(IPV6ADDRESS$ + "\\%25" + ZONEID$), + //RFC 6874 + IPV6ADDRZ_RELAXED$ = subexp(IPV6ADDRESS$ + subexp("\\%25|\\%(?!" + HEXDIG$$ + "{2})") + ZONEID$), + //RFC 6874, with relaxed parsing rules + IPVFUTURE$ = subexp("[vV]" + HEXDIG$$ + "+\\." + merge(UNRESERVED$$, SUB_DELIMS$$, "[\\:]") + "+"), + IP_LITERAL$ = subexp("\\[" + subexp(IPV6ADDRZ_RELAXED$ + "|" + IPV6ADDRESS$ + "|" + IPVFUTURE$) + "\\]"), + //RFC 6874 + REG_NAME$ = subexp(subexp(PCT_ENCODED$ + "|" + merge(UNRESERVED$$, SUB_DELIMS$$)) + "*"), + HOST$ = subexp(IP_LITERAL$ + "|" + IPV4ADDRESS$ + "(?!" + REG_NAME$ + ")" + "|" + REG_NAME$), + PORT$ = subexp(DIGIT$$ + "*"), + AUTHORITY$ = subexp(subexp(USERINFO$ + "@") + "?" + HOST$ + subexp("\\:" + PORT$) + "?"), + PCHAR$ = subexp(PCT_ENCODED$ + "|" + merge(UNRESERVED$$, SUB_DELIMS$$, "[\\:\\@]")), + SEGMENT$ = subexp(PCHAR$ + "*"), + SEGMENT_NZ$ = subexp(PCHAR$ + "+"), + SEGMENT_NZ_NC$ = subexp(subexp(PCT_ENCODED$ + "|" + merge(UNRESERVED$$, SUB_DELIMS$$, "[\\@]")) + "+"), + PATH_ABEMPTY$ = subexp(subexp("\\/" + SEGMENT$) + "*"), + PATH_ABSOLUTE$ = subexp("\\/" + subexp(SEGMENT_NZ$ + PATH_ABEMPTY$) + "?"), + //simplified + PATH_NOSCHEME$ = subexp(SEGMENT_NZ_NC$ + PATH_ABEMPTY$), + //simplified + PATH_ROOTLESS$ = subexp(SEGMENT_NZ$ + PATH_ABEMPTY$), + //simplified + PATH_EMPTY$ = "(?!" + PCHAR$ + ")", + PATH$ = subexp(PATH_ABEMPTY$ + "|" + PATH_ABSOLUTE$ + "|" + PATH_NOSCHEME$ + "|" + PATH_ROOTLESS$ + "|" + PATH_EMPTY$), + QUERY$ = subexp(subexp(PCHAR$ + "|" + merge("[\\/\\?]", IPRIVATE$$)) + "*"), + FRAGMENT$ = subexp(subexp(PCHAR$ + "|[\\/\\?]") + "*"), + HIER_PART$ = subexp(subexp("\\/\\/" + AUTHORITY$ + PATH_ABEMPTY$) + "|" + PATH_ABSOLUTE$ + "|" + PATH_ROOTLESS$ + "|" + PATH_EMPTY$), + URI$ = subexp(SCHEME$ + "\\:" + HIER_PART$ + subexp("\\?" + QUERY$) + "?" + subexp("\\#" + FRAGMENT$) + "?"), + RELATIVE_PART$ = subexp(subexp("\\/\\/" + AUTHORITY$ + PATH_ABEMPTY$) + "|" + PATH_ABSOLUTE$ + "|" + PATH_NOSCHEME$ + "|" + PATH_EMPTY$), + RELATIVE$ = subexp(RELATIVE_PART$ + subexp("\\?" + QUERY$) + "?" + subexp("\\#" + FRAGMENT$) + "?"), + URI_REFERENCE$ = subexp(URI$ + "|" + RELATIVE$), + ABSOLUTE_URI$ = subexp(SCHEME$ + "\\:" + HIER_PART$ + subexp("\\?" + QUERY$) + "?"), + GENERIC_REF$ = "^(" + SCHEME$ + ")\\:" + subexp(subexp("\\/\\/(" + subexp("(" + USERINFO$ + ")@") + "?(" + HOST$ + ")" + subexp("\\:(" + PORT$ + ")") + "?)") + "?(" + PATH_ABEMPTY$ + "|" + PATH_ABSOLUTE$ + "|" + PATH_ROOTLESS$ + "|" + PATH_EMPTY$ + ")") + subexp("\\?(" + QUERY$ + ")") + "?" + subexp("\\#(" + FRAGMENT$ + ")") + "?$", + RELATIVE_REF$ = "^(){0}" + subexp(subexp("\\/\\/(" + subexp("(" + USERINFO$ + ")@") + "?(" + HOST$ + ")" + subexp("\\:(" + PORT$ + ")") + "?)") + "?(" + PATH_ABEMPTY$ + "|" + PATH_ABSOLUTE$ + "|" + PATH_NOSCHEME$ + "|" + PATH_EMPTY$ + ")") + subexp("\\?(" + QUERY$ + ")") + "?" + subexp("\\#(" + FRAGMENT$ + ")") + "?$", + ABSOLUTE_REF$ = "^(" + SCHEME$ + ")\\:" + subexp(subexp("\\/\\/(" + subexp("(" + USERINFO$ + ")@") + "?(" + HOST$ + ")" + subexp("\\:(" + PORT$ + ")") + "?)") + "?(" + PATH_ABEMPTY$ + "|" + PATH_ABSOLUTE$ + "|" + PATH_ROOTLESS$ + "|" + PATH_EMPTY$ + ")") + subexp("\\?(" + QUERY$ + ")") + "?$", + SAMEDOC_REF$ = "^" + subexp("\\#(" + FRAGMENT$ + ")") + "?$", + AUTHORITY_REF$ = "^" + subexp("(" + USERINFO$ + ")@") + "?(" + HOST$ + ")" + subexp("\\:(" + PORT$ + ")") + "?$"; + return { + NOT_SCHEME: new RegExp(merge("[^]", ALPHA$$, DIGIT$$, "[\\+\\-\\.]"), "g"), + NOT_USERINFO: new RegExp(merge("[^\\%\\:]", UNRESERVED$$, SUB_DELIMS$$), "g"), + NOT_HOST: new RegExp(merge("[^\\%\\[\\]\\:]", UNRESERVED$$, SUB_DELIMS$$), "g"), + NOT_PATH: new RegExp(merge("[^\\%\\/\\:\\@]", UNRESERVED$$, SUB_DELIMS$$), "g"), + NOT_PATH_NOSCHEME: new RegExp(merge("[^\\%\\/\\@]", UNRESERVED$$, SUB_DELIMS$$), "g"), + NOT_QUERY: new RegExp(merge("[^\\%]", UNRESERVED$$, SUB_DELIMS$$, "[\\:\\@\\/\\?]", IPRIVATE$$), "g"), + NOT_FRAGMENT: new RegExp(merge("[^\\%]", UNRESERVED$$, SUB_DELIMS$$, "[\\:\\@\\/\\?]"), "g"), + ESCAPE: new RegExp(merge("[^]", UNRESERVED$$, SUB_DELIMS$$), "g"), + UNRESERVED: new RegExp(UNRESERVED$$, "g"), + OTHER_CHARS: new RegExp(merge("[^\\%]", UNRESERVED$$, RESERVED$$), "g"), + PCT_ENCODED: new RegExp(PCT_ENCODED$, "g"), + IPV4ADDRESS: new RegExp("^(" + IPV4ADDRESS$ + ")$"), + IPV6ADDRESS: new RegExp("^\\[?(" + IPV6ADDRESS$ + ")" + subexp(subexp("\\%25|\\%(?!" + HEXDIG$$ + "{2})") + "(" + ZONEID$ + ")") + "?\\]?$") //RFC 6874, with relaxed parsing rules + }; +} +var URI_PROTOCOL = buildExps(false); + +var IRI_PROTOCOL = buildExps(true); + +var slicedToArray = function () { + function sliceIterator(arr, i) { + var _arr = []; + var _n = true; + var _d = false; + var _e = undefined; + + try { + for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { + _arr.push(_s.value); + + if (i && _arr.length === i) break; + } + } catch (err) { + _d = true; + _e = err; + } finally { + try { + if (!_n && _i["return"]) _i["return"](); + } finally { + if (_d) throw _e; + } + } + + return _arr; + } + + return function (arr, i) { + if (Array.isArray(arr)) { + return arr; + } else if (Symbol.iterator in Object(arr)) { + return sliceIterator(arr, i); + } else { + throw new TypeError("Invalid attempt to destructure non-iterable instance"); + } + }; +}(); + + + + + + + + + + + + + +var toConsumableArray = function (arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + + return arr2; + } else { + return Array.from(arr); + } +}; + +/** Highest positive signed 32-bit float value */ + +var maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1 + +/** Bootstring parameters */ +var base = 36; +var tMin = 1; +var tMax = 26; +var skew = 38; +var damp = 700; +var initialBias = 72; +var initialN = 128; // 0x80 +var delimiter = '-'; // '\x2D' + +/** Regular expressions */ +var regexPunycode = /^xn--/; +var regexNonASCII = /[^\0-\x7E]/; // non-ASCII chars +var regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g; // RFC 3490 separators + +/** Error messages */ +var errors = { + 'overflow': 'Overflow: input needs wider integers to process', + 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', + 'invalid-input': 'Invalid input' +}; + +/** Convenience shortcuts */ +var baseMinusTMin = base - tMin; +var floor = Math.floor; +var stringFromCharCode = String.fromCharCode; + +/*--------------------------------------------------------------------------*/ + +/** + * A generic error utility function. + * @private + * @param {String} type The error type. + * @returns {Error} Throws a `RangeError` with the applicable error message. + */ +function error$1(type) { + throw new RangeError(errors[type]); +} + +/** + * A generic `Array#map` utility function. + * @private + * @param {Array} array The array to iterate over. + * @param {Function} callback The function that gets called for every array + * item. + * @returns {Array} A new array of values returned by the callback function. + */ +function map(array, fn) { + var result = []; + var length = array.length; + while (length--) { + result[length] = fn(array[length]); + } + return result; +} + +/** + * A simple `Array#map`-like wrapper to work with domain name strings or email + * addresses. + * @private + * @param {String} domain The domain name or email address. + * @param {Function} callback The function that gets called for every + * character. + * @returns {Array} A new string of characters returned by the callback + * function. + */ +function mapDomain(string, fn) { + var parts = string.split('@'); + var result = ''; + if (parts.length > 1) { + // In email addresses, only the domain name should be punycoded. Leave + // the local part (i.e. everything up to `@`) intact. + result = parts[0] + '@'; + string = parts[1]; + } + // Avoid `split(regex)` for IE8 compatibility. See #17. + string = string.replace(regexSeparators, '\x2E'); + var labels = string.split('.'); + var encoded = map(labels, fn).join('.'); + return result + encoded; +} + +/** + * Creates an array containing the numeric code points of each Unicode + * character in the string. While JavaScript uses UCS-2 internally, + * this function will convert a pair of surrogate halves (each of which + * UCS-2 exposes as separate characters) into a single code point, + * matching UTF-16. + * @see `punycode.ucs2.encode` + * @see + * @memberOf punycode.ucs2 + * @name decode + * @param {String} string The Unicode input string (UCS-2). + * @returns {Array} The new array of code points. + */ +function ucs2decode(string) { + var output = []; + var counter = 0; + var length = string.length; + while (counter < length) { + var value = string.charCodeAt(counter++); + if (value >= 0xD800 && value <= 0xDBFF && counter < length) { + // It's a high surrogate, and there is a next character. + var extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { + // Low surrogate. + output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); + } else { + // It's an unmatched surrogate; only append this code unit, in case the + // next code unit is the high surrogate of a surrogate pair. + output.push(value); + counter--; + } + } else { + output.push(value); + } + } + return output; +} + +/** + * Creates a string based on an array of numeric code points. + * @see `punycode.ucs2.decode` + * @memberOf punycode.ucs2 + * @name encode + * @param {Array} codePoints The array of numeric code points. + * @returns {String} The new Unicode string (UCS-2). + */ +var ucs2encode = function ucs2encode(array) { + return String.fromCodePoint.apply(String, toConsumableArray(array)); +}; + +/** + * Converts a basic code point into a digit/integer. + * @see `digitToBasic()` + * @private + * @param {Number} codePoint The basic numeric code point value. + * @returns {Number} The numeric value of a basic code point (for use in + * representing integers) in the range `0` to `base - 1`, or `base` if + * the code point does not represent a value. + */ +var basicToDigit = function basicToDigit(codePoint) { + if (codePoint - 0x30 < 0x0A) { + return codePoint - 0x16; + } + if (codePoint - 0x41 < 0x1A) { + return codePoint - 0x41; + } + if (codePoint - 0x61 < 0x1A) { + return codePoint - 0x61; + } + return base; +}; + +/** + * Converts a digit/integer into a basic code point. + * @see `basicToDigit()` + * @private + * @param {Number} digit The numeric value of a basic code point. + * @returns {Number} The basic code point whose value (when used for + * representing integers) is `digit`, which needs to be in the range + * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is + * used; else, the lowercase form is used. The behavior is undefined + * if `flag` is non-zero and `digit` has no uppercase form. + */ +var digitToBasic = function digitToBasic(digit, flag) { + // 0..25 map to ASCII a..z or A..Z + // 26..35 map to ASCII 0..9 + return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5); +}; + +/** + * Bias adaptation function as per section 3.4 of RFC 3492. + * https://tools.ietf.org/html/rfc3492#section-3.4 + * @private + */ +var adapt = function adapt(delta, numPoints, firstTime) { + var k = 0; + delta = firstTime ? floor(delta / damp) : delta >> 1; + delta += floor(delta / numPoints); + for (; /* no initialization */delta > baseMinusTMin * tMax >> 1; k += base) { + delta = floor(delta / baseMinusTMin); + } + return floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); +}; + +/** + * Converts a Punycode string of ASCII-only symbols to a string of Unicode + * symbols. + * @memberOf punycode + * @param {String} input The Punycode string of ASCII-only symbols. + * @returns {String} The resulting string of Unicode symbols. + */ +var decode = function decode(input) { + // Don't use UCS-2. + var output = []; + var inputLength = input.length; + var i = 0; + var n = initialN; + var bias = initialBias; + + // Handle the basic code points: let `basic` be the number of input code + // points before the last delimiter, or `0` if there is none, then copy + // the first basic code points to the output. + + var basic = input.lastIndexOf(delimiter); + if (basic < 0) { + basic = 0; + } + + for (var j = 0; j < basic; ++j) { + // if it's not a basic code point + if (input.charCodeAt(j) >= 0x80) { + error$1('not-basic'); + } + output.push(input.charCodeAt(j)); + } + + // Main decoding loop: start just after the last delimiter if any basic code + // points were copied; start at the beginning otherwise. + + for (var index = basic > 0 ? basic + 1 : 0; index < inputLength;) /* no final expression */{ + + // `index` is the index of the next character to be consumed. + // Decode a generalized variable-length integer into `delta`, + // which gets added to `i`. The overflow checking is easier + // if we increase `i` as we go, then subtract off its starting + // value at the end to obtain `delta`. + var oldi = i; + for (var w = 1, k = base;; /* no condition */k += base) { + + if (index >= inputLength) { + error$1('invalid-input'); + } + + var digit = basicToDigit(input.charCodeAt(index++)); + + if (digit >= base || digit > floor((maxInt - i) / w)) { + error$1('overflow'); + } + + i += digit * w; + var t = k <= bias ? tMin : k >= bias + tMax ? tMax : k - bias; + + if (digit < t) { + break; + } + + var baseMinusT = base - t; + if (w > floor(maxInt / baseMinusT)) { + error$1('overflow'); + } + + w *= baseMinusT; + } + + var out = output.length + 1; + bias = adapt(i - oldi, out, oldi == 0); + + // `i` was supposed to wrap around from `out` to `0`, + // incrementing `n` each time, so we'll fix that now: + if (floor(i / out) > maxInt - n) { + error$1('overflow'); + } + + n += floor(i / out); + i %= out; + + // Insert `n` at position `i` of the output. + output.splice(i++, 0, n); + } + + return String.fromCodePoint.apply(String, output); +}; + +/** + * Converts a string of Unicode symbols (e.g. a domain name label) to a + * Punycode string of ASCII-only symbols. + * @memberOf punycode + * @param {String} input The string of Unicode symbols. + * @returns {String} The resulting Punycode string of ASCII-only symbols. + */ +var encode = function encode(input) { + var output = []; + + // Convert the input in UCS-2 to an array of Unicode code points. + input = ucs2decode(input); + + // Cache the length. + var inputLength = input.length; + + // Initialize the state. + var n = initialN; + var delta = 0; + var bias = initialBias; + + // Handle the basic code points. + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = input[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var _currentValue2 = _step.value; + + if (_currentValue2 < 0x80) { + output.push(stringFromCharCode(_currentValue2)); + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + var basicLength = output.length; + var handledCPCount = basicLength; + + // `handledCPCount` is the number of code points that have been handled; + // `basicLength` is the number of basic code points. + + // Finish the basic string with a delimiter unless it's empty. + if (basicLength) { + output.push(delimiter); + } + + // Main encoding loop: + while (handledCPCount < inputLength) { + + // All non-basic code points < n have been handled already. Find the next + // larger one: + var m = maxInt; + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = input[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var currentValue = _step2.value; + + if (currentValue >= n && currentValue < m) { + m = currentValue; + } + } + + // Increase `delta` enough to advance the decoder's state to , + // but guard against overflow. + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2.return) { + _iterator2.return(); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + + var handledCPCountPlusOne = handledCPCount + 1; + if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) { + error$1('overflow'); + } + + delta += (m - n) * handledCPCountPlusOne; + n = m; + + var _iteratorNormalCompletion3 = true; + var _didIteratorError3 = false; + var _iteratorError3 = undefined; + + try { + for (var _iterator3 = input[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { + var _currentValue = _step3.value; + + if (_currentValue < n && ++delta > maxInt) { + error$1('overflow'); + } + if (_currentValue == n) { + // Represent delta as a generalized variable-length integer. + var q = delta; + for (var k = base;; /* no condition */k += base) { + var t = k <= bias ? tMin : k >= bias + tMax ? tMax : k - bias; + if (q < t) { + break; + } + var qMinusT = q - t; + var baseMinusT = base - t; + output.push(stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))); + q = floor(qMinusT / baseMinusT); + } + + output.push(stringFromCharCode(digitToBasic(q, 0))); + bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); + delta = 0; + ++handledCPCount; + } + } + } catch (err) { + _didIteratorError3 = true; + _iteratorError3 = err; + } finally { + try { + if (!_iteratorNormalCompletion3 && _iterator3.return) { + _iterator3.return(); + } + } finally { + if (_didIteratorError3) { + throw _iteratorError3; + } + } + } + + ++delta; + ++n; + } + return output.join(''); +}; + +/** + * Converts a Punycode string representing a domain name or an email address + * to Unicode. Only the Punycoded parts of the input will be converted, i.e. + * it doesn't matter if you call it on a string that has already been + * converted to Unicode. + * @memberOf punycode + * @param {String} input The Punycoded domain name or email address to + * convert to Unicode. + * @returns {String} The Unicode representation of the given Punycode + * string. + */ +var toUnicode = function toUnicode(input) { + return mapDomain(input, function (string) { + return regexPunycode.test(string) ? decode(string.slice(4).toLowerCase()) : string; + }); +}; + +/** + * Converts a Unicode string representing a domain name or an email address to + * Punycode. Only the non-ASCII parts of the domain name will be converted, + * i.e. it doesn't matter if you call it with a domain that's already in + * ASCII. + * @memberOf punycode + * @param {String} input The domain name or email address to convert, as a + * Unicode string. + * @returns {String} The Punycode representation of the given domain name or + * email address. + */ +var toASCII = function toASCII(input) { + return mapDomain(input, function (string) { + return regexNonASCII.test(string) ? 'xn--' + encode(string) : string; + }); +}; + +/*--------------------------------------------------------------------------*/ + +/** Define the public API */ +var punycode = { + /** + * A string representing the current Punycode.js version number. + * @memberOf punycode + * @type String + */ + 'version': '2.1.0', + /** + * An object of methods to convert from JavaScript's internal character + * representation (UCS-2) to Unicode code points, and back. + * @see + * @memberOf punycode + * @type Object + */ + 'ucs2': { + 'decode': ucs2decode, + 'encode': ucs2encode + }, + 'decode': decode, + 'encode': encode, + 'toASCII': toASCII, + 'toUnicode': toUnicode +}; + +/** + * URI.js + * + * @fileoverview An RFC 3986 compliant, scheme extendable URI parsing/validating/resolving library for JavaScript. + * @author Gary Court + * @see http://github.com/garycourt/uri-js + */ +/** + * Copyright 2011 Gary Court. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY GARY COURT ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARY COURT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of Gary Court. + */ +var SCHEMES = {}; +function pctEncChar(chr) { + var c = chr.charCodeAt(0); + var e = void 0; + if (c < 16) e = "%0" + c.toString(16).toUpperCase();else if (c < 128) e = "%" + c.toString(16).toUpperCase();else if (c < 2048) e = "%" + (c >> 6 | 192).toString(16).toUpperCase() + "%" + (c & 63 | 128).toString(16).toUpperCase();else e = "%" + (c >> 12 | 224).toString(16).toUpperCase() + "%" + (c >> 6 & 63 | 128).toString(16).toUpperCase() + "%" + (c & 63 | 128).toString(16).toUpperCase(); + return e; +} +function pctDecChars(str) { + var newStr = ""; + var i = 0; + var il = str.length; + while (i < il) { + var c = parseInt(str.substr(i + 1, 2), 16); + if (c < 128) { + newStr += String.fromCharCode(c); + i += 3; + } else if (c >= 194 && c < 224) { + if (il - i >= 6) { + var c2 = parseInt(str.substr(i + 4, 2), 16); + newStr += String.fromCharCode((c & 31) << 6 | c2 & 63); + } else { + newStr += str.substr(i, 6); + } + i += 6; + } else if (c >= 224) { + if (il - i >= 9) { + var _c = parseInt(str.substr(i + 4, 2), 16); + var c3 = parseInt(str.substr(i + 7, 2), 16); + newStr += String.fromCharCode((c & 15) << 12 | (_c & 63) << 6 | c3 & 63); + } else { + newStr += str.substr(i, 9); + } + i += 9; + } else { + newStr += str.substr(i, 3); + i += 3; + } + } + return newStr; +} +function _normalizeComponentEncoding(components, protocol) { + function decodeUnreserved(str) { + var decStr = pctDecChars(str); + return !decStr.match(protocol.UNRESERVED) ? str : decStr; + } + if (components.scheme) components.scheme = String(components.scheme).replace(protocol.PCT_ENCODED, decodeUnreserved).toLowerCase().replace(protocol.NOT_SCHEME, ""); + if (components.userinfo !== undefined) components.userinfo = String(components.userinfo).replace(protocol.PCT_ENCODED, decodeUnreserved).replace(protocol.NOT_USERINFO, pctEncChar).replace(protocol.PCT_ENCODED, toUpperCase); + if (components.host !== undefined) components.host = String(components.host).replace(protocol.PCT_ENCODED, decodeUnreserved).toLowerCase().replace(protocol.NOT_HOST, pctEncChar).replace(protocol.PCT_ENCODED, toUpperCase); + if (components.path !== undefined) components.path = String(components.path).replace(protocol.PCT_ENCODED, decodeUnreserved).replace(components.scheme ? protocol.NOT_PATH : protocol.NOT_PATH_NOSCHEME, pctEncChar).replace(protocol.PCT_ENCODED, toUpperCase); + if (components.query !== undefined) components.query = String(components.query).replace(protocol.PCT_ENCODED, decodeUnreserved).replace(protocol.NOT_QUERY, pctEncChar).replace(protocol.PCT_ENCODED, toUpperCase); + if (components.fragment !== undefined) components.fragment = String(components.fragment).replace(protocol.PCT_ENCODED, decodeUnreserved).replace(protocol.NOT_FRAGMENT, pctEncChar).replace(protocol.PCT_ENCODED, toUpperCase); + return components; +} + +function _stripLeadingZeros(str) { + return str.replace(/^0*(.*)/, "$1") || "0"; +} +function _normalizeIPv4(host, protocol) { + var matches = host.match(protocol.IPV4ADDRESS) || []; + + var _matches = slicedToArray(matches, 2), + address = _matches[1]; + + if (address) { + return address.split(".").map(_stripLeadingZeros).join("."); + } else { + return host; + } +} +function _normalizeIPv6(host, protocol) { + var matches = host.match(protocol.IPV6ADDRESS) || []; + + var _matches2 = slicedToArray(matches, 3), + address = _matches2[1], + zone = _matches2[2]; + + if (address) { + var _address$toLowerCase$ = address.toLowerCase().split('::').reverse(), + _address$toLowerCase$2 = slicedToArray(_address$toLowerCase$, 2), + last = _address$toLowerCase$2[0], + first = _address$toLowerCase$2[1]; + + var firstFields = first ? first.split(":").map(_stripLeadingZeros) : []; + var lastFields = last.split(":").map(_stripLeadingZeros); + var isLastFieldIPv4Address = protocol.IPV4ADDRESS.test(lastFields[lastFields.length - 1]); + var fieldCount = isLastFieldIPv4Address ? 7 : 8; + var lastFieldsStart = lastFields.length - fieldCount; + var fields = Array(fieldCount); + for (var x = 0; x < fieldCount; ++x) { + fields[x] = firstFields[x] || lastFields[lastFieldsStart + x] || ''; + } + if (isLastFieldIPv4Address) { + fields[fieldCount - 1] = _normalizeIPv4(fields[fieldCount - 1], protocol); + } + var allZeroFields = fields.reduce(function (acc, field, index) { + if (!field || field === "0") { + var lastLongest = acc[acc.length - 1]; + if (lastLongest && lastLongest.index + lastLongest.length === index) { + lastLongest.length++; + } else { + acc.push({ index: index, length: 1 }); + } + } + return acc; + }, []); + var longestZeroFields = allZeroFields.sort(function (a, b) { + return b.length - a.length; + })[0]; + var newHost = void 0; + if (longestZeroFields && longestZeroFields.length > 1) { + var newFirst = fields.slice(0, longestZeroFields.index); + var newLast = fields.slice(longestZeroFields.index + longestZeroFields.length); + newHost = newFirst.join(":") + "::" + newLast.join(":"); + } else { + newHost = fields.join(":"); + } + if (zone) { + newHost += "%" + zone; + } + return newHost; + } else { + return host; + } +} +var URI_PARSE = /^(?:([^:\/?#]+):)?(?:\/\/((?:([^\/?#@]*)@)?(\[[^\/?#\]]+\]|[^\/?#:]*)(?:\:(\d*))?))?([^?#]*)(?:\?([^#]*))?(?:#((?:.|\n|\r)*))?/i; +var NO_MATCH_IS_UNDEFINED = "".match(/(){0}/)[1] === undefined; +function parse(uriString) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + var components = {}; + var protocol = options.iri !== false ? IRI_PROTOCOL : URI_PROTOCOL; + if (options.reference === "suffix") uriString = (options.scheme ? options.scheme + ":" : "") + "//" + uriString; + var matches = uriString.match(URI_PARSE); + if (matches) { + if (NO_MATCH_IS_UNDEFINED) { + //store each component + components.scheme = matches[1]; + components.userinfo = matches[3]; + components.host = matches[4]; + components.port = parseInt(matches[5], 10); + components.path = matches[6] || ""; + components.query = matches[7]; + components.fragment = matches[8]; + //fix port number + if (isNaN(components.port)) { + components.port = matches[5]; + } + } else { + //IE FIX for improper RegExp matching + //store each component + components.scheme = matches[1] || undefined; + components.userinfo = uriString.indexOf("@") !== -1 ? matches[3] : undefined; + components.host = uriString.indexOf("//") !== -1 ? matches[4] : undefined; + components.port = parseInt(matches[5], 10); + components.path = matches[6] || ""; + components.query = uriString.indexOf("?") !== -1 ? matches[7] : undefined; + components.fragment = uriString.indexOf("#") !== -1 ? matches[8] : undefined; + //fix port number + if (isNaN(components.port)) { + components.port = uriString.match(/\/\/(?:.|\n)*\:(?:\/|\?|\#|$)/) ? matches[4] : undefined; + } + } + if (components.host) { + //normalize IP hosts + components.host = _normalizeIPv6(_normalizeIPv4(components.host, protocol), protocol); + } + //determine reference type + if (components.scheme === undefined && components.userinfo === undefined && components.host === undefined && components.port === undefined && !components.path && components.query === undefined) { + components.reference = "same-document"; + } else if (components.scheme === undefined) { + components.reference = "relative"; + } else if (components.fragment === undefined) { + components.reference = "absolute"; + } else { + components.reference = "uri"; + } + //check for reference errors + if (options.reference && options.reference !== "suffix" && options.reference !== components.reference) { + components.error = components.error || "URI is not a " + options.reference + " reference."; + } + //find scheme handler + var schemeHandler = SCHEMES[(options.scheme || components.scheme || "").toLowerCase()]; + //check if scheme can't handle IRIs + if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) { + //if host component is a domain name + if (components.host && (options.domainHost || schemeHandler && schemeHandler.domainHost)) { + //convert Unicode IDN -> ASCII IDN + try { + components.host = punycode.toASCII(components.host.replace(protocol.PCT_ENCODED, pctDecChars).toLowerCase()); + } catch (e) { + components.error = components.error || "Host's domain name can not be converted to ASCII via punycode: " + e; + } + } + //convert IRI -> URI + _normalizeComponentEncoding(components, URI_PROTOCOL); + } else { + //normalize encodings + _normalizeComponentEncoding(components, protocol); + } + //perform scheme specific parsing + if (schemeHandler && schemeHandler.parse) { + schemeHandler.parse(components, options); + } + } else { + components.error = components.error || "URI can not be parsed."; + } + return components; +} + +function _recomposeAuthority(components, options) { + var protocol = options.iri !== false ? IRI_PROTOCOL : URI_PROTOCOL; + var uriTokens = []; + if (components.userinfo !== undefined) { + uriTokens.push(components.userinfo); + uriTokens.push("@"); + } + if (components.host !== undefined) { + //normalize IP hosts, add brackets and escape zone separator for IPv6 + uriTokens.push(_normalizeIPv6(_normalizeIPv4(String(components.host), protocol), protocol).replace(protocol.IPV6ADDRESS, function (_, $1, $2) { + return "[" + $1 + ($2 ? "%25" + $2 : "") + "]"; + })); + } + if (typeof components.port === "number") { + uriTokens.push(":"); + uriTokens.push(components.port.toString(10)); + } + return uriTokens.length ? uriTokens.join("") : undefined; +} + +var RDS1 = /^\.\.?\//; +var RDS2 = /^\/\.(\/|$)/; +var RDS3 = /^\/\.\.(\/|$)/; +var RDS5 = /^\/?(?:.|\n)*?(?=\/|$)/; +function removeDotSegments(input) { + var output = []; + while (input.length) { + if (input.match(RDS1)) { + input = input.replace(RDS1, ""); + } else if (input.match(RDS2)) { + input = input.replace(RDS2, "/"); + } else if (input.match(RDS3)) { + input = input.replace(RDS3, "/"); + output.pop(); + } else if (input === "." || input === "..") { + input = ""; + } else { + var im = input.match(RDS5); + if (im) { + var s = im[0]; + input = input.slice(s.length); + output.push(s); + } else { + throw new Error("Unexpected dot segment condition"); + } + } + } + return output.join(""); +} + +function serialize(components) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + var protocol = options.iri ? IRI_PROTOCOL : URI_PROTOCOL; + var uriTokens = []; + //find scheme handler + var schemeHandler = SCHEMES[(options.scheme || components.scheme || "").toLowerCase()]; + //perform scheme specific serialization + if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(components, options); + if (components.host) { + //if host component is an IPv6 address + if (protocol.IPV6ADDRESS.test(components.host)) {} + //TODO: normalize IPv6 address as per RFC 5952 + + //if host component is a domain name + else if (options.domainHost || schemeHandler && schemeHandler.domainHost) { + //convert IDN via punycode + try { + components.host = !options.iri ? punycode.toASCII(components.host.replace(protocol.PCT_ENCODED, pctDecChars).toLowerCase()) : punycode.toUnicode(components.host); + } catch (e) { + components.error = components.error || "Host's domain name can not be converted to " + (!options.iri ? "ASCII" : "Unicode") + " via punycode: " + e; + } + } + } + //normalize encoding + _normalizeComponentEncoding(components, protocol); + if (options.reference !== "suffix" && components.scheme) { + uriTokens.push(components.scheme); + uriTokens.push(":"); + } + var authority = _recomposeAuthority(components, options); + if (authority !== undefined) { + if (options.reference !== "suffix") { + uriTokens.push("//"); + } + uriTokens.push(authority); + if (components.path && components.path.charAt(0) !== "/") { + uriTokens.push("/"); + } + } + if (components.path !== undefined) { + var s = components.path; + if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) { + s = removeDotSegments(s); + } + if (authority === undefined) { + s = s.replace(/^\/\//, "/%2F"); //don't allow the path to start with "//" + } + uriTokens.push(s); + } + if (components.query !== undefined) { + uriTokens.push("?"); + uriTokens.push(components.query); + } + if (components.fragment !== undefined) { + uriTokens.push("#"); + uriTokens.push(components.fragment); + } + return uriTokens.join(""); //merge tokens into a string +} + +function resolveComponents(base, relative) { + var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + var skipNormalization = arguments[3]; + + var target = {}; + if (!skipNormalization) { + base = parse(serialize(base, options), options); //normalize base components + relative = parse(serialize(relative, options), options); //normalize relative components + } + options = options || {}; + if (!options.tolerant && relative.scheme) { + target.scheme = relative.scheme; + //target.authority = relative.authority; + target.userinfo = relative.userinfo; + target.host = relative.host; + target.port = relative.port; + target.path = removeDotSegments(relative.path || ""); + target.query = relative.query; + } else { + if (relative.userinfo !== undefined || relative.host !== undefined || relative.port !== undefined) { + //target.authority = relative.authority; + target.userinfo = relative.userinfo; + target.host = relative.host; + target.port = relative.port; + target.path = removeDotSegments(relative.path || ""); + target.query = relative.query; + } else { + if (!relative.path) { + target.path = base.path; + if (relative.query !== undefined) { + target.query = relative.query; + } else { + target.query = base.query; + } + } else { + if (relative.path.charAt(0) === "/") { + target.path = removeDotSegments(relative.path); + } else { + if ((base.userinfo !== undefined || base.host !== undefined || base.port !== undefined) && !base.path) { + target.path = "/" + relative.path; + } else if (!base.path) { + target.path = relative.path; + } else { + target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative.path; + } + target.path = removeDotSegments(target.path); + } + target.query = relative.query; + } + //target.authority = base.authority; + target.userinfo = base.userinfo; + target.host = base.host; + target.port = base.port; + } + target.scheme = base.scheme; + } + target.fragment = relative.fragment; + return target; +} + +function resolve(baseURI, relativeURI, options) { + var schemelessOptions = assign({ scheme: 'null' }, options); + return serialize(resolveComponents(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true), schemelessOptions); +} + +function normalize(uri, options) { + if (typeof uri === "string") { + uri = serialize(parse(uri, options), options); + } else if (typeOf(uri) === "object") { + uri = parse(serialize(uri, options), options); + } + return uri; +} + +function equal(uriA, uriB, options) { + if (typeof uriA === "string") { + uriA = serialize(parse(uriA, options), options); + } else if (typeOf(uriA) === "object") { + uriA = serialize(uriA, options); + } + if (typeof uriB === "string") { + uriB = serialize(parse(uriB, options), options); + } else if (typeOf(uriB) === "object") { + uriB = serialize(uriB, options); + } + return uriA === uriB; +} + +function escapeComponent(str, options) { + return str && str.toString().replace(!options || !options.iri ? URI_PROTOCOL.ESCAPE : IRI_PROTOCOL.ESCAPE, pctEncChar); +} + +function unescapeComponent(str, options) { + return str && str.toString().replace(!options || !options.iri ? URI_PROTOCOL.PCT_ENCODED : IRI_PROTOCOL.PCT_ENCODED, pctDecChars); +} + +var handler = { + scheme: "http", + domainHost: true, + parse: function parse(components, options) { + //report missing host + if (!components.host) { + components.error = components.error || "HTTP URIs must have a host."; + } + return components; + }, + serialize: function serialize(components, options) { + //normalize the default port + if (components.port === (String(components.scheme).toLowerCase() !== "https" ? 80 : 443) || components.port === "") { + components.port = undefined; + } + //normalize the empty path + if (!components.path) { + components.path = "/"; + } + //NOTE: We do not parse query strings for HTTP URIs + //as WWW Form Url Encoded query strings are part of the HTML4+ spec, + //and not the HTTP spec. + return components; + } +}; + +var handler$1 = { + scheme: "https", + domainHost: handler.domainHost, + parse: handler.parse, + serialize: handler.serialize +}; + +var O = {}; +var isIRI = true; +//RFC 3986 +var UNRESERVED$$ = "[A-Za-z0-9\\-\\.\\_\\~" + (isIRI ? "\\xA0-\\u200D\\u2010-\\u2029\\u202F-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF" : "") + "]"; +var HEXDIG$$ = "[0-9A-Fa-f]"; //case-insensitive +var PCT_ENCODED$ = subexp(subexp("%[EFef]" + HEXDIG$$ + "%" + HEXDIG$$ + HEXDIG$$ + "%" + HEXDIG$$ + HEXDIG$$) + "|" + subexp("%[89A-Fa-f]" + HEXDIG$$ + "%" + HEXDIG$$ + HEXDIG$$) + "|" + subexp("%" + HEXDIG$$ + HEXDIG$$)); //expanded +//RFC 5322, except these symbols as per RFC 6068: @ : / ? # [ ] & ; = +//const ATEXT$$ = "[A-Za-z0-9\\!\\#\\$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\_\\`\\{\\|\\}\\~]"; +//const WSP$$ = "[\\x20\\x09]"; +//const OBS_QTEXT$$ = "[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]"; //(%d1-8 / %d11-12 / %d14-31 / %d127) +//const QTEXT$$ = merge("[\\x21\\x23-\\x5B\\x5D-\\x7E]", OBS_QTEXT$$); //%d33 / %d35-91 / %d93-126 / obs-qtext +//const VCHAR$$ = "[\\x21-\\x7E]"; +//const WSP$$ = "[\\x20\\x09]"; +//const OBS_QP$ = subexp("\\\\" + merge("[\\x00\\x0D\\x0A]", OBS_QTEXT$$)); //%d0 / CR / LF / obs-qtext +//const FWS$ = subexp(subexp(WSP$$ + "*" + "\\x0D\\x0A") + "?" + WSP$$ + "+"); +//const QUOTED_PAIR$ = subexp(subexp("\\\\" + subexp(VCHAR$$ + "|" + WSP$$)) + "|" + OBS_QP$); +//const QUOTED_STRING$ = subexp('\\"' + subexp(FWS$ + "?" + QCONTENT$) + "*" + FWS$ + "?" + '\\"'); +var ATEXT$$ = "[A-Za-z0-9\\!\\$\\%\\'\\*\\+\\-\\^\\_\\`\\{\\|\\}\\~]"; +var QTEXT$$ = "[\\!\\$\\%\\'\\(\\)\\*\\+\\,\\-\\.0-9\\<\\>A-Z\\x5E-\\x7E]"; +var VCHAR$$ = merge(QTEXT$$, "[\\\"\\\\]"); +var SOME_DELIMS$$ = "[\\!\\$\\'\\(\\)\\*\\+\\,\\;\\:\\@]"; +var UNRESERVED = new RegExp(UNRESERVED$$, "g"); +var PCT_ENCODED = new RegExp(PCT_ENCODED$, "g"); +var NOT_LOCAL_PART = new RegExp(merge("[^]", ATEXT$$, "[\\.]", '[\\"]', VCHAR$$), "g"); +var NOT_HFNAME = new RegExp(merge("[^]", UNRESERVED$$, SOME_DELIMS$$), "g"); +var NOT_HFVALUE = NOT_HFNAME; +function decodeUnreserved(str) { + var decStr = pctDecChars(str); + return !decStr.match(UNRESERVED) ? str : decStr; +} +var handler$2 = { + scheme: "mailto", + parse: function parse$$1(components, options) { + var mailtoComponents = components; + var to = mailtoComponents.to = mailtoComponents.path ? mailtoComponents.path.split(",") : []; + mailtoComponents.path = undefined; + if (mailtoComponents.query) { + var unknownHeaders = false; + var headers = {}; + var hfields = mailtoComponents.query.split("&"); + for (var x = 0, xl = hfields.length; x < xl; ++x) { + var hfield = hfields[x].split("="); + switch (hfield[0]) { + case "to": + var toAddrs = hfield[1].split(","); + for (var _x = 0, _xl = toAddrs.length; _x < _xl; ++_x) { + to.push(toAddrs[_x]); + } + break; + case "subject": + mailtoComponents.subject = unescapeComponent(hfield[1], options); + break; + case "body": + mailtoComponents.body = unescapeComponent(hfield[1], options); + break; + default: + unknownHeaders = true; + headers[unescapeComponent(hfield[0], options)] = unescapeComponent(hfield[1], options); + break; + } + } + if (unknownHeaders) mailtoComponents.headers = headers; + } + mailtoComponents.query = undefined; + for (var _x2 = 0, _xl2 = to.length; _x2 < _xl2; ++_x2) { + var addr = to[_x2].split("@"); + addr[0] = unescapeComponent(addr[0]); + if (!options.unicodeSupport) { + //convert Unicode IDN -> ASCII IDN + try { + addr[1] = punycode.toASCII(unescapeComponent(addr[1], options).toLowerCase()); + } catch (e) { + mailtoComponents.error = mailtoComponents.error || "Email address's domain name can not be converted to ASCII via punycode: " + e; + } + } else { + addr[1] = unescapeComponent(addr[1], options).toLowerCase(); + } + to[_x2] = addr.join("@"); + } + return mailtoComponents; + }, + serialize: function serialize$$1(mailtoComponents, options) { + var components = mailtoComponents; + var to = toArray(mailtoComponents.to); + if (to) { + for (var x = 0, xl = to.length; x < xl; ++x) { + var toAddr = String(to[x]); + var atIdx = toAddr.lastIndexOf("@"); + var localPart = toAddr.slice(0, atIdx).replace(PCT_ENCODED, decodeUnreserved).replace(PCT_ENCODED, toUpperCase).replace(NOT_LOCAL_PART, pctEncChar); + var domain = toAddr.slice(atIdx + 1); + //convert IDN via punycode + try { + domain = !options.iri ? punycode.toASCII(unescapeComponent(domain, options).toLowerCase()) : punycode.toUnicode(domain); + } catch (e) { + components.error = components.error || "Email address's domain name can not be converted to " + (!options.iri ? "ASCII" : "Unicode") + " via punycode: " + e; + } + to[x] = localPart + "@" + domain; + } + components.path = to.join(","); + } + var headers = mailtoComponents.headers = mailtoComponents.headers || {}; + if (mailtoComponents.subject) headers["subject"] = mailtoComponents.subject; + if (mailtoComponents.body) headers["body"] = mailtoComponents.body; + var fields = []; + for (var name in headers) { + if (headers[name] !== O[name]) { + fields.push(name.replace(PCT_ENCODED, decodeUnreserved).replace(PCT_ENCODED, toUpperCase).replace(NOT_HFNAME, pctEncChar) + "=" + headers[name].replace(PCT_ENCODED, decodeUnreserved).replace(PCT_ENCODED, toUpperCase).replace(NOT_HFVALUE, pctEncChar)); + } + } + if (fields.length) { + components.query = fields.join("&"); + } + return components; + } +}; + +var URN_PARSE = /^([^\:]+)\:(.*)/; +//RFC 2141 +var handler$3 = { + scheme: "urn", + parse: function parse$$1(components, options) { + var matches = components.path && components.path.match(URN_PARSE); + var urnComponents = components; + if (matches) { + var scheme = options.scheme || urnComponents.scheme || "urn"; + var nid = matches[1].toLowerCase(); + var nss = matches[2]; + var urnScheme = scheme + ":" + (options.nid || nid); + var schemeHandler = SCHEMES[urnScheme]; + urnComponents.nid = nid; + urnComponents.nss = nss; + urnComponents.path = undefined; + if (schemeHandler) { + urnComponents = schemeHandler.parse(urnComponents, options); + } + } else { + urnComponents.error = urnComponents.error || "URN can not be parsed."; + } + return urnComponents; + }, + serialize: function serialize$$1(urnComponents, options) { + var scheme = options.scheme || urnComponents.scheme || "urn"; + var nid = urnComponents.nid; + var urnScheme = scheme + ":" + (options.nid || nid); + var schemeHandler = SCHEMES[urnScheme]; + if (schemeHandler) { + urnComponents = schemeHandler.serialize(urnComponents, options); + } + var uriComponents = urnComponents; + var nss = urnComponents.nss; + uriComponents.path = (nid || options.nid) + ":" + nss; + return uriComponents; + } +}; + +var UUID = /^[0-9A-Fa-f]{8}(?:\-[0-9A-Fa-f]{4}){3}\-[0-9A-Fa-f]{12}$/; +//RFC 4122 +var handler$4 = { + scheme: "urn:uuid", + parse: function parse(urnComponents, options) { + var uuidComponents = urnComponents; + uuidComponents.uuid = uuidComponents.nss; + uuidComponents.nss = undefined; + if (!options.tolerant && (!uuidComponents.uuid || !uuidComponents.uuid.match(UUID))) { + uuidComponents.error = uuidComponents.error || "UUID is not valid."; + } + return uuidComponents; + }, + serialize: function serialize(uuidComponents, options) { + var urnComponents = uuidComponents; + //normalize UUID + urnComponents.nss = (uuidComponents.uuid || "").toLowerCase(); + return urnComponents; + } +}; + +SCHEMES[handler.scheme] = handler; +SCHEMES[handler$1.scheme] = handler$1; +SCHEMES[handler$2.scheme] = handler$2; +SCHEMES[handler$3.scheme] = handler$3; +SCHEMES[handler$4.scheme] = handler$4; + +exports.SCHEMES = SCHEMES; +exports.pctEncChar = pctEncChar; +exports.pctDecChars = pctDecChars; +exports.parse = parse; +exports.removeDotSegments = removeDotSegments; +exports.serialize = serialize; +exports.resolveComponents = resolveComponents; +exports.resolve = resolve; +exports.normalize = normalize; +exports.equal = equal; +exports.escapeComponent = escapeComponent; +exports.unescapeComponent = unescapeComponent; + +Object.defineProperty(exports, '__esModule', { value: true }); + +}))); +//# sourceMappingURL=uri.all.js.map + + +/***/ }), + +/***/ 855: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + + +module.exports = { + copy: copy, + checkDataType: checkDataType, + checkDataTypes: checkDataTypes, + coerceToTypes: coerceToTypes, + toHash: toHash, + getProperty: getProperty, + escapeQuotes: escapeQuotes, + equal: __webpack_require__(832), + ucs2length: __webpack_require__(691), + varOccurences: varOccurences, + varReplace: varReplace, + cleanUpCode: cleanUpCode, + finalCleanUpCode: finalCleanUpCode, + schemaHasRules: schemaHasRules, + schemaHasRulesExcept: schemaHasRulesExcept, + schemaUnknownRules: schemaUnknownRules, + toQuotedString: toQuotedString, + getPathExpr: getPathExpr, + getPath: getPath, + getData: getData, + unescapeFragment: unescapeFragment, + unescapeJsonPointer: unescapeJsonPointer, + escapeFragment: escapeFragment, + escapeJsonPointer: escapeJsonPointer +}; + + +function copy(o, to) { + to = to || {}; + for (var key in o) to[key] = o[key]; + return to; +} + + +function checkDataType(dataType, data, negate) { + var EQUAL = negate ? ' !== ' : ' === ' + , AND = negate ? ' || ' : ' && ' + , OK = negate ? '!' : '' + , NOT = negate ? '' : '!'; + switch (dataType) { + case 'null': return data + EQUAL + 'null'; + case 'array': return OK + 'Array.isArray(' + data + ')'; + case 'object': return '(' + OK + data + AND + + 'typeof ' + data + EQUAL + '"object"' + AND + + NOT + 'Array.isArray(' + data + '))'; + case 'integer': return '(typeof ' + data + EQUAL + '"number"' + AND + + NOT + '(' + data + ' % 1)' + + AND + data + EQUAL + data + ')'; + default: return 'typeof ' + data + EQUAL + '"' + dataType + '"'; + } +} + + +function checkDataTypes(dataTypes, data) { + switch (dataTypes.length) { + case 1: return checkDataType(dataTypes[0], data, true); + default: + var code = ''; + var types = toHash(dataTypes); + if (types.array && types.object) { + code = types.null ? '(': '(!' + data + ' || '; + code += 'typeof ' + data + ' !== "object")'; + delete types.null; + delete types.array; + delete types.object; + } + if (types.number) delete types.integer; + for (var t in types) + code += (code ? ' && ' : '' ) + checkDataType(t, data, true); + + return code; + } +} + + +var COERCE_TO_TYPES = toHash([ 'string', 'number', 'integer', 'boolean', 'null' ]); +function coerceToTypes(optionCoerceTypes, dataTypes) { + if (Array.isArray(dataTypes)) { + var types = []; + for (var i=0; i= lvl) throw new Error('Cannot access property/index ' + up + ' levels up, current level is ' + lvl); + return paths[lvl - up]; + } + + if (up > lvl) throw new Error('Cannot access data ' + up + ' levels up, current level is ' + lvl); + data = 'data' + ((lvl - up) || ''); + if (!jsonPointer) return data; + } + + var expr = data; + var segments = jsonPointer.split('/'); + for (var i=0; i 0 : it.util.schemaHasRules($propertySch, it.RULES.all)))) { + $required[$required.length] = $property; + } + } + } + } else { + var $required = $schema; + } + } + if ($isData || $required.length) { + var $currentErrorPath = it.errorPath, + $loopRequired = $isData || $required.length >= it.opts.loopRequired, + $ownProperties = it.opts.ownProperties; + if ($breakOnError) { + out += ' var missing' + ($lvl) + '; '; + if ($loopRequired) { + if (!$isData) { + out += ' var ' + ($vSchema) + ' = validate.schema' + ($schemaPath) + '; '; + } + var $i = 'i' + $lvl, + $propertyPath = 'schema' + $lvl + '[' + $i + ']', + $missingProperty = '\' + ' + $propertyPath + ' + \''; + if (it.opts._errorDataPathProperty) { + it.errorPath = it.util.getPathExpr($currentErrorPath, $propertyPath, it.opts.jsonPointers); + } + out += ' var ' + ($valid) + ' = true; '; + if ($isData) { + out += ' if (schema' + ($lvl) + ' === undefined) ' + ($valid) + ' = true; else if (!Array.isArray(schema' + ($lvl) + ')) ' + ($valid) + ' = false; else {'; + } + out += ' for (var ' + ($i) + ' = 0; ' + ($i) + ' < ' + ($vSchema) + '.length; ' + ($i) + '++) { ' + ($valid) + ' = ' + ($data) + '[' + ($vSchema) + '[' + ($i) + ']] !== undefined '; + if ($ownProperties) { + out += ' && Object.prototype.hasOwnProperty.call(' + ($data) + ', ' + ($vSchema) + '[' + ($i) + ']) '; + } + out += '; if (!' + ($valid) + ') break; } '; + if ($isData) { + out += ' } '; + } + out += ' if (!' + ($valid) + ') { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \''; + if (it.opts._errorDataPathProperty) { + out += 'is a required property'; + } else { + out += 'should have required property \\\'' + ($missingProperty) + '\\\''; + } + out += '\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } else { '; + } else { + out += ' if ( '; + var arr2 = $required; + if (arr2) { + var $propertyKey, $i = -1, + l2 = arr2.length - 1; + while ($i < l2) { + $propertyKey = arr2[$i += 1]; + if ($i) { + out += ' || '; + } + var $prop = it.util.getProperty($propertyKey), + $useData = $data + $prop; + out += ' ( ( ' + ($useData) + ' === undefined '; + if ($ownProperties) { + out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') '; + } + out += ') && (missing' + ($lvl) + ' = ' + (it.util.toQuotedString(it.opts.jsonPointers ? $propertyKey : $prop)) + ') ) '; + } + } + out += ') { '; + var $propertyPath = 'missing' + $lvl, + $missingProperty = '\' + ' + $propertyPath + ' + \''; + if (it.opts._errorDataPathProperty) { + it.errorPath = it.opts.jsonPointers ? it.util.getPathExpr($currentErrorPath, $propertyPath, true) : $currentErrorPath + ' + ' + $propertyPath; + } + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \''; + if (it.opts._errorDataPathProperty) { + out += 'is a required property'; + } else { + out += 'should have required property \\\'' + ($missingProperty) + '\\\''; + } + out += '\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } else { '; + } + } else { + if ($loopRequired) { + if (!$isData) { + out += ' var ' + ($vSchema) + ' = validate.schema' + ($schemaPath) + '; '; + } + var $i = 'i' + $lvl, + $propertyPath = 'schema' + $lvl + '[' + $i + ']', + $missingProperty = '\' + ' + $propertyPath + ' + \''; + if (it.opts._errorDataPathProperty) { + it.errorPath = it.util.getPathExpr($currentErrorPath, $propertyPath, it.opts.jsonPointers); + } + if ($isData) { + out += ' if (' + ($vSchema) + ' && !Array.isArray(' + ($vSchema) + ')) { var err = '; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \''; + if (it.opts._errorDataPathProperty) { + out += 'is a required property'; + } else { + out += 'should have required property \\\'' + ($missingProperty) + '\\\''; + } + out += '\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + out += '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } else if (' + ($vSchema) + ' !== undefined) { '; + } + out += ' for (var ' + ($i) + ' = 0; ' + ($i) + ' < ' + ($vSchema) + '.length; ' + ($i) + '++) { if (' + ($data) + '[' + ($vSchema) + '[' + ($i) + ']] === undefined '; + if ($ownProperties) { + out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', ' + ($vSchema) + '[' + ($i) + ']) '; + } + out += ') { var err = '; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \''; + if (it.opts._errorDataPathProperty) { + out += 'is a required property'; + } else { + out += 'should have required property \\\'' + ($missingProperty) + '\\\''; + } + out += '\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + out += '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } } '; + if ($isData) { + out += ' } '; + } + } else { + var arr3 = $required; + if (arr3) { + var $propertyKey, i3 = -1, + l3 = arr3.length - 1; + while (i3 < l3) { + $propertyKey = arr3[i3 += 1]; + var $prop = it.util.getProperty($propertyKey), + $missingProperty = it.util.escapeQuotes($propertyKey), + $useData = $data + $prop; + if (it.opts._errorDataPathProperty) { + it.errorPath = it.util.getPath($currentErrorPath, $propertyKey, it.opts.jsonPointers); + } + out += ' if ( ' + ($useData) + ' === undefined '; + if ($ownProperties) { + out += ' || ! Object.prototype.hasOwnProperty.call(' + ($data) + ', \'' + (it.util.escapeQuotes($propertyKey)) + '\') '; + } + out += ') { var err = '; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('required') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { missingProperty: \'' + ($missingProperty) + '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \''; + if (it.opts._errorDataPathProperty) { + out += 'is a required property'; + } else { + out += 'should have required property \\\'' + ($missingProperty) + '\\\''; + } + out += '\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + out += '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } '; + } + } + } + } + it.errorPath = $currentErrorPath; + } else if ($breakOnError) { + out += ' if (true) {'; + } + return out; +} + + +/***/ }), + +/***/ 866: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2017 Joyent, Inc. + +module.exports = { + read: read, + verify: verify, + sign: sign, + signAsync: signAsync, + write: write +}; + +var assert = __webpack_require__(477); +var asn1 = __webpack_require__(62); +var Buffer = __webpack_require__(215).Buffer; +var algs = __webpack_require__(98); +var utils = __webpack_require__(270); +var Key = __webpack_require__(852); +var PrivateKey = __webpack_require__(502); +var pem = __webpack_require__(268); +var Identity = __webpack_require__(378); +var Signature = __webpack_require__(575); +var Certificate = __webpack_require__(752); +var pkcs8 = __webpack_require__(707); + +/* + * This file is based on RFC5280 (X.509). + */ + +/* Helper to read in a single mpint */ +function readMPInt(der, nm) { + assert.strictEqual(der.peek(), asn1.Ber.Integer, + nm + ' is not an Integer'); + return (utils.mpNormalize(der.readString(asn1.Ber.Integer, true))); +} + +function verify(cert, key) { + var sig = cert.signatures.x509; + assert.object(sig, 'x509 signature'); + + var algParts = sig.algo.split('-'); + if (algParts[0] !== key.type) + return (false); + + var blob = sig.cache; + if (blob === undefined) { + var der = new asn1.BerWriter(); + writeTBSCert(cert, der); + blob = der.buffer; + } + + var verifier = key.createVerify(algParts[1]); + verifier.write(blob); + return (verifier.verify(sig.signature)); +} + +function Local(i) { + return (asn1.Ber.Context | asn1.Ber.Constructor | i); +} + +function Context(i) { + return (asn1.Ber.Context | i); +} + +var SIGN_ALGS = { + 'rsa-md5': '1.2.840.113549.1.1.4', + 'rsa-sha1': '1.2.840.113549.1.1.5', + 'rsa-sha256': '1.2.840.113549.1.1.11', + 'rsa-sha384': '1.2.840.113549.1.1.12', + 'rsa-sha512': '1.2.840.113549.1.1.13', + 'dsa-sha1': '1.2.840.10040.4.3', + 'dsa-sha256': '2.16.840.1.101.3.4.3.2', + 'ecdsa-sha1': '1.2.840.10045.4.1', + 'ecdsa-sha256': '1.2.840.10045.4.3.2', + 'ecdsa-sha384': '1.2.840.10045.4.3.3', + 'ecdsa-sha512': '1.2.840.10045.4.3.4', + 'ed25519-sha512': '1.3.101.112' +}; +Object.keys(SIGN_ALGS).forEach(function (k) { + SIGN_ALGS[SIGN_ALGS[k]] = k; +}); +SIGN_ALGS['1.3.14.3.2.3'] = 'rsa-md5'; +SIGN_ALGS['1.3.14.3.2.29'] = 'rsa-sha1'; + +var EXTS = { + 'issuerKeyId': '2.5.29.35', + 'altName': '2.5.29.17', + 'basicConstraints': '2.5.29.19', + 'keyUsage': '2.5.29.15', + 'extKeyUsage': '2.5.29.37' +}; + +function read(buf, options) { + if (typeof (buf) === 'string') { + buf = Buffer.from(buf, 'binary'); + } + assert.buffer(buf, 'buf'); + + var der = new asn1.BerReader(buf); + + der.readSequence(); + if (Math.abs(der.length - der.remain) > 1) { + throw (new Error('DER sequence does not contain whole byte ' + + 'stream')); + } + + var tbsStart = der.offset; + der.readSequence(); + var sigOffset = der.offset + der.length; + var tbsEnd = sigOffset; + + if (der.peek() === Local(0)) { + der.readSequence(Local(0)); + var version = der.readInt(); + assert.ok(version <= 3, + 'only x.509 versions up to v3 supported'); + } + + var cert = {}; + cert.signatures = {}; + var sig = (cert.signatures.x509 = {}); + sig.extras = {}; + + cert.serial = readMPInt(der, 'serial'); + + der.readSequence(); + var after = der.offset + der.length; + var certAlgOid = der.readOID(); + var certAlg = SIGN_ALGS[certAlgOid]; + if (certAlg === undefined) + throw (new Error('unknown signature algorithm ' + certAlgOid)); + + der._offset = after; + cert.issuer = Identity.parseAsn1(der); + + der.readSequence(); + cert.validFrom = readDate(der); + cert.validUntil = readDate(der); + + cert.subjects = [Identity.parseAsn1(der)]; + + der.readSequence(); + after = der.offset + der.length; + cert.subjectKey = pkcs8.readPkcs8(undefined, 'public', der); + der._offset = after; + + /* issuerUniqueID */ + if (der.peek() === Local(1)) { + der.readSequence(Local(1)); + sig.extras.issuerUniqueID = + buf.slice(der.offset, der.offset + der.length); + der._offset += der.length; + } + + /* subjectUniqueID */ + if (der.peek() === Local(2)) { + der.readSequence(Local(2)); + sig.extras.subjectUniqueID = + buf.slice(der.offset, der.offset + der.length); + der._offset += der.length; + } + + /* extensions */ + if (der.peek() === Local(3)) { + der.readSequence(Local(3)); + var extEnd = der.offset + der.length; + der.readSequence(); + + while (der.offset < extEnd) + readExtension(cert, buf, der); + + assert.strictEqual(der.offset, extEnd); + } + + assert.strictEqual(der.offset, sigOffset); + + der.readSequence(); + after = der.offset + der.length; + var sigAlgOid = der.readOID(); + var sigAlg = SIGN_ALGS[sigAlgOid]; + if (sigAlg === undefined) + throw (new Error('unknown signature algorithm ' + sigAlgOid)); + der._offset = after; + + var sigData = der.readString(asn1.Ber.BitString, true); + if (sigData[0] === 0) + sigData = sigData.slice(1); + var algParts = sigAlg.split('-'); + + sig.signature = Signature.parse(sigData, algParts[0], 'asn1'); + sig.signature.hashAlgorithm = algParts[1]; + sig.algo = sigAlg; + sig.cache = buf.slice(tbsStart, tbsEnd); + + return (new Certificate(cert)); +} + +function readDate(der) { + if (der.peek() === asn1.Ber.UTCTime) { + return (utcTimeToDate(der.readString(asn1.Ber.UTCTime))); + } else if (der.peek() === asn1.Ber.GeneralizedTime) { + return (gTimeToDate(der.readString(asn1.Ber.GeneralizedTime))); + } else { + throw (new Error('Unsupported date format')); + } +} + +function writeDate(der, date) { + if (date.getUTCFullYear() >= 2050 || date.getUTCFullYear() < 1950) { + der.writeString(dateToGTime(date), asn1.Ber.GeneralizedTime); + } else { + der.writeString(dateToUTCTime(date), asn1.Ber.UTCTime); + } +} + +/* RFC5280, section 4.2.1.6 (GeneralName type) */ +var ALTNAME = { + OtherName: Local(0), + RFC822Name: Context(1), + DNSName: Context(2), + X400Address: Local(3), + DirectoryName: Local(4), + EDIPartyName: Local(5), + URI: Context(6), + IPAddress: Context(7), + OID: Context(8) +}; + +/* RFC5280, section 4.2.1.12 (KeyPurposeId) */ +var EXTPURPOSE = { + 'serverAuth': '1.3.6.1.5.5.7.3.1', + 'clientAuth': '1.3.6.1.5.5.7.3.2', + 'codeSigning': '1.3.6.1.5.5.7.3.3', + + /* See https://github.com/joyent/oid-docs/blob/master/root.md */ + 'joyentDocker': '1.3.6.1.4.1.38678.1.4.1', + 'joyentCmon': '1.3.6.1.4.1.38678.1.4.2' +}; +var EXTPURPOSE_REV = {}; +Object.keys(EXTPURPOSE).forEach(function (k) { + EXTPURPOSE_REV[EXTPURPOSE[k]] = k; +}); + +var KEYUSEBITS = [ + 'signature', 'identity', 'keyEncryption', + 'encryption', 'keyAgreement', 'ca', 'crl' +]; + +function readExtension(cert, buf, der) { + der.readSequence(); + var after = der.offset + der.length; + var extId = der.readOID(); + var id; + var sig = cert.signatures.x509; + if (!sig.extras.exts) + sig.extras.exts = []; + + var critical; + if (der.peek() === asn1.Ber.Boolean) + critical = der.readBoolean(); + + switch (extId) { + case (EXTS.basicConstraints): + der.readSequence(asn1.Ber.OctetString); + der.readSequence(); + var bcEnd = der.offset + der.length; + var ca = false; + if (der.peek() === asn1.Ber.Boolean) + ca = der.readBoolean(); + if (cert.purposes === undefined) + cert.purposes = []; + if (ca === true) + cert.purposes.push('ca'); + var bc = { oid: extId, critical: critical }; + if (der.offset < bcEnd && der.peek() === asn1.Ber.Integer) + bc.pathLen = der.readInt(); + sig.extras.exts.push(bc); + break; + case (EXTS.extKeyUsage): + der.readSequence(asn1.Ber.OctetString); + der.readSequence(); + if (cert.purposes === undefined) + cert.purposes = []; + var ekEnd = der.offset + der.length; + while (der.offset < ekEnd) { + var oid = der.readOID(); + cert.purposes.push(EXTPURPOSE_REV[oid] || oid); + } + /* + * This is a bit of a hack: in the case where we have a cert + * that's only allowed to do serverAuth or clientAuth (and not + * the other), we want to make sure all our Subjects are of + * the right type. But we already parsed our Subjects and + * decided if they were hosts or users earlier (since it appears + * first in the cert). + * + * So we go through and mutate them into the right kind here if + * it doesn't match. This might not be hugely beneficial, as it + * seems that single-purpose certs are not often seen in the + * wild. + */ + if (cert.purposes.indexOf('serverAuth') !== -1 && + cert.purposes.indexOf('clientAuth') === -1) { + cert.subjects.forEach(function (ide) { + if (ide.type !== 'host') { + ide.type = 'host'; + ide.hostname = ide.uid || + ide.email || + ide.components[0].value; + } + }); + } else if (cert.purposes.indexOf('clientAuth') !== -1 && + cert.purposes.indexOf('serverAuth') === -1) { + cert.subjects.forEach(function (ide) { + if (ide.type !== 'user') { + ide.type = 'user'; + ide.uid = ide.hostname || + ide.email || + ide.components[0].value; + } + }); + } + sig.extras.exts.push({ oid: extId, critical: critical }); + break; + case (EXTS.keyUsage): + der.readSequence(asn1.Ber.OctetString); + var bits = der.readString(asn1.Ber.BitString, true); + var setBits = readBitField(bits, KEYUSEBITS); + setBits.forEach(function (bit) { + if (cert.purposes === undefined) + cert.purposes = []; + if (cert.purposes.indexOf(bit) === -1) + cert.purposes.push(bit); + }); + sig.extras.exts.push({ oid: extId, critical: critical, + bits: bits }); + break; + case (EXTS.altName): + der.readSequence(asn1.Ber.OctetString); + der.readSequence(); + var aeEnd = der.offset + der.length; + while (der.offset < aeEnd) { + switch (der.peek()) { + case ALTNAME.OtherName: + case ALTNAME.EDIPartyName: + der.readSequence(); + der._offset += der.length; + break; + case ALTNAME.OID: + der.readOID(ALTNAME.OID); + break; + case ALTNAME.RFC822Name: + /* RFC822 specifies email addresses */ + var email = der.readString(ALTNAME.RFC822Name); + id = Identity.forEmail(email); + if (!cert.subjects[0].equals(id)) + cert.subjects.push(id); + break; + case ALTNAME.DirectoryName: + der.readSequence(ALTNAME.DirectoryName); + id = Identity.parseAsn1(der); + if (!cert.subjects[0].equals(id)) + cert.subjects.push(id); + break; + case ALTNAME.DNSName: + var host = der.readString( + ALTNAME.DNSName); + id = Identity.forHost(host); + if (!cert.subjects[0].equals(id)) + cert.subjects.push(id); + break; + default: + der.readString(der.peek()); + break; + } + } + sig.extras.exts.push({ oid: extId, critical: critical }); + break; + default: + sig.extras.exts.push({ + oid: extId, + critical: critical, + data: der.readString(asn1.Ber.OctetString, true) + }); + break; + } + + der._offset = after; +} + +var UTCTIME_RE = + /^([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?Z$/; +function utcTimeToDate(t) { + var m = t.match(UTCTIME_RE); + assert.ok(m, 'timestamps must be in UTC'); + var d = new Date(); + + var thisYear = d.getUTCFullYear(); + var century = Math.floor(thisYear / 100) * 100; + + var year = parseInt(m[1], 10); + if (thisYear % 100 < 50 && year >= 60) + year += (century - 1); + else + year += century; + d.setUTCFullYear(year, parseInt(m[2], 10) - 1, parseInt(m[3], 10)); + d.setUTCHours(parseInt(m[4], 10), parseInt(m[5], 10)); + if (m[6] && m[6].length > 0) + d.setUTCSeconds(parseInt(m[6], 10)); + return (d); +} + +var GTIME_RE = + /^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?Z$/; +function gTimeToDate(t) { + var m = t.match(GTIME_RE); + assert.ok(m); + var d = new Date(); + + d.setUTCFullYear(parseInt(m[1], 10), parseInt(m[2], 10) - 1, + parseInt(m[3], 10)); + d.setUTCHours(parseInt(m[4], 10), parseInt(m[5], 10)); + if (m[6] && m[6].length > 0) + d.setUTCSeconds(parseInt(m[6], 10)); + return (d); +} + +function zeroPad(n, m) { + if (m === undefined) + m = 2; + var s = '' + n; + while (s.length < m) + s = '0' + s; + return (s); +} + +function dateToUTCTime(d) { + var s = ''; + s += zeroPad(d.getUTCFullYear() % 100); + s += zeroPad(d.getUTCMonth() + 1); + s += zeroPad(d.getUTCDate()); + s += zeroPad(d.getUTCHours()); + s += zeroPad(d.getUTCMinutes()); + s += zeroPad(d.getUTCSeconds()); + s += 'Z'; + return (s); +} + +function dateToGTime(d) { + var s = ''; + s += zeroPad(d.getUTCFullYear(), 4); + s += zeroPad(d.getUTCMonth() + 1); + s += zeroPad(d.getUTCDate()); + s += zeroPad(d.getUTCHours()); + s += zeroPad(d.getUTCMinutes()); + s += zeroPad(d.getUTCSeconds()); + s += 'Z'; + return (s); +} + +function sign(cert, key) { + if (cert.signatures.x509 === undefined) + cert.signatures.x509 = {}; + var sig = cert.signatures.x509; + + sig.algo = key.type + '-' + key.defaultHashAlgorithm(); + if (SIGN_ALGS[sig.algo] === undefined) + return (false); + + var der = new asn1.BerWriter(); + writeTBSCert(cert, der); + var blob = der.buffer; + sig.cache = blob; + + var signer = key.createSign(); + signer.write(blob); + cert.signatures.x509.signature = signer.sign(); + + return (true); +} + +function signAsync(cert, signer, done) { + if (cert.signatures.x509 === undefined) + cert.signatures.x509 = {}; + var sig = cert.signatures.x509; + + var der = new asn1.BerWriter(); + writeTBSCert(cert, der); + var blob = der.buffer; + sig.cache = blob; + + signer(blob, function (err, signature) { + if (err) { + done(err); + return; + } + sig.algo = signature.type + '-' + signature.hashAlgorithm; + if (SIGN_ALGS[sig.algo] === undefined) { + done(new Error('Invalid signing algorithm "' + + sig.algo + '"')); + return; + } + sig.signature = signature; + done(); + }); +} + +function write(cert, options) { + var sig = cert.signatures.x509; + assert.object(sig, 'x509 signature'); + + var der = new asn1.BerWriter(); + der.startSequence(); + if (sig.cache) { + der._ensure(sig.cache.length); + sig.cache.copy(der._buf, der._offset); + der._offset += sig.cache.length; + } else { + writeTBSCert(cert, der); + } + + der.startSequence(); + der.writeOID(SIGN_ALGS[sig.algo]); + if (sig.algo.match(/^rsa-/)) + der.writeNull(); + der.endSequence(); + + var sigData = sig.signature.toBuffer('asn1'); + var data = Buffer.alloc(sigData.length + 1); + data[0] = 0; + sigData.copy(data, 1); + der.writeBuffer(data, asn1.Ber.BitString); + der.endSequence(); + + return (der.buffer); +} + +function writeTBSCert(cert, der) { + var sig = cert.signatures.x509; + assert.object(sig, 'x509 signature'); + + der.startSequence(); + + der.startSequence(Local(0)); + der.writeInt(2); + der.endSequence(); + + der.writeBuffer(utils.mpNormalize(cert.serial), asn1.Ber.Integer); + + der.startSequence(); + der.writeOID(SIGN_ALGS[sig.algo]); + if (sig.algo.match(/^rsa-/)) + der.writeNull(); + der.endSequence(); + + cert.issuer.toAsn1(der); + + der.startSequence(); + writeDate(der, cert.validFrom); + writeDate(der, cert.validUntil); + der.endSequence(); + + var subject = cert.subjects[0]; + var altNames = cert.subjects.slice(1); + subject.toAsn1(der); + + pkcs8.writePkcs8(der, cert.subjectKey); + + if (sig.extras && sig.extras.issuerUniqueID) { + der.writeBuffer(sig.extras.issuerUniqueID, Local(1)); + } + + if (sig.extras && sig.extras.subjectUniqueID) { + der.writeBuffer(sig.extras.subjectUniqueID, Local(2)); + } + + if (altNames.length > 0 || subject.type === 'host' || + (cert.purposes !== undefined && cert.purposes.length > 0) || + (sig.extras && sig.extras.exts)) { + der.startSequence(Local(3)); + der.startSequence(); + + var exts = []; + if (cert.purposes !== undefined && cert.purposes.length > 0) { + exts.push({ + oid: EXTS.basicConstraints, + critical: true + }); + exts.push({ + oid: EXTS.keyUsage, + critical: true + }); + exts.push({ + oid: EXTS.extKeyUsage, + critical: true + }); + } + exts.push({ oid: EXTS.altName }); + if (sig.extras && sig.extras.exts) + exts = sig.extras.exts; + + for (var i = 0; i < exts.length; ++i) { + der.startSequence(); + der.writeOID(exts[i].oid); + + if (exts[i].critical !== undefined) + der.writeBoolean(exts[i].critical); + + if (exts[i].oid === EXTS.altName) { + der.startSequence(asn1.Ber.OctetString); + der.startSequence(); + if (subject.type === 'host') { + der.writeString(subject.hostname, + Context(2)); + } + for (var j = 0; j < altNames.length; ++j) { + if (altNames[j].type === 'host') { + der.writeString( + altNames[j].hostname, + ALTNAME.DNSName); + } else if (altNames[j].type === + 'email') { + der.writeString( + altNames[j].email, + ALTNAME.RFC822Name); + } else { + /* + * Encode anything else as a + * DN style name for now. + */ + der.startSequence( + ALTNAME.DirectoryName); + altNames[j].toAsn1(der); + der.endSequence(); + } + } + der.endSequence(); + der.endSequence(); + } else if (exts[i].oid === EXTS.basicConstraints) { + der.startSequence(asn1.Ber.OctetString); + der.startSequence(); + var ca = (cert.purposes.indexOf('ca') !== -1); + var pathLen = exts[i].pathLen; + der.writeBoolean(ca); + if (pathLen !== undefined) + der.writeInt(pathLen); + der.endSequence(); + der.endSequence(); + } else if (exts[i].oid === EXTS.extKeyUsage) { + der.startSequence(asn1.Ber.OctetString); + der.startSequence(); + cert.purposes.forEach(function (purpose) { + if (purpose === 'ca') + return; + if (KEYUSEBITS.indexOf(purpose) !== -1) + return; + var oid = purpose; + if (EXTPURPOSE[purpose] !== undefined) + oid = EXTPURPOSE[purpose]; + der.writeOID(oid); + }); + der.endSequence(); + der.endSequence(); + } else if (exts[i].oid === EXTS.keyUsage) { + der.startSequence(asn1.Ber.OctetString); + /* + * If we parsed this certificate from a byte + * stream (i.e. we didn't generate it in sshpk) + * then we'll have a ".bits" property on the + * ext with the original raw byte contents. + * + * If we have this, use it here instead of + * regenerating it. This guarantees we output + * the same data we parsed, so signatures still + * validate. + */ + if (exts[i].bits !== undefined) { + der.writeBuffer(exts[i].bits, + asn1.Ber.BitString); + } else { + var bits = writeBitField(cert.purposes, + KEYUSEBITS); + der.writeBuffer(bits, + asn1.Ber.BitString); + } + der.endSequence(); + } else { + der.writeBuffer(exts[i].data, + asn1.Ber.OctetString); + } + + der.endSequence(); + } + + der.endSequence(); + der.endSequence(); + } + + der.endSequence(); +} + +/* + * Reads an ASN.1 BER bitfield out of the Buffer produced by doing + * `BerReader#readString(asn1.Ber.BitString)`. That function gives us the raw + * contents of the BitString tag, which is a count of unused bits followed by + * the bits as a right-padded byte string. + * + * `bits` is the Buffer, `bitIndex` should contain an array of string names + * for the bits in the string, ordered starting with bit #0 in the ASN.1 spec. + * + * Returns an array of Strings, the names of the bits that were set to 1. + */ +function readBitField(bits, bitIndex) { + var bitLen = 8 * (bits.length - 1) - bits[0]; + var setBits = {}; + for (var i = 0; i < bitLen; ++i) { + var byteN = 1 + Math.floor(i / 8); + var bit = 7 - (i % 8); + var mask = 1 << bit; + var bitVal = ((bits[byteN] & mask) !== 0); + var name = bitIndex[i]; + if (bitVal && typeof (name) === 'string') { + setBits[name] = true; + } + } + return (Object.keys(setBits)); +} + +/* + * `setBits` is an array of strings, containing the names for each bit that + * sould be set to 1. `bitIndex` is same as in `readBitField()`. + * + * Returns a Buffer, ready to be written out with `BerWriter#writeString()`. + */ +function writeBitField(setBits, bitIndex) { + var bitLen = bitIndex.length; + var blen = Math.ceil(bitLen / 8); + var unused = blen * 8 - bitLen; + var bits = Buffer.alloc(1 + blen); // zero-filled + bits[0] = unused; + for (var i = 0; i < bitLen; ++i) { + var byteN = 1 + Math.floor(i / 8); + var bit = 7 - (i % 8); + var mask = 1 << bit; + var name = bitIndex[i]; + if (name === undefined) + continue; + var bitVal = (setBits.indexOf(name) !== -1); + if (bitVal) { + bits[byteN] |= mask; + } + } + return (bits); +} + + +/***/ }), + +/***/ 867: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +var URI = __webpack_require__(853) + , equal = __webpack_require__(832) + , util = __webpack_require__(855) + , SchemaObject = __webpack_require__(955) + , traverse = __webpack_require__(340); + +module.exports = resolve; + +resolve.normalizeId = normalizeId; +resolve.fullPath = getFullPath; +resolve.url = resolveUrl; +resolve.ids = resolveIds; +resolve.inlineRef = inlineRef; +resolve.schema = resolveSchema; + +/** + * [resolve and compile the references ($ref)] + * @this Ajv + * @param {Function} compile reference to schema compilation funciton (localCompile) + * @param {Object} root object with information about the root schema for the current schema + * @param {String} ref reference to resolve + * @return {Object|Function} schema object (if the schema can be inlined) or validation function + */ +function resolve(compile, root, ref) { + /* jshint validthis: true */ + var refVal = this._refs[ref]; + if (typeof refVal == 'string') { + if (this._refs[refVal]) refVal = this._refs[refVal]; + else return resolve.call(this, compile, root, refVal); + } + + refVal = refVal || this._schemas[ref]; + if (refVal instanceof SchemaObject) { + return inlineRef(refVal.schema, this._opts.inlineRefs) + ? refVal.schema + : refVal.validate || this._compile(refVal); + } + + var res = resolveSchema.call(this, root, ref); + var schema, v, baseId; + if (res) { + schema = res.schema; + root = res.root; + baseId = res.baseId; + } + + if (schema instanceof SchemaObject) { + v = schema.validate || compile.call(this, schema.schema, root, undefined, baseId); + } else if (schema !== undefined) { + v = inlineRef(schema, this._opts.inlineRefs) + ? schema + : compile.call(this, schema, root, undefined, baseId); + } + + return v; +} + + +/** + * Resolve schema, its root and baseId + * @this Ajv + * @param {Object} root root object with properties schema, refVal, refs + * @param {String} ref reference to resolve + * @return {Object} object with properties schema, root, baseId + */ +function resolveSchema(root, ref) { + /* jshint validthis: true */ + var p = URI.parse(ref) + , refPath = _getFullPath(p) + , baseId = getFullPath(this._getId(root.schema)); + if (Object.keys(root.schema).length === 0 || refPath !== baseId) { + var id = normalizeId(refPath); + var refVal = this._refs[id]; + if (typeof refVal == 'string') { + return resolveRecursive.call(this, root, refVal, p); + } else if (refVal instanceof SchemaObject) { + if (!refVal.validate) this._compile(refVal); + root = refVal; + } else { + refVal = this._schemas[id]; + if (refVal instanceof SchemaObject) { + if (!refVal.validate) this._compile(refVal); + if (id == normalizeId(ref)) + return { schema: refVal, root: root, baseId: baseId }; + root = refVal; + } else { + return; + } + } + if (!root.schema) return; + baseId = getFullPath(this._getId(root.schema)); + } + return getJsonPointer.call(this, p, baseId, root.schema, root); +} + + +/* @this Ajv */ +function resolveRecursive(root, ref, parsedRef) { + /* jshint validthis: true */ + var res = resolveSchema.call(this, root, ref); + if (res) { + var schema = res.schema; + var baseId = res.baseId; + root = res.root; + var id = this._getId(schema); + if (id) baseId = resolveUrl(baseId, id); + return getJsonPointer.call(this, parsedRef, baseId, schema, root); + } +} + + +var PREVENT_SCOPE_CHANGE = util.toHash(['properties', 'patternProperties', 'enum', 'dependencies', 'definitions']); +/* @this Ajv */ +function getJsonPointer(parsedRef, baseId, schema, root) { + /* jshint validthis: true */ + parsedRef.fragment = parsedRef.fragment || ''; + if (parsedRef.fragment.slice(0,1) != '/') return; + var parts = parsedRef.fragment.split('/'); + + for (var i = 1; i < parts.length; i++) { + var part = parts[i]; + if (part) { + part = util.unescapeFragment(part); + schema = schema[part]; + if (schema === undefined) break; + var id; + if (!PREVENT_SCOPE_CHANGE[part]) { + id = this._getId(schema); + if (id) baseId = resolveUrl(baseId, id); + if (schema.$ref) { + var $ref = resolveUrl(baseId, schema.$ref); + var res = resolveSchema.call(this, root, $ref); + if (res) { + schema = res.schema; + root = res.root; + baseId = res.baseId; + } + } + } + } + } + if (schema !== undefined && schema !== root.schema) + return { schema: schema, root: root, baseId: baseId }; +} + + +var SIMPLE_INLINED = util.toHash([ + 'type', 'format', 'pattern', + 'maxLength', 'minLength', + 'maxProperties', 'minProperties', + 'maxItems', 'minItems', + 'maximum', 'minimum', + 'uniqueItems', 'multipleOf', + 'required', 'enum' +]); +function inlineRef(schema, limit) { + if (limit === false) return false; + if (limit === undefined || limit === true) return checkNoRef(schema); + else if (limit) return countKeys(schema) <= limit; +} + + +function checkNoRef(schema) { + var item; + if (Array.isArray(schema)) { + for (var i=0; i%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i; +// For the source: https://gist.github.com/dperini/729294 +// For test cases: https://mathiasbynens.be/demo/url-regex +// @todo Delete current URL in favour of the commented out URL rule when this issue is fixed https://github.com/eslint/eslint/issues/7983. +// var URL = /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)(?:\.(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu; +var URL = /^(?:(?:http[s\u017F]?|ftp):\/\/)(?:(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+(?::(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?@)?(?:(?!10(?:\.[0-9]{1,3}){3})(?!127(?:\.[0-9]{1,3}){3})(?!169\.254(?:\.[0-9]{1,3}){2})(?!192\.168(?:\.[0-9]{1,3}){2})(?!172\.(?:1[6-9]|2[0-9]|3[01])(?:\.[0-9]{1,3}){2})(?:[1-9][0-9]?|1[0-9][0-9]|2[01][0-9]|22[0-3])(?:\.(?:1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])){2}(?:\.(?:[1-9][0-9]?|1[0-9][0-9]|2[0-4][0-9]|25[0-4]))|(?:(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)(?:\.(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)*(?:\.(?:(?:[KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){2,})))(?::[0-9]{2,5})?(?:\/(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?$/i; +var UUID = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i; +var JSON_POINTER = /^(?:\/(?:[^~/]|~0|~1)*)*$/; +var JSON_POINTER_URI_FRAGMENT = /^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i; +var RELATIVE_JSON_POINTER = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/; + + +module.exports = formats; + +function formats(mode) { + mode = mode == 'full' ? 'full' : 'fast'; + return util.copy(formats[mode]); +} + + +formats.fast = { + // date: http://tools.ietf.org/html/rfc3339#section-5.6 + date: /^\d\d\d\d-[0-1]\d-[0-3]\d$/, + // date-time: http://tools.ietf.org/html/rfc3339#section-5.6 + time: /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i, + 'date-time': /^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i, + // uri: https://github.com/mafintosh/is-my-json-valid/blob/master/formats.js + uri: /^(?:[a-z][a-z0-9+-.]*:)(?:\/?\/)?[^\s]*$/i, + 'uri-reference': /^(?:(?:[a-z][a-z0-9+-.]*:)?\/?\/)?(?:[^\\\s#][^\s#]*)?(?:#[^\\\s]*)?$/i, + 'uri-template': URITEMPLATE, + url: URL, + // email (sources from jsen validator): + // http://stackoverflow.com/questions/201323/using-a-regular-expression-to-validate-an-email-address#answer-8829363 + // http://www.w3.org/TR/html5/forms.html#valid-e-mail-address (search for 'willful violation') + email: /^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i, + hostname: HOSTNAME, + // optimized https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9780596802837/ch07s16.html + ipv4: /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/, + // optimized http://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses + ipv6: /^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i, + regex: regex, + // uuid: http://tools.ietf.org/html/rfc4122 + uuid: UUID, + // JSON-pointer: https://tools.ietf.org/html/rfc6901 + // uri fragment: https://tools.ietf.org/html/rfc3986#appendix-A + 'json-pointer': JSON_POINTER, + 'json-pointer-uri-fragment': JSON_POINTER_URI_FRAGMENT, + // relative JSON-pointer: http://tools.ietf.org/html/draft-luff-relative-json-pointer-00 + 'relative-json-pointer': RELATIVE_JSON_POINTER +}; + + +formats.full = { + date: date, + time: time, + 'date-time': date_time, + uri: uri, + 'uri-reference': URIREF, + 'uri-template': URITEMPLATE, + url: URL, + email: /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i, + hostname: hostname, + ipv4: /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/, + ipv6: /^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i, + regex: regex, + uuid: UUID, + 'json-pointer': JSON_POINTER, + 'json-pointer-uri-fragment': JSON_POINTER_URI_FRAGMENT, + 'relative-json-pointer': RELATIVE_JSON_POINTER +}; + + +function isLeapYear(year) { + // https://tools.ietf.org/html/rfc3339#appendix-C + return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); +} + + +function date(str) { + // full-date from http://tools.ietf.org/html/rfc3339#section-5.6 + var matches = str.match(DATE); + if (!matches) return false; + + var year = +matches[1]; + var month = +matches[2]; + var day = +matches[3]; + + return month >= 1 && month <= 12 && day >= 1 && + day <= (month == 2 && isLeapYear(year) ? 29 : DAYS[month]); +} + + +function time(str, full) { + var matches = str.match(TIME); + if (!matches) return false; + + var hour = matches[1]; + var minute = matches[2]; + var second = matches[3]; + var timeZone = matches[5]; + return ((hour <= 23 && minute <= 59 && second <= 59) || + (hour == 23 && minute == 59 && second == 60)) && + (!full || timeZone); +} + + +var DATE_TIME_SEPARATOR = /t|\s/i; +function date_time(str) { + // http://tools.ietf.org/html/rfc3339#section-5.6 + var dateTime = str.split(DATE_TIME_SEPARATOR); + return dateTime.length == 2 && date(dateTime[0]) && time(dateTime[1], true); +} + + +function hostname(str) { + // https://tools.ietf.org/html/rfc1034#section-3.5 + // https://tools.ietf.org/html/rfc1123#section-2 + return str.length <= 255 && HOSTNAME.test(str); +} + + +var NOT_URI_FRAGMENT = /\/|:/; +function uri(str) { + // http://jmrware.com/articles/2009/uri_regexp/URI_regex.html + optional protocol + required "." + return NOT_URI_FRAGMENT.test(str) && URI.test(str); +} + + +var Z_ANCHOR = /[^\\]\\Z/; +function regex(str) { + if (Z_ANCHOR.test(str)) return false; + try { + new RegExp(str); + return true; + } catch(e) { + return false; + } +} + + +/***/ }), + +/***/ 883: +/***/ (function(module) { + +module.exports = {"$id":"header.json#","$schema":"http://json-schema.org/draft-06/schema#","type":"object","required":["name","value"],"properties":{"name":{"type":"string"},"value":{"type":"string"},"comment":{"type":"string"}}}; + +/***/ }), + +/***/ 886: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +var crypto = __webpack_require__(417); +var BigInteger = __webpack_require__(242).BigInteger; +var ECPointFp = __webpack_require__(729).ECPointFp; +var Buffer = __webpack_require__(215).Buffer; +exports.ECCurves = __webpack_require__(959); + +// zero prepad +function unstupid(hex,len) +{ + return (hex.length >= len) ? hex : unstupid("0"+hex,len); +} + +exports.ECKey = function(curve, key, isPublic) +{ + var priv; + var c = curve(); + var n = c.getN(); + var bytes = Math.floor(n.bitLength()/8); + + if(key) + { + if(isPublic) + { + var curve = c.getCurve(); +// var x = key.slice(1,bytes+1); // skip the 04 for uncompressed format +// var y = key.slice(bytes+1); +// this.P = new ECPointFp(curve, +// curve.fromBigInteger(new BigInteger(x.toString("hex"), 16)), +// curve.fromBigInteger(new BigInteger(y.toString("hex"), 16))); + this.P = curve.decodePointHex(key.toString("hex")); + }else{ + if(key.length != bytes) return false; + priv = new BigInteger(key.toString("hex"), 16); + } + }else{ + var n1 = n.subtract(BigInteger.ONE); + var r = new BigInteger(crypto.randomBytes(n.bitLength())); + priv = r.mod(n1).add(BigInteger.ONE); + this.P = c.getG().multiply(priv); + } + if(this.P) + { +// var pubhex = unstupid(this.P.getX().toBigInteger().toString(16),bytes*2)+unstupid(this.P.getY().toBigInteger().toString(16),bytes*2); +// this.PublicKey = Buffer.from("04"+pubhex,"hex"); + this.PublicKey = Buffer.from(c.getCurve().encodeCompressedPointHex(this.P),"hex"); + } + if(priv) + { + this.PrivateKey = Buffer.from(unstupid(priv.toString(16),bytes*2),"hex"); + this.deriveSharedSecret = function(key) + { + if(!key || !key.P) return false; + var S = key.P.multiply(priv); + return Buffer.from(unstupid(S.getX().toBigInteger().toString(16),bytes*2),"hex"); + } + } +} + + + +/***/ }), + +/***/ 890: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +var MissingRefError = __webpack_require__(844).MissingRef; + +module.exports = compileAsync; + + +/** + * Creates validating function for passed schema with asynchronous loading of missing schemas. + * `loadSchema` option should be a function that accepts schema uri and returns promise that resolves with the schema. + * @this Ajv + * @param {Object} schema schema object + * @param {Boolean} meta optional true to compile meta-schema; this parameter can be skipped + * @param {Function} callback an optional node-style callback, it is called with 2 parameters: error (or null) and validating function. + * @return {Promise} promise that resolves with a validating function. + */ +function compileAsync(schema, meta, callback) { + /* eslint no-shadow: 0 */ + /* global Promise */ + /* jshint validthis: true */ + var self = this; + if (typeof this._opts.loadSchema != 'function') + throw new Error('options.loadSchema should be a function'); + + if (typeof meta == 'function') { + callback = meta; + meta = undefined; + } + + var p = loadMetaSchemaOf(schema).then(function () { + var schemaObj = self._addSchema(schema, undefined, meta); + return schemaObj.validate || _compileAsync(schemaObj); + }); + + if (callback) { + p.then( + function(v) { callback(null, v); }, + callback + ); + } + + return p; + + + function loadMetaSchemaOf(sch) { + var $schema = sch.$schema; + return $schema && !self.getSchema($schema) + ? compileAsync.call(self, { $ref: $schema }, true) + : Promise.resolve(); + } + + + function _compileAsync(schemaObj) { + try { return self._compile(schemaObj); } + catch(e) { + if (e instanceof MissingRefError) return loadMissingSchema(e); + throw e; + } + + + function loadMissingSchema(e) { + var ref = e.missingSchema; + if (added(ref)) throw new Error('Schema ' + ref + ' is loaded but ' + e.missingRef + ' cannot be resolved'); + + var schemaPromise = self._loadingSchemas[ref]; + if (!schemaPromise) { + schemaPromise = self._loadingSchemas[ref] = self._opts.loadSchema(ref); + schemaPromise.then(removePromise, removePromise); + } + + return schemaPromise.then(function (sch) { + if (!added(ref)) { + return loadMetaSchemaOf(sch).then(function () { + if (!added(ref)) self.addSchema(sch, ref, undefined, meta); + }); + } + }).then(function() { + return _compileAsync(schemaObj); + }); + + function removePromise() { + delete self._loadingSchemas[ref]; + } + + function added(ref) { + return self._refs[ref] || self._schemas[ref]; + } + } + } +} + + +/***/ }), + +/***/ 892: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var iterate = __webpack_require__(157) + , initState = __webpack_require__(147) + , terminator = __webpack_require__(939) + ; + +// Public API +module.exports = serialOrdered; +// sorting helpers +module.exports.ascending = ascending; +module.exports.descending = descending; + +/** + * Runs iterator over provided sorted array elements in series + * + * @param {array|object} list - array or object (named list) to iterate over + * @param {function} iterator - iterator to run + * @param {function} sortMethod - custom sort function + * @param {function} callback - invoked when all elements processed + * @returns {function} - jobs terminator + */ +function serialOrdered(list, iterator, sortMethod, callback) +{ + var state = initState(list, sortMethod); + + iterate(list, iterator, state, function iteratorHandler(error, result) + { + if (error) + { + callback(error, result); + return; + } + + state.index++; + + // are we there yet? + if (state.index < (state['keyedList'] || list).length) + { + iterate(list, iterator, state, iteratorHandler); + return; + } + + // done here + callback(null, state.results); + }); + + return terminator.bind(state, callback); +} + +/* + * -- Sort methods + */ + +/** + * sort helper to sort array elements in ascending order + * + * @param {mixed} a - an item to compare + * @param {mixed} b - an item to compare + * @returns {number} - comparison result + */ +function ascending(a, b) +{ + return a < b ? -1 : a > b ? 1 : 0; +} + +/** + * sort helper to sort array elements in descending order + * + * @param {mixed} a - an item to compare + * @param {mixed} b - an item to compare + * @returns {number} - comparison result + */ +function descending(a, b) +{ + return -1 * ascending(a, b); +} + + +/***/ }), + +/***/ 893: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2017 Joyent, Inc. + +module.exports = { + read: read, + verify: verify, + sign: sign, + signAsync: signAsync, + write: write, + + /* Internal private API */ + fromBuffer: fromBuffer, + toBuffer: toBuffer +}; + +var assert = __webpack_require__(477); +var SSHBuffer = __webpack_require__(940); +var crypto = __webpack_require__(417); +var Buffer = __webpack_require__(215).Buffer; +var algs = __webpack_require__(98); +var Key = __webpack_require__(852); +var PrivateKey = __webpack_require__(502); +var Identity = __webpack_require__(378); +var rfc4253 = __webpack_require__(538); +var Signature = __webpack_require__(575); +var utils = __webpack_require__(270); +var Certificate = __webpack_require__(752); + +function verify(cert, key) { + /* + * We always give an issuerKey, so if our verify() is being called then + * there was no signature. Return false. + */ + return (false); +} + +var TYPES = { + 'user': 1, + 'host': 2 +}; +Object.keys(TYPES).forEach(function (k) { TYPES[TYPES[k]] = k; }); + +var ECDSA_ALGO = /^ecdsa-sha2-([^@-]+)-cert-v01@openssh.com$/; + +function read(buf, options) { + if (Buffer.isBuffer(buf)) + buf = buf.toString('ascii'); + var parts = buf.trim().split(/[ \t\n]+/g); + if (parts.length < 2 || parts.length > 3) + throw (new Error('Not a valid SSH certificate line')); + + var algo = parts[0]; + var data = parts[1]; + + data = Buffer.from(data, 'base64'); + return (fromBuffer(data, algo)); +} + +function fromBuffer(data, algo, partial) { + var sshbuf = new SSHBuffer({ buffer: data }); + var innerAlgo = sshbuf.readString(); + if (algo !== undefined && innerAlgo !== algo) + throw (new Error('SSH certificate algorithm mismatch')); + if (algo === undefined) + algo = innerAlgo; + + var cert = {}; + cert.signatures = {}; + cert.signatures.openssh = {}; + + cert.signatures.openssh.nonce = sshbuf.readBuffer(); + + var key = {}; + var parts = (key.parts = []); + key.type = getAlg(algo); + + var partCount = algs.info[key.type].parts.length; + while (parts.length < partCount) + parts.push(sshbuf.readPart()); + assert.ok(parts.length >= 1, 'key must have at least one part'); + + var algInfo = algs.info[key.type]; + if (key.type === 'ecdsa') { + var res = ECDSA_ALGO.exec(algo); + assert.ok(res !== null); + assert.strictEqual(res[1], parts[0].data.toString()); + } + + for (var i = 0; i < algInfo.parts.length; ++i) { + parts[i].name = algInfo.parts[i]; + if (parts[i].name !== 'curve' && + algInfo.normalize !== false) { + var p = parts[i]; + p.data = utils.mpNormalize(p.data); + } + } + + cert.subjectKey = new Key(key); + + cert.serial = sshbuf.readInt64(); + + var type = TYPES[sshbuf.readInt()]; + assert.string(type, 'valid cert type'); + + cert.signatures.openssh.keyId = sshbuf.readString(); + + var principals = []; + var pbuf = sshbuf.readBuffer(); + var psshbuf = new SSHBuffer({ buffer: pbuf }); + while (!psshbuf.atEnd()) + principals.push(psshbuf.readString()); + if (principals.length === 0) + principals = ['*']; + + cert.subjects = principals.map(function (pr) { + if (type === 'user') + return (Identity.forUser(pr)); + else if (type === 'host') + return (Identity.forHost(pr)); + throw (new Error('Unknown identity type ' + type)); + }); + + cert.validFrom = int64ToDate(sshbuf.readInt64()); + cert.validUntil = int64ToDate(sshbuf.readInt64()); + + var exts = []; + var extbuf = new SSHBuffer({ buffer: sshbuf.readBuffer() }); + var ext; + while (!extbuf.atEnd()) { + ext = { critical: true }; + ext.name = extbuf.readString(); + ext.data = extbuf.readBuffer(); + exts.push(ext); + } + extbuf = new SSHBuffer({ buffer: sshbuf.readBuffer() }); + while (!extbuf.atEnd()) { + ext = { critical: false }; + ext.name = extbuf.readString(); + ext.data = extbuf.readBuffer(); + exts.push(ext); + } + cert.signatures.openssh.exts = exts; + + /* reserved */ + sshbuf.readBuffer(); + + var signingKeyBuf = sshbuf.readBuffer(); + cert.issuerKey = rfc4253.read(signingKeyBuf); + + /* + * OpenSSH certs don't give the identity of the issuer, just their + * public key. So, we use an Identity that matches anything. The + * isSignedBy() function will later tell you if the key matches. + */ + cert.issuer = Identity.forHost('**'); + + var sigBuf = sshbuf.readBuffer(); + cert.signatures.openssh.signature = + Signature.parse(sigBuf, cert.issuerKey.type, 'ssh'); + + if (partial !== undefined) { + partial.remainder = sshbuf.remainder(); + partial.consumed = sshbuf._offset; + } + + return (new Certificate(cert)); +} + +function int64ToDate(buf) { + var i = buf.readUInt32BE(0) * 4294967296; + i += buf.readUInt32BE(4); + var d = new Date(); + d.setTime(i * 1000); + d.sourceInt64 = buf; + return (d); +} + +function dateToInt64(date) { + if (date.sourceInt64 !== undefined) + return (date.sourceInt64); + var i = Math.round(date.getTime() / 1000); + var upper = Math.floor(i / 4294967296); + var lower = Math.floor(i % 4294967296); + var buf = Buffer.alloc(8); + buf.writeUInt32BE(upper, 0); + buf.writeUInt32BE(lower, 4); + return (buf); +} + +function sign(cert, key) { + if (cert.signatures.openssh === undefined) + cert.signatures.openssh = {}; + try { + var blob = toBuffer(cert, true); + } catch (e) { + delete (cert.signatures.openssh); + return (false); + } + var sig = cert.signatures.openssh; + var hashAlgo = undefined; + if (key.type === 'rsa' || key.type === 'dsa') + hashAlgo = 'sha1'; + var signer = key.createSign(hashAlgo); + signer.write(blob); + sig.signature = signer.sign(); + return (true); +} + +function signAsync(cert, signer, done) { + if (cert.signatures.openssh === undefined) + cert.signatures.openssh = {}; + try { + var blob = toBuffer(cert, true); + } catch (e) { + delete (cert.signatures.openssh); + done(e); + return; + } + var sig = cert.signatures.openssh; + + signer(blob, function (err, signature) { + if (err) { + done(err); + return; + } + try { + /* + * This will throw if the signature isn't of a + * type/algo that can be used for SSH. + */ + signature.toBuffer('ssh'); + } catch (e) { + done(e); + return; + } + sig.signature = signature; + done(); + }); +} + +function write(cert, options) { + if (options === undefined) + options = {}; + + var blob = toBuffer(cert); + var out = getCertType(cert.subjectKey) + ' ' + blob.toString('base64'); + if (options.comment) + out = out + ' ' + options.comment; + return (out); +} + + +function toBuffer(cert, noSig) { + assert.object(cert.signatures.openssh, 'signature for openssh format'); + var sig = cert.signatures.openssh; + + if (sig.nonce === undefined) + sig.nonce = crypto.randomBytes(16); + var buf = new SSHBuffer({}); + buf.writeString(getCertType(cert.subjectKey)); + buf.writeBuffer(sig.nonce); + + var key = cert.subjectKey; + var algInfo = algs.info[key.type]; + algInfo.parts.forEach(function (part) { + buf.writePart(key.part[part]); + }); + + buf.writeInt64(cert.serial); + + var type = cert.subjects[0].type; + assert.notStrictEqual(type, 'unknown'); + cert.subjects.forEach(function (id) { + assert.strictEqual(id.type, type); + }); + type = TYPES[type]; + buf.writeInt(type); + + if (sig.keyId === undefined) { + sig.keyId = cert.subjects[0].type + '_' + + (cert.subjects[0].uid || cert.subjects[0].hostname); + } + buf.writeString(sig.keyId); + + var sub = new SSHBuffer({}); + cert.subjects.forEach(function (id) { + if (type === TYPES.host) + sub.writeString(id.hostname); + else if (type === TYPES.user) + sub.writeString(id.uid); + }); + buf.writeBuffer(sub.toBuffer()); + + buf.writeInt64(dateToInt64(cert.validFrom)); + buf.writeInt64(dateToInt64(cert.validUntil)); + + var exts = sig.exts; + if (exts === undefined) + exts = []; + + var extbuf = new SSHBuffer({}); + exts.forEach(function (ext) { + if (ext.critical !== true) + return; + extbuf.writeString(ext.name); + extbuf.writeBuffer(ext.data); + }); + buf.writeBuffer(extbuf.toBuffer()); + + extbuf = new SSHBuffer({}); + exts.forEach(function (ext) { + if (ext.critical === true) + return; + extbuf.writeString(ext.name); + extbuf.writeBuffer(ext.data); + }); + buf.writeBuffer(extbuf.toBuffer()); + + /* reserved */ + buf.writeBuffer(Buffer.alloc(0)); + + sub = rfc4253.write(cert.issuerKey); + buf.writeBuffer(sub); + + if (!noSig) + buf.writeBuffer(sig.signature.toBuffer('ssh')); + + return (buf.toBuffer()); +} + +function getAlg(certType) { + if (certType === 'ssh-rsa-cert-v01@openssh.com') + return ('rsa'); + if (certType === 'ssh-dss-cert-v01@openssh.com') + return ('dsa'); + if (certType.match(ECDSA_ALGO)) + return ('ecdsa'); + if (certType === 'ssh-ed25519-cert-v01@openssh.com') + return ('ed25519'); + throw (new Error('Unsupported cert type ' + certType)); +} + +function getCertType(key) { + if (key.type === 'rsa') + return ('ssh-rsa-cert-v01@openssh.com'); + if (key.type === 'dsa') + return ('ssh-dss-cert-v01@openssh.com'); + if (key.type === 'ecdsa') + return ('ecdsa-sha2-' + key.curve + '-cert-v01@openssh.com'); + if (key.type === 'ed25519') + return ('ssh-ed25519-cert-v01@openssh.com'); + throw (new Error('Unsupported key type ' + key.type)); +} + + +/***/ }), + +/***/ 894: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +//all requires must be explicit because browserify won't work with dynamic requires +module.exports = { + '$ref': __webpack_require__(266), + allOf: __webpack_require__(107), + anyOf: __webpack_require__(902), + '$comment': __webpack_require__(28), + const: __webpack_require__(662), + contains: __webpack_require__(154), + dependencies: __webpack_require__(233), + 'enum': __webpack_require__(281), + format: __webpack_require__(687), + 'if': __webpack_require__(479), + items: __webpack_require__(643), + maximum: __webpack_require__(341), + minimum: __webpack_require__(341), + maxItems: __webpack_require__(85), + minItems: __webpack_require__(85), + maxLength: __webpack_require__(772), + minLength: __webpack_require__(772), + maxProperties: __webpack_require__(560), + minProperties: __webpack_require__(560), + multipleOf: __webpack_require__(397), + not: __webpack_require__(673), + oneOf: __webpack_require__(653), + pattern: __webpack_require__(542), + properties: __webpack_require__(343), + propertyNames: __webpack_require__(35), + required: __webpack_require__(858), + uniqueItems: __webpack_require__(899), + validate: __webpack_require__(967) +}; + + +/***/ }), + +/***/ 897: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +var utils = __webpack_require__(581); +var formats = __webpack_require__(13); + +var arrayPrefixGenerators = { + brackets: function brackets(prefix) { // eslint-disable-line func-name-matching + return prefix + '[]'; + }, + indices: function indices(prefix, key) { // eslint-disable-line func-name-matching + return prefix + '[' + key + ']'; + }, + repeat: function repeat(prefix) { // eslint-disable-line func-name-matching + return prefix; + } +}; + +var toISO = Date.prototype.toISOString; + +var defaults = { + delimiter: '&', + encode: true, + encoder: utils.encode, + encodeValuesOnly: false, + serializeDate: function serializeDate(date) { // eslint-disable-line func-name-matching + return toISO.call(date); + }, + skipNulls: false, + strictNullHandling: false +}; + +var stringify = function stringify( // eslint-disable-line func-name-matching + object, + prefix, + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots, + serializeDate, + formatter, + encodeValuesOnly +) { + var obj = object; + if (typeof filter === 'function') { + obj = filter(prefix, obj); + } else if (obj instanceof Date) { + obj = serializeDate(obj); + } else if (obj === null) { + if (strictNullHandling) { + return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder) : prefix; + } + + obj = ''; + } + + if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || utils.isBuffer(obj)) { + if (encoder) { + var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder); + return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder))]; + } + return [formatter(prefix) + '=' + formatter(String(obj))]; + } + + var values = []; + + if (typeof obj === 'undefined') { + return values; + } + + var objKeys; + if (Array.isArray(filter)) { + objKeys = filter; + } else { + var keys = Object.keys(obj); + objKeys = sort ? keys.sort(sort) : keys; + } + + for (var i = 0; i < objKeys.length; ++i) { + var key = objKeys[i]; + + if (skipNulls && obj[key] === null) { + continue; + } + + if (Array.isArray(obj)) { + values = values.concat(stringify( + obj[key], + generateArrayPrefix(prefix, key), + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots, + serializeDate, + formatter, + encodeValuesOnly + )); + } else { + values = values.concat(stringify( + obj[key], + prefix + (allowDots ? '.' + key : '[' + key + ']'), + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots, + serializeDate, + formatter, + encodeValuesOnly + )); + } + } + + return values; +}; + +module.exports = function (object, opts) { + var obj = object; + var options = opts ? utils.assign({}, opts) : {}; + + if (options.encoder !== null && options.encoder !== undefined && typeof options.encoder !== 'function') { + throw new TypeError('Encoder has to be a function.'); + } + + var delimiter = typeof options.delimiter === 'undefined' ? defaults.delimiter : options.delimiter; + var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling; + var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : defaults.skipNulls; + var encode = typeof options.encode === 'boolean' ? options.encode : defaults.encode; + var encoder = typeof options.encoder === 'function' ? options.encoder : defaults.encoder; + var sort = typeof options.sort === 'function' ? options.sort : null; + var allowDots = typeof options.allowDots === 'undefined' ? false : options.allowDots; + var serializeDate = typeof options.serializeDate === 'function' ? options.serializeDate : defaults.serializeDate; + var encodeValuesOnly = typeof options.encodeValuesOnly === 'boolean' ? options.encodeValuesOnly : defaults.encodeValuesOnly; + if (typeof options.format === 'undefined') { + options.format = formats['default']; + } else if (!Object.prototype.hasOwnProperty.call(formats.formatters, options.format)) { + throw new TypeError('Unknown format option provided.'); + } + var formatter = formats.formatters[options.format]; + var objKeys; + var filter; + + if (typeof options.filter === 'function') { + filter = options.filter; + obj = filter('', obj); + } else if (Array.isArray(options.filter)) { + filter = options.filter; + objKeys = filter; + } + + var keys = []; + + if (typeof obj !== 'object' || obj === null) { + return ''; + } + + var arrayFormat; + if (options.arrayFormat in arrayPrefixGenerators) { + arrayFormat = options.arrayFormat; + } else if ('indices' in options) { + arrayFormat = options.indices ? 'indices' : 'repeat'; + } else { + arrayFormat = 'indices'; + } + + var generateArrayPrefix = arrayPrefixGenerators[arrayFormat]; + + if (!objKeys) { + objKeys = Object.keys(obj); + } + + if (sort) { + objKeys.sort(sort); + } + + for (var i = 0; i < objKeys.length; ++i) { + var key = objKeys[i]; + + if (skipNulls && obj[key] === null) { + continue; + } + + keys = keys.concat(stringify( + obj[key], + key, + generateArrayPrefix, + strictNullHandling, + skipNulls, + encode ? encoder : null, + filter, + sort, + allowDots, + serializeDate, + formatter, + encodeValuesOnly + )); + } + + var joined = keys.join(delimiter); + var prefix = options.addQueryPrefix === true ? '?' : ''; + + return joined.length > 0 ? prefix + joined : ''; +}; + + +/***/ }), + +/***/ 899: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_uniqueItems(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $isData = it.opts.$data && $schema && $schema.$data, + $schemaValue; + if ($isData) { + out += ' var schema' + ($lvl) + ' = ' + (it.util.getData($schema.$data, $dataLvl, it.dataPathArr)) + '; '; + $schemaValue = 'schema' + $lvl; + } else { + $schemaValue = $schema; + } + if (($schema || $isData) && it.opts.uniqueItems !== false) { + if ($isData) { + out += ' var ' + ($valid) + '; if (' + ($schemaValue) + ' === false || ' + ($schemaValue) + ' === undefined) ' + ($valid) + ' = true; else if (typeof ' + ($schemaValue) + ' != \'boolean\') ' + ($valid) + ' = false; else { '; + } + out += ' var i = ' + ($data) + '.length , ' + ($valid) + ' = true , j; if (i > 1) { '; + var $itemType = it.schema.items && it.schema.items.type, + $typeIsArray = Array.isArray($itemType); + if (!$itemType || $itemType == 'object' || $itemType == 'array' || ($typeIsArray && ($itemType.indexOf('object') >= 0 || $itemType.indexOf('array') >= 0))) { + out += ' outer: for (;i--;) { for (j = i; j--;) { if (equal(' + ($data) + '[i], ' + ($data) + '[j])) { ' + ($valid) + ' = false; break outer; } } } '; + } else { + out += ' var itemIndices = {}, item; for (;i--;) { var item = ' + ($data) + '[i]; '; + var $method = 'checkDataType' + ($typeIsArray ? 's' : ''); + out += ' if (' + (it.util[$method]($itemType, 'item', true)) + ') continue; '; + if ($typeIsArray) { + out += ' if (typeof item == \'string\') item = \'"\' + item; '; + } + out += ' if (typeof itemIndices[item] == \'number\') { ' + ($valid) + ' = false; j = itemIndices[item]; break; } itemIndices[item] = i; } '; + } + out += ' } '; + if ($isData) { + out += ' } '; + } + out += ' if (!' + ($valid) + ') { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('uniqueItems') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { i: i, j: j } '; + if (it.opts.messages !== false) { + out += ' , message: \'should NOT have duplicate items (items ## \' + j + \' and \' + i + \' are identical)\' '; + } + if (it.opts.verbose) { + out += ' , schema: '; + if ($isData) { + out += 'validate.schema' + ($schemaPath); + } else { + out += '' + ($schema); + } + out += ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } '; + if ($breakOnError) { + out += ' else { '; + } + } else { + if ($breakOnError) { + out += ' if (true) { '; + } + } + return out; +} + + +/***/ }), + +/***/ 902: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_anyOf(it, $keyword, $ruleType) { + var out = ' '; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + var $errs = 'errs__' + $lvl; + var $it = it.util.copy(it); + var $closingBraces = ''; + $it.level++; + var $nextValid = 'valid' + $it.level; + var $noEmptySchema = $schema.every(function($sch) { + return (it.opts.strictKeywords ? typeof $sch == 'object' && Object.keys($sch).length > 0 : it.util.schemaHasRules($sch, it.RULES.all)); + }); + if ($noEmptySchema) { + var $currentBaseId = $it.baseId; + out += ' var ' + ($errs) + ' = errors; var ' + ($valid) + ' = false; '; + var $wasComposite = it.compositeRule; + it.compositeRule = $it.compositeRule = true; + var arr1 = $schema; + if (arr1) { + var $sch, $i = -1, + l1 = arr1.length - 1; + while ($i < l1) { + $sch = arr1[$i += 1]; + $it.schema = $sch; + $it.schemaPath = $schemaPath + '[' + $i + ']'; + $it.errSchemaPath = $errSchemaPath + '/' + $i; + out += ' ' + (it.validate($it)) + ' '; + $it.baseId = $currentBaseId; + out += ' ' + ($valid) + ' = ' + ($valid) + ' || ' + ($nextValid) + '; if (!' + ($valid) + ') { '; + $closingBraces += '}'; + } + } + it.compositeRule = $it.compositeRule = $wasComposite; + out += ' ' + ($closingBraces) + ' if (!' + ($valid) + ') { var err = '; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ('anyOf') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} '; + if (it.opts.messages !== false) { + out += ' , message: \'should match some schema in anyOf\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + out += '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError(vErrors); '; + } else { + out += ' validate.errors = vErrors; return false; '; + } + } + out += ' } else { errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; } '; + if (it.opts.allErrors) { + out += ' } '; + } + out = it.util.cleanUpCode(out); + } else { + if ($breakOnError) { + out += ' if (true) { '; + } + } + return out; +} + + +/***/ }), + +/***/ 909: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2012 Joyent, Inc. All rights reserved. + +var assert = __webpack_require__(477); +var sshpk = __webpack_require__(650); +var util = __webpack_require__(669); + +var HASH_ALGOS = { + 'sha1': true, + 'sha256': true, + 'sha512': true +}; + +var PK_ALGOS = { + 'rsa': true, + 'dsa': true, + 'ecdsa': true +}; + +function HttpSignatureError(message, caller) { + if (Error.captureStackTrace) + Error.captureStackTrace(this, caller || HttpSignatureError); + + this.message = message; + this.name = caller.name; +} +util.inherits(HttpSignatureError, Error); + +function InvalidAlgorithmError(message) { + HttpSignatureError.call(this, message, InvalidAlgorithmError); +} +util.inherits(InvalidAlgorithmError, HttpSignatureError); + +function validateAlgorithm(algorithm) { + var alg = algorithm.toLowerCase().split('-'); + + if (alg.length !== 2) { + throw (new InvalidAlgorithmError(alg[0].toUpperCase() + ' is not a ' + + 'valid algorithm')); + } + + if (alg[0] !== 'hmac' && !PK_ALGOS[alg[0]]) { + throw (new InvalidAlgorithmError(alg[0].toUpperCase() + ' type keys ' + + 'are not supported')); + } + + if (!HASH_ALGOS[alg[1]]) { + throw (new InvalidAlgorithmError(alg[1].toUpperCase() + ' is not a ' + + 'supported hash algorithm')); + } + + return (alg); +} + +///--- API + +module.exports = { + + HASH_ALGOS: HASH_ALGOS, + PK_ALGOS: PK_ALGOS, + + HttpSignatureError: HttpSignatureError, + InvalidAlgorithmError: InvalidAlgorithmError, + + validateAlgorithm: validateAlgorithm, + + /** + * Converts an OpenSSH public key (rsa only) to a PKCS#8 PEM file. + * + * The intent of this module is to interoperate with OpenSSL only, + * specifically the node crypto module's `verify` method. + * + * @param {String} key an OpenSSH public key. + * @return {String} PEM encoded form of the RSA public key. + * @throws {TypeError} on bad input. + * @throws {Error} on invalid ssh key formatted data. + */ + sshKeyToPEM: function sshKeyToPEM(key) { + assert.string(key, 'ssh_key'); + + var k = sshpk.parseKey(key, 'ssh'); + return (k.toString('pem')); + }, + + + /** + * Generates an OpenSSH fingerprint from an ssh public key. + * + * @param {String} key an OpenSSH public key. + * @return {String} key fingerprint. + * @throws {TypeError} on bad input. + * @throws {Error} if what you passed doesn't look like an ssh public key. + */ + fingerprint: function fingerprint(key) { + assert.string(key, 'ssh_key'); + + var k = sshpk.parseKey(key, 'ssh'); + return (k.fingerprint('md5').toString('hex')); + }, + + /** + * Converts a PKGCS#8 PEM file to an OpenSSH public key (rsa) + * + * The reverse of the above function. + */ + pemToRsaSSHKey: function pemToRsaSSHKey(pem, comment) { + assert.equal('string', typeof (pem), 'typeof pem'); + + var k = sshpk.parseKey(pem, 'pem'); + k.comment = comment; + return (k.toString('ssh')); + } +}; + + +/***/ }), + +/***/ 919: +/***/ (function(module) { + +module.exports = {"$id":"entry.json#","$schema":"http://json-schema.org/draft-06/schema#","type":"object","optional":true,"required":["startedDateTime","time","request","response","cache","timings"],"properties":{"pageref":{"type":"string"},"startedDateTime":{"type":"string","format":"date-time","pattern":"^(\\d{4})(-)?(\\d\\d)(-)?(\\d\\d)(T)?(\\d\\d)(:)?(\\d\\d)(:)?(\\d\\d)(\\.\\d+)?(Z|([+-])(\\d\\d)(:)?(\\d\\d))"},"time":{"type":"number","min":0},"request":{"$ref":"request.json#"},"response":{"$ref":"response.json#"},"cache":{"$ref":"cache.json#"},"timings":{"$ref":"timings.json#"},"serverIPAddress":{"type":"string","oneOf":[{"format":"ipv4"},{"format":"ipv6"}]},"connection":{"type":"string"},"comment":{"type":"string"}}}; + +/***/ }), + +/***/ 921: +/***/ (function(module) { + +"use strict"; + + + +var Cache = module.exports = function Cache() { + this._cache = {}; +}; + + +Cache.prototype.put = function Cache_put(key, value) { + this._cache[key] = value; +}; + + +Cache.prototype.get = function Cache_get(key) { + return this._cache[key]; +}; + + +Cache.prototype.del = function Cache_del(key) { + delete this._cache[key]; +}; + + +Cache.prototype.clear = function Cache_clear() { + this._cache = {}; +}; + + +/***/ }), + +/***/ 928: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var CombinedStream = __webpack_require__(547); +var util = __webpack_require__(669); +var path = __webpack_require__(622); +var http = __webpack_require__(605); +var https = __webpack_require__(211); +var parseUrl = __webpack_require__(835).parse; +var fs = __webpack_require__(747); +var mime = __webpack_require__(779); +var asynckit = __webpack_require__(334); +var populate = __webpack_require__(69); + +// Public API +module.exports = FormData; + +// make it a Stream +util.inherits(FormData, CombinedStream); + +/** + * Create readable "multipart/form-data" streams. + * Can be used to submit forms + * and file uploads to other web applications. + * + * @constructor + * @param {Object} options - Properties to be added/overriden for FormData and CombinedStream + */ +function FormData(options) { + if (!(this instanceof FormData)) { + return new FormData(); + } + + this._overheadLength = 0; + this._valueLength = 0; + this._valuesToMeasure = []; + + CombinedStream.call(this); + + options = options || {}; + for (var option in options) { + this[option] = options[option]; + } +} + +FormData.LINE_BREAK = '\r\n'; +FormData.DEFAULT_CONTENT_TYPE = 'application/octet-stream'; + +FormData.prototype.append = function(field, value, options) { + + options = options || {}; + + // allow filename as single option + if (typeof options == 'string') { + options = {filename: options}; + } + + var append = CombinedStream.prototype.append.bind(this); + + // all that streamy business can't handle numbers + if (typeof value == 'number') { + value = '' + value; + } + + // https://github.com/felixge/node-form-data/issues/38 + if (util.isArray(value)) { + // Please convert your array into string + // the way web server expects it + this._error(new Error('Arrays are not supported.')); + return; + } + + var header = this._multiPartHeader(field, value, options); + var footer = this._multiPartFooter(); + + append(header); + append(value); + append(footer); + + // pass along options.knownLength + this._trackLength(header, value, options); +}; + +FormData.prototype._trackLength = function(header, value, options) { + var valueLength = 0; + + // used w/ getLengthSync(), when length is known. + // e.g. for streaming directly from a remote server, + // w/ a known file a size, and not wanting to wait for + // incoming file to finish to get its size. + if (options.knownLength != null) { + valueLength += +options.knownLength; + } else if (Buffer.isBuffer(value)) { + valueLength = value.length; + } else if (typeof value === 'string') { + valueLength = Buffer.byteLength(value); + } + + this._valueLength += valueLength; + + // @check why add CRLF? does this account for custom/multiple CRLFs? + this._overheadLength += + Buffer.byteLength(header) + + FormData.LINE_BREAK.length; + + // empty or either doesn't have path or not an http response + if (!value || ( !value.path && !(value.readable && value.hasOwnProperty('httpVersion')) )) { + return; + } + + // no need to bother with the length + if (!options.knownLength) { + this._valuesToMeasure.push(value); + } +}; + +FormData.prototype._lengthRetriever = function(value, callback) { + + if (value.hasOwnProperty('fd')) { + + // take read range into a account + // `end` = Infinity –> read file till the end + // + // TODO: Looks like there is bug in Node fs.createReadStream + // it doesn't respect `end` options without `start` options + // Fix it when node fixes it. + // https://github.com/joyent/node/issues/7819 + if (value.end != undefined && value.end != Infinity && value.start != undefined) { + + // when end specified + // no need to calculate range + // inclusive, starts with 0 + callback(null, value.end + 1 - (value.start ? value.start : 0)); + + // not that fast snoopy + } else { + // still need to fetch file size from fs + fs.stat(value.path, function(err, stat) { + + var fileSize; + + if (err) { + callback(err); + return; + } + + // update final size based on the range options + fileSize = stat.size - (value.start ? value.start : 0); + callback(null, fileSize); + }); + } + + // or http response + } else if (value.hasOwnProperty('httpVersion')) { + callback(null, +value.headers['content-length']); + + // or request stream http://github.com/mikeal/request + } else if (value.hasOwnProperty('httpModule')) { + // wait till response come back + value.on('response', function(response) { + value.pause(); + callback(null, +response.headers['content-length']); + }); + value.resume(); + + // something else + } else { + callback('Unknown stream'); + } +}; + +FormData.prototype._multiPartHeader = function(field, value, options) { + // custom header specified (as string)? + // it becomes responsible for boundary + // (e.g. to handle extra CRLFs on .NET servers) + if (typeof options.header == 'string') { + return options.header; + } + + var contentDisposition = this._getContentDisposition(value, options); + var contentType = this._getContentType(value, options); + + var contents = ''; + var headers = { + // add custom disposition as third element or keep it two elements if not + 'Content-Disposition': ['form-data', 'name="' + field + '"'].concat(contentDisposition || []), + // if no content type. allow it to be empty array + 'Content-Type': [].concat(contentType || []) + }; + + // allow custom headers. + if (typeof options.header == 'object') { + populate(headers, options.header); + } + + var header; + for (var prop in headers) { + if (!headers.hasOwnProperty(prop)) continue; + header = headers[prop]; + + // skip nullish headers. + if (header == null) { + continue; + } + + // convert all headers to arrays. + if (!Array.isArray(header)) { + header = [header]; + } + + // add non-empty headers. + if (header.length) { + contents += prop + ': ' + header.join('; ') + FormData.LINE_BREAK; + } + } + + return '--' + this.getBoundary() + FormData.LINE_BREAK + contents + FormData.LINE_BREAK; +}; + +FormData.prototype._getContentDisposition = function(value, options) { + + var filename + , contentDisposition + ; + + if (typeof options.filepath === 'string') { + // custom filepath for relative paths + filename = path.normalize(options.filepath).replace(/\\/g, '/'); + } else if (options.filename || value.name || value.path) { + // custom filename take precedence + // formidable and the browser add a name property + // fs- and request- streams have path property + filename = path.basename(options.filename || value.name || value.path); + } else if (value.readable && value.hasOwnProperty('httpVersion')) { + // or try http response + filename = path.basename(value.client._httpMessage.path); + } + + if (filename) { + contentDisposition = 'filename="' + filename + '"'; + } + + return contentDisposition; +}; + +FormData.prototype._getContentType = function(value, options) { + + // use custom content-type above all + var contentType = options.contentType; + + // or try `name` from formidable, browser + if (!contentType && value.name) { + contentType = mime.lookup(value.name); + } + + // or try `path` from fs-, request- streams + if (!contentType && value.path) { + contentType = mime.lookup(value.path); + } + + // or if it's http-reponse + if (!contentType && value.readable && value.hasOwnProperty('httpVersion')) { + contentType = value.headers['content-type']; + } + + // or guess it from the filepath or filename + if (!contentType && (options.filepath || options.filename)) { + contentType = mime.lookup(options.filepath || options.filename); + } + + // fallback to the default content type if `value` is not simple value + if (!contentType && typeof value == 'object') { + contentType = FormData.DEFAULT_CONTENT_TYPE; + } + + return contentType; +}; + +FormData.prototype._multiPartFooter = function() { + return function(next) { + var footer = FormData.LINE_BREAK; + + var lastPart = (this._streams.length === 0); + if (lastPart) { + footer += this._lastBoundary(); + } + + next(footer); + }.bind(this); +}; + +FormData.prototype._lastBoundary = function() { + return '--' + this.getBoundary() + '--' + FormData.LINE_BREAK; +}; + +FormData.prototype.getHeaders = function(userHeaders) { + var header; + var formHeaders = { + 'content-type': 'multipart/form-data; boundary=' + this.getBoundary() + }; + + for (header in userHeaders) { + if (userHeaders.hasOwnProperty(header)) { + formHeaders[header.toLowerCase()] = userHeaders[header]; + } + } + + return formHeaders; +}; + +FormData.prototype.getBoundary = function() { + if (!this._boundary) { + this._generateBoundary(); + } + + return this._boundary; +}; + +FormData.prototype._generateBoundary = function() { + // This generates a 50 character boundary similar to those used by Firefox. + // They are optimized for boyer-moore parsing. + var boundary = '--------------------------'; + for (var i = 0; i < 24; i++) { + boundary += Math.floor(Math.random() * 10).toString(16); + } + + this._boundary = boundary; +}; + +// Note: getLengthSync DOESN'T calculate streams length +// As workaround one can calculate file size manually +// and add it as knownLength option +FormData.prototype.getLengthSync = function() { + var knownLength = this._overheadLength + this._valueLength; + + // Don't get confused, there are 3 "internal" streams for each keyval pair + // so it basically checks if there is any value added to the form + if (this._streams.length) { + knownLength += this._lastBoundary().length; + } + + // https://github.com/form-data/form-data/issues/40 + if (!this.hasKnownLength()) { + // Some async length retrievers are present + // therefore synchronous length calculation is false. + // Please use getLength(callback) to get proper length + this._error(new Error('Cannot calculate proper length in synchronous way.')); + } + + return knownLength; +}; + +// Public API to check if length of added values is known +// https://github.com/form-data/form-data/issues/196 +// https://github.com/form-data/form-data/issues/262 +FormData.prototype.hasKnownLength = function() { + var hasKnownLength = true; + + if (this._valuesToMeasure.length) { + hasKnownLength = false; + } + + return hasKnownLength; +}; + +FormData.prototype.getLength = function(cb) { + var knownLength = this._overheadLength + this._valueLength; + + if (this._streams.length) { + knownLength += this._lastBoundary().length; + } + + if (!this._valuesToMeasure.length) { + process.nextTick(cb.bind(this, null, knownLength)); + return; + } + + asynckit.parallel(this._valuesToMeasure, this._lengthRetriever, function(err, values) { + if (err) { + cb(err); + return; + } + + values.forEach(function(length) { + knownLength += length; + }); + + cb(null, knownLength); + }); +}; + +FormData.prototype.submit = function(params, cb) { + var request + , options + , defaults = {method: 'post'} + ; + + // parse provided url if it's string + // or treat it as options object + if (typeof params == 'string') { + + params = parseUrl(params); + options = populate({ + port: params.port, + path: params.pathname, + host: params.hostname, + protocol: params.protocol + }, defaults); + + // use custom params + } else { + + options = populate(params, defaults); + // if no port provided use default one + if (!options.port) { + options.port = options.protocol == 'https:' ? 443 : 80; + } + } + + // put that good code in getHeaders to some use + options.headers = this.getHeaders(params.headers); + + // https if specified, fallback to http in any other case + if (options.protocol == 'https:') { + request = https.request(options); + } else { + request = http.request(options); + } + + // get content length and fire away + this.getLength(function(err, length) { + if (err) { + this._error(err); + return; + } + + // add content length + request.setHeader('Content-Length', length); + + this.pipe(request); + if (cb) { + request.on('error', cb); + request.on('response', cb.bind(this, null)); + } + }.bind(this)); + + return request; +}; + +FormData.prototype._error = function(err) { + if (!this.error) { + this.error = err; + this.pause(); + this.emit('error', err); + } +}; + +FormData.prototype.toString = function () { + return '[object FormData]'; +}; + + +/***/ }), + +/***/ 939: +/***/ (function(module, __unusedexports, __webpack_require__) { + +var abort = __webpack_require__(566) + , async = __webpack_require__(751) + ; + +// API +module.exports = terminator; + +/** + * Terminates jobs in the attached state context + * + * @this AsyncKitState# + * @param {function} callback - final callback to invoke after termination + */ +function terminator(callback) +{ + if (!Object.keys(this.jobs).length) + { + return; + } + + // fast forward iteration index + this.index = this.size; + + // abort jobs + abort(this); + + // send back results we have so far + async(callback)(null, this.results); +} + + +/***/ }), + +/***/ 940: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2015 Joyent, Inc. + +module.exports = SSHBuffer; + +var assert = __webpack_require__(477); +var Buffer = __webpack_require__(215).Buffer; + +function SSHBuffer(opts) { + assert.object(opts, 'options'); + if (opts.buffer !== undefined) + assert.buffer(opts.buffer, 'options.buffer'); + + this._size = opts.buffer ? opts.buffer.length : 1024; + this._buffer = opts.buffer || Buffer.alloc(this._size); + this._offset = 0; +} + +SSHBuffer.prototype.toBuffer = function () { + return (this._buffer.slice(0, this._offset)); +}; + +SSHBuffer.prototype.atEnd = function () { + return (this._offset >= this._buffer.length); +}; + +SSHBuffer.prototype.remainder = function () { + return (this._buffer.slice(this._offset)); +}; + +SSHBuffer.prototype.skip = function (n) { + this._offset += n; +}; + +SSHBuffer.prototype.expand = function () { + this._size *= 2; + var buf = Buffer.alloc(this._size); + this._buffer.copy(buf, 0); + this._buffer = buf; +}; + +SSHBuffer.prototype.readPart = function () { + return ({data: this.readBuffer()}); +}; + +SSHBuffer.prototype.readBuffer = function () { + var len = this._buffer.readUInt32BE(this._offset); + this._offset += 4; + assert.ok(this._offset + len <= this._buffer.length, + 'length out of bounds at +0x' + this._offset.toString(16) + + ' (data truncated?)'); + var buf = this._buffer.slice(this._offset, this._offset + len); + this._offset += len; + return (buf); +}; + +SSHBuffer.prototype.readString = function () { + return (this.readBuffer().toString()); +}; + +SSHBuffer.prototype.readCString = function () { + var offset = this._offset; + while (offset < this._buffer.length && + this._buffer[offset] !== 0x00) + offset++; + assert.ok(offset < this._buffer.length, 'c string does not terminate'); + var str = this._buffer.slice(this._offset, offset).toString(); + this._offset = offset + 1; + return (str); +}; + +SSHBuffer.prototype.readInt = function () { + var v = this._buffer.readUInt32BE(this._offset); + this._offset += 4; + return (v); +}; + +SSHBuffer.prototype.readInt64 = function () { + assert.ok(this._offset + 8 < this._buffer.length, + 'buffer not long enough to read Int64'); + var v = this._buffer.slice(this._offset, this._offset + 8); + this._offset += 8; + return (v); +}; + +SSHBuffer.prototype.readChar = function () { + var v = this._buffer[this._offset++]; + return (v); +}; + +SSHBuffer.prototype.writeBuffer = function (buf) { + while (this._offset + 4 + buf.length > this._size) + this.expand(); + this._buffer.writeUInt32BE(buf.length, this._offset); + this._offset += 4; + buf.copy(this._buffer, this._offset); + this._offset += buf.length; +}; + +SSHBuffer.prototype.writeString = function (str) { + this.writeBuffer(Buffer.from(str, 'utf8')); +}; + +SSHBuffer.prototype.writeCString = function (str) { + while (this._offset + 1 + str.length > this._size) + this.expand(); + this._buffer.write(str, this._offset); + this._offset += str.length; + this._buffer[this._offset++] = 0; +}; + +SSHBuffer.prototype.writeInt = function (v) { + while (this._offset + 4 > this._size) + this.expand(); + this._buffer.writeUInt32BE(v, this._offset); + this._offset += 4; +}; + +SSHBuffer.prototype.writeInt64 = function (v) { + assert.buffer(v, 'value'); + if (v.length > 8) { + var lead = v.slice(0, v.length - 8); + for (var i = 0; i < lead.length; ++i) { + assert.strictEqual(lead[i], 0, + 'must fit in 64 bits of precision'); + } + v = v.slice(v.length - 8, v.length); + } + while (this._offset + 8 > this._size) + this.expand(); + v.copy(this._buffer, this._offset); + this._offset += 8; +}; + +SSHBuffer.prototype.writeChar = function (v) { + while (this._offset + 1 > this._size) + this.expand(); + this._buffer[this._offset++] = v; +}; + +SSHBuffer.prototype.writePart = function (p) { + this.writeBuffer(p.data); +}; + +SSHBuffer.prototype.write = function (buf) { + while (this._offset + buf.length > this._size) + this.expand(); + buf.copy(this._buffer, this._offset); + this._offset += buf.length; +}; + + +/***/ }), + +/***/ 942: +/***/ (function(module, __unusedexports, __webpack_require__) { + + +/*! + * Copyright 2010 LearnBoost + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Module dependencies. + */ + +var crypto = __webpack_require__(417) + , parse = __webpack_require__(835).parse + ; + +/** + * Valid keys. + */ + +var keys = + [ 'acl' + , 'location' + , 'logging' + , 'notification' + , 'partNumber' + , 'policy' + , 'requestPayment' + , 'torrent' + , 'uploadId' + , 'uploads' + , 'versionId' + , 'versioning' + , 'versions' + , 'website' + ] + +/** + * Return an "Authorization" header value with the given `options` + * in the form of "AWS :" + * + * @param {Object} options + * @return {String} + * @api private + */ + +function authorization (options) { + return 'AWS ' + options.key + ':' + sign(options) +} + +module.exports = authorization +module.exports.authorization = authorization + +/** + * Simple HMAC-SHA1 Wrapper + * + * @param {Object} options + * @return {String} + * @api private + */ + +function hmacSha1 (options) { + return crypto.createHmac('sha1', options.secret).update(options.message).digest('base64') +} + +module.exports.hmacSha1 = hmacSha1 + +/** + * Create a base64 sha1 HMAC for `options`. + * + * @param {Object} options + * @return {String} + * @api private + */ + +function sign (options) { + options.message = stringToSign(options) + return hmacSha1(options) +} +module.exports.sign = sign + +/** + * Create a base64 sha1 HMAC for `options`. + * + * Specifically to be used with S3 presigned URLs + * + * @param {Object} options + * @return {String} + * @api private + */ + +function signQuery (options) { + options.message = queryStringToSign(options) + return hmacSha1(options) +} +module.exports.signQuery= signQuery + +/** + * Return a string for sign() with the given `options`. + * + * Spec: + * + * \n + * \n + * \n + * \n + * [headers\n] + * + * + * @param {Object} options + * @return {String} + * @api private + */ + +function stringToSign (options) { + var headers = options.amazonHeaders || '' + if (headers) headers += '\n' + var r = + [ options.verb + , options.md5 + , options.contentType + , options.date ? options.date.toUTCString() : '' + , headers + options.resource + ] + return r.join('\n') +} +module.exports.stringToSign = stringToSign + +/** + * Return a string for sign() with the given `options`, but is meant exclusively + * for S3 presigned URLs + * + * Spec: + * + * \n + * + * + * @param {Object} options + * @return {String} + * @api private + */ + +function queryStringToSign (options){ + return 'GET\n\n\n' + options.date + '\n' + options.resource +} +module.exports.queryStringToSign = queryStringToSign + +/** + * Perform the following: + * + * - ignore non-amazon headers + * - lowercase fields + * - sort lexicographically + * - trim whitespace between ":" + * - join with newline + * + * @param {Object} headers + * @return {String} + * @api private + */ + +function canonicalizeHeaders (headers) { + var buf = [] + , fields = Object.keys(headers) + ; + for (var i = 0, len = fields.length; i < len; ++i) { + var field = fields[i] + , val = headers[field] + , field = field.toLowerCase() + ; + if (0 !== field.indexOf('x-amz')) continue + buf.push(field + ':' + val) + } + return buf.sort().join('\n') +} +module.exports.canonicalizeHeaders = canonicalizeHeaders + +/** + * Perform the following: + * + * - ignore non sub-resources + * - sort lexicographically + * + * @param {String} resource + * @return {String} + * @api private + */ + +function canonicalizeResource (resource) { + var url = parse(resource, true) + , path = url.pathname + , buf = [] + ; + + Object.keys(url.query).forEach(function(key){ + if (!~keys.indexOf(key)) return + var val = '' == url.query[key] ? '' : '=' + encodeURIComponent(url.query[key]) + buf.push(key + val) + }) + + return path + (buf.length ? '?' + buf.sort().join('&') : '') +} +module.exports.canonicalizeResource = canonicalizeResource + + +/***/ }), + +/***/ 944: +/***/ (function(module) { + +module.exports = isTypedArray +isTypedArray.strict = isStrictTypedArray +isTypedArray.loose = isLooseTypedArray + +var toString = Object.prototype.toString +var names = { + '[object Int8Array]': true + , '[object Int16Array]': true + , '[object Int32Array]': true + , '[object Uint8Array]': true + , '[object Uint8ClampedArray]': true + , '[object Uint16Array]': true + , '[object Uint32Array]': true + , '[object Float32Array]': true + , '[object Float64Array]': true +} + +function isTypedArray(arr) { + return ( + isStrictTypedArray(arr) + || isLooseTypedArray(arr) + ) +} + +function isStrictTypedArray(arr) { + return ( + arr instanceof Int8Array + || arr instanceof Int16Array + || arr instanceof Int32Array + || arr instanceof Uint8Array + || arr instanceof Uint8ClampedArray + || arr instanceof Uint16Array + || arr instanceof Uint32Array + || arr instanceof Float32Array + || arr instanceof Float64Array + ) +} + +function isLooseTypedArray(arr) { + return names[toString.call(arr)] +} + + +/***/ }), + +/***/ 952: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +var metaSchema = __webpack_require__(522); + +module.exports = { + $id: 'https://github.com/epoberezkin/ajv/blob/master/lib/definition_schema.js', + definitions: { + simpleTypes: metaSchema.definitions.simpleTypes + }, + type: 'object', + dependencies: { + schema: ['validate'], + $data: ['validate'], + statements: ['inline'], + valid: {not: {required: ['macro']}} + }, + properties: { + type: metaSchema.properties.type, + schema: {type: 'boolean'}, + statements: {type: 'boolean'}, + dependencies: { + type: 'array', + items: {type: 'string'} + }, + metaSchema: {type: 'object'}, + modifying: {type: 'boolean'}, + valid: {type: 'boolean'}, + $data: {type: 'boolean'}, + async: {type: 'boolean'}, + errors: { + anyOf: [ + {type: 'boolean'}, + {const: 'full'} + ] + } + } +}; + + +/***/ }), + +/***/ 955: +/***/ (function(module, __unusedexports, __webpack_require__) { + +"use strict"; + + +var util = __webpack_require__(855); + +module.exports = SchemaObject; + +function SchemaObject(obj) { + util.copy(obj, this); +} + + +/***/ }), + +/***/ 956: +/***/ (function(module, __unusedexports, __webpack_require__) { + +/* + * verror.js: richer JavaScript errors + */ + +var mod_assertplus = __webpack_require__(477); +var mod_util = __webpack_require__(669); + +var mod_extsprintf = __webpack_require__(697); +var mod_isError = __webpack_require__(286).isError; +var sprintf = mod_extsprintf.sprintf; + +/* + * Public interface + */ + +/* So you can 'var VError = require('verror')' */ +module.exports = VError; +/* For compatibility */ +VError.VError = VError; +/* Other exported classes */ +VError.SError = SError; +VError.WError = WError; +VError.MultiError = MultiError; + +/* + * Common function used to parse constructor arguments for VError, WError, and + * SError. Named arguments to this function: + * + * strict force strict interpretation of sprintf arguments, even + * if the options in "argv" don't say so + * + * argv error's constructor arguments, which are to be + * interpreted as described in README.md. For quick + * reference, "argv" has one of the following forms: + * + * [ sprintf_args... ] (argv[0] is a string) + * [ cause, sprintf_args... ] (argv[0] is an Error) + * [ options, sprintf_args... ] (argv[0] is an object) + * + * This function normalizes these forms, producing an object with the following + * properties: + * + * options equivalent to "options" in third form. This will never + * be a direct reference to what the caller passed in + * (i.e., it may be a shallow copy), so it can be freely + * modified. + * + * shortmessage result of sprintf(sprintf_args), taking options.strict + * into account as described in README.md. + */ +function parseConstructorArguments(args) +{ + var argv, options, sprintf_args, shortmessage, k; + + mod_assertplus.object(args, 'args'); + mod_assertplus.bool(args.strict, 'args.strict'); + mod_assertplus.array(args.argv, 'args.argv'); + argv = args.argv; + + /* + * First, figure out which form of invocation we've been given. + */ + if (argv.length === 0) { + options = {}; + sprintf_args = []; + } else if (mod_isError(argv[0])) { + options = { 'cause': argv[0] }; + sprintf_args = argv.slice(1); + } else if (typeof (argv[0]) === 'object') { + options = {}; + for (k in argv[0]) { + options[k] = argv[0][k]; + } + sprintf_args = argv.slice(1); + } else { + mod_assertplus.string(argv[0], + 'first argument to VError, SError, or WError ' + + 'constructor must be a string, object, or Error'); + options = {}; + sprintf_args = argv; + } + + /* + * Now construct the error's message. + * + * extsprintf (which we invoke here with our caller's arguments in order + * to construct this Error's message) is strict in its interpretation of + * values to be processed by the "%s" specifier. The value passed to + * extsprintf must actually be a string or something convertible to a + * String using .toString(). Passing other values (notably "null" and + * "undefined") is considered a programmer error. The assumption is + * that if you actually want to print the string "null" or "undefined", + * then that's easy to do that when you're calling extsprintf; on the + * other hand, if you did NOT want that (i.e., there's actually a bug + * where the program assumes some variable is non-null and tries to + * print it, which might happen when constructing a packet or file in + * some specific format), then it's better to stop immediately than + * produce bogus output. + * + * However, sometimes the bug is only in the code calling VError, and a + * programmer might prefer to have the error message contain "null" or + * "undefined" rather than have the bug in the error path crash the + * program (making the first bug harder to identify). For that reason, + * by default VError converts "null" or "undefined" arguments to their + * string representations and passes those to extsprintf. Programmers + * desiring the strict behavior can use the SError class or pass the + * "strict" option to the VError constructor. + */ + mod_assertplus.object(options); + if (!options.strict && !args.strict) { + sprintf_args = sprintf_args.map(function (a) { + return (a === null ? 'null' : + a === undefined ? 'undefined' : a); + }); + } + + if (sprintf_args.length === 0) { + shortmessage = ''; + } else { + shortmessage = sprintf.apply(null, sprintf_args); + } + + return ({ + 'options': options, + 'shortmessage': shortmessage + }); +} + +/* + * See README.md for reference documentation. + */ +function VError() +{ + var args, obj, parsed, cause, ctor, message, k; + + args = Array.prototype.slice.call(arguments, 0); + + /* + * This is a regrettable pattern, but JavaScript's built-in Error class + * is defined to work this way, so we allow the constructor to be called + * without "new". + */ + if (!(this instanceof VError)) { + obj = Object.create(VError.prototype); + VError.apply(obj, arguments); + return (obj); + } + + /* + * For convenience and backwards compatibility, we support several + * different calling forms. Normalize them here. + */ + parsed = parseConstructorArguments({ + 'argv': args, + 'strict': false + }); + + /* + * If we've been given a name, apply it now. + */ + if (parsed.options.name) { + mod_assertplus.string(parsed.options.name, + 'error\'s "name" must be a string'); + this.name = parsed.options.name; + } + + /* + * For debugging, we keep track of the original short message (attached + * this Error particularly) separately from the complete message (which + * includes the messages of our cause chain). + */ + this.jse_shortmsg = parsed.shortmessage; + message = parsed.shortmessage; + + /* + * If we've been given a cause, record a reference to it and update our + * message appropriately. + */ + cause = parsed.options.cause; + if (cause) { + mod_assertplus.ok(mod_isError(cause), 'cause is not an Error'); + this.jse_cause = cause; + + if (!parsed.options.skipCauseMessage) { + message += ': ' + cause.message; + } + } + + /* + * If we've been given an object with properties, shallow-copy that + * here. We don't want to use a deep copy in case there are non-plain + * objects here, but we don't want to use the original object in case + * the caller modifies it later. + */ + this.jse_info = {}; + if (parsed.options.info) { + for (k in parsed.options.info) { + this.jse_info[k] = parsed.options.info[k]; + } + } + + this.message = message; + Error.call(this, message); + + if (Error.captureStackTrace) { + ctor = parsed.options.constructorOpt || this.constructor; + Error.captureStackTrace(this, ctor); + } + + return (this); +} + +mod_util.inherits(VError, Error); +VError.prototype.name = 'VError'; + +VError.prototype.toString = function ve_toString() +{ + var str = (this.hasOwnProperty('name') && this.name || + this.constructor.name || this.constructor.prototype.name); + if (this.message) + str += ': ' + this.message; + + return (str); +}; + +/* + * This method is provided for compatibility. New callers should use + * VError.cause() instead. That method also uses the saner `null` return value + * when there is no cause. + */ +VError.prototype.cause = function ve_cause() +{ + var cause = VError.cause(this); + return (cause === null ? undefined : cause); +}; + +/* + * Static methods + * + * These class-level methods are provided so that callers can use them on + * instances of Errors that are not VErrors. New interfaces should be provided + * only using static methods to eliminate the class of programming mistake where + * people fail to check whether the Error object has the corresponding methods. + */ + +VError.cause = function (err) +{ + mod_assertplus.ok(mod_isError(err), 'err must be an Error'); + return (mod_isError(err.jse_cause) ? err.jse_cause : null); +}; + +VError.info = function (err) +{ + var rv, cause, k; + + mod_assertplus.ok(mod_isError(err), 'err must be an Error'); + cause = VError.cause(err); + if (cause !== null) { + rv = VError.info(cause); + } else { + rv = {}; + } + + if (typeof (err.jse_info) == 'object' && err.jse_info !== null) { + for (k in err.jse_info) { + rv[k] = err.jse_info[k]; + } + } + + return (rv); +}; + +VError.findCauseByName = function (err, name) +{ + var cause; + + mod_assertplus.ok(mod_isError(err), 'err must be an Error'); + mod_assertplus.string(name, 'name'); + mod_assertplus.ok(name.length > 0, 'name cannot be empty'); + + for (cause = err; cause !== null; cause = VError.cause(cause)) { + mod_assertplus.ok(mod_isError(cause)); + if (cause.name == name) { + return (cause); + } + } + + return (null); +}; + +VError.hasCauseWithName = function (err, name) +{ + return (VError.findCauseByName(err, name) !== null); +}; + +VError.fullStack = function (err) +{ + mod_assertplus.ok(mod_isError(err), 'err must be an Error'); + + var cause = VError.cause(err); + + if (cause) { + return (err.stack + '\ncaused by: ' + VError.fullStack(cause)); + } + + return (err.stack); +}; + +VError.errorFromList = function (errors) +{ + mod_assertplus.arrayOfObject(errors, 'errors'); + + if (errors.length === 0) { + return (null); + } + + errors.forEach(function (e) { + mod_assertplus.ok(mod_isError(e)); + }); + + if (errors.length == 1) { + return (errors[0]); + } + + return (new MultiError(errors)); +}; + +VError.errorForEach = function (err, func) +{ + mod_assertplus.ok(mod_isError(err), 'err must be an Error'); + mod_assertplus.func(func, 'func'); + + if (err instanceof MultiError) { + err.errors().forEach(function iterError(e) { func(e); }); + } else { + func(err); + } +}; + + +/* + * SError is like VError, but stricter about types. You cannot pass "null" or + * "undefined" as string arguments to the formatter. + */ +function SError() +{ + var args, obj, parsed, options; + + args = Array.prototype.slice.call(arguments, 0); + if (!(this instanceof SError)) { + obj = Object.create(SError.prototype); + SError.apply(obj, arguments); + return (obj); + } + + parsed = parseConstructorArguments({ + 'argv': args, + 'strict': true + }); + + options = parsed.options; + VError.call(this, options, '%s', parsed.shortmessage); + + return (this); +} + +/* + * We don't bother setting SError.prototype.name because once constructed, + * SErrors are just like VErrors. + */ +mod_util.inherits(SError, VError); + + +/* + * Represents a collection of errors for the purpose of consumers that generally + * only deal with one error. Callers can extract the individual errors + * contained in this object, but may also just treat it as a normal single + * error, in which case a summary message will be printed. + */ +function MultiError(errors) +{ + mod_assertplus.array(errors, 'list of errors'); + mod_assertplus.ok(errors.length > 0, 'must be at least one error'); + this.ase_errors = errors; + + VError.call(this, { + 'cause': errors[0] + }, 'first of %d error%s', errors.length, errors.length == 1 ? '' : 's'); +} + +mod_util.inherits(MultiError, VError); +MultiError.prototype.name = 'MultiError'; + +MultiError.prototype.errors = function me_errors() +{ + return (this.ase_errors.slice(0)); +}; + + +/* + * See README.md for reference details. + */ +function WError() +{ + var args, obj, parsed, options; + + args = Array.prototype.slice.call(arguments, 0); + if (!(this instanceof WError)) { + obj = Object.create(WError.prototype); + WError.apply(obj, args); + return (obj); + } + + parsed = parseConstructorArguments({ + 'argv': args, + 'strict': false + }); + + options = parsed.options; + options['skipCauseMessage'] = true; + VError.call(this, options, '%s', parsed.shortmessage); + + return (this); +} + +mod_util.inherits(WError, VError); +WError.prototype.name = 'WError'; + +WError.prototype.toString = function we_toString() +{ + var str = (this.hasOwnProperty('name') && this.name || + this.constructor.name || this.constructor.prototype.name); + if (this.message) + str += ': ' + this.message; + if (this.jse_cause && this.jse_cause.message) + str += '; caused by ' + this.jse_cause.toString(); + + return (str); +}; + +/* + * For purely historical reasons, WError's cause() function allows you to set + * the cause. + */ +WError.prototype.cause = function we_cause(c) +{ + if (mod_isError(c)) + this.jse_cause = c; + + return (this.jse_cause); +}; + + +/***/ }), + +/***/ 959: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Named EC curves + +// Requires ec.js, jsbn.js, and jsbn2.js +var BigInteger = __webpack_require__(242).BigInteger +var ECCurveFp = __webpack_require__(729).ECCurveFp + + +// ---------------- +// X9ECParameters + +// constructor +function X9ECParameters(curve,g,n,h) { + this.curve = curve; + this.g = g; + this.n = n; + this.h = h; +} + +function x9getCurve() { + return this.curve; +} + +function x9getG() { + return this.g; +} + +function x9getN() { + return this.n; +} + +function x9getH() { + return this.h; +} + +X9ECParameters.prototype.getCurve = x9getCurve; +X9ECParameters.prototype.getG = x9getG; +X9ECParameters.prototype.getN = x9getN; +X9ECParameters.prototype.getH = x9getH; + +// ---------------- +// SECNamedCurves + +function fromHex(s) { return new BigInteger(s, 16); } + +function secp128r1() { + // p = 2^128 - 2^97 - 1 + var p = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF"); + var a = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC"); + var b = fromHex("E87579C11079F43DD824993C2CEE5ED3"); + //byte[] S = Hex.decode("000E0D4D696E6768756151750CC03A4473D03679"); + var n = fromHex("FFFFFFFE0000000075A30D1B9038A115"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + var G = curve.decodePointHex("04" + + "161FF7528B899B2D0C28607CA52C5B86" + + "CF5AC8395BAFEB13C02DA292DDED7A83"); + return new X9ECParameters(curve, G, n, h); +} + +function secp160k1() { + // p = 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73"); + var a = BigInteger.ZERO; + var b = fromHex("7"); + //byte[] S = null; + var n = fromHex("0100000000000000000001B8FA16DFAB9ACA16B6B3"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + var G = curve.decodePointHex("04" + + "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB" + + "938CF935318FDCED6BC28286531733C3F03C4FEE"); + return new X9ECParameters(curve, G, n, h); +} + +function secp160r1() { + // p = 2^160 - 2^31 - 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF"); + var a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC"); + var b = fromHex("1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45"); + //byte[] S = Hex.decode("1053CDE42C14D696E67687561517533BF3F83345"); + var n = fromHex("0100000000000000000001F4C8F927AED3CA752257"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + var G = curve.decodePointHex("04" + + "4A96B5688EF573284664698968C38BB913CBFC82" + + "23A628553168947D59DCC912042351377AC5FB32"); + return new X9ECParameters(curve, G, n, h); +} + +function secp192k1() { + // p = 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37"); + var a = BigInteger.ZERO; + var b = fromHex("3"); + //byte[] S = null; + var n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + var G = curve.decodePointHex("04" + + "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D" + + "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D"); + return new X9ECParameters(curve, G, n, h); +} + +function secp192r1() { + // p = 2^192 - 2^64 - 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"); + var a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC"); + var b = fromHex("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1"); + //byte[] S = Hex.decode("3045AE6FC8422F64ED579528D38120EAE12196D5"); + var n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + var G = curve.decodePointHex("04" + + "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012" + + "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811"); + return new X9ECParameters(curve, G, n, h); +} + +function secp224r1() { + // p = 2^224 - 2^96 + 1 + var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001"); + var a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE"); + var b = fromHex("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4"); + //byte[] S = Hex.decode("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5"); + var n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + var G = curve.decodePointHex("04" + + "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21" + + "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34"); + return new X9ECParameters(curve, G, n, h); +} + +function secp256r1() { + // p = 2^224 (2^32 - 1) + 2^192 + 2^96 - 1 + var p = fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"); + var a = fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"); + var b = fromHex("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"); + //byte[] S = Hex.decode("C49D360886E704936A6678E1139D26B7819F7E90"); + var n = fromHex("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"); + var h = BigInteger.ONE; + var curve = new ECCurveFp(p, a, b); + var G = curve.decodePointHex("04" + + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296" + + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"); + return new X9ECParameters(curve, G, n, h); +} + +// TODO: make this into a proper hashtable +function getSECCurveByName(name) { + if(name == "secp128r1") return secp128r1(); + if(name == "secp160k1") return secp160k1(); + if(name == "secp160r1") return secp160r1(); + if(name == "secp192k1") return secp192k1(); + if(name == "secp192r1") return secp192r1(); + if(name == "secp224r1") return secp224r1(); + if(name == "secp256r1") return secp256r1(); + return null; +} + +module.exports = { + "secp128r1":secp128r1, + "secp160k1":secp160k1, + "secp160r1":secp160r1, + "secp192k1":secp192k1, + "secp192r1":secp192r1, + "secp224r1":secp224r1, + "secp256r1":secp256r1 +} + + +/***/ }), + +/***/ 964: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + + +var crypto = __webpack_require__(417) + +function randomString (size) { + var bits = (size + 1) * 6 + var buffer = crypto.randomBytes(Math.ceil(bits / 8)) + var string = buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '') + return string.slice(0, size) +} + +function calculatePayloadHash (payload, algorithm, contentType) { + var hash = crypto.createHash(algorithm) + hash.update('hawk.1.payload\n') + hash.update((contentType ? contentType.split(';')[0].trim().toLowerCase() : '') + '\n') + hash.update(payload || '') + hash.update('\n') + return hash.digest('base64') +} + +exports.calculateMac = function (credentials, opts) { + var normalized = 'hawk.1.header\n' + + opts.ts + '\n' + + opts.nonce + '\n' + + (opts.method || '').toUpperCase() + '\n' + + opts.resource + '\n' + + opts.host.toLowerCase() + '\n' + + opts.port + '\n' + + (opts.hash || '') + '\n' + + if (opts.ext) { + normalized = normalized + opts.ext.replace('\\', '\\\\').replace('\n', '\\n') + } + + normalized = normalized + '\n' + + if (opts.app) { + normalized = normalized + opts.app + '\n' + (opts.dlg || '') + '\n' + } + + var hmac = crypto.createHmac(credentials.algorithm, credentials.key).update(normalized) + var digest = hmac.digest('base64') + return digest +} + +exports.header = function (uri, method, opts) { + var timestamp = opts.timestamp || Math.floor((Date.now() + (opts.localtimeOffsetMsec || 0)) / 1000) + var credentials = opts.credentials + if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) { + return '' + } + + if (['sha1', 'sha256'].indexOf(credentials.algorithm) === -1) { + return '' + } + + var artifacts = { + ts: timestamp, + nonce: opts.nonce || randomString(6), + method: method, + resource: uri.pathname + (uri.search || ''), + host: uri.hostname, + port: uri.port || (uri.protocol === 'http:' ? 80 : 443), + hash: opts.hash, + ext: opts.ext, + app: opts.app, + dlg: opts.dlg + } + + if (!artifacts.hash && (opts.payload || opts.payload === '')) { + artifacts.hash = calculatePayloadHash(opts.payload, credentials.algorithm, opts.contentType) + } + + var mac = exports.calculateMac(credentials, artifacts) + + var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== '' + var header = 'Hawk id="' + credentials.id + + '", ts="' + artifacts.ts + + '", nonce="' + artifacts.nonce + + (artifacts.hash ? '", hash="' + artifacts.hash : '') + + (hasExt ? '", ext="' + artifacts.ext.replace(/\\/g, '\\\\').replace(/"/g, '\\"') : '') + + '", mac="' + mac + '"' + + if (artifacts.app) { + header = header + ', app="' + artifacts.app + (artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"' + } + + return header +} + + +/***/ }), + +/***/ 967: +/***/ (function(module) { + +"use strict"; + +module.exports = function generate_validate(it, $keyword, $ruleType) { + var out = ''; + var $async = it.schema.$async === true, + $refKeywords = it.util.schemaHasRulesExcept(it.schema, it.RULES.all, '$ref'), + $id = it.self._getId(it.schema); + if (it.opts.strictKeywords) { + var $unknownKwd = it.util.schemaUnknownRules(it.schema, it.RULES.keywords); + if ($unknownKwd) { + var $keywordsMsg = 'unknown keyword: ' + $unknownKwd; + if (it.opts.strictKeywords === 'log') it.logger.warn($keywordsMsg); + else throw new Error($keywordsMsg); + } + } + if (it.isTop) { + out += ' var validate = '; + if ($async) { + it.async = true; + out += 'async '; + } + out += 'function(data, dataPath, parentData, parentDataProperty, rootData) { \'use strict\'; '; + if ($id && (it.opts.sourceCode || it.opts.processCode)) { + out += ' ' + ('/\*# sourceURL=' + $id + ' */') + ' '; + } + } + if (typeof it.schema == 'boolean' || !($refKeywords || it.schema.$ref)) { + var $keyword = 'false schema'; + var $lvl = it.level; + var $dataLvl = it.dataLevel; + var $schema = it.schema[$keyword]; + var $schemaPath = it.schemaPath + it.util.getProperty($keyword); + var $errSchemaPath = it.errSchemaPath + '/' + $keyword; + var $breakOnError = !it.opts.allErrors; + var $errorKeyword; + var $data = 'data' + ($dataLvl || ''); + var $valid = 'valid' + $lvl; + if (it.schema === false) { + if (it.isTop) { + $breakOnError = true; + } else { + out += ' var ' + ($valid) + ' = false; '; + } + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ($errorKeyword || 'false schema') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} '; + if (it.opts.messages !== false) { + out += ' , message: \'boolean schema is false\' '; + } + if (it.opts.verbose) { + out += ' , schema: false , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + } else { + if (it.isTop) { + if ($async) { + out += ' return data; '; + } else { + out += ' validate.errors = null; return true; '; + } + } else { + out += ' var ' + ($valid) + ' = true; '; + } + } + if (it.isTop) { + out += ' }; return validate; '; + } + return out; + } + if (it.isTop) { + var $top = it.isTop, + $lvl = it.level = 0, + $dataLvl = it.dataLevel = 0, + $data = 'data'; + it.rootId = it.resolve.fullPath(it.self._getId(it.root.schema)); + it.baseId = it.baseId || it.rootId; + delete it.isTop; + it.dataPathArr = [undefined]; + if (it.schema.default !== undefined && it.opts.useDefaults && it.opts.strictDefaults) { + var $defaultMsg = 'default is ignored in the schema root'; + if (it.opts.strictDefaults === 'log') it.logger.warn($defaultMsg); + else throw new Error($defaultMsg); + } + out += ' var vErrors = null; '; + out += ' var errors = 0; '; + out += ' if (rootData === undefined) rootData = data; '; + } else { + var $lvl = it.level, + $dataLvl = it.dataLevel, + $data = 'data' + ($dataLvl || ''); + if ($id) it.baseId = it.resolve.url(it.baseId, $id); + if ($async && !it.async) throw new Error('async schema in sync schema'); + out += ' var errs_' + ($lvl) + ' = errors;'; + } + var $valid = 'valid' + $lvl, + $breakOnError = !it.opts.allErrors, + $closingBraces1 = '', + $closingBraces2 = ''; + var $errorKeyword; + var $typeSchema = it.schema.type, + $typeIsArray = Array.isArray($typeSchema); + if ($typeSchema && it.opts.nullable && it.schema.nullable === true) { + if ($typeIsArray) { + if ($typeSchema.indexOf('null') == -1) $typeSchema = $typeSchema.concat('null'); + } else if ($typeSchema != 'null') { + $typeSchema = [$typeSchema, 'null']; + $typeIsArray = true; + } + } + if ($typeIsArray && $typeSchema.length == 1) { + $typeSchema = $typeSchema[0]; + $typeIsArray = false; + } + if (it.schema.$ref && $refKeywords) { + if (it.opts.extendRefs == 'fail') { + throw new Error('$ref: validation keywords used in schema at path "' + it.errSchemaPath + '" (see option extendRefs)'); + } else if (it.opts.extendRefs !== true) { + $refKeywords = false; + it.logger.warn('$ref: keywords ignored in schema at path "' + it.errSchemaPath + '"'); + } + } + if (it.schema.$comment && it.opts.$comment) { + out += ' ' + (it.RULES.all.$comment.code(it, '$comment')); + } + if ($typeSchema) { + if (it.opts.coerceTypes) { + var $coerceToTypes = it.util.coerceToTypes(it.opts.coerceTypes, $typeSchema); + } + var $rulesGroup = it.RULES.types[$typeSchema]; + if ($coerceToTypes || $typeIsArray || $rulesGroup === true || ($rulesGroup && !$shouldUseGroup($rulesGroup))) { + var $schemaPath = it.schemaPath + '.type', + $errSchemaPath = it.errSchemaPath + '/type'; + var $schemaPath = it.schemaPath + '.type', + $errSchemaPath = it.errSchemaPath + '/type', + $method = $typeIsArray ? 'checkDataTypes' : 'checkDataType'; + out += ' if (' + (it.util[$method]($typeSchema, $data, true)) + ') { '; + if ($coerceToTypes) { + var $dataType = 'dataType' + $lvl, + $coerced = 'coerced' + $lvl; + out += ' var ' + ($dataType) + ' = typeof ' + ($data) + '; '; + if (it.opts.coerceTypes == 'array') { + out += ' if (' + ($dataType) + ' == \'object\' && Array.isArray(' + ($data) + ')) ' + ($dataType) + ' = \'array\'; '; + } + out += ' var ' + ($coerced) + ' = undefined; '; + var $bracesCoercion = ''; + var arr1 = $coerceToTypes; + if (arr1) { + var $type, $i = -1, + l1 = arr1.length - 1; + while ($i < l1) { + $type = arr1[$i += 1]; + if ($i) { + out += ' if (' + ($coerced) + ' === undefined) { '; + $bracesCoercion += '}'; + } + if (it.opts.coerceTypes == 'array' && $type != 'array') { + out += ' if (' + ($dataType) + ' == \'array\' && ' + ($data) + '.length == 1) { ' + ($coerced) + ' = ' + ($data) + ' = ' + ($data) + '[0]; ' + ($dataType) + ' = typeof ' + ($data) + '; } '; + } + if ($type == 'string') { + out += ' if (' + ($dataType) + ' == \'number\' || ' + ($dataType) + ' == \'boolean\') ' + ($coerced) + ' = \'\' + ' + ($data) + '; else if (' + ($data) + ' === null) ' + ($coerced) + ' = \'\'; '; + } else if ($type == 'number' || $type == 'integer') { + out += ' if (' + ($dataType) + ' == \'boolean\' || ' + ($data) + ' === null || (' + ($dataType) + ' == \'string\' && ' + ($data) + ' && ' + ($data) + ' == +' + ($data) + ' '; + if ($type == 'integer') { + out += ' && !(' + ($data) + ' % 1)'; + } + out += ')) ' + ($coerced) + ' = +' + ($data) + '; '; + } else if ($type == 'boolean') { + out += ' if (' + ($data) + ' === \'false\' || ' + ($data) + ' === 0 || ' + ($data) + ' === null) ' + ($coerced) + ' = false; else if (' + ($data) + ' === \'true\' || ' + ($data) + ' === 1) ' + ($coerced) + ' = true; '; + } else if ($type == 'null') { + out += ' if (' + ($data) + ' === \'\' || ' + ($data) + ' === 0 || ' + ($data) + ' === false) ' + ($coerced) + ' = null; '; + } else if (it.opts.coerceTypes == 'array' && $type == 'array') { + out += ' if (' + ($dataType) + ' == \'string\' || ' + ($dataType) + ' == \'number\' || ' + ($dataType) + ' == \'boolean\' || ' + ($data) + ' == null) ' + ($coerced) + ' = [' + ($data) + ']; '; + } + } + } + out += ' ' + ($bracesCoercion) + ' if (' + ($coerced) + ' === undefined) { '; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ($errorKeyword || 'type') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { type: \''; + if ($typeIsArray) { + out += '' + ($typeSchema.join(",")); + } else { + out += '' + ($typeSchema); + } + out += '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should be '; + if ($typeIsArray) { + out += '' + ($typeSchema.join(",")); + } else { + out += '' + ($typeSchema); + } + out += '\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } else { '; + var $parentData = $dataLvl ? 'data' + (($dataLvl - 1) || '') : 'parentData', + $parentDataProperty = $dataLvl ? it.dataPathArr[$dataLvl] : 'parentDataProperty'; + out += ' ' + ($data) + ' = ' + ($coerced) + '; '; + if (!$dataLvl) { + out += 'if (' + ($parentData) + ' !== undefined)'; + } + out += ' ' + ($parentData) + '[' + ($parentDataProperty) + '] = ' + ($coerced) + '; } '; + } else { + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ($errorKeyword || 'type') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { type: \''; + if ($typeIsArray) { + out += '' + ($typeSchema.join(",")); + } else { + out += '' + ($typeSchema); + } + out += '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should be '; + if ($typeIsArray) { + out += '' + ($typeSchema.join(",")); + } else { + out += '' + ($typeSchema); + } + out += '\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + } + out += ' } '; + } + } + if (it.schema.$ref && !$refKeywords) { + out += ' ' + (it.RULES.all.$ref.code(it, '$ref')) + ' '; + if ($breakOnError) { + out += ' } if (errors === '; + if ($top) { + out += '0'; + } else { + out += 'errs_' + ($lvl); + } + out += ') { '; + $closingBraces2 += '}'; + } + } else { + var arr2 = it.RULES; + if (arr2) { + var $rulesGroup, i2 = -1, + l2 = arr2.length - 1; + while (i2 < l2) { + $rulesGroup = arr2[i2 += 1]; + if ($shouldUseGroup($rulesGroup)) { + if ($rulesGroup.type) { + out += ' if (' + (it.util.checkDataType($rulesGroup.type, $data)) + ') { '; + } + if (it.opts.useDefaults) { + if ($rulesGroup.type == 'object' && it.schema.properties) { + var $schema = it.schema.properties, + $schemaKeys = Object.keys($schema); + var arr3 = $schemaKeys; + if (arr3) { + var $propertyKey, i3 = -1, + l3 = arr3.length - 1; + while (i3 < l3) { + $propertyKey = arr3[i3 += 1]; + var $sch = $schema[$propertyKey]; + if ($sch.default !== undefined) { + var $passData = $data + it.util.getProperty($propertyKey); + if (it.compositeRule) { + if (it.opts.strictDefaults) { + var $defaultMsg = 'default is ignored for: ' + $passData; + if (it.opts.strictDefaults === 'log') it.logger.warn($defaultMsg); + else throw new Error($defaultMsg); + } + } else { + out += ' if (' + ($passData) + ' === undefined '; + if (it.opts.useDefaults == 'empty') { + out += ' || ' + ($passData) + ' === null || ' + ($passData) + ' === \'\' '; + } + out += ' ) ' + ($passData) + ' = '; + if (it.opts.useDefaults == 'shared') { + out += ' ' + (it.useDefault($sch.default)) + ' '; + } else { + out += ' ' + (JSON.stringify($sch.default)) + ' '; + } + out += '; '; + } + } + } + } + } else if ($rulesGroup.type == 'array' && Array.isArray(it.schema.items)) { + var arr4 = it.schema.items; + if (arr4) { + var $sch, $i = -1, + l4 = arr4.length - 1; + while ($i < l4) { + $sch = arr4[$i += 1]; + if ($sch.default !== undefined) { + var $passData = $data + '[' + $i + ']'; + if (it.compositeRule) { + if (it.opts.strictDefaults) { + var $defaultMsg = 'default is ignored for: ' + $passData; + if (it.opts.strictDefaults === 'log') it.logger.warn($defaultMsg); + else throw new Error($defaultMsg); + } + } else { + out += ' if (' + ($passData) + ' === undefined '; + if (it.opts.useDefaults == 'empty') { + out += ' || ' + ($passData) + ' === null || ' + ($passData) + ' === \'\' '; + } + out += ' ) ' + ($passData) + ' = '; + if (it.opts.useDefaults == 'shared') { + out += ' ' + (it.useDefault($sch.default)) + ' '; + } else { + out += ' ' + (JSON.stringify($sch.default)) + ' '; + } + out += '; '; + } + } + } + } + } + } + var arr5 = $rulesGroup.rules; + if (arr5) { + var $rule, i5 = -1, + l5 = arr5.length - 1; + while (i5 < l5) { + $rule = arr5[i5 += 1]; + if ($shouldUseRule($rule)) { + var $code = $rule.code(it, $rule.keyword, $rulesGroup.type); + if ($code) { + out += ' ' + ($code) + ' '; + if ($breakOnError) { + $closingBraces1 += '}'; + } + } + } + } + } + if ($breakOnError) { + out += ' ' + ($closingBraces1) + ' '; + $closingBraces1 = ''; + } + if ($rulesGroup.type) { + out += ' } '; + if ($typeSchema && $typeSchema === $rulesGroup.type && !$coerceToTypes) { + out += ' else { '; + var $schemaPath = it.schemaPath + '.type', + $errSchemaPath = it.errSchemaPath + '/type'; + var $$outStack = $$outStack || []; + $$outStack.push(out); + out = ''; /* istanbul ignore else */ + if (it.createErrors !== false) { + out += ' { keyword: \'' + ($errorKeyword || 'type') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: { type: \''; + if ($typeIsArray) { + out += '' + ($typeSchema.join(",")); + } else { + out += '' + ($typeSchema); + } + out += '\' } '; + if (it.opts.messages !== false) { + out += ' , message: \'should be '; + if ($typeIsArray) { + out += '' + ($typeSchema.join(",")); + } else { + out += '' + ($typeSchema); + } + out += '\' '; + } + if (it.opts.verbose) { + out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' '; + } + out += ' } '; + } else { + out += ' {} '; + } + var __err = out; + out = $$outStack.pop(); + if (!it.compositeRule && $breakOnError) { + /* istanbul ignore if */ + if (it.async) { + out += ' throw new ValidationError([' + (__err) + ']); '; + } else { + out += ' validate.errors = [' + (__err) + ']; return false; '; + } + } else { + out += ' var err = ' + (__err) + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; + } + out += ' } '; + } + } + if ($breakOnError) { + out += ' if (errors === '; + if ($top) { + out += '0'; + } else { + out += 'errs_' + ($lvl); + } + out += ') { '; + $closingBraces2 += '}'; + } + } + } + } + } + if ($breakOnError) { + out += ' ' + ($closingBraces2) + ' '; + } + if ($top) { + if ($async) { + out += ' if (errors === 0) return data; '; + out += ' else throw new ValidationError(vErrors); '; + } else { + out += ' validate.errors = vErrors; '; + out += ' return errors === 0; '; + } + out += ' }; return validate;'; + } else { + out += ' var ' + ($valid) + ' = errors === errs_' + ($lvl) + ';'; + } + out = it.util.cleanUpCode(out); + if ($top) { + out = it.util.finalCleanUpCode(out, $async); + } + + function $shouldUseGroup($rulesGroup) { + var rules = $rulesGroup.rules; + for (var i = 0; i < rules.length; i++) + if ($shouldUseRule(rules[i])) return true; + } + + function $shouldUseRule($rule) { + return it.schema[$rule.keyword] !== undefined || ($rule.implements && $ruleImplementsSomeKeyword($rule)); + } + + function $ruleImplementsSomeKeyword($rule) { + var impl = $rule.implements; + for (var i = 0; i < impl.length; i++) + if (it.schema[impl[i]] !== undefined) return true; + } + return out; +} + + +/***/ }), + +/***/ 972: +/***/ (function(module, __unusedexports, __webpack_require__) { + +/*! + * mime-db + * Copyright(c) 2014 Jonathan Ong + * MIT Licensed + */ + +/** + * Module exports. + */ + +module.exports = __webpack_require__(512) + + +/***/ }), + +/***/ 982: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2017 Joyent, Inc. + +module.exports = { + read: read, + write: write +}; + +var assert = __webpack_require__(477); +var Buffer = __webpack_require__(215).Buffer; +var Key = __webpack_require__(852); +var PrivateKey = __webpack_require__(502); +var utils = __webpack_require__(270); +var SSHBuffer = __webpack_require__(940); +var Dhe = __webpack_require__(290); + +var supportedAlgos = { + 'rsa-sha1' : 5, + 'rsa-sha256' : 8, + 'rsa-sha512' : 10, + 'ecdsa-p256-sha256' : 13, + 'ecdsa-p384-sha384' : 14 + /* + * ed25519 is hypothetically supported with id 15 + * but the common tools available don't appear to be + * capable of generating/using ed25519 keys + */ +}; + +var supportedAlgosById = {}; +Object.keys(supportedAlgos).forEach(function (k) { + supportedAlgosById[supportedAlgos[k]] = k.toUpperCase(); +}); + +function read(buf, options) { + if (typeof (buf) !== 'string') { + assert.buffer(buf, 'buf'); + buf = buf.toString('ascii'); + } + var lines = buf.split('\n'); + if (lines[0].match(/^Private-key-format\: v1/)) { + var algElems = lines[1].split(' '); + var algoNum = parseInt(algElems[1], 10); + var algoName = algElems[2]; + if (!supportedAlgosById[algoNum]) + throw (new Error('Unsupported algorithm: ' + algoName)); + return (readDNSSECPrivateKey(algoNum, lines.slice(2))); + } + + // skip any comment-lines + var line = 0; + /* JSSTYLED */ + while (lines[line].match(/^\;/)) + line++; + // we should now have *one single* line left with our KEY on it. + if ((lines[line].match(/\. IN KEY /) || + lines[line].match(/\. IN DNSKEY /)) && lines[line+1].length === 0) { + return (readRFC3110(lines[line])); + } + throw (new Error('Cannot parse dnssec key')); +} + +function readRFC3110(keyString) { + var elems = keyString.split(' '); + //unused var flags = parseInt(elems[3], 10); + //unused var protocol = parseInt(elems[4], 10); + var algorithm = parseInt(elems[5], 10); + if (!supportedAlgosById[algorithm]) + throw (new Error('Unsupported algorithm: ' + algorithm)); + var base64key = elems.slice(6, elems.length).join(); + var keyBuffer = Buffer.from(base64key, 'base64'); + if (supportedAlgosById[algorithm].match(/^RSA-/)) { + // join the rest of the body into a single base64-blob + var publicExponentLen = keyBuffer.readUInt8(0); + if (publicExponentLen != 3 && publicExponentLen != 1) + throw (new Error('Cannot parse dnssec key: ' + + 'unsupported exponent length')); + + var publicExponent = keyBuffer.slice(1, publicExponentLen+1); + publicExponent = utils.mpNormalize(publicExponent); + var modulus = keyBuffer.slice(1+publicExponentLen); + modulus = utils.mpNormalize(modulus); + // now, make the key + var rsaKey = { + type: 'rsa', + parts: [] + }; + rsaKey.parts.push({ name: 'e', data: publicExponent}); + rsaKey.parts.push({ name: 'n', data: modulus}); + return (new Key(rsaKey)); + } + if (supportedAlgosById[algorithm] === 'ECDSA-P384-SHA384' || + supportedAlgosById[algorithm] === 'ECDSA-P256-SHA256') { + var curve = 'nistp384'; + var size = 384; + if (supportedAlgosById[algorithm].match(/^ECDSA-P256-SHA256/)) { + curve = 'nistp256'; + size = 256; + } + + var ecdsaKey = { + type: 'ecdsa', + curve: curve, + size: size, + parts: [ + {name: 'curve', data: Buffer.from(curve) }, + {name: 'Q', data: utils.ecNormalize(keyBuffer) } + ] + }; + return (new Key(ecdsaKey)); + } + throw (new Error('Unsupported algorithm: ' + + supportedAlgosById[algorithm])); +} + +function elementToBuf(e) { + return (Buffer.from(e.split(' ')[1], 'base64')); +} + +function readDNSSECRSAPrivateKey(elements) { + var rsaParams = {}; + elements.forEach(function (element) { + if (element.split(' ')[0] === 'Modulus:') + rsaParams['n'] = elementToBuf(element); + else if (element.split(' ')[0] === 'PublicExponent:') + rsaParams['e'] = elementToBuf(element); + else if (element.split(' ')[0] === 'PrivateExponent:') + rsaParams['d'] = elementToBuf(element); + else if (element.split(' ')[0] === 'Prime1:') + rsaParams['p'] = elementToBuf(element); + else if (element.split(' ')[0] === 'Prime2:') + rsaParams['q'] = elementToBuf(element); + else if (element.split(' ')[0] === 'Exponent1:') + rsaParams['dmodp'] = elementToBuf(element); + else if (element.split(' ')[0] === 'Exponent2:') + rsaParams['dmodq'] = elementToBuf(element); + else if (element.split(' ')[0] === 'Coefficient:') + rsaParams['iqmp'] = elementToBuf(element); + }); + // now, make the key + var key = { + type: 'rsa', + parts: [ + { name: 'e', data: utils.mpNormalize(rsaParams['e'])}, + { name: 'n', data: utils.mpNormalize(rsaParams['n'])}, + { name: 'd', data: utils.mpNormalize(rsaParams['d'])}, + { name: 'p', data: utils.mpNormalize(rsaParams['p'])}, + { name: 'q', data: utils.mpNormalize(rsaParams['q'])}, + { name: 'dmodp', + data: utils.mpNormalize(rsaParams['dmodp'])}, + { name: 'dmodq', + data: utils.mpNormalize(rsaParams['dmodq'])}, + { name: 'iqmp', + data: utils.mpNormalize(rsaParams['iqmp'])} + ] + }; + return (new PrivateKey(key)); +} + +function readDNSSECPrivateKey(alg, elements) { + if (supportedAlgosById[alg].match(/^RSA-/)) { + return (readDNSSECRSAPrivateKey(elements)); + } + if (supportedAlgosById[alg] === 'ECDSA-P384-SHA384' || + supportedAlgosById[alg] === 'ECDSA-P256-SHA256') { + var d = Buffer.from(elements[0].split(' ')[1], 'base64'); + var curve = 'nistp384'; + var size = 384; + if (supportedAlgosById[alg] === 'ECDSA-P256-SHA256') { + curve = 'nistp256'; + size = 256; + } + // DNSSEC generates the public-key on the fly (go calculate it) + var publicKey = utils.publicFromPrivateECDSA(curve, d); + var Q = publicKey.part['Q'].data; + var ecdsaKey = { + type: 'ecdsa', + curve: curve, + size: size, + parts: [ + {name: 'curve', data: Buffer.from(curve) }, + {name: 'd', data: d }, + {name: 'Q', data: Q } + ] + }; + return (new PrivateKey(ecdsaKey)); + } + throw (new Error('Unsupported algorithm: ' + supportedAlgosById[alg])); +} + +function dnssecTimestamp(date) { + var year = date.getFullYear() + ''; //stringify + var month = (date.getMonth() + 1); + var timestampStr = year + month + date.getUTCDate(); + timestampStr += '' + date.getUTCHours() + date.getUTCMinutes(); + timestampStr += date.getUTCSeconds(); + return (timestampStr); +} + +function rsaAlgFromOptions(opts) { + if (!opts || !opts.hashAlgo || opts.hashAlgo === 'sha1') + return ('5 (RSASHA1)'); + else if (opts.hashAlgo === 'sha256') + return ('8 (RSASHA256)'); + else if (opts.hashAlgo === 'sha512') + return ('10 (RSASHA512)'); + else + throw (new Error('Unknown or unsupported hash: ' + + opts.hashAlgo)); +} + +function writeRSA(key, options) { + // if we're missing parts, add them. + if (!key.part.dmodp || !key.part.dmodq) { + utils.addRSAMissing(key); + } + + var out = ''; + out += 'Private-key-format: v1.3\n'; + out += 'Algorithm: ' + rsaAlgFromOptions(options) + '\n'; + var n = utils.mpDenormalize(key.part['n'].data); + out += 'Modulus: ' + n.toString('base64') + '\n'; + var e = utils.mpDenormalize(key.part['e'].data); + out += 'PublicExponent: ' + e.toString('base64') + '\n'; + var d = utils.mpDenormalize(key.part['d'].data); + out += 'PrivateExponent: ' + d.toString('base64') + '\n'; + var p = utils.mpDenormalize(key.part['p'].data); + out += 'Prime1: ' + p.toString('base64') + '\n'; + var q = utils.mpDenormalize(key.part['q'].data); + out += 'Prime2: ' + q.toString('base64') + '\n'; + var dmodp = utils.mpDenormalize(key.part['dmodp'].data); + out += 'Exponent1: ' + dmodp.toString('base64') + '\n'; + var dmodq = utils.mpDenormalize(key.part['dmodq'].data); + out += 'Exponent2: ' + dmodq.toString('base64') + '\n'; + var iqmp = utils.mpDenormalize(key.part['iqmp'].data); + out += 'Coefficient: ' + iqmp.toString('base64') + '\n'; + // Assume that we're valid as-of now + var timestamp = new Date(); + out += 'Created: ' + dnssecTimestamp(timestamp) + '\n'; + out += 'Publish: ' + dnssecTimestamp(timestamp) + '\n'; + out += 'Activate: ' + dnssecTimestamp(timestamp) + '\n'; + return (Buffer.from(out, 'ascii')); +} + +function writeECDSA(key, options) { + var out = ''; + out += 'Private-key-format: v1.3\n'; + + if (key.curve === 'nistp256') { + out += 'Algorithm: 13 (ECDSAP256SHA256)\n'; + } else if (key.curve === 'nistp384') { + out += 'Algorithm: 14 (ECDSAP384SHA384)\n'; + } else { + throw (new Error('Unsupported curve')); + } + var base64Key = key.part['d'].data.toString('base64'); + out += 'PrivateKey: ' + base64Key + '\n'; + + // Assume that we're valid as-of now + var timestamp = new Date(); + out += 'Created: ' + dnssecTimestamp(timestamp) + '\n'; + out += 'Publish: ' + dnssecTimestamp(timestamp) + '\n'; + out += 'Activate: ' + dnssecTimestamp(timestamp) + '\n'; + + return (Buffer.from(out, 'ascii')); +} + +function write(key, options) { + if (PrivateKey.isPrivateKey(key)) { + if (key.type === 'rsa') { + return (writeRSA(key, options)); + } else if (key.type === 'ecdsa') { + return (writeECDSA(key, options)); + } else { + throw (new Error('Unsupported algorithm: ' + key.type)); + } + } else if (Key.isKey(key)) { + /* + * RFC3110 requires a keyname, and a keytype, which we + * don't really have a mechanism for specifying such + * additional metadata. + */ + throw (new Error('Format "dnssec" only supports ' + + 'writing private keys')); + } else { + throw (new Error('key is not a Key or PrivateKey')); + } +} + + +/***/ }), + +/***/ 985: +/***/ (function(module) { + +module.exports = function(size) { + return new LruCache(size) +} + +function LruCache(size) { + this.capacity = size | 0 + this.map = Object.create(null) + this.list = new DoublyLinkedList() +} + +LruCache.prototype.get = function(key) { + var node = this.map[key] + if (node == null) return undefined + this.used(node) + return node.val +} + +LruCache.prototype.set = function(key, val) { + var node = this.map[key] + if (node != null) { + node.val = val + } else { + if (!this.capacity) this.prune() + if (!this.capacity) return false + node = new DoublyLinkedNode(key, val) + this.map[key] = node + this.capacity-- + } + this.used(node) + return true +} + +LruCache.prototype.used = function(node) { + this.list.moveToFront(node) +} + +LruCache.prototype.prune = function() { + var node = this.list.pop() + if (node != null) { + delete this.map[node.key] + this.capacity++ + } +} + + +function DoublyLinkedList() { + this.firstNode = null + this.lastNode = null +} + +DoublyLinkedList.prototype.moveToFront = function(node) { + if (this.firstNode == node) return + + this.remove(node) + + if (this.firstNode == null) { + this.firstNode = node + this.lastNode = node + node.prev = null + node.next = null + } else { + node.prev = null + node.next = this.firstNode + node.next.prev = node + this.firstNode = node + } +} + +DoublyLinkedList.prototype.pop = function() { + var lastNode = this.lastNode + if (lastNode != null) { + this.remove(lastNode) + } + return lastNode +} + +DoublyLinkedList.prototype.remove = function(node) { + if (this.firstNode == node) { + this.firstNode = node.next + } else if (node.prev != null) { + node.prev.next = node.next + } + if (this.lastNode == node) { + this.lastNode = node.prev + } else if (node.next != null) { + node.next.prev = node.prev + } +} + + +function DoublyLinkedNode(key, val) { + this.key = key + this.val = val + this.prev = null + this.next = null +} + + +/***/ }), + +/***/ 993: +/***/ (function(module) { + +module.exports = {"$id":"cache.json#","$schema":"http://json-schema.org/draft-06/schema#","properties":{"beforeRequest":{"oneOf":[{"type":"null"},{"$ref":"beforeRequest.json#"}]},"afterRequest":{"oneOf":[{"type":"null"},{"$ref":"afterRequest.json#"}]},"comment":{"type":"string"}}}; + +/***/ }), + +/***/ 998: +/***/ (function(module, __unusedexports, __webpack_require__) { + +// Copyright 2011 Mark Cavage All rights reserved. + +var assert = __webpack_require__(357); +var Buffer = __webpack_require__(215).Buffer; +var ASN1 = __webpack_require__(362); +var errors = __webpack_require__(584); + + +// --- Globals + +var newInvalidAsn1Error = errors.newInvalidAsn1Error; + +var DEFAULT_OPTS = { + size: 1024, + growthFactor: 8 +}; + + +// --- Helpers + +function merge(from, to) { + assert.ok(from); + assert.equal(typeof (from), 'object'); + assert.ok(to); + assert.equal(typeof (to), 'object'); + + var keys = Object.getOwnPropertyNames(from); + keys.forEach(function (key) { + if (to[key]) + return; + + var value = Object.getOwnPropertyDescriptor(from, key); + Object.defineProperty(to, key, value); + }); + + return to; +} + + + +// --- API + +function Writer(options) { + options = merge(DEFAULT_OPTS, options || {}); + + this._buf = Buffer.alloc(options.size || 1024); + this._size = this._buf.length; + this._offset = 0; + this._options = options; + + // A list of offsets in the buffer where we need to insert + // sequence tag/len pairs. + this._seq = []; +} + +Object.defineProperty(Writer.prototype, 'buffer', { + get: function () { + if (this._seq.length) + throw newInvalidAsn1Error(this._seq.length + ' unended sequence(s)'); + + return (this._buf.slice(0, this._offset)); + } +}); + +Writer.prototype.writeByte = function (b) { + if (typeof (b) !== 'number') + throw new TypeError('argument must be a Number'); + + this._ensure(1); + this._buf[this._offset++] = b; +}; + + +Writer.prototype.writeInt = function (i, tag) { + if (typeof (i) !== 'number') + throw new TypeError('argument must be a Number'); + if (typeof (tag) !== 'number') + tag = ASN1.Integer; + + var sz = 4; + + while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000 >> 0)) && + (sz > 1)) { + sz--; + i <<= 8; + } + + if (sz > 4) + throw newInvalidAsn1Error('BER ints cannot be > 0xffffffff'); + + this._ensure(2 + sz); + this._buf[this._offset++] = tag; + this._buf[this._offset++] = sz; + + while (sz-- > 0) { + this._buf[this._offset++] = ((i & 0xff000000) >>> 24); + i <<= 8; + } + +}; + + +Writer.prototype.writeNull = function () { + this.writeByte(ASN1.Null); + this.writeByte(0x00); +}; + + +Writer.prototype.writeEnumeration = function (i, tag) { + if (typeof (i) !== 'number') + throw new TypeError('argument must be a Number'); + if (typeof (tag) !== 'number') + tag = ASN1.Enumeration; + + return this.writeInt(i, tag); +}; + + +Writer.prototype.writeBoolean = function (b, tag) { + if (typeof (b) !== 'boolean') + throw new TypeError('argument must be a Boolean'); + if (typeof (tag) !== 'number') + tag = ASN1.Boolean; + + this._ensure(3); + this._buf[this._offset++] = tag; + this._buf[this._offset++] = 0x01; + this._buf[this._offset++] = b ? 0xff : 0x00; +}; + + +Writer.prototype.writeString = function (s, tag) { + if (typeof (s) !== 'string') + throw new TypeError('argument must be a string (was: ' + typeof (s) + ')'); + if (typeof (tag) !== 'number') + tag = ASN1.OctetString; + + var len = Buffer.byteLength(s); + this.writeByte(tag); + this.writeLength(len); + if (len) { + this._ensure(len); + this._buf.write(s, this._offset); + this._offset += len; + } +}; + + +Writer.prototype.writeBuffer = function (buf, tag) { + if (typeof (tag) !== 'number') + throw new TypeError('tag must be a number'); + if (!Buffer.isBuffer(buf)) + throw new TypeError('argument must be a buffer'); + + this.writeByte(tag); + this.writeLength(buf.length); + this._ensure(buf.length); + buf.copy(this._buf, this._offset, 0, buf.length); + this._offset += buf.length; +}; + + +Writer.prototype.writeStringArray = function (strings) { + if ((!strings instanceof Array)) + throw new TypeError('argument must be an Array[String]'); + + var self = this; + strings.forEach(function (s) { + self.writeString(s); + }); +}; + +// This is really to solve DER cases, but whatever for now +Writer.prototype.writeOID = function (s, tag) { + if (typeof (s) !== 'string') + throw new TypeError('argument must be a string'); + if (typeof (tag) !== 'number') + tag = ASN1.OID; + + if (!/^([0-9]+\.){3,}[0-9]+$/.test(s)) + throw new Error('argument is not a valid OID string'); + + function encodeOctet(bytes, octet) { + if (octet < 128) { + bytes.push(octet); + } else if (octet < 16384) { + bytes.push((octet >>> 7) | 0x80); + bytes.push(octet & 0x7F); + } else if (octet < 2097152) { + bytes.push((octet >>> 14) | 0x80); + bytes.push(((octet >>> 7) | 0x80) & 0xFF); + bytes.push(octet & 0x7F); + } else if (octet < 268435456) { + bytes.push((octet >>> 21) | 0x80); + bytes.push(((octet >>> 14) | 0x80) & 0xFF); + bytes.push(((octet >>> 7) | 0x80) & 0xFF); + bytes.push(octet & 0x7F); + } else { + bytes.push(((octet >>> 28) | 0x80) & 0xFF); + bytes.push(((octet >>> 21) | 0x80) & 0xFF); + bytes.push(((octet >>> 14) | 0x80) & 0xFF); + bytes.push(((octet >>> 7) | 0x80) & 0xFF); + bytes.push(octet & 0x7F); + } + } + + var tmp = s.split('.'); + var bytes = []; + bytes.push(parseInt(tmp[0], 10) * 40 + parseInt(tmp[1], 10)); + tmp.slice(2).forEach(function (b) { + encodeOctet(bytes, parseInt(b, 10)); + }); + + var self = this; + this._ensure(2 + bytes.length); + this.writeByte(tag); + this.writeLength(bytes.length); + bytes.forEach(function (b) { + self.writeByte(b); + }); +}; + + +Writer.prototype.writeLength = function (len) { + if (typeof (len) !== 'number') + throw new TypeError('argument must be a Number'); + + this._ensure(4); + + if (len <= 0x7f) { + this._buf[this._offset++] = len; + } else if (len <= 0xff) { + this._buf[this._offset++] = 0x81; + this._buf[this._offset++] = len; + } else if (len <= 0xffff) { + this._buf[this._offset++] = 0x82; + this._buf[this._offset++] = len >> 8; + this._buf[this._offset++] = len; + } else if (len <= 0xffffff) { + this._buf[this._offset++] = 0x83; + this._buf[this._offset++] = len >> 16; + this._buf[this._offset++] = len >> 8; + this._buf[this._offset++] = len; + } else { + throw newInvalidAsn1Error('Length too long (> 4 bytes)'); + } +}; + +Writer.prototype.startSequence = function (tag) { + if (typeof (tag) !== 'number') + tag = ASN1.Sequence | ASN1.Constructor; + + this.writeByte(tag); + this._seq.push(this._offset); + this._ensure(3); + this._offset += 3; +}; + + +Writer.prototype.endSequence = function () { + var seq = this._seq.pop(); + var start = seq + 3; + var len = this._offset - start; + + if (len <= 0x7f) { + this._shift(start, len, -2); + this._buf[seq] = len; + } else if (len <= 0xff) { + this._shift(start, len, -1); + this._buf[seq] = 0x81; + this._buf[seq + 1] = len; + } else if (len <= 0xffff) { + this._buf[seq] = 0x82; + this._buf[seq + 1] = len >> 8; + this._buf[seq + 2] = len; + } else if (len <= 0xffffff) { + this._shift(start, len, 1); + this._buf[seq] = 0x83; + this._buf[seq + 1] = len >> 16; + this._buf[seq + 2] = len >> 8; + this._buf[seq + 3] = len; + } else { + throw newInvalidAsn1Error('Sequence too long'); + } +}; + + +Writer.prototype._shift = function (start, len, shift) { + assert.ok(start !== undefined); + assert.ok(len !== undefined); + assert.ok(shift); + + this._buf.copy(this._buf, start + shift, start, start + len); + this._offset += shift; +}; + +Writer.prototype._ensure = function (len) { + assert.ok(len); + + if (this._size - this._offset < len) { + var sz = this._size * this._options.growthFactor; + if (sz - this._offset < len) + sz += len; + + var buf = Buffer.alloc(sz); + + this._buf.copy(buf, 0, 0, this._offset); + this._buf = buf; + this._size = sz; + } +}; + + + +// --- Exported API + +module.exports = Writer; + + +/***/ }) + +/******/ }); \ No newline at end of file diff --git a/.github/actions/send-tweet/index.js b/.github/actions/send-tweet/index.js new file mode 100644 index 0000000000..aa6140ba8b --- /dev/null +++ b/.github/actions/send-tweet/index.js @@ -0,0 +1,37 @@ +/*! + * Copyright 2020 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const core = require('@actions/core'); +const Twitter = require('twitter'); + +function sendTweet() { + const twitter = new Twitter({ + consumer_key: core.getInput('consumer-key'), + consumer_secret: core.getInput('consumer-secret'), + access_token_key: core.getInput('access-token'), + access_token_secret: core.getInput('access-token-secret') + }); + + return twitter.post('/statuses/update', {status: core.getInput('status')}) + .then(() => { + return; + }) + .catch((err) => { + core.setFailed(err.message); + }); +} + +sendTweet(); diff --git a/.github/actions/send-tweet/package-lock.json b/.github/actions/send-tweet/package-lock.json new file mode 100644 index 0000000000..e59bab6c1d --- /dev/null +++ b/.github/actions/send-tweet/package-lock.json @@ -0,0 +1,364 @@ +{ + "name": "send-tweet", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@actions/core": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.2.tgz", + "integrity": "sha512-IbCx7oefq+Gi6FWbSs2Fnw8VkEI6Y4gvjrYprY3RV//ksq/KPMlClOerJ4jRosyal6zkUIc8R9fS/cpRMlGClg==" + }, + "@zeit/ncc": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@zeit/ncc/-/ncc-0.21.1.tgz", + "integrity": "sha512-M9WzgquSOt2nsjRkYM9LRylBLmmlwNCwYbm3Up3PDEshfvdmIfqpFNSK8EJvR18NwZjGHE5z2avlDtYQx2JQnw==", + "dev": true + }, + "ajv": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", + "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "mime-db": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" + }, + "mime-types": { + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", + "requires": { + "mime-db": "1.43.0" + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "psl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", + "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "twitter": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/twitter/-/twitter-1.7.1.tgz", + "integrity": "sha1-B2I3jx3BwFDkj2ZqypBOJLGpYvQ=", + "requires": { + "deep-extend": "^0.5.0", + "request": "^2.72.0" + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + } + } +} diff --git a/.github/actions/send-tweet/package.json b/.github/actions/send-tweet/package.json new file mode 100644 index 0000000000..0f22b3fa1d --- /dev/null +++ b/.github/actions/send-tweet/package.json @@ -0,0 +1,23 @@ +{ + "name": "send-tweet", + "version": "1.0.0", + "description": "Send Tweets from GitHub Actions workflows.", + "main": "index.js", + "scripts": { + "pack": "ncc build" + }, + "keywords": [ + "Firebase", + "Release", + "Automation" + ], + "author": "Firebase (https://firebase.google.com/)", + "license": "Apache-2.0", + "dependencies": { + "@actions/core": "^1.2.2", + "twitter": "^1.7.1" + }, + "devDependencies": { + "@zeit/ncc": "^0.21.1" + } +} diff --git a/.github/resources/integ-service-account.json.gpg b/.github/resources/integ-service-account.json.gpg new file mode 100644 index 0000000000..fbfa531167 Binary files /dev/null and b/.github/resources/integ-service-account.json.gpg differ diff --git a/.github/scripts/generate_changelog.sh b/.github/scripts/generate_changelog.sh new file mode 100755 index 0000000000..3c97dca0cf --- /dev/null +++ b/.github/scripts/generate_changelog.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +set -e +set -u + +function printChangelog() { + local TITLE=$1 + shift + # Skip the sentinel value. + local ENTRIES=("${@:2}") + if [ ${#ENTRIES[@]} -ne 0 ]; then + echo "### ${TITLE}" + echo "" + for ((i = 0; i < ${#ENTRIES[@]}; i++)) + do + echo "* ${ENTRIES[$i]}" + done + echo "" + fi +} + +if [[ -z "${GITHUB_SHA}" ]]; then + GITHUB_SHA="HEAD" +fi + +LAST_TAG=`git describe --tags $(git rev-list --tags --max-count=1) 2> /dev/null` || true +if [[ -z "${LAST_TAG}" ]]; then + echo "[INFO] No tags found. Including all commits up to ${GITHUB_SHA}." + VERSION_RANGE="${GITHUB_SHA}" +else + echo "[INFO] Last release tag: ${LAST_TAG}." + COMMIT_SHA=`git show-ref -s ${LAST_TAG}` + echo "[INFO] Last release commit: ${COMMIT_SHA}." + VERSION_RANGE="${COMMIT_SHA}..${GITHUB_SHA}" + echo "[INFO] Including all commits in the range ${VERSION_RANGE}." +fi + +echo "" + +# Older versions of Bash (< 4.4) treat empty arrays as unbound variables, which triggers +# errors when referencing them. Therefore we initialize each of these arrays with an empty +# sentinel value, and later skip them. +CHANGES=("") +FIXES=("") +FEATS=("") +MISC=("") + +while read -r line +do + COMMIT_MSG=`echo ${line} | cut -d ' ' -f 2-` + if [[ $COMMIT_MSG =~ ^change(\(.*\))?: ]]; then + CHANGES+=("$COMMIT_MSG") + elif [[ $COMMIT_MSG =~ ^fix(\(.*\))?: ]]; then + FIXES+=("$COMMIT_MSG") + elif [[ $COMMIT_MSG =~ ^feat(\(.*\))?: ]]; then + FEATS+=("$COMMIT_MSG") + else + MISC+=("${COMMIT_MSG}") + fi +done < <(git log ${VERSION_RANGE} --oneline) + +printChangelog "Breaking Changes" "${CHANGES[@]}" +printChangelog "New Features" "${FEATS[@]}" +printChangelog "Bug Fixes" "${FIXES[@]}" +printChangelog "Miscellaneous" "${MISC[@]}" diff --git a/.github/scripts/publish_package.sh b/.github/scripts/publish_package.sh new file mode 100755 index 0000000000..42d40c199e --- /dev/null +++ b/.github/scripts/publish_package.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Copyright 2020 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e +set -u + +echo "//wombat-dressing-room.appspot.com/:_authToken=${NPM_AUTH_TOKEN}" >> .npmrc + +readonly UNPREFIXED_VERSION=`echo ${VERSION} | cut -c 2-` +npm publish ./dist/firebase-admin-${UNPREFIXED_VERSION}.tgz --registry https://wombat-dressing-room.appspot.com diff --git a/.github/scripts/publish_preflight_check.sh b/.github/scripts/publish_preflight_check.sh new file mode 100755 index 0000000000..fdba60bff8 --- /dev/null +++ b/.github/scripts/publish_preflight_check.sh @@ -0,0 +1,170 @@ +#!/bin/bash + +# Copyright 2020 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +###################################### Outputs ##################################### + +# 1. version: The version of this release including the 'v' prefix (e.g. v1.2.3). +# 2. changelog: Formatted changelog text for this release. + +#################################################################################### + +set -e +set -u + +function echo_info() { + local MESSAGE=$1 + echo "[INFO] ${MESSAGE}" +} + +function echo_warn() { + local MESSAGE=$1 + echo "[WARN] ${MESSAGE}" +} + +function terminate() { + echo "" + echo_warn "--------------------------------------------" + echo_warn "PREFLIGHT FAILED" + echo_warn "--------------------------------------------" + exit 1 +} + + +echo_info "Starting publish preflight check..." +echo_info "Git revision : ${GITHUB_SHA}" +echo_info "Workflow triggered by : ${GITHUB_ACTOR}" +echo_info "GitHub event : ${GITHUB_EVENT_NAME}" + + +echo_info "" +echo_info "--------------------------------------------" +echo_info "Extracting release version" +echo_info "--------------------------------------------" +echo_info "" + +echo_info "Loading version from: package.json" + +readonly VERSION_SCRIPT="const pkg = require('./package.json'); console.log(pkg.version);" +readonly RELEASE_VERSION=`node -e "${VERSION_SCRIPT}"` || true +if [[ -z "${RELEASE_VERSION}" ]]; then + echo_warn "Failed to extract release version from: package.json" + terminate +fi + +if [[ ! "${RELEASE_VERSION}" =~ ^([0-9]*)\.([0-9]*)\.([0-9]*)$ ]]; then + echo_warn "Malformed release version string: ${RELEASE_VERSION}. Exiting." + terminate +fi + +echo_info "Extracted release version: ${RELEASE_VERSION}" +echo "::set-output name=version::v${RELEASE_VERSION}" + + +echo_info "" +echo_info "--------------------------------------------" +echo_info "Check release artifacts" +echo_info "--------------------------------------------" +echo_info "" + +if [[ ! -d dist ]]; then + echo_warn "dist directory does not exist." + terminate +fi + +readonly RELEASE_ARTIFACT="dist/firebase-admin-${RELEASE_VERSION}.tgz" +if [[ -f "${RELEASE_ARTIFACT}" ]]; then + echo_info "Found release artifact: ${RELEASE_ARTIFACT}" +else + echo_warn "Release artifact ${RELEASE_ARTIFACT} not found." + terminate +fi + +readonly ARTIFACT_COUNT=`ls dist/ | wc -l` +if [[ $ARTIFACT_COUNT -ne 1 ]]; then + echo_warn "Unexpected artifacts in the distribution directory." + ls -l dist + terminate +fi + + +echo_info "" +echo_info "--------------------------------------------" +echo_info "Checking previous releases" +echo_info "--------------------------------------------" +echo_info "" + +readonly NPM_DIST_TARBALL=`npm show firebase-admin@${RELEASE_VERSION} dist.tarball` +if [[ -n "${NPM_DIST_TARBALL}" ]]; then + echo_warn "Release version ${RELEASE_VERSION} already present in NPM." + terminate +else + echo_info "Release version ${RELEASE_VERSION} not found in NPM." +fi + + +echo_info "" +echo_info "--------------------------------------------" +echo_info "Checking release tag" +echo_info "--------------------------------------------" +echo_info "" + +echo_info "---< git fetch --depth=1 origin +refs/tags/*:refs/tags/* >---" +git fetch --depth=1 origin +refs/tags/*:refs/tags/* +echo "" + +readonly EXISTING_TAG=`git rev-parse -q --verify "refs/tags/v${RELEASE_VERSION}"` || true +if [[ -n "${EXISTING_TAG}" ]]; then + echo_warn "Tag v${RELEASE_VERSION} already exists. Exiting." + echo_warn "If the tag was created in a previous unsuccessful attempt, delete it and try again." + echo_warn " $ git tag -d v${RELEASE_VERSION}" + echo_warn " $ git push --delete origin v${RELEASE_VERSION}" + + readonly RELEASE_URL="https://github.com/firebase/firebase-admin-node/releases/tag/v${RELEASE_VERSION}" + echo_warn "Delete any corresponding releases at ${RELEASE_URL}." + terminate +fi + +echo_info "Tag v${RELEASE_VERSION} does not exist." + + +echo_info "" +echo_info "--------------------------------------------" +echo_info "Generating changelog" +echo_info "--------------------------------------------" +echo_info "" + +echo_info "---< git fetch origin master --prune --unshallow >---" +git fetch origin master --prune --unshallow +echo "" + +echo_info "Generating changelog from history..." +readonly CURRENT_DIR=$(dirname "$0") +readonly CHANGELOG=`${CURRENT_DIR}/generate_changelog.sh` +echo "$CHANGELOG" + +# Parse and preformat the text to handle multi-line output. +# See https://github.community/t5/GitHub-Actions/set-output-Truncates-Multiline-Strings/td-p/37870 +FILTERED_CHANGELOG=`echo "$CHANGELOG" | grep -v "\\[INFO\\]"` || true +FILTERED_CHANGELOG="${FILTERED_CHANGELOG//'%'/'%25'}" +FILTERED_CHANGELOG="${FILTERED_CHANGELOG//$'\n'/'%0A'}" +FILTERED_CHANGELOG="${FILTERED_CHANGELOG//$'\r'/'%0D'}" +echo "::set-output name=changelog::${FILTERED_CHANGELOG}" + + +echo "" +echo_info "--------------------------------------------" +echo_info "PREFLIGHT SUCCESSFUL" +echo_info "--------------------------------------------" diff --git a/.github/scripts/run_integration_tests.sh b/.github/scripts/run_integration_tests.sh new file mode 100755 index 0000000000..fd479df552 --- /dev/null +++ b/.github/scripts/run_integration_tests.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Copyright 2020 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e +set -u + +gpg --quiet --batch --yes --decrypt --passphrase="${FIREBASE_SERVICE_ACCT_KEY}" \ + --output test/resources/key.json .github/resources/integ-service-account.json.gpg + +echo "${FIREBASE_API_KEY}" > test/resources/apikey.txt + +npm run test:integration -- --updateRules --testMultiTenancy diff --git a/verifyReleaseTarball.sh b/.github/scripts/verify_package.sh similarity index 65% rename from verifyReleaseTarball.sh rename to .github/scripts/verify_package.sh index 4f56a5ec3a..f8c37b4db0 100755 --- a/verifyReleaseTarball.sh +++ b/.github/scripts/verify_package.sh @@ -21,55 +21,53 @@ # applications. set -e +set -u if [ -z "$1" ]; then echo "[ERROR] No package name provided." - echo "[INFO] Usage: ./verifyReleaseTarball.sh " + echo "[INFO] Usage: ./verify_package.sh " exit 1 fi -# Variables PKG_NAME="$1" -ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -MOCHA_CLI="$ROOT/node_modules/.bin/mocha -r ts-node/register" -DIR="$ROOT/test/integration/typescript" -WORK_DIR=`mktemp -d` - -if [ ! -f "$ROOT/$PKG_NAME" ]; then - echo "Package $PKG_NAME does not exist." +if [ ! -f "${PKG_NAME}" ]; then + echo "Package ${PKG_NAME} does not exist." exit 1 fi -# check if tmp dir was created -if [[ ! "$WORK_DIR" || ! -d "$WORK_DIR" ]]; then +# create a temporary directory +WORK_DIR=`mktemp -d` +if [[ ! "${WORK_DIR}" || ! -d "${WORK_DIR}" ]]; then echo "Could not create temp dir" exit 1 fi # deletes the temp directory -function cleanup { - rm -rf "$WORK_DIR" - echo "Deleted temp working directory $WORK_DIR" +function cleanup { + rm -rf "${WORK_DIR}" + echo "Deleted temp working directory ${WORK_DIR}" } # register the cleanup function to be called on the EXIT signal trap cleanup EXIT -# Enter work dir -pushd "$WORK_DIR" +# Copy package and test sources into working directory +cp "${PKG_NAME}" "${WORK_DIR}" +cp -r test/integration/typescript/* "${WORK_DIR}" +cp test/resources/mock.key.json "${WORK_DIR}" -# Copy test sources into working directory -cp -r $DIR/* . -cp "$ROOT/test/resources/mock.key.json" . +# Enter work dir +pushd "${WORK_DIR}" # Install the test package npm install # Install firebase-admin package -npm install -S "$ROOT/$PKG_NAME" +npm install -S "${PKG_NAME}" echo "> tsc -p tsconfig.json" -$ROOT/node_modules/.bin/tsc -p tsconfig.json +./node_modules/.bin/tsc -p tsconfig.json +MOCHA_CLI="./node_modules/.bin/mocha -r ts-node/register" echo "> $MOCHA_CLI src/*.test.ts" -$MOCHA_CLI src/*.test.ts \ No newline at end of file +$MOCHA_CLI src/*.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e0c9931157..aa9b669139 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,6 @@ name: Continuous Integration -on: [push, pull_request] +on: push jobs: build: @@ -21,6 +21,5 @@ jobs: run: | npm ci npm run build + npm run build:tests npm test - env: - CI: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..24da56e189 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,154 @@ +# Copyright 2020 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Release Candidate + +on: + # Only run the workflow when a PR is updated or when a developer explicitly requests + # a build by sending a 'firebase_build' event. + pull_request: + types: [opened, synchronize, closed] + + repository_dispatch: + types: + - firebase_build + +jobs: + stage_release: + # To publish a release, merge the release PR with the label 'release:publish'. + # To stage a release without publishing it, send a 'firebase_build' event or apply + # the 'release:stage' label to a PR. + if: github.event.action == 'firebase_build' || + contains(github.event.pull_request.labels.*.name, 'release:stage') || + (github.event.pull_request.merged && + contains(github.event.pull_request.labels.*.name, 'release:publish')) + + runs-on: ubuntu-latest + + # When manually triggering the build, the requester can specify a target branch or a tag + # via the 'ref' client parameter. + steps: + - name: Checkout source for staging + uses: actions/checkout@v2 + with: + ref: ${{ github.event.client_payload.ref || github.ref }} + + - name: Set up Node.js + uses: actions/setup-node@v1 + with: + node-version: 8.x + + - name: Install and build + run: | + npm ci + npm run build + npm run build:tests + + - name: Run unit tests + run: npm test + + - name: Run integration tests + run: ./.github/scripts/run_integration_tests.sh + env: + FIREBASE_SERVICE_ACCT_KEY: ${{ secrets.FIREBASE_SERVICE_ACCT_KEY }} + FIREBASE_API_KEY: ${{ secrets.FIREBASE_API_KEY }} + + - name: Package release artifacts + run: | + npm pack + mkdir -p dist + cp *.tgz dist/ + + # Attach the packaged artifacts to the workflow output. These can be manually + # downloaded for later inspection if necessary. + - name: Archive artifacts + uses: actions/upload-artifact@v1 + with: + name: dist + path: dist + + - name: Verify tarball + run: | + PACKAGE_TARBALL=`ls firebase-admin-*.tgz` + ./.github/scripts/verify_package.sh $PACKAGE_TARBALL + + publish_release: + needs: stage_release + + # Check whether the release should be published. We publish only when the trigger PR is + # 1. merged + # 2. to the master branch + # 3. with the label 'release:publish', and + # 4. the title prefix '[chore] Release '. + if: github.event.pull_request.merged && + github.ref == 'master' && + contains(github.event.pull_request.labels.*.name, 'release:publish') && + startsWith(github.event.pull_request.title, '[chore] Release ') + + runs-on: ubuntu-latest + + steps: + - name: Checkout source for publish + uses: actions/checkout@v2 + + # Download the artifacts created by the stage_release job. + - name: Download release candidates + uses: actions/download-artifact@v1 + with: + name: dist + + # Node.js and NPM are needed to complete the publish. + - name: Set up Node.js + uses: actions/setup-node@v1 + with: + node-version: 8.x + + - name: Publish preflight check + id: preflight + run: ./.github/scripts/publish_preflight_check.sh + + # We pull this action from a custom fork of a contributor until + # https://github.com/actions/create-release/pull/32 is merged. Also note that v1 of + # this action does not support the "body" parameter. + - name: Create release tag + uses: fleskesvor/create-release@1a72e235c178bf2ae6c51a8ae36febc24568c5fe + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.preflight.outputs.version }} + release_name: Firebase Admin Node.js SDK ${{ steps.preflight.outputs.version }} + body: ${{ steps.preflight.outputs.changelog }} + draft: false + prerelease: false + + - name: Publish to NPM + run: ./.github/scripts/publish_package.sh + env: + NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} + VERSION: ${{ steps.preflight.outputs.version }} + + # Post to Twitter if explicitly opted-in by adding the label 'release:tweet'. + - name: Post to Twitter + if: success() && + contains(github.event.pull_request.labels.*.name, 'release:tweet') + uses: ./.github/actions/send-tweet + with: + status: > + ${{ steps.preflight.outputs.version }} of @Firebase Admin Node.js SDK is avaialble. + https://github.com/firebase/firebase-admin-node/releases/tag/${{ steps.preflight.outputs.version }} + consumer-key: ${{ secrets.TWITTER_CONSUMER_KEY }} + consumer-secret: ${{ secrets.TWITTER_CONSUMER_SECRET }} + access-token: ${{ secrets.TWITTER_ACCESS_TOKEN }} + access-token-secret: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} + continue-on-error: true diff --git a/createReleaseTarball.sh b/createReleaseTarball.sh deleted file mode 100755 index 9ca9fa5c6a..0000000000 --- a/createReleaseTarball.sh +++ /dev/null @@ -1,178 +0,0 @@ -# Copyright 2017 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -#!/bin/bash - -# Helper function to print the usage instructions. -printUsage () { - echo "[INFO] Usage: $0 " - echo "[INFO] is the version number of the tarball you want to create." -} - - -############## -# PROLOGUE # -############## - -echo "[INFO] This script only affects your local repo and can be used at any time during development or release." -echo - -# Print usage instructions if the first argument is -h or --help. -if [[ $1 == "-h" || $1 == "--help" ]]; then - printUsage - exit 1 -fi - -VERSION=$1 -VERSION_WITHOUT_RC=${VERSION%-*} - - -############################# -# VALIDATE VERSION NUMBER # -############################# -if [[ -z $VERSION ]]; then - echo "[ERROR] Version number not provided." - echo - printUsage - exit 1 -elif ! [[ $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+)?$ ]]; then - echo "[ERROR] Version number (${VERSION}) must be a valid SemVer number." - echo - printUsage - exit 1 -fi - - -################### -# VALIDATE REPO # -################### -# Ensure the checked out branch is master. -CHECKED_OUT_BRANCH="$(git branch | grep "*" | awk -F ' ' '{print $2}')" -if [[ $CHECKED_OUT_BRANCH != "master" ]]; then - read -p "[WARNING] You are on the '${CHECKED_OUT_BRANCH}' branch, not 'master'. Continue? (y/N) " CONTINUE - echo - - if ! [[ $CONTINUE == "y" || $CONTINUE == "Y" ]]; then - echo "[INFO] You chose not to continue." - exit 1 - fi -fi - -# Make sure the master branch does not have existing changes. -if ! git --git-dir=".git" diff --quiet; then - read -p "[WARNING] You have uncommitted changes on the current branch. Continue? (y/N) " CONTINUE - echo - - if ! [[ $CONTINUE == "y" || $CONTINUE == "Y" ]]; then - echo "[INFO] You chose not to continue." - exit 1 - fi -fi - - -######################### -# UPDATE package.json # -######################### -echo "[INFO] Updating version number in package.json to ${VERSION_WITHOUT_RC}..." -sed -i '' -e s/"\"version\": \".*\""/"\"version\": \"${VERSION_WITHOUT_RC}\""/ package.json -echo - - -############################ -# REINSTALL DEPENDENCIES # -############################ -echo "[INFO] Removing lib/, and node_modules/..." -rm -rf lib/ node_modules/ -if [[ $? -ne 0 ]]; then - echo "Error: Failed to remove lib/, and node_modules/." - exit 1 -fi -echo - -echo "[INFO] Installing production node modules..." -npm install --production -if [[ $? -ne 0 ]]; then - echo "Error: Failed to install production node modules." - exit 1 -fi -echo - - -############################ -# CREATE RELEASE TARBALL # -############################ -echo "[INFO] Installing and building all node modules..." -npm install -if [[ $? -ne 0 ]]; then - echo "Error: Failed to install all node modules." - exit 1 -fi -echo - -echo "[INFO] Running linter..." -npm run lint -if [[ $? -ne 0 ]]; then - echo "Error: Linter failed." - exit 1 -fi -echo - -echo "[INFO] Running unit tests..." -npm run test:unit -if [[ $? -ne 0 ]]; then - echo "Error: Unit tests failed." - exit 1 -fi -echo - -echo "[INFO] Running integration tests..." -npm run test:integration -- --updateRules --testMultiTenancy -if [[ $? -ne 0 ]]; then - echo "Error: Integration tests failed." - exit 1 -fi -echo - -echo "[INFO] Packaging up release tarball..." -npm pack -if [[ $? -ne 0 ]]; then - echo "Error: Failed to package up release tarball." - exit 1 -fi -echo - -# Since npm pack uses the version number in the package.json when creating the tarball, -# rename the tarball to include the RC version. -mv firebase-admin-${VERSION_WITHOUT_RC}.tgz firebase-admin-${VERSION}.tgz - - -############################ -# VERIFY RELEASE TARBALL # -############################ -echo "[INFO] Running release tarball verification..." -bash verifyReleaseTarball.sh firebase-admin-${VERSION}.tgz -if [[ $? -ne 0 ]]; then - echo "Error: Release tarball failed verification." - exit 1 -fi -echo - - -############## -# EPILOGUE # -############## - -echo "[INFO] firebase-admin-${VERSION}.tgz successfully created!" -echo "[INFO] Create a CL for the updated package.json if this is an actual release." -echo diff --git a/docgen/content-sources/node/toc.yaml b/docgen/content-sources/node/toc.yaml index 3177206b11..82b9c80f21 100644 --- a/docgen/content-sources/node/toc.yaml +++ b/docgen/content-sources/node/toc.yaml @@ -30,6 +30,10 @@ toc: path: /docs/reference/admin/node/admin.auth.AuthProviderConfig - title: "AuthProviderConfigFilter" path: /docs/reference/admin/node/admin.auth.AuthProviderConfigFilter + - title: "CreateMultiFactorInfoRequest" + path: /docs/reference/admin/node/admin.auth.CreateMultiFactorInfoRequest + - title: "CreatePhoneMultiFactorInfoRequest" + path: /docs/reference/admin/node/admin.auth.CreatePhoneMultiFactorInfoRequest - title: "CreateRequest" path: /docs/reference/admin/node/admin.auth.CreateRequest - title: "CreateTenantRequest" @@ -38,10 +42,20 @@ toc: path: /docs/reference/admin/node/admin.auth.ListProviderConfigResults - title: "ListTenantsResult" path: /docs/reference/admin/node/admin.auth.ListTenantsResult + - title: "MultiFactorCreateSettings" + path: /docs/reference/admin/node/admin.auth.MultiFactorCreateSettings + - title: "MultiFactorInfo" + path: /docs/reference/admin/node/admin.auth.MultiFactorInfo + - title: "MultiFactorSettings" + path: /docs/reference/admin/node/admin.auth.MultiFactorSettings + - title: "MultiFactorUpdateSettings" + path: /docs/reference/admin/node/admin.auth.MultiFactorUpdateSettings - title: "OIDCAuthProviderConfig" path: /docs/reference/admin/node/admin.auth.OIDCAuthProviderConfig - title: "OIDCUpdateAuthProviderRequest" path: /docs/reference/admin/node/admin.auth.OIDCUpdateAuthProviderRequest + - title: "PhoneMultiFactorInfo" + path: /docs/reference/admin/node/admin.auth.PhoneMultiFactorInfo - title: "SAMLAuthProviderConfig" path: /docs/reference/admin/node/admin.auth.SAMLAuthProviderConfig - title: "SAMLUpdateAuthProviderRequest" @@ -52,6 +66,10 @@ toc: path: /docs/reference/admin/node/admin.auth.TenantAwareAuth - title: "TenantManager" path: /docs/reference/admin/node/admin.auth.TenantManager + - title: "UpdateMultiFactorInfoRequest" + path: /docs/reference/admin/node/admin.auth.UpdateMultiFactorInfoRequest + - title: "UpdatePhoneMultiFactorInfoRequest" + path: /docs/reference/admin/node/admin.auth.UpdatePhoneMultiFactorInfoRequest - title: "UpdateRequest" path: /docs/reference/admin/node/admin.auth.UpdateRequest - title: "UpdateTenantRequest" @@ -68,6 +86,10 @@ toc: path: /docs/reference/admin/node/admin.auth.UserInfo - title: "UserMetadata" path: /docs/reference/admin/node/admin.auth.UserMetadata + - title: "UserMetadataRequest" + path: /docs/reference/admin/node/admin.auth.UserMetadataRequest + - title: "UserProviderRequest" + path: /docs/reference/admin/node/admin.auth.UserProviderRequest - title: "UserRecord" path: /docs/reference/admin/node/admin.auth.UserRecord - title: "SessionCookieOptions" diff --git a/docgen/generate-docs.js b/docgen/generate-docs.js index b0abb98c9d..bc04538bfb 100644 --- a/docgen/generate-docs.js +++ b/docgen/generate-docs.js @@ -28,7 +28,7 @@ const repoPath = path.resolve(`${__dirname}/..`); // Command-line options. const { source: sourceFile } = yargs .option('source', { - default: `${repoPath}/src/index.d.ts`, + default: `${repoPath}/src/*.d.ts`, describe: 'Typescript source file(s)', type: 'string' }) @@ -283,7 +283,7 @@ function addFirestoreTypeAliases() { let contentBlock = firestoreHeader; lineReader.on('line', (line) => { line = line.trim(); - if (line.startsWith('export import') && line.indexOf('_firestore.')) { + if (line.startsWith('export import') && line.indexOf('_firestore.') >= 0) { const typeName = line.split(' ')[2]; if (firestoreExcludes.indexOf(typeName) === -1) { contentBlock += ` diff --git a/gulpfile.js b/gulpfile.js index 82c10c774c..959cf46c05 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -107,7 +107,7 @@ gulp.task('copyDatabase', function() { }); gulp.task('copyTypings', function() { - return gulp.src('src/index.d.ts') + return gulp.src('src/*.d.ts') // Add header .pipe(header(banner)) diff --git a/package-lock.json b/package-lock.json index 69fd1341fc..243ca9fa29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "8.9.2", + "version": "8.10.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -665,6 +665,12 @@ "@types/promises-a-plus": "*" } }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, "@types/firebase-token-generator": { "version": "2.0.28", "resolved": "http://registry.npmjs.org/@types/firebase-token-generator/-/firebase-token-generator-2.0.28.tgz", @@ -680,6 +686,12 @@ "@types/node": "*" } }, + "@types/json-schema": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", + "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", + "dev": true + }, "@types/jsonwebtoken": { "version": "7.2.8", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-7.2.8.tgz", @@ -792,6 +804,114 @@ "integrity": "sha512-Set5ZdrAaKI/qHdFlVMgm/GsAv/wkXhSTuZFkJ+JI7HK+wIkIlOaUXSXieIvJ0+OvGIqtREFoE+NHJtEq0gtEw==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.20.0.tgz", + "integrity": "sha512-cimIdVDV3MakiGJqMXw51Xci6oEDEoPkvh8ggJe2IIzcc0fYqAxOXN6Vbeanahz6dLZq64W+40iUEc9g32FLDQ==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "2.20.0", + "eslint-utils": "^1.4.3", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "tsutils": "^3.17.1" + }, + "dependencies": { + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.20.0.tgz", + "integrity": "sha512-fEBy9xYrwG9hfBLFEwGW2lKwDRTmYzH3DwTmYbT+SMycmxAoPl0eGretnBFj/s+NfYBG63w/5c3lsvqqz5mYag==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.20.0", + "eslint-scope": "^5.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.20.0.tgz", + "integrity": "sha512-o8qsKaosLh2qhMZiHNtaHKTHyCHc3Triq6aMnwnWj7budm3xAY9owSZzV1uon5T9cWmJRJGzTFa90aex4m77Lw==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "2.20.0", + "@typescript-eslint/typescript-estree": "2.20.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.20.0.tgz", + "integrity": "sha512-WlFk8QtI8pPaE7JGQGxU7nGcnk1ccKAJkhbVookv94ZcAef3m6oCE/jEDL6dGte3JcD7reKrA0o55XhBRiVT3A==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^6.3.0", + "tsutils": "^3.17.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, "abab": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", @@ -808,9 +928,9 @@ } }, "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", "dev": true }, "acorn-globals": { @@ -821,8 +941,22 @@ "requires": { "acorn": "^6.0.1", "acorn-walk": "^6.0.1" + }, + "dependencies": { + "acorn": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "dev": true + } } }, + "acorn-jsx": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", + "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", + "dev": true + }, "acorn-walk": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", @@ -859,6 +993,15 @@ "ansi-wrap": "^0.1.0" } }, + "ansi-escapes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", + "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, "ansi-gray": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", @@ -1127,6 +1270,12 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, "async-done": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", @@ -2032,6 +2181,12 @@ } } }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -2077,6 +2232,12 @@ "supports-color": "^2.0.0" } }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", @@ -2173,6 +2334,21 @@ } } }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", @@ -2756,6 +2932,15 @@ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "dom-storage": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/dom-storage/-/dom-storage-2.1.0.tgz", @@ -3023,12 +3208,240 @@ } } }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ajv": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz", + "integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "espree": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz", + "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==", + "dev": true, + "requires": { + "acorn": "^7.1.0", + "acorn-jsx": "^5.1.0", + "eslint-visitor-keys": "^1.1.0" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "esquery": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.1.0.tgz", + "integrity": "sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, "estraverse": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", @@ -3141,6 +3554,17 @@ } } }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", @@ -3255,6 +3679,24 @@ "websocket-driver": ">=0.5.1" } }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -3348,10 +3790,38 @@ "integrity": "sha1-l2fXWewTq9yZuhFf1eqZ2Lk9EgY=", "dev": true }, - "flagged-respawn": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "dev": true + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", "dev": true }, "flush-write-stream": { @@ -4115,8 +4585,7 @@ "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "optional": true + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" }, "gaxios": { "version": "2.1.0", @@ -5122,6 +5591,22 @@ "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", "dev": true }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -5148,6 +5633,115 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, + "inquirer": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.4.tgz", + "integrity": "sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^2.4.2", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.2.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "interpret": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", @@ -5376,6 +5970,12 @@ "isobject": "^3.0.1" } }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -5686,6 +6286,12 @@ "xml-name-validator": "^3.0.0" }, "dependencies": { + "acorn": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "dev": true + }, "tough-cookie": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", @@ -6367,9 +6973,9 @@ } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.3.tgz", + "integrity": "sha512-+bMdgqjMN/Z77a6NlY/I3U5LlRDbnmaAk6lDveAPKwSpcPM4tKAuYsvYF8xjhOPXhOYGe/73vVLVez5PW+jqhw==", "dev": true }, "mixin-deep": { @@ -6474,6 +7080,12 @@ "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", "dev": true }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, "mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -6510,6 +7122,12 @@ "to-regex": "^3.0.1" } }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "neo-async": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", @@ -6935,7 +7553,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "optional": true, "requires": { "mimic-fn": "^2.1.0" } @@ -7036,6 +7653,12 @@ "lcid": "^1.0.0" } }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -7088,6 +7711,15 @@ "release-zalgo": "^1.0.0" } }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", @@ -7592,6 +8224,12 @@ } } }, + "regexpp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz", + "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==", + "dev": true + }, "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -7865,6 +8503,16 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -7942,6 +8590,15 @@ "glob": "^7.0.5" } }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, "run-sequence": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/run-sequence/-/run-sequence-1.2.2.tgz", @@ -7952,6 +8609,15 @@ "gulp-util": "*" } }, + "rxjs": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", + "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -8121,6 +8787,34 @@ "integrity": "sha512-9stIF1utB0ywNHNT7RgiXbdmen8QDCRsrTjw+G9TgKt1Yexjiv8TOWZ6WHsTPz57Yky3DIswZvEqX8fpuHNDtQ==", "dev": true }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, "snakeize": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/snakeize/-/snakeize-0.1.0.tgz", @@ -8530,6 +9224,70 @@ "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", "dev": true }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, "teeny-request": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-5.3.1.tgz", @@ -8582,6 +9340,12 @@ } } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "textextensions": { "version": "1.0.2", "resolved": "http://registry.npmjs.org/textextensions/-/textextensions-1.0.2.tgz", @@ -8606,6 +9370,12 @@ "thenify": ">= 3.1.0 < 4" } }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, "through2": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.0.tgz", @@ -8693,6 +9463,15 @@ "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", "dev": true }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "to-absolute-glob": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", @@ -8917,67 +9696,6 @@ "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==", "dev": true }, - "tslint": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.17.0.tgz", - "integrity": "sha512-pflx87WfVoYepTet3xLfDOLDm9Jqi61UXIKePOuca0qoAZyrGWonDG9VTbji58Fy+8gciUn8Bt7y69+KEVjc/w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^3.2.0", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.29.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -9014,6 +9732,12 @@ "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", "dev": true }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -9255,6 +9979,12 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, "v8flags": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz", @@ -9538,6 +10268,12 @@ "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", "dev": true }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "wordwrap": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", @@ -9559,6 +10295,15 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, "write-file-atomic": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", diff --git a/package.json b/package.json index a844be1d36..e595446284 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "8.9.2", + "version": "8.10.0", "description": "Firebase admin SDK for Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0", @@ -10,16 +10,16 @@ }, "scripts": { "build": "gulp build", + "build:tests": "gulp compile_test", "prepare": "npm run build", - "lint": "run-p lint:src lint:unit lint:integration", + "lint": "run-p lint:src lint:test", "test": "run-s lint test:unit", "integration": "run-s build test:integration", "test:unit": "mocha test/unit/*.spec.ts --require ts-node/register", "test:integration": "mocha test/integration/*.ts --slow 5000 --timeout 20000 --require ts-node/register", "test:coverage": "nyc npm run test:unit", - "lint:src": "tslint --format stylish -p tsconfig.json", - "lint:unit": "tslint -c tslint-test.json --format stylish test/unit/*.ts test/unit/**/*.ts", - "lint:integration": "tslint -c tslint-test.json --format stylish test/integration/*.ts", + "lint:src": "eslint src/ --ext .ts", + "lint:test": "eslint test/ --ext .ts", "apidocs": "node docgen/generate-docs.js --api node" }, "nyc": { @@ -82,12 +82,15 @@ "@types/scrypt": "^6.0.0", "@types/sinon": "^4.1.3", "@types/sinon-chai": "^2.7.27", + "@typescript-eslint/eslint-plugin": "^2.20.0", + "@typescript-eslint/parser": "^2.20.0", "bcrypt": "^3.0.0", "chai": "^3.5.0", "chai-as-promised": "^6.0.0", "chalk": "^1.1.3", "child-process-promise": "^2.2.1", "del": "^2.2.1", + "eslint": "^6.8.0", "firebase-token-generator": "^2.0.0", "gulp": "^4.0.2", "gulp-header": "^1.8.8", @@ -109,7 +112,6 @@ "sinon": "^4.5.0", "sinon-chai": "^2.14.0", "ts-node": "^3.3.0", - "tslint": "^5.17.0", "typedoc": "^0.15.0", "typescript": "^3.7.3", "yargs": "^13.2.2" diff --git a/src/auth.d.ts b/src/auth.d.ts new file mode 100644 index 0000000000..cfc2c1fd9c --- /dev/null +++ b/src/auth.d.ts @@ -0,0 +1,1818 @@ +import * as _admin from './index.d'; + +/* eslint-disable @typescript-eslint/ban-types */ + +export namespace admin.auth { + + /** + * Interface representing a user's metadata. + */ + interface UserMetadata { + + /** + * The date the user last signed in, formatted as a UTC string. + */ + lastSignInTime: string; + + /** + * The date the user was created, formatted as a UTC string. + * + */ + creationTime: string; + + /** + * @return A JSON-serializable representation of this object. + */ + toJSON(): Object; + } + + /** + * Interface representing a user's info from a third-party identity provider + * such as Google or Facebook. + */ + interface UserInfo { + + /** + * The user identifier for the linked provider. + */ + uid: string; + + /** + * The display name for the linked provider. + */ + displayName: string; + + /** + * The email for the linked provider. + */ + email: string; + + /** + * The phone number for the linked provider. + */ + phoneNumber: string; + + /** + * The photo URL for the linked provider. + */ + photoURL: string; + + /** + * The linked provider ID (for example, "google.com" for the Google provider). + */ + providerId: string; + + /** + * @return A JSON-serializable representation of this object. + */ + toJSON(): Object; + } + + /** + * Interface representing the common properties of a user enrolled second factor. + */ + interface MultiFactorInfo { + + /** + * The ID of the enrolled second factor. This ID is unique to the user. + */ + uid: string; + + /** + * The optional display name of the enrolled second factor. + */ + displayName?: string; + + /** + * The optional date the second factor was enrolled, formatted as a UTC string. + */ + enrollmentTime?: string; + + /** + * The type identifier of the second factor. For SMS second factors, this is `phone`. + */ + factorId: string; + + /** + * @return A JSON-serializable representation of this object. + */ + toJSON(): Object; + } + + /** + * Interface representing a phone specific user enrolled second factor. + */ + interface PhoneMultiFactorInfo extends MultiFactorInfo { + + /** + * The phone number associated with a phone second factor. + */ + phoneNumber: string; + } + + /** + * Interface representing a user. + */ + interface UserRecord { + + /** + * The user's `uid`. + */ + uid: string; + + /** + * The user's primary email, if set. + */ + email?: string; + + /** + * Whether or not the user's primary email is verified. + */ + emailVerified: boolean; + + /** + * The user's display name. + */ + displayName?: string; + + /** + * The user's primary phone number, if set. + */ + phoneNumber?: string; + + /** + * The user's photo URL. + */ + photoURL?: string; + + /** + * Whether or not the user is disabled: `true` for disabled; `false` for + * enabled. + */ + disabled: boolean; + + /** + * Additional metadata about the user. + */ + metadata: admin.auth.UserMetadata; + + /** + * An array of providers (for example, Google, Facebook) linked to the user. + */ + providerData: admin.auth.UserInfo[]; + + /** + * The user's hashed password (base64-encoded), only if Firebase Auth hashing + * algorithm (SCRYPT) is used. If a different hashing algorithm had been used + * when uploading this user, as is typical when migrating from another Auth + * system, this will be an empty string. If no password is set, this is + * null. This is only available when the user is obtained from + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#listUsers `listUsers()`}. + * + */ + passwordHash?: string; + + /** + * The user's password salt (base64-encoded), only if Firebase Auth hashing + * algorithm (SCRYPT) is used. If a different hashing algorithm had been used to + * upload this user, typical when migrating from another Auth system, this will + * be an empty string. If no password is set, this is null. This is only + * available when the user is obtained from + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#listUsers `listUsers()`}. + * + */ + passwordSalt?: string; + + /** + * The user's custom claims object if available, typically used to define + * user roles and propagated to an authenticated user's ID token. + * This is set via + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#setCustomUserClaims `setCustomUserClaims()`} + */ + customClaims?: Object; + + /** + * The date the user's tokens are valid after, formatted as a UTC string. + * This is updated every time the user's refresh token are revoked either + * from the {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#revokeRefreshTokens `revokeRefreshTokens()`} + * API or from the Firebase Auth backend on big account changes (password + * resets, password or email updates, etc). + */ + tokensValidAfterTime?: string; + + /** + * The ID of the tenant the user belongs to, if available. + */ + tenantId?: string | null; + + /** + * The multi-factor related properties for the current user, if available. + */ + multiFactor?: admin.auth.MultiFactorSettings; + + /** + * @return A JSON-serializable representation of this object. + */ + toJSON(): Object; + } + + /** + * The multi-factor related user settings. + */ + interface MultiFactorSettings { + /** + * List of second factors enrolled with the current user. + * Currently only phone second factors are supported. + */ + enrolledFactors: admin.auth.MultiFactorInfo[]; + + /** + * @return A JSON-serializable representation of this multi-factor object. + */ + toJSON(): Object; + } + + /** + * The multi-factor related user settings for create operations. + */ + interface MultiFactorCreateSettings { + + /** + * The created user's list of enrolled second factors. + */ + enrolledFactors: admin.auth.CreateMultiFactorInfoRequest[]; + } + + /** + * The multi-factor related user settings for update operations. + */ + interface MultiFactorUpdateSettings { + + /** + * The updated list of enrolled second factors. The provided list overwrites the user's + * existing list of second factors. + * When null is passed, all of the user's existing second factors are removed. + */ + enrolledFactors: admin.auth.UpdateMultiFactorInfoRequest[] | null; + } + + /** + * Interface representing common properties of a user enrolled second factor + * for an `UpdateRequest`. + */ + interface UpdateMultiFactorInfoRequest { + + /** + * The ID of the enrolled second factor. This ID is unique to the user. When not provided, + * a new one is provisioned by the Auth server. + */ + uid?: string; + + /** + * The optional display name for an enrolled second factor. + */ + displayName?: string; + + /** + * The optional date the second factor was enrolled, formatted as a UTC string. + */ + enrollmentTime?: string; + + /** + * The type identifier of the second factor. For SMS second factors, this is `phone`. + */ + factorId: string; + } + + /** + * Interface representing a phone specific user enrolled second factor + * for an `UpdateRequest`. + */ + interface UpdatePhoneMultiFactorInfoRequest extends UpdateMultiFactorInfoRequest { + + /** + * The phone number associated with a phone second factor. + */ + phoneNumber: string; + } + + /** + * Interface representing the properties to update on the provided user. + */ + interface UpdateRequest { + + /** + * Whether or not the user is disabled: `true` for disabled; + * `false` for enabled. + */ + disabled?: boolean; + + /** + * The user's display name. + */ + displayName?: string | null; + + /** + * The user's primary email. + */ + email?: string; + + /** + * Whether or not the user's primary email is verified. + */ + emailVerified?: boolean; + + /** + * The user's unhashed password. + */ + password?: string; + + /** + * The user's primary phone number. + */ + phoneNumber?: string | null; + + /** + * The user's photo URL. + */ + photoURL?: string | null; + + /** + * The user's updated multi-factor related properties. + */ + multiFactor?: admin.auth.MultiFactorUpdateSettings; + } + + /** + * Interface representing base properties of a user enrolled second factor for a + * `CreateRequest`. + */ + interface CreateMultiFactorInfoRequest { + + /** + * The optional display name for an enrolled second factor. + */ + displayName?: string; + + /** + * The type identifier of the second factor. For SMS second factors, this is `phone`. + */ + factorId: string; + } + + /** + * Interface representing a phone specific user enrolled second factor for a + * `CreateRequest`. + */ + interface CreatePhoneMultiFactorInfoRequest extends CreateMultiFactorInfoRequest { + + /** + * The phone number associated with a phone second factor. + */ + phoneNumber: string; + } + + /** + * Interface representing the properties to set on a new user record to be + * created. + */ + interface CreateRequest extends UpdateRequest { + + /** + * The user's `uid`. + */ + uid?: string; + + /** + * The user's multi-factor related properties. + */ + multiFactor?: admin.auth.MultiFactorCreateSettings; + } + + /** + * Interface representing a decoded Firebase ID token, returned from the + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#verifyIdToken `verifyIdToken()`} method. + * + * Firebase ID tokens are OpenID Connect spec-compliant JSON Web Tokens (JWTs). + * See the + * [ID Token section of the OpenID Connect spec](http://openid.net/specs/openid-connect-core-1_0.html#IDToken) + * for more information about the specific properties below. + */ + interface DecodedIdToken { + + /** + * The audience for which this token is intended. + * + * This value is a string equal to your Firebase project ID, the unique + * identifier for your Firebase project, which can be found in [your project's + * settings](https://console.firebase.google.com/project/_/settings/general/android:com.random.android). + */ + aud: string; + + /** + * Time, in seconds since the Unix epoch, when the end-user authentication + * occurred. + * + * This value is not set when this particular ID token was created, but when the + * user initially logged in to this session. In a single session, the Firebase + * SDKs will refresh a user's ID tokens every hour. Each ID token will have a + * different [`iat`](#iat) value, but the same `auth_time` value. + */ + auth_time: number; + + /** + * The ID token's expiration time, in seconds since the Unix epoch. That is, the + * time at which this ID token expires and should no longer be considered valid. + * + * The Firebase SDKs transparently refresh ID tokens every hour, issuing a new + * ID token with up to a one hour expiration. + */ + exp: number; + + /** + * Information about the sign in event, including which sign in provider was + * used and provider-specific identity details. + * + * This data is provided by the Firebase Authentication service and is a + * reserved claim in the ID token. + */ + firebase: { + + /** + * Provider-specific identity details corresponding + * to the provider used to sign in the user. + */ + identities: { + [key: string]: any; + }; + + /** + * The ID of the provider used to sign in the user. + * One of `"anonymous"`, `"password"`, `"facebook.com"`, `"github.com"`, + * `"google.com"`, `"twitter.com"`, or `"custom"`. + */ + sign_in_provider: string; + + /** + * The type identifier or `factorId` of the second factor, provided the + * ID token was obtained from a multi-factor authenticated user. + * For phone, this is `"phone"`. + */ + sign_in_second_factor?: string; + + /** + * The `uid` of the second factor used to sign in, provided the + * ID token was obtained from a multi-factor authenticated user. + */ + second_factor_identifier?: string; + + /** + * The ID of the tenant the user belongs to, if available. + */ + tenant?: string; + [key: string]: any; + }; + + /** + * The ID token's issued-at time, in seconds since the Unix epoch. That is, the + * time at which this ID token was issued and should start to be considered + * valid. + * + * The Firebase SDKs transparently refresh ID tokens every hour, issuing a new + * ID token with a new issued-at time. If you want to get the time at which the + * user session corresponding to the ID token initially occurred, see the + * [`auth_time`](#auth_time) property. + */ + iat: number; + + /** + * The issuer identifier for the issuer of the response. + * + * This value is a URL with the format + * `https://securetoken.google.com/`, where `` is the + * same project ID specified in the [`aud`](#aud) property. + */ + iss: string; + + /** + * The `uid` corresponding to the user who the ID token belonged to. + * + * As a convenience, this value is copied over to the [`uid`](#uid) property. + */ + sub: string; + + /** + * The `uid` corresponding to the user who the ID token belonged to. + * + * This value is not actually in the JWT token claims itself. It is added as a + * convenience, and is set as the value of the [`sub`](#sub) property. + */ + uid: string; + [key: string]: any; + } + + /** + * Interface representing the object returned from a + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#listUsers `listUsers()`} operation. Contains the list + * of users for the current batch and the next page token if available. + */ + interface ListUsersResult { + + /** + * The list of {@link admin.auth.UserRecord `UserRecord`} objects for the + * current downloaded batch. + */ + users: admin.auth.UserRecord[]; + + /** + * The next page token if available. This is needed for the next batch download. + */ + pageToken?: string; + } + + type HashAlgorithmType = 'SCRYPT' | 'STANDARD_SCRYPT' | 'HMAC_SHA512' | + 'HMAC_SHA256' | 'HMAC_SHA1' | 'HMAC_MD5' | 'MD5' | 'PBKDF_SHA1' | 'BCRYPT' | + 'PBKDF2_SHA256' | 'SHA512' | 'SHA256' | 'SHA1'; + + /** + * Interface representing the user import options needed for + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#importUsers `importUsers()`} method. This is used to + * provide the password hashing algorithm information. + */ + interface UserImportOptions { + + /** + * The password hashing information. + */ + hash: { + + /** + * The password hashing algorithm identifier. The following algorithm + * identifiers are supported: + * `SCRYPT`, `STANDARD_SCRYPT`, `HMAC_SHA512`, `HMAC_SHA256`, `HMAC_SHA1`, + * `HMAC_MD5`, `MD5`, `PBKDF_SHA1`, `BCRYPT`, `PBKDF2_SHA256`, `SHA512`, + * `SHA256` and `SHA1`. + */ + algorithm: HashAlgorithmType; + + /** + * The signing key used in the hash algorithm in buffer bytes. + * Required by hashing algorithms `SCRYPT`, `HMAC_SHA512`, `HMAC_SHA256`, + * `HAMC_SHA1` and `HMAC_MD5`. + */ + key?: Buffer; + + /** + * The salt separator in buffer bytes which is appended to salt when + * verifying a password. This is only used by the `SCRYPT` algorithm. + */ + saltSeparator?: Buffer; + + /** + * The number of rounds for hashing calculation. + * Required for `SCRYPT`, `MD5`, `SHA512`, `SHA256`, `SHA1`, `PBKDF_SHA1` and + * `PBKDF2_SHA256`. + */ + rounds?: number; + + /** + * The memory cost required for `SCRYPT` algorithm, or the CPU/memory cost. + * Required for `STANDARD_SCRYPT` algorithm. + */ + memoryCost?: number; + + /** + * The parallelization of the hashing algorithm. Required for the + * `STANDARD_SCRYPT` algorithm. + */ + parallelization?: number; + + /** + * The block size (normally 8) of the hashing algorithm. Required for the + * `STANDARD_SCRYPT` algorithm. + */ + blockSize?: number; + /** + * The derived key length of the hashing algorithm. Required for the + * `STANDARD_SCRYPT` algorithm. + */ + derivedKeyLength?: number; + }; + } + + /** + * Interface representing the response from the + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#importUsers `importUsers()`} method for batch + * importing users to Firebase Auth. + */ + interface UserImportResult { + + /** + * The number of user records that failed to import to Firebase Auth. + */ + failureCount: number; + + /** + * The number of user records that successfully imported to Firebase Auth. + */ + successCount: number; + + /** + * An array of errors corresponding to the provided users to import. The + * length of this array is equal to [`failureCount`](#failureCount). + */ + errors: _admin.FirebaseArrayIndexError[]; + } + + /** + * User metadata to include when importing a user. + */ + interface UserMetadataRequest { + + /** + * The date the user last signed in, formatted as a UTC string. + */ + lastSignInTime?: string; + + /** + * The date the user was created, formatted as a UTC string. + */ + creationTime?: string; + } + + /** + * User provider data to include when importing a user. + */ + interface UserProviderRequest { + + /** + * The user identifier for the linked provider. + */ + uid: string; + + /** + * The display name for the linked provider. + */ + displayName?: string; + + /** + * The email for the linked provider. + */ + email?: string; + + /** + * The phone number for the linked provider. + */ + phoneNumber?: string; + + /** + * The photo URL for the linked provider. + */ + photoURL?: string; + + /** + * The linked provider ID (for example, "google.com" for the Google provider). + */ + providerId: string; + } + + /** + * Interface representing a user to import to Firebase Auth via the + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#importUsers `importUsers()`} method. + */ + interface UserImportRecord { + + /** + * The user's `uid`. + */ + uid: string; + + /** + * The user's primary email, if set. + */ + email?: string; + + /** + * Whether or not the user's primary email is verified. + */ + emailVerified?: boolean; + + /** + * The user's display name. + */ + displayName?: string; + + /** + * The user's primary phone number, if set. + */ + phoneNumber?: string; + + /** + * The user's photo URL. + */ + photoURL?: string; + + /** + * Whether or not the user is disabled: `true` for disabled; `false` for + * enabled. + */ + disabled?: boolean; + + /** + * Additional metadata about the user. + */ + metadata?: admin.auth.UserMetadataRequest; + + /** + * An array of providers (for example, Google, Facebook) linked to the user. + */ + providerData?: admin.auth.UserProviderRequest[]; + + /** + * The user's custom claims object if available, typically used to define + * user roles and propagated to an authenticated user's ID token. + */ + customClaims?: Object; + + /** + * The buffer of bytes representing the user's hashed password. + * When a user is to be imported with a password hash, + * {@link admin.auth.UserImportOptions `UserImportOptions`} are required to be + * specified to identify the hashing algorithm used to generate this hash. + */ + passwordHash?: Buffer; + + /** + * The buffer of bytes representing the user's password salt. + */ + passwordSalt?: Buffer; + + /** + * The identifier of the tenant where user is to be imported to. + * When not provided in an `admin.auth.Auth` context, the user is uploaded to + * the default parent project. + * When not provided in an `admin.auth.TenantAwareAuth` context, the user is uploaded + * to the tenant corresponding to that `TenantAwareAuth` instance's tenant ID. + */ + tenantId?: string | null; + + /** + * The user's multi-factor related properties. + */ + multiFactor?: admin.auth.MultiFactorUpdateSettings; + } + + /** + * Interface representing the session cookie options needed for the + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#createSessionCookie `createSessionCookie()`} method. + */ + interface SessionCookieOptions { + + /** + * The session cookie custom expiration in milliseconds. The minimum allowed is + * 5 minutes and the maxium allowed is 2 weeks. + */ + expiresIn: number; + } + + /** + * This is the interface that defines the required continue/state URL with + * optional Android and iOS bundle identifiers. + */ + interface ActionCodeSettings { + + /** + * Defines the link continue/state URL, which has different meanings in + * different contexts: + *
    + *
  • When the link is handled in the web action widgets, this is the deep + * link in the `continueUrl` query parameter.
  • + *
  • When the link is handled in the app directly, this is the `continueUrl` + * query parameter in the deep link of the Dynamic Link.
  • + *
+ */ + url: string; + + /** + * Whether to open the link via a mobile app or a browser. + * The default is false. When set to true, the action code link is sent + * as a Universal Link or Android App Link and is opened by the app if + * installed. In the false case, the code is sent to the web widget first + * and then redirects to the app if installed. + */ + handleCodeInApp?: boolean; + + /** + * Defines the iOS bundle ID. This will try to open the link in an iOS app if it + * is installed. + */ + iOS?: { + + /** + * Defines the required iOS bundle ID of the app where the link should be + * handled if the application is already installed on the device. + */ + bundleId: string; + }; + + /** + * Defines the Android package name. This will try to open the link in an + * android app if it is installed. If `installApp` is passed, it specifies + * whether to install the Android app if the device supports it and the app is + * not already installed. If this field is provided without a `packageName`, an + * error is thrown explaining that the `packageName` must be provided in + * conjunction with this field. If `minimumVersion` is specified, and an older + * version of the app is installed, the user is taken to the Play Store to + * upgrade the app. + */ + android?: { + + /** + * Defines the required Android package name of the app where the link should be + * handled if the Android app is installed. + */ + packageName: string; + + /** + * Whether to install the Android app if the device supports it and the app is + * not already installed. + */ + installApp?: boolean; + + /** + * The Android minimum version if available. If the installed app is an older + * version, the user is taken to the GOogle Play Store to upgrade the app. + */ + minimumVersion?: string; + }; + + /** + * Defines the dynamic link domain to use for the current link if it is to be + * opened using Firebase Dynamic Links, as multiple dynamic link domains can be + * configured per project. This field provides the ability to explicitly choose + * configured per project. This fields provides the ability explicitly choose + * one. If none is provided, the oldest domain is used by default. + */ + dynamicLinkDomain?: string; + } + + /** + * Interface representing a tenant configuration. + * + * Multi-tenancy support requires Google Cloud's Identity Platform + * (GCIP). To learn more about GCIP, including pricing and features, + * see the [GCIP documentation](https://cloud.google.com/identity-platform) + * + * Before multi-tenancy can be used on a Google Cloud Identity Platform project, + * tenants must be allowed on that project via the Cloud Console UI. + * + * A tenant configuration provides information such as the display name, tenant + * identifier and email authentication configuration. + * For OIDC/SAML provider configuration management, `TenantAwareAuth` instances should + * be used instead of a `Tenant` to retrieve the list of configured IdPs on a tenant. + * When configuring these providers, note that tenants will inherit + * whitelisted domains and authenticated redirect URIs of their parent project. + * + * All other settings of a tenant will also be inherited. These will need to be managed + * from the Cloud Console UI. + */ + interface Tenant { + + /** + * The tenant identifier. + */ + tenantId: string; + + /** + * The tenant display name. + */ + displayName?: string; + + /** + * The email sign in provider configuration. + */ + emailSignInConfig?: { + + /** + * Whether email provider is enabled. + */ + enabled: boolean; + + /** + * Whether password is required for email sign-in. When not required, + * email sign-in can be performed with password or via email link sign-in. + */ + passwordRequired?: boolean; + }; + + /** + * @return A JSON-serializable representation of this object. + */ + toJSON(): Object; + } + + /** + * Interface representing the properties to update on the provided tenant. + */ + interface UpdateTenantRequest { + + /** + * The tenant display name. + */ + displayName?: string; + + /** + * The email sign in configuration. + */ + emailSignInConfig?: { + + /** + * Whether email provider is enabled. + */ + enabled: boolean; + + /** + * Whether password is required for email sign-in. When not required, + * email sign-in can be performed with password or via email link sign-in. + */ + passwordRequired?: boolean; + }; + } + + /** + * Interface representing the properties to set on a new tenant. + */ + type CreateTenantRequest = UpdateTenantRequest; + + /** + * Interface representing the object returned from a + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#listTenants `listTenants()`} + * operation. + * Contains the list of tenants for the current batch and the next page token if available. + */ + interface ListTenantsResult { + + /** + * The list of {@link admin.auth.Tenant `Tenant`} objects for the downloaded batch. + */ + tenants: admin.auth.Tenant[]; + + /** + * The next page token if available. This is needed for the next batch download. + */ + pageToken?: string; + } + + /** + * The filter interface used for listing provider configurations. This is used + * when specifying how to list configured identity providers via + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#listProviderConfigs `listProviderConfigs()`}. + */ + interface AuthProviderConfigFilter { + + /** + * The Auth provider configuration filter. This can be either `saml` or `oidc`. + * The former is used to look up SAML providers only, while the latter is used + * for OIDC providers. + */ + type: 'saml' | 'oidc'; + + /** + * The maximum number of results to return per page. The default and maximum is + * 100. + */ + maxResults?: number; + + /** + * The next page token. When not specified, the lookup starts from the beginning + * of the list. + */ + pageToken?: string; + } + + /** + * The base Auth provider configuration interface. + */ + interface AuthProviderConfig { + + /** + * The provider ID defined by the developer. + * For a SAML provider, this is always prefixed by `saml.`. + * For an OIDC provider, this is always prefixed by `oidc.`. + */ + providerId: string; + + /** + * The user-friendly display name to the current configuration. This name is + * also used as the provider label in the Cloud Console. + */ + displayName: string; + + /** + * Whether the provider configuration is enabled or disabled. A user + * cannot sign in using a disabled provider. + */ + enabled: boolean; + } + + /** + * The + * [SAML](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html) + * Auth provider configuration interface. A SAML provider can be created via + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#createProviderConfig `createProviderConfig()`}. + */ + interface SAMLAuthProviderConfig extends admin.auth.AuthProviderConfig { + + /** + * The SAML IdP entity identifier. + */ + idpEntityId: string; + + /** + * The SAML IdP SSO URL. This must be a valid URL. + */ + ssoURL: string; + + /** + * The list of SAML IdP X.509 certificates issued by CA for this provider. + * Multiple certificates are accepted to prevent outages during + * IdP key rotation (for example ADFS rotates every 10 days). When the Auth + * server receives a SAML response, it will match the SAML response with the + * certificate on record. Otherwise the response is rejected. + * Developers are expected to manage the certificate updates as keys are + * rotated. + */ + x509Certificates: string[]; + + /** + * The SAML relying party (service provider) entity ID. + * This is defined by the developer but needs to be provided to the SAML IdP. + */ + rpEntityId: string; + + /** + * This is fixed and must always be the same as the OAuth redirect URL + * provisioned by Firebase Auth, + * `https://project-id.firebaseapp.com/__/auth/handler` unless a custom + * `authDomain` is used. + * The callback URL should also be provided to the SAML IdP during + * configuration. + */ + callbackURL?: string; + } + + /** + * The [OIDC](https://openid.net/specs/openid-connect-core-1_0-final.html) Auth + * provider configuration interface. An OIDC provider can be created via + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#createProviderConfig `createProviderConfig()`}. + */ + interface OIDCAuthProviderConfig extends admin.auth.AuthProviderConfig { + + /** + * This is the required client ID used to confirm the audience of an OIDC + * provider's + * [ID token](https://openid.net/specs/openid-connect-core-1_0-final.html#IDToken). + */ + clientId: string; + + /** + * This is the required provider issuer used to match the provider issuer of + * the ID token and to determine the corresponding OIDC discovery document, eg. + * [`/.well-known/openid-configuration`](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig). + * This is needed for the following: + *
    + *
  • To verify the provided issuer.
  • + *
  • Determine the authentication/authorization endpoint during the OAuth + * `id_token` authentication flow.
  • + *
  • To retrieve the public signing keys via `jwks_uri` to verify the OIDC + * provider's ID token's signature.
  • + *
  • To determine the claims_supported to construct the user attributes to be + * returned in the additional user info response.
  • + *
+ * ID token validation will be performed as defined in the + * [spec](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation). + */ + issuer: string; + } + + /** + * The request interface for updating a SAML Auth provider. This is used + * when updating a SAML provider's configuration via + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#updateProviderConfig `updateProviderConfig()`}. + */ + interface SAMLUpdateAuthProviderRequest { + + /** + * The SAML provider's updated display name. If not provided, the existing + * configuration's value is not modified. + */ + displayName?: string; + + /** + * Whether the SAML provider is enabled or not. If not provided, the existing + * configuration's setting is not modified. + */ + enabled?: boolean; + + /** + * The SAML provider's updated IdP entity ID. If not provided, the existing + * configuration's value is not modified. + */ + idpEntityId?: string; + + /** + * The SAML provider's updated SSO URL. If not provided, the existing + * configuration's value is not modified. + */ + ssoURL?: string; + + /** + * The SAML provider's updated list of X.509 certificated. If not provided, the + * existing configuration list is not modified. + */ + x509Certificates?: string[]; + + /** + * The SAML provider's updated RP entity ID. If not provided, the existing + * configuration's value is not modified. + */ + rpEntityId?: string; + + /** + * The SAML provider's callback URL. If not provided, the existing + * configuration's value is not modified. + */ + callbackURL?: string; + } + + /** + * The request interface for updating an OIDC Auth provider. This is used + * when updating an OIDC provider's configuration via + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#updateProviderConfig `updateProviderConfig()`}. + */ + interface OIDCUpdateAuthProviderRequest { + + /** + * The OIDC provider's updated display name. If not provided, the existing + * configuration's value is not modified. + */ + displayName?: string; + + /** + * Whether the OIDC provider is enabled or not. If not provided, the existing + * configuration's setting is not modified. + */ + enabled?: boolean; + + /** + * The OIDC provider's updated client ID. If not provided, the existing + * configuration's value is not modified. + */ + clientId?: string; + + /** + * The OIDC provider's updated issuer. If not provided, the existing + * configuration's value is not modified. + */ + issuer?: string; + } + + /** + * The response interface for listing provider configs. This is only available + * when listing all identity providers' configurations via + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#listProviderConfigs `listProviderConfigs()`}. + */ + interface ListProviderConfigResults { + + /** + * The list of providers for the specified type in the current page. + */ + providerConfigs: admin.auth.AuthProviderConfig[]; + + /** + * The next page token, if available. + */ + pageToken?: string; + } + + + type UpdateAuthProviderRequest = + admin.auth.SAMLUpdateAuthProviderRequest | admin.auth.OIDCUpdateAuthProviderRequest; + + interface BaseAuth { + + /** + * Creates a new Firebase custom token (JWT) that can be sent back to a client + * device to use to sign in with the client SDKs' `signInWithCustomToken()` + * methods. (Tenant-aware instances will also embed the tenant ID in the + * token.) + * + * See [Create Custom Tokens](/docs/auth/admin/create-custom-tokens) for code + * samples and detailed documentation. + * + * @param uid The `uid` to use as the custom token's subject. + * @param developerClaims Optional additional claims to include + * in the custom token's payload. + * + * @return A promise fulfilled with a custom token for the + * provided `uid` and payload. + */ + createCustomToken(uid: string, developerClaims?: Object): Promise; + + /** + * Creates a new user. + * + * See [Create a user](/docs/auth/admin/manage-users#create_a_user) for code + * samples and detailed documentation. + * + * @param properties The properties to set on the + * new user record to be created. + * + * @return A promise fulfilled with the user + * data corresponding to the newly created user. + */ + createUser(properties: admin.auth.CreateRequest): Promise; + + /** + * Deletes an existing user. + * + * See [Delete a user](/docs/auth/admin/manage-users#delete_a_user) for code + * samples and detailed documentation. + * + * @param uid The `uid` corresponding to the user to delete. + * + * @return An empty promise fulfilled once the user has been + * deleted. + */ + deleteUser(uid: string): Promise; + + /** + * Gets the user data for the user corresponding to a given `uid`. + * + * See [Retrieve user data](/docs/auth/admin/manage-users#retrieve_user_data) + * for code samples and detailed documentation. + * + * @param uid The `uid` corresponding to the user whose data to fetch. + * + * @return A promise fulfilled with the user + * data corresponding to the provided `uid`. + */ + getUser(uid: string): Promise; + + /** + * Gets the user data for the user corresponding to a given email. + * + * See [Retrieve user data](/docs/auth/admin/manage-users#retrieve_user_data) + * for code samples and detailed documentation. + * + * @param email The email corresponding to the user whose data to + * fetch. + * + * @return A promise fulfilled with the user + * data corresponding to the provided email. + */ + getUserByEmail(email: string): Promise; + + /** + * Gets the user data for the user corresponding to a given phone number. The + * phone number has to conform to the E.164 specification. + * + * See [Retrieve user data](/docs/auth/admin/manage-users#retrieve_user_data) + * for code samples and detailed documentation. + * + * @param phoneNumber The phone number corresponding to the user whose + * data to fetch. + * + * @return A promise fulfilled with the user + * data corresponding to the provided phone number. + */ + getUserByPhoneNumber(phoneNumber: string): Promise; + + /** + * Retrieves a list of users (single batch only) with a size of `maxResults` + * starting from the offset as specified by `pageToken`. This is used to + * retrieve all the users of a specified project in batches. + * + * See [List all users](/docs/auth/admin/manage-users#list_all_users) + * for code samples and detailed documentation. + * + * @param maxResults The page size, 1000 if undefined. This is also + * the maximum allowed limit. + * @param pageToken The next page token. If not specified, returns + * users starting without any offset. + * @return A promise that resolves with + * the current batch of downloaded users and the next page token. + */ + listUsers(maxResults?: number, pageToken?: string): Promise; + + /** + * Updates an existing user. + * + * See [Update a user](/docs/auth/admin/manage-users#update_a_user) for code + * samples and detailed documentation. + * + * @param uid The `uid` corresponding to the user to delete. + * @param properties The properties to update on + * the provided user. + * + * @return A promise fulfilled with the + * updated user data. + */ + updateUser(uid: string, properties: admin.auth.UpdateRequest): Promise; + + /** + * Verifies a Firebase ID token (JWT). If the token is valid, the promise is + * fulfilled with the token's decoded claims; otherwise, the promise is + * rejected. + * An optional flag can be passed to additionally check whether the ID token + * was revoked. + * + * See [Verify ID Tokens](/docs/auth/admin/verify-id-tokens) for code samples + * and detailed documentation. + * + * @param idToken The ID token to verify. + * @param checkRevoked Whether to check if the ID token was revoked. + * This requires an extra request to the Firebase Auth backend to check + * the `tokensValidAfterTime` time for the corresponding user. + * When not specified, this additional check is not applied. + * + * @return A promise fulfilled with the + * token's decoded claims if the ID token is valid; otherwise, a rejected + * promise. + */ + verifyIdToken(idToken: string, checkRevoked?: boolean): Promise; + + /** + * Sets additional developer claims on an existing user identified by the + * provided `uid`, typically used to define user roles and levels of + * access. These claims should propagate to all devices where the user is + * already signed in (after token expiration or when token refresh is forced) + * and the next time the user signs in. If a reserved OIDC claim name + * is used (sub, iat, iss, etc), an error is thrown. They are set on the + * authenticated user's ID token JWT. + * + * See + * [Defining user roles and access levels](/docs/auth/admin/custom-claims) + * for code samples and detailed documentation. + * + * @param uid The `uid` of the user to edit. + * @param customUserClaims The developer claims to set. If null is + * passed, existing custom claims are deleted. Passing a custom claims payload + * larger than 1000 bytes will throw an error. Custom claims are added to the + * user's ID token which is transmitted on every authenticated request. + * For profile non-access related user attributes, use database or other + * separate storage systems. + * @return A promise that resolves when the operation completes + * successfully. + */ + setCustomUserClaims(uid: string, customUserClaims: Object | null): Promise; + + /** + * Revokes all refresh tokens for an existing user. + * + * This API will update the user's + * {@link admin.auth.UserRecord#tokensValidAfterTime `tokensValidAfterTime`} to + * the current UTC. It is important that the server on which this is called has + * its clock set correctly and synchronized. + * + * While this will revoke all sessions for a specified user and disable any + * new ID tokens for existing sessions from getting minted, existing ID tokens + * may remain active until their natural expiration (one hour). To verify that + * ID tokens are revoked, use + * {@link admin.auth.Auth#verifyIdToken `verifyIdToken(idToken, true)`} + * where `checkRevoked` is set to true. + * + * @param uid The `uid` corresponding to the user whose refresh tokens + * are to be revoked. + * + * @return An empty promise fulfilled once the user's refresh + * tokens have been revoked. + */ + revokeRefreshTokens(uid: string): Promise; + + /** + * Imports the provided list of users into Firebase Auth. + * A maximum of 1000 users are allowed to be imported one at a time. + * When importing users with passwords, + * {@link admin.auth.UserImportOptions `UserImportOptions`} are required to be + * specified. + * This operation is optimized for bulk imports and will ignore checks on `uid`, + * `email` and other identifier uniqueness which could result in duplications. + * + * @param users The list of user records to import to Firebase Auth. + * @param options The user import options, required when the users provided include + * password credentials. + * @return A promise that resolves when + * the operation completes with the result of the import. This includes the + * number of successful imports, the number of failed imports and their + * corresponding errors. + */ + importUsers( + users: admin.auth.UserImportRecord[], + options?: admin.auth.UserImportOptions, + ): Promise; + + /** + * Creates a new Firebase session cookie with the specified options. The created + * JWT string can be set as a server-side session cookie with a custom cookie + * policy, and be used for session management. The session cookie JWT will have + * the same payload claims as the provided ID token. + * + * See [Manage Session Cookies](/docs/auth/admin/manage-cookies) for code + * samples and detailed documentation. + * + * @param idToken The Firebase ID token to exchange for a session + * cookie. + * @param sessionCookieOptions The session + * cookie options which includes custom session duration. + * + * @return A promise that resolves on success with the + * created session cookie. + */ + createSessionCookie( + idToken: string, + sessionCookieOptions: admin.auth.SessionCookieOptions, + ): Promise; + + /** + * Verifies a Firebase session cookie. Returns a Promise with the cookie claims. + * Rejects the promise if the cookie could not be verified. If `checkRevoked` is + * set to true, verifies if the session corresponding to the session cookie was + * revoked. If the corresponding user's session was revoked, an + * `auth/session-cookie-revoked` error is thrown. If not specified the check is + * not performed. + * + * See [Verify Session Cookies](/docs/auth/admin/manage-cookies#verify_session_cookie_and_check_permissions) + * for code samples and detailed documentation + * + * @param sessionCookie The session cookie to verify. + * @param checkForRevocation Whether to check if the session cookie was + * revoked. This requires an extra request to the Firebase Auth backend to + * check the `tokensValidAfterTime` time for the corresponding user. + * When not specified, this additional check is not performed. + * + * @return A promise fulfilled with the + * session cookie's decoded claims if the session cookie is valid; otherwise, + * a rejected promise. + */ + verifySessionCookie( + sessionCookie: string, + checkForRevocation?: boolean, + ): Promise; + + /** + * Generates the out of band email action link to reset a user's password. + * The link is generated for the user with the specified email address. The + * optional {@link admin.auth.ActionCodeSettings `ActionCodeSettings`} object + * defines whether the link is to be handled by a mobile app or browser and the + * additional state information to be passed in the deep link, etc. + * + * @example + * ```javascript + * var actionCodeSettings = { + * url: 'https://www.example.com/?email=user@example.com', + * iOS: { + * bundleId: 'com.example.ios' + * }, + * android: { + * packageName: 'com.example.android', + * installApp: true, + * minimumVersion: '12' + * }, + * handleCodeInApp: true, + * dynamicLinkDomain: 'custom.page.link' + * }; + * admin.auth() + * .generatePasswordResetLink('user@example.com', actionCodeSettings) + * .then(function(link) { + * // The link was successfully generated. + * }) + * .catch(function(error) { + * // Some error occurred, you can inspect the code: error.code + * }); + * ``` + * + * @param email The email address of the user whose password is to be + * reset. + * @param actionCodeSettings The action + * code settings. If specified, the state/continue URL is set as the + * "continueUrl" parameter in the password reset link. The default password + * reset landing page will use this to display a link to go back to the app + * if it is installed. + * If the actionCodeSettings is not specified, no URL is appended to the + * action URL. + * The state URL provided must belong to a domain that is whitelisted by the + * developer in the console. Otherwise an error is thrown. + * Mobile app redirects are only applicable if the developer configures + * and accepts the Firebase Dynamic Links terms of service. + * The Android package name and iOS bundle ID are respected only if they + * are configured in the same Firebase Auth project. + * @return A promise that resolves with the generated link. + */ + generatePasswordResetLink( + email: string, + actionCodeSettings?: admin.auth.ActionCodeSettings, + ): Promise; + + /** + * Generates the out of band email action link to verify the user's ownership + * of the specified email. The + * {@link admin.auth.ActionCodeSettings `ActionCodeSettings`} object provided + * as an argument to this method defines whether the link is to be handled by a + * mobile app or browser along with additional state information to be passed in + * the deep link, etc. + * + * @example + * ```javascript + * var actionCodeSettings = { + * url: 'https://www.example.com/cart?email=user@example.com&cartId=123', + * iOS: { + * bundleId: 'com.example.ios' + * }, + * android: { + * packageName: 'com.example.android', + * installApp: true, + * minimumVersion: '12' + * }, + * handleCodeInApp: true, + * dynamicLinkDomain: 'custom.page.link' + * }; + * admin.auth() + * .generateEmailVerificationLink('user@example.com', actionCodeSettings) + * .then(function(link) { + * // The link was successfully generated. + * }) + * .catch(function(error) { + * // Some error occurred, you can inspect the code: error.code + * }); + * ``` + * + * @param email The email account to verify. + * @param actionCodeSettings The action + * code settings. If specified, the state/continue URL is set as the + * "continueUrl" parameter in the email verification link. The default email + * verification landing page will use this to display a link to go back to + * the app if it is installed. + * If the actionCodeSettings is not specified, no URL is appended to the + * action URL. + * The state URL provided must belong to a domain that is whitelisted by the + * developer in the console. Otherwise an error is thrown. + * Mobile app redirects are only applicable if the developer configures + * and accepts the Firebase Dynamic Links terms of service. + * The Android package name and iOS bundle ID are respected only if they + * are configured in the same Firebase Auth project. + * @return A promise that resolves with the generated link. + */ + generateEmailVerificationLink( + email: string, + actionCodeSettings?: admin.auth.ActionCodeSettings, + ): Promise; + + /** + * Generates the out of band email action link to sign in or sign up the owner + * of the specified email. The + * {@link admin.auth.ActionCodeSettings `ActionCodeSettings`} object provided + * as an argument to this method defines whether the link is to be handled by a + * mobile app or browser along with additional state information to be passed in + * the deep link, etc. + * + * @example + * ```javascript + * var actionCodeSettings = { + * // The URL to redirect to for sign-in completion. This is also the deep + * // link for mobile redirects. The domain (www.example.com) for this URL + * // must be whitelisted in the Firebase Console. + * url: 'https://www.example.com/finishSignUp?cartId=1234', + * iOS: { + * bundleId: 'com.example.ios' + * }, + * android: { + * packageName: 'com.example.android', + * installApp: true, + * minimumVersion: '12' + * }, + * // This must be true. + * handleCodeInApp: true, + * dynamicLinkDomain: 'custom.page.link' + * }; + * admin.auth() + * .generateSignInWithEmailLink('user@example.com', actionCodeSettings) + * .then(function(link) { + * // The link was successfully generated. + * }) + * .catch(function(error) { + * // Some error occurred, you can inspect the code: error.code + * }); + * ``` + * + * @param email The email account to sign in with. + * @param actionCodeSettings The action + * code settings. These settings provide Firebase with instructions on how + * to construct the email link. This includes the sign in completion URL or + * the deep link for redirects and the mobile apps to use when the + * sign-in link is opened on an Android or iOS device. + * Mobile app redirects are only applicable if the developer configures + * and accepts the Firebase Dynamic Links terms of service. + * The Android package name and iOS bundle ID are respected only if they + * are configured in the same Firebase Auth project. + * @return A promise that resolves with the generated link. + */ + generateSignInWithEmailLink( + email: string, + actionCodeSettings: admin.auth.ActionCodeSettings, + ): Promise; + + /** + * Returns the list of existing provider configurations matching the filter + * provided. At most, 100 provider configs can be listed at a time. + * + * SAML and OIDC provider support requires Google Cloud's Identity Platform + * (GCIP). To learn more about GCIP, including pricing and features, + * see the [GCIP documentation](https://cloud.google.com/identity-platform). + * + * @param options The provider config filter to apply. + * @return A promise that resolves with the list of provider configs meeting the + * filter requirements. + */ + listProviderConfigs( + options: admin.auth.AuthProviderConfigFilter + ): Promise; + + /** + * Looks up an Auth provider configuration by the provided ID. + * Returns a promise that resolves with the provider configuration + * corresponding to the provider ID specified. If the specified ID does not + * exist, an `auth/configuration-not-found` error is thrown. + * + * SAML and OIDC provider support requires Google Cloud's Identity Platform + * (GCIP). To learn more about GCIP, including pricing and features, + * see the [GCIP documentation](https://cloud.google.com/identity-platform). + * + * @param providerId The provider ID corresponding to the provider + * config to return. + * @return A promise that resolves + * with the configuration corresponding to the provided ID. + */ + getProviderConfig(providerId: string): Promise; + + /** + * Deletes the provider configuration corresponding to the provider ID passed. + * If the specified ID does not exist, an `auth/configuration-not-found` error + * is thrown. + * + * SAML and OIDC provider support requires Google Cloud's Identity Platform + * (GCIP). To learn more about GCIP, including pricing and features, + * see the [GCIP documentation](https://cloud.google.com/identity-platform). + * + * @param providerId The provider ID corresponding to the provider + * config to delete. + * @return A promise that resolves on completion. + */ + deleteProviderConfig(providerId: string): Promise; + + /** + * Returns a promise that resolves with the updated `AuthProviderConfig` + * corresponding to the provider ID specified. + * If the specified ID does not exist, an `auth/configuration-not-found` error + * is thrown. + * + * SAML and OIDC provider support requires Google Cloud's Identity Platform + * (GCIP). To learn more about GCIP, including pricing and features, + * see the [GCIP documentation](https://cloud.google.com/identity-platform). + * + * @param providerId The provider ID corresponding to the provider + * config to update. + * @param updatedConfig The updated configuration. + * @return A promise that resolves with the updated provider configuration. + */ + updateProviderConfig( + providerId: string, updatedConfig: admin.auth.UpdateAuthProviderRequest + ): Promise; + + /** + * Returns a promise that resolves with the newly created `AuthProviderConfig` + * when the new provider configuration is created. + * + * SAML and OIDC provider support requires Google Cloud's Identity Platform + * (GCIP). To learn more about GCIP, including pricing and features, + * see the [GCIP documentation](https://cloud.google.com/identity-platform). + * + * @param config The provider configuration to create. + * @return A promise that resolves with the created provider configuration. + */ + createProviderConfig( + config: admin.auth.AuthProviderConfig + ): Promise; + } + + /** + * Tenant-aware `Auth` interface used for managing users, configuring SAML/OIDC providers, + * generating email links for password reset, email verification, etc for specific tenants. + * + * Multi-tenancy support requires Google Cloud's Identity Platform + * (GCIP). To learn more about GCIP, including pricing and features, + * see the [GCIP documentation](https://cloud.google.com/identity-platform) + * + * Each tenant contains its own identity providers, settings and sets of users. + * Using `TenantAwareAuth`, users for a specific tenant and corresponding OIDC/SAML + * configurations can also be managed, ID tokens for users signed in to a specific tenant + * can be verified, and email action links can also be generated for users belonging to the + * tenant. + * + * `TenantAwareAuth` instances for a specific `tenantId` can be instantiated by calling + * `auth.tenantManager().authForTenant(tenantId)`. + */ + interface TenantAwareAuth extends BaseAuth { + + /** + * The tenant identifier corresponding to this `TenantAwareAuth` instance. + * All calls to the user management APIs, OIDC/SAML provider management APIs, email link + * generation APIs, etc will only be applied within the scope of this tenant. + */ + tenantId: string; + } + + interface Auth extends admin.auth.BaseAuth { + app: _admin.app.App; + + /** + * @return The tenant manager instance associated with the current project. + */ + tenantManager(): admin.auth.TenantManager; + } + + /** + * Defines the tenant manager used to help manage tenant related operations. + * This includes: + *
    + *
  • The ability to create, update, list, get and delete tenants for the underlying + * project.
  • + *
  • Getting a `TenantAwareAuth` instance for running Auth related operations + * (user management, provider configuration management, token verification, + * email link generation, etc) in the context of a specified tenant.
  • + *
+ */ + interface TenantManager { + /** + * @param tenantId The tenant ID whose `TenantAwareAuth` instance is to be returned. + * + * @return The `TenantAwareAuth` instance corresponding to this tenant identifier. + */ + authForTenant(tenantId: string): admin.auth.TenantAwareAuth; + + /** + * Gets the tenant configuration for the tenant corresponding to a given `tenantId`. + * + * @param tenantId The tenant identifier corresponding to the tenant whose data to fetch. + * + * @return A promise fulfilled with the tenant configuration to the provided `tenantId`. + */ + getTenant(tenantId: string): Promise; + + /** + * Retrieves a list of tenants (single batch only) with a size of `maxResults` + * starting from the offset as specified by `pageToken`. This is used to + * retrieve all the tenants of a specified project in batches. + * + * @param maxResults The page size, 1000 if undefined. This is also + * the maximum allowed limit. + * @param pageToken The next page token. If not specified, returns + * tenants starting without any offset. + * + * @return A promise that resolves with + * a batch of downloaded tenants and the next page token. + */ + listTenants(maxResults?: number, pageToken?: string): Promise; + + /** + * Deletes an existing tenant. + * + * @param tenantId The `tenantId` corresponding to the tenant to delete. + * + * @return An empty promise fulfilled once the tenant has been deleted. + */ + deleteTenant(tenantId: string): Promise; + + /** + * Creates a new tenant. + * When creating new tenants, tenants that use separate billing and quota will require their + * own project and must be defined as `full_service`. + * + * @param tenantOptions The properties to set on the new tenant configuration to be created. + * + * @return A promise fulfilled with the tenant configuration corresponding to the newly + * created tenant. + */ + createTenant(tenantOptions: admin.auth.CreateTenantRequest): Promise; + + /** + * Updates an existing tenant configuration. + * + * @param tenantId The `tenantId` corresponding to the tenant to delete. + * @param tenantOptions The properties to update on the provided tenant. + * + * @return A promise fulfilled with the update tenant data. + */ + updateTenant(tenantId: string, tenantOptions: admin.auth.UpdateTenantRequest): Promise; + } +} diff --git a/src/auth/action-code-settings-builder.ts b/src/auth/action-code-settings-builder.ts index 0961722850..159e78af48 100644 --- a/src/auth/action-code-settings-builder.ts +++ b/src/auth/action-code-settings-builder.ts @@ -68,7 +68,7 @@ export class ActionCodeSettingsBuilder { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, '"ActionCodeSettings" must be a non-null object.', - ); + ); } if (typeof actionCodeSettings.url === 'undefined') { throw new FirebaseAuthError( @@ -84,8 +84,8 @@ export class ActionCodeSettingsBuilder { if (typeof actionCodeSettings.handleCodeInApp !== 'undefined' && !validator.isBoolean(actionCodeSettings.handleCodeInApp)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, - '"ActionCodeSettings.handleCodeInApp" must be a boolean.', + AuthClientErrorCode.INVALID_ARGUMENT, + '"ActionCodeSettings.handleCodeInApp" must be a boolean.', ); } this.canHandleCodeInApp = actionCodeSettings.handleCodeInApp || false; @@ -169,7 +169,7 @@ export class ActionCodeSettingsBuilder { }; // Remove all null and undefined fields from request. for (const key in request) { - if (request.hasOwnProperty(key)) { + if (Object.prototype.hasOwnProperty.call(request, key)) { if (typeof request[key] === 'undefined' || request[key] === null) { delete request[key]; } diff --git a/src/auth/auth-api-request.ts b/src/auth/auth-api-request.ts index 3b3f7a7f21..3f26f87985 100755 --- a/src/auth/auth-api-request.ts +++ b/src/auth/auth-api-request.ts @@ -25,7 +25,7 @@ import { import {CreateRequest, UpdateRequest} from './user-record'; import { UserImportBuilder, UserImportOptions, UserImportRecord, - UserImportResult, + UserImportResult, AuthFactorInfo, convertMultiFactorInfoToServerFormat, } from './user-import-builder'; import * as utils from '../utils/index'; import {ActionCodeSettings, ActionCodeSettingsBuilder} from './action-code-settings-builder'; @@ -86,6 +86,16 @@ const FIREBASE_AUTH_TENANT_URL_FORMAT = FIREBASE_AUTH_BASE_URL_FORMAT.replace( const MAX_LIST_TENANT_PAGE_SIZE = 1000; +/** + * Enum for the user write operation type. + */ +enum WriteOperationType { + Create = 'create', + Update = 'update', + Upload = 'upload', +} + + /** Defines a base utility to help with resource URL construction. */ class AuthResourceUrlBuilder { @@ -135,7 +145,7 @@ class AuthResourceUrlBuilder { if (!validator.isNonEmptyString(projectId)) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_CREDENTIAL, - 'Failed to determine project ID for Auth. Initialize the ' + 'Failed to determine project ID for Auth. Initialize the ' + 'SDK with service account credentials or set project ID as an app option. ' + 'Alternatively set the GOOGLE_CLOUD_PROJECT environment variable.', ); @@ -180,6 +190,72 @@ class TenantAwareAuthResourceUrlBuilder extends AuthResourceUrlBuilder { } +/** + * Validates an AuthFactorInfo object. All unsupported parameters + * are removed from the original request. If an invalid field is passed + * an error is thrown. + * + * @param request The AuthFactorInfo request object. + * @param writeOperationType The write operation type. + */ +function validateAuthFactorInfo(request: AuthFactorInfo, writeOperationType: WriteOperationType): void { + const validKeys = { + mfaEnrollmentId: true, + displayName: true, + phoneInfo: true, + enrolledAt: true, + }; + // Remove unsupported keys from the original request. + for (const key in request) { + if (!(key in validKeys)) { + delete request[key]; + } + } + // No enrollment ID is available for signupNewUser. Use another identifier. + const authFactorInfoIdentifier = + request.mfaEnrollmentId || request.phoneInfo || JSON.stringify(request); + const uidRequired = writeOperationType !== WriteOperationType.Create; + if ((typeof request.mfaEnrollmentId !== 'undefined' || uidRequired) && + !validator.isNonEmptyString(request.mfaEnrollmentId)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_UID, + `The second factor "uid" must be a valid non-empty string.`, + ); + } + if (typeof request.displayName !== 'undefined' && + !validator.isString(request.displayName)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_DISPLAY_NAME, + `The second factor "displayName" for "${authFactorInfoIdentifier}" must be a valid string.`, + ); + } + // enrolledAt must be a valid UTC date string. + if (typeof request.enrolledAt !== 'undefined' && + !validator.isISODateString(request.enrolledAt)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ENROLLMENT_TIME, + `The second factor "enrollmentTime" for "${authFactorInfoIdentifier}" must be a valid ` + + `UTC date string.`); + } + // Validate required fields depending on second factor type. + if (typeof request.phoneInfo !== 'undefined') { + // phoneNumber should be a string and a valid phone number. + if (!validator.isPhoneNumber(request.phoneInfo)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_PHONE_NUMBER, + `The second factor "phoneNumber" for "${authFactorInfoIdentifier}" must be a non-empty ` + + `E.164 standard compliant identifier string.`); + } + } else { + // Invalid second factor. For example, a phone second factor may have been provided without + // a phone number. A TOTP based second factor may require a secret key, etc. + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ENROLLED_FACTORS, + `MFAInfo object provided is invalid.`); + } +} + + /** * Validates a providerUserInfo object. All unsupported parameters * are removed from the original request. If an invalid field is passed @@ -187,7 +263,7 @@ class TenantAwareAuthResourceUrlBuilder extends AuthResourceUrlBuilder { * * @param {any} request The providerUserInfo request object. */ -function validateProviderUserInfo(request: any) { +function validateProviderUserInfo(request: any): void { const validKeys = { rawId: true, providerId: true, @@ -244,10 +320,11 @@ function validateProviderUserInfo(request: any) { * are removed from the original request. If an invalid field is passed * an error is thrown. * - * @param {any} request The create/edit request object. - * @param {boolean=} uploadAccountRequest Whether to validate as an uploadAccount request. + * @param request The create/edit request object. + * @param writeOperationType The write operation type. */ -function validateCreateEditRequest(request: any, uploadAccountRequest: boolean = false) { +function validateCreateEditRequest(request: any, writeOperationType: WriteOperationType): void { + const uploadAccountRequest = writeOperationType === WriteOperationType.Upload; // Hash set of whitelisted parameters. const validKeys = { displayName: true, @@ -272,6 +349,9 @@ function validateCreateEditRequest(request: any, uploadAccountRequest: boolean = createdAt: uploadAccountRequest, lastLoginAt: uploadAccountRequest, providerUserInfo: uploadAccountRequest, + mfaInfo: uploadAccountRequest, + // Only for non-uploadAccount requests. + mfa: !uploadAccountRequest, }; // Remove invalid keys from original request. for (const key in request) { @@ -370,7 +450,7 @@ function validateCreateEditRequest(request: any, uploadAccountRequest: boolean = const invalidClaims: string[] = []; // Check for any invalid claims. RESERVED_CLAIMS.forEach((blacklistedClaim) => { - if (developerClaims.hasOwnProperty(blacklistedClaim)) { + if (Object.prototype.hasOwnProperty.call(developerClaims, blacklistedClaim)) { invalidClaims.push(blacklistedClaim); } }); @@ -379,8 +459,8 @@ function validateCreateEditRequest(request: any, uploadAccountRequest: boolean = throw new FirebaseAuthError( AuthClientErrorCode.FORBIDDEN_CLAIM, invalidClaims.length > 1 ? - `Developer claims "${invalidClaims.join('", "')}" are reserved and cannot be specified.` : - `Developer claim "${invalidClaims[0]}" is reserved and cannot be specified.`, + `Developer claims "${invalidClaims.join('", "')}" are reserved and cannot be specified.` : + `Developer claim "${invalidClaims[0]}" is reserved and cannot be specified.`, ); } // Check claims payload does not exceed maxmimum size. @@ -410,32 +490,49 @@ function validateCreateEditRequest(request: any, uploadAccountRequest: boolean = validateProviderUserInfo(providerUserInfoEntry); }); } + // mfaInfo is used for importUsers. + // mfa.enrollments is used for setAccountInfo. + // enrollments has to be an array of valid AuthFactorInfo requests. + let enrollments: AuthFactorInfo[] | null = null; + if (request.mfaInfo) { + enrollments = request.mfaInfo; + } else if (request.mfa && request.mfa.enrollments) { + enrollments = request.mfa.enrollments; + } + if (enrollments) { + if (!validator.isArray(enrollments)) { + throw new FirebaseAuthError(AuthClientErrorCode.INVALID_ENROLLED_FACTORS); + } + enrollments.forEach((authFactorInfoEntry: AuthFactorInfo) => { + validateAuthFactorInfo(authFactorInfoEntry, writeOperationType); + }); + } } /** Instantiates the createSessionCookie endpoint settings. */ export const FIREBASE_AUTH_CREATE_SESSION_COOKIE = new ApiSettings(':createSessionCookie', 'POST') - // Set request validator. - .setRequestValidator((request: any) => { - // Validate the ID token is a non-empty string. - if (!validator.isNonEmptyString(request.idToken)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_ID_TOKEN); - } - // Validate the custom session cookie duration. - if (!validator.isNumber(request.validDuration) || + // Set request validator. + .setRequestValidator((request: any) => { + // Validate the ID token is a non-empty string. + if (!validator.isNonEmptyString(request.idToken)) { + throw new FirebaseAuthError(AuthClientErrorCode.INVALID_ID_TOKEN); + } + // Validate the custom session cookie duration. + if (!validator.isNumber(request.validDuration) || request.validDuration < MIN_SESSION_COOKIE_DURATION_SECS || request.validDuration > MAX_SESSION_COOKIE_DURATION_SECS) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_SESSION_COOKIE_DURATION); - } - }) - // Set response validator. - .setResponseValidator((response: any) => { - // Response should always contain the session cookie. - if (!validator.isNonEmptyString(response.sessionCookie)) { - throw new FirebaseAuthError(AuthClientErrorCode.INTERNAL_ERROR); - } - }); + throw new FirebaseAuthError(AuthClientErrorCode.INVALID_SESSION_COOKIE_DURATION); + } + }) + // Set response validator. + .setResponseValidator((response: any) => { + // Response should always contain the session cookie. + if (!validator.isNonEmptyString(response.sessionCookie)) { + throw new FirebaseAuthError(AuthClientErrorCode.INTERNAL_ERROR); + } + }); /** Instantiates the uploadAccount endpoint settings. */ @@ -505,10 +602,10 @@ export const FIREBASE_AUTH_SET_ACCOUNT_INFO = new ApiSettings('/accounts:update' // Throw error when tenantId is passed in POST body. if (typeof request.tenantId !== 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, - '"tenantId" is an invalid "UpdateRequest" property.'); + AuthClientErrorCode.INVALID_ARGUMENT, + '"tenantId" is an invalid "UpdateRequest" property.'); } - validateCreateEditRequest(request); + validateCreateEditRequest(request, WriteOperationType.Update); }) // Set response validator. .setResponseValidator((response: any) => { @@ -542,10 +639,10 @@ export const FIREBASE_AUTH_SIGN_UP_NEW_USER = new ApiSettings('/accounts', 'POST // Throw error when tenantId is passed in POST body. if (typeof request.tenantId !== 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, - '"tenantId" is an invalid "CreateRequest" property.'); + AuthClientErrorCode.INVALID_ARGUMENT, + '"tenantId" is an invalid "CreateRequest" property.'); } - validateCreateEditRequest(request); + validateCreateEditRequest(request, WriteOperationType.Create); }) // Set response validator. .setResponseValidator((response: any) => { @@ -757,7 +854,7 @@ export abstract class AbstractAuthRequestHandler { validDuration: expiresIn / 1000, }; return this.invokeRequestHandler(this.getAuthUrlBuilder(), FIREBASE_AUTH_CREATE_SESSION_COOKIE, request) - .then((response: any) => response.sessionCookie); + .then((response: any) => response.sessionCookie); } /** @@ -825,8 +922,8 @@ export abstract class AbstractAuthRequestHandler { * and no page token are returned. */ public downloadAccount( - maxResults: number = MAX_DOWNLOAD_ACCOUNT_PAGE_SIZE, - pageToken?: string): Promise<{users: object[], nextPageToken?: string}> { + maxResults: number = MAX_DOWNLOAD_ACCOUNT_PAGE_SIZE, + pageToken?: string): Promise<{users: object[]; nextPageToken?: string}> { // Construct request. const request = { maxResults, @@ -837,13 +934,13 @@ export abstract class AbstractAuthRequestHandler { delete request.nextPageToken; } return this.invokeRequestHandler(this.getAuthUrlBuilder(), FIREBASE_AUTH_DOWNLOAD_ACCOUNT, request) - .then((response: any) => { - // No more users available. - if (!response.users) { - response.users = []; - } - return response as {users: object[], nextPageToken?: string}; - }); + .then((response: any) => { + // No more users available. + if (!response.users) { + response.users = []; + } + return response as {users: object[]; nextPageToken?: string}; + }); } /** @@ -860,14 +957,14 @@ export abstract class AbstractAuthRequestHandler { * of failed uploads and their corresponding errors. */ public uploadAccount( - users: UserImportRecord[], options?: UserImportOptions): Promise { + users: UserImportRecord[], options?: UserImportOptions): Promise { // This will throw if any error is detected in the hash options. // For errors in the list of users, this will not throw and will report the errors and the // corresponding user index in the user import generated response below. // No need to validate raw request or raw response as this is done in UserImportBuilder. const userImportBuilder = new UserImportBuilder(users, options, (userRequest: any) => { // Pass true to validate the uploadAccount specific fields. - validateCreateEditRequest(userRequest, true); + validateCreateEditRequest(userRequest, WriteOperationType.Upload); }); const request = userImportBuilder.buildRequest(); // Fail quickly if more users than allowed are to be imported. @@ -885,7 +982,7 @@ export abstract class AbstractAuthRequestHandler { return this.invokeRequestHandler(this.getAuthUrlBuilder(), FIREBASE_AUTH_UPLOAD_ACCOUNT, request) .then((response: any) => { // No error object is returned if no error encountered. - const failedUploads = (response.error || []) as Array<{index: number, message: string}>; + const failedUploads = (response.error || []) as Array<{index: number; message: string}>; // Rewrite response as UserImportResult and re-insert client previously detected errors. return userImportBuilder.buildResponse(failedUploads); }); @@ -938,9 +1035,9 @@ export abstract class AbstractAuthRequestHandler { customAttributes: JSON.stringify(customUserClaims), }; return this.invokeRequestHandler(this.getAuthUrlBuilder(), FIREBASE_AUTH_SET_ACCOUNT_INFO, request) - .then((response: any) => { - return response.localId as string; - }); + .then((response: any) => { + return response.localId as string; + }); } /** @@ -1014,10 +1111,32 @@ export abstract class AbstractAuthRequestHandler { request.disableUser = request.disabled; delete request.disabled; } + // Construct mfa related user data. + if (validator.isNonNullObject(request.multiFactor)) { + if (request.multiFactor.enrolledFactors === null) { + // Remove all second factors. + request.mfa = {}; + } else if (validator.isArray(request.multiFactor.enrolledFactors)) { + request.mfa = { + enrollments: [], + }; + try { + request.multiFactor.enrolledFactors.forEach((multiFactorInfo: any) => { + request.mfa.enrollments.push(convertMultiFactorInfoToServerFormat(multiFactorInfo)); + }); + } catch (e) { + return Promise.reject(e); + } + if (request.mfa.enrollments.length === 0) { + delete request.mfa.enrollments; + } + } + delete request.multiFactor; + } return this.invokeRequestHandler(this.getAuthUrlBuilder(), FIREBASE_AUTH_SET_ACCOUNT_INFO, request) - .then((response: any) => { - return response.localId as string; - }); + .then((response: any) => { + return response.localId as string; + }); } /** @@ -1041,12 +1160,12 @@ export abstract class AbstractAuthRequestHandler { const request: any = { localId: uid, // validSince is in UTC seconds. - validSince: Math.ceil(new Date().getTime() / 1000), + validSince: Math.floor(new Date().getTime() / 1000), }; return this.invokeRequestHandler(this.getAuthUrlBuilder(), FIREBASE_AUTH_SET_ACCOUNT_INFO, request) - .then((response: any) => { - return response.localId as string; - }); + .then((response: any) => { + return response.localId as string; + }); } /** @@ -1078,6 +1197,32 @@ export abstract class AbstractAuthRequestHandler { request.localId = request.uid; delete request.uid; } + // Construct mfa related user data. + if (validator.isNonNullObject(request.multiFactor)) { + if (validator.isNonEmptyArray(request.multiFactor.enrolledFactors)) { + const mfaInfo: AuthFactorInfo[] = []; + try { + request.multiFactor.enrolledFactors.forEach((multiFactorInfo: any) => { + // Enrollment time and uid are not allowed for signupNewUser endpoint. + // They will automatically be provisioned server side. + if (multiFactorInfo.enrollmentTime) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + '"enrollmentTime" is not supported when adding second factors via "createUser()"'); + } else if (multiFactorInfo.uid) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + '"uid" is not supported when adding second factors via "createUser()"'); + } + mfaInfo.push(convertMultiFactorInfoToServerFormat(multiFactorInfo)); + }); + } catch (e) { + return Promise.reject(e); + } + request.mfaInfo = mfaInfo; + } + delete request.multiFactor; + } return this.invokeRequestHandler(this.getAuthUrlBuilder(), FIREBASE_AUTH_SIGN_UP_NEW_USER, request) .then((response: any) => { // Return the user id. @@ -1098,8 +1243,8 @@ export abstract class AbstractAuthRequestHandler { * @return {Promise} A promise that resolves with the email action link. */ public getEmailActionLink( - requestType: string, email: string, - actionCodeSettings?: ActionCodeSettings): Promise { + requestType: string, email: string, + actionCodeSettings?: ActionCodeSettings): Promise { let request = {requestType, email, returnOobLink: true}; // ActionCodeSettings required for email link sign-in to determine the url where the sign-in will // be completed. @@ -1153,9 +1298,9 @@ export abstract class AbstractAuthRequestHandler { * configuration and no page token are returned. */ public listOAuthIdpConfigs( - maxResults: number = MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE, - pageToken?: string): Promise { - const request: {pageSize: number, pageToken?: string} = { + maxResults: number = MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE, + pageToken?: string): Promise { + const request: {pageSize: number; pageToken?: string} = { pageSize: maxResults, }; // Add next page token if provided. @@ -1163,13 +1308,13 @@ export abstract class AbstractAuthRequestHandler { request.pageToken = pageToken; } return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), LIST_OAUTH_IDP_CONFIGS, request) - .then((response: any) => { - if (!response.oauthIdpConfigs) { - response.oauthIdpConfigs = []; - delete response.nextPageToken; - } - return response as {oauthIdpConfigs: object[], nextPageToken?: string}; - }); + .then((response: any) => { + if (!response.oauthIdpConfigs) { + response.oauthIdpConfigs = []; + delete response.nextPageToken; + } + return response as {oauthIdpConfigs: object[]; nextPageToken?: string}; + }); } /** @@ -1183,7 +1328,7 @@ export abstract class AbstractAuthRequestHandler { return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); } return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), DELETE_OAUTH_IDP_CONFIG, {}, {providerId}) - .then((response: any) => { + .then(() => { // Return nothing. }); } @@ -1224,7 +1369,7 @@ export abstract class AbstractAuthRequestHandler { * configuration. */ public updateOAuthIdpConfig( - providerId: string, options: OIDCUpdateAuthProviderRequest): Promise { + providerId: string, options: OIDCUpdateAuthProviderRequest): Promise { if (!OIDCConfig.isProviderId(providerId)) { return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); } @@ -1275,9 +1420,9 @@ export abstract class AbstractAuthRequestHandler { * configuration and no page token are returned. */ public listInboundSamlConfigs( - maxResults: number = MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE, - pageToken?: string): Promise { - const request: {pageSize: number, pageToken?: string} = { + maxResults: number = MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE, + pageToken?: string): Promise { + const request: {pageSize: number; pageToken?: string} = { pageSize: maxResults, }; // Add next page token if provided. @@ -1285,13 +1430,13 @@ export abstract class AbstractAuthRequestHandler { request.pageToken = pageToken; } return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), LIST_INBOUND_SAML_CONFIGS, request) - .then((response: any) => { - if (!response.inboundSamlConfigs) { - response.inboundSamlConfigs = []; - delete response.nextPageToken; - } - return response as {inboundSamlConfigs: object[], nextPageToken?: string}; - }); + .then((response: any) => { + if (!response.inboundSamlConfigs) { + response.inboundSamlConfigs = []; + delete response.nextPageToken; + } + return response as {inboundSamlConfigs: object[]; nextPageToken?: string}; + }); } /** @@ -1305,7 +1450,7 @@ export abstract class AbstractAuthRequestHandler { return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); } return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), DELETE_INBOUND_SAML_CONFIG, {}, {providerId}) - .then((response: any) => { + .then(() => { // Return nothing. }); } @@ -1347,7 +1492,7 @@ export abstract class AbstractAuthRequestHandler { * configuration. */ public updateInboundSamlConfig( - providerId: string, options: SAMLUpdateAuthProviderRequest): Promise { + providerId: string, options: SAMLUpdateAuthProviderRequest): Promise { if (!SAMLConfig.isProviderId(providerId)) { return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); } @@ -1381,8 +1526,8 @@ export abstract class AbstractAuthRequestHandler { * @return {Promise} A promise that resolves with the response. */ protected invokeRequestHandler( - urlBuilder: AuthResourceUrlBuilder, apiSettings: ApiSettings, - requestData: object, additionalResourceParams?: object): Promise { + urlBuilder: AuthResourceUrlBuilder, apiSettings: ApiSettings, + requestData: object, additionalResourceParams?: object): Promise { return urlBuilder.getUrl(apiSettings.getEndpoint(), additionalResourceParams) .then((url) => { // Validate request. @@ -1457,33 +1602,33 @@ export abstract class AbstractAuthRequestHandler { /** Instantiates the getTenant endpoint settings. */ const GET_TENANT = new ApiSettings('/tenants/{tenantId}', 'GET') - // Set response validator. - .setResponseValidator((response: any) => { - // Response should always contain at least the tenant name. - if (!validator.isNonEmptyString(response.name)) { - throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, - 'INTERNAL ASSERT FAILED: Unable to get tenant', - ); - } - }); +// Set response validator. + .setResponseValidator((response: any) => { + // Response should always contain at least the tenant name. + if (!validator.isNonEmptyString(response.name)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INTERNAL_ERROR, + 'INTERNAL ASSERT FAILED: Unable to get tenant', + ); + } + }); /** Instantiates the deleteTenant endpoint settings. */ const DELETE_TENANT = new ApiSettings('/tenants/{tenantId}', 'DELETE'); /** Instantiates the updateTenant endpoint settings. */ const UPDATE_TENANT = new ApiSettings('/tenants/{tenantId}?updateMask={updateMask}', 'PATCH') - // Set response validator. - .setResponseValidator((response: any) => { - // Response should always contain at least the tenant name. - if (!validator.isNonEmptyString(response.name) || +// Set response validator. + .setResponseValidator((response: any) => { + // Response should always contain at least the tenant name. + if (!validator.isNonEmptyString(response.name) || !Tenant.getTenantIdFromResourceName(response.name)) { - throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, - 'INTERNAL ASSERT FAILED: Unable to update tenant', - ); - } - }); + throw new FirebaseAuthError( + AuthClientErrorCode.INTERNAL_ERROR, + 'INTERNAL ASSERT FAILED: Unable to update tenant', + ); + } + }); /** Instantiates the listTenants endpoint settings. */ const LIST_TENANTS = new ApiSettings('/tenants', 'GET') @@ -1508,17 +1653,17 @@ const LIST_TENANTS = new ApiSettings('/tenants', 'GET') /** Instantiates the createTenant endpoint settings. */ const CREATE_TENANT = new ApiSettings('/tenants', 'POST') - // Set response validator. - .setResponseValidator((response: any) => { - // Response should always contain at least the tenant name. - if (!validator.isNonEmptyString(response.name) || +// Set response validator. + .setResponseValidator((response: any) => { + // Response should always contain at least the tenant name. + if (!validator.isNonEmptyString(response.name) || !Tenant.getTenantIdFromResourceName(response.name)) { - throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, - 'INTERNAL ASSERT FAILED: Unable to create new tenant', - ); - } - }); + throw new FirebaseAuthError( + AuthClientErrorCode.INTERNAL_ERROR, + 'INTERNAL ASSERT FAILED: Unable to create new tenant', + ); + } + }); /** @@ -1585,8 +1730,8 @@ export class AuthRequestHandler extends AbstractAuthRequestHandler { * and no page token are returned. */ public listTenants( - maxResults: number = MAX_LIST_TENANT_PAGE_SIZE, - pageToken?: string): Promise<{tenants: TenantServerResponse[], nextPageToken?: string}> { + maxResults: number = MAX_LIST_TENANT_PAGE_SIZE, + pageToken?: string): Promise<{tenants: TenantServerResponse[]; nextPageToken?: string}> { const request = { pageSize: maxResults, pageToken, @@ -1596,13 +1741,13 @@ export class AuthRequestHandler extends AbstractAuthRequestHandler { delete request.pageToken; } return this.invokeRequestHandler(this.tenantMgmtResourceBuilder, LIST_TENANTS, request) - .then((response: any) => { - if (!response.tenants) { - response.tenants = []; - delete response.nextPageToken; - } - return response as {tenants: TenantServerResponse[], nextPageToken?: string}; - }); + .then((response: any) => { + if (!response.tenants) { + response.tenants = []; + delete response.nextPageToken; + } + return response as {tenants: TenantServerResponse[]; nextPageToken?: string}; + }); } /** @@ -1616,7 +1761,7 @@ export class AuthRequestHandler extends AbstractAuthRequestHandler { return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_TENANT_ID)); } return this.invokeRequestHandler(this.tenantMgmtResourceBuilder, DELETE_TENANT, {}, {tenantId}) - .then((response: any) => { + .then(() => { // Return nothing. }); } @@ -1715,14 +1860,14 @@ export class TenantAwareAuthRequestHandler extends AbstractAuthRequestHandler { * of failed uploads and their corresponding errors. */ public uploadAccount( - users: UserImportRecord[], options?: UserImportOptions): Promise { + users: UserImportRecord[], options?: UserImportOptions): Promise { // Add additional check to match tenant ID of imported user records. users.forEach((user: UserImportRecord, index: number) => { if (validator.isNonEmptyString(user.tenantId) && user.tenantId !== this.tenantId) { throw new FirebaseAuthError( - AuthClientErrorCode.MISMATCHING_TENANT_ID, - `UserRecord of index "${index}" has mismatching tenant ID "${user.tenantId}"`); + AuthClientErrorCode.MISMATCHING_TENANT_ID, + `UserRecord of index "${index}" has mismatching tenant ID "${user.tenantId}"`); } }); return super.uploadAccount(users, options); diff --git a/src/auth/auth-config.ts b/src/auth/auth-config.ts index 0249769128..95527eb1a7 100755 --- a/src/auth/auth-config.ts +++ b/src/auth/auth-config.ts @@ -179,10 +179,10 @@ export class EmailSignInConfig implements EmailSignInProviderConfig { public static buildServerRequest(options: EmailSignInProviderConfig): EmailSignInConfigServerRequest { const request: EmailSignInConfigServerRequest = {}; EmailSignInConfig.validate(options); - if (options.hasOwnProperty('enabled')) { + if (Object.prototype.hasOwnProperty.call(options, 'enabled')) { request.allowPasswordSignup = options.enabled; } - if (options.hasOwnProperty('passwordRequired')) { + if (Object.prototype.hasOwnProperty.call(options, 'passwordRequired')) { request.enableEmailLinkSignin = !options.passwordRequired; } return request; @@ -193,7 +193,7 @@ export class EmailSignInConfig implements EmailSignInProviderConfig { * * @param {any} options The options object to validate. */ - private static validate(options: EmailSignInProviderConfig) { + private static validate(options: EmailSignInProviderConfig): void { // TODO: Validate the request. const validKeys = { enabled: true, @@ -284,8 +284,8 @@ export class SAMLConfig implements SAMLAuthProviderConfig { * @return {?SAMLConfigServerRequest} The resulting server request or null if not valid. */ public static buildServerRequest( - options: SAMLAuthProviderRequest, - ignoreMissingFields: boolean = false): SAMLConfigServerRequest | null { + options: SAMLAuthProviderRequest, + ignoreMissingFields = false): SAMLConfigServerRequest | null { const makeRequest = validator.isNonNullObject(options) && (options.providerId || ignoreMissingFields); if (!makeRequest) { @@ -349,7 +349,7 @@ export class SAMLConfig implements SAMLAuthProviderConfig { * @param {SAMLAuthProviderRequest} options The options object to validate. * @param {boolean=} ignoreMissingFields Whether to ignore missing fields. */ - public static validate(options: SAMLAuthProviderRequest, ignoreMissingFields: boolean = false) { + public static validate(options: SAMLAuthProviderRequest, ignoreMissingFields = false): void { const validKeys = { enabled: true, displayName: true, @@ -409,7 +409,7 @@ export class SAMLConfig implements SAMLAuthProviderConfig { !validator.isNonEmptyString(options.rpEntityId)) { throw new FirebaseAuthError( !options.rpEntityId ? AuthClientErrorCode.MISSING_SAML_RELYING_PARTY_CONFIG : - AuthClientErrorCode.INVALID_CONFIG, + AuthClientErrorCode.INVALID_CONFIG, '"SAMLAuthProviderConfig.rpEntityId" must be a valid non-empty string.', ); } @@ -544,8 +544,8 @@ export class OIDCConfig implements OIDCAuthProviderConfig { * @return {?OIDCConfigServerRequest} The resulting server request or null if not valid. */ public static buildServerRequest( - options: OIDCAuthProviderRequest, - ignoreMissingFields: boolean = false): OIDCConfigServerRequest | null { + options: OIDCAuthProviderRequest, + ignoreMissingFields = false): OIDCConfigServerRequest | null { const makeRequest = validator.isNonNullObject(options) && (options.providerId || ignoreMissingFields); if (!makeRequest) { @@ -590,7 +590,7 @@ export class OIDCConfig implements OIDCAuthProviderConfig { * @param {OIDCAuthProviderRequest} options The options object to validate. * @param {boolean=} ignoreMissingFields Whether to ignore missing fields. */ - public static validate(options: OIDCAuthProviderRequest, ignoreMissingFields: boolean = false) { + public static validate(options: OIDCAuthProviderRequest, ignoreMissingFields = false): void { const validKeys = { enabled: true, displayName: true, diff --git a/src/auth/auth.ts b/src/auth/auth.ts index eddea69cb3..cbc22cd9d4 100755 --- a/src/auth/auth.ts +++ b/src/auth/auth.ts @@ -70,6 +70,8 @@ export interface DecodedIdToken { [key: string]: any; }; sign_in_provider: string; + sign_in_second_factor?: string; + second_factor_identifier?: string; [key: string]: any; }; iat: number; @@ -142,7 +144,7 @@ export class BaseAuth { * @return {Promise} A Promise that will be fulfilled after a successful * verification. */ - public verifyIdToken(idToken: string, checkRevoked: boolean = false): Promise { + public verifyIdToken(idToken: string, checkRevoked = false): Promise { return this.idTokenVerifier.verifyJWT(idToken) .then((decodedIdToken: DecodedIdToken) => { // Whether to check if the token was revoked. @@ -266,7 +268,7 @@ export class BaseAuth { */ public deleteUser(uid: string): Promise { return this.authRequestHandler.deleteAccount(uid) - .then((response) => { + .then(() => { // Return nothing on success. }); } @@ -296,7 +298,7 @@ export class BaseAuth { */ public setCustomUserClaims(uid: string, customUserClaims: object): Promise { return this.authRequestHandler.setCustomUserClaims(uid, customUserClaims) - .then((existingUid) => { + .then(() => { // Return nothing on success. }); } @@ -313,7 +315,7 @@ export class BaseAuth { */ public revokeRefreshTokens(uid: string): Promise { return this.authRequestHandler.revokeRefreshTokens(uid) - .then((existingUid) => { + .then(() => { // Return nothing on success. }); } @@ -332,7 +334,7 @@ export class BaseAuth { * of failed uploads and their corresponding errors. */ public importUsers( - users: UserImportRecord[], options?: UserImportOptions): Promise { + users: UserImportRecord[], options?: UserImportOptions): Promise { return this.authRequestHandler.uploadAccount(users, options); } @@ -348,7 +350,7 @@ export class BaseAuth { * @return {Promise} A promise that resolves on success with the created session cookie. */ public createSessionCookie( - idToken: string, sessionCookieOptions: SessionCookieOptions): Promise { + idToken: string, sessionCookieOptions: SessionCookieOptions): Promise { // Return rejected promise if expiresIn is not available. if (!validator.isNonNullObject(sessionCookieOptions) || !validator.isNumber(sessionCookieOptions.expiresIn)) { @@ -371,7 +373,7 @@ export class BaseAuth { * verification. */ public verifySessionCookie( - sessionCookie: string, checkRevoked: boolean = false): Promise { + sessionCookie: string, checkRevoked = false): Promise { return this.sessionCookieVerifier.verifyJWT(sessionCookie) .then((decodedIdToken: DecodedIdToken) => { // Whether to check if the token was revoked. @@ -444,7 +446,7 @@ export class BaseAuth { providerConfigs, }; // Delete result.pageToken if undefined. - if (response.hasOwnProperty('nextPageToken')) { + if (Object.prototype.hasOwnProperty.call(response, 'nextPageToken')) { result.pageToken = response.nextPageToken; } return result; @@ -475,9 +477,9 @@ export class BaseAuth { }); } return Promise.reject( - new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, - `"AuthProviderConfigFilter.type" must be either "saml' or "oidc"`)); + new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + `"AuthProviderConfigFilter.type" must be either "saml' or "oidc"`)); } /** @@ -526,7 +528,7 @@ export class BaseAuth { * @return {Promise} A promise that resolves with the updated provider configuration. */ public updateProviderConfig( - providerId: string, updatedConfig: UpdateAuthProviderRequest): Promise { + providerId: string, updatedConfig: UpdateAuthProviderRequest): Promise { if (!validator.isNonNullObject(updatedConfig)) { return Promise.reject(new FirebaseAuthError( AuthClientErrorCode.INVALID_CONFIG, @@ -585,7 +587,7 @@ export class BaseAuth { * verification. */ private verifyDecodedJWTNotRevoked( - decodedIdToken: DecodedIdToken, revocationErrorInfo: ErrorInfo): Promise { + decodedIdToken: DecodedIdToken, revocationErrorInfo: ErrorInfo): Promise { // Get tokens valid after time for the corresponding user. return this.getUser(decodedIdToken.sub) .then((user: UserRecord) => { @@ -639,7 +641,7 @@ export class TenantAwareAuth extends BaseAuth { * @return {Promise} A Promise that will be fulfilled after a successful * verification. */ - public verifyIdToken(idToken: string, checkRevoked: boolean = false): Promise { + public verifyIdToken(idToken: string, checkRevoked = false): Promise { return super.verifyIdToken(idToken, checkRevoked) .then((decodedClaims) => { // Validate tenant ID. @@ -662,7 +664,7 @@ export class TenantAwareAuth extends BaseAuth { * @return {Promise} A promise that resolves on success with the created session cookie. */ public createSessionCookie( - idToken: string, sessionCookieOptions: SessionCookieOptions): Promise { + idToken: string, sessionCookieOptions: SessionCookieOptions): Promise { // Validate arguments before processing. if (!validator.isNonEmptyString(idToken)) { return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_ID_TOKEN)); @@ -673,7 +675,7 @@ export class TenantAwareAuth extends BaseAuth { } // This will verify the ID token and then match the tenant ID before creating the session cookie. return this.verifyIdToken(idToken) - .then((decodedIdTokenClaims) => { + .then(() => { return super.createSessionCookie(idToken, sessionCookieOptions); }); } @@ -691,7 +693,7 @@ export class TenantAwareAuth extends BaseAuth { * verification. */ public verifySessionCookie( - sessionCookie: string, checkRevoked: boolean = false): Promise { + sessionCookie: string, checkRevoked = false): Promise { return super.verifySessionCookie(sessionCookie, checkRevoked) .then((decodedClaims) => { if (decodedClaims.firebase.tenant !== this.tenantId) { diff --git a/src/auth/credential.ts b/src/auth/credential.ts index cb878ff554..43de4d3ed3 100644 --- a/src/auth/credential.ts +++ b/src/auth/credential.ts @@ -132,6 +132,7 @@ export class ServiceAccountCredential implements Credential { ].join(' '), }; + // eslint-disable-next-line @typescript-eslint/no-var-requires const jwt = require('jsonwebtoken'); // This method is actually synchronous so we can capture and return the buffer. return jwt.sign(claims, this.privateKey, { @@ -189,6 +190,7 @@ class ServiceAccount { throw new FirebaseAppError(AppErrorCodes.INVALID_CREDENTIAL, errorMessage); } + // eslint-disable-next-line @typescript-eslint/no-var-requires const forge = require('node-forge'); try { forge.pki.privateKeyFromPem(this.privateKey); @@ -386,7 +388,7 @@ export function isApplicationDefault(credential?: Credential): boolean { * @param key Name of the property to copy. * @param alt Alternative name of the property to copy. */ -function copyAttr(to: {[key: string]: any}, from: {[key: string]: any}, key: string, alt: string) { +function copyAttr(to: {[key: string]: any}, from: {[key: string]: any}, key: string, alt: string): void { const tmp = from[key] || from[alt]; if (typeof tmp !== 'undefined') { to[key] = tmp; diff --git a/src/auth/tenant-manager.ts b/src/auth/tenant-manager.ts index 5af985a04a..73950a76bd 100644 --- a/src/auth/tenant-manager.ts +++ b/src/auth/tenant-manager.ts @@ -86,10 +86,10 @@ export class TenantManager { * empty list of tenants and no page token are returned. */ public listTenants( - maxResults?: number, - pageToken?: string): Promise { + maxResults?: number, + pageToken?: string): Promise { return this.authRequestHandler.listTenants(maxResults, pageToken) - .then((response: {tenants: TenantServerResponse[], nextPageToken?: string}) => { + .then((response: {tenants: TenantServerResponse[]; nextPageToken?: string}) => { // List of tenants to return. const tenants: Tenant[] = []; // Convert each user response to a Tenant. diff --git a/src/auth/tenant.ts b/src/auth/tenant.ts index 8fda7d3c7f..0b2ed1dca9 100755 --- a/src/auth/tenant.ts +++ b/src/auth/tenant.ts @@ -62,7 +62,7 @@ export class Tenant { * @return {object} The equivalent server request. */ public static buildServerRequest( - tenantOptions: TenantOptions, createRequest: boolean): TenantOptionsServerRequest { + tenantOptions: TenantOptions, createRequest: boolean): TenantOptionsServerRequest { Tenant.validate(tenantOptions, createRequest); let request: TenantOptionsServerRequest = {}; if (typeof tenantOptions.emailSignInConfig !== 'undefined') { @@ -95,7 +95,7 @@ export class Tenant { * @param {any} request The tenant options object to validate. * @param {boolean} createRequest Whether this is a create request. */ - private static validate(request: any, createRequest: boolean) { + private static validate(request: any, createRequest: boolean): void { const validKeys = { displayName: true, emailSignInConfig: true, diff --git a/src/auth/token-generator.ts b/src/auth/token-generator.ts index 553874c1b9..96188af944 100644 --- a/src/auth/token-generator.ts +++ b/src/auth/token-generator.ts @@ -101,7 +101,7 @@ export class ServiceAccountSigner implements CryptoSigner { * @inheritDoc */ public sign(buffer: Buffer): Promise { - const crypto = require('crypto'); + const crypto = require('crypto'); // eslint-disable-line @typescript-eslint/no-var-requires const sign = crypto.createSign('RSA-SHA256'); sign.update(buffer); return Promise.resolve(sign.sign(this.credential.privateKey)); @@ -170,8 +170,8 @@ export class IAMSigner implements CryptoSigner { throw FirebaseAuthError.fromServerError(errorCode, errorMsg, error); } throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, - 'Error returned from server: ' + error + '. Additionally, an ' + + AuthClientErrorCode.INTERNAL_ERROR, + 'Error returned from server: ' + error + '. Additionally, an ' + 'internal error occurred while attempting to extract the ' + 'errorcode from the error.', ); @@ -285,7 +285,7 @@ export class FirebaseTokenGenerator { if (typeof developerClaims !== 'undefined') { for (const key in developerClaims) { /* istanbul ignore else */ - if (developerClaims.hasOwnProperty(key)) { + if (Object.prototype.hasOwnProperty.call(developerClaims, key)) { if (BLACKLISTED_CLAIMS.indexOf(key) !== -1) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, @@ -311,6 +311,7 @@ export class FirebaseTokenGenerator { uid, }; if (this.tenantId) { + // eslint-disable-next-line @typescript-eslint/camelcase body.tenant_id = this.tenantId; } if (Object.keys(claims).length > 0) { @@ -324,9 +325,9 @@ export class FirebaseTokenGenerator { }); } - private encodeSegment(segment: object | Buffer) { + private encodeSegment(segment: object | Buffer): string { const buffer: Buffer = (segment instanceof Buffer) ? segment : Buffer.from(JSON.stringify(segment)); - return toWebSafeBase64(buffer).replace(/\=+$/, ''); + return toWebSafeBase64(buffer).replace(/=+$/, ''); } /** diff --git a/src/auth/token-verifier.ts b/src/auth/token-verifier.ts index 6a72f1e8a5..cc0c4f4c02 100644 --- a/src/auth/token-verifier.ts +++ b/src/auth/token-verifier.ts @@ -214,7 +214,7 @@ export class FirebaseTokenVerifier { } return this.fetchPublicKeys().then((publicKeys) => { - if (!publicKeys.hasOwnProperty(header.kid)) { + if (!Object.prototype.hasOwnProperty.call(publicKeys, header.kid)) { return Promise.reject( new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, @@ -261,8 +261,8 @@ export class FirebaseTokenVerifier { // is actually correct. if (typeof decodedToken === 'string') { return reject(new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, - "Unexpected decodedToken. Expected an object but got a string: '" + decodedToken + "'", + AuthClientErrorCode.INTERNAL_ERROR, + "Unexpected decodedToken. Expected an object but got a string: '" + decodedToken + "'", )); } else { const decodedIdToken = (decodedToken as DecodedIdToken); @@ -299,7 +299,7 @@ export class FirebaseTokenVerifier { // error responses. throw new HttpError(resp); } - if (resp.headers.hasOwnProperty('cache-control')) { + if (Object.prototype.hasOwnProperty.call(resp.headers, 'cache-control')) { const cacheControlHeader: string = resp.headers['cache-control']; const parts = cacheControlHeader.split(','); parts.forEach((part) => { diff --git a/src/auth/user-import-builder.ts b/src/auth/user-import-builder.ts old mode 100644 new mode 100755 index 396dfa49da..97650ec227 --- a/src/auth/user-import-builder.ts +++ b/src/auth/user-import-builder.ts @@ -39,6 +39,14 @@ export interface UserImportOptions { }; } +interface SecondFactor { + uid: string; + phoneNumber: string; + displayName?: string; + enrollmentTime?: string; + factorId: string; +} + /** User import record as accepted from developer. */ export interface UserImportRecord { @@ -54,18 +62,31 @@ export interface UserImportRecord { creationTime?: string; }; providerData?: Array<{ - uid: string, - displayName?: string, - email?: string, - photoURL?: string, - providerId: string, + uid: string; + displayName?: string; + email?: string; + photoURL?: string; + providerId: string; }>; + multiFactor?: { + enrolledFactors: SecondFactor[]; + }; customClaims?: object; passwordHash?: Buffer; passwordSalt?: Buffer; tenantId?: string; } +/** Interface representing an Auth second factor in Auth server format. */ +export interface AuthFactorInfo { + // Not required for signupNewUser endpoint. + mfaEnrollmentId?: string; + displayName?: string; + phoneInfo?: string; + enrolledAt?: string; + [key: string]: any; +} + /** UploadAccount endpoint request user interface. */ interface UploadAccountUser { @@ -83,6 +104,7 @@ interface UploadAccountUser { displayName?: string; photoUrl?: string; }>; + mfaInfo?: AuthFactorInfo[]; passwordHash?: string; salt?: string; lastLoginAt?: number; @@ -124,6 +146,49 @@ export interface UserImportResult { export type ValidatorFunction = (data: UploadAccountUser) => void; +/** + * Converts a client format second factor object to server format. + * @param multiFactorInfo The client format second factor. + * @return The corresponding AuthFactorInfo server request format. + */ +export function convertMultiFactorInfoToServerFormat(multiFactorInfo: SecondFactor): AuthFactorInfo { + let enrolledAt; + if (typeof multiFactorInfo.enrollmentTime !== 'undefined') { + if (validator.isUTCDateString(multiFactorInfo.enrollmentTime)) { + // Convert from UTC date string (client side format) to ISO date string (server side format). + enrolledAt = new Date(multiFactorInfo.enrollmentTime).toISOString(); + } else { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ENROLLMENT_TIME, + `The second factor "enrollmentTime" for "${multiFactorInfo.uid}" must be a valid ` + + `UTC date string.`); + } + } + // Currently only phone second factors are supported. + if (multiFactorInfo.factorId === 'phone') { + // If any required field is missing or invalid, validation will still fail later. + const authFactorInfo: AuthFactorInfo = { + mfaEnrollmentId: multiFactorInfo.uid, + displayName: multiFactorInfo.displayName, + // Required for all phone second factors. + phoneInfo: multiFactorInfo.phoneNumber, + enrolledAt, + }; + for (const objKey in authFactorInfo) { + if (typeof authFactorInfo[objKey] === 'undefined') { + delete authFactorInfo[objKey]; + } + } + return authFactorInfo; + } else { + // Unsupported second factor. + throw new FirebaseAuthError( + AuthClientErrorCode.UNSUPPORTED_SECOND_FACTOR, + `Unsupported second factor "${JSON.stringify(multiFactorInfo)}" provided.`); + } +} + + /** * @param {any} obj The object to check for number field within. * @param {string} key The entry key. @@ -145,7 +210,7 @@ function getNumberField(obj: any, key: string): number { * @return {UploadAccountUser} The corresponding UploadAccountUser to return. */ function populateUploadAccountUser( - user: UserImportRecord, userValidator?: ValidatorFunction): UploadAccountUser { + user: UserImportRecord, userValidator?: ValidatorFunction): UploadAccountUser { const result: UploadAccountUser = { localId: user.uid, email: user.email, @@ -155,6 +220,7 @@ function populateUploadAccountUser( photoUrl: user.photoURL, phoneNumber: user.phoneNumber, providerUserInfo: [], + mfaInfo: [], tenantId: user.tenantId, customAttributes: user.customClaims && JSON.stringify(user.customClaims), }; @@ -193,6 +259,15 @@ function populateUploadAccountUser( }); }); } + + // Convert user.multiFactor.enrolledFactors to server format. + if (validator.isNonNullObject(user.multiFactor) && + validator.isNonEmptyArray(user.multiFactor.enrolledFactors)) { + user.multiFactor.enrolledFactors.forEach((multiFactorInfo) => { + result.mfaInfo!.push(convertMultiFactorInfoToServerFormat(multiFactorInfo)); + }); + } + // Remove blank fields. let key: keyof UploadAccountUser; for (key in result) { @@ -203,6 +278,9 @@ function populateUploadAccountUser( if (result.providerUserInfo!.length === 0) { delete result.providerUserInfo; } + if (result.mfaInfo!.length === 0) { + delete result.mfaInfo; + } // Validate the constructured user individual request. This will throw if an error // is detected. if (typeof userValidator === 'function') { @@ -231,9 +309,9 @@ export class UserImportBuilder { * @constructor */ constructor( - users: UserImportRecord[], - options?: UserImportOptions, - userRequestValidator?: ValidatorFunction) { + users: UserImportRecord[], + options?: UserImportOptions, + userRequestValidator?: ValidatorFunction) { this.requiresHashOptions = false; this.validatedUsers = []; this.userImportResultErrors = []; @@ -261,7 +339,7 @@ export class UserImportBuilder { * uploadAccount response. */ public buildResponse( - failedUploads: Array<{index: number, message: string}>): UserImportResult { + failedUploads: Array<{index: number; message: string}>): UserImportResult { // Initialize user import result. const importResult: UserImportResult = { successCount: this.validatedUsers.length, @@ -296,7 +374,7 @@ export class UserImportBuilder { * @return {UploadAccountOptions} The populated UploadAccount options. */ private populateOptions( - options: UserImportOptions | undefined, requiresHashOptions: boolean): UploadAccountOptions { + options: UserImportOptions | undefined, requiresHashOptions: boolean): UploadAccountOptions { let populatedOptions: UploadAccountOptions; if (!requiresHashOptions) { return {}; @@ -323,152 +401,152 @@ export class UserImportBuilder { let rounds: number; switch (options.hash.algorithm) { - case 'HMAC_SHA512': - case 'HMAC_SHA256': - case 'HMAC_SHA1': - case 'HMAC_MD5': - if (!validator.isBuffer(options.hash.key)) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_KEY, - `A non-empty "hash.key" byte buffer must be provided for ` + + case 'HMAC_SHA512': + case 'HMAC_SHA256': + case 'HMAC_SHA1': + case 'HMAC_MD5': + if (!validator.isBuffer(options.hash.key)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_HASH_KEY, + `A non-empty "hash.key" byte buffer must be provided for ` + `hash algorithm ${options.hash.algorithm}.`, - ); - } - populatedOptions = { - hashAlgorithm: options.hash.algorithm, - signerKey: utils.toWebSafeBase64(options.hash.key), - }; - break; - - case 'MD5': - case 'SHA1': - case 'SHA256': - case 'SHA512': - // MD5 is [0,8192] but SHA1, SHA256, and SHA512 are [1,8192] - rounds = getNumberField(options.hash, 'rounds'); - const minRounds = options.hash.algorithm === 'MD5' ? 0 : 1; - if (isNaN(rounds) || rounds < minRounds || rounds > 8192) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_ROUNDS, - `A valid "hash.rounds" number between ${minRounds} and 8192 must be provided for ` + + ); + } + populatedOptions = { + hashAlgorithm: options.hash.algorithm, + signerKey: utils.toWebSafeBase64(options.hash.key), + }; + break; + + case 'MD5': + case 'SHA1': + case 'SHA256': + case 'SHA512': { + // MD5 is [0,8192] but SHA1, SHA256, and SHA512 are [1,8192] + rounds = getNumberField(options.hash, 'rounds'); + const minRounds = options.hash.algorithm === 'MD5' ? 0 : 1; + if (isNaN(rounds) || rounds < minRounds || rounds > 8192) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_HASH_ROUNDS, + `A valid "hash.rounds" number between ${minRounds} and 8192 must be provided for ` + `hash algorithm ${options.hash.algorithm}.`, - ); - } - populatedOptions = { - hashAlgorithm: options.hash.algorithm, - rounds, - }; - break; - - case 'PBKDF_SHA1': - case 'PBKDF2_SHA256': - rounds = getNumberField(options.hash, 'rounds'); - if (isNaN(rounds) || rounds < 0 || rounds > 120000) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_ROUNDS, - `A valid "hash.rounds" number between 0 and 120000 must be provided for ` + + ); + } + populatedOptions = { + hashAlgorithm: options.hash.algorithm, + rounds, + }; + break; + } + case 'PBKDF_SHA1': + case 'PBKDF2_SHA256': + rounds = getNumberField(options.hash, 'rounds'); + if (isNaN(rounds) || rounds < 0 || rounds > 120000) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_HASH_ROUNDS, + `A valid "hash.rounds" number between 0 and 120000 must be provided for ` + `hash algorithm ${options.hash.algorithm}.`, - ); - } - populatedOptions = { - hashAlgorithm: options.hash.algorithm, - rounds, - }; - break; - - case 'SCRYPT': - if (!validator.isBuffer(options.hash.key)) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_KEY, - `A "hash.key" byte buffer must be provided for ` + + ); + } + populatedOptions = { + hashAlgorithm: options.hash.algorithm, + rounds, + }; + break; + + case 'SCRYPT': { + if (!validator.isBuffer(options.hash.key)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_HASH_KEY, + `A "hash.key" byte buffer must be provided for ` + `hash algorithm ${options.hash.algorithm}.`, - ); - } - rounds = getNumberField(options.hash, 'rounds'); - if (isNaN(rounds) || rounds <= 0 || rounds > 8) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_ROUNDS, - `A valid "hash.rounds" number between 1 and 8 must be provided for ` + + ); + } + rounds = getNumberField(options.hash, 'rounds'); + if (isNaN(rounds) || rounds <= 0 || rounds > 8) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_HASH_ROUNDS, + `A valid "hash.rounds" number between 1 and 8 must be provided for ` + `hash algorithm ${options.hash.algorithm}.`, - ); - } - const memoryCost = getNumberField(options.hash, 'memoryCost'); - if (isNaN(memoryCost) || memoryCost <= 0 || memoryCost > 14) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_MEMORY_COST, - `A valid "hash.memoryCost" number between 1 and 14 must be provided for ` + + ); + } + const memoryCost = getNumberField(options.hash, 'memoryCost'); + if (isNaN(memoryCost) || memoryCost <= 0 || memoryCost > 14) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_HASH_MEMORY_COST, + `A valid "hash.memoryCost" number between 1 and 14 must be provided for ` + `hash algorithm ${options.hash.algorithm}.`, - ); - } - if (typeof options.hash.saltSeparator !== 'undefined' && + ); + } + if (typeof options.hash.saltSeparator !== 'undefined' && !validator.isBuffer(options.hash.saltSeparator)) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_SALT_SEPARATOR, - `"hash.saltSeparator" must be a byte buffer.`, - ); - } - populatedOptions = { - hashAlgorithm: options.hash.algorithm, - signerKey: utils.toWebSafeBase64(options.hash.key), - rounds, - memoryCost, - saltSeparator: utils.toWebSafeBase64(options.hash.saltSeparator || Buffer.from('')), - }; - break; - - case 'BCRYPT': - populatedOptions = { - hashAlgorithm: options.hash.algorithm, - }; - break; - - case 'STANDARD_SCRYPT': - const cpuMemCost = getNumberField(options.hash, 'memoryCost'); - if (isNaN(cpuMemCost)) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_MEMORY_COST, - `A valid "hash.memoryCost" number must be provided for ` + - `hash algorithm ${options.hash.algorithm}.`, - ); - } - const parallelization = getNumberField(options.hash, 'parallelization'); - if (isNaN(parallelization)) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_PARALLELIZATION, - `A valid "hash.parallelization" number must be provided for ` + + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_HASH_SALT_SEPARATOR, + `"hash.saltSeparator" must be a byte buffer.`, + ); + } + populatedOptions = { + hashAlgorithm: options.hash.algorithm, + signerKey: utils.toWebSafeBase64(options.hash.key), + rounds, + memoryCost, + saltSeparator: utils.toWebSafeBase64(options.hash.saltSeparator || Buffer.from('')), + }; + break; + } + case 'BCRYPT': + populatedOptions = { + hashAlgorithm: options.hash.algorithm, + }; + break; + + case 'STANDARD_SCRYPT': { + const cpuMemCost = getNumberField(options.hash, 'memoryCost'); + if (isNaN(cpuMemCost)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_HASH_MEMORY_COST, + `A valid "hash.memoryCost" number must be provided for ` + `hash algorithm ${options.hash.algorithm}.`, - ); - } - const blockSize = getNumberField(options.hash, 'blockSize'); - if (isNaN(blockSize)) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_BLOCK_SIZE, - `A valid "hash.blockSize" number must be provided for ` + + ); + } + const parallelization = getNumberField(options.hash, 'parallelization'); + if (isNaN(parallelization)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_HASH_PARALLELIZATION, + `A valid "hash.parallelization" number must be provided for ` + `hash algorithm ${options.hash.algorithm}.`, - ); - } - const dkLen = getNumberField(options.hash, 'derivedKeyLength'); - if (isNaN(dkLen)) { - throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_DERIVED_KEY_LENGTH, - `A valid "hash.derivedKeyLength" number must be provided for ` + + ); + } + const blockSize = getNumberField(options.hash, 'blockSize'); + if (isNaN(blockSize)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_HASH_BLOCK_SIZE, + `A valid "hash.blockSize" number must be provided for ` + `hash algorithm ${options.hash.algorithm}.`, - ); - } - populatedOptions = { - hashAlgorithm: options.hash.algorithm, - cpuMemCost, - parallelization, - blockSize, - dkLen, - }; - break; - - default: + ); + } + const dkLen = getNumberField(options.hash, 'derivedKeyLength'); + if (isNaN(dkLen)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_ALGORITHM, - `Unsupported hash algorithm provider "${options.hash.algorithm}".`, + AuthClientErrorCode.INVALID_HASH_DERIVED_KEY_LENGTH, + `A valid "hash.derivedKeyLength" number must be provided for ` + + `hash algorithm ${options.hash.algorithm}.`, ); + } + populatedOptions = { + hashAlgorithm: options.hash.algorithm, + cpuMemCost, + parallelization, + blockSize, + dkLen, + }; + break; + } + default: + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_HASH_ALGORITHM, + `Unsupported hash algorithm provider "${options.hash.algorithm}".`, + ); } return populatedOptions; } @@ -484,7 +562,7 @@ export class UserImportBuilder { * @return {UploadAccountUser[]} The populated uploadAccount users. */ private populateUsers( - users: UserImportRecord[], userValidator?: ValidatorFunction): UploadAccountUser[] { + users: UserImportRecord[], userValidator?: ValidatorFunction): UploadAccountUser[] { const populatedUsers: UploadAccountUser[] = []; users.forEach((user, index) => { try { diff --git a/src/auth/user-record.ts b/src/auth/user-record.ts index f8c3adafc7..64422c1969 100644 --- a/src/auth/user-record.ts +++ b/src/auth/user-record.ts @@ -15,6 +15,7 @@ */ import {deepCopy} from '../utils/deep-copy'; +import {isNonNullObject} from '../utils/validator'; import * as utils from '../utils'; import {AuthClientErrorCode, FirebaseAuthError} from '../utils/error'; @@ -41,6 +42,42 @@ function parseDate(time: any): string | null { return null; } +/** + * Interface representing base properties of a user enrolled second factor for a + * `CreateRequest`. + */ +export interface CreateMultiFactorInfoRequest { + displayName?: string; + factorId: string; +} + +/** + * Interface representing a phone specific user enrolled second factor for a + * `CreateRequest`. + */ +export interface CreatePhoneMultiFactorInfoRequest extends CreateMultiFactorInfoRequest { + phoneNumber: string; +} + +/** + * Interface representing common properties of a user enrolled second factor + * for an `UpdateRequest`. + */ +export interface UpdateMultiFactorInfoRequest { + uid?: string; + displayName?: string; + enrollmentTime?: string; + factorId: string; +} + +/** + * Interface representing a phone specific user enrolled second factor + * for an `UpdateRequest`. + */ +export interface UpdatePhoneMultiFactorInfoRequest extends UpdateMultiFactorInfoRequest { + phoneNumber: string; +} + /** Parameters for update user operation */ export interface UpdateRequest { disabled?: boolean; @@ -50,18 +87,224 @@ export interface UpdateRequest { password?: string; phoneNumber?: string | null; photoURL?: string | null; + multiFactor?: { + enrolledFactors: UpdateMultiFactorInfoRequest[] | null; + }; } /** Parameters for create user operation */ export interface CreateRequest extends UpdateRequest { uid?: string; + multiFactor?: { + enrolledFactors: CreateMultiFactorInfoRequest[]; + }; +} + +export interface MultiFactorInfoResponse { + mfaEnrollmentId: string; + displayName?: string; + phoneInfo?: string; + enrolledAt?: string; + [key: string]: any; +} + +export interface ProviderUserInfoResponse { + rawId: string; + displayName?: string; + email?: string; + photoUrl?: string; + phoneNumber?: string; + providerId: string; + federatedId?: string; +} + +export interface GetAccountInfoUserResponse { + localId: string; + email?: string; + emailVerified?: boolean; + phoneNumber?: string; + displayName?: string; + photoUrl?: string; + disabled?: boolean; + passwordHash?: string; + salt?: string; + customAttributes?: string; + validSince?: string; + tenantId?: string; + providerUserInfo?: ProviderUserInfoResponse[]; + mfaInfo?: MultiFactorInfoResponse[]; + createdAt?: string; + lastLoginAt?: string; + [key: string]: any; +} + +/** Enums for multi-factor identifiers. */ +export enum MultiFactorId { + Phone = 'phone', +} + +/** + * Abstract class representing a multi-factor info interface. + */ +export abstract class MultiFactorInfo { + public readonly uid: string; + public readonly displayName: string | null; + public readonly factorId: MultiFactorId; + public readonly enrollmentTime: string; + + /** + * Initializes the MultiFactorInfo associated subclass using the server side. + * If no MultiFactorInfo is associated with the response, null is returned. + * + * @param response The server side response. + * @constructor + */ + public static initMultiFactorInfo(response: MultiFactorInfoResponse): MultiFactorInfo | null { + let multiFactorInfo: MultiFactorInfo | null = null; + // Only PhoneMultiFactorInfo currently available. + try { + multiFactorInfo = new PhoneMultiFactorInfo(response); + } catch (e) { + // Ignore error. + } + return multiFactorInfo; + } + + /** + * Initializes the MultiFactorInfo object using the server side response. + * + * @param response The server side response. + * @constructor + */ + constructor(response: MultiFactorInfoResponse) { + this.initFromServerResponse(response); + } + + /** @return The plain object representation. */ + public toJSON(): any { + return { + uid: this.uid, + displayName: this.displayName, + factorId: this.factorId, + enrollmentTime: this.enrollmentTime, + }; + } + + /** + * Returns the factor ID based on the response provided. + * + * @param response The server side response. + * @return The multi-factor ID associated with the provided response. If the response is + * not associated with any known multi-factor ID, null is returned. + */ + protected abstract getFactorId(response: MultiFactorInfoResponse): MultiFactorId | null; + + /** + * Initializes the MultiFactorInfo object using the provided server response. + * + * @param response The server side response. + */ + private initFromServerResponse(response: MultiFactorInfoResponse): void { + const factorId = response && this.getFactorId(response); + if (!factorId || !response || !response.mfaEnrollmentId) { + throw new FirebaseAuthError( + AuthClientErrorCode.INTERNAL_ERROR, + 'INTERNAL ASSERT FAILED: Invalid multi-factor info response'); + } + utils.addReadonlyGetter(this, 'uid', response.mfaEnrollmentId); + utils.addReadonlyGetter(this, 'factorId', factorId); + utils.addReadonlyGetter(this, 'displayName', response.displayName || null); + // Encoded using [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. + // For example, "2017-01-15T01:30:15.01Z". + // This can be parsed directly via Date constructor. + // This can be computed using Data.prototype.toISOString. + if (response.enrolledAt) { + utils.addReadonlyGetter( + this, 'enrollmentTime', new Date(response.enrolledAt).toUTCString()); + } else { + utils.addReadonlyGetter(this, 'enrollmentTime', null); + } + } +} + +/** Class representing a phone MultiFactorInfo object. */ +export class PhoneMultiFactorInfo extends MultiFactorInfo { + public readonly phoneNumber: string; + + /** + * Initializes the PhoneMultiFactorInfo object using the server side response. + * + * @param response The server side response. + * @constructor + */ + constructor(response: MultiFactorInfoResponse) { + super(response); + utils.addReadonlyGetter(this, 'phoneNumber', response.phoneInfo); + } + + /** @return The plain object representation. */ + public toJSON(): any { + return Object.assign( + super.toJSON(), + { + phoneNumber: this.phoneNumber, + }); + } + + /** + * Returns the factor ID based on the response provided. + * + * @param response The server side response. + * @return The multi-factor ID associated with the provided response. If the response is + * not associated with any known multi-factor ID, null is returned. + */ + protected getFactorId(response: MultiFactorInfoResponse): MultiFactorId | null { + return (response && response.phoneInfo) ? MultiFactorId.Phone : null; + } +} + +/** Class representing multi-factor related properties of a user. */ +export class MultiFactor { + public readonly enrolledFactors: ReadonlyArray; + + /** + * Initializes the MultiFactor object using the server side or JWT format response. + * + * @param response The server side response. + * @constructor + */ + constructor(response: GetAccountInfoUserResponse) { + const parsedEnrolledFactors: MultiFactorInfo[] = []; + if (!isNonNullObject(response)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INTERNAL_ERROR, + 'INTERNAL ASSERT FAILED: Invalid multi-factor response'); + } else if (response.mfaInfo) { + response.mfaInfo.forEach((factorResponse) => { + const multiFactorInfo = MultiFactorInfo.initMultiFactorInfo(factorResponse); + if (multiFactorInfo) { + parsedEnrolledFactors.push(multiFactorInfo); + } + }); + } + // Make enrolled factors immutable. + utils.addReadonlyGetter( + this, 'enrolledFactors', Object.freeze(parsedEnrolledFactors)); + } + + /** @return The plain object representation. */ + public toJSON(): any { + return { + enrolledFactors: this.enrolledFactors.map((info) => info.toJSON()), + }; + } } /** * User metadata class that provides metadata information like user account creation * and last sign in time. * - * @param {object} response The server side response returned from the getAccountInfo + * @param response The server side response returned from the getAccountInfo * endpoint. * @constructor */ @@ -69,7 +312,7 @@ export class UserMetadata { public readonly creationTime: string; public readonly lastSignInTime: string; - constructor(response: any) { + constructor(response: GetAccountInfoUserResponse) { // Creation date should always be available but due to some backend bugs there // were cases in the past where users did not have creation date properly set. // This included legacy Firebase migrating project users and some anonymous users. @@ -78,7 +321,7 @@ export class UserMetadata { utils.addReadonlyGetter(this, 'lastSignInTime', parseDate(response.lastLoginAt)); } - /** @return {object} The plain object representation of the user's metadata. */ + /** @return The plain object representation of the user's metadata. */ public toJSON(): object { return { lastSignInTime: this.lastSignInTime, @@ -91,7 +334,7 @@ export class UserMetadata { * User info class that provides provider user information for different * Firebase providers like google.com, facebook.com, password, etc. * - * @param {object} response The server side response returned from the getAccountInfo + * @param response The server side response returned from the getAccountInfo * endpoint. * @constructor */ @@ -103,7 +346,7 @@ export class UserInfo { public readonly providerId: string; public readonly phoneNumber: string; - constructor(response: any) { + constructor(response: ProviderUserInfoResponse) { // Provider user id and provider id are required. if (!response.rawId || !response.providerId) { throw new FirebaseAuthError( @@ -119,7 +362,7 @@ export class UserInfo { utils.addReadonlyGetter(this, 'phoneNumber', response.phoneNumber); } - /** @return {object} The plain object representation of the current provider data. */ + /** @return The plain object representation of the current provider data. */ public toJSON(): object { return { uid: this.uid, @@ -136,7 +379,7 @@ export class UserInfo { * User record class that defines the Firebase user object populated from * the Firebase Auth getAccountInfo response. * - * @param {any} response The server side response returned from the getAccountInfo + * @param response The server side response returned from the getAccountInfo * endpoint. * @constructor */ @@ -155,8 +398,9 @@ export class UserRecord { public readonly customClaims: object; public readonly tenantId?: string | null; public readonly tokensValidAfterTime?: string; + public readonly multiFactor?: MultiFactor; - constructor(response: any) { + constructor(response: GetAccountInfoUserResponse) { // The Firebase user id is required. if (!response.localId) { throw new FirebaseAuthError( @@ -189,23 +433,25 @@ export class UserRecord { } utils.addReadonlyGetter(this, 'passwordSalt', response.salt); - try { + if (response.customAttributes) { utils.addReadonlyGetter( - this, 'customClaims', JSON.parse(response.customAttributes)); - } catch (e) { - // Ignore error. - utils.addReadonlyGetter(this, 'customClaims', undefined); + this, 'customClaims', JSON.parse(response.customAttributes)); } + let validAfterTime: string | null = null; // Convert validSince first to UTC milliseconds and then to UTC date string. if (typeof response.validSince !== 'undefined') { - validAfterTime = parseDate(response.validSince * 1000); + validAfterTime = parseDate(parseInt(response.validSince, 10) * 1000); } utils.addReadonlyGetter(this, 'tokensValidAfterTime', validAfterTime || undefined); utils.addReadonlyGetter(this, 'tenantId', response.tenantId); + const multiFactor = new MultiFactor(response); + if (multiFactor.enrolledFactors.length > 0) { + utils.addReadonlyGetter(this, 'multiFactor', multiFactor); + } } - /** @return {object} The plain object representation of the user record. */ + /** @return The plain object representation of the user record. */ public toJSON(): object { const json: any = { uid: this.uid, @@ -223,10 +469,13 @@ export class UserRecord { tokensValidAfterTime: this.tokensValidAfterTime, tenantId: this.tenantId, }; + if (this.multiFactor) { + json.multiFactor = this.multiFactor.toJSON(); + } json.providerData = []; for (const entry of this.providerData) { - // Convert each provider data to json. - json.providerData.push(entry.toJSON()); + // Convert each provider data to json. + json.providerData.push(entry.toJSON()); } return json; } diff --git a/src/database.d.ts b/src/database.d.ts new file mode 100644 index 0000000000..4ea333e483 --- /dev/null +++ b/src/database.d.ts @@ -0,0 +1,1640 @@ +import * as _admin from './index.d'; + +/* eslint-disable @typescript-eslint/ban-types */ + +export namespace admin.database { + + /** + * The Firebase Realtime Database service interface. + * + * Do not call this constructor directly. Instead, use + * [`admin.database()`](admin.database#database). + * + * See + * {@link + * https://firebase.google.com/docs/database/admin/start/ + * Introduction to the Admin Database API} + * for a full guide on how to use the Firebase Realtime Database service. + */ + interface Database { + app: _admin.app.App; + + /** + * Disconnects from the server (all Database operations will be completed + * offline). + * + * The client automatically maintains a persistent connection to the Database + * server, which will remain active indefinitely and reconnect when + * disconnected. However, the `goOffline()` and `goOnline()` methods may be used + * to control the client connection in cases where a persistent connection is + * undesirable. + * + * While offline, the client will no longer receive data updates from the + * Database. However, all Database operations performed locally will continue to + * immediately fire events, allowing your application to continue behaving + * normally. Additionally, each operation performed locally will automatically + * be queued and retried upon reconnection to the Database server. + * + * To reconnect to the Database and begin receiving remote events, see + * `goOnline()`. + * + * @example + * ```javascript + * admin.database().goOffline(); + * ``` + */ + goOffline(): void; + + /** + * Reconnects to the server and synchronizes the offline Database state + * with the server state. + * + * This method should be used after disabling the active connection with + * `goOffline()`. Once reconnected, the client will transmit the proper data + * and fire the appropriate events so that your client "catches up" + * automatically. + * + * @example + * ```javascript + * admin.database().goOnline(); + * ``` + */ + goOnline(): void; + + /** + * Returns a `Reference` representing the location in the Database + * corresponding to the provided path. Also can be invoked with an existing + * `Reference` as the argument. In that case returns a new `Reference` + * pointing to the same location. If no path argument is + * provided, returns a `Reference` that represents the root of the Database. + * + * @example + * ```javascript + * // Get a reference to the root of the Database + * var rootRef = admin.database.ref(); + * ``` + * + * @example + * ```javascript + * // Get a reference to the /users/ada node + * var adaRef = admin.database().ref("users/ada"); + * // The above is shorthand for the following operations: + * //var rootRef = admin.database().ref(); + * //var adaRef = rootRef.child("users/ada"); + * ``` + * + * @example + * ```javascript + * var adaRef = admin.database().ref("users/ada"); + * // Get a new reference pointing to the same location. + * var anotherAdaRef = admin.database().ref(adaRef); + * ``` + * + * + * @param path Optional path representing + * the location the returned `Reference` will point. Alternatively, a + * `Reference` object to copy. If not provided, the returned `Reference` will + * point to the root of the Database. + * @return If a path is provided, a `Reference` + * pointing to the provided path. Otherwise, a `Reference` pointing to the + * root of the Database. + */ + ref(path?: string | admin.database.Reference): admin.database.Reference; + + /** + * Returns a `Reference` representing the location in the Database + * corresponding to the provided Firebase URL. + * + * An exception is thrown if the URL is not a valid Firebase Database URL or it + * has a different domain than the current `Database` instance. + * + * Note that all query parameters (`orderBy`, `limitToLast`, etc.) are ignored + * and are not applied to the returned `Reference`. + * + * @example + * ```javascript + * // Get a reference to the root of the Database + * var rootRef = admin.database().ref("https://.firebaseio.com"); + * ``` + * + * @example + * ```javascript + * // Get a reference to the /users/ada node + * var adaRef = admin.database().ref("https://.firebaseio.com/users/ada"); + * ``` + * + * @param url The Firebase URL at which the returned `Reference` will + * point. + * @return A `Reference` pointing to the provided Firebase URL. + */ + refFromURL(url: string): admin.database.Reference; + + /** + * Gets the currently applied security rules as a string. The return value consists of + * the rules source including comments. + * + * @return A promise fulfilled with the rules as a raw string. + */ + getRules(): Promise; + + /** + * Gets the currently applied security rules as a parsed JSON object. Any comments in + * the original source are stripped away. + * + * @return A promise fulfilled with the parsed rules object. + */ + getRulesJSON(): Promise; + + /** + * Sets the specified rules on the Firebase Realtime Database instance. If the rules source is + * specified as a string or a Buffer, it may include comments. + * + * @param source Source of the rules to apply. Must not be `null` or empty. + * @return Resolves when the rules are set on the Realtime Database. + */ + setRules(source: string | Buffer | object): Promise; + } + + /** + * A `DataSnapshot` contains data from a Database location. + * + * Any time you read data from the Database, you receive the data as a + * `DataSnapshot`. A `DataSnapshot` is passed to the event callbacks you attach + * with `on()` or `once()`. You can extract the contents of the snapshot as a + * JavaScript object by calling the `val()` method. Alternatively, you can + * traverse into the snapshot by calling `child()` to return child snapshots + * (which you could then call `val()` on). + * + * A `DataSnapshot` is an efficiently generated, immutable copy of the data at + * a Database location. It cannot be modified and will never change (to modify + * data, you always call the `set()` method on a `Reference` directly). + */ + interface DataSnapshot { + key: string | null; + ref: admin.database.Reference; + + /** + * Gets another `DataSnapshot` for the location at the specified relative path. + * + * Passing a relative path to the `child()` method of a DataSnapshot returns + * another `DataSnapshot` for the location at the specified relative path. The + * relative path can either be a simple child name (for example, "ada") or a + * deeper, slash-separated path (for example, "ada/name/first"). If the child + * location has no data, an empty `DataSnapshot` (that is, a `DataSnapshot` + * whose value is `null`) is returned. + * + * @example + * ```javascript + * // Assume we have the following data in the Database: + * { + * "name": { + * "first": "Ada", + * "last": "Lovelace" + * } + * } + * + * // Test for the existence of certain keys within a DataSnapshot + * var ref = admin.database().ref("users/ada"); + * ref.once("value") + * .then(function(snapshot) { + * var name = snapshot.child("name").val(); // {first:"Ada",last:"Lovelace"} + * var firstName = snapshot.child("name/first").val(); // "Ada" + * var lastName = snapshot.child("name").child("last").val(); // "Lovelace" + * var age = snapshot.child("age").val(); // null + * }); + * ``` + * + * @param path A relative path to the location of child data. + * @return `DataSnapshot` for the location at the specified relative path. + */ + child(path: string): admin.database.DataSnapshot; + + /** + * Returns true if this `DataSnapshot` contains any data. It is slightly more + * efficient than using `snapshot.val() !== null`. + * + * @example + * ```javascript + * // Assume we have the following data in the Database: + * { + * "name": { + * "first": "Ada", + * "last": "Lovelace" + * } + * } + * + * // Test for the existence of certain keys within a DataSnapshot + * var ref = admin.database().ref("users/ada"); + * ref.once("value") + * .then(function(snapshot) { + * var a = snapshot.exists(); // true + * var b = snapshot.child("name").exists(); // true + * var c = snapshot.child("name/first").exists(); // true + * var d = snapshot.child("name/middle").exists(); // false + * }); + * ``` + * + * @return Whether this `DataSnapshot` contains any data. + */ + exists(): boolean; + + /** + * Exports the entire contents of the DataSnapshot as a JavaScript object. + * + * The `exportVal()` method is similar to `val()`, except priority information + * is included (if available), making it suitable for backing up your data. + * + * @return The DataSnapshot's contents as a JavaScript value (Object, + * Array, string, number, boolean, or `null`). + */ + exportVal(): any; + + /** + * Enumerates the top-level children in the `DataSnapshot`. + * + * Because of the way JavaScript objects work, the ordering of data in the + * JavaScript object returned by `val()` is not guaranteed to match the ordering + * on the server nor the ordering of `child_added` events. That is where + * `forEach()` comes in handy. It guarantees the children of a `DataSnapshot` + * will be iterated in their query order. + * + * If no explicit `orderBy*()` method is used, results are returned + * ordered by key (unless priorities are used, in which case, results are + * returned by priority). + * + * @example + * ```javascript + * + * // Assume we have the following data in the Database: + * { + * "users": { + * "ada": { + * "first": "Ada", + * "last": "Lovelace" + * }, + * "alan": { + * "first": "Alan", + * "last": "Turing" + * } + * } + * } + * + * // Loop through users in order with the forEach() method. The callback + * // provided to forEach() will be called synchronously with a DataSnapshot + * // for each child: + * var query = admin.database().ref("users").orderByKey(); + * query.once("value") + * .then(function(snapshot) { + * snapshot.forEach(function(childSnapshot) { + * // key will be "ada" the first time and "alan" the second time + * var key = childSnapshot.key; + * // childData will be the actual contents of the child + * var childData = childSnapshot.val(); + * }); + * }); + * ``` + * + * @example + * ```javascript + * // You can cancel the enumeration at any point by having your callback + * // function return true. For example, the following code sample will only + * // fire the callback function one time: + * var query = admin.database().ref("users").orderByKey(); + * query.once("value") + * .then(function(snapshot) { + * snapshot.forEach(function(childSnapshot) { + * var key = childSnapshot.key; // "ada" + * + * // Cancel enumeration + * return true; + * }); + * }); + * ``` + * + * @param action A function + * that will be called for each child `DataSnapshot`. The callback can return + * true to cancel further enumeration. + * @return True if enumeration was canceled due to your callback + * returning true. + */ + forEach(action: (a: admin.database.DataSnapshot) => boolean | void): boolean; + + /** + * Gets the priority value of the data in this `DataSnapshot`. + * + * Applications need not use priority but can order collections by + * ordinary properties (see + * {@link + * https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data + * Sorting and filtering data}). + * + * @return The the priority value of the data in this `DataSnapshot`. + */ + getPriority(): string | number | null; + + /** + * Returns true if the specified child path has (non-null) data. + * + * @example + * ```javascript + * // Assume we have the following data in the Database: + * { + * "name": { + * "first": "Ada", + * "last": "Lovelace" + * } + * } + * + * // Determine which child keys in DataSnapshot have data. + * var ref = admin.database().ref("users/ada"); + * ref.once("value") + * .then(function(snapshot) { + * var hasName = snapshot.hasChild("name"); // true + * var hasAge = snapshot.hasChild("age"); // false + * }); + * ``` + * + * @param path A relative path to the location of a potential child. + * @return `true` if data exists at the specified child path; else + * `false`. + */ + hasChild(path: string): boolean; + + /** + * Returns whether or not the `DataSnapshot` has any non-`null` child + * properties. + * + * You can use `hasChildren()` to determine if a `DataSnapshot` has any + * children. If it does, you can enumerate them using `forEach()`. If it + * doesn't, then either this snapshot contains a primitive value (which can be + * retrieved with `val()`) or it is empty (in which case, `val()` will return + * `null`). + * + * @example + * ```javascript + * // Assume we have the following data in the Database: + * { + * "name": { + * "first": "Ada", + * "last": "Lovelace" + * } + * } + * + * var ref = admin.database().ref("users/ada"); + * ref.once("value") + * .then(function(snapshot) { + * var a = snapshot.hasChildren(); // true + * var b = snapshot.child("name").hasChildren(); // true + * var c = snapshot.child("name/first").hasChildren(); // false + * }); + * ``` + * + * @return True if this snapshot has any children; else false. + */ + hasChildren(): boolean; + + /** + * Returns the number of child properties of this `DataSnapshot`. + * + * @example + * ```javascript + * // Assume we have the following data in the Database: + * { + * "name": { + * "first": "Ada", + * "last": "Lovelace" + * } + * } + * + * var ref = admin.database().ref("users/ada"); + * ref.once("value") + * .then(function(snapshot) { + * var a = snapshot.numChildren(); // 1 ("name") + * var b = snapshot.child("name").numChildren(); // 2 ("first", "last") + * var c = snapshot.child("name/first").numChildren(); // 0 + * }); + * ``` + * + * @return The number of child properties of this `DataSnapshot`. + */ + numChildren(): number; + + /** + * @return A JSON-serializable representation of this object. + */ + toJSON(): Object | null; + + /** + * Extracts a JavaScript value from a `DataSnapshot`. + * + * Depending on the data in a `DataSnapshot`, the `val()` method may return a + * scalar type (string, number, or boolean), an array, or an object. It may also + * return null, indicating that the `DataSnapshot` is empty (contains no data). + * + * @example + * ```javascript + * // Write and then read back a string from the Database. + * ref.set("hello") + * .then(function() { + * return ref.once("value"); + * }) + * .then(function(snapshot) { + * var data = snapshot.val(); // data === "hello" + * }); + * ``` + * + * @example + * ```javascript + * // Write and then read back a JavaScript object from the Database. + * ref.set({ name: "Ada", age: 36 }) + * .then(function() { + * return ref.once("value"); + * }) + * .then(function(snapshot) { + * var data = snapshot.val(); + * // data is { "name": "Ada", "age": 36 } + * // data.name === "Ada" + * // data.age === 36 + * }); + * ``` + * + * @return The DataSnapshot's contents as a JavaScript value (Object, + * Array, string, number, boolean, or `null`). + */ + val(): any; + } + + /** + * The `onDisconnect` class allows you to write or clear data when your client + * disconnects from the Database server. These updates occur whether your + * client disconnects cleanly or not, so you can rely on them to clean up data + * even if a connection is dropped or a client crashes. + * + * The `onDisconnect` class is most commonly used to manage presence in + * applications where it is useful to detect how many clients are connected and + * when other clients disconnect. See + * {@link + * https://firebase.google.com/docs/database/web/offline-capabilities + * Enabling Offline Capabilities in JavaScript} for more information. + * + * To avoid problems when a connection is dropped before the requests can be + * transferred to the Database server, these functions should be called before + * any data is written. + * + * Note that `onDisconnect` operations are only triggered once. If you want an + * operation to occur each time a disconnect occurs, you'll need to re-establish + * the `onDisconnect` operations each time you reconnect. + */ + interface OnDisconnect { + + /** + * Cancels all previously queued `onDisconnect()` set or update events for this + * location and all children. + * + * If a write has been queued for this location via a `set()` or `update()` at a + * parent location, the write at this location will be canceled, though all + * other siblings will still be written. + * + * @example + * ```javascript + * var ref = admin.database().ref("onlineState"); + * ref.onDisconnect().set(false); + * // ... sometime later + * ref.onDisconnect().cancel(); + * ``` + * + * @param onComplete An optional callback function that is + * called when synchronization to the server has completed. The callback + * will be passed a single parameter: null for success, or an Error object + * indicating a failure. + * @return Resolves when synchronization to the server is complete. + */ + cancel(onComplete?: (a: Error | null) => any): Promise; + + /** + * Ensures the data at this location is deleted when the client is disconnected + * (due to closing the browser, navigating to a new page, or network issues). + * + * @param onComplete An optional callback function that is + * called when synchronization to the server has completed. The callback + * will be passed a single parameter: null for success, or an Error object + * indicating a failure. + * @return Resolves when synchronization to the server is complete. + */ + remove(onComplete?: (a: Error | null) => any): Promise; + + /** + * Ensures the data at this location is set to the specified value when the + * client is disconnected (due to closing the browser, navigating to a new page, + * or network issues). + * + * `set()` is especially useful for implementing "presence" systems, where a + * value should be changed or cleared when a user disconnects so that they + * appear "offline" to other users. See + * {@link + * https://firebase.google.com/docs/database/web/offline-capabilities + * Enabling Offline Capabilities in JavaScript} for more information. + * + * Note that `onDisconnect` operations are only triggered once. If you want an + * operation to occur each time a disconnect occurs, you'll need to re-establish + * the `onDisconnect` operations each time. + * + * @example + * ```javascript + * var ref = admin.database().ref("users/ada/status"); + * ref.onDisconnect().set("I disconnected!"); + * ``` + * + * @param value The value to be written to this location on + * disconnect (can be an object, array, string, number, boolean, or null). + * @param onComplete An optional callback function that + * will be called when synchronization to the database server has completed. + * The callback will be passed a single parameter: null for success, or an + * `Error` object indicating a failure. + * @return A promise that resolves when synchronization to the database is complete. + */ + set(value: any, onComplete?: (a: Error | null) => any): Promise; + + /** + * Ensures the data at this location is set to the specified value and priority + * when the client is disconnected (due to closing the browser, navigating to a + * new page, or network issues). + * + * @param value The value to be written to this location on + * disconnect (can be an object, array, string, number, boolean, or null). + * @param priority + * @param onComplete An optional callback function that is + * called when synchronization to the server has completed. The callback + * will be passed a single parameter: null for success, or an Error object + * indicating a failure. + * @return A promise that resolves when synchronization to the database is complete. + */ + setWithPriority( + value: any, + priority: number | string | null, + onComplete?: (a: Error | null) => any + ): Promise; + + /** + * Writes multiple values at this location when the client is disconnected (due + * to closing the browser, navigating to a new page, or network issues). + * + * The `values` argument contains multiple property-value pairs that will be + * written to the Database together. Each child property can either be a simple + * property (for example, "name") or a relative path (for example, "name/first") + * from the current location to the data to update. + * + * As opposed to the `set()` method, `update()` can be use to selectively update + * only the referenced properties at the current location (instead of replacing + * all the child properties at the current location). + * + * See {@link https://firebase.google.com/docs/reference/admin/node/admin.database.Reference#update} + * for examples of using the connected version of `update`. + * + * @example + * ```javascript + * var ref = admin.database().ref("users/ada"); + * ref.update({ + * onlineState: true, + * status: "I'm online." + * }); + * ref.onDisconnect().update({ + * onlineState: false, + * status: "I'm offline." + * }); + * ``` + * + * @param values Object containing multiple values. + * @param onComplete An optional callback function that will + * be called when synchronization to the server has completed. The + * callback will be passed a single parameter: null for success, or an Error + * object indicating a failure. + * @return Resolves when synchronization to the + * Database is complete. + */ + update(values: Object, onComplete?: (a: Error | null) => any): Promise; + } + + type EventType = 'value' | 'child_added' | 'child_changed' | 'child_moved' | 'child_removed'; + + /** + * A `Query` sorts and filters the data at a Database location so only a subset + * of the child data is included. This can be used to order a collection of + * data by some attribute (for example, height of dinosaurs) as well as to + * restrict a large list of items (for example, chat messages) down to a number + * suitable for synchronizing to the client. Queries are created by chaining + * together one or more of the filter methods defined here. + * + * Just as with a `Reference`, you can receive data from a `Query` by using the + * `on()` method. You will only receive events and `DataSnapshot`s for the + * subset of the data that matches your query. + * + * See + * {@link + * https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data + * Sorting and filtering data} for more information. + */ + interface Query { + ref: admin.database.Reference; + + /** + * Creates a `Query` with the specified ending point. + * + * Using `startAt()`, `endAt()`, and `equalTo()` allows you to choose arbitrary + * starting and ending points for your queries. + * + * The ending point is inclusive, so children with exactly the specified value + * will be included in the query. The optional key argument can be used to + * further limit the range of the query. If it is specified, then children that + * have exactly the specified value must also have a key name less than or equal + * to the specified key. + * + * You can read more about `endAt()` in + * {@link + * https://firebase.google.com/docs/database/web/lists-of-data#filtering_data + * Filtering data}. + * + * @example + * ```javascript + * // Find all dinosaurs whose names come before Pterodactyl lexicographically. + * var ref = admin.database().ref("dinosaurs"); + * ref.orderByKey().endAt("pterodactyl").on("child_added", function(snapshot) { + * console.log(snapshot.key); + * }); + * ``` + * + * @param value The value to end at. The argument + * type depends on which `orderBy*()` function was used in this query. + * Specify a value that matches the `orderBy*()` type. When used in + * combination with `orderByKey()`, the value must be a string. + * @param key The child key to end at, among the children with the + * previously specified priority. This argument is only allowed if ordering by + * priority. + * @return A new `Query` object. + */ + endAt(value: number | string | boolean | null, key?: string): admin.database.Query; + + /** + * Creates a `Query` that includes children that match the specified value. + * + * Using `startAt()`, `endAt()`, and `equalTo()` allows us to choose arbitrary + * starting and ending points for our queries. + * + * The optional key argument can be used to further limit the range of the + * query. If it is specified, then children that have exactly the specified + * value must also have exactly the specified key as their key name. This can be + * used to filter result sets with many matches for the same value. + * + * You can read more about `equalTo()` in + * {@link + * https://firebase.google.com/docs/database/web/lists-of-data#filtering_data + * Filtering data}. + * + * @example + * // Find all dinosaurs whose height is exactly 25 meters. + * var ref = admin.database().ref("dinosaurs"); + * ref.orderByChild("height").equalTo(25).on("child_added", function(snapshot) { + * console.log(snapshot.key); + * }); + * + * @param value The value to match for. The + * argument type depends on which `orderBy*()` function was used in this + * query. Specify a value that matches the `orderBy*()` type. When used in + * combination with `orderByKey()`, the value must be a string. + * @param key The child key to start at, among the children with the + * previously specified priority. This argument is only allowed if ordering by + * priority. + * @return A new `Query` object. + */ + equalTo(value: number | string | boolean | null, key?: string): admin.database.Query; + + /** + * Returns whether or not the current and provided queries represent the same + * location, have the same query parameters, and are from the same instance of + * `admin.app.App`. + * + * Two `Reference` objects are equivalent if they represent the same location + * and are from the same instance of `admin.app.App`. + * + * Two `Query` objects are equivalent if they represent the same location, have + * the same query parameters, and are from the same instance of `admin.app.App`. + * Equivalent queries share the same sort order, limits, and starting and + * ending points. + * + * @example + * ```javascript + * var rootRef = admin.database().ref(); + * var usersRef = rootRef.child("users"); + * + * usersRef.isEqual(rootRef); // false + * usersRef.isEqual(rootRef.child("users")); // true + * usersRef.parent.isEqual(rootRef); // true + * ``` + * + * @example + * ```javascript + * var rootRef = admin.database().ref(); + * var usersRef = rootRef.child("users"); + * var usersQuery = usersRef.limitToLast(10); + * + * usersQuery.isEqual(usersRef); // false + * usersQuery.isEqual(usersRef.limitToLast(10)); // true + * usersQuery.isEqual(rootRef.limitToLast(10)); // false + * usersQuery.isEqual(usersRef.orderByKey().limitToLast(10)); // false + * ``` + * + * @param other The query to compare against. + * @return Whether or not the current and provided queries are + * equivalent. + */ + isEqual(other: admin.database.Query | null): boolean; + + /** + * Generates a new `Query` limited to the first specific number of children. + * + * The `limitToFirst()` method is used to set a maximum number of children to be + * synced for a given callback. If we set a limit of 100, we will initially only + * receive up to 100 `child_added` events. If we have fewer than 100 messages + * stored in our Database, a `child_added` event will fire for each message. + * However, if we have over 100 messages, we will only receive a `child_added` + * event for the first 100 ordered messages. As items change, we will receive + * `child_removed` events for each item that drops out of the active list so + * that the total number stays at 100. + * + * You can read more about `limitToFirst()` in + * {@link + * https://firebase.google.com/docs/database/web/lists-of-data#filtering_data + * Filtering data}. + * + * @example + * ```javascript + * // Find the two shortest dinosaurs. + * var ref = admin.database().ref("dinosaurs"); + * ref.orderByChild("height").limitToFirst(2).on("child_added", function(snapshot) { + * // This will be called exactly two times (unless there are less than two + * // dinosaurs in the Database). + * + * // It will also get fired again if one of the first two dinosaurs is + * // removed from the data set, as a new dinosaur will now be the second + * // shortest. + * console.log(snapshot.key); + * }); + * ``` + * + * @param limit The maximum number of nodes to include in this query. + * @return A `Query` object. + */ + limitToFirst(limit: number): admin.database.Query; + + /** + * Generates a new `Query` object limited to the last specific number of + * children. + * + * The `limitToLast()` method is used to set a maximum number of children to be + * synced for a given callback. If we set a limit of 100, we will initially only + * receive up to 100 `child_added` events. If we have fewer than 100 messages + * stored in our Database, a `child_added` event will fire for each message. + * However, if we have over 100 messages, we will only receive a `child_added` + * event for the last 100 ordered messages. As items change, we will receive + * `child_removed` events for each item that drops out of the active list so + * that the total number stays at 100. + * + * You can read more about `limitToLast()` in + * {@link + * https://firebase.google.com/docs/database/web/lists-of-data#filtering_data + * Filtering data}. + * + * @example + * ```javascript + * // Find the two heaviest dinosaurs. + * var ref = admin.database().ref("dinosaurs"); + * ref.orderByChild("weight").limitToLast(2).on("child_added", function(snapshot) { + * // This callback will be triggered exactly two times, unless there are + * // fewer than two dinosaurs stored in the Database. It will also get fired + * // for every new, heavier dinosaur that gets added to the data set. + * console.log(snapshot.key); + * }); + * ``` + * + * @param limit The maximum number of nodes to include in this query. + * @return A `Query` object. + */ + limitToLast(limit: number): admin.database.Query; + + /** + * Detaches a callback previously attached with `on()`. + * + * Detach a callback previously attached with `on()`. Note that if `on()` was + * called multiple times with the same eventType and callback, the callback + * will be called multiple times for each event, and `off()` must be called + * multiple times to remove the callback. Calling `off()` on a parent listener + * will not automatically remove listeners registered on child nodes, `off()` + * must also be called on any child listeners to remove the callback. + * + * If a callback is not specified, all callbacks for the specified eventType + * will be removed. Similarly, if no eventType or callback is specified, all + * callbacks for the `Reference` will be removed. + * + * @example + * ```javascript + * var onValueChange = function(dataSnapshot) { ... }; + * ref.on('value', onValueChange); + * ref.child('meta-data').on('child_added', onChildAdded); + * // Sometime later... + * ref.off('value', onValueChange); + * + * // You must also call off() for any child listeners on ref + * // to cancel those callbacks + * ref.child('meta-data').off('child_added', onValueAdded); + * ``` + * + * @example + * ```javascript + * // Or you can save a line of code by using an inline function + * // and on()'s return value. + * var onValueChange = ref.on('value', function(dataSnapshot) { ... }); + * // Sometime later... + * ref.off('value', onValueChange); + * ``` + * + * @param eventType One of the following strings: "value", + * "child_added", "child_changed", "child_removed", or "child_moved." + * @param callback The callback function that was passed to `on()`. + * @param context The context that was passed to `on()`. + */ + off( + eventType?: admin.database.EventType, + callback?: (a: admin.database.DataSnapshot, b?: string | null) => any, + context?: Object | null + ): void; + + /** + * Listens for data changes at a particular location. + * + * This is the primary way to read data from a Database. Your callback + * will be triggered for the initial data and again whenever the data changes. + * Use `off( )` to stop receiving updates. See + * {@link https://firebase.google.com/docs/database/web/retrieve-data + * Retrieve Data on the Web} + * for more details. + * + *

value event

+ * + * This event will trigger once with the initial data stored at this location, + * and then trigger again each time the data changes. The `DataSnapshot` passed + * to the callback will be for the location at which `on()` was called. It + * won't trigger until the entire contents has been synchronized. If the + * location has no data, it will be triggered with an empty `DataSnapshot` + * (`val()` will return `null`). + * + *

child_added event

+ * + * This event will be triggered once for each initial child at this location, + * and it will be triggered again every time a new child is added. The + * `DataSnapshot` passed into the callback will reflect the data for the + * relevant child. For ordering purposes, it is passed a second argument which + * is a string containing the key of the previous sibling child by sort order + * (or `null` if it is the first child). + * + *

child_removed event

+ * + * This event will be triggered once every time a child is removed. The + * `DataSnapshot` passed into the callback will be the old data for the child + * that was removed. A child will get removed when either: + * + * - a client explicitly calls `remove()` on that child or one of its ancestors + * - a client calls `set(null)` on that child or one of its ancestors + * - that child has all of its children removed + * - there is a query in effect which now filters out the child (because it's + * sort order changed or the max limit was hit) + * + *

child_changed event

+ * + * This event will be triggered when the data stored in a child (or any of its + * descendants) changes. Note that a single `child_changed` event may represent + * multiple changes to the child. The `DataSnapshot` passed to the callback will + * contain the new child contents. For ordering purposes, the callback is also + * passed a second argument which is a string containing the key of the previous + * sibling child by sort order (or `null` if it is the first child). + * + *

child_moved event

+ * + * This event will be triggered when a child's sort order changes such that its + * position relative to its siblings changes. The `DataSnapshot` passed to the + * callback will be for the data of the child that has moved. It is also passed + * a second argument which is a string containing the key of the previous + * sibling child by sort order (or `null` if it is the first child). + * + * @example + * ```javascript + * // Handle a new value. + * ref.on('value', function(dataSnapshot) { + * ... + * }); + * ``` + * + * @example + * ```javascript + * // Handle a new child. + * ref.on('child_added', function(childSnapshot, prevChildKey) { + * ... + * }); + * ``` + * + * @example + * ```javascript + * // Handle child removal. + * ref.on('child_removed', function(oldChildSnapshot) { + * ... + * }); + * ``` + * + * @example + * ```javascript + * // Handle child data changes. + * ref.on('child_changed', function(childSnapshot, prevChildKey) { + * ... + * }); + * ``` + * + * @example + * ```javascript + * // Handle child ordering changes. + * ref.on('child_moved', function(childSnapshot, prevChildKey) { + * ... + * }); + * ``` + * + * @param eventType One of the following strings: "value", + * "child_added", "child_changed", "child_removed", or "child_moved." + * @param callback A callback that fires when the specified event occurs. The callback is + * passed a DataSnapshot. For ordering purposes, "child_added", + * "child_changed", and "child_moved" will also be passed a string containing + * the key of the previous child, by sort order (or `null` if it is the + * first child). + * @param cancelCallbackOrContext An optional + * callback that will be notified if your event subscription is ever canceled + * because your client does not have permission to read this data (or it had + * permission but has now lost it). This callback will be passed an `Error` + * object indicating why the failure occurred. + * @param context If provided, this object will be used as `this` + * when calling your callback(s). + * @return The provided + * callback function is returned unmodified. This is just for convenience if + * you want to pass an inline function to `on()`, but store the callback + * function for later passing to `off()`. + */ + on( + eventType: admin.database.EventType, + callback: (a: admin.database.DataSnapshot | null, b?: string) => any, + cancelCallbackOrContext?: Object | null, + context?: Object | null + ): (a: admin.database.DataSnapshot | null, b?: string) => any; + + /** + * Listens for exactly one event of the specified event type, and then stops + * listening. + * + * This is equivalent to calling `on()`, and then calling `off()` inside the + * callback function. See `on()` for details on the event types. + * + * @example + * ```javascript + * // Basic usage of .once() to read the data located at ref. + * ref.once('value') + * .then(function(dataSnapshot) { + * // handle read data. + * }); + * ``` + * + * @param eventType One of the following strings: "value", + * "child_added", "child_changed", "child_removed", or "child_moved." + * @param successCallback A callback that fires when the specified event occurs. The callback is + * passed a `DataSnapshot`. For ordering purposes, "child_added", + * "child_changed", and "child_moved" will also be passed a string containing + * the key of the previous child by sort order (or `null` if it is the + * first child). + * @param failureCallbackOrContext An optional + * callback that will be notified if your client does not have permission to + * read the data. This callback will be passed an `Error` object indicating + * why the failure occurred. + * @param context If provided, this object will be used as `this` + * when calling your callback(s). + * @return {!Promise} + */ + once( + eventType: admin.database.EventType, + successCallback?: (a: admin.database.DataSnapshot, b?: string) => any, + failureCallbackOrContext?: Object | null, + context?: Object | null + ): Promise; + + /** + * Generates a new `Query` object ordered by the specified child key. + * + * Queries can only order by one key at a time. Calling `orderByChild()` + * multiple times on the same query is an error. + * + * Firebase queries allow you to order your data by any child key on the fly. + * However, if you know in advance what your indexes will be, you can define + * them via the .indexOn rule in your Security Rules for better performance. See + * the {@link https://firebase.google.com/docs/database/security/indexing-data + * .indexOn} rule for more information. + * + * You can read more about `orderByChild()` in + * {@link + * https://firebase.google.com/docs/database/web/lists-of-data#sort_data + * Sort data}. + * + * @example + * ```javascript + * var ref = admin.database().ref("dinosaurs"); + * ref.orderByChild("height").on("child_added", function(snapshot) { + * console.log(snapshot.key + " was " + snapshot.val().height + " m tall"); + * }); + * ``` + * + * @param path + * @return A new `Query` object. + */ + orderByChild(path: string): admin.database.Query; + + /** + * Generates a new `Query` object ordered by key. + * + * Sorts the results of a query by their (ascending) key values. + * + * You can read more about `orderByKey()` in + * {@link + * https://firebase.google.com/docs/database/web/lists-of-data#sort_data + * Sort data}. + * + * @example + * ```javascript + * var ref = admin.database().ref("dinosaurs"); + * ref.orderByKey().on("child_added", function(snapshot) { + * console.log(snapshot.key); + * }); + * ``` + * + * @return A new `Query` object. + */ + orderByKey(): admin.database.Query; + + /** + * Generates a new `Query` object ordered by priority. + * + * Applications need not use priority but can order collections by + * ordinary properties (see + * {@link + * https://firebase.google.com/docs/database/web/lists-of-data#sort_data + * Sort data} for alternatives to priority. + * + * @return A new `Query` object. + */ + orderByPriority(): admin.database.Query; + + /** + * Generates a new `Query` object ordered by value. + * + * If the children of a query are all scalar values (string, number, or + * boolean), you can order the results by their (ascending) values. + * + * You can read more about `orderByValue()` in + * {@link + * https://firebase.google.com/docs/database/web/lists-of-data#sort_data + * Sort data}. + * + * @example + * ```javascript + * var scoresRef = admin.database().ref("scores"); + * scoresRef.orderByValue().limitToLast(3).on("value", function(snapshot) { + * snapshot.forEach(function(data) { + * console.log("The " + data.key + " score is " + data.val()); + * }); + * }); + * ``` + * + * @return A new `Query` object. + */ + orderByValue(): admin.database.Query; + + /** + * Creates a `Query` with the specified starting point. + * + * Using `startAt()`, `endAt()`, and `equalTo()` allows you to choose arbitrary + * starting and ending points for your queries. + * + * The starting point is inclusive, so children with exactly the specified value + * will be included in the query. The optional key argument can be used to + * further limit the range of the query. If it is specified, then children that + * have exactly the specified value must also have a key name greater than or + * equal to the specified key. + * + * You can read more about `startAt()` in + * {@link + * https://firebase.google.com/docs/database/web/lists-of-data#filtering_data + * Filtering data}. + * + * @example + * ```javascript + * // Find all dinosaurs that are at least three meters tall. + * var ref = admin.database().ref("dinosaurs"); + * ref.orderByChild("height").startAt(3).on("child_added", function(snapshot) { + * console.log(snapshot.key) + * }); + * ``` + * + * @param value The value to start at. The argument + * type depends on which `orderBy*()` function was used in this query. + * Specify a value that matches the `orderBy*()` type. When used in + * combination with `orderByKey()`, the value must be a string. + * @param key The child key to start at. This argument is allowed if + * ordering by child, value, or priority. + * @return A new `Query` object. + */ + startAt(value: number | string | boolean | null, key?: string): admin.database.Query; + + /** + * @return A JSON-serializable representation of this object. + */ + toJSON(): Object; + + /** + * Gets the absolute URL for this location. + * + * The `toString()` method returns a URL that is ready to be put into a browser, + * curl command, or a `admin.database().refFromURL()` call. Since all of those + * expect the URL to be url-encoded, `toString()` returns an encoded URL. + * + * Append '.json' to the returned URL when typed into a browser to download + * JSON-formatted data. If the location is secured (that is, not publicly + * readable), you will get a permission-denied error. + * + * @example + * ```javascript + * // Calling toString() on a root Firebase reference returns the URL where its + * // data is stored within the Database: + * var rootRef = admin.database().ref(); + * var rootUrl = rootRef.toString(); + * // rootUrl === "https://sample-app.firebaseio.com/". + * + * // Calling toString() at a deeper Firebase reference returns the URL of that + * // deep path within the Database: + * var adaRef = rootRef.child('users/ada'); + * var adaURL = adaRef.toString(); + * // adaURL === "https://sample-app.firebaseio.com/users/ada". + * ``` + * + * @return The absolute URL for this location. + * @override + */ + toString(): string; + } + + /** + * A `Reference` represents a specific location in your Database and can be used + * for reading or writing data to that Database location. + * + * You can reference the root or child location in your Database by calling + * `admin.database().ref()` or `admin.database().ref("child/path")`. + * + * Writing is done with the `set()` method and reading can be done with the + * `on()` method. See + * {@link + * https://firebase.google.com/docs/database/web/read-and-write + * Read and Write Data on the Web} + */ + interface Reference extends admin.database.Query { + + /** + * The last part of the `Reference`'s path. + * + * For example, `"ada"` is the key for + * `https://.firebaseio.com/users/ada`. + * + * The key of a root `Reference` is `null`. + * + * @example + * ```javascript + * // The key of a root reference is null + * var rootRef = admin.database().ref(); + * var key = rootRef.key; // key === null + * ``` + * + * @example + * ```javascript + * // The key of any non-root reference is the last token in the path + * var adaRef = admin.database().ref("users/ada"); + * var key = adaRef.key; // key === "ada" + * key = adaRef.child("name/last").key; // key === "last" + * ``` + */ + key: string | null; + + /** + * The parent location of a `Reference`. + * + * The parent of a root `Reference` is `null`. + * + * @example + * ```javascript + * // The parent of a root reference is null + * var rootRef = admin.database().ref(); + * parent = rootRef.parent; // parent === null + * ``` + * + * @example + * ```javascript + * // The parent of any non-root reference is the parent location + * var usersRef = admin.database().ref("users"); + * var adaRef = admin.database().ref("users/ada"); + * // usersRef and adaRef.parent represent the same location + * ``` + */ + parent: admin.database.Reference | null; + + /** + * The root `Reference` of the Database. + * + * @example + * ```javascript + * // The root of a root reference is itself + * var rootRef = admin.database().ref(); + * // rootRef and rootRef.root represent the same location + * ``` + * + * @example + * ```javascript + * // The root of any non-root reference is the root location + * var adaRef = admin.database().ref("users/ada"); + * // rootRef and adaRef.root represent the same location + * ``` + */ + root: admin.database.Reference; + path: string; + + /** + * Gets a `Reference` for the location at the specified relative path. + * + * The relative path can either be a simple child name (for example, "ada") or + * a deeper slash-separated path (for example, "ada/name/first"). + * + * @example + * ```javascript + * var usersRef = admin.database().ref('users'); + * var adaRef = usersRef.child('ada'); + * var adaFirstNameRef = adaRef.child('name/first'); + * var path = adaFirstNameRef.toString(); + * // path is now 'https://sample-app.firebaseio.com/users/ada/name/first' + * ``` + * + * @param path A relative path from this location to the desired child + * location. + * @return The specified child location. + */ + child(path: string): admin.database.Reference; + + /** + * Returns an `OnDisconnect` object - see + * {@link + * https://firebase.google.com/docs/database/web/offline-capabilities + * Enabling Offline Capabilities in JavaScript} for more information on how + * to use it. + * + * @return An `OnDisconnect` object . + */ + onDisconnect(): admin.database.OnDisconnect; + + /** + * Generates a new child location using a unique key and returns its + * `Reference`. + * + * This is the most common pattern for adding data to a collection of items. + * + * If you provide a value to `push()`, the value will be written to the + * generated location. If you don't pass a value, nothing will be written to the + * Database and the child will remain empty (but you can use the `Reference` + * elsewhere). + * + * The unique key generated by `push()` are ordered by the current time, so the + * resulting list of items will be chronologically sorted. The keys are also + * designed to be unguessable (they contain 72 random bits of entropy). + * + * + * See + * {@link + * https://firebase.google.com/docs/database/web/lists-of-data#append_to_a_list_of_data + * Append to a list of data} + *
See + * {@link + * https://firebase.googleblog.com/2015/02/the-2120-ways-to-ensure-unique_68.html + * The 2^120 Ways to Ensure Unique Identifiers} + * + * @example + * ```javascript + * var messageListRef = admin.database().ref('message_list'); + * var newMessageRef = messageListRef.push(); + * newMessageRef.set({ + * user_id: 'ada', + * text: 'The Analytical Engine weaves algebraical patterns just as the Jacquard loom weaves flowers and leaves.' + * }); + * // We've appended a new message to the message_list location. + * var path = newMessageRef.toString(); + * // path will be something like + * // 'https://sample-app.firebaseio.com/message_list/-IKo28nwJLH0Nc5XeFmj' + * ``` + * + * @param value Optional value to be written at the generated location. + * @param onComplete Callback called when write to server is + * complete. + * @return Combined `Promise` and + * `Reference`; resolves when write is complete, but can be used immediately + * as the `Reference` to the child location. + */ + push(value?: any, onComplete?: (a: Error | null) => any): admin.database.ThenableReference; + + /** + * Removes the data at this Database location. + * + * Any data at child locations will also be deleted. + * + * The effect of the remove will be visible immediately and the corresponding + * event 'value' will be triggered. Synchronization of the remove to the + * Firebase servers will also be started, and the returned Promise will resolve + * when complete. If provided, the onComplete callback will be called + * asynchronously after synchronization has finished. + * + * @example + * ```javascript + * var adaRef = admin.database().ref('users/ada'); + * adaRef.remove() + * .then(function() { + * console.log("Remove succeeded.") + * }) + * .catch(function(error) { + * console.log("Remove failed: " + error.message) + * }); + * ``` + * + * @param onComplete Callback called when write to server is + * complete. + * @return Resolves when remove on server is complete. + */ + remove(onComplete?: (a: Error | null) => any): Promise; + + /** + * Writes data to this Database location. + * + * This will overwrite any data at this location and all child locations. + * + * The effect of the write will be visible immediately, and the corresponding + * events ("value", "child_added", etc.) will be triggered. Synchronization of + * the data to the Firebase servers will also be started, and the returned + * Promise will resolve when complete. If provided, the `onComplete` callback + * will be called asynchronously after synchronization has finished. + * + * Passing `null` for the new value is equivalent to calling `remove()`; namely, + * all data at this location and all child locations will be deleted. + * + * `set()` will remove any priority stored at this location, so if priority is + * meant to be preserved, you need to use `setWithPriority()` instead. + * + * Note that modifying data with `set()` will cancel any pending transactions + * at that location, so extreme care should be taken if mixing `set()` and + * `transaction()` to modify the same data. + * + * A single `set()` will generate a single "value" event at the location where + * the `set()` was performed. + * + * @example + * ```javascript + * var adaNameRef = admin.database().ref('users/ada/name'); + * adaNameRef.child('first').set('Ada'); + * adaNameRef.child('last').set('Lovelace'); + * // We've written 'Ada' to the Database location storing Ada's first name, + * // and 'Lovelace' to the location storing her last name. + * ``` + * + * @example + * ```javascript + * adaNameRef.set({ first: 'Ada', last: 'Lovelace' }); + * // Exact same effect as the previous example, except we've written + * // Ada's first and last name simultaneously. + * ``` + * + * @example + * ```javascript + * adaNameRef.set({ first: 'Ada', last: 'Lovelace' }) + * .then(function() { + * console.log('Synchronization succeeded'); + * }) + * .catch(function(error) { + * console.log('Synchronization failed'); + * }); + * // Same as the previous example, except we will also log a message + * // when the data has finished synchronizing. + * ``` + * + * @param value The value to be written (string, number, boolean, object, + * array, or null). + * @param onComplete Callback called when write to server is + * complete. + * @return Resolves when write to server is complete. + */ + set(value: any, onComplete?: (a: Error | null) => any): Promise; + + /** + * Sets a priority for the data at this Database location. + * + * Applications need not use priority but can order collections by + * ordinary properties (see + * {@link + * https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data + * Sorting and filtering data}). + * + * @param priority + * @param onComplete + * @return + */ + setPriority( + priority: string | number | null, + onComplete: (a: Error | null) => any + ): Promise; + + /** + * Writes data the Database location. Like `set()` but also specifies the + * priority for that data. + * + * Applications need not use priority but can order collections by + * ordinary properties (see + * {@link + * https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data + * Sorting and filtering data}). + * + * @param newVal + * @param newPriority + * @param onComplete + * @return + */ + setWithPriority( + newVal: any, newPriority: string | number | null, + onComplete?: (a: Error | null) => any + ): Promise; + + /** + * Atomically modifies the data at this location. + * + * Atomically modify the data at this location. Unlike a normal `set()`, which + * just overwrites the data regardless of its previous value, `transaction()` is + * used to modify the existing value to a new value, ensuring there are no + * conflicts with other clients writing to the same location at the same time. + * + * To accomplish this, you pass `transaction()` an update function which is used + * to transform the current value into a new value. If another client writes to + * the location before your new value is successfully written, your update + * function will be called again with the new current value, and the write will + * be retried. This will happen repeatedly until your write succeeds without + * conflict or you abort the transaction by not returning a value from your + * update function. + * + * Note: Modifying data with `set()` will cancel any pending transactions at + * that location, so extreme care should be taken if mixing `set()` and + * `transaction()` to update the same data. + * + * Note: When using transactions with Security and Firebase Rules in place, be + * aware that a client needs `.read` access in addition to `.write` access in + * order to perform a transaction. This is because the client-side nature of + * transactions requires the client to read the data in order to transactionally + * update it. + * + * @example + * ```javascript + * // Increment Ada's rank by 1. + * var adaRankRef = admin.database().ref('users/ada/rank'); + * adaRankRef.transaction(function(currentRank) { + * // If users/ada/rank has never been set, currentRank will be `null`. + * return currentRank + 1; + * }); + * ``` + * + * @example + * ```javascript + * // Try to create a user for ada, but only if the user id 'ada' isn't + * // already taken + * var adaRef = admin.database().ref('users/ada'); + * adaRef.transaction(function(currentData) { + * if (currentData === null) { + * return { name: { first: 'Ada', last: 'Lovelace' } }; + * } else { + * console.log('User ada already exists.'); + * return; // Abort the transaction. + * } + * }, function(error, committed, snapshot) { + * if (error) { + * console.log('Transaction failed abnormally!', error); + * } else if (!committed) { + * console.log('We aborted the transaction (because ada already exists).'); + * } else { + * console.log('User ada added!'); + * } + * console.log("Ada's data: ", snapshot.val()); + * }); + * ``` + * + * @param transactionUpdate A developer-supplied function which + * will be passed the current data stored at this location (as a JavaScript + * object). The function should return the new value it would like written (as + * a JavaScript object). If `undefined` is returned (i.e. you return with no + * arguments) the transaction will be aborted and the data at this location + * will not be modified. + * @param onComplete A callback + * function that will be called when the transaction completes. The callback + * is passed three arguments: a possibly-null `Error`, a `boolean` indicating + * whether the transaction was committed, and a `DataSnapshot` indicating the + * final result. If the transaction failed abnormally, the first argument will + * be an `Error` object indicating the failure cause. If the transaction + * finished normally, but no data was committed because no data was returned + * from `transactionUpdate`, then second argument will be false. If the + * transaction completed and committed data to Firebase, the second argument + * will be true. Regardless, the third argument will be a `DataSnapshot` + * containing the resulting data in this location. + * @param applyLocally By default, events are raised each time the + * transaction update function runs. So if it is run multiple times, you may + * see intermediate states. You can set this to false to suppress these + * intermediate states and instead wait until the transaction has completed + * before events are raised. + * @return Returns a Promise that can optionally be used instead of the `onComplete` + * callback to handle success and failure. + */ + transaction( + transactionUpdate: (a: any) => any, + onComplete?: (a: Error | null, b: boolean, c: admin.database.DataSnapshot | null) => any, + applyLocally?: boolean + ): Promise<{ + committed: boolean; + snapshot: admin.database.DataSnapshot | null; + }>; + + /** + * Writes multiple values to the Database at once. + * + * The `values` argument contains multiple property-value pairs that will be + * written to the Database together. Each child property can either be a simple + * property (for example, "name") or a relative path (for example, + * "name/first") from the current location to the data to update. + * + * As opposed to the `set()` method, `update()` can be use to selectively update + * only the referenced properties at the current location (instead of replacing + * all the child properties at the current location). + * + * The effect of the write will be visible immediately, and the corresponding + * events ('value', 'child_added', etc.) will be triggered. Synchronization of + * the data to the Firebase servers will also be started, and the returned + * Promise will resolve when complete. If provided, the `onComplete` callback + * will be called asynchronously after synchronization has finished. + * + * A single `update()` will generate a single "value" event at the location + * where the `update()` was performed, regardless of how many children were + * modified. + * + * Note that modifying data with `update()` will cancel any pending + * transactions at that location, so extreme care should be taken if mixing + * `update()` and `transaction()` to modify the same data. + * + * Passing `null` to `update()` will remove the data at this location. + * + * See + * {@link + * https://firebase.googleblog.com/2015/09/introducing-multi-location-updates-and_86.html + * Introducing multi-location updates and more}. + * + * @example + * ```javascript + * var adaNameRef = admin.database().ref('users/ada/name'); + * // Modify the 'first' and 'last' properties, but leave other data at + * // adaNameRef unchanged. + * adaNameRef.update({ first: 'Ada', last: 'Lovelace' }); + * ``` + * + * @param values Object containing multiple values. + * @param onComplete Callback called when write to server is + * complete. + * @return Resolves when update on server is complete. + */ + update(values: Object, onComplete?: (a: Error | null) => any): Promise; + } + + /** + * @extends {Reference} + */ + interface ThenableReference extends admin.database.Reference, Promise { } + + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ + function enableLogging(logger?: boolean | ((message: string) => any), persistent?: boolean): any; +} + +/* eslint-disable @typescript-eslint/no-unused-vars */ +export namespace admin.database.ServerValue { + const TIMESTAMP: number; +} diff --git a/src/database/database.ts b/src/database/database.ts index 666447f4ae..3da2a356ba 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -16,7 +16,7 @@ import { AuthorizedHttpClient, HttpRequestConfig, HttpError } from '../utils/api class DatabaseInternals implements FirebaseServiceInternalsInterface { public databases: { - [dbUrl: string]: Database, + [dbUrl: string]: Database; } = {}; /** @@ -77,8 +77,8 @@ export class DatabaseService implements FirebaseServiceInterface { let db: Database = this.INTERNAL.databases[dbUrl]; if (typeof db === 'undefined') { - const rtdb = require('@firebase/database'); - const { version } = require('../../package.json'); + const rtdb = require('@firebase/database'); // eslint-disable-line @typescript-eslint/no-var-requires + const { version } = require('../../package.json'); // eslint-disable-line @typescript-eslint/no-var-requires db = rtdb.initStandalone(this.appInternal, dbUrl, version).instance; const rulesClient = new DatabaseRulesClient(this.app, dbUrl); @@ -183,11 +183,11 @@ class DatabaseRulesClient { if (!validator.isNonEmptyString(source) && !validator.isBuffer(source) && !validator.isNonNullObject(source)) { - const error = new FirebaseDatabaseError({ - code: 'invalid-argument', - message: 'Source must be a non-empty string, Buffer or an object.', - }); - return Promise.reject(error); + const error = new FirebaseDatabaseError({ + code: 'invalid-argument', + message: 'Source must be a non-empty string, Buffer or an object.', + }); + return Promise.reject(error); } const req: HttpRequestConfig = { diff --git a/src/firebase-app.ts b/src/firebase-app.ts index c363afa24f..c272a400d9 100644 --- a/src/firebase-app.ts +++ b/src/firebase-app.ts @@ -191,7 +191,7 @@ export class FirebaseAppInternals { * * @param {function(string)} listener The listener that will be called with each new token. */ - public addAuthTokenListener(listener: (token: string) => void) { + public addAuthTokenListener(listener: (token: string) => void): void { this.tokenListeners_.push(listener); if (this.cachedToken_) { listener(this.cachedToken_.accessToken); @@ -203,7 +203,7 @@ export class FirebaseAppInternals { * * @param {function(string)} listener The listener to remove. */ - public removeAuthTokenListener(listener: (token: string) => void) { + public removeAuthTokenListener(listener: (token: string) => void): void { this.tokenListeners_ = this.tokenListeners_.filter((other) => other !== listener); } @@ -227,7 +227,7 @@ export class FirebaseAppInternals { private setTokenRefreshTimeout(delayInMilliseconds: number, numRetries: number): void { this.tokenRefreshTimeout_ = setTimeout(() => { this.getToken(/* forceRefresh */ true) - .catch((error) => { + .catch(() => { // Ignore the error since this might just be an intermittent failure. If we really cannot // refresh the token, an error will be logged once the existing token expires and we try // to fetch a fresh one. diff --git a/src/firebase-namespace.ts b/src/firebase-namespace.ts index dea6756d0b..21d3a3949c 100644 --- a/src/firebase-namespace.ts +++ b/src/firebase-namespace.ts @@ -46,7 +46,7 @@ const DEFAULT_APP_NAME = '[DEFAULT]'; * If the environment variable contains a string that starts with '{' it will be parsed as JSON, * otherwise it will be assumed to be pointing to a file. */ -export const FIREBASE_CONFIG_VAR: string = 'FIREBASE_CONFIG'; +export const FIREBASE_CONFIG_VAR = 'FIREBASE_CONFIG'; let globalAppDefaultCred: Credential; @@ -183,10 +183,11 @@ export class FirebaseNamespaceInternals { * @param {AppHook} [appHook] Optional callback that handles app-related events like app creation and deletion. * @return {FirebaseServiceNamespace} The Firebase service's namespace. */ - public registerService(serviceName: string, - createService: FirebaseServiceFactory, - serviceProperties?: object, - appHook?: AppHook): FirebaseServiceNamespace { + public registerService( + serviceName: string, + createService: FirebaseServiceFactory, + serviceProperties?: object, + appHook?: AppHook): FirebaseServiceNamespace { let errorMessage; if (typeof serviceName === 'undefined') { errorMessage = `No service name provided. Service name must be a non-empty string.`; @@ -208,11 +209,9 @@ export class FirebaseNamespaceInternals { this.appHooks_[serviceName] = appHook; } - let serviceNamespace: FirebaseServiceNamespace; - // The service namespace is an accessor function which takes a FirebaseApp instance // or uses the default app if no FirebaseApp instance is provided - serviceNamespace = (appArg?: FirebaseApp) => { + const serviceNamespace: FirebaseServiceNamespace = (appArg?: FirebaseApp) => { if (typeof appArg === 'undefined') { appArg = this.app(); } @@ -239,7 +238,7 @@ export class FirebaseNamespaceInternals { * @param {FirebaseApp} app The FirebaseApp instance whose app hooks to call. * @param {string} eventName The event name representing which app hooks to call. */ - private callAppHooks_(app: FirebaseApp, eventName: string) { + private callAppHooks_(app: FirebaseApp, eventName: string): void { Object.keys(this.serviceFactories).forEach((serviceName) => { if (this.appHooks_[serviceName]) { this.appHooks_[serviceName](eventName, app); @@ -342,6 +341,8 @@ export class FirebaseNamespace { const fn: FirebaseServiceNamespace = (app?: FirebaseApp) => { return this.ensureApp(app).database(); }; + + // eslint-disable-next-line @typescript-eslint/no-var-requires return Object.assign(fn, require('@firebase/database')); } @@ -377,6 +378,8 @@ export class FirebaseNamespace { let fn: FirebaseServiceNamespace = (app?: FirebaseApp) => { return this.ensureApp(app).firestore(); }; + + // eslint-disable-next-line @typescript-eslint/no-var-requires const firestore = require('@google-cloud/firestore'); fn = Object.assign(fn, firestore.Firestore); diff --git a/src/firestore/firestore.ts b/src/firestore/firestore.ts index c733321da7..89e7fe2771 100644 --- a/src/firestore/firestore.ts +++ b/src/firestore/firestore.ts @@ -73,12 +73,13 @@ export function getFirestoreOptions(app: FirebaseApp): Settings { const projectId: string | null = utils.getExplicitProjectId(app); const credential = app.options.credential; + // eslint-disable-next-line @typescript-eslint/no-var-requires const { version: firebaseVersion } = require('../../package.json'); if (credential instanceof ServiceAccountCredential) { return { credentials: { - private_key: credential.privateKey, - client_email: credential.clientEmail, + private_key: credential.privateKey, // eslint-disable-line @typescript-eslint/camelcase + client_email: credential.clientEmail, // eslint-disable-line @typescript-eslint/camelcase }, // When the SDK is initialized with ServiceAccountCredentials an explicit projectId is // guaranteed to be available. diff --git a/src/index.d.ts b/src/index.d.ts index 6fa677d1df..06b09e297c 100755 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -18,6 +18,15 @@ import { Bucket } from '@google-cloud/storage'; import * as _firestore from '@google-cloud/firestore'; import { Agent } from 'http'; +import * as _auth from './auth'; +import * as _database from './database'; +import * as _messaging from './messaging'; +import * as _instanceId from './instance-id'; +import * as _projectManagement from './project-management'; +import * as _securityRules from './security-rules'; + +/* eslint-disable @typescript-eslint/ban-types */ + /** * `admin` is a global namespace from which all Firebase Admin * services are accessed. @@ -179,8 +188,8 @@ declare namespace admin { httpAgent?: Agent; } - var SDK_VERSION: string; - var apps: (admin.app.App | null)[]; + const SDK_VERSION: string; + const apps: (admin.app.App | null)[]; function app(name?: string): admin.app.App; @@ -271,38 +280,6 @@ declare namespace admin { */ function messaging(app?: admin.app.App): admin.messaging.Messaging; - /** - * Gets the {@link admin.machineLearning.MachineLearning `MachineLearning`} service for the - * default app or a given app. - * - * `admin.machineLearning()` can be called with no arguments to access the - * default app's {@link admin.machineLearning.MachineLearning - * `MachineLearning`} service or as `admin.machineLearning(app)` to access - * the {@link admin.machineLearning.MachineLearning `MachineLearning`} - * service associated with a specific app. - * - * @example - * ```javascript - * // Get the MachineLearning service for the default app - * var defaultMachineLearning = admin.machineLearning(); - * ``` - * - * @example - * ```javascript - * // Get the MachineLearning service for a given app - * var otherMachineLearning = admin.machineLearning(otherApp); - * ``` - * - * @param app Optional app whose `MachineLearning` service to - * return. If not provided, the default `MachineLearning` service - * will be returned. - * - * @return The default `MachineLearning` service if no app is provided or the - * `MachineLearning` service associated with the provided app. - */ - function machineLearning(app?: admin.app.App): - admin.machineLearning.MachineLearning; - /** * Gets the {@link admin.storage.Storage `Storage`} service for the * default app or a given app. @@ -427,6 +404,37 @@ declare namespace admin { */ function securityRules(app?: admin.app.App): admin.securityRules.SecurityRules; + /** + * Gets the {@link admin.machineLearning.MachineLearning `MachineLearning`} service for the + * default app or a given app. + * + * `admin.machineLearning()` can be called with no arguments to access the + * default app's {@link admin.machineLearning.MachineLearning + * `MachineLearning`} service or as `admin.machineLearning(app)` to access + * the {@link admin.machineLearning.MachineLearning `MachineLearning`} + * service associated with a specific app. + * + * @example + * ```javascript + * // Get the MachineLearning service for the default app + * var defaultMachineLearning = admin.machineLearning(); + * ``` + * + * @example + * ```javascript + * // Get the MachineLearning service for a given app + * var otherMachineLearning = admin.machineLearning(otherApp); + * ``` + * + * @param app Optional app whose `MachineLearning` service to + * return. If not provided, the default `MachineLearning` service + * will be returned. + * + * @return The default `MachineLearning` service if no app is provided or the + * `MachineLearning` service associated with the provided app. + */ + function machineLearning(app?: admin.app.App): admin.machineLearning.MachineLearning; + function initializeApp(options?: admin.AppOptions, name?: string): admin.app.App; } @@ -485,8 +493,8 @@ declare namespace admin.app { database(url?: string): admin.database.Database; firestore(): admin.firestore.Firestore; instanceId(): admin.instanceId.InstanceId; - messaging(): admin.messaging.Messaging; machineLearning(): admin.machineLearning.MachineLearning; + messaging(): admin.messaging.Messaging; projectManagement(): admin.projectManagement.ProjectManagement; securityRules(): admin.securityRules.SecurityRules; storage(): admin.storage.Storage; @@ -513,5527 +521,508 @@ declare namespace admin.app { } declare namespace admin.auth { + export import UserMetadata = _auth.admin.auth.UserMetadata; + export import UserInfo = _auth.admin.auth.UserInfo; + export import UserRecord = _auth.admin.auth.UserRecord; + export import UpdateRequest = _auth.admin.auth.UpdateRequest; + export import CreateRequest = _auth.admin.auth.CreateRequest; + export import DecodedIdToken = _auth.admin.auth.DecodedIdToken; + export import ListUsersResult = _auth.admin.auth.ListUsersResult; + export import HashAlgorithmType = _auth.admin.auth.HashAlgorithmType; + export import UserImportOptions = _auth.admin.auth.UserImportOptions; + export import UserImportResult = _auth.admin.auth.UserImportResult; + export import UserImportRecord = _auth.admin.auth.UserImportRecord; + export import SessionCookieOptions = _auth.admin.auth.SessionCookieOptions; + export import ActionCodeSettings = _auth.admin.auth.ActionCodeSettings; + export import Tenant = _auth.admin.auth.Tenant; + export import UpdateTenantRequest = _auth.admin.auth.UpdateTenantRequest; + export import CreateTenantRequest = _auth.admin.auth.CreateTenantRequest; + export import ListTenantsResult = _auth.admin.auth.ListTenantsResult; + export import AuthProviderConfigFilter = _auth.admin.auth.AuthProviderConfigFilter; + export import AuthProviderConfig = _auth.admin.auth.AuthProviderConfig; + export import SAMLAuthProviderConfig = _auth.admin.auth.SAMLAuthProviderConfig; + export import OIDCAuthProviderConfig = _auth.admin.auth.OIDCAuthProviderConfig; + export import SAMLUpdateAuthProviderRequest = _auth.admin.auth.SAMLUpdateAuthProviderRequest; + export import OIDCUpdateAuthProviderRequest = _auth.admin.auth.OIDCUpdateAuthProviderRequest; + export import ListProviderConfigResults = _auth.admin.auth.ListProviderConfigResults; + export import UpdateAuthProviderRequest = _auth.admin.auth.UpdateAuthProviderRequest; + export import BaseAuth = _auth.admin.auth.BaseAuth; + export import TenantAwareAuth = _auth.admin.auth.TenantAwareAuth; + export import Auth = _auth.admin.auth.Auth; + export import TenantManager = _auth.admin.auth.TenantManager; + export import MultiFactorInfo = _auth.admin.auth.MultiFactorInfo; + export import PhoneMultiFactorInfo = _auth.admin.auth.PhoneMultiFactorInfo; + export import CreateMultiFactorInfoRequest = _auth.admin.auth.CreateMultiFactorInfoRequest; + export import CreatePhoneMultiFactorInfoRequest = _auth.admin.auth.CreatePhoneMultiFactorInfoRequest; + export import UpdateMultiFactorInfoRequest = _auth.admin.auth.UpdateMultiFactorInfoRequest; + export import UpdatePhoneMultiFactorInfoRequest = _auth.admin.auth.UpdatePhoneMultiFactorInfoRequest; + export import MultiFactorCreateSettings = _auth.admin.auth.MultiFactorCreateSettings; + export import MultiFactorUpdateSettings = _auth.admin.auth.MultiFactorUpdateSettings; +} - /** - * Interface representing a user's metadata. - */ - interface UserMetadata { +declare namespace admin.credential { - /** - * The date the user last signed in, formatted as a UTC string. - */ - lastSignInTime: string; + /** + * Interface that provides Google OAuth2 access tokens used to authenticate + * with Firebase services. + * + * In most cases, you will not need to implement this yourself and can instead + * use the default implementations provided by + * {@link admin.credential `admin.credential`}. + */ + interface Credential { /** - * The date the user was created, formatted as a UTC string. + * Returns a Google OAuth2 access token object used to authenticate with + * Firebase services. * + * This object contains the following properties: + * * `access_token` (`string`): The actual Google OAuth2 access token. + * * `expires_in` (`number`): The number of seconds from when the token was + * issued that it expires. + * + * @return A Google OAuth2 access token object. */ - creationTime: string; - - /** - * @return A JSON-serializable representation of this object. - */ - toJSON(): Object; + getAccessToken(): Promise; } + /** - * Interface representing a user's info from a third-party identity provider - * such as Google or Facebook. + * Returns a credential created from the + * {@link + * https://developers.google.com/identity/protocols/application-default-credentials + * Google Application Default Credentials} + * that grants admin access to Firebase services. This credential can be used + * in the call to + * {@link + * https://firebase.google.com/docs/reference/admin/node/admin#.initializeApp + * `admin.initializeApp()`}. + * + * Google Application Default Credentials are available on any Google + * infrastructure, such as Google App Engine and Google Compute Engine. + * + * See + * {@link + * https://firebase.google.com/docs/admin/setup#initialize_the_sdk + * Initialize the SDK} + * for more details. + * + * @example + * ```javascript + * admin.initializeApp({ + * credential: admin.credential.applicationDefault(), + * databaseURL: "https://.firebaseio.com" + * }); + * ``` + * + * @param {!Object=} httpAgent Optional [HTTP Agent](https://nodejs.org/api/http.html#http_class_http_agent) + * to be used when retrieving access tokens from Google token servers. + * + * @return {!admin.credential.Credential} A credential authenticated via Google + * Application Default Credentials that can be used to initialize an app. */ - interface UserInfo { - - /** - * The user identifier for the linked provider. - */ - uid: string; - - /** - * The display name for the linked provider. - */ - displayName: string; + function applicationDefault(httpAgent?: Agent): admin.credential.Credential; - /** - * The email for the linked provider. - */ - email: string; + /** + * Returns a credential created from the provided service account that grants + * admin access to Firebase services. This credential can be used in the call + * to + * {@link + * https://firebase.google.com/docs/reference/admin/node/admin#.initializeApp + * `admin.initializeApp()`}. + * + * See + * {@link + * https://firebase.google.com/docs/admin/setup#initialize_the_sdk + * Initialize the SDK} + * for more details. + * + * @example + * ```javascript + * // Providing a path to a service account key JSON file + * var serviceAccount = require("path/to/serviceAccountKey.json"); + * admin.initializeApp({ + * credential: admin.credential.cert(serviceAccount), + * databaseURL: "https://.firebaseio.com" + * }); + * ``` + * + * @example + * ```javascript + * // Providing a service account object inline + * admin.initializeApp({ + * credential: admin.credential.cert({ + * projectId: "", + * clientEmail: "foo@.iam.gserviceaccount.com", + * privateKey: "-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----\n" + * }), + * databaseURL: "https://.firebaseio.com" + * }); + * ``` + * + * @param serviceAccountPathOrObject The path to a service + * account key JSON file or an object representing a service account key. + * @param httpAgent Optional [HTTP Agent](https://nodejs.org/api/http.html#http_class_http_agent) + * to be used when retrieving access tokens from Google token servers. + * + * @return A credential authenticated via the + * provided service account that can be used to initialize an app. + */ + function cert(serviceAccountPathOrObject: string | admin.ServiceAccount, httpAgent?: Agent): admin.credential.Credential; - /** - * The phone number for the linked provider. - */ - phoneNumber: string; + /** + * Returns a credential created from the provided refresh token that grants + * admin access to Firebase services. This credential can be used in the call + * to + * {@link + * https://firebase.google.com/docs/reference/admin/node/admin#.initializeApp + * `admin.initializeApp()`}. + * + * See + * {@link + * https://firebase.google.com/docs/admin/setup#initialize_the_sdk + * Initialize the SDK} + * for more details. + * + * @example + * ```javascript + * // Providing a path to a refresh token JSON file + * var refreshToken = require("path/to/refreshToken.json"); + * admin.initializeApp({ + * credential: admin.credential.refreshToken(refreshToken), + * databaseURL: "https://.firebaseio.com" + * }); + * ``` + * + * @param refreshTokenPathOrObject The path to a Google + * OAuth2 refresh token JSON file or an object representing a Google OAuth2 + * refresh token. + * @param httpAgent Optional [HTTP Agent](https://nodejs.org/api/http.html#http_class_http_agent) + * to be used when retrieving access tokens from Google token servers. + * + * @return A credential authenticated via the + * provided service account that can be used to initialize an app. + */ + function refreshToken(refreshTokenPathOrObject: string | Object, httpAgent?: Agent): admin.credential.Credential; +} - /** - * The photo URL for the linked provider. - */ - photoURL: string; +declare namespace admin.database { + export import Database = _database.admin.database.Database; + export import DataSnapshot = _database.admin.database.DataSnapshot; + export import OnDisconnect = _database.admin.database.OnDisconnect; + export import EventType = _database.admin.database.EventType; + export import Query = _database.admin.database.Query; + export import Reference = _database.admin.database.Reference; + export import ThenableReference = _database.admin.database.ThenableReference; + export import enableLogging = _database.admin.database.enableLogging; + export import ServerValue = _database.admin.database.ServerValue; +} - /** - * The linked provider ID (for example, "google.com" for the Google provider). - */ - providerId: string; +declare namespace admin.messaging { + export import Message = _messaging.admin.messaging.Message; + export import MulticastMessage = _messaging.admin.messaging.MulticastMessage; + export import AndroidConfig = _messaging.admin.messaging.AndroidConfig; + export import AndroidNotification = _messaging.admin.messaging.AndroidNotification; + export import LightSettings = _messaging.admin.messaging.LightSettings; + export import AndroidFcmOptions = _messaging.admin.messaging.AndroidFcmOptions; + export import ApnsConfig = _messaging.admin.messaging.ApnsConfig; + export import ApnsPayload = _messaging.admin.messaging.ApnsPayload; + export import Aps = _messaging.admin.messaging.Aps; + export import ApsAlert = _messaging.admin.messaging.ApsAlert; + export import CriticalSound = _messaging.admin.messaging.CriticalSound; + export import ApnsFcmOptions = _messaging.admin.messaging.ApnsFcmOptions; + export import FcmOptions = _messaging.admin.messaging.FcmOptions; + export import Notification = _messaging.admin.messaging.Notification; + export import WebpushConfig = _messaging.admin.messaging.WebpushConfig; + export import WebpushFcmOptions = _messaging.admin.messaging.WebpushFcmOptions; + export import WebpushNotification = _messaging.admin.messaging.WebpushNotification; + export import MessagingTopicManagementResponse = _messaging.admin.messaging.MessagingTopicManagementResponse; + export import BatchResponse = _messaging.admin.messaging.BatchResponse; + export import SendResponse = _messaging.admin.messaging.SendResponse; + export import Messaging = _messaging.admin.messaging.Messaging; + + // Legacy API types. + export import DataMessagePayload = _messaging.admin.messaging.DataMessagePayload; + export import NotificationMessagePayload = _messaging.admin.messaging.NotificationMessagePayload; + export import MessagingPayload = _messaging.admin.messaging.MessagingPayload; + export import MessagingOptions = _messaging.admin.messaging.MessagingOptions; + export import MessagingDevicesResponse = _messaging.admin.messaging.MessagingDevicesResponse; + export import MessagingDeviceResult = _messaging.admin.messaging.MessagingDeviceResult; + export import MessagingDeviceGroupResponse = _messaging.admin.messaging.MessagingDeviceGroupResponse; + export import MessagingTopicResponse = _messaging.admin.messaging.MessagingTopicResponse; + export import MessagingConditionResponse = _messaging.admin.messaging.MessagingConditionResponse; +} - /** - * @return A JSON-serializable representation of this object. - */ - toJSON(): Object; - } +declare namespace admin.storage { /** - * Interface representing a user. + * The default `Storage` service if no + * app is provided or the `Storage` service associated with the provided + * app. */ - interface UserRecord { - - /** - * The user's `uid`. - */ - uid: string; - + interface Storage { /** - * The user's primary email, if set. + * Optional app whose `Storage` service to + * return. If not provided, the default `Storage` service will be returned. */ - email?: string; - + app: admin.app.App; /** - * Whether or not the user's primary email is verified. + * @returns A [Bucket](https://cloud.google.com/nodejs/docs/reference/storage/latest/Bucket) + * instance as defined in the `@google-cloud/storage` package. */ - emailVerified: boolean; + bucket(name?: string): Bucket; + } +} - /** - * The user's display name. - */ - displayName?: string; - - /** - * The user's primary phone number, if set. - */ - phoneNumber?: string; - - /** - * The user's photo URL. - */ - photoURL?: string; - - /** - * Whether or not the user is disabled: `true` for disabled; `false` for - * enabled. - */ - disabled: boolean; - - /** - * Additional metadata about the user. - */ - metadata: admin.auth.UserMetadata; - - /** - * An array of providers (for example, Google, Facebook) linked to the user. - */ - providerData: admin.auth.UserInfo[]; - - /** - * The user's hashed password (base64-encoded), only if Firebase Auth hashing - * algorithm (SCRYPT) is used. If a different hashing algorithm had been used - * when uploading this user, as is typical when migrating from another Auth - * system, this will be an empty string. If no password is set, this is - * null. This is only available when the user is obtained from - * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#listUsers `listUsers()`}. - * - */ - passwordHash?: string; - - /** - * The user's password salt (base64-encoded), only if Firebase Auth hashing - * algorithm (SCRYPT) is used. If a different hashing algorithm had been used to - * upload this user, typical when migrating from another Auth system, this will - * be an empty string. If no password is set, this is null. This is only - * available when the user is obtained from - * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#listUsers `listUsers()`}. - * - */ - passwordSalt?: string; - - /** - * The user's custom claims object if available, typically used to define - * user roles and propagated to an authenticated user's ID token. - * This is set via - * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#setCustomUserClaims `setCustomUserClaims()`} - */ - customClaims?: Object; - - /** - * The date the user's tokens are valid after, formatted as a UTC string. - * This is updated every time the user's refresh token are revoked either - * from the {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#revokeRefreshTokens `revokeRefreshTokens()`} - * API or from the Firebase Auth backend on big account changes (password - * resets, password or email updates, etc). - */ - tokensValidAfterTime?: string; - - /** - * The ID of the tenant the user belongs to, if available. - */ - tenantId?: string | null; - - /** - * @return A JSON-serializable representation of this object. - */ - toJSON(): Object; - } - - /** - * Interface representing the properties to update on the provided user. - */ - interface UpdateRequest { - - /** - * Whether or not the user is disabled: `true` for disabled; - * `false` for enabled. - */ - disabled?: boolean; - - /** - * The user's display name. - */ - displayName?: string | null; - - /** - * The user's primary email. - */ - email?: string; - - /** - * Whether or not the user's primary email is verified. - */ - emailVerified?: boolean; - - /** - * The user's unhashed password. - */ - password?: string; - - /** - * The user's primary phone number. - */ - phoneNumber?: string | null; - - /** - * The user's photo URL. - */ - photoURL?: string | null; - } - - /** - * Interface representing the properties to set on a new user record to be - * created. - */ - interface CreateRequest extends UpdateRequest { - - /** - * The user's `uid`. - */ - uid?: string; - } - - /** - * Interface representing a decoded Firebase ID token, returned from the - * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#verifyIdToken `verifyIdToken()`} method. - * - * Firebase ID tokens are OpenID Connect spec-compliant JSON Web Tokens (JWTs). - * See the - * [ID Token section of the OpenID Connect spec](http://openid.net/specs/openid-connect-core-1_0.html#IDToken) - * for more information about the specific properties below. - */ - interface DecodedIdToken { - - /** - * The audience for which this token is intended. - * - * This value is a string equal to your Firebase project ID, the unique - * identifier for your Firebase project, which can be found in [your project's - * settings](https://console.firebase.google.com/project/_/settings/general/android:com.random.android). - */ - aud: string; - - /** - * Time, in seconds since the Unix epoch, when the end-user authentication - * occurred. - * - * This value is not set when this particular ID token was created, but when the - * user initially logged in to this session. In a single session, the Firebase - * SDKs will refresh a user's ID tokens every hour. Each ID token will have a - * different [`iat`](#iat) value, but the same `auth_time` value. - */ - auth_time: number; - - /** - * The ID token's expiration time, in seconds since the Unix epoch. That is, the - * time at which this ID token expires and should no longer be considered valid. - * - * The Firebase SDKs transparently refresh ID tokens every hour, issuing a new - * ID token with up to a one hour expiration. - */ - exp: number; - - /** - * Information about the sign in event, including which sign in provider was - * used and provider-specific identity details. - * - * This data is provided by the Firebase Authentication service and is a - * reserved claim in the ID token. - */ - firebase: { - - /** - * Provider-specific identity details corresponding - * to the provider used to sign in the user. - */ - identities: { - [key: string]: any; - }; - - /** - * The ID of the provider used to sign in the user. - * One of `"anonymous"`, `"password"`, `"facebook.com"`, `"github.com"`, - * `"google.com"`, `"twitter.com"`, or `"custom"`. - */ - sign_in_provider: string; - - /** - * The ID of the tenant the user belongs to, if available. - */ - tenant?: string; - [key: string]: any; - }; - - /** - * The ID token's issued-at time, in seconds since the Unix epoch. That is, the - * time at which this ID token was issued and should start to be considered - * valid. - * - * The Firebase SDKs transparently refresh ID tokens every hour, issuing a new - * ID token with a new issued-at time. If you want to get the time at which the - * user session corresponding to the ID token initially occurred, see the - * [`auth_time`](#auth_time) property. - */ - iat: number; - - /** - * The issuer identifier for the issuer of the response. - * - * This value is a URL with the format - * `https://securetoken.google.com/`, where `` is the - * same project ID specified in the [`aud`](#aud) property. - */ - iss: string; - - /** - * The `uid` corresponding to the user who the ID token belonged to. - * - * As a convenience, this value is copied over to the [`uid`](#uid) property. - */ - sub: string; - - /** - * The `uid` corresponding to the user who the ID token belonged to. - * - * This value is not actually in the JWT token claims itself. It is added as a - * convenience, and is set as the value of the [`sub`](#sub) property. - */ - uid: string; - [key: string]: any; - } - - /** - * Interface representing the object returned from a - * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#listUsers `listUsers()`} operation. Contains the list - * of users for the current batch and the next page token if available. - */ - interface ListUsersResult { - - /** - * The list of {@link admin.auth.UserRecord `UserRecord`} objects for the - * current downloaded batch. - */ - users: admin.auth.UserRecord[]; - - /** - * The next page token if available. This is needed for the next batch download. - */ - pageToken?: string; - } - - type HashAlgorithmType = 'SCRYPT' | 'STANDARD_SCRYPT' | 'HMAC_SHA512' | - 'HMAC_SHA256' | 'HMAC_SHA1' | 'HMAC_MD5' | 'MD5' | 'PBKDF_SHA1' | 'BCRYPT' | - 'PBKDF2_SHA256' | 'SHA512' | 'SHA256' | 'SHA1'; - - /** - * Interface representing the user import options needed for - * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#importUsers `importUsers()`} method. This is used to - * provide the password hashing algorithm information. - */ - interface UserImportOptions { - - /** - * The password hashing information. - */ - hash: { - - /** - * The password hashing algorithm identifier. The following algorithm - * identifiers are supported: - * `SCRYPT`, `STANDARD_SCRYPT`, `HMAC_SHA512`, `HMAC_SHA256`, `HMAC_SHA1`, - * `HMAC_MD5`, `MD5`, `PBKDF_SHA1`, `BCRYPT`, `PBKDF2_SHA256`, `SHA512`, - * `SHA256` and `SHA1`. - */ - algorithm: HashAlgorithmType; - - /** - * The signing key used in the hash algorithm in buffer bytes. - * Required by hashing algorithms `SCRYPT`, `HMAC_SHA512`, `HMAC_SHA256`, - * `HAMC_SHA1` and `HMAC_MD5`. - */ - key?: Buffer; - - /** - * The salt separator in buffer bytes which is appended to salt when - * verifying a password. This is only used by the `SCRYPT` algorithm. - */ - saltSeparator?: Buffer; - - /** - * The number of rounds for hashing calculation. - * Required for `SCRYPT`, `MD5`, `SHA512`, `SHA256`, `SHA1`, `PBKDF_SHA1` and - * `PBKDF2_SHA256`. - */ - rounds?: number; - - /** - * The memory cost required for `SCRYPT` algorithm, or the CPU/memory cost. - * Required for `STANDARD_SCRYPT` algorithm. - */ - memoryCost?: number; - - /** - * The parallelization of the hashing algorithm. Required for the - * `STANDARD_SCRYPT` algorithm. - */ - parallelization?: number; - - /** - * The block size (normally 8) of the hashing algorithm. Required for the - * `STANDARD_SCRYPT` algorithm. - */ - blockSize?: number; - /** - * The derived key length of the hashing algorithm. Required for the - * `STANDARD_SCRYPT` algorithm. - */ - derivedKeyLength?: number; - }; - } - - /** - * Interface representing the response from the - * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#importUsers `importUsers()`} method for batch - * importing users to Firebase Auth. - */ - interface UserImportResult { - - /** - * The number of user records that failed to import to Firebase Auth. - */ - failureCount: number; - - /** - * The number of user records that successfully imported to Firebase Auth. - */ - successCount: number; - - /** - * An array of errors corresponding to the provided users to import. The - * length of this array is equal to [`failureCount`](#failureCount). - */ - errors: admin.FirebaseArrayIndexError[]; - } - - /** - * Interface representing a user to import to Firebase Auth via the - * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#importUsers `importUsers()`} method. - */ - interface UserImportRecord { - - /** - * The user's `uid`. - */ - uid: string; - - /** - * The user's primary email, if set. - */ - email?: string; - - /** - * Whether or not the user's primary email is verified. - */ - emailVerified: boolean; - - /** - * The user's display name. - */ - displayName?: string; - - /** - * The user's primary phone number, if set. - */ - phoneNumber?: string; - - /** - * The user's photo URL. - */ - photoURL?: string; - - /** - * Whether or not the user is disabled: `true` for disabled; `false` for - * enabled. - */ - disabled: boolean; - - /** - * Additional metadata about the user. - */ - metadata: admin.auth.UserMetadata; - - /** - * An array of providers (for example, Google, Facebook) linked to the user. - */ - providerData?: admin.auth.UserInfo[]; - - /** - * The user's custom claims object if available, typically used to define - * user roles and propagated to an authenticated user's ID token. - */ - customClaims?: Object; - - /** - * The buffer of bytes representing the user's hashed password. - * When a user is to be imported with a password hash, - * {@link admin.auth.UserImportOptions `UserImportOptions`} are required to be - * specified to identify the hashing algorithm used to generate this hash. - */ - passwordHash?: Buffer; - - /** - * The buffer of bytes representing the user's password salt. - */ - passwordSalt?: Buffer; - - /** - * The identifier of the tenant where user is to be imported to. - * When not provided in an `admin.auth.Auth` context, the user is uploaded to - * the default parent project. - * When not provided in an `admin.auth.TenantAwareAuth` context, the user is uploaded - * to the tenant corresponding to that `TenantAwareAuth` instance's tenant ID. - */ - tenantId?: string | null; - } - - /** - * Interface representing the session cookie options needed for the - * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#createSessionCookie `createSessionCookie()`} method. - */ - interface SessionCookieOptions { - - /** - * The session cookie custom expiration in milliseconds. The minimum allowed is - * 5 minutes and the maxium allowed is 2 weeks. - */ - expiresIn: number; - } - - /** - * This is the interface that defines the required continue/state URL with - * optional Android and iOS bundle identifiers. - */ - interface ActionCodeSettings { - - /** - * Defines the link continue/state URL, which has different meanings in - * different contexts: - *
    - *
  • When the link is handled in the web action widgets, this is the deep - * link in the `continueUrl` query parameter.
  • - *
  • When the link is handled in the app directly, this is the `continueUrl` - * query parameter in the deep link of the Dynamic Link.
  • - *
- */ - url: string; - - /** - * Whether to open the link via a mobile app or a browser. - * The default is false. When set to true, the action code link is sent - * as a Universal Link or Android App Link and is opened by the app if - * installed. In the false case, the code is sent to the web widget first - * and then redirects to the app if installed. - */ - handleCodeInApp?: boolean; - - /** - * Defines the iOS bundle ID. This will try to open the link in an iOS app if it - * is installed. - */ - iOS?: { - - /** - * Defines the required iOS bundle ID of the app where the link should be - * handled if the application is already installed on the device. - */ - bundleId: string; - }; - - /** - * Defines the Android package name. This will try to open the link in an - * android app if it is installed. If `installApp` is passed, it specifies - * whether to install the Android app if the device supports it and the app is - * not already installed. If this field is provided without a `packageName`, an - * error is thrown explaining that the `packageName` must be provided in - * conjunction with this field. If `minimumVersion` is specified, and an older - * version of the app is installed, the user is taken to the Play Store to - * upgrade the app. - */ - android?: { - - /** - * Defines the required Android package name of the app where the link should be - * handled if the Android app is installed. - */ - packageName: string; - - /** - * Whether to install the Android app if the device supports it and the app is - * not already installed. - */ - installApp?: boolean; - - /** - * The Android minimum version if available. If the installed app is an older - * version, the user is taken to the GOogle Play Store to upgrade the app. - */ - minimumVersion?: string; - }; - - /** - * Defines the dynamic link domain to use for the current link if it is to be - * opened using Firebase Dynamic Links, as multiple dynamic link domains can be - * configured per project. This field provides the ability to explicitly choose - * configured per project. This fields provides the ability explicitly choose - * one. If none is provided, the oldest domain is used by default. - */ - dynamicLinkDomain?: string; - } - - /** - * Interface representing a tenant configuration. - * - * Multi-tenancy support requires Google Cloud's Identity Platform - * (GCIP). To learn more about GCIP, including pricing and features, - * see the [GCIP documentation](https://cloud.google.com/identity-platform) - * - * Before multi-tenancy can be used on a Google Cloud Identity Platform project, - * tenants must be allowed on that project via the Cloud Console UI. - * - * A tenant configuration provides information such as the display name, tenant - * identifier and email authentication configuration. - * For OIDC/SAML provider configuration management, `TenantAwareAuth` instances should - * be used instead of a `Tenant` to retrieve the list of configured IdPs on a tenant. - * When configuring these providers, note that tenants will inherit - * whitelisted domains and authenticated redirect URIs of their parent project. - * - * All other settings of a tenant will also be inherited. These will need to be managed - * from the Cloud Console UI. - */ - interface Tenant { - - /** - * The tenant identifier. - */ - tenantId: string; - - /** - * The tenant display name. - */ - displayName?: string; - - /** - * The email sign in provider configuration. - */ - emailSignInConfig?: { - - /** - * Whether email provider is enabled. - */ - enabled: boolean; - - /** - * Whether password is required for email sign-in. When not required, - * email sign-in can be performed with password or via email link sign-in. - */ - passwordRequired?: boolean - }; - - /** - * @return A JSON-serializable representation of this object. - */ - toJSON(): Object; - } - - /** - * Interface representing the properties to update on the provided tenant. - */ - interface UpdateTenantRequest { - - /** - * The tenant display name. - */ - displayName?: string; - - /** - * The email sign in configuration. - */ - emailSignInConfig?: { - - /** - * Whether email provider is enabled. - */ - enabled: boolean; - - /** - * Whether password is required for email sign-in. When not required, - * email sign-in can be performed with password or via email link sign-in. - */ - passwordRequired?: boolean; - }; - } - - /** - * Interface representing the properties to set on a new tenant. - */ - interface CreateTenantRequest extends UpdateTenantRequest { - } - - /** - * Interface representing the object returned from a - * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#listTenants `listTenants()`} - * operation. - * Contains the list of tenants for the current batch and the next page token if available. - */ - interface ListTenantsResult { - - /** - * The list of {@link admin.auth.Tenant `Tenant`} objects for the downloaded batch. - */ - tenants: admin.auth.Tenant[]; - - /** - * The next page token if available. This is needed for the next batch download. - */ - pageToken?: string; - } - - /** - * The filter interface used for listing provider configurations. This is used - * when specifying how to list configured identity providers via - * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#listProviderConfigs `listProviderConfigs()`}. - */ - interface AuthProviderConfigFilter { - - /** - * The Auth provider configuration filter. This can be either `saml` or `oidc`. - * The former is used to look up SAML providers only, while the latter is used - * for OIDC providers. - */ - type: 'saml' | 'oidc'; - - /** - * The maximum number of results to return per page. The default and maximum is - * 100. - */ - maxResults?: number; - - /** - * The next page token. When not specified, the lookup starts from the beginning - * of the list. - */ - pageToken?: string; - } - - /** - * The base Auth provider configuration interface. - */ - interface AuthProviderConfig { - - /** - * The provider ID defined by the developer. - * For a SAML provider, this is always prefixed by `saml.`. - * For an OIDC provider, this is always prefixed by `oidc.`. - */ - providerId: string; - - /** - * The user-friendly display name to the current configuration. This name is - * also used as the provider label in the Cloud Console. - */ - displayName: string; - - /** - * Whether the provider configuration is enabled or disabled. A user - * cannot sign in using a disabled provider. - */ - enabled: boolean; - } - - /** - * The - * [SAML](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html) - * Auth provider configuration interface. A SAML provider can be created via - * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#createProviderConfig `createProviderConfig()`}. - */ - interface SAMLAuthProviderConfig extends admin.auth.AuthProviderConfig { - - /** - * The SAML IdP entity identifier. - */ - idpEntityId: string; - - /** - * The SAML IdP SSO URL. This must be a valid URL. - */ - ssoURL: string; - - /** - * The list of SAML IdP X.509 certificates issued by CA for this provider. - * Multiple certificates are accepted to prevent outages during - * IdP key rotation (for example ADFS rotates every 10 days). When the Auth - * server receives a SAML response, it will match the SAML response with the - * certificate on record. Otherwise the response is rejected. - * Developers are expected to manage the certificate updates as keys are - * rotated. - */ - x509Certificates: string[]; - - /** - * The SAML relying party (service provider) entity ID. - * This is defined by the developer but needs to be provided to the SAML IdP. - */ - rpEntityId: string; - - /** - * This is fixed and must always be the same as the OAuth redirect URL - * provisioned by Firebase Auth, - * `https://project-id.firebaseapp.com/__/auth/handler` unless a custom - * `authDomain` is used. - * The callback URL should also be provided to the SAML IdP during - * configuration. - */ - callbackURL?: string; - } - - /** - * The [OIDC](https://openid.net/specs/openid-connect-core-1_0-final.html) Auth - * provider configuration interface. An OIDC provider can be created via - * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#createProviderConfig `createProviderConfig()`}. - */ - interface OIDCAuthProviderConfig extends admin.auth.AuthProviderConfig { - - /** - * This is the required client ID used to confirm the audience of an OIDC - * provider's - * [ID token](https://openid.net/specs/openid-connect-core-1_0-final.html#IDToken). - */ - clientId: string; - - /** - * This is the required provider issuer used to match the provider issuer of - * the ID token and to determine the corresponding OIDC discovery document, eg. - * [`/.well-known/openid-configuration`](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig). - * This is needed for the following: - *
    - *
  • To verify the provided issuer.
  • - *
  • Determine the authentication/authorization endpoint during the OAuth - * `id_token` authentication flow.
  • - *
  • To retrieve the public signing keys via `jwks_uri` to verify the OIDC - * provider's ID token's signature.
  • - *
  • To determine the claims_supported to construct the user attributes to be - * returned in the additional user info response.
  • - *
- * ID token validation will be performed as defined in the - * [spec](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation). - */ - issuer: string; - } - - /** - * The request interface for updating a SAML Auth provider. This is used - * when updating a SAML provider's configuration via - * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#updateProviderConfig `updateProviderConfig()`}. - */ - interface SAMLUpdateAuthProviderRequest { - - /** - * The SAML provider's updated display name. If not provided, the existing - * configuration's value is not modified. - */ - displayName?: string; - - /** - * Whether the SAML provider is enabled or not. If not provided, the existing - * configuration's setting is not modified. - */ - enabled?: boolean; - - /** - * The SAML provider's updated IdP entity ID. If not provided, the existing - * configuration's value is not modified. - */ - idpEntityId?: string; - - /** - * The SAML provider's updated SSO URL. If not provided, the existing - * configuration's value is not modified. - */ - ssoURL?: string; - - /** - * The SAML provider's updated list of X.509 certificated. If not provided, the - * existing configuration list is not modified. - */ - x509Certificates?: string[]; - - /** - * The SAML provider's updated RP entity ID. If not provided, the existing - * configuration's value is not modified. - */ - rpEntityId?: string; - - /** - * The SAML provider's callback URL. If not provided, the existing - * configuration's value is not modified. - */ - callbackURL?: string; - } - - /** - * The request interface for updating an OIDC Auth provider. This is used - * when updating an OIDC provider's configuration via - * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#updateProviderConfig `updateProviderConfig()`}. - */ - interface OIDCUpdateAuthProviderRequest { - - /** - * The OIDC provider's updated display name. If not provided, the existing - * configuration's value is not modified. - */ - displayName?: string; - - /** - * Whether the OIDC provider is enabled or not. If not provided, the existing - * configuration's setting is not modified. - */ - enabled?: boolean; - - /** - * The OIDC provider's updated client ID. If not provided, the existing - * configuration's value is not modified. - */ - clientId?: string; - - /** - * The OIDC provider's updated issuer. If not provided, the existing - * configuration's value is not modified. - */ - issuer?: string; - } - - /** - * The response interface for listing provider configs. This is only available - * when listing all identity providers' configurations via - * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#listProviderConfigs `listProviderConfigs()`}. - */ - interface ListProviderConfigResults { - - /** - * The list of providers for the specified type in the current page. - */ - providerConfigs: admin.auth.AuthProviderConfig[]; - - /** - * The next page token, if available. - */ - pageToken?: string; - } - - - type UpdateAuthProviderRequest = - admin.auth.SAMLUpdateAuthProviderRequest | admin.auth.OIDCUpdateAuthProviderRequest; - - interface BaseAuth { - - /** - * Creates a new Firebase custom token (JWT) that can be sent back to a client - * device to use to sign in with the client SDKs' `signInWithCustomToken()` - * methods. (Tenant-aware instances will also embed the tenant ID in the - * token.) - * - * See [Create Custom Tokens](/docs/auth/admin/create-custom-tokens) for code - * samples and detailed documentation. - * - * @param uid The `uid` to use as the custom token's subject. - * @param developerClaims Optional additional claims to include - * in the custom token's payload. - * - * @return A promise fulfilled with a custom token for the - * provided `uid` and payload. - */ - createCustomToken(uid: string, developerClaims?: Object): Promise; - - /** - * Creates a new user. - * - * See [Create a user](/docs/auth/admin/manage-users#create_a_user) for code - * samples and detailed documentation. - * - * @param properties The properties to set on the - * new user record to be created. - * - * @return A promise fulfilled with the user - * data corresponding to the newly created user. - */ - createUser(properties: admin.auth.CreateRequest): Promise; - - /** - * Deletes an existing user. - * - * See [Delete a user](/docs/auth/admin/manage-users#delete_a_user) for code - * samples and detailed documentation. - * - * @param uid The `uid` corresponding to the user to delete. - * - * @return An empty promise fulfilled once the user has been - * deleted. - */ - deleteUser(uid: string): Promise; - - /** - * Gets the user data for the user corresponding to a given `uid`. - * - * See [Retrieve user data](/docs/auth/admin/manage-users#retrieve_user_data) - * for code samples and detailed documentation. - * - * @param uid The `uid` corresponding to the user whose data to fetch. - * - * @return A promise fulfilled with the user - * data corresponding to the provided `uid`. - */ - getUser(uid: string): Promise; - - /** - * Gets the user data for the user corresponding to a given email. - * - * See [Retrieve user data](/docs/auth/admin/manage-users#retrieve_user_data) - * for code samples and detailed documentation. - * - * @param email The email corresponding to the user whose data to - * fetch. - * - * @return A promise fulfilled with the user - * data corresponding to the provided email. - */ - getUserByEmail(email: string): Promise; - - /** - * Gets the user data for the user corresponding to a given phone number. The - * phone number has to conform to the E.164 specification. - * - * See [Retrieve user data](/docs/auth/admin/manage-users#retrieve_user_data) - * for code samples and detailed documentation. - * - * @param phoneNumber The phone number corresponding to the user whose - * data to fetch. - * - * @return A promise fulfilled with the user - * data corresponding to the provided phone number. - */ - getUserByPhoneNumber(phoneNumber: string): Promise; - - /** - * Retrieves a list of users (single batch only) with a size of `maxResults` - * starting from the offset as specified by `pageToken`. This is used to - * retrieve all the users of a specified project in batches. - * - * See [List all users](/docs/auth/admin/manage-users#list_all_users) - * for code samples and detailed documentation. - * - * @param maxResults The page size, 1000 if undefined. This is also - * the maximum allowed limit. - * @param pageToken The next page token. If not specified, returns - * users starting without any offset. - * @return A promise that resolves with - * the current batch of downloaded users and the next page token. - */ - listUsers(maxResults?: number, pageToken?: string): Promise; - - /** - * Updates an existing user. - * - * See [Update a user](/docs/auth/admin/manage-users#update_a_user) for code - * samples and detailed documentation. - * - * @param uid The `uid` corresponding to the user to delete. - * @param properties The properties to update on - * the provided user. - * - * @return A promise fulfilled with the - * updated user data. - */ - updateUser(uid: string, properties: admin.auth.UpdateRequest): Promise; - - /** - * Verifies a Firebase ID token (JWT). If the token is valid, the promise is - * fulfilled with the token's decoded claims; otherwise, the promise is - * rejected. - * An optional flag can be passed to additionally check whether the ID token - * was revoked. - * - * See [Verify ID Tokens](/docs/auth/admin/verify-id-tokens) for code samples - * and detailed documentation. - * - * @param idToken The ID token to verify. - * @param checkRevoked Whether to check if the ID token was revoked. - * This requires an extra request to the Firebase Auth backend to check - * the `tokensValidAfterTime` time for the corresponding user. - * When not specified, this additional check is not applied. - * - * @return A promise fulfilled with the - * token's decoded claims if the ID token is valid; otherwise, a rejected - * promise. - */ - verifyIdToken(idToken: string, checkRevoked?: boolean): Promise; - - /** - * Sets additional developer claims on an existing user identified by the - * provided `uid`, typically used to define user roles and levels of - * access. These claims should propagate to all devices where the user is - * already signed in (after token expiration or when token refresh is forced) - * and the next time the user signs in. If a reserved OIDC claim name - * is used (sub, iat, iss, etc), an error is thrown. They are set on the - * authenticated user's ID token JWT. - * - * See - * [Defining user roles and access levels](/docs/auth/admin/custom-claims) - * for code samples and detailed documentation. - * - * @param uid The `uid` of the user to edit. - * @param customUserClaims The developer claims to set. If null is - * passed, existing custom claims are deleted. Passing a custom claims payload - * larger than 1000 bytes will throw an error. Custom claims are added to the - * user's ID token which is transmitted on every authenticated request. - * For profile non-access related user attributes, use database or other - * separate storage systems. - * @return A promise that resolves when the operation completes - * successfully. - */ - setCustomUserClaims(uid: string, customUserClaims: Object | null): Promise; - - /** - * Revokes all refresh tokens for an existing user. - * - * This API will update the user's - * {@link admin.auth.UserRecord#tokensValidAfterTime `tokensValidAfterTime`} to - * the current UTC. It is important that the server on which this is called has - * its clock set correctly and synchronized. - * - * While this will revoke all sessions for a specified user and disable any - * new ID tokens for existing sessions from getting minted, existing ID tokens - * may remain active until their natural expiration (one hour). To verify that - * ID tokens are revoked, use - * {@link admin.auth.Auth#verifyIdToken `verifyIdToken(idToken, true)`} - * where `checkRevoked` is set to true. - * - * @param uid The `uid` corresponding to the user whose refresh tokens - * are to be revoked. - * - * @return An empty promise fulfilled once the user's refresh - * tokens have been revoked. - */ - revokeRefreshTokens(uid: string): Promise; - - /** - * Imports the provided list of users into Firebase Auth. - * A maximum of 1000 users are allowed to be imported one at a time. - * When importing users with passwords, - * {@link admin.auth.UserImportOptions `UserImportOptions`} are required to be - * specified. - * This operation is optimized for bulk imports and will ignore checks on `uid`, - * `email` and other identifier uniqueness which could result in duplications. - * - * @param users The list of user records to import to Firebase Auth. - * @param options The user import options, required when the users provided include - * password credentials. - * @return A promise that resolves when - * the operation completes with the result of the import. This includes the - * number of successful imports, the number of failed imports and their - * corresponding errors. - */ - importUsers( - users: admin.auth.UserImportRecord[], - options?: admin.auth.UserImportOptions, - ): Promise - - /** - * Creates a new Firebase session cookie with the specified options. The created - * JWT string can be set as a server-side session cookie with a custom cookie - * policy, and be used for session management. The session cookie JWT will have - * the same payload claims as the provided ID token. - * - * See [Manage Session Cookies](/docs/auth/admin/manage-cookies) for code - * samples and detailed documentation. - * - * @param idToken The Firebase ID token to exchange for a session - * cookie. - * @param sessionCookieOptions The session - * cookie options which includes custom session duration. - * - * @return A promise that resolves on success with the - * created session cookie. - */ - createSessionCookie( - idToken: string, - sessionCookieOptions: admin.auth.SessionCookieOptions, - ): Promise; - - /** - * Verifies a Firebase session cookie. Returns a Promise with the cookie claims. - * Rejects the promise if the cookie could not be verified. If `checkRevoked` is - * set to true, verifies if the session corresponding to the session cookie was - * revoked. If the corresponding user's session was revoked, an - * `auth/session-cookie-revoked` error is thrown. If not specified the check is - * not performed. - * - * See [Verify Session Cookies](/docs/auth/admin/manage-cookies#verify_session_cookie_and_check_permissions) - * for code samples and detailed documentation - * - * @param sessionCookie The session cookie to verify. - * @param checkForRevocation Whether to check if the session cookie was - * revoked. This requires an extra request to the Firebase Auth backend to - * check the `tokensValidAfterTime` time for the corresponding user. - * When not specified, this additional check is not performed. - * - * @return A promise fulfilled with the - * session cookie's decoded claims if the session cookie is valid; otherwise, - * a rejected promise. - */ - verifySessionCookie( - sessionCookie: string, - checkForRevocation?: boolean, - ): Promise; - - /** - * Generates the out of band email action link to reset a user's password. - * The link is generated for the user with the specified email address. The - * optional {@link admin.auth.ActionCodeSettings `ActionCodeSettings`} object - * defines whether the link is to be handled by a mobile app or browser and the - * additional state information to be passed in the deep link, etc. - * - * @example - * ```javascript - * var actionCodeSettings = { - * url: 'https://www.example.com/?email=user@example.com', - * iOS: { - * bundleId: 'com.example.ios' - * }, - * android: { - * packageName: 'com.example.android', - * installApp: true, - * minimumVersion: '12' - * }, - * handleCodeInApp: true, - * dynamicLinkDomain: 'custom.page.link' - * }; - * admin.auth() - * .generatePasswordResetLink('user@example.com', actionCodeSettings) - * .then(function(link) { - * // The link was successfully generated. - * }) - * .catch(function(error) { - * // Some error occurred, you can inspect the code: error.code - * }); - * ``` - * - * @param email The email address of the user whose password is to be - * reset. - * @param actionCodeSettings The action - * code settings. If specified, the state/continue URL is set as the - * "continueUrl" parameter in the password reset link. The default password - * reset landing page will use this to display a link to go back to the app - * if it is installed. - * If the actionCodeSettings is not specified, no URL is appended to the - * action URL. - * The state URL provided must belong to a domain that is whitelisted by the - * developer in the console. Otherwise an error is thrown. - * Mobile app redirects are only applicable if the developer configures - * and accepts the Firebase Dynamic Links terms of service. - * The Android package name and iOS bundle ID are respected only if they - * are configured in the same Firebase Auth project. - * @return A promise that resolves with the generated link. - */ - generatePasswordResetLink( - email: string, - actionCodeSettings?: admin.auth.ActionCodeSettings, - ): Promise; - - /** - * Generates the out of band email action link to verify the user's ownership - * of the specified email. The - * {@link admin.auth.ActionCodeSettings `ActionCodeSettings`} object provided - * as an argument to this method defines whether the link is to be handled by a - * mobile app or browser along with additional state information to be passed in - * the deep link, etc. - * - * @example - * ```javascript - * var actionCodeSettings = { - * url: 'https://www.example.com/cart?email=user@example.com&cartId=123', - * iOS: { - * bundleId: 'com.example.ios' - * }, - * android: { - * packageName: 'com.example.android', - * installApp: true, - * minimumVersion: '12' - * }, - * handleCodeInApp: true, - * dynamicLinkDomain: 'custom.page.link' - * }; - * admin.auth() - * .generateEmailVerificationLink('user@example.com', actionCodeSettings) - * .then(function(link) { - * // The link was successfully generated. - * }) - * .catch(function(error) { - * // Some error occurred, you can inspect the code: error.code - * }); - * ``` - * - * @param email The email account to verify. - * @param actionCodeSettings The action - * code settings. If specified, the state/continue URL is set as the - * "continueUrl" parameter in the email verification link. The default email - * verification landing page will use this to display a link to go back to - * the app if it is installed. - * If the actionCodeSettings is not specified, no URL is appended to the - * action URL. - * The state URL provided must belong to a domain that is whitelisted by the - * developer in the console. Otherwise an error is thrown. - * Mobile app redirects are only applicable if the developer configures - * and accepts the Firebase Dynamic Links terms of service. - * The Android package name and iOS bundle ID are respected only if they - * are configured in the same Firebase Auth project. - * @return A promise that resolves with the generated link. - */ - generateEmailVerificationLink( - email: string, - actionCodeSettings?: admin.auth.ActionCodeSettings, - ): Promise; - - /** - * Generates the out of band email action link to sign in or sign up the owner - * of the specified email. The - * {@link admin.auth.ActionCodeSettings `ActionCodeSettings`} object provided - * as an argument to this method defines whether the link is to be handled by a - * mobile app or browser along with additional state information to be passed in - * the deep link, etc. - * - * @example - * ```javascript - * var actionCodeSettings = { - * // The URL to redirect to for sign-in completion. This is also the deep - * // link for mobile redirects. The domain (www.example.com) for this URL - * // must be whitelisted in the Firebase Console. - * url: 'https://www.example.com/finishSignUp?cartId=1234', - * iOS: { - * bundleId: 'com.example.ios' - * }, - * android: { - * packageName: 'com.example.android', - * installApp: true, - * minimumVersion: '12' - * }, - * // This must be true. - * handleCodeInApp: true, - * dynamicLinkDomain: 'custom.page.link' - * }; - * admin.auth() - * .generateSignInWithEmailLink('user@example.com', actionCodeSettings) - * .then(function(link) { - * // The link was successfully generated. - * }) - * .catch(function(error) { - * // Some error occurred, you can inspect the code: error.code - * }); - * ``` - * - * @param email The email account to sign in with. - * @param actionCodeSettings The action - * code settings. These settings provide Firebase with instructions on how - * to construct the email link. This includes the sign in completion URL or - * the deep link for redirects and the mobile apps to use when the - * sign-in link is opened on an Android or iOS device. - * Mobile app redirects are only applicable if the developer configures - * and accepts the Firebase Dynamic Links terms of service. - * The Android package name and iOS bundle ID are respected only if they - * are configured in the same Firebase Auth project. - * @return A promise that resolves with the generated link. - */ - generateSignInWithEmailLink( - email: string, - actionCodeSettings: admin.auth.ActionCodeSettings, - ): Promise; - - /** - * Returns the list of existing provider configurations matching the filter - * provided. At most, 100 provider configs can be listed at a time. - * - * SAML and OIDC provider support requires Google Cloud's Identity Platform - * (GCIP). To learn more about GCIP, including pricing and features, - * see the [GCIP documentation](https://cloud.google.com/identity-platform). - * - * @param options The provider config filter to apply. - * @return A promise that resolves with the list of provider configs meeting the - * filter requirements. - */ - listProviderConfigs( - options: admin.auth.AuthProviderConfigFilter - ): Promise; - - /** - * Looks up an Auth provider configuration by the provided ID. - * Returns a promise that resolves with the provider configuration - * corresponding to the provider ID specified. If the specified ID does not - * exist, an `auth/configuration-not-found` error is thrown. - * - * SAML and OIDC provider support requires Google Cloud's Identity Platform - * (GCIP). To learn more about GCIP, including pricing and features, - * see the [GCIP documentation](https://cloud.google.com/identity-platform). - * - * @param providerId The provider ID corresponding to the provider - * config to return. - * @return A promise that resolves - * with the configuration corresponding to the provided ID. - */ - getProviderConfig(providerId: string): Promise; - - /** - * Deletes the provider configuration corresponding to the provider ID passed. - * If the specified ID does not exist, an `auth/configuration-not-found` error - * is thrown. - * - * SAML and OIDC provider support requires Google Cloud's Identity Platform - * (GCIP). To learn more about GCIP, including pricing and features, - * see the [GCIP documentation](https://cloud.google.com/identity-platform). - * - * @param providerId The provider ID corresponding to the provider - * config to delete. - * @return A promise that resolves on completion. - */ - deleteProviderConfig(providerId: string): Promise; - - /** - * Returns a promise that resolves with the updated `AuthProviderConfig` - * corresponding to the provider ID specified. - * If the specified ID does not exist, an `auth/configuration-not-found` error - * is thrown. - * - * SAML and OIDC provider support requires Google Cloud's Identity Platform - * (GCIP). To learn more about GCIP, including pricing and features, - * see the [GCIP documentation](https://cloud.google.com/identity-platform). - * - * @param providerId The provider ID corresponding to the provider - * config to update. - * @param updatedConfig The updated configuration. - * @return A promise that resolves with the updated provider configuration. - */ - updateProviderConfig( - providerId: string, updatedConfig: admin.auth.UpdateAuthProviderRequest - ): Promise; - - /** - * Returns a promise that resolves with the newly created `AuthProviderConfig` - * when the new provider configuration is created. - * - * SAML and OIDC provider support requires Google Cloud's Identity Platform - * (GCIP). To learn more about GCIP, including pricing and features, - * see the [GCIP documentation](https://cloud.google.com/identity-platform). - * - * @param config The provider configuration to create. - * @return A promise that resolves with the created provider configuration. - */ - createProviderConfig( - config: admin.auth.AuthProviderConfig - ): Promise; - } - - /** - * Tenant-aware `Auth` interface used for managing users, configuring SAML/OIDC providers, - * generating email links for password reset, email verification, etc for specific tenants. - * - * Multi-tenancy support requires Google Cloud's Identity Platform - * (GCIP). To learn more about GCIP, including pricing and features, - * see the [GCIP documentation](https://cloud.google.com/identity-platform) - * - * Each tenant contains its own identity providers, settings and sets of users. - * Using `TenantAwareAuth`, users for a specific tenant and corresponding OIDC/SAML - * configurations can also be managed, ID tokens for users signed in to a specific tenant - * can be verified, and email action links can also be generated for users belonging to the - * tenant. - * - * `TenantAwareAuth` instances for a specific `tenantId` can be instantiated by calling - * `auth.tenantManager().authForTenant(tenantId)`. - */ - interface TenantAwareAuth extends BaseAuth { - - /** - * The tenant identifier corresponding to this `TenantAwareAuth` instance. - * All calls to the user management APIs, OIDC/SAML provider management APIs, email link - * generation APIs, etc will only be applied within the scope of this tenant. - */ - tenantId: string; - } - - interface Auth extends admin.auth.BaseAuth { - app: admin.app.App; - - /** - * @return The tenant manager instance associated with the current project. - */ - tenantManager(): admin.auth.TenantManager; - } - - /** - * Defines the tenant manager used to help manage tenant related operations. - * This includes: - *
    - *
  • The ability to create, update, list, get and delete tenants for the underlying - * project.
  • - *
  • Getting a `TenantAwareAuth` instance for running Auth related operations - * (user management, provider configuration management, token verification, - * email link generation, etc) in the context of a specified tenant.
  • - *
- */ - interface TenantManager { - /** - * @param tenantId The tenant ID whose `TenantAwareAuth` instance is to be returned. - * - * @return The `TenantAwareAuth` instance corresponding to this tenant identifier. - */ - authForTenant(tenantId: string): admin.auth.TenantAwareAuth; - - /** - * Gets the tenant configuration for the tenant corresponding to a given `tenantId`. - * - * @param tenantId The tenant identifier corresponding to the tenant whose data to fetch. - * - * @return A promise fulfilled with the tenant configuration to the provided `tenantId`. - */ - getTenant(tenantId: string): Promise; - - /** - * Retrieves a list of tenants (single batch only) with a size of `maxResults` - * starting from the offset as specified by `pageToken`. This is used to - * retrieve all the tenants of a specified project in batches. - * - * @param maxResults The page size, 1000 if undefined. This is also - * the maximum allowed limit. - * @param pageToken The next page token. If not specified, returns - * tenants starting without any offset. - * - * @return A promise that resolves with - * a batch of downloaded tenants and the next page token. - */ - listTenants(maxResults?: number, pageToken?: string): Promise; - - /** - * Deletes an existing tenant. - * - * @param tenantId The `tenantId` corresponding to the tenant to delete. - * - * @return An empty promise fulfilled once the tenant has been deleted. - */ - deleteTenant(tenantId: string): Promise; - - /** - * Creates a new tenant. - * When creating new tenants, tenants that use separate billing and quota will require their - * own project and must be defined as `full_service`. - * - * @param tenantOptions The properties to set on the new tenant configuration to be created. - * - * @return A promise fulfilled with the tenant configuration corresponding to the newly - * created tenant. - */ - createTenant(tenantOptions: admin.auth.CreateTenantRequest): Promise; - - /** - * Updates an existing tenant configuration. - * - * @param tenantId The `tenantId` corresponding to the tenant to delete. - * @param tenantOptions The properties to update on the provided tenant. - * - * @return A promise fulfilled with the update tenant data. - */ - updateTenant(tenantId: string, tenantOptions: admin.auth.UpdateTenantRequest): Promise; - } -} - -declare namespace admin.credential { - - /** - * Interface that provides Google OAuth2 access tokens used to authenticate - * with Firebase services. - * - * In most cases, you will not need to implement this yourself and can instead - * use the default implementations provided by - * {@link admin.credential `admin.credential`}. - */ - interface Credential { - - /** - * Returns a Google OAuth2 access token object used to authenticate with - * Firebase services. - * - * This object contains the following properties: - * * `access_token` (`string`): The actual Google OAuth2 access token. - * * `expires_in` (`number`): The number of seconds from when the token was - * issued that it expires. - * - * @return A Google OAuth2 access token object. - */ - getAccessToken(): Promise; - } - - - /** - * Returns a credential created from the - * {@link - * https://developers.google.com/identity/protocols/application-default-credentials - * Google Application Default Credentials} - * that grants admin access to Firebase services. This credential can be used - * in the call to - * {@link - * https://firebase.google.com/docs/reference/admin/node/admin#.initializeApp - * `admin.initializeApp()`}. - * - * Google Application Default Credentials are available on any Google - * infrastructure, such as Google App Engine and Google Compute Engine. - * - * See - * {@link - * https://firebase.google.com/docs/admin/setup#initialize_the_sdk - * Initialize the SDK} - * for more details. - * - * @example - * ```javascript - * admin.initializeApp({ - * credential: admin.credential.applicationDefault(), - * databaseURL: "https://.firebaseio.com" - * }); - * ``` - * - * @param {!Object=} httpAgent Optional [HTTP Agent](https://nodejs.org/api/http.html#http_class_http_agent) - * to be used when retrieving access tokens from Google token servers. - * - * @return {!admin.credential.Credential} A credential authenticated via Google - * Application Default Credentials that can be used to initialize an app. - */ - function applicationDefault(httpAgent?: Agent): admin.credential.Credential; - - /** - * Returns a credential created from the provided service account that grants - * admin access to Firebase services. This credential can be used in the call - * to - * {@link - * https://firebase.google.com/docs/reference/admin/node/admin#.initializeApp - * `admin.initializeApp()`}. - * - * See - * {@link - * https://firebase.google.com/docs/admin/setup#initialize_the_sdk - * Initialize the SDK} - * for more details. - * - * @example - * ```javascript - * // Providing a path to a service account key JSON file - * var serviceAccount = require("path/to/serviceAccountKey.json"); - * admin.initializeApp({ - * credential: admin.credential.cert(serviceAccount), - * databaseURL: "https://.firebaseio.com" - * }); - * ``` - * - * @example - * ```javascript - * // Providing a service account object inline - * admin.initializeApp({ - * credential: admin.credential.cert({ - * projectId: "", - * clientEmail: "foo@.iam.gserviceaccount.com", - * privateKey: "-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----\n" - * }), - * databaseURL: "https://.firebaseio.com" - * }); - * ``` - * - * @param serviceAccountPathOrObject The path to a service - * account key JSON file or an object representing a service account key. - * @param httpAgent Optional [HTTP Agent](https://nodejs.org/api/http.html#http_class_http_agent) - * to be used when retrieving access tokens from Google token servers. - * - * @return A credential authenticated via the - * provided service account that can be used to initialize an app. - */ - function cert(serviceAccountPathOrObject: string | admin.ServiceAccount, httpAgent?: Agent): admin.credential.Credential; - - /** - * Returns a credential created from the provided refresh token that grants - * admin access to Firebase services. This credential can be used in the call - * to - * {@link - * https://firebase.google.com/docs/reference/admin/node/admin#.initializeApp - * `admin.initializeApp()`}. - * - * See - * {@link - * https://firebase.google.com/docs/admin/setup#initialize_the_sdk - * Initialize the SDK} - * for more details. - * - * @example - * ```javascript - * // Providing a path to a refresh token JSON file - * var refreshToken = require("path/to/refreshToken.json"); - * admin.initializeApp({ - * credential: admin.credential.refreshToken(refreshToken), - * databaseURL: "https://.firebaseio.com" - * }); - * ``` - * - * @param refreshTokenPathOrObject The path to a Google - * OAuth2 refresh token JSON file or an object representing a Google OAuth2 - * refresh token. - * @param httpAgent Optional [HTTP Agent](https://nodejs.org/api/http.html#http_class_http_agent) - * to be used when retrieving access tokens from Google token servers. - * - * @return A credential authenticated via the - * provided service account that can be used to initialize an app. - */ - function refreshToken(refreshTokenPathOrObject: string | Object, httpAgent?: Agent): admin.credential.Credential; -} - -declare namespace admin.database { - - /** - * The Firebase Realtime Database service interface. - * - * Do not call this constructor directly. Instead, use - * [`admin.database()`](admin.database#database). - * - * See - * {@link - * https://firebase.google.com/docs/database/admin/start/ - * Introduction to the Admin Database API} - * for a full guide on how to use the Firebase Realtime Database service. - */ - interface Database { - app: admin.app.App; - - /** - * Disconnects from the server (all Database operations will be completed - * offline). - * - * The client automatically maintains a persistent connection to the Database - * server, which will remain active indefinitely and reconnect when - * disconnected. However, the `goOffline()` and `goOnline()` methods may be used - * to control the client connection in cases where a persistent connection is - * undesirable. - * - * While offline, the client will no longer receive data updates from the - * Database. However, all Database operations performed locally will continue to - * immediately fire events, allowing your application to continue behaving - * normally. Additionally, each operation performed locally will automatically - * be queued and retried upon reconnection to the Database server. - * - * To reconnect to the Database and begin receiving remote events, see - * `goOnline()`. - * - * @example - * ```javascript - * admin.database().goOffline(); - * ``` - */ - goOffline(): void; - - /** - * Reconnects to the server and synchronizes the offline Database state - * with the server state. - * - * This method should be used after disabling the active connection with - * `goOffline()`. Once reconnected, the client will transmit the proper data - * and fire the appropriate events so that your client "catches up" - * automatically. - * - * @example - * ```javascript - * admin.database().goOnline(); - * ``` - */ - goOnline(): void; - - /** - * Returns a `Reference` representing the location in the Database - * corresponding to the provided path. Also can be invoked with an existing - * `Reference` as the argument. In that case returns a new `Reference` - * pointing to the same location. If no path argument is - * provided, returns a `Reference` that represents the root of the Database. - * - * @example - * ```javascript - * // Get a reference to the root of the Database - * var rootRef = admin.database.ref(); - * ``` - * - * @example - * ```javascript - * // Get a reference to the /users/ada node - * var adaRef = admin.database().ref("users/ada"); - * // The above is shorthand for the following operations: - * //var rootRef = admin.database().ref(); - * //var adaRef = rootRef.child("users/ada"); - * ``` - * - * @example - * ```javascript - * var adaRef = admin.database().ref("users/ada"); - * // Get a new reference pointing to the same location. - * var anotherAdaRef = admin.database().ref(adaRef); - * ``` - * - * - * @param path Optional path representing - * the location the returned `Reference` will point. Alternatively, a - * `Reference` object to copy. If not provided, the returned `Reference` will - * point to the root of the Database. - * @return If a path is provided, a `Reference` - * pointing to the provided path. Otherwise, a `Reference` pointing to the - * root of the Database. - */ - ref(path?: string | admin.database.Reference): admin.database.Reference; - - /** - * Returns a `Reference` representing the location in the Database - * corresponding to the provided Firebase URL. - * - * An exception is thrown if the URL is not a valid Firebase Database URL or it - * has a different domain than the current `Database` instance. - * - * Note that all query parameters (`orderBy`, `limitToLast`, etc.) are ignored - * and are not applied to the returned `Reference`. - * - * @example - * ```javascript - * // Get a reference to the root of the Database - * var rootRef = admin.database().ref("https://.firebaseio.com"); - * ``` - * - * @example - * ```javascript - * // Get a reference to the /users/ada node - * var adaRef = admin.database().ref("https://.firebaseio.com/users/ada"); - * ``` - * - * @param url The Firebase URL at which the returned `Reference` will - * point. - * @return A `Reference` pointing to the provided Firebase URL. - */ - refFromURL(url: string): admin.database.Reference; - - /** - * Gets the currently applied security rules as a string. The return value consists of - * the rules source including comments. - * - * @return A promise fulfilled with the rules as a raw string. - */ - getRules(): Promise; - - /** - * Gets the currently applied security rules as a parsed JSON object. Any comments in - * the original source are stripped away. - * - * @return A promise fulfilled with the parsed rules object. - */ - getRulesJSON(): Promise; - - /** - * Sets the specified rules on the Firebase Realtime Database instance. If the rules source is - * specified as a string or a Buffer, it may include comments. - * - * @param source Source of the rules to apply. Must not be `null` or empty. - * @return Resolves when the rules are set on the Realtime Database. - */ - setRules(source: string | Buffer | object): Promise; - } - - /** - * A `DataSnapshot` contains data from a Database location. - * - * Any time you read data from the Database, you receive the data as a - * `DataSnapshot`. A `DataSnapshot` is passed to the event callbacks you attach - * with `on()` or `once()`. You can extract the contents of the snapshot as a - * JavaScript object by calling the `val()` method. Alternatively, you can - * traverse into the snapshot by calling `child()` to return child snapshots - * (which you could then call `val()` on). - * - * A `DataSnapshot` is an efficiently generated, immutable copy of the data at - * a Database location. It cannot be modified and will never change (to modify - * data, you always call the `set()` method on a `Reference` directly). - */ - interface DataSnapshot { - key: string | null; - ref: admin.database.Reference; - - /** - * Gets another `DataSnapshot` for the location at the specified relative path. - * - * Passing a relative path to the `child()` method of a DataSnapshot returns - * another `DataSnapshot` for the location at the specified relative path. The - * relative path can either be a simple child name (for example, "ada") or a - * deeper, slash-separated path (for example, "ada/name/first"). If the child - * location has no data, an empty `DataSnapshot` (that is, a `DataSnapshot` - * whose value is `null`) is returned. - * - * @example - * ```javascript - * // Assume we have the following data in the Database: - * { - * "name": { - * "first": "Ada", - * "last": "Lovelace" - * } - * } - * - * // Test for the existence of certain keys within a DataSnapshot - * var ref = admin.database().ref("users/ada"); - * ref.once("value") - * .then(function(snapshot) { - * var name = snapshot.child("name").val(); // {first:"Ada",last:"Lovelace"} - * var firstName = snapshot.child("name/first").val(); // "Ada" - * var lastName = snapshot.child("name").child("last").val(); // "Lovelace" - * var age = snapshot.child("age").val(); // null - * }); - * ``` - * - * @param path A relative path to the location of child data. - * @return `DataSnapshot` for the location at the specified relative path. - */ - child(path: string): admin.database.DataSnapshot; - - /** - * Returns true if this `DataSnapshot` contains any data. It is slightly more - * efficient than using `snapshot.val() !== null`. - * - * @example - * ```javascript - * // Assume we have the following data in the Database: - * { - * "name": { - * "first": "Ada", - * "last": "Lovelace" - * } - * } - * - * // Test for the existence of certain keys within a DataSnapshot - * var ref = admin.database().ref("users/ada"); - * ref.once("value") - * .then(function(snapshot) { - * var a = snapshot.exists(); // true - * var b = snapshot.child("name").exists(); // true - * var c = snapshot.child("name/first").exists(); // true - * var d = snapshot.child("name/middle").exists(); // false - * }); - * ``` - * - * @return Whether this `DataSnapshot` contains any data. - */ - exists(): boolean; - - /** - * Exports the entire contents of the DataSnapshot as a JavaScript object. - * - * The `exportVal()` method is similar to `val()`, except priority information - * is included (if available), making it suitable for backing up your data. - * - * @return The DataSnapshot's contents as a JavaScript value (Object, - * Array, string, number, boolean, or `null`). - */ - exportVal(): any; - - /** - * Enumerates the top-level children in the `DataSnapshot`. - * - * Because of the way JavaScript objects work, the ordering of data in the - * JavaScript object returned by `val()` is not guaranteed to match the ordering - * on the server nor the ordering of `child_added` events. That is where - * `forEach()` comes in handy. It guarantees the children of a `DataSnapshot` - * will be iterated in their query order. - * - * If no explicit `orderBy*()` method is used, results are returned - * ordered by key (unless priorities are used, in which case, results are - * returned by priority). - * - * @example - * ```javascript - * - * // Assume we have the following data in the Database: - * { - * "users": { - * "ada": { - * "first": "Ada", - * "last": "Lovelace" - * }, - * "alan": { - * "first": "Alan", - * "last": "Turing" - * } - * } - * } - * - * // Loop through users in order with the forEach() method. The callback - * // provided to forEach() will be called synchronously with a DataSnapshot - * // for each child: - * var query = admin.database().ref("users").orderByKey(); - * query.once("value") - * .then(function(snapshot) { - * snapshot.forEach(function(childSnapshot) { - * // key will be "ada" the first time and "alan" the second time - * var key = childSnapshot.key; - * // childData will be the actual contents of the child - * var childData = childSnapshot.val(); - * }); - * }); - * ``` - * - * @example - * ```javascript - * // You can cancel the enumeration at any point by having your callback - * // function return true. For example, the following code sample will only - * // fire the callback function one time: - * var query = admin.database().ref("users").orderByKey(); - * query.once("value") - * .then(function(snapshot) { - * snapshot.forEach(function(childSnapshot) { - * var key = childSnapshot.key; // "ada" - * - * // Cancel enumeration - * return true; - * }); - * }); - * ``` - * - * @param action A function - * that will be called for each child `DataSnapshot`. The callback can return - * true to cancel further enumeration. - * @return True if enumeration was canceled due to your callback - * returning true. - */ - forEach(action: (a: admin.database.DataSnapshot) => boolean | void): boolean; - - /** - * Gets the priority value of the data in this `DataSnapshot`. - * - * Applications need not use priority but can order collections by - * ordinary properties (see - * {@link - * https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data - * Sorting and filtering data}). - * - * @return The the priority value of the data in this `DataSnapshot`. - */ - getPriority(): string | number | null; - - /** - * Returns true if the specified child path has (non-null) data. - * - * @example - * ```javascript - * // Assume we have the following data in the Database: - * { - * "name": { - * "first": "Ada", - * "last": "Lovelace" - * } - * } - * - * // Determine which child keys in DataSnapshot have data. - * var ref = admin.database().ref("users/ada"); - * ref.once("value") - * .then(function(snapshot) { - * var hasName = snapshot.hasChild("name"); // true - * var hasAge = snapshot.hasChild("age"); // false - * }); - * ``` - * - * @param path A relative path to the location of a potential child. - * @return `true` if data exists at the specified child path; else - * `false`. - */ - hasChild(path: string): boolean; - - /** - * Returns whether or not the `DataSnapshot` has any non-`null` child - * properties. - * - * You can use `hasChildren()` to determine if a `DataSnapshot` has any - * children. If it does, you can enumerate them using `forEach()`. If it - * doesn't, then either this snapshot contains a primitive value (which can be - * retrieved with `val()`) or it is empty (in which case, `val()` will return - * `null`). - * - * @example - * ```javascript - * // Assume we have the following data in the Database: - * { - * "name": { - * "first": "Ada", - * "last": "Lovelace" - * } - * } - * - * var ref = admin.database().ref("users/ada"); - * ref.once("value") - * .then(function(snapshot) { - * var a = snapshot.hasChildren(); // true - * var b = snapshot.child("name").hasChildren(); // true - * var c = snapshot.child("name/first").hasChildren(); // false - * }); - * ``` - * - * @return True if this snapshot has any children; else false. - */ - hasChildren(): boolean; - - /** - * Returns the number of child properties of this `DataSnapshot`. - * - * @example - * ```javascript - * // Assume we have the following data in the Database: - * { - * "name": { - * "first": "Ada", - * "last": "Lovelace" - * } - * } - * - * var ref = admin.database().ref("users/ada"); - * ref.once("value") - * .then(function(snapshot) { - * var a = snapshot.numChildren(); // 1 ("name") - * var b = snapshot.child("name").numChildren(); // 2 ("first", "last") - * var c = snapshot.child("name/first").numChildren(); // 0 - * }); - * ``` - * - * @return The number of child properties of this `DataSnapshot`. - */ - numChildren(): number; - - /** - * @return A JSON-serializable representation of this object. - */ - toJSON(): Object | null; - - /** - * Extracts a JavaScript value from a `DataSnapshot`. - * - * Depending on the data in a `DataSnapshot`, the `val()` method may return a - * scalar type (string, number, or boolean), an array, or an object. It may also - * return null, indicating that the `DataSnapshot` is empty (contains no data). - * - * @example - * ```javascript - * // Write and then read back a string from the Database. - * ref.set("hello") - * .then(function() { - * return ref.once("value"); - * }) - * .then(function(snapshot) { - * var data = snapshot.val(); // data === "hello" - * }); - * ``` - * - * @example - * ```javascript - * // Write and then read back a JavaScript object from the Database. - * ref.set({ name: "Ada", age: 36 }) - * .then(function() { - * return ref.once("value"); - * }) - * .then(function(snapshot) { - * var data = snapshot.val(); - * // data is { "name": "Ada", "age": 36 } - * // data.name === "Ada" - * // data.age === 36 - * }); - * ``` - * - * @return The DataSnapshot's contents as a JavaScript value (Object, - * Array, string, number, boolean, or `null`). - */ - val(): any; - } - - /** - * The `onDisconnect` class allows you to write or clear data when your client - * disconnects from the Database server. These updates occur whether your - * client disconnects cleanly or not, so you can rely on them to clean up data - * even if a connection is dropped or a client crashes. - * - * The `onDisconnect` class is most commonly used to manage presence in - * applications where it is useful to detect how many clients are connected and - * when other clients disconnect. See - * {@link - * https://firebase.google.com/docs/database/web/offline-capabilities - * Enabling Offline Capabilities in JavaScript} for more information. - * - * To avoid problems when a connection is dropped before the requests can be - * transferred to the Database server, these functions should be called before - * any data is written. - * - * Note that `onDisconnect` operations are only triggered once. If you want an - * operation to occur each time a disconnect occurs, you'll need to re-establish - * the `onDisconnect` operations each time you reconnect. - */ - interface OnDisconnect { - - /** - * Cancels all previously queued `onDisconnect()` set or update events for this - * location and all children. - * - * If a write has been queued for this location via a `set()` or `update()` at a - * parent location, the write at this location will be canceled, though all - * other siblings will still be written. - * - * @example - * ```javascript - * var ref = admin.database().ref("onlineState"); - * ref.onDisconnect().set(false); - * // ... sometime later - * ref.onDisconnect().cancel(); - * ``` - * - * @param onComplete An optional callback function that is - * called when synchronization to the server has completed. The callback - * will be passed a single parameter: null for success, or an Error object - * indicating a failure. - * @return Resolves when synchronization to the server is complete. - */ - cancel(onComplete?: (a: Error | null) => any): Promise; - - /** - * Ensures the data at this location is deleted when the client is disconnected - * (due to closing the browser, navigating to a new page, or network issues). - * - * @param onComplete An optional callback function that is - * called when synchronization to the server has completed. The callback - * will be passed a single parameter: null for success, or an Error object - * indicating a failure. - * @return Resolves when synchronization to the server is complete. - */ - remove(onComplete?: (a: Error | null) => any): Promise; - - /** - * Ensures the data at this location is set to the specified value when the - * client is disconnected (due to closing the browser, navigating to a new page, - * or network issues). - * - * `set()` is especially useful for implementing "presence" systems, where a - * value should be changed or cleared when a user disconnects so that they - * appear "offline" to other users. See - * {@link - * https://firebase.google.com/docs/database/web/offline-capabilities - * Enabling Offline Capabilities in JavaScript} for more information. - * - * Note that `onDisconnect` operations are only triggered once. If you want an - * operation to occur each time a disconnect occurs, you'll need to re-establish - * the `onDisconnect` operations each time. - * - * @example - * ```javascript - * var ref = admin.database().ref("users/ada/status"); - * ref.onDisconnect().set("I disconnected!"); - * ``` - * - * @param value The value to be written to this location on - * disconnect (can be an object, array, string, number, boolean, or null). - * @param onComplete An optional callback function that - * will be called when synchronization to the database server has completed. - * The callback will be passed a single parameter: null for success, or an - * `Error` object indicating a failure. - * @return A promise that resolves when synchronization to the database is complete. - */ - set(value: any, onComplete?: (a: Error | null) => any): Promise; - - /** - * Ensures the data at this location is set to the specified value and priority - * when the client is disconnected (due to closing the browser, navigating to a - * new page, or network issues). - * - * @param value The value to be written to this location on - * disconnect (can be an object, array, string, number, boolean, or null). - * @param priority - * @param onComplete An optional callback function that is - * called when synchronization to the server has completed. The callback - * will be passed a single parameter: null for success, or an Error object - * indicating a failure. - * @return A promise that resolves when synchronization to the database is complete. - */ - setWithPriority( - value: any, - priority: number | string | null, - onComplete?: (a: Error | null) => any - ): Promise; - - /** - * Writes multiple values at this location when the client is disconnected (due - * to closing the browser, navigating to a new page, or network issues). - * - * The `values` argument contains multiple property-value pairs that will be - * written to the Database together. Each child property can either be a simple - * property (for example, "name") or a relative path (for example, "name/first") - * from the current location to the data to update. - * - * As opposed to the `set()` method, `update()` can be use to selectively update - * only the referenced properties at the current location (instead of replacing - * all the child properties at the current location). - * - * See {@link https://firebase.google.com/docs/reference/admin/node/admin.database.Reference#update} - * for examples of using the connected version of `update`. - * - * @example - * ```javascript - * var ref = admin.database().ref("users/ada"); - * ref.update({ - * onlineState: true, - * status: "I'm online." - * }); - * ref.onDisconnect().update({ - * onlineState: false, - * status: "I'm offline." - * }); - * ``` - * - * @param values Object containing multiple values. - * @param onComplete An optional callback function that will - * be called when synchronization to the server has completed. The - * callback will be passed a single parameter: null for success, or an Error - * object indicating a failure. - * @return Resolves when synchronization to the - * Database is complete. - */ - update(values: Object, onComplete?: (a: Error | null) => any): Promise; - } - - type EventType = 'value' | 'child_added' | 'child_changed' | 'child_moved' | 'child_removed'; - - /** - * A `Query` sorts and filters the data at a Database location so only a subset - * of the child data is included. This can be used to order a collection of - * data by some attribute (for example, height of dinosaurs) as well as to - * restrict a large list of items (for example, chat messages) down to a number - * suitable for synchronizing to the client. Queries are created by chaining - * together one or more of the filter methods defined here. - * - * Just as with a `Reference`, you can receive data from a `Query` by using the - * `on()` method. You will only receive events and `DataSnapshot`s for the - * subset of the data that matches your query. - * - * See - * {@link - * https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data - * Sorting and filtering data} for more information. - */ - interface Query { - ref: admin.database.Reference; - - /** - * Creates a `Query` with the specified ending point. - * - * Using `startAt()`, `endAt()`, and `equalTo()` allows you to choose arbitrary - * starting and ending points for your queries. - * - * The ending point is inclusive, so children with exactly the specified value - * will be included in the query. The optional key argument can be used to - * further limit the range of the query. If it is specified, then children that - * have exactly the specified value must also have a key name less than or equal - * to the specified key. - * - * You can read more about `endAt()` in - * {@link - * https://firebase.google.com/docs/database/web/lists-of-data#filtering_data - * Filtering data}. - * - * @example - * ```javascript - * // Find all dinosaurs whose names come before Pterodactyl lexicographically. - * var ref = admin.database().ref("dinosaurs"); - * ref.orderByKey().endAt("pterodactyl").on("child_added", function(snapshot) { - * console.log(snapshot.key); - * }); - * ``` - * - * @param value The value to end at. The argument - * type depends on which `orderBy*()` function was used in this query. - * Specify a value that matches the `orderBy*()` type. When used in - * combination with `orderByKey()`, the value must be a string. - * @param key The child key to end at, among the children with the - * previously specified priority. This argument is only allowed if ordering by - * priority. - * @return A new `Query` object. - */ - endAt(value: number | string | boolean | null, key?: string): admin.database.Query; - - /** - * Creates a `Query` that includes children that match the specified value. - * - * Using `startAt()`, `endAt()`, and `equalTo()` allows us to choose arbitrary - * starting and ending points for our queries. - * - * The optional key argument can be used to further limit the range of the - * query. If it is specified, then children that have exactly the specified - * value must also have exactly the specified key as their key name. This can be - * used to filter result sets with many matches for the same value. - * - * You can read more about `equalTo()` in - * {@link - * https://firebase.google.com/docs/database/web/lists-of-data#filtering_data - * Filtering data}. - * - * @example - * // Find all dinosaurs whose height is exactly 25 meters. - * var ref = admin.database().ref("dinosaurs"); - * ref.orderByChild("height").equalTo(25).on("child_added", function(snapshot) { - * console.log(snapshot.key); - * }); - * - * @param value The value to match for. The - * argument type depends on which `orderBy*()` function was used in this - * query. Specify a value that matches the `orderBy*()` type. When used in - * combination with `orderByKey()`, the value must be a string. - * @param key The child key to start at, among the children with the - * previously specified priority. This argument is only allowed if ordering by - * priority. - * @return A new `Query` object. - */ - equalTo(value: number | string | boolean | null, key?: string): admin.database.Query; - - /** - * Returns whether or not the current and provided queries represent the same - * location, have the same query parameters, and are from the same instance of - * `admin.app.App`. - * - * Two `Reference` objects are equivalent if they represent the same location - * and are from the same instance of `admin.app.App`. - * - * Two `Query` objects are equivalent if they represent the same location, have - * the same query parameters, and are from the same instance of `admin.app.App`. - * Equivalent queries share the same sort order, limits, and starting and - * ending points. - * - * @example - * ```javascript - * var rootRef = admin.database().ref(); - * var usersRef = rootRef.child("users"); - * - * usersRef.isEqual(rootRef); // false - * usersRef.isEqual(rootRef.child("users")); // true - * usersRef.parent.isEqual(rootRef); // true - * ``` - * - * @example - * ```javascript - * var rootRef = admin.database().ref(); - * var usersRef = rootRef.child("users"); - * var usersQuery = usersRef.limitToLast(10); - * - * usersQuery.isEqual(usersRef); // false - * usersQuery.isEqual(usersRef.limitToLast(10)); // true - * usersQuery.isEqual(rootRef.limitToLast(10)); // false - * usersQuery.isEqual(usersRef.orderByKey().limitToLast(10)); // false - * ``` - * - * @param other The query to compare against. - * @return Whether or not the current and provided queries are - * equivalent. - */ - isEqual(other: admin.database.Query | null): boolean; - - /** - * Generates a new `Query` limited to the first specific number of children. - * - * The `limitToFirst()` method is used to set a maximum number of children to be - * synced for a given callback. If we set a limit of 100, we will initially only - * receive up to 100 `child_added` events. If we have fewer than 100 messages - * stored in our Database, a `child_added` event will fire for each message. - * However, if we have over 100 messages, we will only receive a `child_added` - * event for the first 100 ordered messages. As items change, we will receive - * `child_removed` events for each item that drops out of the active list so - * that the total number stays at 100. - * - * You can read more about `limitToFirst()` in - * {@link - * https://firebase.google.com/docs/database/web/lists-of-data#filtering_data - * Filtering data}. - * - * @example - * ```javascript - * // Find the two shortest dinosaurs. - * var ref = admin.database().ref("dinosaurs"); - * ref.orderByChild("height").limitToFirst(2).on("child_added", function(snapshot) { - * // This will be called exactly two times (unless there are less than two - * // dinosaurs in the Database). - * - * // It will also get fired again if one of the first two dinosaurs is - * // removed from the data set, as a new dinosaur will now be the second - * // shortest. - * console.log(snapshot.key); - * }); - * ``` - * - * @param limit The maximum number of nodes to include in this query. - * @return A `Query` object. - */ - limitToFirst(limit: number): admin.database.Query; - - /** - * Generates a new `Query` object limited to the last specific number of - * children. - * - * The `limitToLast()` method is used to set a maximum number of children to be - * synced for a given callback. If we set a limit of 100, we will initially only - * receive up to 100 `child_added` events. If we have fewer than 100 messages - * stored in our Database, a `child_added` event will fire for each message. - * However, if we have over 100 messages, we will only receive a `child_added` - * event for the last 100 ordered messages. As items change, we will receive - * `child_removed` events for each item that drops out of the active list so - * that the total number stays at 100. - * - * You can read more about `limitToLast()` in - * {@link - * https://firebase.google.com/docs/database/web/lists-of-data#filtering_data - * Filtering data}. - * - * @example - * ```javascript - * // Find the two heaviest dinosaurs. - * var ref = admin.database().ref("dinosaurs"); - * ref.orderByChild("weight").limitToLast(2).on("child_added", function(snapshot) { - * // This callback will be triggered exactly two times, unless there are - * // fewer than two dinosaurs stored in the Database. It will also get fired - * // for every new, heavier dinosaur that gets added to the data set. - * console.log(snapshot.key); - * }); - * ``` - * - * @param limit The maximum number of nodes to include in this query. - * @return A `Query` object. - */ - limitToLast(limit: number): admin.database.Query; - - /** - * Detaches a callback previously attached with `on()`. - * - * Detach a callback previously attached with `on()`. Note that if `on()` was - * called multiple times with the same eventType and callback, the callback - * will be called multiple times for each event, and `off()` must be called - * multiple times to remove the callback. Calling `off()` on a parent listener - * will not automatically remove listeners registered on child nodes, `off()` - * must also be called on any child listeners to remove the callback. - * - * If a callback is not specified, all callbacks for the specified eventType - * will be removed. Similarly, if no eventType or callback is specified, all - * callbacks for the `Reference` will be removed. - * - * @example - * ```javascript - * var onValueChange = function(dataSnapshot) { ... }; - * ref.on('value', onValueChange); - * ref.child('meta-data').on('child_added', onChildAdded); - * // Sometime later... - * ref.off('value', onValueChange); - * - * // You must also call off() for any child listeners on ref - * // to cancel those callbacks - * ref.child('meta-data').off('child_added', onValueAdded); - * ``` - * - * @example - * ```javascript - * // Or you can save a line of code by using an inline function - * // and on()'s return value. - * var onValueChange = ref.on('value', function(dataSnapshot) { ... }); - * // Sometime later... - * ref.off('value', onValueChange); - * ``` - * - * @param eventType One of the following strings: "value", - * "child_added", "child_changed", "child_removed", or "child_moved." - * @param callback The callback function that was passed to `on()`. - * @param context The context that was passed to `on()`. - */ - off( - eventType?: admin.database.EventType, - callback?: (a: admin.database.DataSnapshot, b?: string | null) => any, - context?: Object | null - ): void; - - /** - * Listens for data changes at a particular location. - * - * This is the primary way to read data from a Database. Your callback - * will be triggered for the initial data and again whenever the data changes. - * Use `off( )` to stop receiving updates. See - * {@link https://firebase.google.com/docs/database/web/retrieve-data - * Retrieve Data on the Web} - * for more details. - * - *

value event

- * - * This event will trigger once with the initial data stored at this location, - * and then trigger again each time the data changes. The `DataSnapshot` passed - * to the callback will be for the location at which `on()` was called. It - * won't trigger until the entire contents has been synchronized. If the - * location has no data, it will be triggered with an empty `DataSnapshot` - * (`val()` will return `null`). - * - *

child_added event

- * - * This event will be triggered once for each initial child at this location, - * and it will be triggered again every time a new child is added. The - * `DataSnapshot` passed into the callback will reflect the data for the - * relevant child. For ordering purposes, it is passed a second argument which - * is a string containing the key of the previous sibling child by sort order - * (or `null` if it is the first child). - * - *

child_removed event

- * - * This event will be triggered once every time a child is removed. The - * `DataSnapshot` passed into the callback will be the old data for the child - * that was removed. A child will get removed when either: - * - * - a client explicitly calls `remove()` on that child or one of its ancestors - * - a client calls `set(null)` on that child or one of its ancestors - * - that child has all of its children removed - * - there is a query in effect which now filters out the child (because it's - * sort order changed or the max limit was hit) - * - *

child_changed event

- * - * This event will be triggered when the data stored in a child (or any of its - * descendants) changes. Note that a single `child_changed` event may represent - * multiple changes to the child. The `DataSnapshot` passed to the callback will - * contain the new child contents. For ordering purposes, the callback is also - * passed a second argument which is a string containing the key of the previous - * sibling child by sort order (or `null` if it is the first child). - * - *

child_moved event

- * - * This event will be triggered when a child's sort order changes such that its - * position relative to its siblings changes. The `DataSnapshot` passed to the - * callback will be for the data of the child that has moved. It is also passed - * a second argument which is a string containing the key of the previous - * sibling child by sort order (or `null` if it is the first child). - * - * @example - * ```javascript - * // Handle a new value. - * ref.on('value', function(dataSnapshot) { - * ... - * }); - * ``` - * - * @example - * ```javascript - * // Handle a new child. - * ref.on('child_added', function(childSnapshot, prevChildKey) { - * ... - * }); - * ``` - * - * @example - * ```javascript - * // Handle child removal. - * ref.on('child_removed', function(oldChildSnapshot) { - * ... - * }); - * ``` - * - * @example - * ```javascript - * // Handle child data changes. - * ref.on('child_changed', function(childSnapshot, prevChildKey) { - * ... - * }); - * ``` - * - * @example - * ```javascript - * // Handle child ordering changes. - * ref.on('child_moved', function(childSnapshot, prevChildKey) { - * ... - * }); - * ``` - * - * @param eventType One of the following strings: "value", - * "child_added", "child_changed", "child_removed", or "child_moved." - * @param callback A callback that fires when the specified event occurs. The callback is - * passed a DataSnapshot. For ordering purposes, "child_added", - * "child_changed", and "child_moved" will also be passed a string containing - * the key of the previous child, by sort order (or `null` if it is the - * first child). - * @param cancelCallbackOrContext An optional - * callback that will be notified if your event subscription is ever canceled - * because your client does not have permission to read this data (or it had - * permission but has now lost it). This callback will be passed an `Error` - * object indicating why the failure occurred. - * @param context If provided, this object will be used as `this` - * when calling your callback(s). - * @return The provided - * callback function is returned unmodified. This is just for convenience if - * you want to pass an inline function to `on()`, but store the callback - * function for later passing to `off()`. - */ - on( - eventType: admin.database.EventType, - callback: (a: admin.database.DataSnapshot | null, b?: string) => any, - cancelCallbackOrContext?: Object | null, - context?: Object | null - ): (a: admin.database.DataSnapshot | null, b?: string) => any; - - /** - * Listens for exactly one event of the specified event type, and then stops - * listening. - * - * This is equivalent to calling `on()`, and then calling `off()` inside the - * callback function. See `on()` for details on the event types. - * - * @example - * ```javascript - * // Basic usage of .once() to read the data located at ref. - * ref.once('value') - * .then(function(dataSnapshot) { - * // handle read data. - * }); - * ``` - * - * @param eventType One of the following strings: "value", - * "child_added", "child_changed", "child_removed", or "child_moved." - * @param successCallback A callback that fires when the specified event occurs. The callback is - * passed a `DataSnapshot`. For ordering purposes, "child_added", - * "child_changed", and "child_moved" will also be passed a string containing - * the key of the previous child by sort order (or `null` if it is the - * first child). - * @param failureCallbackOrContext An optional - * callback that will be notified if your client does not have permission to - * read the data. This callback will be passed an `Error` object indicating - * why the failure occurred. - * @param context If provided, this object will be used as `this` - * when calling your callback(s). - * @return {!Promise} - */ - once( - eventType: admin.database.EventType, - successCallback?: (a: admin.database.DataSnapshot, b?: string) => any, - failureCallbackOrContext?: Object | null, - context?: Object | null - ): Promise; - - /** - * Generates a new `Query` object ordered by the specified child key. - * - * Queries can only order by one key at a time. Calling `orderByChild()` - * multiple times on the same query is an error. - * - * Firebase queries allow you to order your data by any child key on the fly. - * However, if you know in advance what your indexes will be, you can define - * them via the .indexOn rule in your Security Rules for better performance. See - * the {@link https://firebase.google.com/docs/database/security/indexing-data - * .indexOn} rule for more information. - * - * You can read more about `orderByChild()` in - * {@link - * https://firebase.google.com/docs/database/web/lists-of-data#sort_data - * Sort data}. - * - * @example - * ```javascript - * var ref = admin.database().ref("dinosaurs"); - * ref.orderByChild("height").on("child_added", function(snapshot) { - * console.log(snapshot.key + " was " + snapshot.val().height + " m tall"); - * }); - * ``` - * - * @param path - * @return A new `Query` object. - */ - orderByChild(path: string): admin.database.Query; - - /** - * Generates a new `Query` object ordered by key. - * - * Sorts the results of a query by their (ascending) key values. - * - * You can read more about `orderByKey()` in - * {@link - * https://firebase.google.com/docs/database/web/lists-of-data#sort_data - * Sort data}. - * - * @example - * ```javascript - * var ref = admin.database().ref("dinosaurs"); - * ref.orderByKey().on("child_added", function(snapshot) { - * console.log(snapshot.key); - * }); - * ``` - * - * @return A new `Query` object. - */ - orderByKey(): admin.database.Query; - - /** - * Generates a new `Query` object ordered by priority. - * - * Applications need not use priority but can order collections by - * ordinary properties (see - * {@link - * https://firebase.google.com/docs/database/web/lists-of-data#sort_data - * Sort data} for alternatives to priority. - * - * @return A new `Query` object. - */ - orderByPriority(): admin.database.Query; - - /** - * Generates a new `Query` object ordered by value. - * - * If the children of a query are all scalar values (string, number, or - * boolean), you can order the results by their (ascending) values. - * - * You can read more about `orderByValue()` in - * {@link - * https://firebase.google.com/docs/database/web/lists-of-data#sort_data - * Sort data}. - * - * @example - * ```javascript - * var scoresRef = admin.database().ref("scores"); - * scoresRef.orderByValue().limitToLast(3).on("value", function(snapshot) { - * snapshot.forEach(function(data) { - * console.log("The " + data.key + " score is " + data.val()); - * }); - * }); - * ``` - * - * @return A new `Query` object. - */ - orderByValue(): admin.database.Query; - - /** - * Creates a `Query` with the specified starting point. - * - * Using `startAt()`, `endAt()`, and `equalTo()` allows you to choose arbitrary - * starting and ending points for your queries. - * - * The starting point is inclusive, so children with exactly the specified value - * will be included in the query. The optional key argument can be used to - * further limit the range of the query. If it is specified, then children that - * have exactly the specified value must also have a key name greater than or - * equal to the specified key. - * - * You can read more about `startAt()` in - * {@link - * https://firebase.google.com/docs/database/web/lists-of-data#filtering_data - * Filtering data}. - * - * @example - * ```javascript - * // Find all dinosaurs that are at least three meters tall. - * var ref = admin.database().ref("dinosaurs"); - * ref.orderByChild("height").startAt(3).on("child_added", function(snapshot) { - * console.log(snapshot.key) - * }); - * ``` - * - * @param value The value to start at. The argument - * type depends on which `orderBy*()` function was used in this query. - * Specify a value that matches the `orderBy*()` type. When used in - * combination with `orderByKey()`, the value must be a string. - * @param key The child key to start at. This argument is allowed if - * ordering by child, value, or priority. - * @return A new `Query` object. - */ - startAt(value: number | string | boolean | null, key?: string): admin.database.Query; - - /** - * @return A JSON-serializable representation of this object. - */ - toJSON(): Object; - - /** - * Gets the absolute URL for this location. - * - * The `toString()` method returns a URL that is ready to be put into a browser, - * curl command, or a `admin.database().refFromURL()` call. Since all of those - * expect the URL to be url-encoded, `toString()` returns an encoded URL. - * - * Append '.json' to the returned URL when typed into a browser to download - * JSON-formatted data. If the location is secured (that is, not publicly - * readable), you will get a permission-denied error. - * - * @example - * ```javascript - * // Calling toString() on a root Firebase reference returns the URL where its - * // data is stored within the Database: - * var rootRef = admin.database().ref(); - * var rootUrl = rootRef.toString(); - * // rootUrl === "https://sample-app.firebaseio.com/". - * - * // Calling toString() at a deeper Firebase reference returns the URL of that - * // deep path within the Database: - * var adaRef = rootRef.child('users/ada'); - * var adaURL = adaRef.toString(); - * // adaURL === "https://sample-app.firebaseio.com/users/ada". - * ``` - * - * @return The absolute URL for this location. - * @override - */ - toString(): string; - } - - /** - * A `Reference` represents a specific location in your Database and can be used - * for reading or writing data to that Database location. - * - * You can reference the root or child location in your Database by calling - * `admin.database().ref()` or `admin.database().ref("child/path")`. - * - * Writing is done with the `set()` method and reading can be done with the - * `on()` method. See - * {@link - * https://firebase.google.com/docs/database/web/read-and-write - * Read and Write Data on the Web} - */ - interface Reference extends admin.database.Query { - - /** - * The last part of the `Reference`'s path. - * - * For example, `"ada"` is the key for - * `https://.firebaseio.com/users/ada`. - * - * The key of a root `Reference` is `null`. - * - * @example - * ```javascript - * // The key of a root reference is null - * var rootRef = admin.database().ref(); - * var key = rootRef.key; // key === null - * ``` - * - * @example - * ```javascript - * // The key of any non-root reference is the last token in the path - * var adaRef = admin.database().ref("users/ada"); - * var key = adaRef.key; // key === "ada" - * key = adaRef.child("name/last").key; // key === "last" - * ``` - */ - key: string | null; - - /** - * The parent location of a `Reference`. - * - * The parent of a root `Reference` is `null`. - * - * @example - * ```javascript - * // The parent of a root reference is null - * var rootRef = admin.database().ref(); - * parent = rootRef.parent; // parent === null - * ``` - * - * @example - * ```javascript - * // The parent of any non-root reference is the parent location - * var usersRef = admin.database().ref("users"); - * var adaRef = admin.database().ref("users/ada"); - * // usersRef and adaRef.parent represent the same location - * ``` - */ - parent: admin.database.Reference | null; - - /** - * The root `Reference` of the Database. - * - * @example - * ```javascript - * // The root of a root reference is itself - * var rootRef = admin.database().ref(); - * // rootRef and rootRef.root represent the same location - * ``` - * - * @example - * ```javascript - * // The root of any non-root reference is the root location - * var adaRef = admin.database().ref("users/ada"); - * // rootRef and adaRef.root represent the same location - * ``` - */ - root: admin.database.Reference; - path: string; - - /** - * Gets a `Reference` for the location at the specified relative path. - * - * The relative path can either be a simple child name (for example, "ada") or - * a deeper slash-separated path (for example, "ada/name/first"). - * - * @example - * ```javascript - * var usersRef = admin.database().ref('users'); - * var adaRef = usersRef.child('ada'); - * var adaFirstNameRef = adaRef.child('name/first'); - * var path = adaFirstNameRef.toString(); - * // path is now 'https://sample-app.firebaseio.com/users/ada/name/first' - * ``` - * - * @param path A relative path from this location to the desired child - * location. - * @return The specified child location. - */ - child(path: string): admin.database.Reference; - - /** - * Returns an `OnDisconnect` object - see - * {@link - * https://firebase.google.com/docs/database/web/offline-capabilities - * Enabling Offline Capabilities in JavaScript} for more information on how - * to use it. - * - * @return An `OnDisconnect` object . - */ - onDisconnect(): admin.database.OnDisconnect; - - /** - * Generates a new child location using a unique key and returns its - * `Reference`. - * - * This is the most common pattern for adding data to a collection of items. - * - * If you provide a value to `push()`, the value will be written to the - * generated location. If you don't pass a value, nothing will be written to the - * Database and the child will remain empty (but you can use the `Reference` - * elsewhere). - * - * The unique key generated by `push()` are ordered by the current time, so the - * resulting list of items will be chronologically sorted. The keys are also - * designed to be unguessable (they contain 72 random bits of entropy). - * - * - * See - * {@link - * https://firebase.google.com/docs/database/web/lists-of-data#append_to_a_list_of_data - * Append to a list of data} - *
See - * {@link - * https://firebase.googleblog.com/2015/02/the-2120-ways-to-ensure-unique_68.html - * The 2^120 Ways to Ensure Unique Identifiers} - * - * @example - * ```javascript - * var messageListRef = admin.database().ref('message_list'); - * var newMessageRef = messageListRef.push(); - * newMessageRef.set({ - * user_id: 'ada', - * text: 'The Analytical Engine weaves algebraical patterns just as the Jacquard loom weaves flowers and leaves.' - * }); - * // We've appended a new message to the message_list location. - * var path = newMessageRef.toString(); - * // path will be something like - * // 'https://sample-app.firebaseio.com/message_list/-IKo28nwJLH0Nc5XeFmj' - * ``` - * - * @param value Optional value to be written at the generated location. - * @param onComplete Callback called when write to server is - * complete. - * @return Combined `Promise` and - * `Reference`; resolves when write is complete, but can be used immediately - * as the `Reference` to the child location. - */ - push(value?: any, onComplete?: (a: Error | null) => any): admin.database.ThenableReference; - - /** - * Removes the data at this Database location. - * - * Any data at child locations will also be deleted. - * - * The effect of the remove will be visible immediately and the corresponding - * event 'value' will be triggered. Synchronization of the remove to the - * Firebase servers will also be started, and the returned Promise will resolve - * when complete. If provided, the onComplete callback will be called - * asynchronously after synchronization has finished. - * - * @example - * ```javascript - * var adaRef = admin.database().ref('users/ada'); - * adaRef.remove() - * .then(function() { - * console.log("Remove succeeded.") - * }) - * .catch(function(error) { - * console.log("Remove failed: " + error.message) - * }); - * ``` - * - * @param onComplete Callback called when write to server is - * complete. - * @return Resolves when remove on server is complete. - */ - remove(onComplete?: (a: Error | null) => any): Promise; - - /** - * Writes data to this Database location. - * - * This will overwrite any data at this location and all child locations. - * - * The effect of the write will be visible immediately, and the corresponding - * events ("value", "child_added", etc.) will be triggered. Synchronization of - * the data to the Firebase servers will also be started, and the returned - * Promise will resolve when complete. If provided, the `onComplete` callback - * will be called asynchronously after synchronization has finished. - * - * Passing `null` for the new value is equivalent to calling `remove()`; namely, - * all data at this location and all child locations will be deleted. - * - * `set()` will remove any priority stored at this location, so if priority is - * meant to be preserved, you need to use `setWithPriority()` instead. - * - * Note that modifying data with `set()` will cancel any pending transactions - * at that location, so extreme care should be taken if mixing `set()` and - * `transaction()` to modify the same data. - * - * A single `set()` will generate a single "value" event at the location where - * the `set()` was performed. - * - * @example - * ```javascript - * var adaNameRef = admin.database().ref('users/ada/name'); - * adaNameRef.child('first').set('Ada'); - * adaNameRef.child('last').set('Lovelace'); - * // We've written 'Ada' to the Database location storing Ada's first name, - * // and 'Lovelace' to the location storing her last name. - * ``` - * - * @example - * ```javascript - * adaNameRef.set({ first: 'Ada', last: 'Lovelace' }); - * // Exact same effect as the previous example, except we've written - * // Ada's first and last name simultaneously. - * ``` - * - * @example - * ```javascript - * adaNameRef.set({ first: 'Ada', last: 'Lovelace' }) - * .then(function() { - * console.log('Synchronization succeeded'); - * }) - * .catch(function(error) { - * console.log('Synchronization failed'); - * }); - * // Same as the previous example, except we will also log a message - * // when the data has finished synchronizing. - * ``` - * - * @param value The value to be written (string, number, boolean, object, - * array, or null). - * @param onComplete Callback called when write to server is - * complete. - * @return Resolves when write to server is complete. - */ - set(value: any, onComplete?: (a: Error | null) => any): Promise; - - /** - * Sets a priority for the data at this Database location. - * - * Applications need not use priority but can order collections by - * ordinary properties (see - * {@link - * https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data - * Sorting and filtering data}). - * - * @param priority - * @param onComplete - * @return - */ - setPriority( - priority: string | number | null, - onComplete: (a: Error | null) => any - ): Promise; - - /** - * Writes data the Database location. Like `set()` but also specifies the - * priority for that data. - * - * Applications need not use priority but can order collections by - * ordinary properties (see - * {@link - * https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data - * Sorting and filtering data}). - * - * @param newVal - * @param newPriority - * @param onComplete - * @return - */ - setWithPriority( - newVal: any, newPriority: string | number | null, - onComplete?: (a: Error | null) => any - ): Promise; - - /** - * Atomically modifies the data at this location. - * - * Atomically modify the data at this location. Unlike a normal `set()`, which - * just overwrites the data regardless of its previous value, `transaction()` is - * used to modify the existing value to a new value, ensuring there are no - * conflicts with other clients writing to the same location at the same time. - * - * To accomplish this, you pass `transaction()` an update function which is used - * to transform the current value into a new value. If another client writes to - * the location before your new value is successfully written, your update - * function will be called again with the new current value, and the write will - * be retried. This will happen repeatedly until your write succeeds without - * conflict or you abort the transaction by not returning a value from your - * update function. - * - * Note: Modifying data with `set()` will cancel any pending transactions at - * that location, so extreme care should be taken if mixing `set()` and - * `transaction()` to update the same data. - * - * Note: When using transactions with Security and Firebase Rules in place, be - * aware that a client needs `.read` access in addition to `.write` access in - * order to perform a transaction. This is because the client-side nature of - * transactions requires the client to read the data in order to transactionally - * update it. - * - * @example - * ```javascript - * // Increment Ada's rank by 1. - * var adaRankRef = admin.database().ref('users/ada/rank'); - * adaRankRef.transaction(function(currentRank) { - * // If users/ada/rank has never been set, currentRank will be `null`. - * return currentRank + 1; - * }); - * ``` - * - * @example - * ```javascript - * // Try to create a user for ada, but only if the user id 'ada' isn't - * // already taken - * var adaRef = admin.database().ref('users/ada'); - * adaRef.transaction(function(currentData) { - * if (currentData === null) { - * return { name: { first: 'Ada', last: 'Lovelace' } }; - * } else { - * console.log('User ada already exists.'); - * return; // Abort the transaction. - * } - * }, function(error, committed, snapshot) { - * if (error) { - * console.log('Transaction failed abnormally!', error); - * } else if (!committed) { - * console.log('We aborted the transaction (because ada already exists).'); - * } else { - * console.log('User ada added!'); - * } - * console.log("Ada's data: ", snapshot.val()); - * }); - * ``` - * - * @param transactionUpdate A developer-supplied function which - * will be passed the current data stored at this location (as a JavaScript - * object). The function should return the new value it would like written (as - * a JavaScript object). If `undefined` is returned (i.e. you return with no - * arguments) the transaction will be aborted and the data at this location - * will not be modified. - * @param onComplete A callback - * function that will be called when the transaction completes. The callback - * is passed three arguments: a possibly-null `Error`, a `boolean` indicating - * whether the transaction was committed, and a `DataSnapshot` indicating the - * final result. If the transaction failed abnormally, the first argument will - * be an `Error` object indicating the failure cause. If the transaction - * finished normally, but no data was committed because no data was returned - * from `transactionUpdate`, then second argument will be false. If the - * transaction completed and committed data to Firebase, the second argument - * will be true. Regardless, the third argument will be a `DataSnapshot` - * containing the resulting data in this location. - * @param applyLocally By default, events are raised each time the - * transaction update function runs. So if it is run multiple times, you may - * see intermediate states. You can set this to false to suppress these - * intermediate states and instead wait until the transaction has completed - * before events are raised. - * @return Returns a Promise that can optionally be used instead of the `onComplete` - * callback to handle success and failure. - */ - transaction( - transactionUpdate: (a: any) => any, - onComplete?: (a: Error | null, b: boolean, c: admin.database.DataSnapshot | null) => any, - applyLocally?: boolean - ): Promise<{ - committed: boolean, - snapshot: admin.database.DataSnapshot | null - }>; - - /** - * Writes multiple values to the Database at once. - * - * The `values` argument contains multiple property-value pairs that will be - * written to the Database together. Each child property can either be a simple - * property (for example, "name") or a relative path (for example, - * "name/first") from the current location to the data to update. - * - * As opposed to the `set()` method, `update()` can be use to selectively update - * only the referenced properties at the current location (instead of replacing - * all the child properties at the current location). - * - * The effect of the write will be visible immediately, and the corresponding - * events ('value', 'child_added', etc.) will be triggered. Synchronization of - * the data to the Firebase servers will also be started, and the returned - * Promise will resolve when complete. If provided, the `onComplete` callback - * will be called asynchronously after synchronization has finished. - * - * A single `update()` will generate a single "value" event at the location - * where the `update()` was performed, regardless of how many children were - * modified. - * - * Note that modifying data with `update()` will cancel any pending - * transactions at that location, so extreme care should be taken if mixing - * `update()` and `transaction()` to modify the same data. - * - * Passing `null` to `update()` will remove the data at this location. - * - * See - * {@link - * https://firebase.googleblog.com/2015/09/introducing-multi-location-updates-and_86.html - * Introducing multi-location updates and more}. - * - * @example - * ```javascript - * var adaNameRef = admin.database().ref('users/ada/name'); - * // Modify the 'first' and 'last' properties, but leave other data at - * // adaNameRef unchanged. - * adaNameRef.update({ first: 'Ada', last: 'Lovelace' }); - * ``` - * - * @param values Object containing multiple values. - * @param onComplete Callback called when write to server is - * complete. - * @return Resolves when update on server is complete. - */ - update(values: Object, onComplete?: (a: Error | null) => any): Promise; - } - - /** - * @extends {admin.database.Reference} - */ - interface ThenableReference extends admin.database.Reference, Promise { } - - function enableLogging(logger?: boolean | ((message: string) => any), persistent?: boolean): any; -} - -declare namespace admin.database.ServerValue { - var TIMESTAMP: number; -} - -type BaseMessage = { - data?: { [key: string]: string }; - notification?: admin.messaging.Notification; - android?: admin.messaging.AndroidConfig; - webpush?: admin.messaging.WebpushConfig; - apns?: admin.messaging.ApnsConfig; - fcmOptions?: admin.messaging.FcmOptions; -}; - -interface TokenMessage extends BaseMessage { - token: string; -} - -interface TopicMessage extends BaseMessage { - topic: string; -} - -interface ConditionMessage extends BaseMessage { - condition: string; -} - -declare namespace admin.messaging { - type Message = TokenMessage | TopicMessage | ConditionMessage; - - interface MulticastMessage extends BaseMessage { - tokens: string[]; - } - - /** - * Represents the Android-specific options that can be included in an - * {@link admin.messaging.Message}. - */ - interface AndroidConfig { - - /** - * Collapse key for the message. Collapse key serves as an identifier for a - * group of messages that can be collapsed, so that only the last message gets - * sent when delivery can be resumed. A maximum of four different collapse keys - * may be active at any given time. - */ - collapseKey?: string; - - /** - * Priority of the message. Must be either `normal` or `high`. - */ - priority?: ('high' | 'normal'); - - /** - * Time-to-live duration of the message in milliseconds. - */ - ttl?: number; - - /** - * Package name of the application where the registration tokens must match - * in order to receive the message. - */ - restrictedPackageName?: string; - - /** - * A collection of data fields to be included in the message. All values must - * be strings. When provided, overrides any data fields set on the top-level - * `admin.messaging.Message`.} - */ - data?: { [key: string]: string }; - - /** - * Android notification to be included in the message. - */ - notification?: AndroidNotification; - - /** - * Options for features provided by the FCM SDK for Android. - */ - fcmOptions?: AndroidFcmOptions; - } - - /** - * Represents the Android-specific notification options that can be included in - * {@link admin.messaging.AndroidConfig}. - */ - interface AndroidNotification { - - /** - * Title of the Android notification. When provided, overrides the title set via - * `admin.messaging.Notification`. - */ - title?: string; - - /** - * Body of the Android notification. When provided, overrides the body set via - * `admin.messaging.Notification`. - */ - body?: string; - - /** - * Icon resource for the Android notification. - */ - icon?: string; - - /** - * Notification icon color in `#rrggbb` format. - */ - color?: string; - - /** - * File name of the sound to be played when the device receives the - * notification. - */ - sound?: string; - - /** - * Notification tag. This is an identifier used to replace existing - * notifications in the notification drawer. If not specified, each request - * creates a new notification. - */ - tag?: string; - - /** - * URL of an image to be displayed in the notification. - */ - imageUrl?: string; - - /** - * Action associated with a user click on the notification. If specified, an - * activity with a matching Intent Filter is launched when a user clicks on the - * notification. - */ - clickAction?: string; - - /** - * Key of the body string in the app's string resource to use to localize the - * body text. - * - */ - bodyLocKey?: string; - - /** - * An array of resource keys that will be used in place of the format - * specifiers in `bodyLocKey`. - */ - bodyLocArgs?: string[]; - - /** - * Key of the title string in the app's string resource to use to localize the - * title text. - */ - titleLocKey?: string; - - /** - * An array of resource keys that will be used in place of the format - * specifiers in `titleLocKey`. - */ - titleLocArgs?: string[]; - - /** - * The Android notification channel ID (new in Android O). The app must create - * a channel with this channel ID before any notification with this channel ID - * can be received. If you don't send this channel ID in the request, or if the - * channel ID provided has not yet been created by the app, FCM uses the channel - * ID specified in the app manifest. - */ - channelId?: string; - - /** - * Sets the "ticker" text, which is sent to accessibility services. Prior to - * API level 21 (Lollipop), sets the text that is displayed in the status bar - * when the notification first arrives. - */ - ticker?: string; - - /** - * When set to `false` or unset, the notification is automatically dismissed when - * the user clicks it in the panel. When set to `true`, the notification persists - * even when the user clicks it. - */ - sticky?: boolean; - - /** - * For notifications that inform users about events with an absolute time reference, sets - * the time that the event in the notification occurred. Notifications - * in the panel are sorted by this time. - */ - eventTimestamp?: Date; - - /** - * Sets whether or not this notification is relevant only to the current device. - * Some notifications can be bridged to other devices for remote display, such as - * a Wear OS watch. This hint can be set to recommend this notification not be bridged. - * See [Wear OS guides](https://developer.android.com/training/wearables/notifications/bridger#existing-method-of-preventing-bridging) - */ - localOnly?: boolean; - - /** - * Sets the relative priority for this notification. Low-priority notifications - * may be hidden from the user in certain situations. Note this priority differs - * from `AndroidMessagePriority`. This priority is processed by the client after - * the message has been delivered. Whereas `AndroidMessagePriority` is an FCM concept - * that controls when the message is delivered. - */ - priority?: ('min' | 'low' | 'default' | 'high' | 'max'); - - /** - * Sets the vibration pattern to use. Pass in an array of milliseconds to - * turn the vibrator on or off. The first value indicates the duration to wait before - * turning the vibrator on. The next value indicates the duration to keep the - * vibrator on. Subsequent values alternate between duration to turn the vibrator - * off and to turn the vibrator on. If `vibrate_timings` is set and `default_vibrate_timings` - * is set to `true`, the default value is used instead of the user-specified `vibrate_timings`. - */ - vibrateTimingsMillis?: number[]; - - /** - * If set to `true`, use the Android framework's default vibrate pattern for the - * notification. Default values are specified in [`config.xml`](https://android.googlesource.com/platform/frameworks/base/+/master/core/res/res/values/config.xml). - * If `default_vibrate_timings` is set to `true` and `vibrate_timings` is also set, - * the default value is used instead of the user-specified `vibrate_timings`. - */ - defaultVibrateTimings?: boolean; - - /** - * If set to `true`, use the Android framework's default sound for the notification. - * Default values are specified in [`config.xml`](https://android.googlesource.com/platform/frameworks/base/+/master/core/res/res/values/config.xml). - */ - defaultSound?: boolean; - - /** - * Settings to control the notification's LED blinking rate and color if LED is - * available on the device. The total blinking time is controlled by the OS. - */ - lightSettings?: LightSettings; - - /** - * If set to `true`, use the Android framework's default LED light settings - * for the notification. Default values are specified in [`config.xml`](https://android.googlesource.com/platform/frameworks/base/+/master/core/res/res/values/config.xml). - * If `default_light_settings` is set to `true` and `light_settings` is also set, - * the user-specified `light_settings` is used instead of the default value. - */ - defaultLightSettings?: boolean; - - /** - * Sets the visibility of the notification. Must be either `private`, `public`, - * or `secret`. If unspecified, defaults to `private`. - */ - visibility?: ('private' | 'public' | 'secret'); - - /** - * Sets the number of items this notification represents. May be displayed as a - * badge count for Launchers that support badging. See [`NotificationBadge`(https://developer.android.com/training/notify-user/badges). - * For example, this might be useful if you're using just one notification to - * represent multiple new messages but you want the count here to represent - * the number of total new messages. If zero or unspecified, systems - * that support badging use the default, which is to increment a number - * displayed on the long-press menu each time a new notification arrives. - */ - notificationCount?: number; - } - - /** - * Represents settings to control notification LED that can be included in - * {@link admin.messaging.AndroidNotification}. - */ - interface LightSettings { - /** - * Required. Sets color of the LED in `#rrggbb` or `#rrggbbaa` format. - */ - color: string; - - /** - * Required. Along with `light_off_duration`, defines the blink rate of LED flashes. - */ - lightOnDurationMillis: number; - - /** - * Required. Along with `light_on_duration`, defines the blink rate of LED flashes. - */ - lightOffDurationMillis: number; - } - - /** - * Represents options for features provided by the FCM SDK for Android. - */ - interface AndroidFcmOptions { - - /** - * The label associated with the message's analytics data. - */ - analyticsLabel?: string; - } - - /** - * Represents the APNs-specific options that can be included in an - * {@link admin.messaging.Message}. Refer to - * [Apple documentation](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html) - * for various headers and payload fields supported by APNs. - */ - interface ApnsConfig { - - /** - * A collection of APNs headers. Header values must be strings. - */ - headers?: { [key: string]: string }; - - /** - * An APNs payload to be included in the message. - */ - payload?: ApnsPayload; - - /** - * Options for features provided by the FCM SDK for iOS. - */ - fcmOptions?: ApnsFcmOptions; - } - /** - * Represents the payload of an APNs message. Mainly consists of the `aps` - * dictionary. But may also contain other arbitrary custom keys. - */ - interface ApnsPayload { - - /** - * The `aps` dictionary to be included in the message. - */ - aps: Aps; - [customData: string]: object; - } - /** - * Represents the [aps dictionary](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html) - * that is part of APNs messages. - */ - interface Aps { - - /** - * Alert to be included in the message. This may be a string or an object of - * type `admin.messaging.ApsAlert`. - */ - alert?: string | ApsAlert; - - /** - * Badge to be displayed with the message. Set to 0 to remove the badge. When - * not specified, the badge will remain unchanged. - */ - badge?: number; - - /** - * Sound to be played with the message. - */ - sound?: string | CriticalSound; - - /** - * Specifies whether to configure a background update notification. - */ - contentAvailable?: boolean; - - /** - * Specifies whether to set the `mutable-content` property on the message - * so the clients can modify the notification via app extensions. - */ - mutableContent?: boolean; - - /** - * Type of the notification. - */ - category?: string; - - /** - * An app-specific identifier for grouping notifications. - */ - threadId?: string; - [customData: string]: any; - } - - interface ApsAlert { - title?: string; - subtitle?: string; - body?: string; - locKey?: string; - locArgs?: string[]; - titleLocKey?: string; - titleLocArgs?: string[]; - subtitleLocKey?: string; - subtitleLocArgs?: string[]; - actionLocKey?: string; - launchImage?: string; - } - - /** - * Represents a critical sound configuration that can be included in the - * `aps` dictionary of an APNs payload. - */ - interface CriticalSound { - - /** - * The critical alert flag. Set to `true` to enable the critical alert. - */ - critical?: boolean; - - /** - * The name of a sound file in the app's main bundle or in the `Library/Sounds` - * folder of the app's container directory. Specify the string "default" to play - * the system sound. - */ - name: string; - - /** - * The volume for the critical alert's sound. Must be a value between 0.0 - * (silent) and 1.0 (full volume). - */ - volume?: number; - } - - /** - * Represents options for features provided by the FCM SDK for iOS. - */ - interface ApnsFcmOptions { - - /** - * The label associated with the message's analytics data. - */ - analyticsLabel?: string; - - /** - * URL of an image to be displayed in the notification. - */ - imageUrl?: string; - } - - /** - * Represents platform-independent options for features provided by the FCM SDKs. - */ - interface FcmOptions { - - /** - * The label associated with the message's analytics data. - */ - analyticsLabel?: string; - } - - - /** - * A notification that can be included in {@link admin.messaging.Message}. - */ - interface Notification { - /** - * The title of the notification. - */ - title?: string; - /** - * The notification body - */ - body?: string; - /** - * URL of an image to be displayed in the notification. - */ - imageUrl?: string; - } - /** - * Represents the WebPush protocol options that can be included in an - * {@link admin.messaging.Message}. - */ - interface WebpushConfig { - - /** - * A collection of WebPush headers. Header values must be strings. - * - * See [WebPush specification](https://tools.ietf.org/html/rfc8030#section-5) - * for supported headers. - */ - headers?: { [key: string]: string }; - - /** - * A collection of data fields. - */ - data?: { [key: string]: string }; - - /** - * A WebPush notification payload to be included in the message. - */ - notification?: WebpushNotification; - - /** - * Options for features provided by the FCM SDK for Web. - */ - fcmOptions?: WebpushFcmOptions; - } - - /** Represents options for features provided by the FCM SDK for Web - * (which are not part of the Webpush standard). - */ - interface WebpushFcmOptions { - - /** - * The link to open when the user clicks on the notification. - * For all URL values, HTTPS is required. - */ - link?: string; - } - - /** - * Represents the WebPush-specific notification options that can be included in - * {@link admin.messaging.WebpushConfig}. This supports most of the standard - * options as defined in the Web Notification - * [specification](https://developer.mozilla.org/en-US/docs/Web/API/notification/Notification). - */ - interface WebpushNotification { - - /** - * Title text of the notification. - */ - title?: string; - - /** - * An array of notification actions representing the actions - * available to the user when the notification is presented. - */ - actions?: Array<{ - - /** - * An action available to the user when the notification is presented - */ - action: string; - - /** - * Optional icon for a notification action. - */ - icon?: string; - - /** - * Title of the notification action. - */ - title: string; - }>; - - /** - * URL of the image used to represent the notification when there is - * not enough space to display the notification itself. - */ - badge?: string; - - /** - * Body text of the notification. - */ - body?: string; - - /** - * Arbitrary data that you want associated with the notification. - * This can be of any data type. - */ - data?: any; - - /** - * The direction in which to display the notification. Must be one - * of `auto`, `ltr` or `rtl`. - */ - dir?: 'auto' | 'ltr' | 'rtl'; - - /** - * URL to the notification icon. - */ - icon?: string; - - /** - * URL of an image to be displayed in the notification. - */ - image?: string; - - /** - * The notification's language as a BCP 47 language tag. - */ - lang?: string; - - /** - * A boolean specifying whether the user should be notified after a - * new notification replaces an old one. Defaults to false. - */ - renotify?: boolean; - - /** - * Indicates that a notification should remain active until the user - * clicks or dismisses it, rather than closing automatically. - * Defaults to false. - */ - requireInteraction?: boolean; - - /** - * A boolean specifying whether the notification should be silent. - * Defaults to false. - */ - silent?: boolean; - - /** - * An identifying tag for the notification. - */ - tag?: string; - - /** - * Timestamp of the notification. Refer to - * https://developer.mozilla.org/en-US/docs/Web/API/notification/timestamp - * for details. - */ - timestamp?: number; - - /** - * A vibration pattern for the device's vibration hardware to emit - * when the notification fires. - */ - vibrate?: number | number[]; - [key: string]: any; - } - /** - * Interface representing an FCM legacy API data message payload. Data - * messages let developers send up to 4KB of custom key-value pairs. The - * keys and values must both be strings. Keys can be any custom string, - * except for the following reserved strings: - * - * * `"from"` - * * Anything starting with `"google."`. - * - * See [Build send requests](/docs/cloud-messaging/send-message) - * for code samples and detailed documentation. - */ - interface DataMessagePayload { - [key: string]: string; - } - - /** - * Interface representing an FCM legacy API notification message payload. - * Notification messages let developers send up to 4KB of predefined - * key-value pairs. Accepted keys are outlined below. - * - * See [Build send requests](/docs/cloud-messaging/send-message) - * for code samples and detailed documentation. - */ - interface NotificationMessagePayload { - tag?: string; - - /** - * The notification's body text. - * - * **Platforms:** iOS, Android, Web - */ - body?: string; - - /** - * The notification's icon. - * - * **Android:** Sets the notification icon to `myicon` for drawable resource - * `myicon`. If you don't send this key in the request, FCM displays the - * launcher icon specified in your app manifest. - * - * **Web:** The URL to use for the notification's icon. - * - * **Platforms:** Android, Web - */ - icon?: string; - - /** - * The value of the badge on the home screen app icon. - * - * If not specified, the badge is not changed. - * - * If set to `0`, the badge is removed. - * - * **Platforms:** iOS - */ - badge?: string; - - /** - * The notification icon's color, expressed in `#rrggbb` format. - * - * **Platforms:** Android - */ - color?: string; - - /** - * Identifier used to replace existing notifications in the notification drawer. - * - * If not specified, each request creates a new notification. - * - * If specified and a notification with the same tag is already being shown, - * the new notification replaces the existing one in the notification drawer. - * - * **Platforms:** Android - */ - sound?: string; - - /** - * The notification's title. - * - * **Platforms:** iOS, Android, Web - */ - title?: string; - - /** - * The key to the body string in the app's string resources to use to localize - * the body text to the user's current localization. - * - * **iOS:** Corresponds to `loc-key` in the APNs payload. See - * [Payload Key Reference](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html) - * and - * [Localizing the Content of Your Remote Notifications](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH10-SW9) - * for more information. - * - * **Android:** See - * [String Resources](http://developer.android.com/guide/topics/resources/string-resource.html) * for more information. - * - * **Platforms:** iOS, Android - */ - bodyLocKey?: string; - - /** - * Variable string values to be used in place of the format specifiers in - * `body_loc_key` to use to localize the body text to the user's current - * localization. - * - * The value should be a stringified JSON array. - * - * **iOS:** Corresponds to `loc-args` in the APNs payload. See - * [Payload Key Reference](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html) - * and - * [Localizing the Content of Your Remote Notifications](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH10-SW9) - * for more information. - * - * **Android:** See - * [Formatting and Styling](http://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling) - * for more information. - * - * **Platforms:** iOS, Android - */ - bodyLocArgs?: string; - - /** - * Action associated with a user click on the notification. If specified, an - * activity with a matching Intent Filter is launched when a user clicks on the - * notification. - * - * * **Platforms:** Android - */ - clickAction?: string; - - /** - * The key to the title string in the app's string resources to use to localize - * the title text to the user's current localization. - * - * **iOS:** Corresponds to `title-loc-key` in the APNs payload. See - * [Payload Key Reference](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html) - * and - * [Localizing the Content of Your Remote Notifications](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH10-SW9) - * for more information. - * - * **Android:** See - * [String Resources](http://developer.android.com/guide/topics/resources/string-resource.html) - * for more information. - * - * **Platforms:** iOS, Android - */ - titleLocKey?: string; - - /** - * Variable string values to be used in place of the format specifiers in - * `title_loc_key` to use to localize the title text to the user's current - * localization. - * - * The value should be a stringified JSON array. - * - * **iOS:** Corresponds to `title-loc-args` in the APNs payload. See - * [Payload Key Reference](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html) - * and - * [Localizing the Content of Your Remote Notifications](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH10-SW9) - * for more information. - * - * **Android:** See - * [Formatting and Styling](http://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling) - * for more information. - * - * **Platforms:** iOS, Android - */ - titleLocArgs?: string; - [key: string]: string | undefined; - } - - /** - * Interface representing a Firebase Cloud Messaging message payload. One or - * both of the `data` and `notification` keys are required. - * - * See - * [Build send requests](/docs/cloud-messaging/send-message) - * for code samples and detailed documentation. - */ - interface MessagingPayload { - - /** - * The data message payload. - */ - data?: admin.messaging.DataMessagePayload; - - /** - * The notification message payload. - */ - notification?: admin.messaging.NotificationMessagePayload; - } - /** - * Interface representing the options that can be provided when sending a - * message via the FCM legacy APIs. - * - * See [Build send requests](/docs/cloud-messaging/send-message) - * for code samples and detailed documentation. - */ - interface MessagingOptions { - - /** - * Whether or not the message should actually be sent. When set to `true`, - * allows developers to test a request without actually sending a message. When - * set to `false`, the message will be sent. - * - * **Default value:** `false` - */ - dryRun?: boolean; - - /** - * The priority of the message. Valid values are `"normal"` and `"high".` On - * iOS, these correspond to APNs priorities `5` and `10`. - * - * By default, notification messages are sent with high priority, and data - * messages are sent with normal priority. Normal priority optimizes the client - * app's battery consumption and should be used unless immediate delivery is - * required. For messages with normal priority, the app may receive the message - * with unspecified delay. - * - * When a message is sent with high priority, it is sent immediately, and the - * app can wake a sleeping device and open a network connection to your server. - * - * For more information, see - * [Setting the priority of a message](/docs/cloud-messaging/concept-options#setting-the-priority-of-a-message). - * - * **Default value:** `"high"` for notification messages, `"normal"` for data - * messages - */ - priority?: string; - - /** - * How long (in seconds) the message should be kept in FCM storage if the device - * is offline. The maximum time to live supported is four weeks, and the default - * value is also four weeks. For more information, see - * [Setting the lifespan of a message](/docs/cloud-messaging/concept-options#ttl). - * - * **Default value:** `2419200` (representing four weeks, in seconds) - */ - timeToLive?: number; - - /** - * String identifying a group of messages (for example, "Updates Available") - * that can be collapsed, so that only the last message gets sent when delivery - * can be resumed. This is used to avoid sending too many of the same messages - * when the device comes back online or becomes active. - * - * There is no guarantee of the order in which messages get sent. - * - * A maximum of four different collapse keys is allowed at any given time. This - * means FCM server can simultaneously store four different - * send-to-sync messages per client app. If you exceed this number, there is no - * guarantee which four collapse keys the FCM server will keep. - * - * **Default value:** None - */ - collapseKey?: string; - - /** - * On iOS, use this field to represent `mutable-content` in the APNs payload. - * When a notification is sent and this is set to `true`, the content of the - * notification can be modified before it is displayed, using a - * [Notification Service app extension](https://developer.apple.com/reference/usernotifications/unnotificationserviceextension) - * - * On Android and Web, this parameter will be ignored. - * - * **Default value:** `false` - */ - mutableContent?: boolean; - - /** - * On iOS, use this field to represent `content-available` in the APNs payload. - * When a notification or data message is sent and this is set to `true`, an - * inactive client app is awoken. On Android, data messages wake the app by - * default. On Chrome, this flag is currently not supported. - * - * **Default value:** `false` - */ - contentAvailable?: boolean; - - /** - * The package name of the application which the registration tokens must match - * in order to receive the message. - * - * **Default value:** None - */ - restrictedPackageName?: string; - [key: string]: any | undefined; - } - - /** - * Interface representing the status of a message sent to an individual device - * via the FCM legacy APIs. - * - * See - * [Send to individual devices](/docs/cloud-messaging/admin/send-messages#send_to_individual_devices) - * for code samples and detailed documentation. - */ - interface MessagingDeviceResult { - - /** - * The error that occurred when processing the message for the recipient. - */ - error?: admin.FirebaseError; - - /** - * A unique ID for the successfully processed message. - */ - messageId?: string; - - /** - * The canonical registration token for the client app that the message was - * processed and sent to. You should use this value as the registration token - * for future requests. Otherwise, future messages might be rejected. - */ - canonicalRegistrationToken?: string; - } - - /** - * Interface representing the server response from the legacy - * {@link https://firebase.google.com/docs/reference/admin/node/admin.messaging.Messaging#sendToDevice `sendToDevice()`} method. - * - * See - * [Send to individual devices](/docs/cloud-messaging/admin/send-messages#send_to_individual_devices) - * for code samples and detailed documentation. - */ - interface MessagingDevicesResponse { - - /** - * The number of results that contain a canonical registration token. A - * canonical registration token is the registration token corresponding to the - * last registration requested by the client app. This is the token that you - * should use when sending future messages to the device. - * - * You can access the canonical registration tokens within the - * [`results`](#results) property. - */ - canonicalRegistrationTokenCount: number; - - /** - * The number of messages that could not be processed and resulted in an error. - */ - failureCount: number; - - /** - * The unique ID number identifying this multicast message. - */ - multicastId: number; - - /** - * An array of `MessagingDeviceResult` objects representing the status of the - * processed messages. The objects are listed in the same order as in the - * request. That is, for each registration token in the request, its result has - * the same index in this array. If only a single registration token is - * provided, this array will contain a single object. - */ - results: admin.messaging.MessagingDeviceResult[]; - - /** - * The number of messages that were successfully processed and sent. - */ - successCount: number; - } - /** - * Interface representing the server response from the - * {@link https://firebase.google.com/docs/reference/admin/node/admin.messaging.Messaging#sendToDeviceGroup `sendToDeviceGroup()`} - * method. - * - * See - * [Send messages to device groups](/docs/cloud-messaging/send-message?authuser=0#send_messages_to_device_groups) - * for code samples and detailed documentation. - */ - interface MessagingDeviceGroupResponse { - - /** - * The number of messages that could not be processed and resulted in an error. - */ - successCount: number; - - /** - * The number of messages that could not be processed and resulted in an error. - */ - failureCount: number; - - /** - * An array of registration tokens that failed to receive the message. - */ - failedRegistrationTokens: string[]; - } - - /** - * Interface representing the server response from the legacy - * {@link https://firebase.google.com/docs/reference/admin/node/admin.messaging.Messaging#sendToTopic `sendToTopic()`} method. - * - * See - * [Send to a topic](/docs/cloud-messaging/admin/send-messages#send_to_a_topic) - * for code samples and detailed documentation. - */ - interface MessagingTopicResponse { - - /** - * The message ID for a successfully received request which FCM will attempt to - * deliver to all subscribed devices. - */ - messageId: number; - } - - /** - * Interface representing the server response from the legacy - * {@link https://firebase.google.com/docs/reference/admin/node/admin.messaging.Messaging#sendToCondition `sendToCondition()`} method. - * - * See - * [Send to a condition](/docs/cloud-messaging/admin/send-messages#send_to_a_condition) - * for code samples and detailed documentation. - */ - interface MessagingConditionResponse { - - /** - * The message ID for a successfully received request which FCM will attempt to - * deliver to all subscribed devices. - */ - messageId: number; - } - - /** - * Interface representing the server response from the - * {@link https://firebase.google.com/docs/reference/admin/node/admin.messaging.Messaging#subscribeToTopic `subscribeToTopic()`} and - * {@link - * admin.messaging.Messaging#unsubscribeFromTopic - * `unsubscribeFromTopic()`} - * methods. - * - * See - * [Manage topics from the server](/docs/cloud-messaging/manage-topics) - * for code samples and detailed documentation. - */ - interface MessagingTopicManagementResponse { - - /** - * The number of registration tokens that could not be subscribed to the topic - * and resulted in an error. - */ - failureCount: number; - - /** - * The number of registration tokens that were successfully subscribed to the - * topic. - */ - successCount: number; - - /** - * An array of errors corresponding to the provided registration token(s). The - * length of this array will be equal to [`failureCount`](#failureCount). - */ - errors: admin.FirebaseArrayIndexError[]; - } - - /** - * Interface representing the server response from the - * {@link https://firebase.google.com/docs/reference/admin/node/admin.messaging.Messaging#sendAll `sendAll()`} and - * {@link https://firebase.google.com/docs/reference/admin/node/admin.messaging.Messaging#sendMulticast `sendMulticast()`} methods. - */ - interface BatchResponse { - - /** - * An array of responses, each corresponding to a message. - */ - responses: admin.messaging.SendResponse[]; - - /** - * The number of messages that were successfully handed off for sending. - */ - successCount: number; - - /** - * The number of messages that resulted in errors when sending. - */ - failureCount: number; - } - /** - * Interface representing the status of an individual message that was sent as - * part of a batch request. - */ - interface SendResponse { - - /** - * A boolean indicating if the message was successfully handed off to FCM or - * not. When true, the `messageId` attribute is guaranteed to be set. When - * false, the `error` attribute is guaranteed to be set. - */ - success: boolean; - - /** - * A unique message ID string, if the message was handed off to FCM for - * delivery. - * - */ - messageId?: string; - - /** - * An error, if the message was not handed off to FCM successfully. - */ - error?: admin.FirebaseError; - } - - /** - * Gets the {@link admin.messaging.Messaging `Messaging`} service for the - * current app. - * - * @example - * ```javascript - * var messaging = app.messaging(); - * // The above is shorthand for: - * // var messaging = admin.messaging(app); - * ``` - * - * @return The `Messaging` service for the current app. - */ - interface Messaging { - /** - * The {@link admin.app.App app} associated with the current `Messaging` service - * instance. - * - * @example - * ```javascript - * var app = messaging.app; - * ``` - */ - app: admin.app.App; - - /** - * Sends the given message via FCM. - * - * @param message The message payload. - * @param dryRun Whether to send the message in the dry-run - * (validation only) mode. - * @return A promise fulfilled with a unique message ID - * string after the message has been successfully handed off to the FCM - * service for delivery. - */ - send(message: admin.messaging.Message, dryRun?: boolean): Promise; - - /** - * Sends all the messages in the given array via Firebase Cloud Messaging. - * Employs batching to send the entire list as a single RPC call. Compared - * to the `send()` method, this method is a significantly more efficient way - * to send multiple messages. - * - * The responses list obtained from the return value - * corresponds to the order of tokens in the `MulticastMessage`. An error - * from this method indicates a total failure -- i.e. none of the messages in - * the list could be sent. Partial failures are indicated by a `BatchResponse` - * return value. - * - * @param messages A non-empty array - * containing up to 500 messages. - * @param dryRun Whether to send the messages in the dry-run - * (validation only) mode. - * @return A Promise fulfilled with an object representing the result of the - * send operation. - */ - sendAll( - messages: Array, - dryRun?: boolean - ): Promise; - - /** - * Sends the given multicast message to all the FCM registration tokens - * specified in it. - * - * This method uses the `sendAll()` API under the hood to send the given - * message to all the target recipients. The responses list obtained from the - * return value corresponds to the order of tokens in the `MulticastMessage`. - * An error from this method indicates a total failure -- i.e. the message was - * not sent to any of the tokens in the list. Partial failures are indicated by - * a `BatchResponse` return value. - * - * @param message A multicast message - * containing up to 500 tokens. - * @param dryRun Whether to send the message in the dry-run - * (validation only) mode. - * @return A Promise fulfilled with an object representing the result of the - * send operation. - */ - sendMulticast( - message: admin.messaging.MulticastMessage, - dryRun?: boolean - ): Promise; - - /** - * Sends an FCM message to a single device corresponding to the provided - * registration token. - * - * See - * [Send to individual devices](/docs/cloud-messaging/admin/legacy-fcm#send_to_individual_devices) - * for code samples and detailed documentation. Takes either a - * `registrationToken` to send to a single device or a - * `registrationTokens` parameter containing an array of tokens to send - * to multiple devices. - * - * @param registrationToken A device registration token or an array of - * device registration tokens to which the message should be sent. - * @param payload The message payload. - * @param options Optional options to - * alter the message. - * - * @return A promise fulfilled with the server's response after the message - * has been sent. - */ - sendToDevice( - registrationToken: string | string[], - payload: admin.messaging.MessagingPayload, - options?: admin.messaging.MessagingOptions - ): Promise; - - /** - * Sends an FCM message to a device group corresponding to the provided - * notification key. - * - * See - * [Send to a device group](/docs/cloud-messaging/admin/legacy-fcm#send_to_a_device_group) - * for code samples and detailed documentation. - * - * @param notificationKey The notification key for the device group to - * which to send the message. - * @param payload The message payload. - * @param options Optional options to - * alter the message. - * - * @return A promise fulfilled with the server's response after the message - * has been sent. - */ - sendToDeviceGroup( - notificationKey: string, - payload: admin.messaging.MessagingPayload, - options?: admin.messaging.MessagingOptions - ): Promise; - - /** - * Sends an FCM message to a topic. - * - * See - * [Send to a topic](/docs/cloud-messaging/admin/legacy-fcm#send_to_a_topic) - * for code samples and detailed documentation. - * - * @param topic The topic to which to send the message. - * @param payload The message payload. - * @param options Optional options to - * alter the message. - * - * @return A promise fulfilled with the server's response after the message - * has been sent. - */ - sendToTopic( - topic: string, - payload: admin.messaging.MessagingPayload, - options?: admin.messaging.MessagingOptions - ): Promise; - - /** - * Sends an FCM message to a condition. - * - * See - * [Send to a condition](/docs/cloud-messaging/admin/legacy-fcm#send_to_a_condition) - * for code samples and detailed documentation. - * - * @param condition The condition determining to which topics to send - * the message. - * @param payload The message payload. - * @param options Optional options to - * alter the message. - * - * @return A promise fulfilled with the server's response after the message - * has been sent. - */ - sendToCondition( - condition: string, - payload: admin.messaging.MessagingPayload, - options?: admin.messaging.MessagingOptions - ): Promise; - - /** - * Subscribes a device to an FCM topic. - * - * See [Subscribe to a - * topic](/docs/cloud-messaging/manage-topics#suscribe_and_unsubscribe_using_the) - * for code samples and detailed documentation. Optionally, you can provide an - * array of tokens to subscribe multiple devices. - * - * @param registrationTokens A token or array of registration tokens - * for the devices to subscribe to the topic. - * @param topic The topic to which to subscribe. - * - * @return A promise fulfilled with the server's response after the device has been - * subscribed to the topic. - */ - subscribeToTopic( - registrationTokens: string | string[], - topic: string - ): Promise; - - /** - * Unsubscribes a device from an FCM topic. - * - * See [Unsubscribe from a - * topic](/docs/cloud-messaging/admin/manage-topic-subscriptions#unsubscribe_from_a_topic) - * for code samples and detailed documentation. Optionally, you can provide an - * array of tokens to unsubscribe multiple devices. - * - * @param registrationTokens A device registration token or an array of - * device registration tokens to unsubscribe from the topic. - * @param topic The topic from which to unsubscribe. - * - * @return A promise fulfilled with the server's response after the device has been - * unsubscribed from the topic. - */ - unsubscribeFromTopic( - registrationTokens: string | string[], - topic: string - ): Promise; - } -} - -declare namespace admin.machineLearning { - /** - * Interface representing options for listing Models. - */ - interface ListModelsOptions { - /** -  * An expression that specifies how to filter the results. - * - * Examples: - * - * ``` - * display_name = your_model - * display_name : experimental_* - * tags: face_detector AND tags: experimental - * state.published = true - * ``` - * - * See https://firebase.google.com/docs/ml-kit/manage-hosted-models#list_your_projects_models - */ - filter?: string; - - /** The number of results to return in each page. */ - pageSize?: number; - - /** A token that specifies the result page to return. */ - pageToken?: string; - } - - /** Response object for a listModels operation. */ - interface ListModelsResult { - /** A list of models in your project. */ - readonly models: Model[]; - - /** - * A token you can use to retrieve the next page of results. If null, the - * current page is the final page. - */ - readonly pageToken?: string; - } - - /** - * A TensorFlow Lite Model output object - */ - interface TFLiteModel { - /** The size of the model. */ - readonly sizeBytes: number; - - /** The URI from which the model was originally provided to Firebase. */ - readonly gcsTfliteUri?: string; - } - - /** - * A Firebase ML Model input object - */ - interface ModelOptions { - /** A name for the model. This is the name you use from your app to load the model. */ - displayName?: string; - - /** Tags for easier model management. */ - tags?: string[]; - - /** - * An object containing the URI of the model in Cloud Storage. - * -  * Example: `tfliteModel: { gcsTfliteUri: 'gs://your-bucket/your-model.tflite' }` -  */ - tfliteModel?: {gcsTfliteUri: string;}; - } - - /** - * A Firebase ML Model output object - */ - interface Model { - /** The ID of the model. */ - readonly modelId: string; - - /** The model's name. This is the name you use from your app to load the model. */ - readonly displayName: string; - - /** The model's tags. */ - readonly tags?: string[]; - - /** The timestamp of the model's creation. */ - readonly createTime: string; - - /** The timestamp of the model's most recent update. */ - readonly updateTime: string; - - /** Error message when model validation fails. */ - readonly validationError?: string; - - /** True if the model is published. */ - readonly published: boolean; - - /** - * The ETag identifier of the current version of the model. This value - * changes whenever you update any of the model's properties. -  */ - readonly etag: string; - - /** - * The hash of the model's `tflite` file. This value changes only when - * you upload a new TensorFlow Lite model. - */ - readonly modelHash?: string; - - /** - * True if the model is locked by a server-side operation. You can't make - * changes to a locked model. See {@link waitForUnlocked `waitForUnlocked()`}. - */ - readonly locked: boolean; - - /** - * Wait for the model to be unlocked. - * - * @param {number} maxTimeSeconds The maximum time in seconds to wait. - * - * @return {Promise} A promise that resolves when the model is unlocked - * or the maximum wait time has passed. - */ - waitForUnlocked(maxTimeSeconds?: number): Promise; - - /** Metadata about the model's TensorFlow Lite model file. */ - readonly tfliteModel?: TFLiteModel; - } - - /** - * The Firebase `MachineLearning` service interface. - * - * Do not call this constructor directly. Instead, use - * [`admin.machineLearning()`](admin.machineLearning#machineLearning). - */ - interface MachineLearning { - /** -  * The {@link admin.app.App} associated with the current `MachineLearning` - * service instance. - */ - app: admin.app.App; - - /** - * Creates a model in Firebase ML. - * - * @param {ModelOptions} model The model to create. - * - * @return {Promise} A Promise fulfilled with the created model. - */ - createModel(model: ModelOptions): Promise; - - /** - * Updates a model in Firebase ML. - * - * @param {string} modelId The ID of the model to update. - * @param {ModelOptions} model The model fields to update. - * - * @return {Promise} A Promise fulfilled with the updated model. - */ - updateModel(modelId: string, model: ModelOptions): Promise; - - /** - * Publishes a model in Firebase ML. - * - * @param {string} modelId The ID of the model to publish. - * - * @return {Promise} A Promise fulfilled with the published model. - */ - publishModel(modelId: string): Promise; - - /** - * Unpublishes a model in Firebase ML. - * - * @param {string} modelId The ID of the model to unpublish. - * - * @return {Promise} A Promise fulfilled with the unpublished model. - */ - unpublishModel(modelId: string): Promise; - - /** - * Gets a model from Firebase ML. - * - * @param {string} modelId The ID of the model to get. - * - * @return {Promise} A Promise fulfilled with the model object. - */ - getModel(modelId: string): Promise; - - /** - * Lists models from Firebase ML. - * - * @param {ListModelsOptions} options The listing options. - * - * @return {Promise} A promise that - * resolves with the current (filtered) list of models and the next page - * token. For the last page, an empty list of models and no page token - * are returned. - */ - listModels(options?: ListModelsOptions): Promise; - - /** - * Deletes a model from Firebase ML. - * - * @param {string} modelId The ID of the model to delete. - */ - deleteModel(modelId: string): Promise; - } -} - -declare namespace admin.storage { - - /** - * The default `Storage` service if no - * app is provided or the `Storage` service associated with the provided - * app. - */ - interface Storage { - /** - * Optional app whose `Storage` service to - * return. If not provided, the default `Storage` service will be returned. - */ - app: admin.app.App; - /** - * @returns A [Bucket](https://cloud.google.com/nodejs/docs/reference/storage/latest/Bucket) - * instance as defined in the `@google-cloud/storage` package. - */ - bucket(name?: string): Bucket; - } -} - -declare namespace admin.firestore { - export import v1beta1 = _firestore.v1beta1; - export import v1 = _firestore.v1; +declare namespace admin.firestore { + export import v1beta1 = _firestore.v1beta1; + export import v1 = _firestore.v1; export import CollectionReference = _firestore.CollectionReference; export import DocumentData = _firestore.DocumentData; - export import DocumentReference = _firestore.DocumentReference; - export import DocumentSnapshot = _firestore.DocumentSnapshot; - export import FieldPath = _firestore.FieldPath; - export import FieldValue = _firestore.FieldValue; - export import Firestore = _firestore.Firestore; - export import GeoPoint = _firestore.GeoPoint; - export import Query = _firestore.Query; - export import QueryDocumentSnapshot = _firestore.QueryDocumentSnapshot; - export import QuerySnapshot = _firestore.QuerySnapshot; - export import Timestamp = _firestore.Timestamp; - export import Transaction = _firestore.Transaction; - export import WriteBatch = _firestore.WriteBatch; - export import WriteResult = _firestore.WriteResult; - - export import setLogFunction = _firestore.setLogFunction; -} - -declare namespace admin.instanceId { - /** - * Gets the {@link admin.instanceId.InstanceId `InstanceId`} service for the - * current app. - * - * @example - * ```javascript - * var instanceId = app.instanceId(); - * // The above is shorthand for: - * // var instanceId = admin.instanceId(app); - * ``` - * - * @return The `InstanceId` service for the - * current app. - */ - interface InstanceId { - app: admin.app.App; - - /** - * Deletes the specified instance ID and the associated data from Firebase. - * - * Note that Google Analytics for Firebase uses its own form of Instance ID to - * keep track of analytics data. Therefore deleting a Firebase Instance ID does - * not delete Analytics data. See - * [Delete an Instance ID](/support/privacy/manage-iids#delete_an_instance_id) - * for more information. - * - * @param instanceId The instance ID to be deleted. - * - * @return A promise fulfilled when the instance ID is deleted. - */ - deleteInstanceId(instanceId: string): Promise; - } -} - -declare namespace admin.projectManagement { - - /** - * A SHA-1 or SHA-256 certificate. - * - * Do not call this constructor directly. Instead, use - * [`projectManagement.shaCertificate()`](admin.projectManagement.ProjectManagement#shaCertificate). - */ - interface ShaCertificate { - - /** - * The SHA certificate type. - * - * @example - * ```javascript - * var certType = shaCertificate.certType; - * ``` - */ - certType: ('sha1' | 'sha256'); - - /** - * The SHA-1 or SHA-256 hash for this certificate. - * - * @example - * ```javascript - * var shaHash = shaCertificate.shaHash; - * ``` - */ - shaHash: string; - - /** - * The fully-qualified resource name that identifies this sha-key. - * - * This is useful when manually constructing requests for Firebase's public API. - * - * @example - * ```javascript - * var resourceName = shaCertificate.resourceName; - * ``` - */ - resourceName?: string; - } - - /** - * Metadata about a Firebase app. - */ - interface AppMetadata { - - /** - * The globally unique, Firebase-assigned identifier of the app. - * - * @example - * ```javascript - * var appId = appMetadata.appId; - * ``` - */ - appId: string; - - /** - * The optional user-assigned display name of the app. - * - * @example - * ```javascript - * var displayName = appMetadata.displayName; - * ``` - */ - displayName?: string; - - /** - * The development platform of the app. Supporting Android and iOS app platforms. - * - * @example - * ```javascript - * var platform = AppPlatform.ANDROID; - * ``` - */ - platform: AppPlatform; - - /** - * The globally unique, user-assigned ID of the parent project for the app. - * - * @example - * ```javascript - * var projectId = appMetadata.projectId; - * ``` - */ - projectId: string; - - /** - * The fully-qualified resource name that identifies this app. - * - * This is useful when manually constructing requests for Firebase's public API. - * - * @example - * ```javascript - * var resourceName = androidAppMetadata.resourceName; - * ``` - */ - resourceName: string; - } + export import DocumentReference = _firestore.DocumentReference; + export import DocumentSnapshot = _firestore.DocumentSnapshot; + export import FieldPath = _firestore.FieldPath; + export import FieldValue = _firestore.FieldValue; + export import Firestore = _firestore.Firestore; + export import GeoPoint = _firestore.GeoPoint; + export import Query = _firestore.Query; + export import QueryDocumentSnapshot = _firestore.QueryDocumentSnapshot; + export import QuerySnapshot = _firestore.QuerySnapshot; + export import Timestamp = _firestore.Timestamp; + export import Transaction = _firestore.Transaction; + export import WriteBatch = _firestore.WriteBatch; + export import WriteResult = _firestore.WriteResult; - /** - * Platforms with which a Firebase App can be associated. - */ - enum AppPlatform { + export import setLogFunction = _firestore.setLogFunction; +} - /** - * Unknown state. This is only used for distinguishing unset values. - */ - PLATFORM_UNKNOWN = 'PLATFORM_UNKNOWN', +declare namespace admin.instanceId { + export import InstanceId = _instanceId.admin.instanceId.InstanceId; +} - /** - * The Firebase App is associated with iOS. - */ - IOS = 'IOS', +declare namespace admin.projectManagement { + export import ShaCertificate = _projectManagement.admin.projectManagement.ShaCertificate; + export import AppMetadata = _projectManagement.admin.projectManagement.AppMetadata; + export import AppPlatform = _projectManagement.admin.projectManagement.AppPlatform; + export import AndroidAppMetadata = _projectManagement.admin.projectManagement.AndroidAppMetadata; + export import IosAppMetadata = _projectManagement.admin.projectManagement.IosAppMetadata; + export import AndroidApp = _projectManagement.admin.projectManagement.AndroidApp; + export import IosApp = _projectManagement.admin.projectManagement.IosApp; + export import ProjectManagement = _projectManagement.admin.projectManagement.ProjectManagement; +} - /** - * The Firebase App is associated with Android. - */ - ANDROID = 'ANDROID', - } +declare namespace admin.securityRules { + export import RulesFile = _securityRules.admin.securityRules.RulesFile; + export import RulesetMetadata = _securityRules.admin.securityRules.RulesetMetadata; + export import Ruleset = _securityRules.admin.securityRules.Ruleset; + export import RulesetMetadataList = _securityRules.admin.securityRules.RulesetMetadataList; + export import SecurityRules = _securityRules.admin.securityRules.SecurityRules; +} +declare namespace admin.machineLearning { /** - * Metadata about a Firebase Android App. + * Interface representing options for listing Models. */ - interface AndroidAppMetadata extends AppMetadata { - - platform: AppPlatform.ANDROID; - + interface ListModelsOptions { /** - * The canonical package name of the Android App, as would appear in the Google Play Developer - * Console. + * An expression that specifies how to filter the results. * - * @example - * ```javascript - * var packageName = androidAppMetadata.packageName; + * Examples: + * + * ``` + * display_name = your_model + * display_name : experimental_* + * tags: face_detector AND tags: experimental + * state.published = true * ``` + * + * See https://firebase.google.com/docs/ml-kit/manage-hosted-models#list_your_projects_models */ - packageName: string; + filter?: string; + + /** The number of results to return in each page. */ + pageSize?: number; + + /** A token that specifies the result page to return. */ + pageToken?: string; } - /** - * Metadata about a Firebase iOS App. - */ - interface IosAppMetadata extends AppMetadata { - platform: AppPlatform.IOS; + /** Response object for a listModels operation. */ + interface ListModelsResult { + /** A list of models in your project. */ + readonly models: Model[]; /** - * The canonical bundle ID of the iOS App as it would appear in the iOS App Store. - * - * @example - * ```javascript - * var bundleId = iosAppMetadata.bundleId; - *``` + * A token you can use to retrieve the next page of results. If null, the + * current page is the final page. */ - bundleId: string; + readonly pageToken?: string; } /** - * A reference to a Firebase Android app. - * - * Do not call this constructor directly. Instead, use - * [`projectManagement.androidApp()`](admin.projectManagement.ProjectManagement#androidApp). + * A TensorFlow Lite Model output object */ - interface AndroidApp { - appId: string; - - /** - * Retrieves metadata about this Android app. - * - * @return A promise that resolves to the retrieved metadata about this Android app. - */ - getMetadata(): Promise; - - /** - * Sets the optional user-assigned display name of the app. - * - * @param newDisplayName The new display name to set. - * - * @return A promise that resolves when the display name has been set. - */ - setDisplayName(newDisplayName: string): Promise; - - /** - * Gets the list of SHA certificates associated with this Android app in Firebase. - * - * @return The list of SHA-1 and SHA-256 certificates associated with this Android app in - * Firebase. - */ - getShaCertificates(): Promise; - - /** - * Adds the given SHA certificate to this Android app. - * - * @param certificateToAdd The SHA certificate to add. - * - * @return A promise that resolves when the given certificate - * has been added to the Android app. - */ - addShaCertificate(certificateToAdd: ShaCertificate): Promise; - - /** - * Deletes the specified SHA certificate from this Android app. - * - * @param certificateToDelete The SHA certificate to delete. - * - * @return A promise that resolves when the specified - * certificate has been removed from the Android app. - */ - deleteShaCertificate(certificateToRemove: ShaCertificate): Promise; + interface TFLiteModel { + /** The size of the model. */ + readonly sizeBytes: number; - /** - * Gets the configuration artifact associated with this app. - * - * @return A promise that resolves to the Android app's - * Firebase config file, in UTF-8 string format. This string is typically - * intended to be written to a JSON file that gets shipped with your Android - * app. - */ - getConfig(): Promise; + /** The URI from which the model was originally provided to Firebase. */ + readonly gcsTfliteUri?: string; } /** - * A reference to a Firebase iOS app. - * - * Do not call this constructor directly. Instead, use - * [`projectManagement.iosApp()`](admin.projectManagement.ProjectManagement#iosApp). + * A Firebase ML Model input object */ - interface IosApp { - appId: string; - - /** - * Retrieves metadata about this iOS app. - * - * @return {!Promise} A promise that - * resolves to the retrieved metadata about this iOS app. - */ - getMetadata(): Promise; + interface ModelOptions { + /** A name for the model. This is the name you use from your app to load the model. */ + displayName?: string; - /** - * Sets the optional user-assigned display name of the app. - * - * @param newDisplayName The new display name to set. - * - * @return A promise that resolves when the display name has - * been set. - */ - setDisplayName(newDisplayName: string): Promise; + /** Tags for easier model management. */ + tags?: string[]; /** - * Gets the configuration artifact associated with this app. + * An object containing the URI of the model in Cloud Storage. * - * @return A promise that resolves to the iOS app's Firebase - * config file, in UTF-8 string format. This string is typically intended to - * be written to a plist file that gets shipped with your iOS app. + * Example: `tfliteModel: { gcsTfliteUri: 'gs://your-bucket/your-model.tflite' }` */ - getConfig(): Promise; + tfliteModel?: {gcsTfliteUri: string}; } /** - * The Firebase ProjectManagement service interface. - * - * Do not call this constructor directly. Instead, use - * [`admin.projectManagement()`](admin.projectManagement#projectManagement). + * A Firebase ML Model output object */ - interface ProjectManagement { - app: admin.app.App; + interface Model { + /** The ID of the model. */ + readonly modelId: string; - /** - * Lists up to 100 Firebase apps associated with this Firebase project. - * - * @return A promise that resolves to the metadata list of the apps. - */ - listAppMetadata(): Promise; + /** The model's name. This is the name you use from your app to load the model. */ + readonly displayName: string; - /** - * Lists up to 100 Firebase Android apps associated with this Firebase project. - * - * @return The list of Android apps. - */ - listAndroidApps(): Promise; + /** The model's tags. */ + readonly tags?: string[]; - /** - * Lists up to 100 Firebase iOS apps associated with this Firebase project. - * - * @return The list of iOS apps. - */ - listIosApps(): Promise; + /** The timestamp of the model's creation. */ + readonly createTime: string; - /** - * Creates an `AndroidApp` object, referencing the specified Android app within - * this Firebase project. - * - * This method does not perform an RPC. - * - * @param appId The `appId` of the Android app to reference. - * - * @return An `AndroidApp` object that references the specified Firebase Android app. - */ - androidApp(appId: string): admin.projectManagement.AndroidApp; + /** The timestamp of the model's most recent update. */ + readonly updateTime: string; - /** - * Update the display name of this Firebase project. - * - * @param newDisplayName The new display name to be updated. - * - * @return A promise that resolves when the project display name has been updated. - */ - setDisplayName(newDisplayName: string): Promise; + /** Error message when model validation fails. */ + readonly validationError?: string; + + /** True if the model is published. */ + readonly published: boolean; /** - * Creates an `iOSApp` object, referencing the specified iOS app within - * this Firebase project. - * - * This method does not perform an RPC. - * - * @param appId The `appId` of the iOS app to reference. - * - * @return An `iOSApp` object that references the specified Firebase iOS app. + * The ETag identifier of the current version of the model. This value + * changes whenever you update any of the model's properties. */ - iosApp(appId: string): admin.projectManagement.IosApp; + readonly etag: string; /** - * Creates a `ShaCertificate` object. - * - * This method does not perform an RPC. - * - * @param shaHash The SHA-1 or SHA-256 hash for this certificate. - * - * @return A `ShaCertificate` object contains the specified SHA hash. + * The hash of the model's `tflite` file. This value changes only when + * you upload a new TensorFlow Lite model. */ - shaCertificate(shaHash: string): admin.projectManagement.ShaCertificate; + readonly modelHash?: string; /** - * Creates a new Firebase Android app associated with this Firebase project. - * - * @param packageName The canonical package name of the Android App, - * as would appear in the Google Play Developer Console. - * @param displayName An optional user-assigned display name for this - * new app. - * - * @return A promise that resolves to the newly created Android app. + * True if the model is locked by a server-side operation. You can't make + * changes to a locked model. See {@link waitForUnlocked `waitForUnlocked()`}. */ - createAndroidApp( - packageName: string, displayName?: string): Promise; + readonly locked: boolean; /** - * Creates a new Firebase iOS app associated with this Firebase project. + * Wait for the model to be unlocked. * - * @param bundleId The iOS app bundle ID to use for this new app. - * @param displayName An optional user-assigned display name for this - * new app. + * @param {number} maxTimeSeconds The maximum time in seconds to wait. * - * @return A promise that resolves to the newly created iOS app. - */ - createIosApp(bundleId: string, displayName?: string): Promise; - } -} - -declare namespace admin.securityRules { - /** - * A source file containing some Firebase security rules. The content includes raw - * source code including text formatting, indentation and comments. Use the - * [`securityRules.createRulesFileFromSource()`](admin.securityRules.SecurityRules#createRulesFileFromSource) - * method to create new instances of this type. - */ - interface RulesFile { - readonly name: string; - readonly content: string; - } - - /** - * Required metadata associated with a ruleset. - */ - interface RulesetMetadata { - /** - * Name of the `Ruleset` as a short string. This can be directly passed into APIs - * like [`securityRules.getRuleset()`](admin.securityRules.SecurityRules#getRuleset) - * and [`securityRules.deleteRuleset()`](admin.securityRules.SecurityRules#deleteRuleset). - */ - readonly name: string; - - /** - * Creation time of the `Ruleset` as a UTC timestamp string. - */ - readonly createTime: string; - } - - /** - * A set of Firebase security rules. - */ - interface Ruleset extends RulesetMetadata { - readonly source: RulesFile[]; - } - - interface RulesetMetadataList { - /** - * A batch of ruleset metadata. + * @return {Promise} A promise that resolves when the model is unlocked + * or the maximum wait time has passed. */ - readonly rulesets: RulesetMetadata[]; + waitForUnlocked(maxTimeSeconds?: number): Promise; - /** - * The next page token if available. This is needed to retrieve the next batch. - */ - readonly nextPageToken?: string; + /** Metadata about the model's TensorFlow Lite model file. */ + readonly tfliteModel?: TFLiteModel; } /** - * The Firebase `SecurityRules` service interface. + * The Firebase `MachineLearning` service interface. * * Do not call this constructor directly. Instead, use - * [`admin.securityRules()`](admin.securityRules#securityRules). + * [`admin.machineLearning()`](admin.machineLearning#machineLearning). */ - interface SecurityRules { + interface MachineLearning { + /** + * The {@link admin.app.App} associated with the current `MachineLearning` + * service instance. + */ app: admin.app.App; /** - * Creates a {@link admin.securityRules.RulesFile `RuleFile`} with the given name - * and source. Throws an error if any of the arguments are invalid. This is a local - * operation, and does not involve any network API calls. + * Creates a model in Firebase ML. * - * @example - * ```javascript - * const source = '// Some rules source'; - * const rulesFile = admin.securityRules().createRulesFileFromSource( - * 'firestore.rules', source); - * ``` + * @param {ModelOptions} model The model to create. * - * @param name Name to assign to the rules file. This is usually a short file name that - * helps identify the file in a ruleset. - * @param source Contents of the rules file. - * @return A new rules file instance. + * @return {Promise} A Promise fulfilled with the created model. */ - createRulesFileFromSource(name: string, source: string | Buffer): RulesFile; + createModel(model: ModelOptions): Promise; /** - * Creates a new {@link admin.securityRules.Ruleset `Ruleset`} from the given - * {@link admin.securityRules.RulesFile `RuleFile`}. + * Updates a model in Firebase ML. * - * @param file Rules file to include in the new `Ruleset`. - * @returns A promise that fulfills with the newly created `Ruleset`. - */ - createRuleset(file: RulesFile): Promise; - - /** - * Gets the {@link admin.securityRules.Ruleset `Ruleset`} identified by the given - * name. The input name should be the short name string without the project ID - * prefix. For example, to retrieve the `projects/project-id/rulesets/my-ruleset`, - * pass the short name "my-ruleset". Rejects with a `not-found` error if the - * specified `Ruleset` cannot be found. + * @param {string} modelId The ID of the model to update. + * @param {ModelOptions} model The model fields to update. * - * @param name Name of the `Ruleset` to retrieve. - * @return A promise that fulfills with the specified `Ruleset`. + * @return {Promise} A Promise fulfilled with the updated model. */ - getRuleset(name: string): Promise; + updateModel(modelId: string, model: ModelOptions): Promise; /** - * Deletes the {@link admin.securityRules.Ruleset `Ruleset`} identified by the given - * name. The input name should be the short name string without the project ID - * prefix. For example, to delete the `projects/project-id/rulesets/my-ruleset`, - * pass the short name "my-ruleset". Rejects with a `not-found` error if the - * specified `Ruleset` cannot be found. + * Publishes a model in Firebase ML. * - * @param name Name of the `Ruleset` to delete. - * @return A promise that fulfills when the `Ruleset` is deleted. - */ - deleteRuleset(name: string): Promise; - - /** - * Retrieves a page of ruleset metadata. + * @param {string} modelId The ID of the model to publish. * - * @param pageSize The page size, 100 if undefined. This is also the maximum allowed - * limit. - * @param nextPageToken The next page token. If not specified, returns rulesets - * starting without any offset. - * @return A promise that fulfills with a page of rulesets. + * @return {Promise} A Promise fulfilled with the published model. */ - listRulesetMetadata( - pageSize?: number, nextPageToken?: string): Promise; + publishModel(modelId: string): Promise; /** - * Gets the {@link admin.securityRules.Ruleset `Ruleset`} currently applied to - * Cloud Firestore. Rejects with a `not-found` error if no ruleset is applied - * on Firestore. + * Unpublishes a model in Firebase ML. * - * @return A promise that fulfills with the Firestore ruleset. - */ - getFirestoreRuleset(): Promise; - - /** - * Creates a new {@link admin.securityRules.Ruleset `Ruleset`} from the given - * source, and applies it to Cloud Firestore. + * @param {string} modelId The ID of the model to unpublish. * - * @param source Rules source to apply. - * @return A promise that fulfills when the ruleset is created and released. + * @return {Promise} A Promise fulfilled with the unpublished model. */ - releaseFirestoreRulesetFromSource(source: string | Buffer): Promise; + unpublishModel(modelId: string): Promise; /** - * Applies the specified {@link admin.securityRules.Ruleset `Ruleset`} ruleset - * to Cloud Firestore. + * Gets a model from Firebase ML. * - * @param ruleset Name of the ruleset to apply or a `RulesetMetadata` object - * containing the name. - * @return A promise that fulfills when the ruleset is released. - */ - releaseFirestoreRuleset(ruleset: string | RulesetMetadata): Promise; - - /** - * Gets the {@link admin.securityRules.Ruleset `Ruleset`} currently applied to a - * Cloud Storage bucket. Rejects with a `not-found` error if no ruleset is applied - * on the bucket. + * @param {string} modelId The ID of the model to get. * - * @param bucket Optional name of the Cloud Storage bucket to be retrieved. If not - * specified, retrieves the ruleset applied on the default bucket configured via - * `AppOptions`. - * @return A promise that fulfills with the Cloud Storage ruleset. + * @return {Promise} A Promise fulfilled with the model object. */ - getStorageRuleset(bucket?: string): Promise; + getModel(modelId: string): Promise; /** - * Creates a new {@link admin.securityRules.Ruleset `Ruleset`} from the given - * source, and applies it to a Cloud Storage bucket. + * Lists models from Firebase ML. + * + * @param {ListModelsOptions} options The listing options. * - * @param source Rules source to apply. - * @param bucket Optional name of the Cloud Storage bucket to apply the rules on. If - * not specified, applies the ruleset on the default bucket configured via - * {@link admin.AppOptions `AppOptions`}. - * @return A promise that fulfills when the ruleset is created and released. + * @return {Promise} A promise that + * resolves with the current (filtered) list of models and the next page + * token. For the last page, an empty list of models and no page token + * are returned. */ - releaseStorageRulesetFromSource( - source: string | Buffer, bucket?: string): Promise; + listModels(options?: ListModelsOptions): Promise; /** - * Applies the specified {@link admin.securityRules.Ruleset `Ruleset`} ruleset - * to a Cloud Storage bucket. + * Deletes a model from Firebase ML. * - * @param ruleset Name of the ruleset to apply or a `RulesetMetadata` object - * containing the name. - * @param bucket Optional name of the Cloud Storage bucket to apply the rules on. If - * not specified, applies the ruleset on the default bucket configured via - * {@link admin.AppOptions `AppOptions`}. - * @return A promise that fulfills when the ruleset is released. + * @param {string} modelId The ID of the model to delete. */ - releaseStorageRuleset( - ruleset: string | RulesetMetadata, bucket?: string): Promise; + deleteModel(modelId: string): Promise; } } diff --git a/src/instance-id.d.ts b/src/instance-id.d.ts new file mode 100644 index 0000000000..616d0d193e --- /dev/null +++ b/src/instance-id.d.ts @@ -0,0 +1,37 @@ +import * as _admin from './index.d'; + +/* eslint-disable-next-line @typescript-eslint/no-unused-vars */ +export namespace admin.instanceId { + /** + * Gets the {@link InstanceId `InstanceId`} service for the + * current app. + * + * @example + * ```javascript + * var instanceId = app.instanceId(); + * // The above is shorthand for: + * // var instanceId = admin.instanceId(app); + * ``` + * + * @return The `InstanceId` service for the + * current app. + */ + interface InstanceId { + app: _admin.app.App; + + /** + * Deletes the specified instance ID and the associated data from Firebase. + * + * Note that Google Analytics for Firebase uses its own form of Instance ID to + * keep track of analytics data. Therefore deleting a Firebase Instance ID does + * not delete Analytics data. See + * [Delete an Instance ID](/support/privacy/manage-iids#delete_an_instance_id) + * for more information. + * + * @param instanceId The instance ID to be deleted. + * + * @return A promise fulfilled when the instance ID is deleted. + */ + deleteInstanceId(instanceId: string): Promise; + } +} diff --git a/src/instance-id/instance-id-request.ts b/src/instance-id/instance-id-request.ts index e4c95aee68..b683ff038b 100644 --- a/src/instance-id/instance-id-request.ts +++ b/src/instance-id/instance-id-request.ts @@ -87,7 +87,7 @@ export class FirebaseInstanceIdRequestHandler { }; return this.httpClient.send(req); }) - .then((response) => { + .then(() => { // return nothing on success }) .catch((err) => { diff --git a/src/instance-id/instance-id.ts b/src/instance-id/instance-id.ts index a3ebf0a189..27e807528d 100644 --- a/src/instance-id/instance-id.ts +++ b/src/instance-id/instance-id.ts @@ -68,7 +68,7 @@ export class InstanceId implements FirebaseServiceInterface { */ public deleteInstanceId(instanceId: string): Promise { return this.requestHandler.deleteInstanceId(instanceId) - .then((result) => { + .then(() => { // Return nothing on success }); } diff --git a/src/machine-learning/machine-learning-api-client.ts b/src/machine-learning/machine-learning-api-client.ts index dcf59a55ff..969ebe6ebe 100644 --- a/src/machine-learning/machine-learning-api-client.ts +++ b/src/machine-learning/machine-learning-api-client.ts @@ -38,11 +38,11 @@ export interface ModelOptions { displayName?: string; tags?: string[]; - tfliteModel?: { gcsTfliteUri: string; }; + tfliteModel?: { gcsTfliteUri: string }; } export interface ModelUpdateOptions extends ModelOptions { - state?: { published?: boolean; }; + state?: { published?: boolean }; } /** Interface representing listModels options. */ @@ -113,14 +113,14 @@ export class MachineLearningApiClient { return Promise.reject(err); } return this.getUrl() - .then((url) => { - const request: HttpRequestConfig = { - method: 'POST', - url: `${url}/models`, - data: model, - }; - return this.sendRequest(request); - }); + .then((url) => { + const request: HttpRequestConfig = { + method: 'POST', + url: `${url}/models`, + data: model, + }; + return this.sendRequest(request); + }); } public updateModel(modelId: string, model: ModelUpdateOptions, updateMask: string[]): Promise { @@ -131,14 +131,14 @@ export class MachineLearningApiClient { return Promise.reject(err); } return this.getUrl() - .then((url) => { - const request: HttpRequestConfig = { - method: 'PATCH', - url: `${url}/models/${modelId}?updateMask=${updateMask.join()}`, - data: model, - }; - return this.sendRequest(request); - }); + .then((url) => { + const request: HttpRequestConfig = { + method: 'PATCH', + url: `${url}/models/${modelId}?updateMask=${updateMask.join()}`, + data: model, + }; + return this.sendRequest(request); + }); } diff --git a/src/machine-learning/machine-learning-utils.ts b/src/machine-learning/machine-learning-utils.ts index d0b881621a..1202314e93 100644 --- a/src/machine-learning/machine-learning-utils.ts +++ b/src/machine-learning/machine-learning-utils.ts @@ -38,23 +38,23 @@ export type MachineLearningErrorCode = export class FirebaseMachineLearningError extends PrefixedFirebaseError { public static fromOperationError(code: number, message: string): FirebaseMachineLearningError { switch (code) { - case 1: return new FirebaseMachineLearningError('cancelled', message); - case 2: return new FirebaseMachineLearningError('unknown-error', message); - case 3: return new FirebaseMachineLearningError('invalid-argument', message); - case 4: return new FirebaseMachineLearningError('deadline-exceeded', message); - case 5: return new FirebaseMachineLearningError('not-found', message); - case 6: return new FirebaseMachineLearningError('already-exists', message); - case 7: return new FirebaseMachineLearningError('permission-denied', message); - case 8: return new FirebaseMachineLearningError('resource-exhausted', message); - case 9: return new FirebaseMachineLearningError('failed-precondition', message); - case 10: return new FirebaseMachineLearningError('aborted', message); - case 11: return new FirebaseMachineLearningError('out-of-range', message); - case 13: return new FirebaseMachineLearningError('internal-error', message); - case 14: return new FirebaseMachineLearningError('service-unavailable', message); - case 15: return new FirebaseMachineLearningError('data-loss', message); - case 16: return new FirebaseMachineLearningError('unauthenticated', message); - default: - return new FirebaseMachineLearningError('unknown-error', message); + case 1: return new FirebaseMachineLearningError('cancelled', message); + case 2: return new FirebaseMachineLearningError('unknown-error', message); + case 3: return new FirebaseMachineLearningError('invalid-argument', message); + case 4: return new FirebaseMachineLearningError('deadline-exceeded', message); + case 5: return new FirebaseMachineLearningError('not-found', message); + case 6: return new FirebaseMachineLearningError('already-exists', message); + case 7: return new FirebaseMachineLearningError('permission-denied', message); + case 8: return new FirebaseMachineLearningError('resource-exhausted', message); + case 9: return new FirebaseMachineLearningError('failed-precondition', message); + case 10: return new FirebaseMachineLearningError('aborted', message); + case 11: return new FirebaseMachineLearningError('out-of-range', message); + case 13: return new FirebaseMachineLearningError('internal-error', message); + case 14: return new FirebaseMachineLearningError('service-unavailable', message); + case 15: return new FirebaseMachineLearningError('data-loss', message); + case 16: return new FirebaseMachineLearningError('unauthenticated', message); + default: + return new FirebaseMachineLearningError('unknown-error', message); } } diff --git a/src/machine-learning/machine-learning.ts b/src/machine-learning/machine-learning.ts index aa6fbe98f4..e521c5d6af 100644 --- a/src/machine-learning/machine-learning.ts +++ b/src/machine-learning/machine-learning.ts @@ -104,11 +104,11 @@ export class MachineLearning implements FirebaseServiceInterface { * @return {Promise} A Promise fulfilled with the updated model. */ public updateModel(modelId: string, model: ModelOptions): Promise { - const updateMask = utils.generateUpdateMask(model); - return this.signUrlIfPresent(model) + const updateMask = utils.generateUpdateMask(model); + return this.signUrlIfPresent(model) .then((modelContent) => this.client.updateModel(modelId, modelContent, updateMask)) .then((operation) => handleOperation(operation)); - } + } /** * Publishes a model in Firebase ML. @@ -253,9 +253,9 @@ export class Model { !validator.isNonEmptyString(model.updateTime) || !validator.isNonEmptyString(model.displayName) || !validator.isNonEmptyString(model.etag)) { - throw new FirebaseMachineLearningError( - 'invalid-server-response', - `Invalid Model response: ${JSON.stringify(model)}`); + throw new FirebaseMachineLearningError( + 'invalid-server-response', + `Invalid Model response: ${JSON.stringify(model)}`); } this.modelId = extractModelId(model.name); diff --git a/src/messaging.d.ts b/src/messaging.d.ts new file mode 100644 index 0000000000..da8b79e713 --- /dev/null +++ b/src/messaging.d.ts @@ -0,0 +1,1331 @@ +import * as _admin from './index.d'; + +type BaseMessage = { + data?: { [key: string]: string }; + notification?: admin.messaging.Notification; + android?: admin.messaging.AndroidConfig; + webpush?: admin.messaging.WebpushConfig; + apns?: admin.messaging.ApnsConfig; + fcmOptions?: admin.messaging.FcmOptions; +}; + +interface TokenMessage extends BaseMessage { + token: string; +} + +interface TopicMessage extends BaseMessage { + topic: string; +} + +interface ConditionMessage extends BaseMessage { + condition: string; +} + +export namespace admin.messaging { + type Message = TokenMessage | TopicMessage | ConditionMessage; + + interface MulticastMessage extends BaseMessage { + tokens: string[]; + } + + /** + * Represents the Android-specific options that can be included in an + * {@link admin.messaging.Message}. + */ + interface AndroidConfig { + + /** + * Collapse key for the message. Collapse key serves as an identifier for a + * group of messages that can be collapsed, so that only the last message gets + * sent when delivery can be resumed. A maximum of four different collapse keys + * may be active at any given time. + */ + collapseKey?: string; + + /** + * Priority of the message. Must be either `normal` or `high`. + */ + priority?: ('high' | 'normal'); + + /** + * Time-to-live duration of the message in milliseconds. + */ + ttl?: number; + + /** + * Package name of the application where the registration tokens must match + * in order to receive the message. + */ + restrictedPackageName?: string; + + /** + * A collection of data fields to be included in the message. All values must + * be strings. When provided, overrides any data fields set on the top-level + * `admin.messaging.Message`.} + */ + data?: { [key: string]: string }; + + /** + * Android notification to be included in the message. + */ + notification?: AndroidNotification; + + /** + * Options for features provided by the FCM SDK for Android. + */ + fcmOptions?: AndroidFcmOptions; + } + + /** + * Represents the Android-specific notification options that can be included in + * {@link admin.messaging.AndroidConfig}. + */ + interface AndroidNotification { + + /** + * Title of the Android notification. When provided, overrides the title set via + * `admin.messaging.Notification`. + */ + title?: string; + + /** + * Body of the Android notification. When provided, overrides the body set via + * `admin.messaging.Notification`. + */ + body?: string; + + /** + * Icon resource for the Android notification. + */ + icon?: string; + + /** + * Notification icon color in `#rrggbb` format. + */ + color?: string; + + /** + * File name of the sound to be played when the device receives the + * notification. + */ + sound?: string; + + /** + * Notification tag. This is an identifier used to replace existing + * notifications in the notification drawer. If not specified, each request + * creates a new notification. + */ + tag?: string; + + /** + * URL of an image to be displayed in the notification. + */ + imageUrl?: string; + + /** + * Action associated with a user click on the notification. If specified, an + * activity with a matching Intent Filter is launched when a user clicks on the + * notification. + */ + clickAction?: string; + + /** + * Key of the body string in the app's string resource to use to localize the + * body text. + * + */ + bodyLocKey?: string; + + /** + * An array of resource keys that will be used in place of the format + * specifiers in `bodyLocKey`. + */ + bodyLocArgs?: string[]; + + /** + * Key of the title string in the app's string resource to use to localize the + * title text. + */ + titleLocKey?: string; + + /** + * An array of resource keys that will be used in place of the format + * specifiers in `titleLocKey`. + */ + titleLocArgs?: string[]; + + /** + * The Android notification channel ID (new in Android O). The app must create + * a channel with this channel ID before any notification with this channel ID + * can be received. If you don't send this channel ID in the request, or if the + * channel ID provided has not yet been created by the app, FCM uses the channel + * ID specified in the app manifest. + */ + channelId?: string; + + /** + * Sets the "ticker" text, which is sent to accessibility services. Prior to + * API level 21 (Lollipop), sets the text that is displayed in the status bar + * when the notification first arrives. + */ + ticker?: string; + + /** + * When set to `false` or unset, the notification is automatically dismissed when + * the user clicks it in the panel. When set to `true`, the notification persists + * even when the user clicks it. + */ + sticky?: boolean; + + /** + * For notifications that inform users about events with an absolute time reference, sets + * the time that the event in the notification occurred. Notifications + * in the panel are sorted by this time. + */ + eventTimestamp?: Date; + + /** + * Sets whether or not this notification is relevant only to the current device. + * Some notifications can be bridged to other devices for remote display, such as + * a Wear OS watch. This hint can be set to recommend this notification not be bridged. + * See [Wear OS guides](https://developer.android.com/training/wearables/notifications/bridger#existing-method-of-preventing-bridging) + */ + localOnly?: boolean; + + /** + * Sets the relative priority for this notification. Low-priority notifications + * may be hidden from the user in certain situations. Note this priority differs + * from `AndroidMessagePriority`. This priority is processed by the client after + * the message has been delivered. Whereas `AndroidMessagePriority` is an FCM concept + * that controls when the message is delivered. + */ + priority?: ('min' | 'low' | 'default' | 'high' | 'max'); + + /** + * Sets the vibration pattern to use. Pass in an array of milliseconds to + * turn the vibrator on or off. The first value indicates the duration to wait before + * turning the vibrator on. The next value indicates the duration to keep the + * vibrator on. Subsequent values alternate between duration to turn the vibrator + * off and to turn the vibrator on. If `vibrate_timings` is set and `default_vibrate_timings` + * is set to `true`, the default value is used instead of the user-specified `vibrate_timings`. + */ + vibrateTimingsMillis?: number[]; + + /** + * If set to `true`, use the Android framework's default vibrate pattern for the + * notification. Default values are specified in [`config.xml`](https://android.googlesource.com/platform/frameworks/base/+/master/core/res/res/values/config.xml). + * If `default_vibrate_timings` is set to `true` and `vibrate_timings` is also set, + * the default value is used instead of the user-specified `vibrate_timings`. + */ + defaultVibrateTimings?: boolean; + + /** + * If set to `true`, use the Android framework's default sound for the notification. + * Default values are specified in [`config.xml`](https://android.googlesource.com/platform/frameworks/base/+/master/core/res/res/values/config.xml). + */ + defaultSound?: boolean; + + /** + * Settings to control the notification's LED blinking rate and color if LED is + * available on the device. The total blinking time is controlled by the OS. + */ + lightSettings?: LightSettings; + + /** + * If set to `true`, use the Android framework's default LED light settings + * for the notification. Default values are specified in [`config.xml`](https://android.googlesource.com/platform/frameworks/base/+/master/core/res/res/values/config.xml). + * If `default_light_settings` is set to `true` and `light_settings` is also set, + * the user-specified `light_settings` is used instead of the default value. + */ + defaultLightSettings?: boolean; + + /** + * Sets the visibility of the notification. Must be either `private`, `public`, + * or `secret`. If unspecified, defaults to `private`. + */ + visibility?: ('private' | 'public' | 'secret'); + + /** + * Sets the number of items this notification represents. May be displayed as a + * badge count for Launchers that support badging. See [`NotificationBadge`(https://developer.android.com/training/notify-user/badges). + * For example, this might be useful if you're using just one notification to + * represent multiple new messages but you want the count here to represent + * the number of total new messages. If zero or unspecified, systems + * that support badging use the default, which is to increment a number + * displayed on the long-press menu each time a new notification arrives. + */ + notificationCount?: number; + } + + /** + * Represents settings to control notification LED that can be included in + * {@link admin.messaging.AndroidNotification}. + */ + interface LightSettings { + /** + * Required. Sets color of the LED in `#rrggbb` or `#rrggbbaa` format. + */ + color: string; + + /** + * Required. Along with `light_off_duration`, defines the blink rate of LED flashes. + */ + lightOnDurationMillis: number; + + /** + * Required. Along with `light_on_duration`, defines the blink rate of LED flashes. + */ + lightOffDurationMillis: number; + } + + /** + * Represents options for features provided by the FCM SDK for Android. + */ + interface AndroidFcmOptions { + + /** + * The label associated with the message's analytics data. + */ + analyticsLabel?: string; + } + + /** + * Represents the APNs-specific options that can be included in an + * {@link admin.messaging.Message}. Refer to + * [Apple documentation](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html) + * for various headers and payload fields supported by APNs. + */ + interface ApnsConfig { + + /** + * A collection of APNs headers. Header values must be strings. + */ + headers?: { [key: string]: string }; + + /** + * An APNs payload to be included in the message. + */ + payload?: ApnsPayload; + + /** + * Options for features provided by the FCM SDK for iOS. + */ + fcmOptions?: ApnsFcmOptions; + } + /** + * Represents the payload of an APNs message. Mainly consists of the `aps` + * dictionary. But may also contain other arbitrary custom keys. + */ + interface ApnsPayload { + + /** + * The `aps` dictionary to be included in the message. + */ + aps: Aps; + [customData: string]: object; + } + /** + * Represents the [aps dictionary](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html) + * that is part of APNs messages. + */ + interface Aps { + + /** + * Alert to be included in the message. This may be a string or an object of + * type `admin.messaging.ApsAlert`. + */ + alert?: string | ApsAlert; + + /** + * Badge to be displayed with the message. Set to 0 to remove the badge. When + * not specified, the badge will remain unchanged. + */ + badge?: number; + + /** + * Sound to be played with the message. + */ + sound?: string | CriticalSound; + + /** + * Specifies whether to configure a background update notification. + */ + contentAvailable?: boolean; + + /** + * Specifies whether to set the `mutable-content` property on the message + * so the clients can modify the notification via app extensions. + */ + mutableContent?: boolean; + + /** + * Type of the notification. + */ + category?: string; + + /** + * An app-specific identifier for grouping notifications. + */ + threadId?: string; + [customData: string]: any; + } + + interface ApsAlert { + title?: string; + subtitle?: string; + body?: string; + locKey?: string; + locArgs?: string[]; + titleLocKey?: string; + titleLocArgs?: string[]; + subtitleLocKey?: string; + subtitleLocArgs?: string[]; + actionLocKey?: string; + launchImage?: string; + } + + /** + * Represents a critical sound configuration that can be included in the + * `aps` dictionary of an APNs payload. + */ + interface CriticalSound { + + /** + * The critical alert flag. Set to `true` to enable the critical alert. + */ + critical?: boolean; + + /** + * The name of a sound file in the app's main bundle or in the `Library/Sounds` + * folder of the app's container directory. Specify the string "default" to play + * the system sound. + */ + name: string; + + /** + * The volume for the critical alert's sound. Must be a value between 0.0 + * (silent) and 1.0 (full volume). + */ + volume?: number; + } + + /** + * Represents options for features provided by the FCM SDK for iOS. + */ + interface ApnsFcmOptions { + + /** + * The label associated with the message's analytics data. + */ + analyticsLabel?: string; + + /** + * URL of an image to be displayed in the notification. + */ + imageUrl?: string; + } + + /** + * Represents platform-independent options for features provided by the FCM SDKs. + */ + interface FcmOptions { + + /** + * The label associated with the message's analytics data. + */ + analyticsLabel?: string; + } + + + /** + * A notification that can be included in {@link admin.messaging.Message}. + */ + interface Notification { + /** + * The title of the notification. + */ + title?: string; + /** + * The notification body + */ + body?: string; + /** + * URL of an image to be displayed in the notification. + */ + imageUrl?: string; + } + /** + * Represents the WebPush protocol options that can be included in an + * {@link admin.messaging.Message}. + */ + interface WebpushConfig { + + /** + * A collection of WebPush headers. Header values must be strings. + * + * See [WebPush specification](https://tools.ietf.org/html/rfc8030#section-5) + * for supported headers. + */ + headers?: { [key: string]: string }; + + /** + * A collection of data fields. + */ + data?: { [key: string]: string }; + + /** + * A WebPush notification payload to be included in the message. + */ + notification?: WebpushNotification; + + /** + * Options for features provided by the FCM SDK for Web. + */ + fcmOptions?: WebpushFcmOptions; + } + + /** Represents options for features provided by the FCM SDK for Web + * (which are not part of the Webpush standard). + */ + interface WebpushFcmOptions { + + /** + * The link to open when the user clicks on the notification. + * For all URL values, HTTPS is required. + */ + link?: string; + } + + /** + * Represents the WebPush-specific notification options that can be included in + * {@link admin.messaging.WebpushConfig}. This supports most of the standard + * options as defined in the Web Notification + * [specification](https://developer.mozilla.org/en-US/docs/Web/API/notification/Notification). + */ + interface WebpushNotification { + + /** + * Title text of the notification. + */ + title?: string; + + /** + * An array of notification actions representing the actions + * available to the user when the notification is presented. + */ + actions?: Array<{ + + /** + * An action available to the user when the notification is presented + */ + action: string; + + /** + * Optional icon for a notification action. + */ + icon?: string; + + /** + * Title of the notification action. + */ + title: string; + }>; + + /** + * URL of the image used to represent the notification when there is + * not enough space to display the notification itself. + */ + badge?: string; + + /** + * Body text of the notification. + */ + body?: string; + + /** + * Arbitrary data that you want associated with the notification. + * This can be of any data type. + */ + data?: any; + + /** + * The direction in which to display the notification. Must be one + * of `auto`, `ltr` or `rtl`. + */ + dir?: 'auto' | 'ltr' | 'rtl'; + + /** + * URL to the notification icon. + */ + icon?: string; + + /** + * URL of an image to be displayed in the notification. + */ + image?: string; + + /** + * The notification's language as a BCP 47 language tag. + */ + lang?: string; + + /** + * A boolean specifying whether the user should be notified after a + * new notification replaces an old one. Defaults to false. + */ + renotify?: boolean; + + /** + * Indicates that a notification should remain active until the user + * clicks or dismisses it, rather than closing automatically. + * Defaults to false. + */ + requireInteraction?: boolean; + + /** + * A boolean specifying whether the notification should be silent. + * Defaults to false. + */ + silent?: boolean; + + /** + * An identifying tag for the notification. + */ + tag?: string; + + /** + * Timestamp of the notification. Refer to + * https://developer.mozilla.org/en-US/docs/Web/API/notification/timestamp + * for details. + */ + timestamp?: number; + + /** + * A vibration pattern for the device's vibration hardware to emit + * when the notification fires. + */ + vibrate?: number | number[]; + [key: string]: any; + } + /** + * Interface representing an FCM legacy API data message payload. Data + * messages let developers send up to 4KB of custom key-value pairs. The + * keys and values must both be strings. Keys can be any custom string, + * except for the following reserved strings: + * + * * `"from"` + * * Anything starting with `"google."`. + * + * See [Build send requests](/docs/cloud-messaging/send-message) + * for code samples and detailed documentation. + */ + interface DataMessagePayload { + [key: string]: string; + } + + /** + * Interface representing an FCM legacy API notification message payload. + * Notification messages let developers send up to 4KB of predefined + * key-value pairs. Accepted keys are outlined below. + * + * See [Build send requests](/docs/cloud-messaging/send-message) + * for code samples and detailed documentation. + */ + interface NotificationMessagePayload { + tag?: string; + + /** + * The notification's body text. + * + * **Platforms:** iOS, Android, Web + */ + body?: string; + + /** + * The notification's icon. + * + * **Android:** Sets the notification icon to `myicon` for drawable resource + * `myicon`. If you don't send this key in the request, FCM displays the + * launcher icon specified in your app manifest. + * + * **Web:** The URL to use for the notification's icon. + * + * **Platforms:** Android, Web + */ + icon?: string; + + /** + * The value of the badge on the home screen app icon. + * + * If not specified, the badge is not changed. + * + * If set to `0`, the badge is removed. + * + * **Platforms:** iOS + */ + badge?: string; + + /** + * The notification icon's color, expressed in `#rrggbb` format. + * + * **Platforms:** Android + */ + color?: string; + + /** + * Identifier used to replace existing notifications in the notification drawer. + * + * If not specified, each request creates a new notification. + * + * If specified and a notification with the same tag is already being shown, + * the new notification replaces the existing one in the notification drawer. + * + * **Platforms:** Android + */ + sound?: string; + + /** + * The notification's title. + * + * **Platforms:** iOS, Android, Web + */ + title?: string; + + /** + * The key to the body string in the app's string resources to use to localize + * the body text to the user's current localization. + * + * **iOS:** Corresponds to `loc-key` in the APNs payload. See + * [Payload Key Reference](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html) + * and + * [Localizing the Content of Your Remote Notifications](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH10-SW9) + * for more information. + * + * **Android:** See + * [String Resources](http://developer.android.com/guide/topics/resources/string-resource.html) * for more information. + * + * **Platforms:** iOS, Android + */ + bodyLocKey?: string; + + /** + * Variable string values to be used in place of the format specifiers in + * `body_loc_key` to use to localize the body text to the user's current + * localization. + * + * The value should be a stringified JSON array. + * + * **iOS:** Corresponds to `loc-args` in the APNs payload. See + * [Payload Key Reference](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html) + * and + * [Localizing the Content of Your Remote Notifications](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH10-SW9) + * for more information. + * + * **Android:** See + * [Formatting and Styling](http://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling) + * for more information. + * + * **Platforms:** iOS, Android + */ + bodyLocArgs?: string; + + /** + * Action associated with a user click on the notification. If specified, an + * activity with a matching Intent Filter is launched when a user clicks on the + * notification. + * + * * **Platforms:** Android + */ + clickAction?: string; + + /** + * The key to the title string in the app's string resources to use to localize + * the title text to the user's current localization. + * + * **iOS:** Corresponds to `title-loc-key` in the APNs payload. See + * [Payload Key Reference](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html) + * and + * [Localizing the Content of Your Remote Notifications](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH10-SW9) + * for more information. + * + * **Android:** See + * [String Resources](http://developer.android.com/guide/topics/resources/string-resource.html) + * for more information. + * + * **Platforms:** iOS, Android + */ + titleLocKey?: string; + + /** + * Variable string values to be used in place of the format specifiers in + * `title_loc_key` to use to localize the title text to the user's current + * localization. + * + * The value should be a stringified JSON array. + * + * **iOS:** Corresponds to `title-loc-args` in the APNs payload. See + * [Payload Key Reference](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html) + * and + * [Localizing the Content of Your Remote Notifications](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH10-SW9) + * for more information. + * + * **Android:** See + * [Formatting and Styling](http://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling) + * for more information. + * + * **Platforms:** iOS, Android + */ + titleLocArgs?: string; + [key: string]: string | undefined; + } + + /** + * Interface representing a Firebase Cloud Messaging message payload. One or + * both of the `data` and `notification` keys are required. + * + * See + * [Build send requests](/docs/cloud-messaging/send-message) + * for code samples and detailed documentation. + */ + interface MessagingPayload { + + /** + * The data message payload. + */ + data?: admin.messaging.DataMessagePayload; + + /** + * The notification message payload. + */ + notification?: admin.messaging.NotificationMessagePayload; + } + /** + * Interface representing the options that can be provided when sending a + * message via the FCM legacy APIs. + * + * See [Build send requests](/docs/cloud-messaging/send-message) + * for code samples and detailed documentation. + */ + interface MessagingOptions { + + /** + * Whether or not the message should actually be sent. When set to `true`, + * allows developers to test a request without actually sending a message. When + * set to `false`, the message will be sent. + * + * **Default value:** `false` + */ + dryRun?: boolean; + + /** + * The priority of the message. Valid values are `"normal"` and `"high".` On + * iOS, these correspond to APNs priorities `5` and `10`. + * + * By default, notification messages are sent with high priority, and data + * messages are sent with normal priority. Normal priority optimizes the client + * app's battery consumption and should be used unless immediate delivery is + * required. For messages with normal priority, the app may receive the message + * with unspecified delay. + * + * When a message is sent with high priority, it is sent immediately, and the + * app can wake a sleeping device and open a network connection to your server. + * + * For more information, see + * [Setting the priority of a message](/docs/cloud-messaging/concept-options#setting-the-priority-of-a-message). + * + * **Default value:** `"high"` for notification messages, `"normal"` for data + * messages + */ + priority?: string; + + /** + * How long (in seconds) the message should be kept in FCM storage if the device + * is offline. The maximum time to live supported is four weeks, and the default + * value is also four weeks. For more information, see + * [Setting the lifespan of a message](/docs/cloud-messaging/concept-options#ttl). + * + * **Default value:** `2419200` (representing four weeks, in seconds) + */ + timeToLive?: number; + + /** + * String identifying a group of messages (for example, "Updates Available") + * that can be collapsed, so that only the last message gets sent when delivery + * can be resumed. This is used to avoid sending too many of the same messages + * when the device comes back online or becomes active. + * + * There is no guarantee of the order in which messages get sent. + * + * A maximum of four different collapse keys is allowed at any given time. This + * means FCM server can simultaneously store four different + * send-to-sync messages per client app. If you exceed this number, there is no + * guarantee which four collapse keys the FCM server will keep. + * + * **Default value:** None + */ + collapseKey?: string; + + /** + * On iOS, use this field to represent `mutable-content` in the APNs payload. + * When a notification is sent and this is set to `true`, the content of the + * notification can be modified before it is displayed, using a + * [Notification Service app extension](https://developer.apple.com/reference/usernotifications/unnotificationserviceextension) + * + * On Android and Web, this parameter will be ignored. + * + * **Default value:** `false` + */ + mutableContent?: boolean; + + /** + * On iOS, use this field to represent `content-available` in the APNs payload. + * When a notification or data message is sent and this is set to `true`, an + * inactive client app is awoken. On Android, data messages wake the app by + * default. On Chrome, this flag is currently not supported. + * + * **Default value:** `false` + */ + contentAvailable?: boolean; + + /** + * The package name of the application which the registration tokens must match + * in order to receive the message. + * + * **Default value:** None + */ + restrictedPackageName?: string; + [key: string]: any | undefined; + } + + /** + * Interface representing the status of a message sent to an individual device + * via the FCM legacy APIs. + * + * See + * [Send to individual devices](/docs/cloud-messaging/admin/send-messages#send_to_individual_devices) + * for code samples and detailed documentation. + */ + interface MessagingDeviceResult { + + /** + * The error that occurred when processing the message for the recipient. + */ + error?: _admin.FirebaseError; + + /** + * A unique ID for the successfully processed message. + */ + messageId?: string; + + /** + * The canonical registration token for the client app that the message was + * processed and sent to. You should use this value as the registration token + * for future requests. Otherwise, future messages might be rejected. + */ + canonicalRegistrationToken?: string; + } + + /** + * Interface representing the server response from the legacy + * {@link https://firebase.google.com/docs/reference/admin/node/admin.messaging.Messaging#sendToDevice `sendToDevice()`} method. + * + * See + * [Send to individual devices](/docs/cloud-messaging/admin/send-messages#send_to_individual_devices) + * for code samples and detailed documentation. + */ + interface MessagingDevicesResponse { + + /** + * The number of results that contain a canonical registration token. A + * canonical registration token is the registration token corresponding to the + * last registration requested by the client app. This is the token that you + * should use when sending future messages to the device. + * + * You can access the canonical registration tokens within the + * [`results`](#results) property. + */ + canonicalRegistrationTokenCount: number; + + /** + * The number of messages that could not be processed and resulted in an error. + */ + failureCount: number; + + /** + * The unique ID number identifying this multicast message. + */ + multicastId: number; + + /** + * An array of `MessagingDeviceResult` objects representing the status of the + * processed messages. The objects are listed in the same order as in the + * request. That is, for each registration token in the request, its result has + * the same index in this array. If only a single registration token is + * provided, this array will contain a single object. + */ + results: admin.messaging.MessagingDeviceResult[]; + + /** + * The number of messages that were successfully processed and sent. + */ + successCount: number; + } + /** + * Interface representing the server response from the + * {@link https://firebase.google.com/docs/reference/admin/node/admin.messaging.Messaging#sendToDeviceGroup `sendToDeviceGroup()`} + * method. + * + * See + * [Send messages to device groups](/docs/cloud-messaging/send-message?authuser=0#send_messages_to_device_groups) + * for code samples and detailed documentation. + */ + interface MessagingDeviceGroupResponse { + + /** + * The number of messages that could not be processed and resulted in an error. + */ + successCount: number; + + /** + * The number of messages that could not be processed and resulted in an error. + */ + failureCount: number; + + /** + * An array of registration tokens that failed to receive the message. + */ + failedRegistrationTokens: string[]; + } + + /** + * Interface representing the server response from the legacy + * {@link https://firebase.google.com/docs/reference/admin/node/admin.messaging.Messaging#sendToTopic `sendToTopic()`} method. + * + * See + * [Send to a topic](/docs/cloud-messaging/admin/send-messages#send_to_a_topic) + * for code samples and detailed documentation. + */ + interface MessagingTopicResponse { + + /** + * The message ID for a successfully received request which FCM will attempt to + * deliver to all subscribed devices. + */ + messageId: number; + } + + /** + * Interface representing the server response from the legacy + * {@link https://firebase.google.com/docs/reference/admin/node/admin.messaging.Messaging#sendToCondition `sendToCondition()`} method. + * + * See + * [Send to a condition](/docs/cloud-messaging/admin/send-messages#send_to_a_condition) + * for code samples and detailed documentation. + */ + interface MessagingConditionResponse { + + /** + * The message ID for a successfully received request which FCM will attempt to + * deliver to all subscribed devices. + */ + messageId: number; + } + + /** + * Interface representing the server response from the + * {@link https://firebase.google.com/docs/reference/admin/node/admin.messaging.Messaging#subscribeToTopic `subscribeToTopic()`} and + * {@link + * admin.messaging.Messaging#unsubscribeFromTopic + * `unsubscribeFromTopic()`} + * methods. + * + * See + * [Manage topics from the server](/docs/cloud-messaging/manage-topics) + * for code samples and detailed documentation. + */ + interface MessagingTopicManagementResponse { + + /** + * The number of registration tokens that could not be subscribed to the topic + * and resulted in an error. + */ + failureCount: number; + + /** + * The number of registration tokens that were successfully subscribed to the + * topic. + */ + successCount: number; + + /** + * An array of errors corresponding to the provided registration token(s). The + * length of this array will be equal to [`failureCount`](#failureCount). + */ + errors: _admin.FirebaseArrayIndexError[]; + } + + /** + * Interface representing the server response from the + * {@link https://firebase.google.com/docs/reference/admin/node/admin.messaging.Messaging#sendAll `sendAll()`} and + * {@link https://firebase.google.com/docs/reference/admin/node/admin.messaging.Messaging#sendMulticast `sendMulticast()`} methods. + */ + interface BatchResponse { + + /** + * An array of responses, each corresponding to a message. + */ + responses: admin.messaging.SendResponse[]; + + /** + * The number of messages that were successfully handed off for sending. + */ + successCount: number; + + /** + * The number of messages that resulted in errors when sending. + */ + failureCount: number; + } + /** + * Interface representing the status of an individual message that was sent as + * part of a batch request. + */ + interface SendResponse { + + /** + * A boolean indicating if the message was successfully handed off to FCM or + * not. When true, the `messageId` attribute is guaranteed to be set. When + * false, the `error` attribute is guaranteed to be set. + */ + success: boolean; + + /** + * A unique message ID string, if the message was handed off to FCM for + * delivery. + * + */ + messageId?: string; + + /** + * An error, if the message was not handed off to FCM successfully. + */ + error?: _admin.FirebaseError; + } + + /** + * Gets the {@link admin.messaging.Messaging `Messaging`} service for the + * current app. + * + * @example + * ```javascript + * var messaging = app.messaging(); + * // The above is shorthand for: + * // var messaging = admin.messaging(app); + * ``` + * + * @return The `Messaging` service for the current app. + */ + interface Messaging { + /** + * The {@link admin.app.App app} associated with the current `Messaging` service + * instance. + * + * @example + * ```javascript + * var app = messaging.app; + * ``` + */ + app: _admin.app.App; + + /** + * Sends the given message via FCM. + * + * @param message The message payload. + * @param dryRun Whether to send the message in the dry-run + * (validation only) mode. + * @return A promise fulfilled with a unique message ID + * string after the message has been successfully handed off to the FCM + * service for delivery. + */ + send(message: admin.messaging.Message, dryRun?: boolean): Promise; + + /** + * Sends all the messages in the given array via Firebase Cloud Messaging. + * Employs batching to send the entire list as a single RPC call. Compared + * to the `send()` method, this method is a significantly more efficient way + * to send multiple messages. + * + * The responses list obtained from the return value + * corresponds to the order of tokens in the `MulticastMessage`. An error + * from this method indicates a total failure -- i.e. none of the messages in + * the list could be sent. Partial failures are indicated by a `BatchResponse` + * return value. + * + * @param messages A non-empty array + * containing up to 500 messages. + * @param dryRun Whether to send the messages in the dry-run + * (validation only) mode. + * @return A Promise fulfilled with an object representing the result of the + * send operation. + */ + sendAll( + messages: Array, + dryRun?: boolean + ): Promise; + + /** + * Sends the given multicast message to all the FCM registration tokens + * specified in it. + * + * This method uses the `sendAll()` API under the hood to send the given + * message to all the target recipients. The responses list obtained from the + * return value corresponds to the order of tokens in the `MulticastMessage`. + * An error from this method indicates a total failure -- i.e. the message was + * not sent to any of the tokens in the list. Partial failures are indicated by + * a `BatchResponse` return value. + * + * @param message A multicast message + * containing up to 500 tokens. + * @param dryRun Whether to send the message in the dry-run + * (validation only) mode. + * @return A Promise fulfilled with an object representing the result of the + * send operation. + */ + sendMulticast( + message: admin.messaging.MulticastMessage, + dryRun?: boolean + ): Promise; + + /** + * Sends an FCM message to a single device corresponding to the provided + * registration token. + * + * See + * [Send to individual devices](/docs/cloud-messaging/admin/legacy-fcm#send_to_individual_devices) + * for code samples and detailed documentation. Takes either a + * `registrationToken` to send to a single device or a + * `registrationTokens` parameter containing an array of tokens to send + * to multiple devices. + * + * @param registrationToken A device registration token or an array of + * device registration tokens to which the message should be sent. + * @param payload The message payload. + * @param options Optional options to + * alter the message. + * + * @return A promise fulfilled with the server's response after the message + * has been sent. + */ + sendToDevice( + registrationToken: string | string[], + payload: admin.messaging.MessagingPayload, + options?: admin.messaging.MessagingOptions + ): Promise; + + /** + * Sends an FCM message to a device group corresponding to the provided + * notification key. + * + * See + * [Send to a device group](/docs/cloud-messaging/admin/legacy-fcm#send_to_a_device_group) + * for code samples and detailed documentation. + * + * @param notificationKey The notification key for the device group to + * which to send the message. + * @param payload The message payload. + * @param options Optional options to + * alter the message. + * + * @return A promise fulfilled with the server's response after the message + * has been sent. + */ + sendToDeviceGroup( + notificationKey: string, + payload: admin.messaging.MessagingPayload, + options?: admin.messaging.MessagingOptions + ): Promise; + + /** + * Sends an FCM message to a topic. + * + * See + * [Send to a topic](/docs/cloud-messaging/admin/legacy-fcm#send_to_a_topic) + * for code samples and detailed documentation. + * + * @param topic The topic to which to send the message. + * @param payload The message payload. + * @param options Optional options to + * alter the message. + * + * @return A promise fulfilled with the server's response after the message + * has been sent. + */ + sendToTopic( + topic: string, + payload: admin.messaging.MessagingPayload, + options?: admin.messaging.MessagingOptions + ): Promise; + + /** + * Sends an FCM message to a condition. + * + * See + * [Send to a condition](/docs/cloud-messaging/admin/legacy-fcm#send_to_a_condition) + * for code samples and detailed documentation. + * + * @param condition The condition determining to which topics to send + * the message. + * @param payload The message payload. + * @param options Optional options to + * alter the message. + * + * @return A promise fulfilled with the server's response after the message + * has been sent. + */ + sendToCondition( + condition: string, + payload: admin.messaging.MessagingPayload, + options?: admin.messaging.MessagingOptions + ): Promise; + + /** + * Subscribes a device to an FCM topic. + * + * See [Subscribe to a + * topic](/docs/cloud-messaging/manage-topics#suscribe_and_unsubscribe_using_the) + * for code samples and detailed documentation. Optionally, you can provide an + * array of tokens to subscribe multiple devices. + * + * @param registrationTokens A token or array of registration tokens + * for the devices to subscribe to the topic. + * @param topic The topic to which to subscribe. + * + * @return A promise fulfilled with the server's response after the device has been + * subscribed to the topic. + */ + subscribeToTopic( + registrationTokens: string | string[], + topic: string + ): Promise; + + /** + * Unsubscribes a device from an FCM topic. + * + * See [Unsubscribe from a + * topic](/docs/cloud-messaging/admin/manage-topic-subscriptions#unsubscribe_from_a_topic) + * for code samples and detailed documentation. Optionally, you can provide an + * array of tokens to unsubscribe multiple devices. + * + * @param registrationTokens A device registration token or an array of + * device registration tokens to unsubscribe from the topic. + * @param topic The topic from which to unsubscribe. + * + * @return A promise fulfilled with the server's response after the device has been + * unsubscribed from the topic. + */ + unsubscribeFromTopic( + registrationTokens: string | string[], + topic: string + ): Promise; + } +} diff --git a/src/messaging/batch-request.ts b/src/messaging/batch-request.ts index b0b2937a32..0c6995a874 100644 --- a/src/messaging/batch-request.ts +++ b/src/messaging/batch-request.ts @@ -19,7 +19,7 @@ import { } from '../utils/api-request'; import { FirebaseAppError, AppErrorCodes } from '../utils/error'; -const PART_BOUNDARY: string = '__END_OF_PART__'; +const PART_BOUNDARY = '__END_OF_PART__'; const TEN_SECONDS_IN_MILLIS = 10000; /** @@ -85,7 +85,7 @@ export class BatchRequestClient { } private getMultipartPayload(requests: SubRequest[]): Buffer { - let buffer: string = ''; + let buffer = ''; requests.forEach((request: SubRequest, idx: number) => { buffer += createPart(request, PART_BOUNDARY, idx); }); @@ -107,7 +107,7 @@ export class BatchRequestClient { */ function createPart(request: SubRequest, boundary: string, idx: number): string { const serializedRequest: string = serializeSubRequest(request); - let part: string = `--${boundary}\r\n`; + let part = `--${boundary}\r\n`; part += `Content-Length: ${serializedRequest.length}\r\n`; part += 'Content-Type: application/http\r\n'; part += `content-id: ${idx + 1}\r\n`; @@ -127,7 +127,7 @@ function createPart(request: SubRequest, boundary: string, idx: number): string */ function serializeSubRequest(request: SubRequest): string { const requestBody: string = JSON.stringify(request.body); - let messagePayload: string = `POST ${request.url} HTTP/1.1\r\n`; + let messagePayload = `POST ${request.url} HTTP/1.1\r\n`; messagePayload += `Content-Length: ${requestBody.length}\r\n`; messagePayload += 'Content-Type: application/json; charset=UTF-8\r\n'; if (request.headers) { diff --git a/src/messaging/messaging-api-request.ts b/src/messaging/messaging-api-request.ts index c9aee5cccd..9af1ff8571 100644 --- a/src/messaging/messaging-api-request.ts +++ b/src/messaging/messaging-api-request.ts @@ -83,13 +83,13 @@ export class FirebaseMessagingRequestHandler { // Return entire response. return response.data; }) - .catch((err) => { - if (err instanceof HttpError) { - throw createFirebaseError(err); - } - // Re-throw the error if it already has the proper format. - throw err; - }); + .catch((err) => { + if (err instanceof HttpError) { + throw createFirebaseError(err); + } + // Re-throw the error if it already has the proper format. + throw err; + }); } /** diff --git a/src/messaging/messaging-errors.ts b/src/messaging/messaging-errors.ts index 1fd1809aec..b317404087 100644 --- a/src/messaging/messaging-errors.ts +++ b/src/messaging/messaging-errors.ts @@ -35,24 +35,24 @@ export function createFirebaseError(err: HttpError): FirebaseMessagingError { } // Non-JSON response - let error: {code: string, message: string}; + let error: {code: string; message: string}; switch (err.response.status) { - case 400: - error = MessagingClientErrorCode.INVALID_ARGUMENT; - break; - case 401: - case 403: - error = MessagingClientErrorCode.AUTHENTICATION_ERROR; - break; - case 500: - error = MessagingClientErrorCode.INTERNAL_ERROR; - break; - case 503: - error = MessagingClientErrorCode.SERVER_UNAVAILABLE; - break; - default: - // Treat non-JSON responses with unexpected status codes as unknown errors. - error = MessagingClientErrorCode.UNKNOWN_ERROR; + case 400: + error = MessagingClientErrorCode.INVALID_ARGUMENT; + break; + case 401: + case 403: + error = MessagingClientErrorCode.AUTHENTICATION_ERROR; + break; + case 500: + error = MessagingClientErrorCode.INTERNAL_ERROR; + break; + case 503: + error = MessagingClientErrorCode.SERVER_UNAVAILABLE; + break; + default: + // Treat non-JSON responses with unexpected status codes as unknown errors. + error = MessagingClientErrorCode.UNKNOWN_ERROR; } return new FirebaseMessagingError({ code: error.code, diff --git a/src/messaging/messaging-types.ts b/src/messaging/messaging-types.ts index 50bb3a4b37..27891d27ed 100644 --- a/src/messaging/messaging-types.ts +++ b/src/messaging/messaging-types.ts @@ -297,7 +297,7 @@ export interface SendResponse { * * @param {Message} Message An object to be validated. */ -export function validateMessage(message: Message) { +export function validateMessage(message: Message): void { if (!validator.isNonNullObject(message)) { throw new FirebaseMessagingError( MessagingClientErrorCode.INVALID_PAYLOAD, 'Message must be a non-null object'); @@ -337,7 +337,7 @@ export function validateMessage(message: Message) { * @param {object} map An object to be validated. * @param {string} label A label to be included in the errors thrown. */ -function validateStringMap(map: {[key: string]: any} | undefined, label: string) { +function validateStringMap(map: {[key: string]: any} | undefined, label: string): void { if (typeof map === 'undefined') { return; } else if (!validator.isNonNullObject(map)) { @@ -357,7 +357,7 @@ function validateStringMap(map: {[key: string]: any} | undefined, label: string) * * @param {WebpushConfig} config An object to be validated. */ -function validateWebpushConfig(config: WebpushConfig | undefined) { +function validateWebpushConfig(config: WebpushConfig | undefined): void { if (typeof config === 'undefined') { return; } else if (!validator.isNonNullObject(config)) { @@ -374,7 +374,7 @@ function validateWebpushConfig(config: WebpushConfig | undefined) { * * @param {ApnsConfig} config An object to be validated. */ -function validateApnsConfig(config: ApnsConfig | undefined) { +function validateApnsConfig(config: ApnsConfig | undefined): void { if (typeof config === 'undefined') { return; } else if (!validator.isNonNullObject(config)) { @@ -391,7 +391,7 @@ function validateApnsConfig(config: ApnsConfig | undefined) { * * @param {ApnsFcmOptions} fcmOptions An object to be validated. */ -function validateApnsFcmOptions(fcmOptions: ApnsFcmOptions | undefined) { +function validateApnsFcmOptions(fcmOptions: ApnsFcmOptions | undefined): void { if (typeof fcmOptions === 'undefined') { return; } else if (!validator.isNonNullObject(fcmOptions)) { @@ -402,8 +402,8 @@ function validateApnsFcmOptions(fcmOptions: ApnsFcmOptions | undefined) { if (typeof fcmOptions.imageUrl !== 'undefined' && !validator.isURL(fcmOptions.imageUrl)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, - 'imageUrl must be a valid URL string'); + MessagingClientErrorCode.INVALID_PAYLOAD, + 'imageUrl must be a valid URL string'); } if (typeof fcmOptions.analyticsLabel !== 'undefined' && !validator.isString(fcmOptions.analyticsLabel)) { @@ -417,8 +417,8 @@ function validateApnsFcmOptions(fcmOptions: ApnsFcmOptions | undefined) { Object.keys(propertyMappings).forEach((key) => { if (key in fcmOptions && propertyMappings[key] in fcmOptions) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, - `Multiple specifications for ${key} in ApnsFcmOptions`); + MessagingClientErrorCode.INVALID_PAYLOAD, + `Multiple specifications for ${key} in ApnsFcmOptions`); } }); renameProperties(fcmOptions, propertyMappings); @@ -429,7 +429,7 @@ function validateApnsFcmOptions(fcmOptions: ApnsFcmOptions | undefined) { * * @param {FcmOptions} fcmOptions An object to be validated. */ -function validateFcmOptions(fcmOptions: FcmOptions | undefined) { +function validateFcmOptions(fcmOptions: FcmOptions | undefined): void { if (typeof fcmOptions === 'undefined') { return; } else if (!validator.isNonNullObject(fcmOptions)) { @@ -448,17 +448,17 @@ function validateFcmOptions(fcmOptions: FcmOptions | undefined) { * * @param {Notification} notification An object to be validated. */ -function validateNotification(notification: Notification | undefined) { +function validateNotification(notification: Notification | undefined): void { if (typeof notification === 'undefined') { return; } else if (!validator.isNonNullObject(notification)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'notification must be a non-null object'); + MessagingClientErrorCode.INVALID_PAYLOAD, 'notification must be a non-null object'); } if (typeof notification.imageUrl !== 'undefined' && !validator.isURL(notification.imageUrl)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'notification.imageUrl must be a valid URL string'); + MessagingClientErrorCode.INVALID_PAYLOAD, 'notification.imageUrl must be a valid URL string'); } const propertyMappings: {[key: string]: string} = { @@ -467,8 +467,8 @@ function validateNotification(notification: Notification | undefined) { Object.keys(propertyMappings).forEach((key) => { if (key in notification && propertyMappings[key] in notification) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, - `Multiple specifications for ${key} in Notification`); + MessagingClientErrorCode.INVALID_PAYLOAD, + `Multiple specifications for ${key} in Notification`); } }); renameProperties(notification, propertyMappings); @@ -479,7 +479,7 @@ function validateNotification(notification: Notification | undefined) { * * @param {ApnsPayload} payload An object to be validated. */ -function validateApnsPayload(payload: ApnsPayload | undefined) { +function validateApnsPayload(payload: ApnsPayload | undefined): void { if (typeof payload === 'undefined') { return; } else if (!validator.isNonNullObject(payload)) { @@ -495,7 +495,7 @@ function validateApnsPayload(payload: ApnsPayload | undefined) { * * @param {Aps} aps An object to be validated. */ -function validateAps(aps: Aps) { +function validateAps(aps: Aps): void { if (typeof aps === 'undefined') { return; } else if (!validator.isNonNullObject(aps)) { @@ -537,7 +537,7 @@ function validateAps(aps: Aps) { } } -function validateApsSound(sound: string | CriticalSound | undefined) { +function validateApsSound(sound: string | CriticalSound | undefined): void { if (typeof sound === 'undefined' || validator.isNonEmptyString(sound)) { return; } else if (!validator.isNonNullObject(sound)) { @@ -583,7 +583,7 @@ function validateApsSound(sound: string | CriticalSound | undefined) { * * @param {string | ApsAlert} alert An alert string or an object to be validated. */ -function validateApsAlert(alert: string | ApsAlert | undefined) { +function validateApsAlert(alert: string | ApsAlert | undefined): void { if (typeof alert === 'undefined' || validator.isString(alert)) { return; } else if (!validator.isNonNullObject(alert)) { @@ -632,7 +632,7 @@ function validateApsAlert(alert: string | ApsAlert | undefined) { * * @param {AndroidConfig} config An object to be validated. */ -function validateAndroidConfig(config: AndroidConfig | undefined) { +function validateAndroidConfig(config: AndroidConfig | undefined): void { if (typeof config === 'undefined') { return; } else if (!validator.isNonNullObject(config)) { @@ -667,7 +667,7 @@ function validateAndroidConfig(config: AndroidConfig | undefined) { * * @param {AndroidNotification} notification An object to be validated. */ -function validateAndroidNotification(notification: AndroidNotification | undefined) { +function validateAndroidNotification(notification: AndroidNotification | undefined): void { if (typeof notification === 'undefined') { return; } else if (!validator.isNonNullObject(notification)) { @@ -694,8 +694,8 @@ function validateAndroidNotification(notification: AndroidNotification | undefin if (typeof notification.imageUrl !== 'undefined' && !validator.isURL(notification.imageUrl)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, - 'android.notification.imageUrl must be a valid URL string'); + MessagingClientErrorCode.INVALID_PAYLOAD, + 'android.notification.imageUrl must be a valid URL string'); } if (typeof notification.eventTimestamp !== 'undefined') { @@ -767,7 +767,7 @@ function validateAndroidNotification(notification: AndroidNotification | undefin * * @param {LightSettings} lightSettings An object to be validated. */ -function validateLightSettings(lightSettings?: LightSettings) { +function validateLightSettings(lightSettings?: LightSettings): void { if (typeof lightSettings === 'undefined') { return; } else if (!validator.isNonNullObject(lightSettings)) { @@ -824,7 +824,7 @@ function validateLightSettings(lightSettings?: LightSettings) { * * @param {AndroidFcmOptions} fcmOptions An object to be validated. */ -function validateAndroidFcmOptions(fcmOptions: AndroidFcmOptions | undefined) { +function validateAndroidFcmOptions(fcmOptions: AndroidFcmOptions | undefined): void { if (typeof fcmOptions === 'undefined') { return; } else if (!validator.isNonNullObject(fcmOptions)) { diff --git a/src/messaging/messaging.ts b/src/messaging/messaging.ts index b37f8c987d..6cabb1104b 100644 --- a/src/messaging/messaging.ts +++ b/src/messaging/messaging.ts @@ -32,6 +32,8 @@ import { import * as utils from '../utils'; import * as validator from '../utils/validator'; +/* eslint-disable @typescript-eslint/camelcase */ + // FCM endpoints const FCM_SEND_HOST = 'fcm.googleapis.com'; const FCM_SEND_PATH = '/fcm/send'; @@ -251,7 +253,7 @@ export class Messaging implements FirebaseServiceInterface { } return this.getUrlPath() .then((urlPath) => { - const request: {message: Message, validate_only?: boolean} = {message: copy}; + const request: {message: Message; validate_only?: boolean} = {message: copy}; if (dryRun) { request.validate_only = true; } @@ -304,7 +306,7 @@ export class Messaging implements FirebaseServiceInterface { .then((urlPath) => { const requests: SubRequest[] = copy.map((message) => { validateMessage(message); - const request: {message: Message, validate_only?: boolean} = {message}; + const request: {message: Message; validate_only?: boolean} = {message}; if (dryRun) { request.validate_only = true; } @@ -718,7 +720,7 @@ export class Messaging implements FirebaseServiceInterface { private validateMessagingPayloadAndOptionsTypes( payload: MessagingPayload, options: MessagingOptions, - ) { + ): void { // Validate the payload is an object if (!validator.isNonNullObject(payload)) { throw new FirebaseMessagingError( @@ -744,7 +746,7 @@ export class Messaging implements FirebaseServiceInterface { * @return {MessagingPayload} A copy of the provided payload with whitelisted properties switched * from camelCase to underscore_case. */ - private validateMessagingPayload(payload: MessagingPayload) { + private validateMessagingPayload(payload: MessagingPayload): MessagingPayload { const payloadCopy: MessagingPayload = deepCopy(payload); const payloadKeys = Object.keys(payloadCopy); @@ -772,7 +774,7 @@ export class Messaging implements FirebaseServiceInterface { ); } - const validatePayload = (payloadKey: string, value: DataMessagePayload | NotificationMessagePayload) => { + const validatePayload = (payloadKey: string, value: DataMessagePayload | NotificationMessagePayload): void => { // Validate each top-level key in the payload is an object if (!validator.isNonNullObject(value)) { throw new FirebaseMessagingError( @@ -835,7 +837,7 @@ export class Messaging implements FirebaseServiceInterface { * @return {MessagingOptions} A copy of the provided options with whitelisted properties switched * from camelCase to underscore_case. */ - private validateMessagingOptions(options: MessagingOptions) { + private validateMessagingOptions(options: MessagingOptions): MessagingOptions { const optionsCopy: MessagingOptions = deepCopy(options); // Validate the options object does not contain blacklisted properties @@ -917,7 +919,7 @@ export class Messaging implements FirebaseServiceInterface { registrationTokenOrTokens: string | string[], methodName: string, errorInfo: ErrorInfo = MessagingClientErrorCode.INVALID_ARGUMENT, - ) { + ): void { if (!validator.isNonEmptyArray(registrationTokenOrTokens) && !validator.isNonEmptyString(registrationTokenOrTokens)) { throw new FirebaseMessagingError( @@ -940,7 +942,7 @@ export class Messaging implements FirebaseServiceInterface { registrationTokenOrTokens: string | string[], methodName: string, errorInfo: ErrorInfo = MessagingClientErrorCode.INVALID_ARGUMENT, - ) { + ): void { if (validator.isArray(registrationTokenOrTokens)) { // Validate the array contains no more than 1,000 registration tokens. if (registrationTokenOrTokens.length > 1000) { @@ -975,7 +977,7 @@ export class Messaging implements FirebaseServiceInterface { topic: string | string[], methodName: string, errorInfo: ErrorInfo = MessagingClientErrorCode.INVALID_ARGUMENT, - ) { + ): void { if (!validator.isNonEmptyString(topic)) { throw new FirebaseMessagingError( errorInfo, @@ -996,7 +998,7 @@ export class Messaging implements FirebaseServiceInterface { topic: string, methodName: string, errorInfo: ErrorInfo = MessagingClientErrorCode.INVALID_ARGUMENT, - ) { + ): void { if (!validator.isTopic(topic)) { throw new FirebaseMessagingError( errorInfo, @@ -1013,7 +1015,7 @@ export class Messaging implements FirebaseServiceInterface { * * @return {string} The normalized topic name. */ - private normalizeTopic(topic: string) { + private normalizeTopic(topic: string): string { if (!/^\/topics\//.test(topic)) { topic = `/topics/${ topic }`; } diff --git a/src/project-management.d.ts b/src/project-management.d.ts new file mode 100644 index 0000000000..755f8c9e94 --- /dev/null +++ b/src/project-management.d.ts @@ -0,0 +1,360 @@ +import * as _admin from './index.d'; + +export namespace admin.projectManagement { + + /** + * A SHA-1 or SHA-256 certificate. + * + * Do not call this constructor directly. Instead, use + * [`projectManagement.shaCertificate()`](admin.projectManagement.ProjectManagement#shaCertificate). + */ + interface ShaCertificate { + + /** + * The SHA certificate type. + * + * @example + * ```javascript + * var certType = shaCertificate.certType; + * ``` + */ + certType: ('sha1' | 'sha256'); + + /** + * The SHA-1 or SHA-256 hash for this certificate. + * + * @example + * ```javascript + * var shaHash = shaCertificate.shaHash; + * ``` + */ + shaHash: string; + + /** + * The fully-qualified resource name that identifies this sha-key. + * + * This is useful when manually constructing requests for Firebase's public API. + * + * @example + * ```javascript + * var resourceName = shaCertificate.resourceName; + * ``` + */ + resourceName?: string; + } + + /** + * Metadata about a Firebase app. + */ + interface AppMetadata { + + /** + * The globally unique, Firebase-assigned identifier of the app. + * + * @example + * ```javascript + * var appId = appMetadata.appId; + * ``` + */ + appId: string; + + /** + * The optional user-assigned display name of the app. + * + * @example + * ```javascript + * var displayName = appMetadata.displayName; + * ``` + */ + displayName?: string; + + /** + * The development platform of the app. Supporting Android and iOS app platforms. + * + * @example + * ```javascript + * var platform = AppPlatform.ANDROID; + * ``` + */ + platform: AppPlatform; + + /** + * The globally unique, user-assigned ID of the parent project for the app. + * + * @example + * ```javascript + * var projectId = appMetadata.projectId; + * ``` + */ + projectId: string; + + /** + * The fully-qualified resource name that identifies this app. + * + * This is useful when manually constructing requests for Firebase's public API. + * + * @example + * ```javascript + * var resourceName = androidAppMetadata.resourceName; + * ``` + */ + resourceName: string; + } + + /** + * Platforms with which a Firebase App can be associated. + */ + enum AppPlatform { + + /** + * Unknown state. This is only used for distinguishing unset values. + */ + PLATFORM_UNKNOWN = 'PLATFORM_UNKNOWN', + + /** + * The Firebase App is associated with iOS. + */ + IOS = 'IOS', + + /** + * The Firebase App is associated with Android. + */ + ANDROID = 'ANDROID', + } + + /** + * Metadata about a Firebase Android App. + */ + interface AndroidAppMetadata extends AppMetadata { + + platform: AppPlatform.ANDROID; + + /** + * The canonical package name of the Android App, as would appear in the Google Play Developer + * Console. + * + * @example + * ```javascript + * var packageName = androidAppMetadata.packageName; + * ``` + */ + packageName: string; + } + + /** + * Metadata about a Firebase iOS App. + */ + interface IosAppMetadata extends AppMetadata { + platform: AppPlatform.IOS; + + /** + * The canonical bundle ID of the iOS App as it would appear in the iOS App Store. + * + * @example + * ```javascript + * var bundleId = iosAppMetadata.bundleId; + *``` + */ + bundleId: string; + } + + /** + * A reference to a Firebase Android app. + * + * Do not call this constructor directly. Instead, use + * [`projectManagement.androidApp()`](admin.projectManagement.ProjectManagement#androidApp). + */ + interface AndroidApp { + appId: string; + + /** + * Retrieves metadata about this Android app. + * + * @return A promise that resolves to the retrieved metadata about this Android app. + */ + getMetadata(): Promise; + + /** + * Sets the optional user-assigned display name of the app. + * + * @param newDisplayName The new display name to set. + * + * @return A promise that resolves when the display name has been set. + */ + setDisplayName(newDisplayName: string): Promise; + + /** + * Gets the list of SHA certificates associated with this Android app in Firebase. + * + * @return The list of SHA-1 and SHA-256 certificates associated with this Android app in + * Firebase. + */ + getShaCertificates(): Promise; + + /** + * Adds the given SHA certificate to this Android app. + * + * @param certificateToAdd The SHA certificate to add. + * + * @return A promise that resolves when the given certificate + * has been added to the Android app. + */ + addShaCertificate(certificateToAdd: ShaCertificate): Promise; + + /** + * Deletes the specified SHA certificate from this Android app. + * + * @param certificateToDelete The SHA certificate to delete. + * + * @return A promise that resolves when the specified + * certificate has been removed from the Android app. + */ + deleteShaCertificate(certificateToRemove: ShaCertificate): Promise; + + /** + * Gets the configuration artifact associated with this app. + * + * @return A promise that resolves to the Android app's + * Firebase config file, in UTF-8 string format. This string is typically + * intended to be written to a JSON file that gets shipped with your Android + * app. + */ + getConfig(): Promise; + } + + /** + * A reference to a Firebase iOS app. + * + * Do not call this constructor directly. Instead, use + * [`projectManagement.iosApp()`](admin.projectManagement.ProjectManagement#iosApp). + */ + interface IosApp { + appId: string; + + /** + * Retrieves metadata about this iOS app. + * + * @return {!Promise} A promise that + * resolves to the retrieved metadata about this iOS app. + */ + getMetadata(): Promise; + + /** + * Sets the optional user-assigned display name of the app. + * + * @param newDisplayName The new display name to set. + * + * @return A promise that resolves when the display name has + * been set. + */ + setDisplayName(newDisplayName: string): Promise; + + /** + * Gets the configuration artifact associated with this app. + * + * @return A promise that resolves to the iOS app's Firebase + * config file, in UTF-8 string format. This string is typically intended to + * be written to a plist file that gets shipped with your iOS app. + */ + getConfig(): Promise; + } + + /** + * The Firebase ProjectManagement service interface. + * + * Do not call this constructor directly. Instead, use + * [`admin.projectManagement()`](admin.projectManagement#projectManagement). + */ + interface ProjectManagement { + app: _admin.app.App; + + /** + * Lists up to 100 Firebase apps associated with this Firebase project. + * + * @return A promise that resolves to the metadata list of the apps. + */ + listAppMetadata(): Promise; + + /** + * Lists up to 100 Firebase Android apps associated with this Firebase project. + * + * @return The list of Android apps. + */ + listAndroidApps(): Promise; + + /** + * Lists up to 100 Firebase iOS apps associated with this Firebase project. + * + * @return The list of iOS apps. + */ + listIosApps(): Promise; + + /** + * Creates an `AndroidApp` object, referencing the specified Android app within + * this Firebase project. + * + * This method does not perform an RPC. + * + * @param appId The `appId` of the Android app to reference. + * + * @return An `AndroidApp` object that references the specified Firebase Android app. + */ + androidApp(appId: string): admin.projectManagement.AndroidApp; + + /** + * Update the display name of this Firebase project. + * + * @param newDisplayName The new display name to be updated. + * + * @return A promise that resolves when the project display name has been updated. + */ + setDisplayName(newDisplayName: string): Promise; + + /** + * Creates an `iOSApp` object, referencing the specified iOS app within + * this Firebase project. + * + * This method does not perform an RPC. + * + * @param appId The `appId` of the iOS app to reference. + * + * @return An `iOSApp` object that references the specified Firebase iOS app. + */ + iosApp(appId: string): admin.projectManagement.IosApp; + + /** + * Creates a `ShaCertificate` object. + * + * This method does not perform an RPC. + * + * @param shaHash The SHA-1 or SHA-256 hash for this certificate. + * + * @return A `ShaCertificate` object contains the specified SHA hash. + */ + shaCertificate(shaHash: string): admin.projectManagement.ShaCertificate; + + /** + * Creates a new Firebase Android app associated with this Firebase project. + * + * @param packageName The canonical package name of the Android App, + * as would appear in the Google Play Developer Console. + * @param displayName An optional user-assigned display name for this + * new app. + * + * @return A promise that resolves to the newly created Android app. + */ + createAndroidApp( + packageName: string, displayName?: string): Promise; + + /** + * Creates a new Firebase iOS app associated with this Firebase project. + * + * @param bundleId The iOS app bundle ID to use for this new app. + * @param displayName An optional user-assigned display name for this + * new app. + * + * @return A promise that resolves to the newly created iOS app. + */ + createIosApp(bundleId: string, displayName?: string): Promise; + } +} diff --git a/src/project-management/android-app.ts b/src/project-management/android-app.ts index 7676d1c603..0cbd8951fc 100644 --- a/src/project-management/android-app.ts +++ b/src/project-management/android-app.ts @@ -27,7 +27,7 @@ export class AndroidApp { private readonly requestHandler: ProjectManagementRequestHandler) { if (!validator.isNonEmptyString(appId)) { throw new FirebaseProjectManagementError( - 'invalid-argument', 'appId must be a non-empty string.'); + 'invalid-argument', 'appId must be a non-empty string.'); } this.resourceName = `projects/-/androidApps/${appId}`; @@ -35,30 +35,30 @@ export class AndroidApp { public getMetadata(): Promise { return this.requestHandler.getResource(this.resourceName) - .then((responseData: any) => { + .then((responseData: any) => { + assertServerResponse( + validator.isNonNullObject(responseData), + responseData, + 'getMetadata()\'s responseData must be a non-null object.'); + + const requiredFieldsList = ['name', 'appId', 'projectId', 'packageName']; + requiredFieldsList.forEach((requiredField) => { assertServerResponse( - validator.isNonNullObject(responseData), - responseData, - 'getMetadata()\'s responseData must be a non-null object.'); - - const requiredFieldsList = ['name', 'appId', 'projectId', 'packageName']; - requiredFieldsList.forEach((requiredField) => { - assertServerResponse( - validator.isNonEmptyString(responseData[requiredField]), - responseData, - `getMetadata()\'s responseData.${requiredField} must be a non-empty string.`); - }); - - const metadata: AndroidAppMetadata = { - platform: AppPlatform.ANDROID, - resourceName: responseData.name, - appId: responseData.appId, - displayName: responseData.displayName || null, - projectId: responseData.projectId, - packageName: responseData.packageName, - }; - return metadata; + validator.isNonEmptyString(responseData[requiredField]), + responseData, + `getMetadata()'s responseData.${requiredField} must be a non-empty string.`); }); + + const metadata: AndroidAppMetadata = { + platform: AppPlatform.ANDROID, + resourceName: responseData.name, + appId: responseData.appId, + displayName: responseData.displayName || null, + projectId: responseData.projectId, + packageName: responseData.packageName, + }; + return metadata; + }); } public setDisplayName(newDisplayName: string): Promise { @@ -67,35 +67,35 @@ export class AndroidApp { public getShaCertificates(): Promise { return this.requestHandler.getAndroidShaCertificates(this.resourceName) - .then((responseData: any) => { - assertServerResponse( - validator.isNonNullObject(responseData), - responseData, - 'getShaCertificates()\'s responseData must be a non-null object.'); + .then((responseData: any) => { + assertServerResponse( + validator.isNonNullObject(responseData), + responseData, + 'getShaCertificates()\'s responseData must be a non-null object.'); - if (!responseData.certificates) { - return []; - } + if (!responseData.certificates) { + return []; + } - assertServerResponse( - validator.isArray(responseData.certificates), - responseData, - '"certificates" field must be present in the getShaCertificates() response data.'); + assertServerResponse( + validator.isArray(responseData.certificates), + responseData, + '"certificates" field must be present in the getShaCertificates() response data.'); - const requiredFieldsList = ['name', 'shaHash']; + const requiredFieldsList = ['name', 'shaHash']; - return responseData.certificates.map((certificateJson: any) => { - requiredFieldsList.forEach((requiredField) => { - assertServerResponse( - validator.isNonEmptyString(certificateJson[requiredField]), - responseData, - `getShaCertificates()\'s responseData.certificates[].${requiredField} must be a ` + return responseData.certificates.map((certificateJson: any) => { + requiredFieldsList.forEach((requiredField) => { + assertServerResponse( + validator.isNonEmptyString(certificateJson[requiredField]), + responseData, + `getShaCertificates()'s responseData.certificates[].${requiredField} must be a ` + `non-empty string.`); - }); - - return new ShaCertificate(certificateJson.shaHash, certificateJson.name); }); + + return new ShaCertificate(certificateJson.shaHash, certificateJson.name); }); + }); } public addShaCertificate(certificateToAdd: ShaCertificate): Promise { @@ -105,8 +105,8 @@ export class AndroidApp { public deleteShaCertificate(certificateToDelete: ShaCertificate): Promise { if (!certificateToDelete.resourceName) { throw new FirebaseProjectManagementError( - 'invalid-argument', - 'Specified certificate does not include a resourceName. (Use AndroidApp.getShaCertificates() to retrieve ' + + 'invalid-argument', + 'Specified certificate does not include a resourceName. (Use AndroidApp.getShaCertificates() to retrieve ' + 'certificates with a resourceName.'); } return this.requestHandler.deleteResource(certificateToDelete.resourceName); @@ -118,20 +118,20 @@ export class AndroidApp { */ public getConfig(): Promise { return this.requestHandler.getConfig(this.resourceName) - .then((responseData: any) => { - assertServerResponse( - validator.isNonNullObject(responseData), - responseData, - 'getConfig()\'s responseData must be a non-null object.'); - - const base64ConfigFileContents = responseData.configFileContents; - assertServerResponse( - validator.isBase64String(base64ConfigFileContents), - responseData, - `getConfig()\'s responseData.configFileContents must be a base64 string.`); - - return Buffer.from(base64ConfigFileContents, 'base64').toString('utf8'); - }); + .then((responseData: any) => { + assertServerResponse( + validator.isNonNullObject(responseData), + responseData, + 'getConfig()\'s responseData must be a non-null object.'); + + const base64ConfigFileContents = responseData.configFileContents; + assertServerResponse( + validator.isBase64String(base64ConfigFileContents), + responseData, + `getConfig()'s responseData.configFileContents must be a base64 string.`); + + return Buffer.from(base64ConfigFileContents, 'base64').toString('utf8'); + }); } } @@ -153,7 +153,7 @@ export class ShaCertificate { this.certType = 'sha256'; } else { throw new FirebaseProjectManagementError( - 'invalid-argument', 'shaHash must be either a sha256 hash or a sha1 hash.'); + 'invalid-argument', 'shaHash must be either a sha256 hash or a sha1 hash.'); } } } diff --git a/src/project-management/ios-app.ts b/src/project-management/ios-app.ts index f9b553d6df..18acb47ae1 100644 --- a/src/project-management/ios-app.ts +++ b/src/project-management/ios-app.ts @@ -27,7 +27,7 @@ export class IosApp { private readonly requestHandler: ProjectManagementRequestHandler) { if (!validator.isNonEmptyString(appId)) { throw new FirebaseProjectManagementError( - 'invalid-argument', 'appId must be a non-empty string.'); + 'invalid-argument', 'appId must be a non-empty string.'); } this.resourceName = `projects/-/iosApps/${appId}`; @@ -35,30 +35,30 @@ export class IosApp { public getMetadata(): Promise { return this.requestHandler.getResource(this.resourceName) - .then((responseData: any) => { - assertServerResponse( - validator.isNonNullObject(responseData), - responseData, - 'getMetadata()\'s responseData must be a non-null object.'); - - const requiredFieldsList = ['name', 'appId', 'projectId', 'bundleId']; - requiredFieldsList.forEach((requiredField) => { - assertServerResponse( - validator.isNonEmptyString(responseData[requiredField]), - responseData, - `getMetadata()\'s responseData.${requiredField} must be a non-empty string.`); - }); + .then((responseData: any) => { + assertServerResponse( + validator.isNonNullObject(responseData), + responseData, + 'getMetadata()\'s responseData must be a non-null object.'); - const metadata: IosAppMetadata = { - platform: AppPlatform.IOS, - resourceName: responseData.name, - appId: responseData.appId, - displayName: responseData.displayName || null, - projectId: responseData.projectId, - bundleId: responseData.bundleId, - }; - return metadata; + const requiredFieldsList = ['name', 'appId', 'projectId', 'bundleId']; + requiredFieldsList.forEach((requiredField) => { + assertServerResponse( + validator.isNonEmptyString(responseData[requiredField]), + responseData, + `getMetadata()'s responseData.${requiredField} must be a non-empty string.`); }); + + const metadata: IosAppMetadata = { + platform: AppPlatform.IOS, + resourceName: responseData.name, + appId: responseData.appId, + displayName: responseData.displayName || null, + projectId: responseData.projectId, + bundleId: responseData.bundleId, + }; + return metadata; + }); } public setDisplayName(newDisplayName: string): Promise { @@ -71,19 +71,19 @@ export class IosApp { */ public getConfig(): Promise { return this.requestHandler.getConfig(this.resourceName) - .then((responseData: any) => { - assertServerResponse( - validator.isNonNullObject(responseData), - responseData, - 'getConfig()\'s responseData must be a non-null object.'); + .then((responseData: any) => { + assertServerResponse( + validator.isNonNullObject(responseData), + responseData, + 'getConfig()\'s responseData must be a non-null object.'); - const base64ConfigFileContents = responseData.configFileContents; - assertServerResponse( - validator.isBase64String(base64ConfigFileContents), - responseData, - `getConfig()\'s responseData.configFileContents must be a base64 string.`); + const base64ConfigFileContents = responseData.configFileContents; + assertServerResponse( + validator.isBase64String(base64ConfigFileContents), + responseData, + `getConfig()'s responseData.configFileContents must be a base64 string.`); - return Buffer.from(base64ConfigFileContents, 'base64').toString('utf8'); - }); + return Buffer.from(base64ConfigFileContents, 'base64').toString('utf8'); + }); } } diff --git a/src/project-management/project-management-api-request.ts b/src/project-management/project-management-api-request.ts index 14d533ddb0..c7528f9157 100644 --- a/src/project-management/project-management-api-request.ts +++ b/src/project-management/project-management-api-request.ts @@ -43,11 +43,11 @@ const CERT_TYPE_API_MAP = { }; export function assertServerResponse( - condition: boolean, responseData: object, message: string): void { + condition: boolean, responseData: object, message: string): void { if (!condition) { throw new FirebaseProjectManagementError( - 'invalid-server-response', - `${message} Response data: ${JSON.stringify(responseData, null, 2)}`); + 'invalid-server-response', + `${message} Response data: ${JSON.stringify(responseData, null, 2)}`); } } @@ -64,50 +64,50 @@ export class ProjectManagementRequestHandler { `https://${PROJECT_MANAGEMENT_HOST_AND_PORT}${PROJECT_MANAGEMENT_BETA_PATH}`; private readonly httpClient: AuthorizedHttpClient; - private static wrapAndRethrowHttpError(errStatusCode: number, errText?: string) { + private static wrapAndRethrowHttpError(errStatusCode: number, errText?: string): void { let errorCode: ProjectManagementErrorCode; let errorMessage: string; switch (errStatusCode) { - case 400: - errorCode = 'invalid-argument'; - errorMessage = 'Invalid argument provided.'; - break; - case 401: - case 403: - errorCode = 'authentication-error'; - errorMessage = 'An error occurred when trying to authenticate. Make sure the credential ' + case 400: + errorCode = 'invalid-argument'; + errorMessage = 'Invalid argument provided.'; + break; + case 401: + case 403: + errorCode = 'authentication-error'; + errorMessage = 'An error occurred when trying to authenticate. Make sure the credential ' + 'used to authenticate this SDK has the proper permissions. See ' + 'https://firebase.google.com/docs/admin/setup for setup instructions.'; - break; - case 404: - errorCode = 'not-found'; - errorMessage = 'The specified entity could not be found.'; - break; - case 409: - errorCode = 'already-exists'; - errorMessage = 'The specified entity already exists.'; - break; - case 500: - errorCode = 'internal-error'; - errorMessage = 'An internal error has occurred. Please retry the request.'; - break; - case 503: - errorCode = 'service-unavailable'; - errorMessage = 'The server could not process the request in time. See the error ' + break; + case 404: + errorCode = 'not-found'; + errorMessage = 'The specified entity could not be found.'; + break; + case 409: + errorCode = 'already-exists'; + errorMessage = 'The specified entity already exists.'; + break; + case 500: + errorCode = 'internal-error'; + errorMessage = 'An internal error has occurred. Please retry the request.'; + break; + case 503: + errorCode = 'service-unavailable'; + errorMessage = 'The server could not process the request in time. See the error ' + 'documentation for more details.'; - break; - default: - errorCode = 'unknown-error'; - errorMessage = 'An unknown server error was returned.'; + break; + default: + errorCode = 'unknown-error'; + errorMessage = 'An unknown server error was returned.'; } if (!errText) { errText = ''; } throw new FirebaseProjectManagementError( - errorCode, - `${ errorMessage } Status code: ${ errStatusCode }. Raw server response: "${ errText }".`); + errorCode, + `${ errorMessage } Status code: ${ errStatusCode }. Raw server response: "${ errText }".`); } /** @@ -124,10 +124,10 @@ export class ProjectManagementRequestHandler { */ public listAndroidApps(parentResourceName: string): Promise { return this.invokeRequestHandler( - 'GET', - `${parentResourceName}/androidApps?page_size=${LIST_APPS_MAX_PAGE_SIZE}`, - /* requestData */ null, - 'v1beta1'); + 'GET', + `${parentResourceName}/androidApps?page_size=${LIST_APPS_MAX_PAGE_SIZE}`, + /* requestData */ null, + 'v1beta1'); } /** @@ -136,10 +136,10 @@ export class ProjectManagementRequestHandler { */ public listIosApps(parentResourceName: string): Promise { return this.invokeRequestHandler( - 'GET', - `${parentResourceName}/iosApps?page_size=${LIST_APPS_MAX_PAGE_SIZE}`, - /* requestData */ null, - 'v1beta1'); + 'GET', + `${parentResourceName}/iosApps?page_size=${LIST_APPS_MAX_PAGE_SIZE}`, + /* requestData */ null, + 'v1beta1'); } /** @@ -148,10 +148,10 @@ export class ProjectManagementRequestHandler { */ public listAppMetadata(parentResourceName: string): Promise { return this.invokeRequestHandler( - 'GET', - `${parentResourceName}:searchApps?page_size=${LIST_APPS_MAX_PAGE_SIZE}`, - /* requestData */ null, - 'v1beta1'); + 'GET', + `${parentResourceName}:searchApps?page_size=${LIST_APPS_MAX_PAGE_SIZE}`, + /* requestData */ null, + 'v1beta1'); } /** @@ -159,7 +159,7 @@ export class ProjectManagementRequestHandler { * to create the Android app within. */ public createAndroidApp( - parentResourceName: string, packageName: string, displayName?: string): Promise { + parentResourceName: string, packageName: string, displayName?: string): Promise { const requestData: any = { packageName, }; @@ -167,18 +167,18 @@ export class ProjectManagementRequestHandler { requestData.displayName = displayName; } return this - .invokeRequestHandler('POST', `${parentResourceName}/androidApps`, requestData, 'v1beta1') - .then((responseData: any) => { - assertServerResponse( - validator.isNonNullObject(responseData), - responseData, - `createAndroidApp's responseData must be a non-null object.`); - assertServerResponse( - validator.isNonEmptyString(responseData.name), - responseData, - `createAndroidApp's responseData.name must be a non-empty string.`); - return this.pollRemoteOperationWithExponentialBackoff(responseData.name); - }); + .invokeRequestHandler('POST', `${parentResourceName}/androidApps`, requestData, 'v1beta1') + .then((responseData: any) => { + assertServerResponse( + validator.isNonNullObject(responseData), + responseData, + `createAndroidApp's responseData must be a non-null object.`); + assertServerResponse( + validator.isNonEmptyString(responseData.name), + responseData, + `createAndroidApp's responseData.name must be a non-empty string.`); + return this.pollRemoteOperationWithExponentialBackoff(responseData.name); + }); } /** @@ -186,7 +186,7 @@ export class ProjectManagementRequestHandler { * to create the iOS app within. */ public createIosApp( - parentResourceName: string, bundleId: string, displayName?: string): Promise { + parentResourceName: string, bundleId: string, displayName?: string): Promise { const requestData: any = { bundleId, }; @@ -194,18 +194,18 @@ export class ProjectManagementRequestHandler { requestData.displayName = displayName; } return this - .invokeRequestHandler('POST', `${parentResourceName}/iosApps`, requestData, 'v1beta1') - .then((responseData: any) => { - assertServerResponse( - validator.isNonNullObject(responseData), - responseData, - `createIosApp's responseData must be a non-null object.`); - assertServerResponse( - validator.isNonEmptyString(responseData.name), - responseData, - `createIosApp's responseData.name must be a non-empty string.`); - return this.pollRemoteOperationWithExponentialBackoff(responseData.name); - }); + .invokeRequestHandler('POST', `${parentResourceName}/iosApps`, requestData, 'v1beta1') + .then((responseData: any) => { + assertServerResponse( + validator.isNonNullObject(responseData), + responseData, + `createIosApp's responseData must be a non-null object.`); + assertServerResponse( + validator.isNonEmptyString(responseData.name), + responseData, + `createIosApp's responseData.name must be a non-empty string.`); + return this.pollRemoteOperationWithExponentialBackoff(responseData.name); + }); } /** @@ -217,9 +217,9 @@ export class ProjectManagementRequestHandler { displayName: newDisplayName, }; return this - .invokeRequestHandler( - 'PATCH', `${resourceName}?update_mask=display_name`, requestData, 'v1beta1') - .then(() => undefined); + .invokeRequestHandler( + 'PATCH', `${resourceName}?update_mask=display_name`, requestData, 'v1beta1') + .then(() => undefined); } /** @@ -228,7 +228,7 @@ export class ProjectManagementRequestHandler { */ public getAndroidShaCertificates(parentResourceName: string): Promise { return this.invokeRequestHandler( - 'GET', `${parentResourceName}/sha`, /* requestData */ null, 'v1beta1'); + 'GET', `${parentResourceName}/sha`, /* requestData */ null, 'v1beta1'); } /** @@ -236,14 +236,14 @@ export class ProjectManagementRequestHandler { * want to add the given SHA certificate to. */ public addAndroidShaCertificate( - parentResourceName: string, certificate: ShaCertificate): Promise { + parentResourceName: string, certificate: ShaCertificate): Promise { const requestData = { shaHash: certificate.shaHash, certType: CERT_TYPE_API_MAP[certificate.certType], }; return this - .invokeRequestHandler('POST', `${parentResourceName}/sha`, requestData, 'v1beta1') - .then(() => undefined); + .invokeRequestHandler('POST', `${parentResourceName}/sha`, requestData, 'v1beta1') + .then(() => undefined); } /** @@ -252,7 +252,7 @@ export class ProjectManagementRequestHandler { */ public getConfig(parentResourceName: string): Promise { return this.invokeRequestHandler( - 'GET', `${parentResourceName}/config`, /* requestData */ null, 'v1beta1'); + 'GET', `${parentResourceName}/config`, /* requestData */ null, 'v1beta1'); } /** @@ -269,32 +269,32 @@ export class ProjectManagementRequestHandler { */ public deleteResource(resourceName: string): Promise { return this - .invokeRequestHandler('DELETE', resourceName, /* requestData */ null, 'v1beta1') - .then(() => undefined); + .invokeRequestHandler('DELETE', resourceName, /* requestData */ null, 'v1beta1') + .then(() => undefined); } private pollRemoteOperationWithExponentialBackoff( - operationResourceName: string): Promise { + operationResourceName: string): Promise { const poller = new ExponentialBackoffPoller(); return poller.poll(() => { return this.invokeRequestHandler('GET', operationResourceName, /* requestData */ null) - .then((responseData: any) => { - if (responseData.error) { - const errStatusCode: number = responseData.error.code || 500; - const errText: string = + .then((responseData: any) => { + if (responseData.error) { + const errStatusCode: number = responseData.error.code || 500; + const errText: string = responseData.error.message || JSON.stringify(responseData.error); - ProjectManagementRequestHandler.wrapAndRethrowHttpError(errStatusCode, errText); - } + ProjectManagementRequestHandler.wrapAndRethrowHttpError(errStatusCode, errText); + } - if (!responseData.done) { - // Continue polling. - return null; - } + if (!responseData.done) { + // Continue polling. + return null; + } - // Polling complete. Resolve with operation response JSON. - return responseData.response; - }); + // Polling complete. Resolve with operation response JSON. + return responseData.response; + }); }); } @@ -302,10 +302,10 @@ export class ProjectManagementRequestHandler { * Invokes the request handler with the provided request data. */ private invokeRequestHandler( - method: HttpMethod, - path: string, - requestData: object | null, - apiVersion: ('v1' | 'v1beta1') = 'v1'): Promise { + method: HttpMethod, + path: string, + requestData: object | null, + apiVersion: ('v1' | 'v1beta1') = 'v1'): Promise { const baseUrlToUse = (apiVersion === 'v1') ? this.baseUrl : this.baseBetaUrl; const request: HttpRequestConfig = { method, @@ -316,20 +316,20 @@ export class ProjectManagementRequestHandler { }; return this.httpClient.send(request) - .then((response) => { - // Send non-JSON responses to the catch() below, where they will be treated as errors. - if (!response.isJson()) { - throw new HttpError(response); - } + .then((response) => { + // Send non-JSON responses to the catch() below, where they will be treated as errors. + if (!response.isJson()) { + throw new HttpError(response); + } - return response.data; - }) - .catch((err) => { - if (err instanceof HttpError) { - ProjectManagementRequestHandler.wrapAndRethrowHttpError( - err.response.status, err.response.text); - } - throw err; - }); + return response.data; + }) + .catch((err) => { + if (err instanceof HttpError) { + ProjectManagementRequestHandler.wrapAndRethrowHttpError( + err.response.status, err.response.text); + } + throw err; + }); } } diff --git a/src/project-management/project-management.ts b/src/project-management/project-management.ts index 3ac3d2dd09..dfe6be3a3f 100644 --- a/src/project-management/project-management.ts +++ b/src/project-management/project-management.ts @@ -56,8 +56,8 @@ export class ProjectManagement implements FirebaseServiceInterface { constructor(readonly app: FirebaseApp) { if (!validator.isNonNullObject(app) || !('options' in app)) { throw new FirebaseProjectManagementError( - 'invalid-argument', - 'First argument passed to admin.projectManagement() must be a valid Firebase app ' + 'invalid-argument', + 'First argument passed to admin.projectManagement() must be a valid Firebase app ' + 'instance.'); } @@ -109,14 +109,14 @@ export class ProjectManagement implements FirebaseServiceInterface { }) .then((responseData: any) => { assertServerResponse( - validator.isNonNullObject(responseData), - responseData, - 'createAndroidApp()\'s responseData must be a non-null object.'); + validator.isNonNullObject(responseData), + responseData, + 'createAndroidApp()\'s responseData must be a non-null object.'); assertServerResponse( - validator.isNonEmptyString(responseData.appId), - responseData, - `"responseData.appId" field must be present in createAndroidApp()'s response data.`); + validator.isNonEmptyString(responseData.appId), + responseData, + `"responseData.appId" field must be present in createAndroidApp()'s response data.`); return new AndroidApp(responseData.appId, this.requestHandler); }); } @@ -131,14 +131,14 @@ export class ProjectManagement implements FirebaseServiceInterface { }) .then((responseData: any) => { assertServerResponse( - validator.isNonNullObject(responseData), - responseData, - 'createIosApp()\'s responseData must be a non-null object.'); + validator.isNonNullObject(responseData), + responseData, + 'createIosApp()\'s responseData must be a non-null object.'); assertServerResponse( - validator.isNonEmptyString(responseData.appId), - responseData, - `"responseData.appId" field must be present in createIosApp()'s response data.`); + validator.isNonEmptyString(responseData.appId), + responseData, + `"responseData.appId" field must be present in createIosApp()'s response data.`); return new IosApp(responseData.appId, this.requestHandler); }); } @@ -178,13 +178,13 @@ export class ProjectManagement implements FirebaseServiceInterface { return responseData.apps.map((appJson: any) => { assertServerResponse( - validator.isNonEmptyString(appJson.appId), - responseData, - `"apps[].appId" field must be present in the listAppMetadata() response data.`); + validator.isNonEmptyString(appJson.appId), + responseData, + `"apps[].appId" field must be present in the listAppMetadata() response data.`); assertServerResponse( - validator.isNonEmptyString(appJson.platform), - responseData, - `"apps[].platform" field must be present in the listAppMetadata() response data.`); + validator.isNonEmptyString(appJson.platform), + responseData, + `"apps[].platform" field must be present in the listAppMetadata() response data.`); const metadata: AppMetadata = { appId: appJson.appId, platform: (AppPlatform as any)[appJson.platform] || AppPlatform.PLATFORM_UNKNOWN, @@ -215,8 +215,8 @@ export class ProjectManagement implements FirebaseServiceInterface { // Assert that a specific project ID was provided within the app. if (!validator.isNonEmptyString(projectId)) { throw new FirebaseProjectManagementError( - 'invalid-project-id', - 'Failed to determine project ID. Initialize the SDK with service account credentials, or ' + 'invalid-project-id', + 'Failed to determine project ID. Initialize the SDK with service account credentials, or ' + 'set project ID as an app option. Alternatively, set the GOOGLE_CLOUD_PROJECT ' + 'environment variable.'); } @@ -245,9 +245,9 @@ export class ProjectManagement implements FirebaseServiceInterface { return responseData.apps.map((appJson: any) => { assertServerResponse( - validator.isNonEmptyString(appJson.appId), - responseData, - `"apps[].appId" field must be present in the ${callerName} response data.`); + validator.isNonEmptyString(appJson.appId), + responseData, + `"apps[].appId" field must be present in the ${callerName} response data.`); if (platform === 'android') { return new AndroidApp(appJson.appId, this.requestHandler); } else { @@ -259,15 +259,15 @@ export class ProjectManagement implements FirebaseServiceInterface { private assertListAppsResponseData(responseData: any, callerName: string): void { assertServerResponse( - validator.isNonNullObject(responseData), - responseData, - `${callerName}\'s responseData must be a non-null object.`); + validator.isNonNullObject(responseData), + responseData, + `${callerName}'s responseData must be a non-null object.`); if (responseData.apps) { assertServerResponse( - validator.isArray(responseData.apps), - responseData, - `"apps" field must be present in the ${callerName} response data.`); - } + validator.isArray(responseData.apps), + responseData, + `"apps" field must be present in the ${callerName} response data.`); + } } } diff --git a/src/security-rules.d.ts b/src/security-rules.d.ts new file mode 100644 index 0000000000..f07a737431 --- /dev/null +++ b/src/security-rules.d.ts @@ -0,0 +1,191 @@ +import * as _admin from './index.d'; + +export namespace admin.securityRules { + /** + * A source file containing some Firebase security rules. The content includes raw + * source code including text formatting, indentation and comments. Use the + * [`securityRules.createRulesFileFromSource()`](admin.securityRules.SecurityRules#createRulesFileFromSource) + * method to create new instances of this type. + */ + interface RulesFile { + readonly name: string; + readonly content: string; + } + + /** + * Required metadata associated with a ruleset. + */ + interface RulesetMetadata { + /** + * Name of the `Ruleset` as a short string. This can be directly passed into APIs + * like [`securityRules.getRuleset()`](admin.securityRules.SecurityRules#getRuleset) + * and [`securityRules.deleteRuleset()`](admin.securityRules.SecurityRules#deleteRuleset). + */ + readonly name: string; + + /** + * Creation time of the `Ruleset` as a UTC timestamp string. + */ + readonly createTime: string; + } + + /** + * A set of Firebase security rules. + */ + interface Ruleset extends RulesetMetadata { + readonly source: admin.securityRules.RulesFile[]; + } + + interface RulesetMetadataList { + /** + * A batch of ruleset metadata. + */ + readonly rulesets: admin.securityRules.RulesetMetadata[]; + + /** + * The next page token if available. This is needed to retrieve the next batch. + */ + readonly nextPageToken?: string; + } + + /** + * The Firebase `SecurityRules` service interface. + * + * Do not call this constructor directly. Instead, use + * [`admin.securityRules()`](admin.securityRules#securityRules). + */ + interface SecurityRules { + app: _admin.app.App; + + /** + * Creates a {@link admin.securityRules.RulesFile `RuleFile`} with the given name + * and source. Throws an error if any of the arguments are invalid. This is a local + * operation, and does not involve any network API calls. + * + * @example + * ```javascript + * const source = '// Some rules source'; + * const rulesFile = admin.securityRules().createRulesFileFromSource( + * 'firestore.rules', source); + * ``` + * + * @param name Name to assign to the rules file. This is usually a short file name that + * helps identify the file in a ruleset. + * @param source Contents of the rules file. + * @return A new rules file instance. + */ + createRulesFileFromSource(name: string, source: string | Buffer): admin.securityRules.RulesFile; + + /** + * Creates a new {@link admin.securityRules.Ruleset `Ruleset`} from the given + * {@link admin.securityRules.RulesFile `RuleFile`}. + * + * @param file Rules file to include in the new `Ruleset`. + * @returns A promise that fulfills with the newly created `Ruleset`. + */ + createRuleset(file: admin.securityRules.RulesFile): Promise; + + /** + * Gets the {@link admin.securityRules.Ruleset `Ruleset`} identified by the given + * name. The input name should be the short name string without the project ID + * prefix. For example, to retrieve the `projects/project-id/rulesets/my-ruleset`, + * pass the short name "my-ruleset". Rejects with a `not-found` error if the + * specified `Ruleset` cannot be found. + * + * @param name Name of the `Ruleset` to retrieve. + * @return A promise that fulfills with the specified `Ruleset`. + */ + getRuleset(name: string): Promise; + + /** + * Deletes the {@link admin.securityRules.Ruleset `Ruleset`} identified by the given + * name. The input name should be the short name string without the project ID + * prefix. For example, to delete the `projects/project-id/rulesets/my-ruleset`, + * pass the short name "my-ruleset". Rejects with a `not-found` error if the + * specified `Ruleset` cannot be found. + * + * @param name Name of the `Ruleset` to delete. + * @return A promise that fulfills when the `Ruleset` is deleted. + */ + deleteRuleset(name: string): Promise; + + /** + * Retrieves a page of ruleset metadata. + * + * @param pageSize The page size, 100 if undefined. This is also the maximum allowed + * limit. + * @param nextPageToken The next page token. If not specified, returns rulesets + * starting without any offset. + * @return A promise that fulfills with a page of rulesets. + */ + listRulesetMetadata( + pageSize?: number, nextPageToken?: string): Promise; + + /** + * Gets the {@link admin.securityRules.Ruleset `Ruleset`} currently applied to + * Cloud Firestore. Rejects with a `not-found` error if no ruleset is applied + * on Firestore. + * + * @return A promise that fulfills with the Firestore ruleset. + */ + getFirestoreRuleset(): Promise; + + /** + * Creates a new {@link admin.securityRules.Ruleset `Ruleset`} from the given + * source, and applies it to Cloud Firestore. + * + * @param source Rules source to apply. + * @return A promise that fulfills when the ruleset is created and released. + */ + releaseFirestoreRulesetFromSource(source: string | Buffer): Promise; + + /** + * Applies the specified {@link admin.securityRules.Ruleset `Ruleset`} ruleset + * to Cloud Firestore. + * + * @param ruleset Name of the ruleset to apply or a `RulesetMetadata` object + * containing the name. + * @return A promise that fulfills when the ruleset is released. + */ + releaseFirestoreRuleset(ruleset: string | admin.securityRules.RulesetMetadata): Promise; + + /** + * Gets the {@link admin.securityRules.Ruleset `Ruleset`} currently applied to a + * Cloud Storage bucket. Rejects with a `not-found` error if no ruleset is applied + * on the bucket. + * + * @param bucket Optional name of the Cloud Storage bucket to be retrieved. If not + * specified, retrieves the ruleset applied on the default bucket configured via + * `AppOptions`. + * @return A promise that fulfills with the Cloud Storage ruleset. + */ + getStorageRuleset(bucket?: string): Promise; + + /** + * Creates a new {@link admin.securityRules.Ruleset `Ruleset`} from the given + * source, and applies it to a Cloud Storage bucket. + * + * @param source Rules source to apply. + * @param bucket Optional name of the Cloud Storage bucket to apply the rules on. If + * not specified, applies the ruleset on the default bucket configured via + * {@link admin.AppOptions `AppOptions`}. + * @return A promise that fulfills when the ruleset is created and released. + */ + releaseStorageRulesetFromSource( + source: string | Buffer, bucket?: string): Promise; + + /** + * Applies the specified {@link admin.securityRules.Ruleset `Ruleset`} ruleset + * to a Cloud Storage bucket. + * + * @param ruleset Name of the ruleset to apply or a `RulesetMetadata` object + * containing the name. + * @param bucket Optional name of the Cloud Storage bucket to apply the rules on. If + * not specified, applies the ruleset on the default bucket configured via + * {@link admin.AppOptions `AppOptions`}. + * @return A promise that fulfills when the ruleset is released. + */ + releaseStorageRuleset( + ruleset: string | admin.securityRules.RulesetMetadata, bucket?: string): Promise; + } +} diff --git a/src/security-rules/security-rules-api-client.ts b/src/security-rules/security-rules-api-client.ts index 01bc02baee..37989081fb 100644 --- a/src/security-rules/security-rules-api-client.ts +++ b/src/security-rules/security-rules-api-client.ts @@ -35,7 +35,7 @@ export interface Release { export interface RulesetContent { readonly source: { - readonly files: Array<{ name: string, content: string }>; + readonly files: Array<{ name: string; content: string }>; }; } @@ -45,7 +45,7 @@ export interface RulesetResponse extends RulesetContent { } export interface ListRulesetsResponse { - readonly rulesets: Array<{ name: string, createTime: string }>; + readonly rulesets: Array<{ name: string; createTime: string }>; readonly nextPageToken?: string; } @@ -123,7 +123,7 @@ export class SecurityRulesApiClient { }); } - public listRulesets(pageSize: number = 100, pageToken?: string): Promise { + public listRulesets(pageSize = 100, pageToken?: string): Promise { if (!validator.isNumber(pageSize)) { const err = new FirebaseSecurityRulesError('invalid-argument', 'Invalid page size.'); return Promise.reject(err); diff --git a/src/security-rules/security-rules.ts b/src/security-rules/security-rules.ts index 77fff948fa..21da0eabab 100644 --- a/src/security-rules/security-rules.ts +++ b/src/security-rules/security-rules.ts @@ -303,7 +303,7 @@ export class SecurityRules implements FirebaseServiceInterface { * without any offset. * @returns {Promise} A promise that fulfills a page of rulesets. */ - public listRulesetMetadata(pageSize: number = 100, nextPageToken?: string): Promise { + public listRulesetMetadata(pageSize = 100, nextPageToken?: string): Promise { return this.client.listRulesets(pageSize, nextPageToken) .then((response) => { return new RulesetMetadataListImpl(response); diff --git a/src/storage/storage.ts b/src/storage/storage.ts index 6b3ddcaef1..afd3b003de 100644 --- a/src/storage/storage.ts +++ b/src/storage/storage.ts @@ -79,8 +79,8 @@ export class Storage implements FirebaseServiceInterface { // guaranteed to be available. projectId: projectId!, credentials: { - private_key: credential.privateKey, - client_email: credential.clientEmail, + private_key: credential.privateKey, // eslint-disable-line @typescript-eslint/camelcase + client_email: credential.clientEmail, // eslint-disable-line @typescript-eslint/camelcase }, }); } else if (isApplicationDefault(app.options.credential)) { diff --git a/src/utils/api-request.ts b/src/utils/api-request.ts index 0ec85bfe8f..9f1b8c18ca 100644 --- a/src/utils/api-request.ts +++ b/src/utils/api-request.ts @@ -213,7 +213,7 @@ export function defaultRetryConfig(): RetryConfig { * * @param retry The configuration to be validated. */ -function validateRetryConfig(retry: RetryConfig) { +function validateRetryConfig(retry: RetryConfig): void { if (!validator.isNumber(retry.maxRetries) || retry.maxRetries < 0) { throw new FirebaseAppError( AppErrorCodes.INVALID_ARGUMENT, 'maxRetries must be a non-negative integer'); @@ -274,7 +274,7 @@ export class HttpClient { * @param {number} retryAttempts Number of retries performed up to now. * @return {Promise} A promise that resolves with the response details. */ - private sendWithRetry(config: HttpRequestConfig, retryAttempts: number = 0): Promise { + private sendWithRetry(config: HttpRequestConfig, retryAttempts = 0): Promise { return AsyncHttpCall.invoke(config) .then((resp) => { return this.createHttpResponse(resp); @@ -481,7 +481,7 @@ class AsyncHttpCall { } } - private execute() { + private execute(): void { const transport: any = this.options.protocol === 'https:' ? https : http; const req: http.ClientRequest = transport.request(this.options, (res: http.IncomingMessage) => { this.handleResponse(res, req); @@ -508,15 +508,15 @@ class AsyncHttpCall { req.end(this.entity); } - private handleResponse(res: http.IncomingMessage, req: http.ClientRequest) { + private handleResponse(res: http.IncomingMessage, req: http.ClientRequest): void { if (req.aborted) { return; } if (!res.statusCode) { throw new FirebaseAppError( - AppErrorCodes.INTERNAL_ERROR, - 'Expected a statusCode on the response from a ClientRequest'); + AppErrorCodes.INTERNAL_ERROR, + 'Expected a statusCode on the response from a ClientRequest'); } const response: LowLevelResponse = { @@ -572,7 +572,7 @@ class AsyncHttpCall { const encodings = ['gzip', 'compress', 'deflate']; if (res.headers['content-encoding'] && encodings.indexOf(res.headers['content-encoding']) !== -1) { // Add the unzipper to the body stream processing pipeline. - const zlib: typeof zlibmod = require('zlib'); + const zlib: typeof zlibmod = require('zlib'); // eslint-disable-line @typescript-eslint/no-var-requires respStream = respStream.pipe(zlib.createUnzip()); // Remove the content-encoding in order to not confuse downstream operations. delete res.headers['content-encoding']; @@ -581,9 +581,9 @@ class AsyncHttpCall { } private handleMultipartResponse( - response: LowLevelResponse, respStream: Readable, boundary: string) { + response: LowLevelResponse, respStream: Readable, boundary: string): void { - const dicer = require('dicer'); + const dicer = require('dicer'); // eslint-disable-line @typescript-eslint/no-var-requires const multipartParser = new dicer({boundary}); const responseBuffer: Buffer[] = []; multipartParser.on('part', (part: any) => { @@ -607,7 +607,7 @@ class AsyncHttpCall { respStream.pipe(multipartParser); } - private handleRegularResponse(response: LowLevelResponse, respStream: Readable) { + private handleRegularResponse(response: LowLevelResponse, respStream: Readable): void { const responseBuffer: Buffer[] = []; respStream.on('data', (chunk: Buffer) => { responseBuffer.push(chunk); @@ -631,7 +631,7 @@ class AsyncHttpCall { * Finalizes the current HTTP call in-flight by either resolving or rejecting the associated * promise. In the event of an error, adds additional useful information to the returned error. */ - private finalizeResponse(response: LowLevelResponse) { + private finalizeResponse(response: LowLevelResponse): void { if (response.status >= 200 && response.status < 300) { this.resolve(response); } else { @@ -652,7 +652,7 @@ class AsyncHttpCall { message: string, code?: string | null, request?: http.ClientRequest | null, - response?: LowLevelResponse) { + response?: LowLevelResponse): void { const error = new Error(message); this.enhanceAndReject(error, code, request, response); @@ -662,7 +662,7 @@ class AsyncHttpCall { error: any, code?: string | null, request?: http.ClientRequest | null, - response?: LowLevelResponse) { + response?: LowLevelResponse): void { this.reject(this.enhanceError(error, code, request, response)); } @@ -778,7 +778,7 @@ class HttpRequestConfigImpl implements HttpRequestConfig { const parsedUrl = new url.URL(fullUrl); const dataObj = this.data as {[key: string]: string}; for (const key in dataObj) { - if (dataObj.hasOwnProperty(key)) { + if (Object.prototype.hasOwnProperty.call(dataObj, key)) { parsedUrl.searchParams.append(key, dataObj[key]); } } @@ -837,7 +837,7 @@ export class ApiSettings { constructor(private endpoint: string, private httpMethod: HttpMethod = 'POST') { this.setRequestValidator(null) - .setResponseValidator(null); + .setResponseValidator(null); } /** @return {string} The backend API endpoint. */ @@ -855,7 +855,7 @@ export class ApiSettings { * @return {ApiSettings} The current API settings instance. */ public setRequestValidator(requestValidator: ApiCallbackFunction | null): ApiSettings { - const nullFunction: (_: object) => void = (_: object) => undefined; + const nullFunction: ApiCallbackFunction = () => undefined; this.requestValidator = requestValidator || nullFunction; return this; } @@ -870,7 +870,7 @@ export class ApiSettings { * @return {ApiSettings} The current API settings instance. */ public setResponseValidator(responseValidator: ApiCallbackFunction | null): ApiSettings { - const nullFunction: (_: object) => void = (_: object) => undefined; + const nullFunction: ApiCallbackFunction = () => undefined; this.responseValidator = responseValidator || nullFunction; return this; } @@ -958,29 +958,29 @@ export class ExponentialBackoffPoller extends EventEmitter { private repoll(): void { this.pollCallback!() - .then((result) => { - if (this.completed) { - return; - } + .then((result) => { + if (this.completed) { + return; + } - if (!result) { - this.repollTimer = + if (!result) { + this.repollTimer = setTimeout(() => this.emit('poll'), this.getPollingDelayMillis()); - this.numTries++; - return; - } - - this.markCompleted(); - this.resolve(result); - }) - .catch((err) => { - if (this.completed) { - return; - } - - this.markCompleted(); - this.reject(err); - }); + this.numTries++; + return; + } + + this.markCompleted(); + this.resolve(result); + }) + .catch((err) => { + if (this.completed) { + return; + } + + this.markCompleted(); + this.reject(err); + }); } private getPollingDelayMillis(): number { diff --git a/src/utils/deep-copy.ts b/src/utils/deep-copy.ts index 8f2520e36f..72a791d30a 100644 --- a/src/utils/deep-copy.ts +++ b/src/utils/deep-copy.ts @@ -46,12 +46,12 @@ export function deepExtend(target: any, source: any): any { } switch (source.constructor) { - case Date: + case Date: { // Treat Dates like scalars; if the target date object had any child // properties - they will be lost! const dateValue = (source as any) as Date; return new Date(dateValue.getTime()); - + } case Object: if (target === undefined) { target = {}; @@ -69,7 +69,7 @@ export function deepExtend(target: any, source: any): any { } for (const prop in source) { - if (!source.hasOwnProperty(prop)) { + if (!Object.prototype.hasOwnProperty.call(source, prop)) { continue; } target[prop] = deepExtend(target[prop], source[prop]); diff --git a/src/utils/error.ts b/src/utils/error.ts index 9f1a8e479d..297447697c 100755 --- a/src/utils/error.ts +++ b/src/utils/error.ts @@ -430,6 +430,14 @@ export class AuthClientErrorCode { code: 'invalid-email', message: 'The email address is improperly formatted.', }; + public static INVALID_ENROLLED_FACTORS = { + code: 'invalid-enrolled-factors', + message: 'The enrolled factors must be a valid array of MultiFactorInfo objects.', + }; + public static INVALID_ENROLLMENT_TIME = { + code: 'invalid-enrollment-time', + message: 'The second factor enrollment time must be a valid UTC date string.', + }; public static INVALID_HASH_ALGORITHM = { code: 'invalid-hash-algorithm', message: 'The hash algorithm must match one of the strings in the list of ' + @@ -558,6 +566,11 @@ export class AuthClientErrorCode { code: 'missing-display-name', message: 'The resource being created or edited is missing a valid display name.', }; + public static MISSING_EMAIL = { + code: 'missing-email', + message: 'The email is required for the specified action. For example, a multi-factor user ' + + 'requires a verified email.', + }; public static MISSING_IOS_BUNDLE_ID = { code: 'missing-ios-bundle-id', message: 'The request is missing an iOS Bundle ID.', @@ -616,6 +629,14 @@ export class AuthClientErrorCode { code: 'quota-exceeded', message: 'The project quota for the specified operation has been exceeded.', }; + public static SECOND_FACTOR_LIMIT_EXCEEDED = { + code: 'second-factor-limit-exceeded', + message: 'The maximum number of allowed second factors on a user has been exceeded.', + }; + public static SECOND_FACTOR_UID_ALREADY_EXISTS = { + code: 'second-factor-uid-already-exists', + message: 'The specified second factor "uid" already exists.', + }; public static SESSION_COOKIE_EXPIRED = { code: 'session-cookie-expired', message: 'The Firebase session cookie is expired.', @@ -637,10 +658,23 @@ export class AuthClientErrorCode { message: 'The domain of the continue URL is not whitelisted. Whitelist the domain in the ' + 'Firebase console.', }; + public static UNSUPPORTED_FIRST_FACTOR = { + code: 'unsupported-first-factor', + message: 'A multi-factor user requires a supported first factor.', + }; + public static UNSUPPORTED_SECOND_FACTOR = { + code: 'unsupported-second-factor', + message: 'The request specified an unsupported type of second factor.', + }; public static UNSUPPORTED_TENANT_OPERATION = { code: 'unsupported-tenant-operation', message: 'This operation is not supported in a multi-tenant context.', }; + public static UNVERIFIED_EMAIL = { + code: 'unverified-email', + message: 'A verified email is required for the specified action. For example, a multi-factor user ' + + 'requires a verified email.', + }; public static USER_NOT_FOUND = { code: 'user-not-found', message: 'There is no user record corresponding to the provided identifier.', @@ -803,6 +837,8 @@ const AUTH_SERVER_TO_CLIENT_CODE: ServerToClientCode = { DUPLICATE_EMAIL: 'EMAIL_ALREADY_EXISTS', // uploadAccount provides a localId that already exists. DUPLICATE_LOCAL_ID: 'UID_ALREADY_EXISTS', + // Request specified a multi-factor enrollment ID that already exists. + DUPLICATE_MFA_ENROLLMENT_ID: 'SECOND_FACTOR_UID_ALREADY_EXISTS', // setAccountInfo email already exists. EMAIL_EXISTS: 'EMAIL_ALREADY_EXISTS', // Reserved claim name. @@ -841,6 +877,9 @@ const AUTH_SERVER_TO_CLIENT_CODE: ServerToClientCode = { MISSING_CONFIG_ID: 'MISSING_PROVIDER_ID', // Missing tenant display name: This can be thrown on CreateTenant and UpdateTenant. MISSING_DISPLAY_NAME: 'MISSING_DISPLAY_NAME', + // Email is required for the specified action. For example a multi-factor user requires + // a verified email. + MISSING_EMAIL: 'MISSING_EMAIL', // Missing iOS bundle ID. MISSING_IOS_BUNDLE_ID: 'MISSING_IOS_BUNDLE_ID', // Missing OIDC issuer. @@ -865,6 +904,8 @@ const AUTH_SERVER_TO_CLIENT_CODE: ServerToClientCode = { PROJECT_NOT_FOUND: 'PROJECT_NOT_FOUND', // In multi-tenancy context: project creation quota exceeded. QUOTA_EXCEEDED: 'QUOTA_EXCEEDED', + // Currently only 5 second factors can be set on the same user. + SECOND_FACTOR_LIMIT_EXCEEDED: 'SECOND_FACTOR_LIMIT_EXCEEDED', // Tenant not found. TENANT_NOT_FOUND: 'TENANT_NOT_FOUND', // Tenant ID mismatch. @@ -873,8 +914,15 @@ const AUTH_SERVER_TO_CLIENT_CODE: ServerToClientCode = { TOKEN_EXPIRED: 'ID_TOKEN_EXPIRED', // Continue URL provided in ActionCodeSettings has a domain that is not whitelisted. UNAUTHORIZED_DOMAIN: 'UNAUTHORIZED_DOMAIN', + // A multi-factor user requires a supported first factor. + UNSUPPORTED_FIRST_FACTOR: 'UNSUPPORTED_FIRST_FACTOR', + // The request specified an unsupported type of second factor. + UNSUPPORTED_SECOND_FACTOR: 'UNSUPPORTED_SECOND_FACTOR', // Operation is not supported in a multi-tenant context. UNSUPPORTED_TENANT_OPERATION: 'UNSUPPORTED_TENANT_OPERATION', + // A verified email is required for the specified action. For example a multi-factor user + // requires a verified email. + UNVERIFIED_EMAIL: 'UNVERIFIED_EMAIL', // User on which action is to be performed is not found. USER_NOT_FOUND: 'USER_NOT_FOUND', // Password provided is too weak. diff --git a/src/utils/index.ts b/src/utils/index.ts index a30bc7e3d0..f6e2f41232 100755 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -132,8 +132,8 @@ export function formatString(str: string, params?: object): string { let formatted = str; Object.keys(params || {}).forEach((key) => { formatted = formatted.replace( - new RegExp('{' + key + '}', 'g'), - (params as {[key: string]: string})[key]); + new RegExp('{' + key + '}', 'g'), + (params as {[key: string]: string})[key]); }); return formatted; } @@ -151,7 +151,7 @@ export function generateUpdateMask(obj: {[key: string]: any}): string[] { return updateMask; } for (const key in obj) { - if (obj.hasOwnProperty(key) && typeof obj[key] !== 'undefined') { + if (Object.prototype.hasOwnProperty.call(obj, key) && typeof obj[key] !== 'undefined') { const maskList = generateUpdateMask(obj[key]); if (maskList.length > 0) { maskList.forEach((mask) => { diff --git a/src/utils/validator.ts b/src/utils/validator.ts old mode 100644 new mode 100755 index 8edc54a97e..d5159ae836 --- a/src/utils/validator.ts +++ b/src/utils/validator.ts @@ -186,6 +186,37 @@ export function isPhoneNumber(phoneNumber: any): boolean { } +/** + * Validates that a string is a valid ISO date string. + * + * @param dateString The string to validate. + * @return Whether the string is a valid ISO date string. + */ +export function isISODateString(dateString: any): boolean { + try { + return isNonEmptyString(dateString) && + (new Date(dateString).toISOString() === dateString); + } catch (e) { + return false; + } +} + + +/** + * Validates that a string is a valid UTC date string. + * + * @param dateString The string to validate. + * @return Whether the string is a valid UTC date string. + */ +export function isUTCDateString(dateString: any): boolean { + try { + return isNonEmptyString(dateString) && + (new Date(dateString).toUTCString() === dateString); + } catch (e) { + return false; + } +} + /** * Validates that a string is a valid web URL. @@ -198,7 +229,7 @@ export function isURL(urlStr: any): boolean { return false; } // Lookup illegal characters. - const re = /[^a-z0-9\:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=\.\-\_\~\%]/i; + const re = /[^a-z0-9:/?#[\]@!$&'()*+,;=.\-_~%]/i; if (re.test(urlStr)) { return false; } @@ -213,12 +244,12 @@ export function isURL(urlStr: any): boolean { } // Validate hostname: Can contain letters, numbers, underscore and dashes separated by a dot. // Each zone must not start with a hyphen or underscore. - if (!hostname || !/^[a-zA-Z0-9]+[\w\-]*([\.]?[a-zA-Z0-9]+[\w\-]*)*$/.test(hostname)) { + if (!hostname || !/^[a-zA-Z0-9]+[\w-]*([.]?[a-zA-Z0-9]+[\w-]*)*$/.test(hostname)) { return false; } // Allow for pathnames: (/chars+)*/? // Where chars can be a combination of: a-z A-Z 0-9 - _ . ~ ! $ & ' ( ) * + , ; = : @ % - const pathnameRe = /^(\/[\w\-\.\~\!\$\'\(\)\*\+\,\;\=\:\@\%]+)*\/?$/; + const pathnameRe = /^(\/[\w\-.~!$'()*+,;=:@%]+)*\/?$/; // Validate pathname. if (pathname && pathname !== '/' && diff --git a/test/integration/auth.spec.ts b/test/integration/auth.spec.ts index b78e1e6c43..19f33b1d05 100755 --- a/test/integration/auth.spec.ts +++ b/test/integration/auth.spec.ts @@ -32,9 +32,7 @@ import { AuthProviderConfig } from '../../src/auth/auth-config'; import { deepExtend, deepCopy } from '../../src/utils/deep-copy'; import { User, FirebaseAuth } from '@firebase/auth-types'; -/* tslint:disable:no-var-requires */ -const chalk = require('chalk'); -/* tslint:enable:no-var-requires */ +const chalk = require('chalk'); // eslint-disable-line @typescript-eslint/no-var-requires chai.should(); chai.use(chaiAsPromised); @@ -43,6 +41,7 @@ const expect = chai.expect; const newUserUid = generateRandomString(20); const nonexistentUid = generateRandomString(20); +const newMultiFactorUserUid = generateRandomString(20); const sessionCookieUids = [ generateRandomString(20), generateRandomString(20), @@ -140,6 +139,52 @@ describe('admin.auth', () => { }); }); + it('createUser() creates a new user with enrolled second factors', () => { + const enrolledFactors = [ + { + phoneNumber: '+16505550001', + displayName: 'Work phone number', + factorId: 'phone', + }, + { + phoneNumber: '+16505550002', + displayName: 'Personal phone number', + factorId: 'phone', + }, + ]; + const newUserData: any = { + uid: newMultiFactorUserUid, + email: generateRandomString(20).toLowerCase() + '@example.com', + emailVerified: true, + password: 'password', + multiFactor: { + enrolledFactors, + }, + }; + return admin.auth().createUser(newUserData) + .then((userRecord) => { + expect(userRecord.uid).to.equal(newMultiFactorUserUid); + // Confirm expected email. + expect(userRecord.email).to.equal(newUserData.email); + // Confirm second factors added to user. + expect(userRecord.multiFactor!.enrolledFactors.length).to.equal(2); + // Confirm first enrolled second factor. + const firstMultiFactor = userRecord.multiFactor!.enrolledFactors[0]; + expect(firstMultiFactor.uid).not.to.be.undefined; + expect(firstMultiFactor.enrollmentTime).not.to.be.undefined; + expect((firstMultiFactor as admin.auth.PhoneMultiFactorInfo).phoneNumber).to.equal(enrolledFactors[0].phoneNumber); + expect(firstMultiFactor.displayName).to.equal(enrolledFactors[0].displayName); + expect(firstMultiFactor.factorId).to.equal(enrolledFactors[0].factorId); + // Confirm second enrolled second factor. + const secondMultiFactor = userRecord.multiFactor!.enrolledFactors[1]; + expect(secondMultiFactor.uid).not.to.be.undefined; + expect(secondMultiFactor.enrollmentTime).not.to.be.undefined; + expect((secondMultiFactor as admin.auth.PhoneMultiFactorInfo).phoneNumber).to.equal(enrolledFactors[1].phoneNumber); + expect(secondMultiFactor.displayName).to.equal(enrolledFactors[1].displayName); + expect(secondMultiFactor.factorId).to.equal(enrolledFactors[1].factorId); + }); + }); + it('createUser() fails when the UID is already in use', () => { const newUserData: any = clone(mockUserData); newUserData.uid = newUserUid; @@ -192,16 +237,16 @@ describe('admin.auth', () => { expect(listUsersResult.users[0].uid).to.equal(uids[1]); expect( - listUsersResult.users[0].passwordHash, - 'Missing passwordHash field. A common cause would be forgetting to ' + listUsersResult.users[0].passwordHash, + 'Missing passwordHash field. A common cause would be forgetting to ' + 'add the "Firebase Authentication Admin" permission. See ' + 'instructions in CONTRIBUTING.md', ).to.be.ok; expect(listUsersResult.users[0].passwordHash!.length).greaterThan(0); expect( - listUsersResult.users[0].passwordSalt, - 'Missing passwordSalt field. A common cause would be forgetting to ' + listUsersResult.users[0].passwordSalt, + 'Missing passwordSalt field. A common cause would be forgetting to ' + 'add the "Firebase Authentication Admin" permission. See ' + 'instructions in CONTRIBUTING.md', ).to.be.ok; @@ -253,7 +298,7 @@ describe('admin.auth', () => { .then(() => { // New sign-in should succeed. return clientAuth().signInWithEmailAndPassword( - mockUserData.email, mockUserData.password); + mockUserData.email, mockUserData.password); }) .then(({user}) => { // Get new session's ID token. @@ -286,13 +331,13 @@ describe('admin.auth', () => { return user!.getIdToken(); }) .then((idToken) => { - // Verify ID token contents. - return admin.auth().verifyIdToken(idToken); + // Verify ID token contents. + return admin.auth().verifyIdToken(idToken); }) .then((decodedIdToken: {[key: string]: any}) => { // Confirm expected claims set on the user's ID token. for (const key in customClaims) { - if (customClaims.hasOwnProperty(key)) { + if (Object.prototype.hasOwnProperty.call(customClaims, key)) { expect(decodedIdToken[key]).to.equal(customClaims[key]); } } @@ -312,24 +357,45 @@ describe('admin.auth', () => { .then((idToken) => { // Verify ID token contents. return admin.auth().verifyIdToken(idToken); - }) - .then((decodedIdToken: {[key: string]: any}) => { - // Confirm all custom claims are cleared. - for (const key in customClaims) { - if (customClaims.hasOwnProperty(key)) { - expect(decodedIdToken[key]).to.be.undefined; - } - } - }); + }) + .then((decodedIdToken: {[key: string]: any}) => { + // Confirm all custom claims are cleared. + for (const key in customClaims) { + if (Object.prototype.hasOwnProperty.call(customClaims, key)) { + expect(decodedIdToken[key]).to.be.undefined; + } + } + }); }); it('updateUser() updates the user record with the given parameters', () => { const updatedDisplayName = 'Updated User ' + newUserUid; + const now = new Date(1476235905000).toUTCString(); + // Update user with enrolled second factors. + const enrolledFactors = [ + { + uid: 'mfaUid1', + phoneNumber: '+16505550001', + displayName: 'Work phone number', + factorId: 'phone', + enrollmentTime: now, + }, + { + uid: 'mfaUid2', + phoneNumber: '+16505550002', + displayName: 'Personal phone number', + factorId: 'phone', + enrollmentTime: now, + }, + ]; return admin.auth().updateUser(newUserUid, { email: updatedEmail, phoneNumber: updatedPhone, emailVerified: true, displayName: updatedDisplayName, + multiFactor: { + enrolledFactors, + }, }) .then((userRecord) => { expect(userRecord.emailVerified).to.be.true; @@ -338,6 +404,31 @@ describe('admin.auth', () => { expect(userRecord.email).to.equal(updatedEmail); // Confirm expected phone number. expect(userRecord.phoneNumber).to.equal(updatedPhone); + // Confirm second factors added to user. + const actualUserRecord: {[key: string]: any} = userRecord.toJSON(); + expect(actualUserRecord.multiFactor.enrolledFactors.length).to.equal(2); + expect(actualUserRecord.multiFactor.enrolledFactors).to.deep.equal(enrolledFactors); + // Update list of second factors. + return admin.auth().updateUser(newUserUid, { + multiFactor: { + enrolledFactors: [enrolledFactors[0]], + }, + }); + }) + .then((userRecord) => { + expect(userRecord.multiFactor!.enrolledFactors.length).to.equal(1); + const actualUserRecord: {[key: string]: any} = userRecord.toJSON(); + expect(actualUserRecord.multiFactor.enrolledFactors[0]).to.deep.equal(enrolledFactors[0]); + // Remove all second factors. + return admin.auth().updateUser(newUserUid, { + multiFactor: { + enrolledFactors: null, + }, + }); + }) + .then((userRecord) => { + // Confirm all second factors removed. + expect(userRecord.multiFactor).to.be.undefined; }); }); @@ -371,40 +462,40 @@ describe('admin.auth', () => { return admin.auth().createCustomToken(newUserUid, { isAdmin: true, }) - .then((customToken) => { - return clientAuth().signInWithCustomToken(customToken); - }) - .then(({user}) => { - expect(user).to.exist; - return user!.getIdToken(); - }) - .then((idToken) => { - return admin.auth().verifyIdToken(idToken); - }) - .then((token) => { - expect(token.uid).to.equal(newUserUid); - expect(token.isAdmin).to.be.true; - }); + .then((customToken) => { + return clientAuth().signInWithCustomToken(customToken); + }) + .then(({user}) => { + expect(user).to.exist; + return user!.getIdToken(); + }) + .then((idToken) => { + return admin.auth().verifyIdToken(idToken); + }) + .then((token) => { + expect(token.uid).to.equal(newUserUid); + expect(token.isAdmin).to.be.true; + }); }); it('createCustomToken() can mint JWTs without a service account', () => { return admin.auth(noServiceAccountApp).createCustomToken(newUserUid, { isAdmin: true, }) - .then((customToken) => { - return clientAuth().signInWithCustomToken(customToken); - }) - .then(({user}) => { - expect(user).to.exist; - return user!.getIdToken(); - }) - .then((idToken) => { - return admin.auth(noServiceAccountApp).verifyIdToken(idToken); - }) - .then((token) => { - expect(token.uid).to.equal(newUserUid); - expect(token.isAdmin).to.be.true; - }); + .then((customToken) => { + return clientAuth().signInWithCustomToken(customToken); + }) + .then(({user}) => { + expect(user).to.exist; + return user!.getIdToken(); + }) + .then((idToken) => { + return admin.auth(noServiceAccountApp).verifyIdToken(idToken); + }) + .then((token) => { + expect(token.uid).to.equal(newUserUid); + expect(token.isAdmin).to.be.true; + }); }); it('verifyIdToken() fails when called with an invalid token', () => { @@ -441,7 +532,7 @@ describe('admin.auth', () => { it('generatePasswordResetLink() should return a password reset link', () => { // Ensure old password set on created user. return admin.auth().updateUser(uid, {password: 'password'}) - .then((userRecord) => { + .then(() => { return admin.auth().generatePasswordResetLink(email, actionCodeSettings); }) .then((link) => { @@ -548,8 +639,8 @@ describe('admin.auth', () => { const promises: Array> = []; createdTenants.forEach((tenantId) => { promises.push( - admin.auth().tenantManager().deleteTenant(tenantId) - .catch((error) => {/** Ignore. */})); + admin.auth().tenantManager().deleteTenant(tenantId) + .catch(() => {/** Ignore. */})); }); return Promise.all(promises); }); @@ -595,7 +686,7 @@ describe('admin.auth', () => { // If user successfully created, make sure it is deleted at the end of the test suite. if (createdUserUid) { return tenantAwareAuth.deleteUser(createdUserUid) - .catch((error) => { + .catch(() => { // Ignore error. }); } @@ -629,12 +720,12 @@ describe('admin.auth', () => { email: updatedEmail, phoneNumber: updatedPhone, }) - .then((userRecord) => { - expect(userRecord.uid).to.equal(createdUserUid); - expect(userRecord.tenantId).to.equal(createdTenantId); - expect(userRecord.email).to.equal(updatedEmail); - expect(userRecord.phoneNumber).to.equal(updatedPhone); - }); + .then((userRecord) => { + expect(userRecord.uid).to.equal(createdUserUid); + expect(userRecord.tenantId).to.equal(createdTenantId); + expect(userRecord.email).to.equal(updatedEmail); + expect(userRecord.phoneNumber).to.equal(updatedPhone); + }); }); it('generateEmailVerificationLink() should generate the link for tenant specific user', () => { @@ -782,7 +873,7 @@ describe('admin.auth', () => { after(() => { if (tenantAwareAuth) { return tenantAwareAuth.deleteProviderConfig(authProviderConfig.providerId) - .catch((error) => { + .catch(() => { // Ignore error. }); } @@ -801,7 +892,7 @@ describe('admin.auth', () => { }) .then((config) => { const modifiedConfig = deepExtend( - {providerId: authProviderConfig.providerId}, modifiedConfigOptions); + {providerId: authProviderConfig.providerId}, modifiedConfigOptions); assertDeepEqualUnordered(modifiedConfig, config); return tenantAwareAuth.deleteProviderConfig(authProviderConfig.providerId); }) @@ -809,7 +900,7 @@ describe('admin.auth', () => { return tenantAwareAuth.getProviderConfig(authProviderConfig.providerId) .should.eventually.be.rejected.and.have.property('code', 'auth/configuration-not-found'); }); - }); + }); }); describe('OIDC management APIs', () => { @@ -840,7 +931,7 @@ describe('admin.auth', () => { after(() => { if (tenantAwareAuth) { return tenantAwareAuth.deleteProviderConfig(authProviderConfig.providerId) - .catch((error) => { + .catch(() => { // Ignore error. }); } @@ -859,7 +950,7 @@ describe('admin.auth', () => { }) .then((config) => { const modifiedConfig = deepExtend( - {providerId: authProviderConfig.providerId}, modifiedConfigOptions); + {providerId: authProviderConfig.providerId}, modifiedConfigOptions); assertDeepEqualUnordered(modifiedConfig, config); return tenantAwareAuth.deleteProviderConfig(authProviderConfig.providerId); }) @@ -867,7 +958,7 @@ describe('admin.auth', () => { return tenantAwareAuth.getProviderConfig(authProviderConfig.providerId) .should.eventually.be.rejected.and.have.property('code', 'auth/configuration-not-found'); }); - }); + }); }); it('getTenant() should resolve with expected tenant', () => { @@ -936,7 +1027,7 @@ describe('admin.auth', () => { .then(() => { return admin.auth().tenantManager().getTenant(createdTenantId); }) - .then((result) => { + .then(() => { throw new Error('unexpected success'); }) .catch((error) => { @@ -969,10 +1060,10 @@ describe('admin.auth', () => { enableRequestSigning: true, }; - const removeTempConfigs = () => { + const removeTempConfigs = (): Promise => { return Promise.all([ - admin.auth().deleteProviderConfig(authProviderConfig1.providerId).catch((error) => {/* empty */}), - admin.auth().deleteProviderConfig(authProviderConfig2.providerId).catch((error) => {/* empty */}), + admin.auth().deleteProviderConfig(authProviderConfig1.providerId).catch(() => {/* empty */}), + admin.auth().deleteProviderConfig(authProviderConfig2.providerId).catch(() => {/* empty */}), ]); }; @@ -1044,7 +1135,7 @@ describe('admin.auth', () => { return admin.auth().updateProviderConfig(authProviderConfig1.providerId, modifiedConfigOptions) .then((config) => { const modifiedConfig = deepExtend( - {providerId: authProviderConfig1.providerId}, modifiedConfigOptions); + {providerId: authProviderConfig1.providerId}, modifiedConfigOptions); assertDeepEqualUnordered(modifiedConfig, config); }); }); @@ -1072,7 +1163,7 @@ describe('admin.auth', () => { return admin.auth().updateProviderConfig(authProviderConfig1.providerId, deltaChanges) .then((config) => { const modifiedConfig = deepExtend( - {providerId: authProviderConfig1.providerId}, modifiedConfigOptions); + {providerId: authProviderConfig1.providerId}, modifiedConfigOptions); assertDeepEqualUnordered(modifiedConfig, config); }); }); @@ -1101,10 +1192,10 @@ describe('admin.auth', () => { clientId: 'CLIENT_ID2', }; - const removeTempConfigs = () => { + const removeTempConfigs = (): Promise => { return Promise.all([ - admin.auth().deleteProviderConfig(authProviderConfig1.providerId).catch((error) => {/* empty */}), - admin.auth().deleteProviderConfig(authProviderConfig2.providerId).catch((error) => {/* empty */}), + admin.auth().deleteProviderConfig(authProviderConfig1.providerId).catch(() => {/* empty */}), + admin.auth().deleteProviderConfig(authProviderConfig2.providerId).catch(() => {/* empty */}), ]); }; @@ -1208,6 +1299,7 @@ describe('admin.auth', () => { it('deleteUser() deletes the user with the given UID', () => { return Promise.all([ admin.auth().deleteUser(newUserUid), + admin.auth().deleteUser(newMultiFactorUserUid), admin.auth().deleteUser(uidFromCreateUserWithoutUid), ]).should.eventually.be.fulfilled; }); @@ -1236,7 +1328,7 @@ describe('admin.auth', () => { expectedExp = Math.floor((new Date().getTime() + expiresIn) / 1000); payloadClaims = decodedIdTokenClaims; payloadClaims.iss = payloadClaims.iss.replace( - 'securetoken.google.com', 'session.firebase.google.com'); + 'securetoken.google.com', 'session.firebase.google.com'); delete payloadClaims.exp; delete payloadClaims.iat; expectedIat = Math.floor(new Date().getTime() / 1000); @@ -1329,7 +1421,7 @@ describe('admin.auth', () => { describe('importUsers()', () => { const randomUid = 'import_' + generateRandomString(20).toLowerCase(); - let importUserRecord: any; + let importUserRecord: admin.auth.UserImportRecord; const rawPassword = 'password'; const rawSalt = 'NaCl'; // Simulate a user stored using SCRYPT being migrated to Firebase Auth via importUsers. @@ -1367,7 +1459,7 @@ describe('admin.auth', () => { const currentRawPassword = userImportTest.rawPassword; const currentRawSalt = userImportTest.rawSalt; return crypto.createHmac('sha256', currentHashKey) - .update(currentRawPassword + currentRawSalt).digest(); + .update(currentRawPassword + currentRawSalt).digest(); }, rawPassword, rawSalt, @@ -1400,7 +1492,7 @@ describe('admin.auth', () => { const currentRawPassword = userImportTest.rawPassword; const currentRawSalt = userImportTest.rawSalt; return Buffer.from(crypto.createHash('md5') - .update(currentRawSalt + currentRawPassword).digest('hex')); + .update(currentRawSalt + currentRawPassword).digest('hex')); }, rawPassword, rawSalt, @@ -1447,7 +1539,7 @@ describe('admin.auth', () => { const dkLen = userImportTest.importOptions.hash.derivedKeyLength!; return Buffer.from(scrypt.hashSync( - currentRawPassword, {N, r, p}, dkLen, Buffer.from(currentRawSalt))); + currentRawPassword, {N, r, p}, dkLen, Buffer.from(currentRawSalt))); }, rawPassword, rawSalt, @@ -1467,7 +1559,7 @@ describe('admin.auth', () => { expect(userImportTest.importOptions.hash.rounds).to.exist; const currentRounds = userImportTest.importOptions.hash.rounds!; return crypto.pbkdf2Sync( - currentRawPassword, currentRawSalt, currentRounds, 64, 'sha256'); + currentRawPassword, currentRawSalt, currentRounds, 64, 'sha256'); }, rawPassword, rawSalt, @@ -1475,7 +1567,7 @@ describe('admin.auth', () => { { name: 'SCRYPT', importOptions: scryptHashOptions as any, - computePasswordHash: (userImportTest: UserImportTest): Buffer => { + computePasswordHash: (): Buffer => { return Buffer.from(scryptPasswordHash, 'base64'); }, rawPassword, @@ -1543,19 +1635,79 @@ describe('admin.auth', () => { return admin.auth().getUser(uid); }).then((userRecord) => { // The phone number provider will be appended to the list of accounts. - importUserRecord.providerData.push({ - uid: importUserRecord.phoneNumber, + importUserRecord.providerData?.push({ + uid: importUserRecord.phoneNumber!, providerId: 'phone', - phoneNumber: importUserRecord.phoneNumber, + phoneNumber: importUserRecord.phoneNumber!, }); const actualUserRecord: {[key: string]: any} = userRecord.toJSON(); for (const key of Object.keys(importUserRecord)) { expect(JSON.stringify(actualUserRecord[key])) - .to.be.equal(JSON.stringify(importUserRecord[key])); + .to.be.equal(JSON.stringify((importUserRecord as any)[key])); } }).should.eventually.be.fulfilled; }); + it('successfully imports users with enrolled second factors', () => { + const uid = generateRandomString(20).toLowerCase(); + const email = uid + '@example.com'; + const now = new Date(1476235905000).toUTCString(); + const enrolledFactors: admin.auth.UpdatePhoneMultiFactorInfoRequest[] = [ + { + uid: 'mfaUid1', + phoneNumber: '+16505550001', + displayName: 'Work phone number', + factorId: 'phone', + enrollmentTime: now, + } , + { + uid: 'mfaUid2', + phoneNumber: '+16505550002', + displayName: 'Personal phone number', + factorId: 'phone', + enrollmentTime: now, + }, + ]; + + importUserRecord = { + uid, + email, + emailVerified: true, + displayName: 'Test User', + disabled: false, + metadata: { + lastSignInTime: now, + creationTime: now, + }, + providerData: [ + { + uid: uid + '-facebook', + displayName: 'Facebook User', + email, + providerId: 'facebook.com', + }, + ], + multiFactor: { + enrolledFactors, + }, + }; + uids.push(importUserRecord.uid); + + return admin.auth().importUsers([importUserRecord]) + .then((result) => { + expect(result.failureCount).to.equal(0); + expect(result.successCount).to.equal(1); + expect(result.errors.length).to.equal(0); + return admin.auth().getUser(uid); + }).then((userRecord) => { + // Confirm second factors added to user. + const actualUserRecord: {[key: string]: any} = userRecord.toJSON(); + expect(actualUserRecord.multiFactor.enrolledFactors.length).to.equal(2); + expect(actualUserRecord.multiFactor.enrolledFactors) + .to.deep.equal(importUserRecord.multiFactor?.enrolledFactors); + }).should.eventually.be.fulfilled; + }); + it('fails when invalid users are provided', () => { const users = [ {uid: generateRandomString(20).toLowerCase(), phoneNumber: '+1error'}, @@ -1591,7 +1743,9 @@ describe('admin.auth', () => { * @retunr {Promise} A promise that resolved on success. */ function testImportAndSignInUser( - importUserRecord: any, importOptions: any, rawPassword: string): Promise { + importUserRecord: admin.auth.UserImportRecord, + importOptions: any, + rawPassword: string): Promise { const users = [importUserRecord]; // Import the user record. return admin.auth().importUsers(users, importOptions) @@ -1601,7 +1755,7 @@ function testImportAndSignInUser( expect(result.successCount).to.equal(1); expect(result.errors.length).to.equal(0); // Sign in with an email and password to the imported account. - return clientAuth().signInWithEmailAndPassword(users[0].email, rawPassword); + return clientAuth().signInWithEmailAndPassword(users[0].email!, rawPassword); }) .then(({user}) => { // Confirm successful sign-in. @@ -1619,7 +1773,7 @@ function testImportAndSignInUser( * @return {Promise} A promise that resolves when the user is deleted * or is found not to exist. */ -function deletePhoneNumberUser(phoneNumber: string) { +function deletePhoneNumberUser(phoneNumber: string): Promise { return admin.auth().getUserByPhoneNumber(phoneNumber) .then((userRecord) => { return safeDelete(userRecord.uid); @@ -1638,7 +1792,7 @@ function deletePhoneNumberUser(phoneNumber: string) { * * @return {Promise} A promise that resolves when test preparations are ready. */ -function cleanup() { +function cleanup(): Promise { // Delete any existing users that could affect the test outcome. const promises: Array> = [ deletePhoneNumberUser(testPhoneNumber), @@ -1716,7 +1870,7 @@ function safeDelete(uid: string): Promise { } }); // Suppress errors in delete queue to not spill over to next item in queue. - deleteQueue = deletePromise.catch((error) => { + deleteQueue = deletePromise.catch(() => { // Do nothing. }); return deletePromise; @@ -1729,9 +1883,9 @@ function safeDelete(uid: string): Promise { * @param {[key: string]: any} expected object. * @param {[key: string]: any} actual object. */ -function assertDeepEqualUnordered(expected: {[key: string]: any}, actual: {[key: string]: any}) { +function assertDeepEqualUnordered(expected: {[key: string]: any}, actual: {[key: string]: any}): void { for (const key in expected) { - if (expected.hasOwnProperty(key)) { + if (Object.prototype.hasOwnProperty.call(expected, key)) { expect(actual[key]) .to.deep.equal(expected[key]); } diff --git a/test/integration/database.spec.ts b/test/integration/database.spec.ts index 3f9818a405..b64e099813 100644 --- a/test/integration/database.spec.ts +++ b/test/integration/database.spec.ts @@ -19,9 +19,8 @@ import * as chai from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; import {defaultApp, nullApp, nonNullApp, cmdArgs, databaseUrl} from './setup'; -/* tslint:disable:no-var-requires */ +// eslint-disable-next-line @typescript-eslint/no-var-requires const chalk = require('chalk'); -/* tslint:enable:no-var-requires */ chai.should(); chai.use(chaiAsPromised); @@ -171,14 +170,14 @@ describe('admin.database', () => { }); }); -// Check for type compilation. This method is not invoked by any -// tests. But it will trigger a TS compilation failure if the RTDB -// typings were not loaded correctly. +// Check for type compilation. This method is not invoked by any tests. But it +// will trigger a TS compilation failure if the RTDB typings were not loaded +// correctly. (Marked as export to avoid compilation warning.) // -// @ts-ignore: purposely unused method. -function addValueEventListener( - db: admin.database.Database, - callback: (s: admin.database.DataSnapshot | null) => any) { +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export function addValueEventListener( + db: admin.database.Database, + callback: (s: admin.database.DataSnapshot | null) => any): void { const eventType: admin.database.EventType = 'value'; db.ref().on(eventType, callback); } diff --git a/test/integration/firestore.spec.ts b/test/integration/firestore.spec.ts index 085212b9c2..d3bbf51493 100644 --- a/test/integration/firestore.spec.ts +++ b/test/integration/firestore.spec.ts @@ -50,7 +50,7 @@ describe('admin.firestore', () => { it('supports basic data access', () => { return reference.set(mountainView) - .then((result) => { + .then(() => { return reference.get(); }) .then((snapshot) => { @@ -58,7 +58,7 @@ describe('admin.firestore', () => { expect(data).to.deep.equal(mountainView); return reference.delete(); }) - .then((result) => { + .then(() => { return reference.get(); }) .then((snapshot) => { @@ -70,7 +70,7 @@ describe('admin.firestore', () => { const expected: any = clone(mountainView); expected.timestamp = admin.firestore.FieldValue.serverTimestamp(); return reference.set(expected) - .then((result) => { + .then(() => { return reference.get(); }) .then((snapshot) => { @@ -117,10 +117,10 @@ describe('admin.firestore', () => { const source = admin.firestore().collection('cities').doc(); const target = admin.firestore().collection('cities').doc(); return source.set(mountainView) - .then((result) => { + .then(() => { return target.set({name: 'Palo Alto', sisterCity: source}); }) - .then((result) => { + .then(() => { return target.get(); }) .then((snapshot) => { @@ -142,10 +142,10 @@ describe('admin.firestore', () => { logs.push(log); }); return source.set({name: 'San Francisco'}) - .then((result) => { + .then(() => { return source.delete(); }) - .then((result) => { + .then(() => { expect(logs.length).greaterThan(0); }); }); diff --git a/test/integration/machine-learning.spec.ts b/test/integration/machine-learning.spec.ts index 40f6f57205..9a358bf0e3 100644 --- a/test/integration/machine-learning.spec.ts +++ b/test/integration/machine-learning.spec.ts @@ -26,11 +26,11 @@ describe('admin.machineLearning', () => { const modelsToDelete: string[] = []; - function scheduleForDelete(model: admin.machineLearning.Model) { + function scheduleForDelete(model: admin.machineLearning.Model): void { modelsToDelete.push(model.modelId); } - function unscheduleForDelete(model: admin.machineLearning.Model) { + function unscheduleForDelete(model: admin.machineLearning.Model): void { modelsToDelete.splice(modelsToDelete.indexOf(model.modelId), 1); } @@ -43,8 +43,8 @@ describe('admin.machineLearning', () => { return Promise.all(promises); } - function createTemporaryModel(options?: admin.machineLearning.ModelOptions) - : Promise { + function createTemporaryModel(options?: admin.machineLearning.ModelOptions): + Promise { let modelOptions: admin.machineLearning.ModelOptions = { displayName: 'nodejs_integration_temp_model', }; @@ -111,10 +111,10 @@ describe('admin.machineLearning', () => { .then((fileName: string) => { modelOptions.tfliteModel!.gcsTfliteUri = fileName; return admin.machineLearning().createModel(modelOptions) - .then((model) => { - scheduleForDelete(model); - verifyModel(model, modelOptions); - }); + .then((model) => { + scheduleForDelete(model); + verifyModel(model, modelOptions); + }); }); }); @@ -152,8 +152,8 @@ describe('admin.machineLearning', () => { }; return createTemporaryModel({displayName: 'node-integration-invalid-argument'}) .then((model) => admin.machineLearning().updateModel(model.modelId, modelOptions) - .should.eventually.be.rejected.and.have.property( - 'code', 'machine-learning/invalid-argument')); + .should.eventually.be.rejected.and.have.property( + 'code', 'machine-learning/invalid-argument')); }); it('updates the displayName', () => { @@ -191,8 +191,8 @@ describe('admin.machineLearning', () => { it('updates the tflite file', () => { return Promise.all([ - createTemporaryModel(), - uploadModelToGcs('model1.tflite', 'valid_model.tflite')]) + createTemporaryModel(), + uploadModelToGcs('model1.tflite', 'valid_model.tflite')]) .then(([model, fileName]) => { const modelOptions: admin.machineLearning.ModelOptions = { tfliteModel: {gcsTfliteUri: fileName}, @@ -232,8 +232,8 @@ describe('admin.machineLearning', () => { it('rejects with invalid-argument when the ModelId is invalid', () => { return admin.machineLearning().publishModel('invalid-model-id') - .should.eventually.be.rejected.and.have.property( - 'code', 'machine-learning/invalid-argument'); + .should.eventually.be.rejected.and.have.property( + 'code', 'machine-learning/invalid-argument'); }); it('publishes the model successfully', () => { @@ -267,8 +267,8 @@ describe('admin.machineLearning', () => { it('rejects with invalid-argument when the ModelId is invalid', () => { return admin.machineLearning().unpublishModel('invalid-model-id') - .should.eventually.be.rejected.and.have.property( - 'code', 'machine-learning/invalid-argument'); + .should.eventually.be.rejected.and.have.property( + 'code', 'machine-learning/invalid-argument'); }); it('unpublishes the model successfully', () => { @@ -446,8 +446,8 @@ describe('admin.machineLearning', () => { it('rejects with invalid-argument when the Model ID is invalid', () => { return admin.machineLearning().deleteModel('invalid-model-id') - .should.eventually.be.rejected.and.have.property( - 'code', 'machine-learning/invalid-argument'); + .should.eventually.be.rejected.and.have.property( + 'code', 'machine-learning/invalid-argument'); }); it('deletes existing Model', () => { @@ -464,7 +464,7 @@ describe('admin.machineLearning', () => { }); }); - function verifyModel(model: admin.machineLearning.Model, expectedOptions: admin.machineLearning.ModelOptions) { + function verifyModel(model: admin.machineLearning.Model, expectedOptions: admin.machineLearning.ModelOptions): void { if (expectedOptions.displayName) { expect(model.displayName).to.equal(expectedOptions.displayName); } else { @@ -487,7 +487,7 @@ describe('admin.machineLearning', () => { } }); -function verifyTfliteModel(model: admin.machineLearning.Model, expectedGcsTfliteUri: string) { +function verifyTfliteModel(model: admin.machineLearning.Model, expectedGcsTfliteUri: string): void { expect(model.tfliteModel!.gcsTfliteUri).to.equal(expectedGcsTfliteUri); if (expectedGcsTfliteUri.endsWith('invalid_model.tflite')) { expect(model.modelHash).to.be.empty; diff --git a/test/integration/project-management.spec.ts b/test/integration/project-management.spec.ts index 4cf269c363..de26f4aaf2 100644 --- a/test/integration/project-management.spec.ts +++ b/test/integration/project-management.spec.ts @@ -42,10 +42,10 @@ describe('admin.projectManagement', () => { before(() => { const androidPromise = ensureAndroidApp() - .then((app) => { - androidApp = app; - return deleteAllShaCertificates(androidApp); - }); + .then((app) => { + androidApp = app; + return deleteAllShaCertificates(androidApp); + }); const iosPromise = ensureIosApp().then((app) => { iosApp = app; }); @@ -56,28 +56,28 @@ describe('admin.projectManagement', () => { describe('listAndroidApps()', () => { it('successfully lists Android apps', () => { return admin.projectManagement().listAndroidApps() - .then((apps) => Promise.all(apps.map((app) => app.getMetadata()))) - .then((metadatas) => { - expect(metadatas.length).to.be.at.least(1); - const metadataOwnedByTest = + .then((apps) => Promise.all(apps.map((app) => app.getMetadata()))) + .then((metadatas) => { + expect(metadatas.length).to.be.at.least(1); + const metadataOwnedByTest = metadatas.find((metadata) => isIntegrationTestApp(metadata.packageName)); - expect(metadataOwnedByTest).to.exist; - expect(metadataOwnedByTest!.appId).to.equal(androidApp.appId); - }); + expect(metadataOwnedByTest).to.exist; + expect(metadataOwnedByTest!.appId).to.equal(androidApp.appId); + }); }); }); describe('listIosApps()', () => { it('successfully lists iOS apps', () => { return admin.projectManagement().listIosApps() - .then((apps) => Promise.all(apps.map((app) => app.getMetadata()))) - .then((metadatas) => { - expect(metadatas.length).to.be.at.least(1); - const metadataOwnedByTest = + .then((apps) => Promise.all(apps.map((app) => app.getMetadata()))) + .then((metadatas) => { + expect(metadatas.length).to.be.at.least(1); + const metadataOwnedByTest = metadatas.find((metadata) => isIntegrationTestApp(metadata.bundleId)); - expect(metadataOwnedByTest).to.exist; - expect(metadataOwnedByTest!.appId).to.equal(iosApp.appId); - }); + expect(metadataOwnedByTest).to.exist; + expect(metadataOwnedByTest!.appId).to.equal(iosApp.appId); + }); }); }); @@ -95,11 +95,11 @@ describe('admin.projectManagement', () => { it('successfully lists metadata of all apps', () => { return admin.projectManagement().listAppMetadata() .then((metadatas) => { - expect(metadatas.length).to.be.at.least(2); - const testAppMetadatas = metadatas.filter((metadata) => - isIntegrationTestAppDisplayName(metadata.displayName) && + expect(metadatas.length).to.be.at.least(2); + const testAppMetadatas = metadatas.filter((metadata) => + isIntegrationTestAppDisplayName(metadata.displayName) && (metadata.appId === androidApp.appId || metadata.appId === iosApp.appId)); - expect(testAppMetadatas).to.have.length(2); + expect(testAppMetadatas).to.have.length(2); }); }); }); @@ -128,10 +128,10 @@ describe('admin.projectManagement', () => { it('successfully sets Android app\'s display name', () => { const newDisplayName = generateUniqueAppDisplayName(); return androidApp.setDisplayName(newDisplayName) - .then(() => androidApp.getMetadata()) - .then((appMetadata) => { - expect(appMetadata.displayName).to.equal(newDisplayName); - }); + .then(() => androidApp.getMetadata()) + .then((appMetadata) => { + expect(appMetadata.displayName).to.equal(newDisplayName); + }); }); }); @@ -139,10 +139,10 @@ describe('admin.projectManagement', () => { it('successfully sets iOS app\'s display name', () => { const newDisplayName = generateUniqueAppDisplayName(); return iosApp.setDisplayName(newDisplayName) - .then(() => iosApp.getMetadata()) - .then((appMetadata) => { - expect(appMetadata.displayName).to.equal(newDisplayName); - }); + .then(() => iosApp.getMetadata()) + .then((appMetadata) => { + expect(appMetadata.displayName).to.equal(newDisplayName); + }); }); }); @@ -155,38 +155,38 @@ describe('admin.projectManagement', () => { // 4. Delete the cert we just created. // 5. Check that this app has no certs. return androidApp.getShaCertificates() - .then((certs) => { - expect(certs.length).to.equal(0); - - const shaCertificate = admin.projectManagement().shaCertificate(SHA_256_HASH); - return androidApp.addShaCertificate(shaCertificate); - }) - .then(() => androidApp.getShaCertificates()) - .then((certs) => { - expect(certs.length).to.equal(1); - expect(certs[0].shaHash).to.equal(SHA_256_HASH); - expect(certs[0].certType).to.equal('sha256'); - expect(certs[0].resourceName).to.not.be.empty; - - return androidApp.deleteShaCertificate(certs[0]); - }) - .then(() => androidApp.getShaCertificates()) - .then((certs) => { - expect(certs.length).to.equal(0); - }); + .then((certs) => { + expect(certs.length).to.equal(0); + + const shaCertificate = admin.projectManagement().shaCertificate(SHA_256_HASH); + return androidApp.addShaCertificate(shaCertificate); + }) + .then(() => androidApp.getShaCertificates()) + .then((certs) => { + expect(certs.length).to.equal(1); + expect(certs[0].shaHash).to.equal(SHA_256_HASH); + expect(certs[0].certType).to.equal('sha256'); + expect(certs[0].resourceName).to.not.be.empty; + + return androidApp.deleteShaCertificate(certs[0]); + }) + .then(() => androidApp.getShaCertificates()) + .then((certs) => { + expect(certs.length).to.equal(0); + }); }); it('add a cert and then remove it fails due to missing resourceName', - () => { - const shaCertificate = + () => { + const shaCertificate = admin.projectManagement().shaCertificate(SHA_256_HASH); - return androidApp.addShaCertificate(shaCertificate) - .then(() => androidApp.deleteShaCertificate(shaCertificate)) - .should.eventually.be - .rejectedWith( - 'Specified certificate does not include a resourceName') - .with.property('code', 'project-management/invalid-argument'); - }); + return androidApp.addShaCertificate(shaCertificate) + .then(() => androidApp.deleteShaCertificate(shaCertificate)) + .should.eventually.be + .rejectedWith( + 'Specified certificate does not include a resourceName') + .with.property('code', 'project-management/invalid-argument'); + }); }); describe('androidApp.getConfig()', () => { @@ -215,18 +215,18 @@ describe('admin.projectManagement', () => { */ function ensureAndroidApp(): Promise { return admin.projectManagement().listAndroidApps() - .then((apps) => Promise.all(apps.map((app) => app.getMetadata()))) - .then((metadatas) => { - const metadataOwnedByTest = + .then((apps) => Promise.all(apps.map((app) => app.getMetadata()))) + .then((metadatas) => { + const metadataOwnedByTest = metadatas.find((metadata) => isIntegrationTestApp(metadata.packageName)); - if (metadataOwnedByTest) { - return admin.projectManagement().androidApp(metadataOwnedByTest.appId); - } + if (metadataOwnedByTest) { + return admin.projectManagement().androidApp(metadataOwnedByTest.appId); + } - // If no Android app owned by these integration tests was found, then create one. - return admin.projectManagement() - .createAndroidApp(generateUniqueAppNamespace(), generateUniqueAppDisplayName()); - }); + // If no Android app owned by these integration tests was found, then create one. + return admin.projectManagement() + .createAndroidApp(generateUniqueAppNamespace(), generateUniqueAppDisplayName()); + }); } /** @@ -236,18 +236,18 @@ function ensureAndroidApp(): Promise { */ function ensureIosApp(): Promise { return admin.projectManagement().listIosApps() - .then((apps) => Promise.all(apps.map((app) => app.getMetadata()))) - .then((metadatas) => { - const metadataOwnedByTest = + .then((apps) => Promise.all(apps.map((app) => app.getMetadata()))) + .then((metadatas) => { + const metadataOwnedByTest = metadatas.find((metadata) => isIntegrationTestApp(metadata.bundleId)); - if (metadataOwnedByTest) { - return admin.projectManagement().iosApp(metadataOwnedByTest.appId); - } + if (metadataOwnedByTest) { + return admin.projectManagement().iosApp(metadataOwnedByTest.appId); + } - // If no iOS app owned by these integration tests was found, then create one. - return admin.projectManagement() - .createIosApp(generateUniqueAppNamespace(), generateUniqueAppDisplayName()); - }); + // If no iOS app owned by these integration tests was found, then create one. + return admin.projectManagement() + .createIosApp(generateUniqueAppNamespace(), generateUniqueAppDisplayName()); + }); } /** @@ -255,30 +255,30 @@ function ensureIosApp(): Promise { */ function deleteAllShaCertificates(androidApp: admin.projectManagement.AndroidApp): Promise { return androidApp.getShaCertificates() - .then((shaCertificates: admin.projectManagement.ShaCertificate[]) => { - return Promise.all(shaCertificates.map((cert) => androidApp.deleteShaCertificate(cert))); - }) - .then(() => undefined); + .then((shaCertificates: admin.projectManagement.ShaCertificate[]) => { + return Promise.all(shaCertificates.map((cert) => androidApp.deleteShaCertificate(cert))); + }) + .then(() => undefined); } /** * @return {string} Dot-separated string that can be used as a unique package name or bundle ID. */ -function generateUniqueAppNamespace() { +function generateUniqueAppNamespace(): string { return APP_NAMESPACE_PREFIX + generateRandomString(APP_NAMESPACE_SUFFIX_LENGTH); } /** * @return {string} Dot-separated string that can be used as a unique app display name. */ -function generateUniqueAppDisplayName() { +function generateUniqueAppDisplayName(): string { return APP_DISPLAY_NAME_PREFIX + generateRandomString(APP_DISPLAY_NAME_SUFFIX_LENGTH); } /** * @return {string} string that can be used as a unique project display name. */ -function generateUniqueProjectDisplayName() { +function generateUniqueProjectDisplayName(): string { return PROJECT_DISPLAY_NAME_PREFIX + generateRandomString(PROJECT_DISPLAY_NAME_SUFFIX_LENGTH); } diff --git a/test/integration/security-rules.spec.ts b/test/integration/security-rules.spec.ts index ce86ed932e..648b74c787 100644 --- a/test/integration/security-rules.spec.ts +++ b/test/integration/security-rules.spec.ts @@ -47,11 +47,11 @@ describe('admin.securityRules', () => { const rulesetsToDelete: string[] = []; - function scheduleForDelete(ruleset: admin.securityRules.Ruleset) { + function scheduleForDelete(ruleset: admin.securityRules.Ruleset): void { rulesetsToDelete.push(ruleset.name); } - function unscheduleForDelete(ruleset: admin.securityRules.Ruleset) { + function unscheduleForDelete(ruleset: admin.securityRules.Ruleset): void { rulesetsToDelete.splice(rulesetsToDelete.indexOf(ruleset.name), 1); } @@ -296,7 +296,7 @@ describe('admin.securityRules', () => { }); }); - function verifyFirestoreRuleset(ruleset: admin.securityRules.Ruleset) { + function verifyFirestoreRuleset(ruleset: admin.securityRules.Ruleset): void { expect(ruleset.name).to.match(RULESET_NAME_PATTERN); const createTime = new Date(ruleset.createTime); expect(ruleset.createTime).equals(createTime.toUTCString()); diff --git a/test/integration/setup.ts b/test/integration/setup.ts index aee39257d3..a3558ae6b2 100644 --- a/test/integration/setup.ts +++ b/test/integration/setup.ts @@ -21,9 +21,8 @@ import path = require('path'); import {random} from 'lodash'; import { Credential, GoogleOAuthAccessToken } from '../../src/auth/credential'; -/* tslint:disable:no-var-requires */ +// eslint-disable-next-line @typescript-eslint/no-var-requires const chalk = require('chalk'); -/* tslint:enable:no-var-requires */ export let databaseUrl: string; export let storageBucket: string; @@ -126,7 +125,7 @@ class CertificatelessCredential implements Credential { * @param allowNumbers Whether to allow numbers in the generated string. The default is true. * @return A random string of the provided length. */ -export function generateRandomString(length: number, allowNumbers: boolean = true): string { +export function generateRandomString(length: number, allowNumbers = true): string { const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' + (allowNumbers ? '0123456789' : ''); let text = ''; diff --git a/test/integration/storage.spec.ts b/test/integration/storage.spec.ts index de6150f7a7..55e2414580 100644 --- a/test/integration/storage.spec.ts +++ b/test/integration/storage.spec.ts @@ -59,7 +59,7 @@ function verifyBucket(bucket: Bucket, testName: string): Promise { expect(data[0].toString()).to.equal(expected); return file.delete(); }) - .then((resp) => { + .then(() => { return file.exists(); }) .then((data) => { diff --git a/test/integration/typescript/package.json b/test/integration/typescript/package.json index 85395e6792..8f467c2a41 100644 --- a/test/integration/typescript/package.json +++ b/test/integration/typescript/package.json @@ -1,14 +1,19 @@ { "name": "firebase-admin-typescript-test", "version": "1.0.0", + "description": "Firebase Admin SDK post package test cases", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/firebase/firebase-admin-node" + }, "devDependencies": { - "@types/google-cloud__storage": "^1.1.1", "@types/chai": "^3.4.35", - "@types/mocha": "^2.2.39", - "@types/node": "^7.0.8", + "@types/mocha": "^2.2.48", + "@types/node": "^8.10.59", "chai": "^3.5.0", - "mocha": "^3.5.0", + "mocha": "^5.2.0", "ts-node": "^3.3.0", - "typescript": "^2.4.2" + "typescript": "^3.7.3" } -} \ No newline at end of file +} diff --git a/test/integration/typescript/src/example.test.ts b/test/integration/typescript/src/example.test.ts index d630936e4e..2b1159f696 100644 --- a/test/integration/typescript/src/example.test.ts +++ b/test/integration/typescript/src/example.test.ts @@ -14,63 +14,84 @@ * limitations under the License. */ -import initApp from './example' +import initApp from './example'; import {expect} from 'chai'; import {Bucket} from '@google-cloud/storage'; import {Firestore} from '@google-cloud/firestore'; import * as admin from 'firebase-admin'; +// eslint-disable-next-line @typescript-eslint/no-var-requires const serviceAccount = require('../mock.key.json'); describe('Init App', () => { - const app: admin.app.App = initApp(serviceAccount, 'TestApp'); - - after(() => { - return app.delete(); - }); - - it('Should return an initialized App', () => { - expect(app.name).to.equal('TestApp'); - }); - - it('Should return a Database client', () => { - const db = admin.database(app); - expect(db).to.be.instanceOf((admin.database as any).Database); - }); - - it('Should return a Database client for URL', () => { - const db = app.database('https://other-mock.firebaseio.com'); - expect(db).to.be.instanceOf((admin.database as any).Database); - }); - - it('Should return a Database ServerValue', () => { - const serverValue = admin.database.ServerValue; - expect(serverValue).to.not.be.null; - }); - - it('Should return a Cloud Storage client', () => { - const bucket: Bucket = app.storage().bucket('TestBucket'); - expect(bucket.name).to.equal('TestBucket') - }); - - it('Should return a Firestore client from the app', () => { - const firestore: Firestore = app.firestore(); - expect(firestore).to.be.instanceOf(admin.firestore.Firestore); - }); - - it('Should return a Firestore client', () => { - const firestore: Firestore = admin.firestore(app); - expect(firestore).to.be.instanceOf(admin.firestore.Firestore); - }); - - it('Should return a Firestore FieldValue', () => { - const fieldValue = admin.firestore.FieldValue; - expect(fieldValue).to.not.be.null; - }); - - it('Should return a DocumentReference', () => { - const ref: admin.firestore.DocumentReference = admin.firestore(app).collection('test').doc(); - expect(ref).to.not.be.null; - }); + const app: admin.app.App = initApp(serviceAccount, 'TestApp'); + + after(() => { + return app.delete(); + }); + + it('Should return an initialized App', () => { + expect(app.name).to.equal('TestApp'); + }); + + it('Should return an Auth client', () => { + const client = admin.auth(app); + expect(client).to.be.instanceOf((admin.auth as any).Auth); + }); + + it('Should return a Messaging client', () => { + const client = admin.messaging(app); + expect(client).to.be.instanceOf((admin.messaging as any).Messaging); + }); + + it('Should return a ProjectManagement client', () => { + const client = admin.projectManagement(app); + expect(client).to.be.instanceOf((admin.projectManagement as any).ProjectManagement); + }); + + it('Should return a SecurityRules client', () => { + const client = admin.securityRules(app); + expect(client).to.be.instanceOf((admin.securityRules as any).SecurityRules); + }); + + it('Should return a Database client', () => { + const db = admin.database(app); + expect(db).to.be.instanceOf((admin.database as any).Database); + }); + + it('Should return a Database client for URL', () => { + const db = app.database('https://other-mock.firebaseio.com'); + expect(db).to.be.instanceOf((admin.database as any).Database); + }); + + it('Should return a Database ServerValue', () => { + const serverValue = admin.database.ServerValue; + expect(serverValue).to.not.be.null; + }); + + it('Should return a Cloud Storage client', () => { + const bucket: Bucket = app.storage().bucket('TestBucket'); + expect(bucket.name).to.equal('TestBucket'); + }); + + it('Should return a Firestore client from the app', () => { + const firestore: Firestore = app.firestore(); + expect(firestore).to.be.instanceOf(admin.firestore.Firestore); + }); + + it('Should return a Firestore client', () => { + const firestore: Firestore = admin.firestore(app); + expect(firestore).to.be.instanceOf(admin.firestore.Firestore); + }); + + it('Should return a Firestore FieldValue', () => { + const fieldValue = admin.firestore.FieldValue; + expect(fieldValue).to.not.be.null; + }); + + it('Should return a DocumentReference', () => { + const ref: admin.firestore.DocumentReference = admin.firestore(app).collection('test').doc(); + expect(ref).to.not.be.null; + }); }); diff --git a/test/integration/typescript/src/example.ts b/test/integration/typescript/src/example.ts index 0de5332cc2..0b254517d1 100644 --- a/test/integration/typescript/src/example.ts +++ b/test/integration/typescript/src/example.ts @@ -16,19 +16,19 @@ import * as firebase from 'firebase-admin'; -export function initApp(serviceAcct: any, name: string) { - return firebase.initializeApp({ - credential: firebase.credential.cert(serviceAcct), - databaseURL: 'https://mock.firebaseio.com' - }, name); +export function initApp(serviceAcct: any, name: string): firebase.app.App { + return firebase.initializeApp({ + credential: firebase.credential.cert(serviceAcct), + databaseURL: 'https://mock.firebaseio.com' + }, name); } export function addValueEventListener( - // Check for type compilation - db: firebase.database.Database, - callback: (s: firebase.database.DataSnapshot) => any) { - let eventType: firebase.database.EventType = 'value'; - db.ref().on(eventType, callback); + // Check for type compilation + db: firebase.database.Database, + callback: (s: firebase.database.DataSnapshot) => any): void { + const eventType: firebase.database.EventType = 'value'; + db.ref().on(eventType, callback); } export default initApp; diff --git a/test/resources/mocks.ts b/test/resources/mocks.ts index bdc87cb3ab..7c73e8fe54 100644 --- a/test/resources/mocks.ts +++ b/test/resources/mocks.ts @@ -32,32 +32,32 @@ import {Credential, GoogleOAuthAccessToken, ServiceAccountCredential} from '../. const ALGORITHM = 'RS256'; const ONE_HOUR_IN_SECONDS = 60 * 60; -export let uid = 'someUid'; -export let projectId = 'project_id'; -export let developerClaims = { +export const uid = 'someUid'; +export const projectId = 'project_id'; +export const developerClaims = { one: 'uno', two: 'dos', }; -export let appName = 'mock-app-name'; +export const appName = 'mock-app-name'; -export let serviceName = 'mock-service-name'; +export const serviceName = 'mock-service-name'; -export let databaseURL = 'https://databaseName.firebaseio.com'; +export const databaseURL = 'https://databaseName.firebaseio.com'; -export let databaseAuthVariableOverride = { 'some#string': 'some#val' }; +export const databaseAuthVariableOverride = { 'some#string': 'some#val' }; -export let storageBucket = 'bucketName.appspot.com'; +export const storageBucket = 'bucketName.appspot.com'; -export let credential = new ServiceAccountCredential(path.resolve(__dirname, './mock.key.json')); +export const credential = new ServiceAccountCredential(path.resolve(__dirname, './mock.key.json')); -export let appOptions: FirebaseAppOptions = { +export const appOptions: FirebaseAppOptions = { credential, databaseURL, storageBucket, }; -export let appOptionsWithOverride: FirebaseAppOptions = { +export const appOptionsWithOverride: FirebaseAppOptions = { credential, databaseAuthVariableOverride, databaseURL, @@ -65,15 +65,15 @@ export let appOptionsWithOverride: FirebaseAppOptions = { projectId, }; -export let appOptionsNoAuth: FirebaseAppOptions = { +export const appOptionsNoAuth: FirebaseAppOptions = { databaseURL, }; -export let appOptionsNoDatabaseUrl: FirebaseAppOptions = { +export const appOptionsNoDatabaseUrl: FirebaseAppOptions = { credential, }; -export let appOptionsAuthDB: FirebaseAppOptions = { +export const appOptionsAuthDB: FirebaseAppOptions = { credential, databaseURL, }; @@ -81,8 +81,8 @@ export let appOptionsAuthDB: FirebaseAppOptions = { export class MockCredential implements Credential { public getAccessToken(): Promise { return Promise.resolve({ - access_token: 'mock-token', - expires_in: 3600, + access_token: 'mock-token', // eslint-disable-line @typescript-eslint/camelcase + expires_in: 3600, // eslint-disable-line @typescript-eslint/camelcase }); } } @@ -137,19 +137,18 @@ export function appRejectedWhileFetchingAccessToken(): FirebaseApp { }, appName, new FirebaseNamespace().INTERNAL); } -export let refreshToken = { +export const refreshToken = { clientId: 'mock-client-id', clientSecret: 'mock-client-secret', refreshToken: 'mock-refresh-token', type: 'refreshToken', }; -/* tslint:disable:no-var-requires */ -export let certificateObject = require('./mock.key.json'); -/* tslint:enable:no-var-requires */ +// eslint-disable-next-line @typescript-eslint/no-var-requires +export const certificateObject = require('./mock.key.json'); // Randomly generated key pairs that don't correspond to anything related to Firebase or GCP -export let keyPairs = [ +export const keyPairs = [ /* tslint:disable:max-line-length */ // The private key for this key pair is identical to the one used in ./mock.key.json { @@ -222,7 +221,7 @@ export function generateSessionCookie(overrides?: object, expiresIn?: number): s export function firebaseServiceFactory( firebaseApp: FirebaseApp, - extendApp?: (props: object) => void, + extendApp?: (props: object) => void, // eslint-disable-line @typescript-eslint/no-unused-vars ): FirebaseServiceInterface { const result = { app: firebaseApp, @@ -233,7 +232,7 @@ export function firebaseServiceFactory( /** Mock socket emitter class. */ export class MockSocketEmitter extends events.EventEmitter { - public setTimeout: (_: number) => void = (timeout: number) => undefined; + public setTimeout: (_: number) => void = () => undefined; } /** Mock stream passthrough class with dummy abort method. */ diff --git a/test/unit/auth/action-code-settings-builder.spec.ts b/test/unit/auth/action-code-settings-builder.spec.ts index 5f799d5a86..78eace7a04 100644 --- a/test/unit/auth/action-code-settings-builder.spec.ts +++ b/test/unit/auth/action-code-settings-builder.spec.ts @@ -164,8 +164,8 @@ describe('ActionCodeSettingsBuilder', () => { url: 'https://www.example.com/path/file?a=1&b=2', handleCodeInApp: true, android: {}, - } as any); - }).to.throw(AuthClientErrorCode.MISSING_ANDROID_PACKAGE_NAME.message); + } as any); + }).to.throw(AuthClientErrorCode.MISSING_ANDROID_PACKAGE_NAME.message); }); const invalidPackageNames = [null, NaN, 0, 1, true, false, '', ['com.example.android'], _.noop]; diff --git a/test/unit/auth/auth-api-request.spec.ts b/test/unit/auth/auth-api-request.spec.ts index be4aa83a6c..5c44ad6185 100755 --- a/test/unit/auth/auth-api-request.spec.ts +++ b/test/unit/auth/auth-api-request.spec.ts @@ -44,7 +44,7 @@ import { SAMLUpdateAuthProviderRequest, SAMLConfigServerResponse, } from '../../../src/auth/auth-config'; import {TenantOptions} from '../../../src/auth/tenant'; -import { UpdateRequest } from '../../../src/auth/user-record'; +import { UpdateRequest, UpdateMultiFactorInfoRequest } from '../../../src/auth/user-record'; chai.should(); chai.use(sinonChai); @@ -62,6 +62,12 @@ interface HandlerTest { path(version: string, api: string, projectId: string): string; } +interface InvalidMultiFactorUpdateTest { + name: string; + error: FirebaseAuthError; + secondFactor: any; +} + /** * @param {number} numOfChars The number of random characters within the string. @@ -547,7 +553,7 @@ describe('FIREBASE_AUTH_SET_ACCOUNT_INFO', () => { expect(() => { const claims = { sub: 'sub', - auth_time: 'time', + auth_time: 'time', // eslint-disable-line @typescript-eslint/camelcase }; return requestValidator({localId: '1234', customAttributes: JSON.stringify(claims)}); }).to.throw(`Developer claims "auth_time", "sub" are reserved and cannot be specified.`); @@ -864,7 +870,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.createSessionCookie('', durationInMs) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -877,7 +883,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.createSessionCookie('ID_TOKEN', 'invalid' as any) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -891,7 +897,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.createSessionCookie('ID_TOKEN', outOfBoundDuration) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -905,7 +911,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.createSessionCookie('ID_TOKEN', outOfBoundDuration) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -924,7 +930,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.createSessionCookie('invalid-token', durationInMs) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -970,7 +976,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.getAccountInfoByEmail('user@example.com') - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -1010,7 +1016,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.getAccountInfoByUid('uid') - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -1031,7 +1037,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.getAccountInfoByUid('uid') - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -1081,7 +1087,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { stubs.push(stub); const requestHandler = handler.init(mockApp); return requestHandler.getAccountInfoByPhoneNumber('invalid') - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -1103,7 +1109,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.getAccountInfoByPhoneNumber('+11234567890') - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -1139,6 +1145,23 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { providerId: 'google.com', }, ], + multiFactor: { + enrolledFactors: [ + { + uid: 'mfaUid1', + phoneNumber: '+16505550001', + displayName: 'Corp phone number', + factorId: 'phone', + enrollmentTime: new Date().toUTCString(), + }, + { + uid: 'mfaUid2', + phoneNumber: '+16505550002', + displayName: 'Personal phone number', + factorId: 'phone', + }, + ], + }, customClaims: {admin: true}, // Tenant ID accepted on user batch upload. tenantId, @@ -1344,6 +1367,80 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }, {uid: 'user16', providerData: [{}]}, {email: 'user17@example.com'}, + { + uid: 'user18', + email: 'user18@example.com', + multiFactor: { + enrolledFactors: [ + { + // Invalid mfa enrollment ID. + uid: '', + factorId: 'phone', + phoneNumber: '+16505550001', + }, + ], + }, + }, + { + uid: 'user19', + email: 'user19@example.com', + multiFactor: { + enrolledFactors: [ + { + uid: 'mfaUid1', + factorId: 'phone', + // Invalid display name. + displayName: false, + phoneNumber: '+16505550002', + }, + ], + }, + }, + { + uid: 'user20', + email: 'user20@example.com', + multiFactor: { + enrolledFactors: [ + { + uid: 'mfaUid2', + factorId: 'phone', + // Invalid enrollment time. + enrollmentTime: 'invalid', + phoneNumber: '+16505550003', + }, + ], + }, + }, + { + uid: 'user21', + email: 'user21@example.com', + multiFactor: { + enrolledFactors: [ + { + uid: 'mfaUid3', + factorId: 'phone', + // Invalid phone number + phoneNumber: 'invalid', + enrollmentTime: new Date().toUTCString(), + }, + ], + }, + }, + { + uid: 'user22', + email: 'user22@example.com', + multiFactor: { + enrolledFactors: [ + { + uid: 'mfaUid3', + // Invalid factor ID. + factorId: 'invalid', + phoneNumber: '+16505550003', + enrollmentTime: new Date().toUTCString(), + }, + ], + }, + }, ] as any; const validOptions = { hash: { @@ -1402,6 +1499,42 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }, {index: 16, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)}, {index: 17, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_UID)}, + { + index: 18, + error: new FirebaseAuthError( + AuthClientErrorCode.INVALID_UID, + `The second factor "uid" must be a valid non-empty string.`, + ), + }, + { + index: 19, + error: new FirebaseAuthError( + AuthClientErrorCode.INVALID_DISPLAY_NAME, + `The second factor "displayName" for "mfaUid1" must be a valid string.`, + ), + }, + { + index: 20, + error: new FirebaseAuthError( + AuthClientErrorCode.INVALID_ENROLLMENT_TIME, + `The second factor "enrollmentTime" for "mfaUid2" must be a valid UTC date string.`, + ), + }, + { + index: 21, + error: new FirebaseAuthError( + AuthClientErrorCode.INVALID_PHONE_NUMBER, + `The second factor "phoneNumber" for "mfaUid3" must be a non-empty ` + + `E.164 standard compliant identifier string.`, + ), + }, + { + index: 22, + error: new FirebaseAuthError( + AuthClientErrorCode.UNSUPPORTED_SECOND_FACTOR, + `Unsupported second factor "${JSON.stringify(testUsers[22].multiFactor.enrolledFactors[0])}" provided.`, + ), + }, ], }; const stub = sinon.stub(HttpClient.prototype, 'send'); @@ -1432,7 +1565,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); const userImportBuilder = new UserImportBuilder(users, options); return requestHandler.uploadAccount(users, options) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -1510,7 +1643,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.downloadAccount(1001, nextPageToken) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -1523,7 +1656,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.downloadAccount(maxResults, '') - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -1545,7 +1678,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.downloadAccount(maxResults, nextPageToken) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -1585,7 +1718,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { stubs.push(stub); const requestHandler = handler.init(mockApp); return requestHandler.deleteAccount('uid') - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -1595,6 +1728,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); describe('updateExistingAccount', () => { + const now = new Date('2019-10-25T04:30:52.000Z'); const path = handler.path('v1', '/accounts:update', 'project_id'); const method = 'POST'; const uid = '12345678'; @@ -1606,6 +1740,22 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { photoURL: 'http://localhost/1234/photo.png', password: 'password', phoneNumber: '+11234567890', + multiFactor: { + enrolledFactors: [ + { + uid: 'enrolledSecondFactor1', + phoneNumber: '+16505557348', + displayName: 'Spouse\'s phone number', + factorId: 'phone', + enrollmentTime: now.toUTCString(), + } as UpdateMultiFactorInfoRequest, + { + uid: 'enrolledSecondFactor2', + phoneNumber: '+16505551000', + factorId: 'phone', + } as UpdateMultiFactorInfoRequest, + ], + }, }; (validData as any).ignoredProperty = 'value'; const expectedValidData = { @@ -1617,11 +1767,26 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { photoUrl: 'http://localhost/1234/photo.png', password: 'password', phoneNumber: '+11234567890', + mfa: { + enrollments: [ + { + mfaEnrollmentId: 'enrolledSecondFactor1', + phoneInfo: '+16505557348', + displayName: 'Spouse\'s phone number', + enrolledAt: now.toISOString(), + }, + { + mfaEnrollmentId: 'enrolledSecondFactor2', + phoneInfo: '+16505551000', + }, + ], + }, }; // Valid request to delete photoURL and displayName. const validDeleteData = deepCopy(validData); validDeleteData.displayName = null; validDeleteData.photoURL = null; + delete validDeleteData.multiFactor; const expectedValidDeleteData = { localId: uid, email: 'user@example.com', @@ -1634,6 +1799,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { // Valid request to delete phoneNumber. const validDeletePhoneNumberData = deepCopy(validData); validDeletePhoneNumberData.phoneNumber = null; + delete validDeletePhoneNumberData.multiFactor; const expectedValidDeletePhoneNumberData = { localId: uid, displayName: 'John Doe', @@ -1644,6 +1810,11 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { password: 'password', deleteProvider: ['phone'], }; + // Valid request to delete all second factors. + const expectedValidDeleteMfaData = { + localId: uid, + mfa: {}, + }; const invalidData = { uid, email: 'user@invalid@', @@ -1740,13 +1911,57 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); }); + it('should be fulfilled given null enrolled factors', () => { + // Successful result server response. + const expectedResult = utils.responseFrom({ + localId: uid, + }); + + const stub = sinon.stub(HttpClient.prototype, 'send').resolves(expectedResult); + stubs.push(stub); + + const requestHandler = handler.init(mockApp); + // Send update request to delete enrolled factors. + return requestHandler.updateExistingAccount(uid, {multiFactor: {enrolledFactors: null}}) + .then((returnedUid: string) => { + // uid should be returned. + expect(returnedUid).to.be.equal(uid); + // Confirm expected rpc request parameters sent. In this case, mfa is set to + // an empty object. + expect(stub).to.have.been.calledOnce.and.calledWith( + callParams(path, method, expectedValidDeleteMfaData)); + }); + }); + + it('should be fulfilled given empty enrolled factors array', () => { + // Successful result server response. + const expectedResult = utils.responseFrom({ + localId: uid, + }); + + const stub = sinon.stub(HttpClient.prototype, 'send').resolves(expectedResult); + stubs.push(stub); + + const requestHandler = handler.init(mockApp); + // Send update request to delete enrolled factors. + return requestHandler.updateExistingAccount(uid, {multiFactor: {enrolledFactors: []}}) + .then((returnedUid: string) => { + // uid should be returned. + expect(returnedUid).to.be.equal(uid); + // Confirm expected rpc request parameters sent. In this case, mfa is set to + // an empty object. + expect(stub).to.have.been.calledOnce.and.calledWith( + callParams(path, method, expectedValidDeleteMfaData)); + }); + }); + it('should be rejected given invalid parameters such as email', () => { // Expected error when an invalid email is provided. const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_EMAIL); const requestHandler = handler.init(mockApp); // Send update request with invalid email. return requestHandler.updateExistingAccount(uid, invalidData) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Invalid email error should be thrown. @@ -1754,6 +1969,92 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); }); + const unsupportedSecondFactor = { + uid: 'enrolledSecondFactor1', + secret: 'SECRET', + displayName: 'Google Authenticator on personal phone', + factorId: 'totp', + }; + const invalidSecondFactorTests: InvalidMultiFactorUpdateTest[] = [ + { + name: 'invalid second factor uid', + error: new FirebaseAuthError( + AuthClientErrorCode.INVALID_UID, + `The second factor "uid" must be a valid non-empty string.`, + ), + secondFactor: { + uid: ['enrollmentId'], + phoneNumber: '+16505557348', + displayName: 'Spouse\'s phone number', + factorId: 'phone', + }, + }, + { + name: 'invalid second factor display name', + error: new FirebaseAuthError( + AuthClientErrorCode.INVALID_DISPLAY_NAME, + `The second factor "displayName" for "enrolledSecondFactor1" must be a valid string.`, + ), + secondFactor: { + uid: 'enrolledSecondFactor1', + phoneNumber: '+16505557348', + displayName: ['Corp phone number'], + factorId: 'phone', + }, + }, + { + name: 'invalid second factor phone number', + error: new FirebaseAuthError( + AuthClientErrorCode.INVALID_PHONE_NUMBER, + `The second factor "phoneNumber" for "enrolledSecondFactor1" must be a non-empty ` + + `E.164 standard compliant identifier string.`), + secondFactor: { + uid: 'enrolledSecondFactor1', + phoneNumber: 'invalid', + displayName: 'Spouse\'s phone number', + factorId: 'phone', + }, + }, + { + name: 'invalid second factor enrollment time', + error: new FirebaseAuthError( + AuthClientErrorCode.INVALID_ENROLLMENT_TIME, + `The second factor "enrollmentTime" for "enrolledSecondFactor1" must be a valid ` + + `UTC date string.`), + secondFactor: { + uid: 'enrolledSecondFactor1', + phoneNumber: '+16505557348', + displayName: 'Spouse\'s phone number', + factorId: 'phone', + enrollmentTime: 'invalid', + }, + }, + { + name: 'invalid second factor type', + error: new FirebaseAuthError( + AuthClientErrorCode.UNSUPPORTED_SECOND_FACTOR, + `Unsupported second factor "${JSON.stringify(unsupportedSecondFactor)}" provided.`), + secondFactor: unsupportedSecondFactor, + }, + ]; + invalidSecondFactorTests.forEach((invalidSecondFactorTest) => { + it(`should be rejected given an ${invalidSecondFactorTest.name}`, () => { + const invalidSecondFactorData = { + multiFactor: { + enrolledFactors: [invalidSecondFactorTest.secondFactor], + }, + }; + const requestHandler = handler.init(mockApp); + return requestHandler.updateExistingAccount(uid, invalidSecondFactorData as any) + .then(() => { + throw new Error('Unexpected success'); + }, (error) => { + // Expected error should be thrown. + expect(error).to.deep.equal(invalidSecondFactorTest.error); + }); + }); + }); + it('should be rejected given a tenant ID to modify', () => { const dataWithModifiedTenantId = deepCopy(validData); (dataWithModifiedTenantId as any).tenantId = 'MODIFIED-TENANT-ID'; @@ -1765,7 +2066,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); // Send update request with tenant ID. return requestHandler.updateExistingAccount(uid, dataWithModifiedTenantId) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Invalid argument error should be thrown. @@ -1779,7 +2080,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); // Send update request with invalid phone number. return requestHandler.updateExistingAccount(uid, invalidPhoneNumberData) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Invalid phone number error should be thrown. @@ -1801,7 +2102,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.updateExistingAccount(uid, validData) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -1868,7 +2169,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); // Send request with invalid uid. return requestHandler.setCustomUserClaims('', claims) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Invalid uid error should be thrown. @@ -1885,7 +2186,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); // Send request with invalid claims. return requestHandler.setCustomUserClaims(uid, 'invalid' as any) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Invalid argument error should be thrown. @@ -1903,7 +2204,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const blacklistedClaims = {admin: true, aud: 'bla'}; // Send request with blacklisted claims. return requestHandler.setCustomUserClaims(uid, blacklistedClaims) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Forbidden claims error should be thrown. @@ -1924,7 +2225,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.setCustomUserClaims(uid, claims) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -1955,8 +2256,8 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be fulfilled given a valid uid', () => { const requestData = { localId: uid, - // Current time should be passed, rounded up. - validSince: Math.ceil((now.getTime() + 5000) / 1000), + // Current time should be passed, rounded down. + validSince: Math.floor((now.getTime() + 5000) / 1000), }; const stub = sinon.stub(HttpClient.prototype, 'send').resolves(expectedResult); stubs.push(stub); @@ -1977,7 +2278,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.revokeRefreshTokens(invalidUid as any) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Invalid uid error should be thrown. @@ -1995,7 +2296,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); const requestData = { localId: uid, - validSince: Math.ceil((now.getTime() + 5000) / 1000), + validSince: Math.floor((now.getTime() + 5000) / 1000), }; const stub = sinon.stub(HttpClient.prototype, 'send').rejects(expectedServerError); stubs.push(stub); @@ -2004,7 +2305,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { // Simulate 5 seconds passed. clock.tick(5000); return requestHandler.revokeRefreshTokens(uid) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -2028,6 +2329,19 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { password: 'password', phoneNumber: '+11234567890', ignoredProperty: 'value', + multiFactor: { + enrolledFactors: [ + { + phoneNumber: '+16505557348', + displayName: 'Spouse\'s phone number', + factorId: 'phone', + }, + { + phoneNumber: '+16505551000', + factorId: 'phone', + }, + ], + }, }; const expectedValidData = { localId: uid, @@ -2038,6 +2352,15 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { photoUrl: 'http://localhost/1234/photo.png', password: 'password', phoneNumber: '+11234567890', + mfaInfo: [ + { + phoneInfo: '+16505557348', + displayName: 'Spouse\'s phone number', + }, + { + phoneInfo: '+16505551000', + }, + ], }; const invalidData = { uid, @@ -2096,7 +2419,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); // Create new account with invalid email. return requestHandler.createNewAccount(invalidData) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Expected invalid email error should be thrown. @@ -2104,6 +2427,118 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); }); + const noEnrolledFactors: any[] = [[], null]; + noEnrolledFactors.forEach((arg) => { + it(`should be fulfilled given "${JSON.stringify(arg)}" enrolled factors`, () => { + // Successful result server response. + const expectedResult = utils.responseFrom({ + localId: uid, + }); + + const stub = sinon.stub(HttpClient.prototype, 'send').resolves(expectedResult); + stubs.push(stub); + + const requestHandler = handler.init(mockApp); + // Send create new account request with no enrolled factors. + const request: any = {uid, multiFactor: {enrolledFactors: null}}; + return requestHandler.createNewAccount(request) + .then((returnedUid: string) => { + // uid should be returned. + expect(returnedUid).to.be.equal(uid); + // Confirm expected rpc request parameters sent. In this case, no mfa info should + // be sent. + expect(stub).to.have.been.calledOnce.and.calledWith( + callParams(path, method, emptyRequest)); + }); + }); + }); + + const unsupportedSecondFactor = { + secret: 'SECRET', + displayName: 'Google Authenticator on personal phone', + // TOTP is not yet supported. + factorId: 'totp', + }; + const invalidSecondFactorTests: InvalidMultiFactorUpdateTest[] = [ + { + name: 'unsupported second factor uid', + error: new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + '"uid" is not supported when adding second factors via "createUser()"', + ), + secondFactor: { + uid: 'enrollmentId', + phoneNumber: '+16505557348', + displayName: 'Spouse\'s phone number', + factorId: 'phone', + }, + }, + { + name: 'invalid second factor display name', + error: new FirebaseAuthError( + AuthClientErrorCode.INVALID_DISPLAY_NAME, + `The second factor "displayName" for "+16505557348" must be a valid string.`, + ), + secondFactor: { + phoneNumber: '+16505557348', + displayName: ['Corp phone number'], + factorId: 'phone', + }, + }, + { + name: 'invalid second factor phone number', + error: new FirebaseAuthError( + AuthClientErrorCode.INVALID_PHONE_NUMBER, + `The second factor "phoneNumber" for "invalid" must be a non-empty ` + + `E.164 standard compliant identifier string.`), + secondFactor: { + phoneNumber: 'invalid', + displayName: 'Spouse\'s phone number', + factorId: 'phone', + }, + }, + { + name: 'unsupported second factor enrollment time', + error: new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + '"enrollmentTime" is not supported when adding second factors via "createUser()"'), + secondFactor: { + phoneNumber: '+16505557348', + displayName: 'Spouse\'s phone number', + factorId: 'phone', + enrollmentTime: new Date().toUTCString(), + }, + }, + { + name: 'invalid second factor type', + error: new FirebaseAuthError( + AuthClientErrorCode.UNSUPPORTED_SECOND_FACTOR, + `Unsupported second factor "${JSON.stringify(unsupportedSecondFactor)}" provided.`), + secondFactor: unsupportedSecondFactor, + }, + ]; + invalidSecondFactorTests.forEach((invalidSecondFactorTest) => { + it(`should be rejected given an ${invalidSecondFactorTest.name}`, () => { + const invalidSecondFactorData = { + uid, + email: 'user@example.com', + emailVerified: true, + password: 'secretpassword', + multiFactor: { + enrolledFactors: [invalidSecondFactorTest.secondFactor], + }, + }; + const requestHandler = handler.init(mockApp); + return requestHandler.createNewAccount(invalidSecondFactorData as any) + .then(() => { + throw new Error('Unexpected success'); + }, (error) => { + // Expected error should be thrown. + expect(error).to.deep.equal(invalidSecondFactorTest.error); + }); + }); + }); + it('should be rejected given tenantId in CreateRequest', () => { // Expected error when a tenantId is provided. const expectedError = new FirebaseAuthError( @@ -2115,7 +2550,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); // Create new account with tenantId. return requestHandler.createNewAccount(validDataWithTenantId) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Expected invalid argument error should be thrown. @@ -2129,7 +2564,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); // Create new account with invalid phone number. return requestHandler.createNewAccount(invalidPhoneNumberData) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Expected invalid phone number error should be thrown. @@ -2152,7 +2587,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { // Send create new account request and simulate a backend error that the user // already exists. return requestHandler.createNewAccount(validData) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -2176,7 +2611,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { // Send create new account request and simulate a backend error that the email // already exists. return requestHandler.createNewAccount(validData) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -2199,7 +2634,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); // Send create new account request with valid data but simulate backend error. return requestHandler.createNewAccount(validData) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -2268,7 +2703,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); // Send create new account request with invalid data. return requestHandler.createNewAccount(invalidData) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -2282,7 +2717,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); // Send create new account request with invalid data. return requestHandler.createNewAccount(invalidPhoneNumberData) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -2303,7 +2738,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); // Send valid create new account request and simulate backend error. return requestHandler.createNewAccount(validData) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -2412,7 +2847,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.getEmailActionLink('PASSWORD_RESET', invalidEmail, actionCodeSettings) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Invalid email error should be thrown. @@ -2429,7 +2864,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.getEmailActionLink(invalidRequestType, email, actionCodeSettings) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Invalid argument error should be thrown. @@ -2446,7 +2881,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.getEmailActionLink('EMAIL_SIGNIN', email, invalidActionCodeSettings) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Invalid argument error should be thrown. @@ -2456,8 +2891,8 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected when the response does not contain a link', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, - 'INTERNAL ASSERT FAILED: Unable to create the email action link'); + AuthClientErrorCode.INTERNAL_ERROR, + 'INTERNAL ASSERT FAILED: Unable to create the email action link'); const requestData = deepExtend({ requestType: 'VERIFY_EMAIL', email, @@ -2470,7 +2905,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.getEmailActionLink('VERIFY_EMAIL', email, actionCodeSettings) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -2496,7 +2931,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.getEmailActionLink('VERIFY_EMAIL', email, actionCodeSettings) - .then((returnedUid: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -2522,19 +2957,19 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { .then((result) => { expect(result).to.deep.equal(expectedResult.data); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(path, expectedHttpMethod, {})); + callParams(path, expectedHttpMethod, {})); }); }); const invalidProviderIds = [ - null, NaN, 0, 1, true, false, '', 'saml.provider', ['oidc.provider'], [], {}, { a: 1 }, _.noop]; + null, NaN, 0, 1, true, false, '', 'saml.provider', ['oidc.provider'], [], {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((invalidProviderId) => { it('should be rejected given an invalid provider ID:' + JSON.stringify(invalidProviderId), () => { const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID); const requestHandler = handler.init(mockApp); return requestHandler.getOAuthIdpConfig(invalidProviderId as any) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -2554,12 +2989,12 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.getOAuthIdpConfig(providerId) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(path, expectedHttpMethod, {})); + callParams(path, expectedHttpMethod, {})); }); }); }); @@ -2590,7 +3025,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { .then((result) => { expect(result).to.deep.equal(expectedResult.data); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(path, expectedHttpMethod, data)); + callParams(path, expectedHttpMethod, data)); }); }); @@ -2607,7 +3042,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { .then((result) => { expect(result).to.deep.equal({oauthIdpConfigs: []}); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(path, expectedHttpMethod, data)); + callParams(path, expectedHttpMethod, data)); }); }); @@ -2624,7 +3059,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { .then((result) => { expect(result).to.deep.equal(expectedResult.data); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(path, expectedHttpMethod, data)); + callParams(path, expectedHttpMethod, data)); }); }); @@ -2637,7 +3072,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.listOAuthIdpConfigs(101, nextPageToken) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -2651,7 +3086,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.listOAuthIdpConfigs(maxResults, '') - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -2674,12 +3109,12 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.listOAuthIdpConfigs(maxResults, nextPageToken) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(path, expectedHttpMethod, data)); + callParams(path, expectedHttpMethod, data)); }); }); }); @@ -2699,19 +3134,19 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { .then((result) => { expect(result).to.be.undefined; expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(path, expectedHttpMethod, {})); + callParams(path, expectedHttpMethod, {})); }); }); const invalidProviderIds = [ - null, NaN, 0, 1, true, false, '', 'saml.provider', ['oidc.provider'], [], {}, { a: 1 }, _.noop]; + null, NaN, 0, 1, true, false, '', 'saml.provider', ['oidc.provider'], [], {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((invalidProviderId) => { it('should be rejected given an invalid provider ID:' + JSON.stringify(invalidProviderId), () => { const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID); const requestHandler = handler.init(mockApp); return requestHandler.deleteOAuthIdpConfig(invalidProviderId as any) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -2731,12 +3166,12 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.deleteOAuthIdpConfig(providerId) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(path, expectedHttpMethod, {})); + callParams(path, expectedHttpMethod, {})); }); }); }); @@ -2771,7 +3206,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { .then((response) => { expect(response).to.deep.equal(expectedResult.data); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(path, expectedHttpMethod, expectedRequest)); + callParams(path, expectedHttpMethod, expectedRequest)); }); }); @@ -2785,7 +3220,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.createOAuthIdpConfig(invalidOptions) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -2802,12 +3237,12 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.createOAuthIdpConfig(configOptions) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(path, expectedHttpMethod, expectedRequest)); + callParams(path, expectedHttpMethod, expectedRequest)); }); }); @@ -2823,12 +3258,12 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.createOAuthIdpConfig(configOptions) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(path, expectedHttpMethod, expectedRequest)); + callParams(path, expectedHttpMethod, expectedRequest)); }); }); }); @@ -2871,7 +3306,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { .then((response) => { expect(response).to.deep.equal(expectedResult.data); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(expectedPath, expectedHttpMethod, expectedRequest)); + callParams(expectedPath, expectedHttpMethod, expectedRequest)); }); }); @@ -2895,7 +3330,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { .then((response) => { expect(response).to.deep.equal(expectedPartialResult.data); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(expectedPath, expectedHttpMethod, partialRequest)); + callParams(expectedPath, expectedHttpMethod, partialRequest)); }); }); @@ -2918,19 +3353,19 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { .then((response) => { expect(response).to.deep.equal(expectedPartialResult.data); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(expectedPath, expectedHttpMethod, partialRequest)); + callParams(expectedPath, expectedHttpMethod, partialRequest)); }); }); const invalidProviderIds = [ - null, NaN, 0, 1, true, false, '', 'saml.provider', ['oidc.provider'], [], {}, { a: 1 }, _.noop]; + null, NaN, 0, 1, true, false, '', 'saml.provider', ['oidc.provider'], [], {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((invalidProviderId) => { it('should be rejected given an invalid provider ID:' + JSON.stringify(invalidProviderId), () => { const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID); const requestHandler = handler.init(mockApp); return requestHandler.updateOAuthIdpConfig(invalidProviderId as any, configOptions) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -2948,7 +3383,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.updateOAuthIdpConfig(providerId, invalidOptions) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -2966,12 +3401,12 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.updateOAuthIdpConfig(providerId, configOptions) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(expectedPath, expectedHttpMethod, expectedRequest)); + callParams(expectedPath, expectedHttpMethod, expectedRequest)); }); }); @@ -2988,12 +3423,12 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.updateOAuthIdpConfig(providerId, configOptions) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(expectedPath, expectedHttpMethod, expectedRequest)); + callParams(expectedPath, expectedHttpMethod, expectedRequest)); }); }); }); @@ -3020,14 +3455,14 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); const invalidProviderIds = [ - null, NaN, 0, 1, true, false, '', 'oidc.provider', ['saml.provider'], [], {}, { a: 1 }, _.noop]; + null, NaN, 0, 1, true, false, '', 'oidc.provider', ['saml.provider'], [], {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((invalidProviderId) => { it('should be rejected given an invalid provider ID:' + JSON.stringify(invalidProviderId), () => { const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID); const requestHandler = handler.init(mockApp); return requestHandler.getInboundSamlConfig(invalidProviderId as any) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3047,7 +3482,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.getInboundSamlConfig(providerId) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3126,7 +3561,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.listInboundSamlConfigs(101, nextPageToken) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3140,7 +3575,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.listInboundSamlConfigs(maxResults, '') - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3163,7 +3598,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.listInboundSamlConfigs(maxResults, nextPageToken) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3191,14 +3626,14 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); const invalidProviderIds = [ - null, NaN, 0, 1, true, false, '', 'oidc.provider', ['saml.provider'], [], {}, { a: 1 }, _.noop]; + null, NaN, 0, 1, true, false, '', 'oidc.provider', ['saml.provider'], [], {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((invalidProviderId) => { it('should be rejected given an invalid provider ID:' + JSON.stringify(invalidProviderId), () => { const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID); const requestHandler = handler.init(mockApp); return requestHandler.deleteInboundSamlConfig(invalidProviderId as any) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3218,7 +3653,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.deleteInboundSamlConfig(providerId) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3272,7 +3707,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { .then((response) => { expect(response).to.deep.equal(expectedResult.data); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(path, expectedHttpMethod, expectedRequest)); + callParams(path, expectedHttpMethod, expectedRequest)); }); }); @@ -3286,7 +3721,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.createInboundSamlConfig(invalidOptions) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3303,12 +3738,12 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.createInboundSamlConfig(configOptions) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(path, expectedHttpMethod, expectedRequest)); + callParams(path, expectedHttpMethod, expectedRequest)); }); }); @@ -3324,12 +3759,12 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.createInboundSamlConfig(configOptions) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(path, expectedHttpMethod, expectedRequest)); + callParams(path, expectedHttpMethod, expectedRequest)); }); }); }); @@ -3402,7 +3837,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { .then((response) => { expect(response).to.deep.equal(expectedResult.data); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(expectedPath, expectedHttpMethod, expectedRequest)); + callParams(expectedPath, expectedHttpMethod, expectedRequest)); }); }); @@ -3435,7 +3870,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { .then((response) => { expect(response).to.deep.equal(expectedPartialResult.data); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(expectedPath, expectedHttpMethod, partialRequest)); + callParams(expectedPath, expectedHttpMethod, partialRequest)); }); }); @@ -3462,19 +3897,19 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { .then((response) => { expect(response).to.deep.equal(expectedPartialResult.data); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(expectedPath, expectedHttpMethod, partialRequest)); + callParams(expectedPath, expectedHttpMethod, partialRequest)); }); }); const invalidProviderIds = [ - null, NaN, 0, 1, true, false, '', 'oidc.provider', ['saml.provider'], [], {}, { a: 1 }, _.noop]; + null, NaN, 0, 1, true, false, '', 'oidc.provider', ['saml.provider'], [], {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((invalidProviderId) => { it('should be rejected given an invalid provider ID:' + JSON.stringify(invalidProviderId), () => { const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID); const requestHandler = handler.init(mockApp); return requestHandler.updateInboundSamlConfig(invalidProviderId as any, configOptions) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3492,7 +3927,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.updateInboundSamlConfig(providerId, invalidOptions) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3510,12 +3945,12 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.updateInboundSamlConfig(providerId, configOptions) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(expectedPath, expectedHttpMethod, expectedRequest)); + callParams(expectedPath, expectedHttpMethod, expectedRequest)); }); }); @@ -3532,12 +3967,12 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp); return requestHandler.updateInboundSamlConfig(providerId, configOptions) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(expectedPath, expectedHttpMethod, expectedRequest)); + callParams(expectedPath, expectedHttpMethod, expectedRequest)); }); }); }); @@ -3570,7 +4005,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.getTenant(invalidTenantId as any) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3590,7 +4025,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.getTenant(tenantId) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3669,7 +4104,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.listTenants(1001, nextPageToken) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3683,7 +4118,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.listTenants(maxResults, '') - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3706,7 +4141,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.listTenants(maxResults, nextPageToken) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3740,7 +4175,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.deleteTenant(invalidTenantId as any) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3760,7 +4195,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.deleteTenant(tenantId) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3810,7 +4245,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.createTenant(invalidOptions) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3827,7 +4262,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.createTenant(tenantOptions) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3847,7 +4282,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.createTenant(tenantOptions) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3871,7 +4306,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.createTenant(tenantOptions) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3910,7 +4345,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { .then((actualResult) => { expect(actualResult).to.deep.equal(expectedResult.data); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(expectedPath, patchMethod, expectedRequest)); + callParams(expectedPath, patchMethod, expectedRequest)); }); }); @@ -3930,7 +4365,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { .then((actualResult) => { expect(actualResult).to.deep.equal(expectedResult.data); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(expectedPath, patchMethod, partialRequest)); + callParams(expectedPath, patchMethod, partialRequest)); }); }); @@ -3950,7 +4385,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { .then((actualResult) => { expect(actualResult).to.deep.equal(expectedResult.data); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(expectedPath, patchMethod, partialRequest)); + callParams(expectedPath, patchMethod, partialRequest)); }); }); @@ -3961,7 +4396,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.updateTenant(invalidTenantId as any, tenantOptions) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3979,7 +4414,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.updateTenant(tenantId, invalidOptions) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); @@ -3997,12 +4432,12 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.updateTenant(tenantId, tenantOptions) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(expectedPath, patchMethod, expectedRequest)); + callParams(expectedPath, patchMethod, expectedRequest)); }); }); @@ -4019,12 +4454,12 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.updateTenant(tenantId, tenantOptions) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(expectedPath, patchMethod, expectedRequest)); + callParams(expectedPath, patchMethod, expectedRequest)); }); }); @@ -4045,12 +4480,12 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.updateTenant(tenantId, tenantOptions) - .then((resp) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { expect(error).to.deep.equal(expectedError); expect(stub).to.have.been.calledOnce.and.calledWith( - callParams(expectedPath, patchMethod, expectedRequest)); + callParams(expectedPath, patchMethod, expectedRequest)); }); }); }); diff --git a/test/unit/auth/auth-config.spec.ts b/test/unit/auth/auth-config.spec.ts index 9fca41ec64..d5777d787d 100755 --- a/test/unit/auth/auth-config.spec.ts +++ b/test/unit/auth/auth-config.spec.ts @@ -271,9 +271,9 @@ describe('SAMLConfig', () => { }); const invalidResourceNames: string[] = [ - '', 'incorrectsaml.', 'saml.provider', 'saml', 'oidc.provider', - 'projects/project1/prefixinboundSamlConfigs/saml.provider', - 'projects/project1/oauthIdpConfigs/saml.provider']; + '', 'incorrectsaml.', 'saml.provider', 'saml', 'oidc.provider', + 'projects/project1/prefixinboundSamlConfigs/saml.provider', + 'projects/project1/oauthIdpConfigs/saml.provider']; invalidResourceNames.forEach((invalidResourceName) => { it(`should return null for invalid resource name "${invalidResourceName}"`, () => { expect(SAMLConfig.getProviderIdFromResourceName(invalidResourceName)).to.be.null; @@ -287,8 +287,8 @@ describe('SAMLConfig', () => { }); const invalidProviderIds = [ - null, NaN, 0, 1, true, false, '', 'incorrectsaml.', 'saml', 'oidc.provider', 'other', [], [1, 'a'], - {}, { a: 1 }, _.noop]; + null, NaN, 0, 1, true, false, '', 'incorrectsaml.', 'saml', 'oidc.provider', 'other', [], [1, 'a'], + {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((invalidProviderId) => { it(`should return false on invalid SAML provider ID "${JSON.stringify(invalidProviderId)}"`, () => { expect(SAMLConfig.isProviderId(invalidProviderId)).to.be.false; @@ -413,7 +413,7 @@ describe('SAMLConfig', () => { }); const invalidProviderIds = [ - null, NaN, 0, 1, true, false, '', 'oidc.provider', 'other', [], [1, 'a'], {}, { a: 1 }, _.noop]; + null, NaN, 0, 1, true, false, '', 'oidc.provider', 'other', [], [1, 'a'], {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((providerId) => { it('should throw on invalid providerId:' + JSON.stringify(providerId), () => { const invalidClientRequest = deepCopy(clientRequest) as any; @@ -582,9 +582,9 @@ describe('OIDCConfig', () => { }); const invalidResourceNames: string[] = [ - '', 'incorrectsaml.', 'oidc.provider', 'oidc', 'saml.provider', - 'projects/project1/prefixoauthIdpConfigs/oidc.provider', - 'projects/project1/inboundSamlConfigs/oidc.provider']; + '', 'incorrectsaml.', 'oidc.provider', 'oidc', 'saml.provider', + 'projects/project1/prefixoauthIdpConfigs/oidc.provider', + 'projects/project1/inboundSamlConfigs/oidc.provider']; invalidResourceNames.forEach((invalidResourceName) => { it(`should return null for invalid resource name "${invalidResourceName}"`, () => { expect(OIDCConfig.getProviderIdFromResourceName(invalidResourceName)).to.be.null; @@ -598,8 +598,8 @@ describe('OIDCConfig', () => { }); const invalidProviderIds = [ - null, NaN, 0, 1, true, false, '', 'incorrectoidc.', 'oidc', 'saml.provider', 'other', [], [1, 'a'], - {}, { a: 1 }, _.noop]; + null, NaN, 0, 1, true, false, '', 'incorrectoidc.', 'oidc', 'saml.provider', 'other', [], [1, 'a'], + {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((invalidProviderId) => { it(`should return false on invalid OIDC provider ID "${JSON.stringify(invalidProviderId)}"`, () => { expect(OIDCConfig.isProviderId(invalidProviderId)).to.be.false; @@ -692,7 +692,7 @@ describe('OIDCConfig', () => { }); const invalidProviderIds = [ - null, NaN, 0, 1, true, false, '', 'other', 'saml.provider', [], [1, 'a'], {}, { a: 1 }, _.noop]; + null, NaN, 0, 1, true, false, '', 'other', 'saml.provider', [], [1, 'a'], {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((providerId) => { it('should throw on invalid providerId:' + JSON.stringify(providerId), () => { const invalidClientRequest = deepCopy(clientRequest) as any; diff --git a/test/unit/auth/auth.spec.ts b/test/unit/auth/auth.spec.ts index 0451be7288..c3a867011f 100755 --- a/test/unit/auth/auth.spec.ts +++ b/test/unit/auth/auth.spec.ts @@ -73,7 +73,7 @@ interface EmailActionTest { * @return {object} A sample valid server response as returned from getAccountInfo * endpoint. */ -function getValidGetAccountInfoResponse(tenantId?: string) { +function getValidGetAccountInfoResponse(tenantId?: string): {kind: string; users: any[]} { const userResponse: any = { localId: 'abcdefghijklmnopqrstuvwxyz', email: 'user@gmail.com', @@ -103,6 +103,18 @@ function getValidGetAccountInfoResponse(tenantId?: string) { rawId: '+11234567890', }, ], + mfaInfo: [ + { + mfaEnrollmentId: 'enrolledSecondFactor1', + phoneInfo: '+16505557348', + displayName: 'Spouse\'s phone number', + enrolledAt: new Date().toISOString(), + }, + { + mfaEnrollmentId: 'enrolledSecondFactor2', + phoneInfo: '+16505551000', + }, + ], photoUrl: 'https://lh3.googleusercontent.com/1234567890/photo.jpg', validSince: '1476136676', lastLoginAt: '1476235905000', @@ -123,7 +135,7 @@ function getValidGetAccountInfoResponse(tenantId?: string) { * @param {any} serverResponse Raw getAccountInfo response. * @return {Object} The corresponding user record. */ -function getValidUserRecord(serverResponse: any) { +function getValidUserRecord(serverResponse: any): UserRecord { return new UserRecord(serverResponse.users[0]); } @@ -140,13 +152,13 @@ function getDecodedIdToken(uid: string, authTime: Date, tenantId?: string): Deco return { iss: 'https://securetoken.google.com/project123456789', aud: 'project123456789', - auth_time: Math.floor(authTime.getTime() / 1000), + auth_time: Math.floor(authTime.getTime() / 1000), // eslint-disable-line @typescript-eslint/camelcase sub: uid, iat: Math.floor(authTime.getTime() / 1000), exp: Math.floor(authTime.getTime() / 1000 + 3600), firebase: { identities: {}, - sign_in_provider: 'custom', + sign_in_provider: 'custom', // eslint-disable-line @typescript-eslint/camelcase tenant: tenantId, }, }; @@ -165,13 +177,13 @@ function getDecodedSessionCookie(uid: string, authTime: Date, tenantId?: string) return { iss: 'https://session.firebase.google.com/project123456789', aud: 'project123456789', - auth_time: Math.floor(authTime.getTime() / 1000), + auth_time: Math.floor(authTime.getTime() / 1000), // eslint-disable-line @typescript-eslint/camelcase sub: uid, iat: Math.floor(authTime.getTime() / 1000), exp: Math.floor(authTime.getTime() / 1000 + 3600), firebase: { identities: {}, - sign_in_provider: 'custom', + sign_in_provider: 'custom', // eslint-disable-line @typescript-eslint/camelcase tenant: tenantId, }, }; @@ -524,7 +536,7 @@ AUTH_CONFIGS.forEach((testConfig) => { stubs.push(getUserStub); // Verify ID token while checking if revoked. return auth.verifyIdToken(mockIdToken, true) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -561,7 +573,7 @@ AUTH_CONFIGS.forEach((testConfig) => { // Verify ID token while checking if revoked. // This should fail with the underlying RPC error. return auth.verifyIdToken(mockIdToken, true) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -603,7 +615,7 @@ AUTH_CONFIGS.forEach((testConfig) => { // Verify ID token while checking if revoked. // This should fail with the underlying token generator verifyIdToken error. return auth.verifyIdToken(mockIdToken, true) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm expected error returned. @@ -621,7 +633,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .returns(Promise.resolve(getDecodedIdToken(uid, validSince))); // Verify ID token. return auth.verifyIdToken(mockIdToken) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm expected error returned. @@ -638,7 +650,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .returns(Promise.resolve(getDecodedIdToken(uid, validSince, 'otherTenantId'))); // Verify ID token. return auth.verifyIdToken(mockIdToken) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm expected error returned. @@ -768,7 +780,7 @@ AUTH_CONFIGS.forEach((testConfig) => { stubs.push(getUserStub); // Verify session cookie while checking if revoked. return auth.verifySessionCookie(mockSessionCookie, true) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -805,7 +817,7 @@ AUTH_CONFIGS.forEach((testConfig) => { // Verify session cookie while checking if revoked. // This should fail with the underlying RPC error. return auth.verifySessionCookie(mockSessionCookie, true) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -847,7 +859,7 @@ AUTH_CONFIGS.forEach((testConfig) => { // Verify session cookie while checking if revoked. // This should fail with the underlying token generator verifySessionCookie error. return auth.verifySessionCookie(mockSessionCookie, true) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm expected error returned. @@ -865,7 +877,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .returns(Promise.resolve(getDecodedSessionCookie(uid, validSince))); // Verify session cookie token. return auth.verifySessionCookie(mockSessionCookie) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm expected error returned. @@ -882,7 +894,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .returns(Promise.resolve(getDecodedSessionCookie(uid, validSince, 'otherTenantId'))); // Verify session cookie token. return auth.verifySessionCookie(mockSessionCookie) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm expected error returned. @@ -960,7 +972,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .rejects(expectedError); stubs.push(stub); return auth.getUser(uid) - .then((userRecord) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -1039,7 +1051,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .rejects(expectedError); stubs.push(stub); return auth.getUserByEmail(email) - .then((userRecord) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -1119,7 +1131,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .rejects(expectedError); stubs.push(stub); return auth.getUserByPhoneNumber(phoneNumber) - .then((userRecord) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -1291,7 +1303,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .rejects(expectedError); stubs.push(createUserStub); return auth.createUser(propertiesToCreate) - .then((userRecord) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -1312,7 +1324,7 @@ AUTH_CONFIGS.forEach((testConfig) => { stubs.push(createUserStub); stubs.push(getUserStub); return auth.createUser(propertiesToCreate) - .then((userRecord) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -1333,7 +1345,7 @@ AUTH_CONFIGS.forEach((testConfig) => { stubs.push(createUserStub); stubs.push(getUserStub); return auth.createUser(propertiesToCreate) - .then((userRecord) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -1445,7 +1457,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .rejects(expectedError); stubs.push(updateUserStub); return auth.updateUser(uid, propertiesToEdit) - .then((userRecord) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -1465,7 +1477,7 @@ AUTH_CONFIGS.forEach((testConfig) => { stubs.push(updateUserStub); stubs.push(getUserStub); return auth.updateUser(uid, propertiesToEdit) - .then((userRecord) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -1709,7 +1721,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .rejects(expectedError); stubs.push(downloadAccountStub); return auth.listUsers(maxResult, pageToken) - .then((results) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -1771,7 +1783,7 @@ AUTH_CONFIGS.forEach((testConfig) => { // Stub revokeRefreshTokens to return expected uid. const revokeRefreshTokensStub = sinon.stub(testConfig.RequestHandler.prototype, 'revokeRefreshTokens') - .resolves(uid); + .resolves(uid); stubs.push(revokeRefreshTokensStub); return auth.revokeRefreshTokens(uid) .then((result) => { @@ -1786,10 +1798,10 @@ AUTH_CONFIGS.forEach((testConfig) => { // Stub revokeRefreshTokens to throw a backend error. const revokeRefreshTokensStub = sinon.stub(testConfig.RequestHandler.prototype, 'revokeRefreshTokens') - .rejects(expectedError); + .rejects(expectedError); stubs.push(revokeRefreshTokensStub); return auth.revokeRefreshTokens(uid) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -1852,7 +1864,7 @@ AUTH_CONFIGS.forEach((testConfig) => { // Stub uploadAccount to return expected result. const uploadAccountStub = sinon.stub(testConfig.RequestHandler.prototype, 'uploadAccount') - .resolves(expectedUserImportResult); + .resolves(expectedUserImportResult); stubs.push(uploadAccountStub); return auth.importUsers(users, options) .then((result) => { @@ -1867,10 +1879,10 @@ AUTH_CONFIGS.forEach((testConfig) => { // Stub uploadAccount to reject with expected error. const uploadAccountStub = sinon.stub(testConfig.RequestHandler.prototype, 'uploadAccount') - .rejects(expectedServerError); + .rejects(expectedServerError); stubs.push(uploadAccountStub); return auth.importUsers(users, options) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -1884,7 +1896,7 @@ AUTH_CONFIGS.forEach((testConfig) => { // Stub uploadAccount to throw with expected error. const uploadAccountStub = sinon.stub(testConfig.RequestHandler.prototype, 'uploadAccount') - .throws(expectedOptionsError); + .throws(expectedOptionsError); stubs.push(uploadAccountStub); expect(() => { return auth.importUsers(users, {hash: {algorithm: 'invalid' as any}}); @@ -1905,7 +1917,7 @@ AUTH_CONFIGS.forEach((testConfig) => { // Stub uploadAccount to return expected result. const uploadAccountStub = sinon.stub(testConfig.RequestHandler.prototype, 'uploadAccount') - .returns(Promise.resolve(expectedUserImportResult)); + .returns(Promise.resolve(expectedUserImportResult)); const usersCopy = deepCopy(users); usersCopy.forEach((user) => { (user as any).tenantId = TENANT_ID; @@ -2026,7 +2038,7 @@ AUTH_CONFIGS.forEach((testConfig) => { // Stub createSessionCookie to return expected sessionCookie. const createSessionCookieStub = sinon.stub(testConfig.RequestHandler.prototype, 'createSessionCookie') - .resolves(sessionCookie); + .resolves(sessionCookie); stubs.push(createSessionCookieStub); return auth.createSessionCookie(idToken, options) .then((result) => { @@ -2052,10 +2064,10 @@ AUTH_CONFIGS.forEach((testConfig) => { // Stub createSessionCookie to throw a backend error. const createSessionCookieStub = sinon.stub(testConfig.RequestHandler.prototype, 'createSessionCookie') - .rejects(expectedError); + .rejects(expectedError); stubs.push(createSessionCookieStub); return auth.createSessionCookie(idToken, options) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -2073,7 +2085,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .returns(Promise.reject(expectedError)); stubs.push(verifyIdTokenStub); return auth.createSessionCookie(idToken, options) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -2157,7 +2169,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .then((actualLink: string) => { // Confirm underlying API called with expected parameters. expect(getEmailActionLinkStub).to.have.been.calledOnce.and.calledWith( - emailActionFlow.requestType, email, actionCodeSettings); + emailActionFlow.requestType, email, actionCodeSettings); // Confirm expected user record response returned. expect(actualLink).to.equal(expectedLink); }); @@ -2178,7 +2190,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .then((actualLink: string) => { // Confirm underlying API called with expected parameters. expect(getEmailActionLinkStub).to.have.been.calledOnce.and.calledWith( - emailActionFlow.requestType, email, undefined); + emailActionFlow.requestType, email, undefined); // Confirm expected user record response returned. expect(actualLink).to.equal(expectedLink); }); @@ -2191,12 +2203,12 @@ AUTH_CONFIGS.forEach((testConfig) => { .rejects(expectedError); stubs.push(getEmailActionLinkStub); return (auth as any)[emailActionFlow.api](email, actionCodeSettings) - .then((actualLink: string) => { + .then(() => { throw new Error('Unexpected success'); }, (error: any) => { // Confirm underlying API called with expected parameters. expect(getEmailActionLinkStub).to.have.been.calledOnce.and.calledWith( - emailActionFlow.requestType, email, actionCodeSettings); + emailActionFlow.requestType, email, actionCodeSettings); // Confirm expected error returned. expect(error).to.equal(expectedError); }); @@ -2218,7 +2230,7 @@ AUTH_CONFIGS.forEach((testConfig) => { }); const invalidProviderIds = [ - undefined, null, NaN, 0, 1, true, false, '', [], [1, 'a'], {}, { a: 1 }, _.noop]; + undefined, null, NaN, 0, 1, true, false, '', [], [1, 'a'], {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((invalidProviderId) => { it(`should be rejected given an invalid provider ID "${JSON.stringify(invalidProviderId)}"`, () => { return (auth as Auth).getProviderConfig(invalidProviderId as any) @@ -2281,7 +2293,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .rejects(expectedError); stubs.push(stub); return (auth as Auth).getProviderConfig(providerId) - .then((config) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -2335,7 +2347,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .rejects(expectedError); stubs.push(stub); return (auth as Auth).getProviderConfig(providerId) - .then((config) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -2469,7 +2481,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .rejects(expectedError); stubs.push(listConfigsStub); return auth.listProviderConfigs(filterOptions) - .then((results) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -2564,7 +2576,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .rejects(expectedError); stubs.push(listConfigsStub); return auth.listProviderConfigs(filterOptions) - .then((results) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -2643,7 +2655,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .rejects(expectedError); stubs.push(stub); return (auth as Auth).deleteProviderConfig(providerId) - .then((config) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -2678,7 +2690,7 @@ AUTH_CONFIGS.forEach((testConfig) => { .rejects(expectedError); stubs.push(stub); return (auth as Auth).deleteProviderConfig(providerId) - .then((config) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -2789,7 +2801,7 @@ AUTH_CONFIGS.forEach((testConfig) => { stubs.push(updateConfigStub); return auth.updateProviderConfig(providerId, configOptions) - .then((actualConfig) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -2855,7 +2867,7 @@ AUTH_CONFIGS.forEach((testConfig) => { stubs.push(updateConfigStub); return auth.updateProviderConfig(providerId, configOptions) - .then((actualConfig) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -2955,7 +2967,7 @@ AUTH_CONFIGS.forEach((testConfig) => { stubs.push(createConfigStub); return (auth as Auth).createProviderConfig(configOptions) - .then((actualConfig) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -3022,7 +3034,7 @@ AUTH_CONFIGS.forEach((testConfig) => { stubs.push(createConfigStub); return (auth as Auth).createProviderConfig(configOptions) - .then((actualConfig) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. diff --git a/test/unit/auth/credential.spec.ts b/test/unit/auth/credential.spec.ts index 0b9ac61925..881dcb7525 100644 --- a/test/unit/auth/credential.spec.ts +++ b/test/unit/auth/credential.spec.ts @@ -42,6 +42,8 @@ chai.should(); chai.use(sinonChai); chai.use(chaiAsPromised); +/* eslint-disable @typescript-eslint/camelcase */ + const expect = chai.expect; const GCLOUD_CREDENTIAL_SUFFIX = 'gcloud/application_default_credentials.json'; @@ -416,7 +418,7 @@ describe('Credential', () => { it('should throw error if type not specified on cert file', () => { fsStub = sinon.stub(fs, 'readFileSync').returns(JSON.stringify({})); expect(() => getApplicationDefault()) - .to.throw(Error, 'Invalid contents in the credentials file'); + .to.throw(Error, 'Invalid contents in the credentials file'); }); it('should throw error if type is unknown on cert file', () => { diff --git a/test/unit/auth/tenant-manager.spec.ts b/test/unit/auth/tenant-manager.spec.ts index bf1c937e63..82f89599d9 100644 --- a/test/unit/auth/tenant-manager.spec.ts +++ b/test/unit/auth/tenant-manager.spec.ts @@ -53,11 +53,11 @@ describe('TenantManager', () => { mockApp = mocks.app(); tenantManager = new TenantManager(mockApp); nullAccessTokenTenantManager = new TenantManager( - mocks.appReturningNullAccessToken()); + mocks.appReturningNullAccessToken()); malformedAccessTokenTenantManager = new TenantManager( - mocks.appReturningMalformedAccessToken()); + mocks.appReturningMalformedAccessToken()); rejectedPromiseAccessTokenTenantManager = new TenantManager( - mocks.appRejectedWhileFetchingAccessToken()); + mocks.appRejectedWhileFetchingAccessToken()); }); @@ -158,7 +158,7 @@ describe('TenantManager', () => { .returns(Promise.reject(expectedError)); stubs.push(stub); return tenantManager.getTenant(tenantId) - .then((tenant) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -290,7 +290,7 @@ describe('TenantManager', () => { .returns(Promise.reject(expectedError)); stubs.push(listTenantsStub); return tenantManager.listTenants(maxResult, pageToken) - .then((results) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -365,7 +365,7 @@ describe('TenantManager', () => { .returns(Promise.reject(expectedError)); stubs.push(stub); return tenantManager.deleteTenant(tenantId) - .then((userRecord) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -456,7 +456,7 @@ describe('TenantManager', () => { .returns(Promise.reject(expectedError)); stubs.push(createTenantStub); return tenantManager.createTenant(tenantOptions) - .then((actualTenant) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. @@ -565,7 +565,7 @@ describe('TenantManager', () => { .returns(Promise.reject(expectedError)); stubs.push(updateTenantStub); return tenantManager.updateTenant(tenantId, tenantOptions) - .then((actualTenant) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. diff --git a/test/unit/auth/token-generator.spec.ts b/test/unit/auth/token-generator.spec.ts index b95c1ad64b..3a6dbe4df4 100644 --- a/test/unit/auth/token-generator.spec.ts +++ b/test/unit/auth/token-generator.spec.ts @@ -84,6 +84,7 @@ describe('CryptoSigner', () => { const payload = Buffer.from('test'); const cert = new ServiceAccountCredential(mocks.certificateObject); + // eslint-disable-next-line @typescript-eslint/no-var-requires const crypto = require('crypto'); const rsa = crypto.createSign('RSA-SHA256'); rsa.update(payload); @@ -372,7 +373,7 @@ describe('FirebaseTokenGenerator', () => { }); it('should be fulfilled given a valid uid and empty object developer claims', () => { - return tokenGenerator.createCustomToken(mocks.uid, {}); + return tokenGenerator.createCustomToken(mocks.uid, {}); }); it('should be fulfilled given a valid uid and valid developer claims', () => { @@ -400,6 +401,7 @@ describe('FirebaseTokenGenerator', () => { }; if (tokenGenerator.tenantId) { + // eslint-disable-next-line @typescript-eslint/camelcase expected.tenant_id = tokenGenerator.tenantId; } @@ -428,6 +430,7 @@ describe('FirebaseTokenGenerator', () => { }; if (tokenGenerator.tenantId) { + // eslint-disable-next-line @typescript-eslint/camelcase expected.tenant_id = tokenGenerator.tenantId; } diff --git a/test/unit/auth/token-verifier.spec.ts b/test/unit/auth/token-verifier.spec.ts index 41dddfe514..bf95f96380 100644 --- a/test/unit/auth/token-verifier.spec.ts +++ b/test/unit/auth/token-verifier.spec.ts @@ -88,7 +88,7 @@ function mockFetchPublicKeysWithErrorResponse(): nock.Scope { .get('/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com') .reply(200, { error: 'message', - error_description: 'description', + error_description: 'description', // eslint-disable-line @typescript-eslint/camelcase }); } @@ -153,7 +153,7 @@ describe('FirebaseTokenVerifier', () => { expiredErrorCode: AuthClientErrorCode.INVALID_ARGUMENT, }, app, - ); + ); }).not.to.throw(); }); diff --git a/test/unit/auth/user-import-builder.spec.ts b/test/unit/auth/user-import-builder.spec.ts old mode 100644 new mode 100755 index 58b100d059..b94e22f1f4 --- a/test/unit/auth/user-import-builder.spec.ts +++ b/test/unit/auth/user-import-builder.spec.ts @@ -19,7 +19,10 @@ import * as sinonChai from 'sinon-chai'; import * as chaiAsPromised from 'chai-as-promised'; import {deepCopy} from '../../../src/utils/deep-copy'; -import {UserImportBuilder, ValidatorFunction, UserImportResult} from '../../../src/auth/user-import-builder'; +import { + UserImportBuilder, ValidatorFunction, UserImportResult, UserImportRecord, + UploadAccountRequest, +} from '../../../src/auth/user-import-builder'; import {AuthClientErrorCode, FirebaseAuthError} from '../../../src/utils/error'; import {toWebSafeBase64} from '../../../src/utils'; @@ -31,8 +34,9 @@ chai.use(chaiAsPromised); const expect = chai.expect; describe('UserImportBuilder', () => { - const nowString = new Date().toUTCString(); - const userRequestValidator: ValidatorFunction = (request) => { + const now = new Date('2019-10-25T04:30:52.000Z'); + const nowString = now.toUTCString(); + const userRequestValidator: ValidatorFunction = () => { // Do not throw an error. }; const userRequestValidatorWithError: ValidatorFunction = (request) => { @@ -75,6 +79,28 @@ describe('UserImportBuilder', () => { passwordSalt: Buffer.from('NaCl'), }, {uid: '5678', phoneNumber: '+16505550101'}, + { + uid: '3456', + email: 'janedoe@example.com', + passwordHash: Buffer.from('password'), + passwordSalt: Buffer.from('NaCl'), + multiFactor: { + enrolledFactors: [ + { + uid: 'enrolledSecondFactor1', + phoneNumber: '+16505557348', + displayName: 'Spouse\'s phone number', + factorId: 'phone', + enrollmentTime: now.toUTCString(), + }, + { + uid: 'enrolledSecondFactor2', + phoneNumber: '+16505551000', + factorId: 'phone', + }, + ], + }, + }, ]; const expectedUsersRequest = [ { @@ -109,6 +135,24 @@ describe('UserImportBuilder', () => { localId: '5678', phoneNumber: '+16505550101', }, + { + localId: '3456', + email: 'janedoe@example.com', + passwordHash: toWebSafeBase64(Buffer.from('password')), + salt: toWebSafeBase64(Buffer.from('NaCl')), + mfaInfo: [ + { + mfaEnrollmentId: 'enrolledSecondFactor1', + phoneInfo: '+16505557348', + displayName: 'Spouse\'s phone number', + enrolledAt: now.toISOString(), + }, + { + mfaEnrollmentId: 'enrolledSecondFactor2', + phoneInfo: '+16505551000', + }, + ], + }, ]; const hmacAlgorithms = ['HMAC_SHA512', 'HMAC_SHA256', 'HMAC_SHA1', 'HMAC_MD5']; @@ -210,23 +254,23 @@ describe('UserImportBuilder', () => { let minRounds: number; let maxRounds: number; switch (algorithm) { - case 'MD5': - minRounds = 0; - maxRounds = 8192; - break; - case 'SHA1': - case 'SHA256': - case 'SHA512': - minRounds = 1; - maxRounds = 8192; - break; - case 'PBKDF_SHA1': - case 'PBKDF2_SHA256': - minRounds = 0; - maxRounds = 120000; - break; - default: - throw new Error('Unexpected algorithm: ' + algorithm); + case 'MD5': + minRounds = 0; + maxRounds = 8192; + break; + case 'SHA1': + case 'SHA256': + case 'SHA512': + minRounds = 1; + maxRounds = 8192; + break; + case 'PBKDF_SHA1': + case 'PBKDF2_SHA256': + minRounds = 0; + maxRounds = 120000; + break; + default: + throw new Error('Unexpected algorithm: ' + algorithm); } const invalidRounds = [minRounds - 1, maxRounds + 1, 'invalid', undefined, null]; @@ -555,7 +599,9 @@ describe('UserImportBuilder', () => { const expectedRequest = { hashAlgorithm: algorithm, // The third user will be removed due to client side error. - users: [expectedUsersRequest[0], expectedUsersRequest[1]], + users: [ + expectedUsersRequest[0], expectedUsersRequest[1], expectedUsersRequest[3], + ], }; const userImportBuilder = new UserImportBuilder(users, validOptions as any, userRequestValidatorWithError); @@ -577,6 +623,77 @@ describe('UserImportBuilder', () => { new UserImportBuilder(noHashUsers, validOptions as any, userRequestValidator); expect(userImportBuilder.buildRequest()).to.deep.equal(expectedRequest); }); + + it('should return expected request with no multi-factor fields when not available', () => { + const noMultiFactorUsers: any[] = [ + {uid: '1234', email: 'user@example.com', multiFactor: null}, + {uid: '5678', phoneNumber: '+16505550101', multiFactor: {enrolledFactors: []}}, + ]; + const expectedRequest = { + users: [ + {localId: '1234', email: 'user@example.com'}, + {localId: '5678', phoneNumber: '+16505550101'}, + ], + }; + const userImportBuilder = + new UserImportBuilder(noMultiFactorUsers, validOptions as any, userRequestValidator); + expect(userImportBuilder.buildRequest()).to.deep.equal(expectedRequest); + }); + + it('should ignore users with invalid second factor enrollment time', () => { + const invalidMultiFactorUsers: UserImportRecord[] = [ + { + uid: '1234', + multiFactor: { + enrolledFactors: [ + { + uid: 'enrolledSecondFactor1', + phoneNumber: '+16505557348', + displayName: 'Spouse\'s phone number', + factorId: 'phone', + enrollmentTime: 'invalid', + }, + ], + }, + }, + {uid: '5678', phoneNumber: '+16505550102'}, + ]; + const expectedRequest: UploadAccountRequest = { + users: [ + {localId: '5678', phoneNumber: '+16505550102'}, + ], + }; + const userImportBuilder = + new UserImportBuilder(invalidMultiFactorUsers, validOptions as any, userRequestValidator); + expect(userImportBuilder.buildRequest()).to.deep.equal(expectedRequest); + }); + + it('should ignore users with unsupported second factors', () => { + const invalidMultiFactorUsers: any = [ + { + uid: '1234', + multiFactor: { + enrolledFactors: [ + { + uid: 'enrolledSecondFactor1', + secret: 'SECRET', + displayName: 'Google Authenticator on personal phone', + factorId: 'totp', + }, + ], + }, + }, + {uid: '5678', phoneNumber: '+16505550102'}, + ]; + const expectedRequest: UploadAccountRequest = { + users: [ + {localId: '5678', phoneNumber: '+16505550102'}, + ], + }; + const userImportBuilder = + new UserImportBuilder(invalidMultiFactorUsers, validOptions as any, userRequestValidator); + expect(userImportBuilder.buildRequest()).to.deep.equal(expectedRequest); + }); }); describe('buildResponse()', () => { @@ -586,10 +703,11 @@ describe('UserImportBuilder', () => { algorithm, }, }; + it('should return the expected response for successful import', () => { const successfulServerResponse: any = []; const successfulUserImportResponse: UserImportResult = { - successCount: 3, + successCount: 4, failureCount: 0, errors: [], }; @@ -604,15 +722,15 @@ describe('UserImportBuilder', () => { {index: 1, message: 'Some error occurred!'}, ]; const serverErrorUserImportResponse = { - successCount: 2, + successCount: 3, failureCount: 1, errors: [ { // Index should match server error index. index: 1, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_USER_IMPORT, - 'Some error occurred!', + AuthClientErrorCode.INVALID_USER_IMPORT, + 'Some error occurred!', ), }, ], @@ -626,7 +744,7 @@ describe('UserImportBuilder', () => { it('should return the expected response for import with client side errors', () => { const successfulServerResponse: any = []; const clientErrorUserImportResponse: UserImportResult = { - successCount: 2, + successCount: 3, failureCount: 1, errors: [ {index: 2, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_PHONE_NUMBER)}, @@ -656,7 +774,8 @@ describe('UserImportBuilder', () => { // The second and fourth users will throw a client side error. // The third and sixth user will throw a server side error. - // Seventh and eighth user will throw a client side error due to invalid type provided. + // Seventh, eighth and nineth user will throw a client side error due to invalid type provided. + // Tenth user will throw a client side error due to an unsupported second factor. const testUsers = [ {uid: 'USER1'}, {uid: 'USER2', email: 'invalid', passwordHash: Buffer.from('userpass')}, @@ -671,10 +790,36 @@ describe('UserImportBuilder', () => { passwordHash: Buffer.from('password'), passwordSalt: 'not a buffer' as any, }, + { + uid: 'USER9', + multiFactor: { + enrolledFactors: [ + { + uid: 'enrollmentId1', + phoneNumber: '+16505551111', + factorId: 'phone', + enrollmentTime: 'invalid', + }, + ], + }, + }, + { + uid: 'USER10', + multiFactor: { + enrolledFactors: [ + { + uid: 'enrollmentId2', + secret: 'SECRET', + displayName: 'Google Authenticator on personal phone', + factorId: 'totp', + } as any, + ], + }, + }, ]; const mixedErrorUserImportResponse = { successCount: 2, - failureCount: 6, + failureCount: 8, errors: [ // Client side detected error. {index: 1, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_EMAIL)}, @@ -682,8 +827,8 @@ describe('UserImportBuilder', () => { { index: 2, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_USER_IMPORT, - 'Some error occurred in USER3!', + AuthClientErrorCode.INVALID_USER_IMPORT, + 'Some error occurred in USER3!', ), }, // Client side detected error. @@ -692,17 +837,30 @@ describe('UserImportBuilder', () => { { index: 5, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_USER_IMPORT, - 'Another error occurred in USER6!', + AuthClientErrorCode.INVALID_USER_IMPORT, + 'Another error occurred in USER6!', ), }, // Client side errors. {index: 6, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_PASSWORD_HASH)}, {index: 7, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_PASSWORD_SALT)}, + { + index: 8, + error: new FirebaseAuthError( + AuthClientErrorCode.INVALID_ENROLLMENT_TIME, + `The second factor "enrollmentTime" for "enrollmentId1" must be a valid ` + + `UTC date string.`), + }, + { + index: 9, + error: new FirebaseAuthError( + AuthClientErrorCode.UNSUPPORTED_SECOND_FACTOR, + `Unsupported second factor "${JSON.stringify(testUsers[9].multiFactor!.enrolledFactors[0])}" provided.`), + }, ], }; const userImportBuilder = new UserImportBuilder( - testUsers, validOptions as any, userRequestValidatorWithMultipleErrors); + testUsers, validOptions as any, userRequestValidatorWithMultipleErrors); expect(userImportBuilder.buildResponse(failingServerResponse)) .to.deep.equal(mixedErrorUserImportResponse); }); diff --git a/test/unit/auth/user-record.spec.ts b/test/unit/auth/user-record.spec.ts index 7b81d83a2a..fce2041ec6 100644 --- a/test/unit/auth/user-record.spec.ts +++ b/test/unit/auth/user-record.spec.ts @@ -19,7 +19,10 @@ import * as sinonChai from 'sinon-chai'; import * as chaiAsPromised from 'chai-as-promised'; import {deepCopy} from '../../../src/utils/deep-copy'; -import {UserInfo, UserMetadata, UserRecord} from '../../../src/auth/user-record'; +import { + UserInfo, UserMetadata, UserRecord, GetAccountInfoUserResponse, ProviderUserInfoResponse, + MultiFactor, PhoneMultiFactorInfo, MultiFactorInfo, MultiFactorInfoResponse, +} from '../../../src/auth/user-record'; chai.should(); @@ -27,13 +30,14 @@ chai.use(sinonChai); chai.use(chaiAsPromised); const expect = chai.expect; +const now = new Date(); /** - * @param {string=} tenantId The optional tenant ID to add to the response. - * @return {object} A sample valid user response as returned from getAccountInfo + * @param tenantId The optional tenant ID to add to the response. + * @return A sample valid user response as returned from getAccountInfo * endpoint. */ -function getValidUserResponse(tenantId?: string): {[key: string]: any} { +function getValidUserResponse(tenantId?: string): GetAccountInfoUserResponse { const response: any = { localId: 'abcdefghijklmnopqrstuvwxyz', email: 'user@gmail.com', @@ -79,6 +83,19 @@ function getValidUserResponse(tenantId?: string): {[key: string]: any} { customAttributes: JSON.stringify({ admin: true, }), + mfaInfo: [ + { + mfaEnrollmentId: 'enrollmentId1', + displayName: 'displayName1', + enrolledAt: now.toISOString(), + phoneInfo: '+16505551234', + }, + { + mfaEnrollmentId: 'enrollmentId2', + enrolledAt: now.toISOString(), + phoneInfo: '+16505556789', + }, + ], }; if (typeof tenantId !== 'undefined') { response.tenantId = tenantId; @@ -87,8 +104,8 @@ function getValidUserResponse(tenantId?: string): {[key: string]: any} { } /** - * @param {string=} tenantId The optional tenant ID to add to the user. - * @return {object} The expected user JSON representation for the above user + * @param tenantId The optional tenant ID to add to the user. + * @return The expected user JSON representation for the above user * server response. */ function getUserJSON(tenantId?: string): object { @@ -145,14 +162,32 @@ function getUserJSON(tenantId?: string): object { }, tokensValidAfterTime: new Date(1476136676000).toUTCString(), tenantId, + multiFactor: { + enrolledFactors: [ + { + uid: 'enrollmentId1', + displayName: 'displayName1', + enrollmentTime: now.toUTCString(), + phoneNumber: '+16505551234', + factorId: 'phone', + }, + { + uid: 'enrollmentId2', + displayName: null, + enrollmentTime: now.toUTCString(), + phoneNumber: '+16505556789', + factorId: 'phone', + }, + ], + }, }; } /** - * @return {object} A sample user info response as returned from getAccountInfo + * @return A sample user info response as returned from getAccountInfo * endpoint. */ -function getUserInfoResponse(): object { +function getUserInfoResponse(): ProviderUserInfoResponse { return { providerId: 'google.com', displayName: 'John Doe', @@ -164,7 +199,7 @@ function getUserInfoResponse(): object { } /** - * @return {object} The JSON representation of the above user info response. + * @return The JSON representation of the above user info response. */ function getUserInfoJSON(): object { return { @@ -178,10 +213,10 @@ function getUserInfoJSON(): object { } /** - * @return {object} A sample user info response with phone number as returned + * @return A sample user info response with phone number as returned * from getAccountInfo endpoint. */ -function getUserInfoWithPhoneNumberResponse(): object { +function getUserInfoWithPhoneNumberResponse(): ProviderUserInfoResponse { return { providerId: 'phone', phoneNumber: '+11234567890', @@ -190,7 +225,7 @@ function getUserInfoWithPhoneNumberResponse(): object { } /** - * @return {object} The JSON representation of the above user info response + * @return The JSON representation of the above user info response * with a phone number. */ function getUserInfoWithPhoneNumberJSON(): object { @@ -204,11 +239,277 @@ function getUserInfoWithPhoneNumberJSON(): object { }; } +describe('PhoneMultiFactorInfo', () => { + const serverResponse: MultiFactorInfoResponse = { + mfaEnrollmentId: 'enrollmentId1', + displayName: 'displayName1', + enrolledAt: now.toISOString(), + phoneInfo: '+16505551234', + }; + const phoneMultiFactorInfo = new PhoneMultiFactorInfo(serverResponse); + const phoneMultiFactorInfoMissingFields = new PhoneMultiFactorInfo({ + mfaEnrollmentId: serverResponse.mfaEnrollmentId, + phoneInfo: serverResponse.phoneInfo, + }); + + describe('constructor', () => { + it('should throw when an empty object is provided', () => { + expect(() => { + return new PhoneMultiFactorInfo({} as any); + }).to.throw('INTERNAL ASSERT FAILED: Invalid multi-factor info response'); + }); + + it('should throw when an undefined response is provided', () => { + expect(() => { + return new PhoneMultiFactorInfo(undefined as any); + }).to.throw('INTERNAL ASSERT FAILED: Invalid multi-factor info response'); + }); + + it('should succeed when mfaEnrollmentId and phoneInfo are both provided', () => { + expect(() => { + return new PhoneMultiFactorInfo({ + mfaEnrollmentId: 'enrollmentId1', + phoneInfo: '+16505551234', + }); + }).not.to.throw(Error); + }); + + it('should throw when only mfaEnrollmentId is provided', () => { + expect(() => { + return new PhoneMultiFactorInfo({ + mfaEnrollmentId: 'enrollmentId1', + } as any); + }).to.throw('INTERNAL ASSERT FAILED: Invalid multi-factor info response'); + }); + + it('should throw when only phoneInfo is provided', () => { + expect(() => { + return new PhoneMultiFactorInfo({ + phoneInfo: '+16505551234', + } as any); + }).to.throw('INTERNAL ASSERT FAILED: Invalid multi-factor info response'); + }); + }); + + describe('getters', () => { + it('should set missing optional fields to null', () => { + expect(phoneMultiFactorInfoMissingFields.uid).to.equal(serverResponse.mfaEnrollmentId); + expect(phoneMultiFactorInfoMissingFields.displayName).to.be.null; + expect(phoneMultiFactorInfoMissingFields.phoneNumber).to.equal(serverResponse.phoneInfo); + expect(phoneMultiFactorInfoMissingFields.enrollmentTime).to.be.null; + expect(phoneMultiFactorInfoMissingFields.factorId).to.equal('phone'); + }); + + it('should return expected factorId', () => { + expect(phoneMultiFactorInfo.factorId).to.equal('phone'); + }); + + it('should throw when modifying readonly factorId property', () => { + expect(() => { + (phoneMultiFactorInfo as any).factorId = 'other'; + }).to.throw(Error); + }); + + it('should return expected displayName', () => { + expect(phoneMultiFactorInfo.displayName).to.equal(serverResponse.displayName); + }); + + it('should throw when modifying readonly displayName property', () => { + expect(() => { + (phoneMultiFactorInfo as any).displayName = 'Modified'; + }).to.throw(Error); + }); + + it('should return expected phoneNumber', () => { + expect(phoneMultiFactorInfo.phoneNumber).to.equal(serverResponse.phoneInfo); + }); + + it('should throw when modifying readonly phoneNumber property', () => { + expect(() => { + (phoneMultiFactorInfo as any).phoneNumber = '+16505551111'; + }).to.throw(Error); + }); + + it('should return expected uid', () => { + expect(phoneMultiFactorInfo.uid).to.equal(serverResponse.mfaEnrollmentId); + }); + + it('should throw when modifying readonly uid property', () => { + expect(() => { + (phoneMultiFactorInfo as any).uid = 'modifiedEnrollmentId'; + }).to.throw(Error); + }); + + it('should return expected enrollmentTime', () => { + expect(phoneMultiFactorInfo.enrollmentTime).to.equal(now.toUTCString()); + }); + + it('should throw when modifying readonly uid property', () => { + expect(() => { + (phoneMultiFactorInfo as any).enrollmentTime = new Date().toISOString(); + }).to.throw(Error); + }); + }); + + describe('toJSON', () => { + it('should return expected JSON object', () => { + expect(phoneMultiFactorInfo.toJSON()).to.deep.equal({ + uid: 'enrollmentId1', + displayName: 'displayName1', + enrollmentTime: now.toUTCString(), + phoneNumber: '+16505551234', + factorId: 'phone', + }); + }); + + it('should return expected JSON object with missing fields set to null', () => { + expect(phoneMultiFactorInfoMissingFields.toJSON()).to.deep.equal({ + uid: 'enrollmentId1', + displayName: null, + enrollmentTime: null, + phoneNumber: '+16505551234', + factorId: 'phone', + }); + }); + }); +}); + +describe('MultiFactorInfo', () => { + const serverResponse: MultiFactorInfoResponse = { + mfaEnrollmentId: 'enrollmentId1', + displayName: 'displayName1', + enrolledAt: now.toISOString(), + phoneInfo: '+16505551234', + }; + const phoneMultiFactorInfo = new PhoneMultiFactorInfo(serverResponse); + + describe('initMultiFactorInfo', () => { + it('should return expected PhoneMultiFactorInfo', () => { + expect(MultiFactorInfo.initMultiFactorInfo(serverResponse)).to.deep.equal(phoneMultiFactorInfo); + }); + + it('should return null for invalid MultiFactorInfo', () => { + expect(MultiFactorInfo.initMultiFactorInfo(undefined as any)).to.be.null; + }); + }); +}); + +describe('MultiFactor', () => { + const serverResponse = { + localId: 'uid123', + mfaInfo: [ + { + mfaEnrollmentId: 'enrollmentId1', + displayName: 'displayName1', + enrolledAt: now.toISOString(), + phoneInfo: '+16505551234', + }, + { + mfaEnrollmentId: 'enrollmentId2', + enrolledAt: now.toISOString(), + phoneInfo: '+16505556789', + }, + { + // Invalid factor. + mfaEnrollmentId: 'enrollmentId3', + }, + { + // Unsupported factor. + mfaEnrollmentId: 'enrollmentId4', + displayName: 'Backup second factor', + enrolledAt: now.toISOString(), + secretKey: 'SECRET_KEY', + }, + ], + }; + const expectedMultiFactorInfo = [ + new PhoneMultiFactorInfo({ + mfaEnrollmentId: 'enrollmentId1', + displayName: 'displayName1', + enrolledAt: now.toISOString(), + phoneInfo: '+16505551234', + }), + new PhoneMultiFactorInfo({ + mfaEnrollmentId: 'enrollmentId2', + enrolledAt: now.toISOString(), + phoneInfo: '+16505556789', + }), + ]; + + describe('constructor', () => { + it('should throw when a non object is provided', () => { + expect(() => { + return new MultiFactor(undefined as any); + }).to.throw('INTERNAL ASSERT FAILED: Invalid multi-factor response'); + }); + + it('should populate an empty enrolledFactors array when given an empty object', () => { + const multiFactor = new MultiFactor({} as any); + + expect(multiFactor.enrolledFactors.length).to.equal(0); + }); + + it('should populate expected enrolledFactors', () => { + const multiFactor = new MultiFactor(serverResponse); + + expect(multiFactor.enrolledFactors.length).to.equal(2); + expect(multiFactor.enrolledFactors[0]).to.deep.equal(expectedMultiFactorInfo[0]); + expect(multiFactor.enrolledFactors[1]).to.deep.equal(expectedMultiFactorInfo[1]); + }); + }); + + describe('getter', () => { + it('should throw when modifying readonly enrolledFactors property', () => { + const multiFactor = new MultiFactor(serverResponse); + + expect(() => { + (multiFactor as any).enrolledFactors = [ + expectedMultiFactorInfo[0], + ]; + }).to.throw(Error); + }); + + it('should throw when modifying readonly enrolledFactors internals', () => { + const multiFactor = new MultiFactor(serverResponse); + + expect(() => { + (multiFactor.enrolledFactors as any)[0] = new PhoneMultiFactorInfo({ + mfaEnrollmentId: 'enrollmentId3', + displayName: 'displayName3', + enrolledAt: now.toISOString(), + phoneInfo: '+16505559999', + }); + }).to.throw(Error); + }); + }); + + describe('toJSON', () => { + it('should return expected JSON object when given an empty response', () => { + const multiFactor = new MultiFactor({} as any); + + expect(multiFactor.toJSON()).to.deep.equal({ + enrolledFactors: [], + }); + }); + + it('should return expected JSON object when given a populated response', () => { + const multiFactor = new MultiFactor(serverResponse); + + expect(multiFactor.toJSON()).to.deep.equal({ + enrolledFactors: [ + expectedMultiFactorInfo[0].toJSON(), + expectedMultiFactorInfo[1].toJSON(), + ], + }); + }); + }); +}); + describe('UserInfo', () => { describe('constructor', () => { it('should throw when an empty object is provided', () => { expect(() => { - return new UserInfo({}); + return new UserInfo({} as any); }).to.throw(Error); }); @@ -220,13 +521,13 @@ describe('UserInfo', () => { it('should throw when only rawId is provided', () => { expect(() => { - return new UserInfo({rawId: '1234567890'}); + return new UserInfo({rawId: '1234567890'} as any); }).to.throw(Error); }); it('should throw when only providerId is provided', () => { expect(() => { - return new UserInfo({providerId: 'google.com'}); + return new UserInfo({providerId: 'google.com'} as any); }).to.throw(Error); }); }); @@ -316,8 +617,9 @@ describe('UserMetadata', () => { const expectedLastLoginAt = 1476235905000; const expectedCreatedAt = 1476136676000; const actualMetadata: UserMetadata = new UserMetadata({ - lastLoginAt: expectedLastLoginAt, - createdAt: expectedCreatedAt, + localId: 'uid123', + lastLoginAt: expectedLastLoginAt.toString(), + createdAt: expectedCreatedAt.toString(), }); const expectedMetadataJSON = { lastSignInTime: new Date(expectedLastLoginAt).toUTCString(), @@ -327,12 +629,12 @@ describe('UserMetadata', () => { describe('constructor', () => { it('should initialize as expected when a valid creationTime is provided', () => { expect(() => { - return new UserMetadata({createdAt: '1476136676000'}); + return new UserMetadata({createdAt: '1476136676000'} as any); }).not.to.throw(Error); }); it('should set creationTime and lastSignInTime to null when not provided', () => { - const metadata = new UserMetadata({}); + const metadata = new UserMetadata({} as any); expect(metadata.creationTime).to.be.null; expect(metadata.lastSignInTime).to.be.null; }); @@ -340,7 +642,7 @@ describe('UserMetadata', () => { it('should set creationTime to null when creationTime value is invalid', () => { const metadata = new UserMetadata({ createdAt: 'invalid', - }); + } as any); expect(metadata.creationTime).to.be.null; expect(metadata.lastSignInTime).to.be.null; }); @@ -349,7 +651,7 @@ describe('UserMetadata', () => { const metadata = new UserMetadata({ createdAt: '1476235905000', lastLoginAt: 'invalid', - }); + } as any); expect(metadata.lastSignInTime).to.be.null; }); }); @@ -387,7 +689,7 @@ describe('UserRecord', () => { describe('constructor', () => { it('should throw when no localId is provided', () => { expect(() => { - return new UserRecord({}); + return new UserRecord({} as any); }).to.throw(Error); }); @@ -447,7 +749,7 @@ describe('UserRecord', () => { it('should return expected photoURL', () => { expect(userRecord.photoURL).to.equal( - 'https://lh3.googleusercontent.com/1234567890/photo.jpg'); + 'https://lh3.googleusercontent.com/1234567890/photo.jpg'); }); it('should throw when modifying readonly photoURL property', () => { @@ -570,7 +872,7 @@ describe('UserRecord', () => { const metadata = new UserMetadata({ createdAt: '1476136676000', lastLoginAt: '1476235905000', - }); + } as any); expect(userRecord.metadata).to.deep.equal(metadata); }); @@ -579,7 +881,7 @@ describe('UserRecord', () => { (userRecord as any).metadata = new UserMetadata({ createdAt: new Date().toUTCString(), lastLoginAt: new Date().toUTCString(), - }); + } as any); }).to.throw(Error); }); @@ -626,13 +928,13 @@ describe('UserRecord', () => { it('should throw when modifying readonly providerData property', () => { expect(() => { (userRecord as any).providerData = [ - new UserInfo({ - providerId: 'google.com', - displayName: 'Jane Doe', - photoUrl: 'https://lh3.googleusercontent.com/00000000/photo.jpg', - email: 'janedoe@gmail.com', - rawId: '00000000', - }), + new UserInfo({ + providerId: 'google.com', + displayName: 'Jane Doe', + photoUrl: 'https://lh3.googleusercontent.com/00000000/photo.jpg', + email: 'janedoe@gmail.com', + rawId: '00000000', + }), ]; }).to.throw(Error); }); @@ -648,18 +950,80 @@ describe('UserRecord', () => { }); it('should return expected tenantId', () => { - const resp = deepCopy(getValidUserResponse('TENANT-ID')); + const resp = deepCopy(getValidUserResponse('TENANT-ID')) as GetAccountInfoUserResponse; const tenantUserRecord = new UserRecord(resp); expect(tenantUserRecord.tenantId).to.equal('TENANT-ID'); }); it('should throw when modifying readonly tenantId property', () => { expect(() => { - const resp = deepCopy(getValidUserResponse('TENANT-ID')); + const resp = deepCopy(getValidUserResponse('TENANT-ID')) as GetAccountInfoUserResponse; const tenantUserRecord = new UserRecord(resp); (tenantUserRecord as any).tenantId = 'OTHER-TENANT-ID'; }).to.throw(Error); }); + + it('should return expected multiFactor', () => { + const multiFactor = new MultiFactor({ + localId: 'uid123', + mfaInfo: [ + { + mfaEnrollmentId: 'enrollmentId1', + displayName: 'displayName1', + enrolledAt: now.toISOString(), + phoneInfo: '+16505551234', + }, + { + mfaEnrollmentId: 'enrollmentId2', + enrolledAt: now.toISOString(), + phoneInfo: '+16505556789', + }, + ], + }); + expect(userRecord.multiFactor).to.deep.equal(multiFactor); + expect(userRecord.multiFactor!.enrolledFactors.length).to.equal(2); + }); + + it('should return undefined multiFactor when not available', () => { + const validUserResponseWithoutMultiFactor = deepCopy(validUserResponse); + delete validUserResponseWithoutMultiFactor.mfaInfo; + const userRecordWithoutMultiFactor = new UserRecord(validUserResponseWithoutMultiFactor); + + expect(userRecordWithoutMultiFactor.multiFactor).to.be.undefined; + }); + + it('should throw when modifying readonly multiFactor property', () => { + expect(() => { + (userRecord as any).multiFactor = new MultiFactor({ + localId: 'uid123', + mfaInfo: [{ + mfaEnrollmentId: 'enrollmentId3', + displayName: 'displayName3', + enrolledAt: now.toISOString(), + phoneInfo: '+16505550000', + }], + }); + }).to.throw(Error); + }); + + it('should throw when modifying readonly multiFactor internals', () => { + expect(() => { + (userRecord.multiFactor!.enrolledFactors[0] as any).displayName = 'Modified'; + }).to.throw(Error); + + expect(() => { + (userRecord.multiFactor!.enrolledFactors as any)[0] = new PhoneMultiFactorInfo({ + mfaEnrollmentId: 'enrollmentId3', + displayName: 'displayName3', + enrolledAt: now.toISOString(), + phoneInfo: '+16505550000', + }); + }).to.throw(Error); + + expect(() => { + (userRecord.multiFactor as any).enrolledFactors = []; + }).to.throw(Error); + }); }); describe('toJSON', () => { @@ -676,7 +1040,7 @@ describe('UserRecord', () => { }); it('should return expected JSON object with tenant ID when available', () => { - const resp = deepCopy(getValidUserResponse('TENANT-ID')); + const resp = deepCopy(getValidUserResponse('TENANT-ID') as GetAccountInfoUserResponse); const tenantUserRecord = new UserRecord(resp); expect(tenantUserRecord.toJSON()).to.deep.equal(getUserJSON('TENANT-ID')); }); diff --git a/test/unit/database/database.spec.ts b/test/unit/database/database.spec.ts index a4dc3c7ca1..54d59432a3 100644 --- a/test/unit/database/database.spec.ts +++ b/test/unit/database/database.spec.ts @@ -152,8 +152,8 @@ describe('Database', () => { const rulesPath = '.settings/rules.json'; function callParamsForGet( - strict: boolean = false, - url: string = `https://databasename.firebaseio.com/${rulesPath}`, + strict = false, + url = `https://databasename.firebaseio.com/${rulesPath}`, ): HttpRequestConfig { const params: HttpRequestConfig = { @@ -305,7 +305,7 @@ describe('Database', () => { function callParamsForPut( data: string | Buffer | object, - url: string = `https://databasename.firebaseio.com/${rulesPath}`, + url = `https://databasename.firebaseio.com/${rulesPath}`, ): HttpRequestConfig { return { diff --git a/test/unit/firebase-app.spec.ts b/test/unit/firebase-app.spec.ts index 42fd427887..84155facde 100644 --- a/test/unit/firebase-app.spec.ts +++ b/test/unit/firebase-app.spec.ts @@ -71,8 +71,8 @@ describe('FirebaseApp', () => { beforeEach(() => { getTokenStub = sinon.stub(ServiceAccountCredential.prototype, 'getAccessToken').resolves({ - access_token: 'mock-access-token', - expires_in: 3600, + access_token: 'mock-access-token', // eslint-disable-line @typescript-eslint/camelcase + expires_in: 3600, // eslint-disable-line @typescript-eslint/camelcase }); clock = sinon.useFakeTimers(1000); @@ -694,8 +694,8 @@ describe('FirebaseApp', () => { it('returns a valid token given a well-formed custom credential implementation', () => { const oracle: GoogleOAuthAccessToken = { - access_token: 'This is a custom token', - expires_in: ONE_HOUR_IN_SECONDS, + access_token: 'This is a custom token', // eslint-disable-line @typescript-eslint/camelcase + expires_in: ONE_HOUR_IN_SECONDS, // eslint-disable-line @typescript-eslint/camelcase }; const credential = { getAccessToken: () => Promise.resolve(oracle), @@ -788,8 +788,8 @@ describe('FirebaseApp', () => { // Restore the stubbed getAccessToken() method. getTokenStub.restore(); getTokenStub = sinon.stub(ServiceAccountCredential.prototype, 'getAccessToken').resolves({ - access_token: 'mock-access-token', - expires_in: 3600, + access_token: 'mock-access-token', // eslint-disable-line @typescript-eslint/camelcase + expires_in: 3600, // eslint-disable-line @typescript-eslint/camelcase }); return mockApp.INTERNAL.getToken().then((token2) => { @@ -904,7 +904,7 @@ describe('FirebaseApp', () => { it('resets the proactive refresh timeout upon a force refresh', () => { // Force a token refresh. return mockApp.INTERNAL.getToken(true).then((token1) => { - // Forward the clock to five minutes and one second before expiry. + // Forward the clock to five minutes and one second before expiry. let expiryInMilliseconds = token1.expirationTime - Date.now(); clock.tick(expiryInMilliseconds - (5 * ONE_MINUTE_IN_MILLISECONDS) - 1000); @@ -941,8 +941,8 @@ describe('FirebaseApp', () => { getTokenStub.restore(); expect(mockApp.options.credential).to.exist; getTokenStub = sinon.stub(mockApp.options.credential!, 'getAccessToken').resolves({ - access_token: utils.generateRandomAccessToken(), - expires_in: 3 * 60 + 10, + access_token: utils.generateRandomAccessToken(), // eslint-disable-line @typescript-eslint/camelcase + expires_in: 3 * 60 + 10, // eslint-disable-line @typescript-eslint/camelcase }); // Expect the call count to initially be zero. expect(getTokenStub.callCount).to.equal(0); diff --git a/test/unit/firebase-namespace.spec.ts b/test/unit/firebase-namespace.spec.ts index 017701c2a7..37ce7deb06 100644 --- a/test/unit/firebase-namespace.spec.ts +++ b/test/unit/firebase-namespace.spec.ts @@ -502,7 +502,7 @@ describe('FirebaseNamespace', () => { it('should return a reference to Machine Learning type', () => { expect(firebaseNamespace.machineLearning.MachineLearning) - .to.be.deep.equal(MachineLearning); + .to.be.deep.equal(MachineLearning); }); }); @@ -655,7 +655,7 @@ describe('FirebaseNamespace', () => { it('should return a reference to ProjectManagement type', () => { expect(firebaseNamespace.projectManagement.ProjectManagement) - .to.be.deep.equal(ProjectManagement); + .to.be.deep.equal(ProjectManagement); }); }); @@ -689,7 +689,7 @@ describe('FirebaseNamespace', () => { it('should return a reference to SecurityRules type', () => { expect(firebaseNamespace.securityRules.SecurityRules) - .to.be.deep.equal(SecurityRules); + .to.be.deep.equal(SecurityRules); }); }); }); diff --git a/test/unit/firebase.spec.ts b/test/unit/firebase.spec.ts index 913f3c5067..4471ed630e 100644 --- a/test/unit/firebase.spec.ts +++ b/test/unit/firebase.spec.ts @@ -40,8 +40,8 @@ describe('Firebase', () => { before(() => { getTokenStub = sinon.stub(ServiceAccountCredential.prototype, 'getAccessToken').resolves({ - access_token: 'mock-access-token', - expires_in: 3600, + access_token: 'mock-access-token', // eslint-disable-line @typescript-eslint/camelcase + expires_in: 3600, // eslint-disable-line @typescript-eslint/camelcase }); }); @@ -129,8 +129,7 @@ describe('Firebase', () => { }); it('should initialize SDK given an application default credential', () => { - let credPath: string | undefined; - credPath = process.env.GOOGLE_APPLICATION_CREDENTIALS; + const credPath: string | undefined = process.env.GOOGLE_APPLICATION_CREDENTIALS; process.env.GOOGLE_APPLICATION_CREDENTIALS = path.resolve(__dirname, '../resources/mock.key.json'); firebaseAdmin.initializeApp({ credential: firebaseAdmin.credential.applicationDefault(), @@ -151,8 +150,8 @@ describe('Firebase', () => { getTokenStub.restore(); getTokenStub = sinon.stub(RefreshTokenCredential.prototype, 'getAccessToken') .resolves({ - access_token: 'mock-access-token', - expires_in: 3600, + access_token: 'mock-access-token', // eslint-disable-line @typescript-eslint/camelcase + expires_in: 3600, // eslint-disable-line @typescript-eslint/camelcase }); firebaseAdmin.initializeApp({ credential: firebaseAdmin.credential.refreshToken(mocks.refreshToken), diff --git a/test/unit/firestore/firestore.spec.ts b/test/unit/firestore/firestore.spec.ts index df72549b40..482211ffa0 100644 --- a/test/unit/firestore/firestore.spec.ts +++ b/test/unit/firestore/firestore.spec.ts @@ -38,6 +38,7 @@ describe('Firestore', () => { + 'credentials. Must initialize the SDK with a certificate credential or application default ' + 'credentials to use Cloud Firestore API.'; + // eslint-disable-next-line @typescript-eslint/no-var-requires const { version: firebaseVersion } = require('../../../package.json'); const defaultCredentialApps = [ { diff --git a/test/unit/instance-id/instance-id-request.spec.ts b/test/unit/instance-id/instance-id-request.spec.ts index 2b83642f11..7515f9cd7e 100644 --- a/test/unit/instance-id/instance-id-request.spec.ts +++ b/test/unit/instance-id/instance-id-request.spec.ts @@ -36,7 +36,7 @@ chai.use(chaiAsPromised); const expect = chai.expect; describe('FirebaseInstanceIdRequestHandler', () => { - const projectId: string = 'project_id'; + const projectId = 'project_id'; const mockAccessToken: string = utils.generateRandomAccessToken(); let stubs: sinon.SinonStub[] = []; let getTokenStub: sinon.SinonStub; @@ -97,52 +97,52 @@ describe('FirebaseInstanceIdRequestHandler', () => { }); it('should throw for HTTP 404 errors', () => { - const stub = sinon.stub(HttpClient.prototype, 'send') - .rejects(utils.errorFrom({}, 404)); - stubs.push(stub); - - const requestHandler = new FirebaseInstanceIdRequestHandler(mockApp); - return requestHandler.deleteInstanceId('test-iid') - .then(() => { - throw new Error('Unexpected success'); - }) - .catch((error) => { - expect(error.code).to.equal('instance-id/api-error'); - expect(error.message).to.equal('Instance ID "test-iid": Failed to find the instance ID.'); - }); + const stub = sinon.stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom({}, 404)); + stubs.push(stub); + + const requestHandler = new FirebaseInstanceIdRequestHandler(mockApp); + return requestHandler.deleteInstanceId('test-iid') + .then(() => { + throw new Error('Unexpected success'); + }) + .catch((error) => { + expect(error.code).to.equal('instance-id/api-error'); + expect(error.message).to.equal('Instance ID "test-iid": Failed to find the instance ID.'); + }); }); it('should throw for HTTP 409 errors', () => { - const stub = sinon.stub(HttpClient.prototype, 'send') - .rejects(utils.errorFrom({}, 409)); - stubs.push(stub); - - const requestHandler = new FirebaseInstanceIdRequestHandler(mockApp); - return requestHandler.deleteInstanceId('test-iid') - .then(() => { - throw new Error('Unexpected success'); - }) - .catch((error) => { - expect(error.code).to.equal('instance-id/api-error'); - expect(error.message).to.equal('Instance ID "test-iid": Already deleted.'); - }); + const stub = sinon.stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom({}, 409)); + stubs.push(stub); + + const requestHandler = new FirebaseInstanceIdRequestHandler(mockApp); + return requestHandler.deleteInstanceId('test-iid') + .then(() => { + throw new Error('Unexpected success'); + }) + .catch((error) => { + expect(error.code).to.equal('instance-id/api-error'); + expect(error.message).to.equal('Instance ID "test-iid": Already deleted.'); + }); }); it('should throw for unexpected HTTP errors', () => { - const expectedResult = {error: 'test error'}; - const stub = sinon.stub(HttpClient.prototype, 'send') - .rejects(utils.errorFrom(expectedResult, 511)); - stubs.push(stub); - - const requestHandler = new FirebaseInstanceIdRequestHandler(mockApp); - return requestHandler.deleteInstanceId('test-iid') - .then(() => { - throw new Error('Unexpected success'); - }) - .catch((error) => { - expect(error.code).to.equal('instance-id/api-error'); - expect(error.message).to.equal('test error'); - }); + const expectedResult = {error: 'test error'}; + const stub = sinon.stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom(expectedResult, 511)); + stubs.push(stub); + + const requestHandler = new FirebaseInstanceIdRequestHandler(mockApp); + return requestHandler.deleteInstanceId('test-iid') + .then(() => { + throw new Error('Unexpected success'); + }) + .catch((error) => { + expect(error.code).to.equal('instance-id/api-error'); + expect(error.message).to.equal('test error'); + }); }); }); }); diff --git a/test/unit/instance-id/instance-id.spec.ts b/test/unit/instance-id/instance-id.spec.ts index c3dc2fdb81..db4a9dcb10 100644 --- a/test/unit/instance-id/instance-id.spec.ts +++ b/test/unit/instance-id/instance-id.spec.ts @@ -164,7 +164,7 @@ describe('InstanceId', () => { .returns(Promise.resolve(null)); stubs.push(stub); return iid.deleteInstanceId(testInstanceId) - .then((result) => { + .then(() => { // Confirm underlying API called with expected parameters. expect(stub).to.have.been.calledOnce.and.calledWith(testInstanceId); }); @@ -176,7 +176,7 @@ describe('InstanceId', () => { .returns(Promise.reject(expectedError)); stubs.push(stub); return iid.deleteInstanceId(testInstanceId) - .then((result) => { + .then(() => { throw new Error('Unexpected success'); }, (error) => { // Confirm underlying API called with expected parameters. diff --git a/test/unit/machine-learning/machine-learning-api-client.spec.ts b/test/unit/machine-learning/machine-learning-api-client.spec.ts index 61dec42b66..b308164a8b 100644 --- a/test/unit/machine-learning/machine-learning-api-client.spec.ts +++ b/test/unit/machine-learning/machine-learning-api-client.spec.ts @@ -489,8 +489,8 @@ describe('MachineLearningApiClient', () => { it('should throw unknown-error when error code is not present', () => { const stub = sinon - .stub(HttpClient.prototype, 'send') - .rejects(utils.errorFrom({}, 404)); + .stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom({}, 404)); stubs.push(stub); const expected = new FirebaseMachineLearningError('unknown-error', 'Unknown server error: {}'); return apiClient.listModels() diff --git a/test/unit/machine-learning/machine-learning.spec.ts b/test/unit/machine-learning/machine-learning.spec.ts index a9956ef6f6..1a49b2cab1 100644 --- a/test/unit/machine-learning/machine-learning.spec.ts +++ b/test/unit/machine-learning/machine-learning.spec.ts @@ -136,7 +136,7 @@ describe('MachineLearning', () => { gcsTfliteUri: string; sizeBytes: number; }; - } + }; } = { done: true, response: MODEL_RESPONSE, @@ -148,7 +148,7 @@ describe('MachineLearning', () => { error?: { code: number; message: string; - } + }; response?: ModelResponse; } = { done: true, @@ -185,8 +185,8 @@ describe('MachineLearning', () => { const machineLearningAny: any = MachineLearning; return new machineLearningAny(invalidApp); }).to.throw( - 'First argument passed to admin.machineLearning() must be a valid Firebase app ' - + 'instance.'); + 'First argument passed to admin.machineLearning() must be a valid Firebase app ' + + 'instance.'); }); }); @@ -195,8 +195,8 @@ describe('MachineLearning', () => { const machineLearningAny: any = MachineLearning; return new machineLearningAny(); }).to.throw( - 'First argument passed to admin.machineLearning() must be a valid Firebase app ' - + 'instance.'); + 'First argument passed to admin.machineLearning() must be a valid Firebase app ' + + 'instance.'); }); it('should throw given invalid credential', () => { @@ -433,8 +433,8 @@ describe('MachineLearning', () => { .resolves(null); stubs.push(stub); return machineLearning.createModel(MODEL_OPTIONS_WITH_GCS) - .should.eventually.be.rejected.and.have.property( - 'message', 'Cannot read property \'done\' of null'); + .should.eventually.be.rejected.and.have.property( + 'message', 'Cannot read property \'done\' of null'); }); it('should reject when API response does not contain a name', () => { @@ -550,8 +550,8 @@ describe('MachineLearning', () => { .resolves(null); stubs.push(stub); return machineLearning.updateModel(MODEL_ID, MODEL_OPTIONS_WITH_GCS) - .should.eventually.be.rejected.and.have.property( - 'message', 'Cannot read property \'done\' of null'); + .should.eventually.be.rejected.and.have.property( + 'message', 'Cannot read property \'done\' of null'); }); it('should reject when API response does not contain a name', () => { @@ -654,8 +654,8 @@ describe('MachineLearning', () => { .resolves(null); stubs.push(stub); return machineLearning.publishModel(MODEL_ID) - .should.eventually.be.rejected.and.have.property( - 'message', 'Cannot read property \'done\' of null'); + .should.eventually.be.rejected.and.have.property( + 'message', 'Cannot read property \'done\' of null'); }); it('should reject when API response does not contain a name', () => { @@ -758,8 +758,8 @@ describe('MachineLearning', () => { .resolves(null); stubs.push(stub); return machineLearning.unpublishModel(MODEL_ID) - .should.eventually.be.rejected.and.have.property( - 'message', 'Cannot read property \'done\' of null'); + .should.eventually.be.rejected.and.have.property( + 'message', 'Cannot read property \'done\' of null'); }); it('should reject when API response does not contain a name', () => { diff --git a/test/unit/messaging/batch-requests.spec.ts b/test/unit/messaging/batch-requests.spec.ts index ae5b505d71..bcfee5ef74 100644 --- a/test/unit/messaging/batch-requests.spec.ts +++ b/test/unit/messaging/batch-requests.spec.ts @@ -16,7 +16,6 @@ 'use strict'; -import * as _ from 'lodash'; import * as chai from 'chai'; import * as sinon from 'sinon'; import * as sinonChai from 'sinon-chai'; @@ -34,6 +33,7 @@ chai.use(chaiAsPromised); const expect = chai.expect; function parseHttpRequest(text: string | Buffer): any { + // eslint-disable-next-line @typescript-eslint/no-var-requires const httpMessageParser = require('http-message-parser'); return httpMessageParser(text); } @@ -50,14 +50,14 @@ function getParsedPartData(obj: object): string { function createMultipartResponse(success: object[], failures: object[] = []): HttpResponse { const multipart: Buffer[] = []; success.forEach((part) => { - let payload: string = ''; + let payload = ''; payload += `HTTP/1.1 200 OK\r\n`; payload += `Content-type: application/json\r\n\r\n`; payload += `${JSON.stringify(part)}\r\n`; multipart.push(Buffer.from(payload, 'utf-8')); }); failures.forEach((part) => { - let payload: string = ''; + let payload = ''; payload += `HTTP/1.1 500 Internal Server Error\r\n`; payload += `Content-type: application/json\r\n\r\n`; payload += `${JSON.stringify(part)}\r\n`; @@ -242,7 +242,7 @@ describe('BatchRequestClient', () => { }); }); - function checkOutgoingRequest(stub: sinon.SinonStub, requests: SubRequest[]) { + function checkOutgoingRequest(stub: sinon.SinonStub, requests: SubRequest[]): void { expect(stub).to.have.been.calledOnce; const args: HttpRequestConfig = stub.getCall(0).args[0]; expect(args.method).to.equal('POST'); diff --git a/test/unit/messaging/messaging.spec.ts b/test/unit/messaging/messaging.spec.ts index 52219c4312..9bd41b374e 100644 --- a/test/unit/messaging/messaging.spec.ts +++ b/test/unit/messaging/messaging.spec.ts @@ -158,6 +158,8 @@ function mockErrorResponse( }); } +/* eslint-disable @typescript-eslint/camelcase */ + function mockSendToDeviceStringRequest(mockFailure = false): nock.Scope { let deviceResult: object = { message_id: `0:${ mocks.messaging.messageId }` }; if (mockFailure) { @@ -299,7 +301,7 @@ function mockTopicSubscriptionRequestWithError( }); } -function disableRetries(messaging: Messaging) { +function disableRetries(messaging: Messaging): void { (messaging as any).messagingRequestHandler.httpClient.retry = null; } @@ -482,7 +484,7 @@ describe('Messaging', () => { return messaging.send( {token: 'mock-token'}, ).should.eventually.be.rejectedWith('test error message') - .and.have.property('code', 'messaging/invalid-argument'); + .and.have.property('code', 'messaging/invalid-argument'); }); it('should fail when the backend server returns a detailed error with FCM error code', () => { @@ -502,7 +504,7 @@ describe('Messaging', () => { return messaging.send( {token: 'mock-token'}, ).should.eventually.be.rejectedWith('test error message') - .and.have.property('code', 'messaging/registration-token-not-registered'); + .and.have.property('code', 'messaging/registration-token-not-registered'); }); ['THIRD_PARTY_AUTH_ERROR', 'APNS_AUTH_ERROR'].forEach((errorCode) => { @@ -523,7 +525,7 @@ describe('Messaging', () => { return messaging.send( {token: 'mock-token'}, ).should.eventually.be.rejectedWith('test error message') - .and.have.property('code', 'messaging/third-party-auth-error'); + .and.have.property('code', 'messaging/third-party-auth-error'); }); }); @@ -538,7 +540,7 @@ describe('Messaging', () => { return messaging.send( {token: 'mock-token'}, ).should.eventually.be.rejectedWith('test error message') - .and.have.property('code', 'messaging/registration-token-not-registered'); + .and.have.property('code', 'messaging/registration-token-not-registered'); }); it('should fail when the backend server returns an unknown error', () => { @@ -561,13 +563,13 @@ describe('Messaging', () => { describe('sendAll()', () => { const validMessage: Message = {token: 'a'}; - function checkSendResponseSuccess(response: SendResponse, messageId: string) { + function checkSendResponseSuccess(response: SendResponse, messageId: string): void { expect(response.success).to.be.true; expect(response.messageId).to.equal(messageId); expect(response.error).to.be.undefined; } - function checkSendResponseFailure(response: SendResponse, code: string, msg?: string) { + function checkSendResponseFailure(response: SendResponse, code: string, msg?: string): void { expect(response.success).to.be.false; expect(response.messageId).to.be.undefined; expect(response.error).to.have.property('code', code); @@ -755,7 +757,7 @@ describe('Messaging', () => { return messaging.sendAll( [validMessage], ).should.eventually.be.rejectedWith('test error message') - .and.have.property('code', 'messaging/invalid-argument'); + .and.have.property('code', 'messaging/invalid-argument'); }); it('should fail when the backend server returns a detailed error with FCM error code', () => { @@ -775,7 +777,7 @@ describe('Messaging', () => { return messaging.sendAll( [validMessage], ).should.eventually.be.rejectedWith('test error message') - .and.have.property('code', 'messaging/registration-token-not-registered'); + .and.have.property('code', 'messaging/registration-token-not-registered'); }); it('should map server error code to client-side error', () => { @@ -789,7 +791,7 @@ describe('Messaging', () => { return messaging.sendAll( [validMessage], ).should.eventually.be.rejectedWith('test error message') - .and.have.property('code', 'messaging/registration-token-not-registered'); + .and.have.property('code', 'messaging/registration-token-not-registered'); }); it('should fail when the backend server returns an unknown error', () => { @@ -1049,7 +1051,7 @@ describe('Messaging', () => { return messaging.sendMulticast( {tokens: ['a']}, ).should.eventually.be.rejectedWith('test error message') - .and.have.property('code', 'messaging/invalid-argument'); + .and.have.property('code', 'messaging/invalid-argument'); }); it('should fail when the backend server returns a detailed error with FCM error code', () => { @@ -1069,7 +1071,7 @@ describe('Messaging', () => { return messaging.sendMulticast( {tokens: ['a']}, ).should.eventually.be.rejectedWith('test error message') - .and.have.property('code', 'messaging/registration-token-not-registered'); + .and.have.property('code', 'messaging/registration-token-not-registered'); }); it('should map server error code to client-side error', () => { @@ -1083,7 +1085,7 @@ describe('Messaging', () => { return messaging.sendMulticast( {tokens: ['a']}, ).should.eventually.be.rejectedWith('test error message') - .and.have.property('code', 'messaging/registration-token-not-registered'); + .and.have.property('code', 'messaging/registration-token-not-registered'); }); it('should fail when the backend server returns an unknown error', () => { @@ -1108,13 +1110,13 @@ describe('Messaging', () => { ).should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential'); }); - function checkSendResponseSuccess(response: SendResponse, messageId: string) { + function checkSendResponseSuccess(response: SendResponse, messageId: string): void { expect(response.success).to.be.true; expect(response.messageId).to.equal(messageId); expect(response.error).to.be.undefined; } - function checkSendResponseFailure(response: SendResponse, code: string, msg?: string) { + function checkSendResponseFailure(response: SendResponse, code: string, msg?: string): void { expect(response.success).to.be.false; expect(response.messageId).to.be.undefined; expect(response.error).to.have.property('code', code); @@ -1528,7 +1530,7 @@ describe('Messaging', () => { disableRetries(messaging); return messaging.sendToDeviceGroup( - mocks.messaging.notificationKey, + mocks.messaging.notificationKey, mocks.messaging.payload, ).should.eventually.be.rejected.and.have.property('code', expectedError); }); @@ -1779,7 +1781,7 @@ describe('Messaging', () => { disableRetries(messaging); return messaging.sendToTopic( - mocks.messaging.topic, + mocks.messaging.topic, mocks.messaging.payload, ).should.eventually.be.rejected.and.have.property('code', expectedError); }); @@ -1998,7 +2000,7 @@ describe('Messaging', () => { disableRetries(messaging); return messaging.sendToCondition( - mocks.messaging.condition, + mocks.messaging.condition, mocks.messaging.payload, ).should.eventually.be.rejected.and.have.property('code', expectedError); }); @@ -2776,9 +2778,9 @@ describe('Messaging', () => { const whitelistedOptionsKeys: { [name: string]: { - type: string, - underscoreCasedKey?: string, - }, + type: string; + underscoreCasedKey?: string; + }; } = { dryRun: { type: 'boolean', underscoreCasedKey: 'dry_run' }, priority: { type: 'string' }, @@ -2791,7 +2793,7 @@ describe('Messaging', () => { _.forEach(whitelistedOptionsKeys, ({ type, underscoreCasedKey }, camelCasedKey) => { let validValue: any; - let invalidValues: Array<{value: any, text: string}>; + let invalidValues: Array<{value: any; text: string}>; if (type === 'string') { invalidValues = [ { value: true, text: 'non-string' }, @@ -3596,7 +3598,7 @@ describe('Messaging', () => { }); }); - function tokenSubscriptionTests(methodName: string) { + function tokenSubscriptionTests(methodName: string): void { const invalidRegistrationTokensArgumentError = 'Registration token(s) provided to ' + `${methodName}() must be a non-empty string or a non-empty array`; diff --git a/test/unit/project-management/android-app.spec.ts b/test/unit/project-management/android-app.spec.ts index cd043c3484..d38c2600b7 100644 --- a/test/unit/project-management/android-app.spec.ts +++ b/test/unit/project-management/android-app.spec.ts @@ -100,22 +100,22 @@ describe('AndroidApp', () => { it('should propagate API errors', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getResource') - .returns(Promise.reject(EXPECTED_ERROR)); + .stub(ProjectManagementRequestHandler.prototype, 'getResource') + .returns(Promise.reject(EXPECTED_ERROR)); stubs.push(stub); return androidApp.getMetadata().should.eventually.be.rejected.and.equal(EXPECTED_ERROR); }); it('should throw with null API response', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getResource') - .returns(Promise.resolve(null)); + .stub(ProjectManagementRequestHandler.prototype, 'getResource') + .returns(Promise.resolve(null)); stubs.push(stub); return androidApp.getMetadata() - .should.eventually.be.rejected - .and.have.property( - 'message', - 'getMetadata()\'s responseData must be a non-null object. Response data: null'); + .should.eventually.be.rejected + .and.have.property( + 'message', + 'getMetadata()\'s responseData must be a non-null object. Response data: null'); }); const requiredFieldsList = ['name', 'appId', 'projectId', 'packageName']; @@ -125,22 +125,22 @@ describe('AndroidApp', () => { delete partialApiResponse[requiredField]; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getResource') - .returns(Promise.resolve(partialApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'getResource') + .returns(Promise.resolve(partialApiResponse)); stubs.push(stub); return androidApp.getMetadata() - .should.eventually.be.rejected - .and.have.property( - 'message', - `getMetadata()\'s responseData.${requiredField} must be a non-empty string. ` + .should.eventually.be.rejected + .and.have.property( + 'message', + `getMetadata()'s responseData.${requiredField} must be a non-empty string. ` + `Response data: ${JSON.stringify(partialApiResponse, null, 2)}`); }); }); it('should resolve with metadata on success', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getResource') - .returns(Promise.resolve(VALID_ANDROID_APP_METADATA_API_RESPONSE)); + .stub(ProjectManagementRequestHandler.prototype, 'getResource') + .returns(Promise.resolve(VALID_ANDROID_APP_METADATA_API_RESPONSE)); stubs.push(stub); return androidApp.getMetadata().should.eventually.deep.equal(VALID_ANDROID_APP_METADATA); }); @@ -151,17 +151,17 @@ describe('AndroidApp', () => { it('should propagate API errors', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'setDisplayName') - .returns(Promise.reject(EXPECTED_ERROR)); + .stub(ProjectManagementRequestHandler.prototype, 'setDisplayName') + .returns(Promise.reject(EXPECTED_ERROR)); stubs.push(stub); return androidApp.setDisplayName(newDisplayName) - .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); + .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); }); it('should resolve on success', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'setDisplayName') - .returns(Promise.resolve()); + .stub(ProjectManagementRequestHandler.prototype, 'setDisplayName') + .returns(Promise.resolve()); stubs.push(stub); return androidApp.setDisplayName(newDisplayName).should.eventually.be.fulfilled; }); @@ -190,23 +190,23 @@ describe('AndroidApp', () => { it('should propagate API errors', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getAndroidShaCertificates') - .returns(Promise.reject(EXPECTED_ERROR)); + .stub(ProjectManagementRequestHandler.prototype, 'getAndroidShaCertificates') + .returns(Promise.reject(EXPECTED_ERROR)); stubs.push(stub); return androidApp.getShaCertificates() - .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); + .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); }); it('should throw with null API response', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getAndroidShaCertificates') - .returns(Promise.resolve(null)); + .stub(ProjectManagementRequestHandler.prototype, 'getAndroidShaCertificates') + .returns(Promise.resolve(null)); stubs.push(stub); return androidApp.getShaCertificates() - .should.eventually.be.rejected - .and.have.property( - 'message', - 'getShaCertificates()\'s responseData must be a non-null object. Response data: ' + .should.eventually.be.rejected + .and.have.property( + 'message', + 'getShaCertificates()\'s responseData must be a non-null object. Response data: ' + 'null'); }); @@ -214,25 +214,25 @@ describe('AndroidApp', () => { const partialApiResponse = {}; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getAndroidShaCertificates') - .returns(Promise.resolve(partialApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'getAndroidShaCertificates') + .returns(Promise.resolve(partialApiResponse)); stubs.push(stub); return androidApp.getShaCertificates() - .should.eventually.deep.equal([]); + .should.eventually.deep.equal([]); }); it('should throw when API response has non-array "certificates" field', () => { const partialApiResponse = { certificates: 'none' }; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getAndroidShaCertificates') - .returns(Promise.resolve(partialApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'getAndroidShaCertificates') + .returns(Promise.resolve(partialApiResponse)); stubs.push(stub); return androidApp.getShaCertificates() - .should.eventually.be.rejected - .and.have.property( - 'message', - '"certificates" field must be present in the getShaCertificates() response data. ' + .should.eventually.be.rejected + .and.have.property( + 'message', + '"certificates" field must be present in the getShaCertificates() response data. ' + 'Response data: ' + JSON.stringify(partialApiResponse, null, 2)); }); @@ -243,14 +243,14 @@ describe('AndroidApp', () => { delete partialApiResponse.certificates[1][requiredField]; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getAndroidShaCertificates') - .returns(Promise.resolve(partialApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'getAndroidShaCertificates') + .returns(Promise.resolve(partialApiResponse)); stubs.push(stub); return androidApp.getShaCertificates() - .should.eventually.be.rejected - .and.have.property( - 'message', - `getShaCertificates()\'s responseData.certificates[].${requiredField} must be a ` + .should.eventually.be.rejected + .and.have.property( + 'message', + `getShaCertificates()'s responseData.certificates[].${requiredField} must be a ` + 'non-empty string. Response data: ' + JSON.stringify(partialApiResponse, null, 2)); }); @@ -258,11 +258,11 @@ describe('AndroidApp', () => { it('should resolve with metadata on success', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getAndroidShaCertificates') - .returns(Promise.resolve(VALID_ANDROID_CERTS_API_RESPONSE)); + .stub(ProjectManagementRequestHandler.prototype, 'getAndroidShaCertificates') + .returns(Promise.resolve(VALID_ANDROID_CERTS_API_RESPONSE)); stubs.push(stub); return androidApp.getShaCertificates() - .should.eventually.deep.equal(VALID_ANDROID_CERTS); + .should.eventually.deep.equal(VALID_ANDROID_CERTS); }); }); @@ -271,17 +271,17 @@ describe('AndroidApp', () => { it('should propagate API errors', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'addAndroidShaCertificate') - .returns(Promise.reject(EXPECTED_ERROR)); + .stub(ProjectManagementRequestHandler.prototype, 'addAndroidShaCertificate') + .returns(Promise.reject(EXPECTED_ERROR)); stubs.push(stub); return androidApp.addShaCertificate(certificateToAdd) - .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); + .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); }); it('should resolve on success', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'addAndroidShaCertificate') - .returns(Promise.resolve()); + .stub(ProjectManagementRequestHandler.prototype, 'addAndroidShaCertificate') + .returns(Promise.resolve()); stubs.push(stub); return androidApp.addShaCertificate(certificateToAdd).should.eventually.be.fulfilled; }); @@ -294,28 +294,28 @@ describe('AndroidApp', () => { it('should propagate API errors', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'deleteResource') - .returns(Promise.reject(EXPECTED_ERROR)); + .stub(ProjectManagementRequestHandler.prototype, 'deleteResource') + .returns(Promise.reject(EXPECTED_ERROR)); stubs.push(stub); return androidApp - .deleteShaCertificate(certificateToDeleteWithResourceName) - .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); + .deleteShaCertificate(certificateToDeleteWithResourceName) + .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); }); it('should fail on certificate without resourceName', () => { expect(() => androidApp.deleteShaCertificate(certificateToDelete)) - .to.throw(FirebaseProjectManagementError) - .with.property('code', 'project-management/invalid-argument'); + .to.throw(FirebaseProjectManagementError) + .with.property('code', 'project-management/invalid-argument'); }); it('should resolve on success', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'deleteResource') - .returns(Promise.resolve()); + .stub(ProjectManagementRequestHandler.prototype, 'deleteResource') + .returns(Promise.resolve()); stubs.push(stub); return androidApp - .deleteShaCertificate(certificateToDeleteWithResourceName) - .should.eventually.be.fulfilled; + .deleteShaCertificate(certificateToDeleteWithResourceName) + .should.eventually.be.fulfilled; }); }); @@ -327,22 +327,22 @@ describe('AndroidApp', () => { it('should propagate API errors', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getConfig') - .returns(Promise.reject(EXPECTED_ERROR)); + .stub(ProjectManagementRequestHandler.prototype, 'getConfig') + .returns(Promise.reject(EXPECTED_ERROR)); stubs.push(stub); return androidApp.getConfig().should.eventually.be.rejected.and.equal(EXPECTED_ERROR); }); it('should throw with null API response', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getConfig') - .returns(Promise.resolve(null)); + .stub(ProjectManagementRequestHandler.prototype, 'getConfig') + .returns(Promise.resolve(null)); stubs.push(stub); return androidApp.getConfig() - .should.eventually.be.rejected - .and.have.property( - 'message', - 'getConfig()\'s responseData must be a non-null object. Response data: null'); + .should.eventually.be.rejected + .and.have.property( + 'message', + 'getConfig()\'s responseData must be a non-null object. Response data: null'); }); it('should throw with non-base64 response.configFileContents', () => { @@ -350,21 +350,21 @@ describe('AndroidApp', () => { apiResponse.configFileContents = '1' + apiResponse.configFileContents; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getConfig') - .returns(Promise.resolve(apiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'getConfig') + .returns(Promise.resolve(apiResponse)); stubs.push(stub); return androidApp.getConfig() - .should.eventually.be.rejected - .and.have.property( - 'message', - `getConfig()\'s responseData.configFileContents must be a base64 string. ` + .should.eventually.be.rejected + .and.have.property( + 'message', + `getConfig()'s responseData.configFileContents must be a base64 string. ` + `Response data: ${JSON.stringify(apiResponse, null, 2)}`); }); it('should resolve with metadata on success', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getConfig') - .returns(Promise.resolve(VALID_ANDROID_CONFIG_API_RESPONSE)); + .stub(ProjectManagementRequestHandler.prototype, 'getConfig') + .returns(Promise.resolve(VALID_ANDROID_CONFIG_API_RESPONSE)); stubs.push(stub); return androidApp.getConfig().should.eventually.deep.equal(VALID_ANDROID_CONFIG); }); diff --git a/test/unit/project-management/ios-app.spec.ts b/test/unit/project-management/ios-app.spec.ts index ab0be5b033..ec300ea554 100644 --- a/test/unit/project-management/ios-app.spec.ts +++ b/test/unit/project-management/ios-app.spec.ts @@ -99,22 +99,22 @@ describe('IosApp', () => { it('should propagate API errors', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getResource') - .returns(Promise.reject(expectedError)); + .stub(ProjectManagementRequestHandler.prototype, 'getResource') + .returns(Promise.reject(expectedError)); stubs.push(stub); return iosApp.getMetadata().should.eventually.be.rejected.and.equal(expectedError); }); it('should throw with null API response', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getResource') - .returns(Promise.resolve(null)); + .stub(ProjectManagementRequestHandler.prototype, 'getResource') + .returns(Promise.resolve(null)); stubs.push(stub); return iosApp.getMetadata() - .should.eventually.be.rejected - .and.have.property( - 'message', - 'getMetadata()\'s responseData must be a non-null object. Response data: null'); + .should.eventually.be.rejected + .and.have.property( + 'message', + 'getMetadata()\'s responseData must be a non-null object. Response data: null'); }); const requiredFieldsList = ['name', 'appId', 'projectId', 'bundleId']; @@ -124,22 +124,22 @@ describe('IosApp', () => { delete partialApiResponse[requiredField]; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getResource') - .returns(Promise.resolve(partialApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'getResource') + .returns(Promise.resolve(partialApiResponse)); stubs.push(stub); return iosApp.getMetadata() - .should.eventually.be.rejected - .and.have.property( - 'message', - `getMetadata()\'s responseData.${requiredField} must be a non-empty string. ` + .should.eventually.be.rejected + .and.have.property( + 'message', + `getMetadata()'s responseData.${requiredField} must be a non-empty string. ` + `Response data: ${JSON.stringify(partialApiResponse, null, 2)}`); }); }); it('should resolve with metadata on success', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getResource') - .returns(Promise.resolve(VALID_IOS_APP_METADATA_API_RESPONSE)); + .stub(ProjectManagementRequestHandler.prototype, 'getResource') + .returns(Promise.resolve(VALID_IOS_APP_METADATA_API_RESPONSE)); stubs.push(stub); return iosApp.getMetadata().should.eventually.deep.equal(VALID_IOS_APP_METADATA); }); @@ -150,17 +150,17 @@ describe('IosApp', () => { it('should propagate API errors', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'setDisplayName') - .returns(Promise.reject(EXPECTED_ERROR)); + .stub(ProjectManagementRequestHandler.prototype, 'setDisplayName') + .returns(Promise.reject(EXPECTED_ERROR)); stubs.push(stub); return iosApp.setDisplayName(newDisplayName) - .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); + .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); }); it('should resolve on success', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'setDisplayName') - .returns(Promise.resolve()); + .stub(ProjectManagementRequestHandler.prototype, 'setDisplayName') + .returns(Promise.resolve()); stubs.push(stub); return iosApp.setDisplayName(newDisplayName).should.eventually.be.fulfilled; }); @@ -174,22 +174,22 @@ describe('IosApp', () => { it('should propagate API errors', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getConfig') - .returns(Promise.reject(EXPECTED_ERROR)); + .stub(ProjectManagementRequestHandler.prototype, 'getConfig') + .returns(Promise.reject(EXPECTED_ERROR)); stubs.push(stub); return iosApp.getConfig().should.eventually.be.rejected.and.equal(EXPECTED_ERROR); }); it('should throw with null API response', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getConfig') - .returns(Promise.resolve(null)); + .stub(ProjectManagementRequestHandler.prototype, 'getConfig') + .returns(Promise.resolve(null)); stubs.push(stub); return iosApp.getConfig() - .should.eventually.be.rejected - .and.have.property( - 'message', - 'getConfig()\'s responseData must be a non-null object. Response data: null'); + .should.eventually.be.rejected + .and.have.property( + 'message', + 'getConfig()\'s responseData must be a non-null object. Response data: null'); }); it('should throw with non-base64 response.configFileContents', () => { @@ -197,21 +197,21 @@ describe('IosApp', () => { apiResponse.configFileContents = '1' + apiResponse.configFileContents; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getConfig') - .returns(Promise.resolve(apiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'getConfig') + .returns(Promise.resolve(apiResponse)); stubs.push(stub); return iosApp.getConfig() - .should.eventually.be.rejected - .and.have.property( - 'message', - `getConfig()\'s responseData.configFileContents must be a base64 string. ` + .should.eventually.be.rejected + .and.have.property( + 'message', + `getConfig()'s responseData.configFileContents must be a base64 string. ` + `Response data: ${JSON.stringify(apiResponse, null, 2)}`); }); it('should resolve with metadata on success', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'getConfig') - .returns(Promise.resolve(VALID_IOS_CONFIG_API_RESPONSE)); + .stub(ProjectManagementRequestHandler.prototype, 'getConfig') + .returns(Promise.resolve(VALID_IOS_CONFIG_API_RESPONSE)); stubs.push(stub); return iosApp.getConfig().should.eventually.deep.equal(VALID_IOS_CONFIG); }); diff --git a/test/unit/project-management/project-management-api-request.spec.ts b/test/unit/project-management/project-management-api-request.spec.ts index 87a3e334af..f2978b4a34 100644 --- a/test/unit/project-management/project-management-api-request.spec.ts +++ b/test/unit/project-management/project-management-api-request.spec.ts @@ -40,18 +40,18 @@ const VALID_SHA_1_HASH = '0123456789abcdefABCDEF012345678901234567'; describe('ProjectManagementRequestHandler', () => { const HOST = 'firebase.googleapis.com'; const PORT = 443; - const PROJECT_RESOURCE_NAME: string = 'projects/test-project-id'; - const APP_ID: string = 'test-app-id'; - const APP_ID_ANDROID: string = 'test-android-app-id'; - const APP_ID_IOS: string = 'test-ios-app-id'; - const ANDROID_APP_RESOURCE_NAME: string = `projects/-/androidApp/${APP_ID}`; - const IOS_APP_RESOURCE_NAME: string = `projects/-/iosApp/${APP_ID}`; - const PACKAGE_NAME: string = 'test-package-name'; - const BUNDLE_ID: string = 'test-bundle-id'; - const DISPLAY_NAME: string = 'test-display-name'; - const DISPLAY_NAME_ANDROID: string = 'test-display-name-android'; - const DISPLAY_NAME_IOS: string = 'test-display-name-ios'; - const OPERATION_RESOURCE_NAME: string = 'test-operation-resource-name'; + const PROJECT_RESOURCE_NAME = 'projects/test-project-id'; + const APP_ID = 'test-app-id'; + const APP_ID_ANDROID = 'test-android-app-id'; + const APP_ID_IOS = 'test-ios-app-id'; + const ANDROID_APP_RESOURCE_NAME = `projects/-/androidApp/${APP_ID}`; + const IOS_APP_RESOURCE_NAME = `projects/-/iosApp/${APP_ID}`; + const PACKAGE_NAME = 'test-package-name'; + const BUNDLE_ID = 'test-bundle-id'; + const DISPLAY_NAME = 'test-display-name'; + const DISPLAY_NAME_ANDROID = 'test-display-name-android'; + const DISPLAY_NAME_IOS = 'test-display-name-ios'; + const OPERATION_RESOURCE_NAME = 'test-operation-resource-name'; const mockAccessToken: string = utils.generateRandomAccessToken(); let stubs: sinon.SinonStub[] = []; @@ -84,7 +84,7 @@ describe('ProjectManagementRequestHandler', () => { return mockApp.delete(); }); - function testHttpErrors(callback: () => Promise) { + function testHttpErrors(callback: () => Promise): void { const errorCodeMap: any = { 400: 'project-management/invalid-argument', 401: 'project-management/authentication-error', @@ -94,28 +94,28 @@ describe('ProjectManagementRequestHandler', () => { 503: 'project-management/service-unavailable', }; Object.keys(errorCodeMap).forEach((errorCode) => { - if (!errorCodeMap.hasOwnProperty(errorCode)) { + if (!Object.prototype.hasOwnProperty.call(errorCodeMap, errorCode)) { return; } it(`should throw for HTTP ${errorCode} errors`, () => { - const stub = sinon.stub(HttpClient.prototype, 'send') - .rejects(utils.errorFrom({}, parseInt(errorCode, 10))); - stubs.push(stub); + const stub = sinon.stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom({}, parseInt(errorCode, 10))); + stubs.push(stub); - return callback() - .should.eventually.be.rejected - .and.have.property('code', errorCodeMap[errorCode]); + return callback() + .should.eventually.be.rejected + .and.have.property('code', errorCodeMap[errorCode]); }); }); it('should throw for HTTP unknown errors', () => { - const stub = sinon.stub(HttpClient.prototype, 'send') - .rejects(utils.errorFrom({}, 1337)); - stubs.push(stub); + const stub = sinon.stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom({}, 1337)); + stubs.push(stub); - return callback() - .should.eventually.be.rejected - .and.have.property('code', 'project-management/unknown-error'); + return callback() + .should.eventually.be.rejected + .and.have.property('code', 'project-management/unknown-error'); }); } @@ -139,22 +139,22 @@ describe('ProjectManagementRequestHandler', () => { }; const stub = sinon.stub(HttpClient.prototype, 'send') - .resolves(utils.responseFrom(expectedResult)); + .resolves(utils.responseFrom(expectedResult)); stubs.push(stub); const url = `https://${HOST}:${PORT}/v1beta1/${PROJECT_RESOURCE_NAME}/androidApps?page_size=100`; return requestHandler.listAndroidApps(PROJECT_RESOURCE_NAME) - .then((result) => { - expect(result).to.deep.equal(expectedResult); - expect(stub).to.have.been.calledOnce.and.calledWith({ - method: 'GET', - url, - data: null, - headers: expectedHeaders, - timeout: 10000, - }); + .then((result) => { + expect(result).to.deep.equal(expectedResult); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'GET', + url, + data: null, + headers: expectedHeaders, + timeout: 10000, }); + }); }); }); @@ -170,21 +170,21 @@ describe('ProjectManagementRequestHandler', () => { }; const stub = sinon.stub(HttpClient.prototype, 'send') - .resolves(utils.responseFrom(expectedResult)); + .resolves(utils.responseFrom(expectedResult)); stubs.push(stub); const url = `https://${HOST}:${PORT}/v1beta1/${PROJECT_RESOURCE_NAME}/iosApps?page_size=100`; return requestHandler.listIosApps(PROJECT_RESOURCE_NAME) - .then((result) => { - expect(result).to.deep.equal(expectedResult); - expect(stub).to.have.been.calledOnce.and.calledWith({ - method: 'GET', - url, - data: null, - headers: expectedHeaders, - timeout: 10000, - }); + .then((result) => { + expect(result).to.deep.equal(expectedResult); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'GET', + url, + data: null, + headers: expectedHeaders, + timeout: 10000, }); + }); }); }); @@ -207,22 +207,22 @@ describe('ProjectManagementRequestHandler', () => { }; const stub = sinon.stub(HttpClient.prototype, 'send') - .resolves(utils.responseFrom(expectedResult)); + .resolves(utils.responseFrom(expectedResult)); stubs.push(stub); const url = `https://${HOST}:${PORT}/v1beta1/${PROJECT_RESOURCE_NAME}:searchApps?page_size=100`; return requestHandler.listAppMetadata(PROJECT_RESOURCE_NAME) - .then((result) => { - expect(result).to.deep.equal(expectedResult); - expect(stub).to.have.been.calledOnce.and.calledWith({ - method: 'GET', - url, - data: null, - headers: expectedHeaders, - timeout: 10000, - }); + .then((result) => { + expect(result).to.deep.equal(expectedResult); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'GET', + url, + data: null, + headers: expectedHeaders, + timeout: 10000, }); + }); }); }); @@ -234,10 +234,10 @@ describe('ProjectManagementRequestHandler', () => { stubs.push(stub); return requestHandler.createAndroidApp(PROJECT_RESOURCE_NAME, PACKAGE_NAME, DISPLAY_NAME) - .should.eventually.be.rejected - .and.have.property( - 'message', - 'createAndroidApp\'s responseData.name must be a non-empty string. Response data: ' + .should.eventually.be.rejected + .and.have.property( + 'message', + 'createAndroidApp\'s responseData.name must be a non-empty string. Response data: ' + '{}'); }); @@ -253,13 +253,13 @@ describe('ProjectManagementRequestHandler', () => { }; const stub = sinon.stub(HttpClient.prototype, 'send') - .onFirstCall().resolves(utils.responseFrom(initialResult)) - .onSecondCall().resolves(utils.responseFrom(pollErrorResult)); + .onFirstCall().resolves(utils.responseFrom(initialResult)) + .onSecondCall().resolves(utils.responseFrom(pollErrorResult)); stubs.push(stub); return requestHandler.createAndroidApp(PROJECT_RESOURCE_NAME, PACKAGE_NAME, DISPLAY_NAME) - .should.eventually.be.rejected - .and.have.property('code', 'project-management/already-exists'); + .should.eventually.be.rejected + .and.have.property('code', 'project-management/already-exists'); }); it('should propagate polling API response thrown errors', () => { @@ -267,13 +267,13 @@ describe('ProjectManagementRequestHandler', () => { const pollError = 'second-poll-error'; const stub = sinon.stub(HttpClient.prototype, 'send') - .onFirstCall().resolves(utils.responseFrom(initialResult)) - .onSecondCall().returns(Promise.reject(pollError)); + .onFirstCall().resolves(utils.responseFrom(initialResult)) + .onSecondCall().returns(Promise.reject(pollError)); stubs.push(stub); return requestHandler.createAndroidApp(PROJECT_RESOURCE_NAME, PACKAGE_NAME, DISPLAY_NAME) - .should.eventually.be.rejected - .and.equal(pollError); + .should.eventually.be.rejected + .and.equal(pollError); }); it('should succeed after multiple polls', () => { @@ -287,9 +287,9 @@ describe('ProjectManagementRequestHandler', () => { }; const stub = sinon.stub(HttpClient.prototype, 'send') - .onFirstCall().resolves(utils.responseFrom(initialResult)) - .onSecondCall().resolves(utils.responseFrom(firstPollResult)) - .onThirdCall().resolves(utils.responseFrom(secondPollResult)); + .onFirstCall().resolves(utils.responseFrom(initialResult)) + .onSecondCall().resolves(utils.responseFrom(firstPollResult)) + .onThirdCall().resolves(utils.responseFrom(secondPollResult)); stubs.push(stub); const initialUrl = `https://${HOST}:${PORT}/v1beta1/${PROJECT_RESOURCE_NAME}/androidApps`; @@ -301,25 +301,25 @@ describe('ProjectManagementRequestHandler', () => { const pollingUrl = `https://${HOST}:${PORT}/v1/${OPERATION_RESOURCE_NAME}`; return requestHandler.createAndroidApp(PROJECT_RESOURCE_NAME, PACKAGE_NAME, DISPLAY_NAME) - .then((result) => { - expect(result).to.equal(expectedJsonResponse); - expect(stub) - .to.have.been.calledThrice - .and.calledWith({ - method: 'POST', - url: initialUrl, - data: initialData, - headers: expectedHeaders, - timeout: 10000, - }) - .and.calledWith({ - method: 'GET', - url: pollingUrl, - data: null, - headers: expectedHeaders, - timeout: 10000, - }); - }); + .then((result) => { + expect(result).to.equal(expectedJsonResponse); + expect(stub) + .to.have.been.calledThrice + .and.calledWith({ + method: 'POST', + url: initialUrl, + data: initialData, + headers: expectedHeaders, + timeout: 10000, + }) + .and.calledWith({ + method: 'GET', + url: pollingUrl, + data: null, + headers: expectedHeaders, + timeout: 10000, + }); + }); }); }); @@ -331,10 +331,10 @@ describe('ProjectManagementRequestHandler', () => { stubs.push(stub); return requestHandler.createIosApp(PROJECT_RESOURCE_NAME, BUNDLE_ID, DISPLAY_NAME) - .should.eventually.be.rejected - .and.have.property( - 'message', - 'createIosApp\'s responseData.name must be a non-empty string. Response data: {}'); + .should.eventually.be.rejected + .and.have.property( + 'message', + 'createIosApp\'s responseData.name must be a non-empty string. Response data: {}'); }); it('should propagate polling API response returned errors', () => { @@ -349,13 +349,13 @@ describe('ProjectManagementRequestHandler', () => { }; const stub = sinon.stub(HttpClient.prototype, 'send') - .onFirstCall().resolves(utils.responseFrom(initialResult)) - .onSecondCall().resolves(utils.responseFrom(pollErrorResult)); + .onFirstCall().resolves(utils.responseFrom(initialResult)) + .onSecondCall().resolves(utils.responseFrom(pollErrorResult)); stubs.push(stub); return requestHandler.createIosApp(PROJECT_RESOURCE_NAME, BUNDLE_ID, DISPLAY_NAME) - .should.eventually.be.rejected - .and.have.property('code', 'project-management/already-exists'); + .should.eventually.be.rejected + .and.have.property('code', 'project-management/already-exists'); }); it('should propagate polling API response thrown errors', () => { @@ -363,13 +363,13 @@ describe('ProjectManagementRequestHandler', () => { const pollError = 'second-poll-error'; const stub = sinon.stub(HttpClient.prototype, 'send') - .onFirstCall().resolves(utils.responseFrom(initialResult)) - .onSecondCall().returns(Promise.reject(pollError)); + .onFirstCall().resolves(utils.responseFrom(initialResult)) + .onSecondCall().returns(Promise.reject(pollError)); stubs.push(stub); return requestHandler.createIosApp(PROJECT_RESOURCE_NAME, BUNDLE_ID, DISPLAY_NAME) - .should.eventually.be.rejected - .and.equal(pollError); + .should.eventually.be.rejected + .and.equal(pollError); }); it('should succeed after multiple polls', () => { @@ -383,9 +383,9 @@ describe('ProjectManagementRequestHandler', () => { }; const stub = sinon.stub(HttpClient.prototype, 'send') - .onFirstCall().resolves(utils.responseFrom(initialResult)) - .onSecondCall().resolves(utils.responseFrom(firstPollResult)) - .onThirdCall().resolves(utils.responseFrom(secondPollResult)); + .onFirstCall().resolves(utils.responseFrom(initialResult)) + .onSecondCall().resolves(utils.responseFrom(firstPollResult)) + .onThirdCall().resolves(utils.responseFrom(secondPollResult)); stubs.push(stub); const initialUrl = `https://${HOST}:${PORT}/v1beta1/${PROJECT_RESOURCE_NAME}/iosApps`; @@ -397,25 +397,25 @@ describe('ProjectManagementRequestHandler', () => { const pollingUrl = `https://${HOST}:${PORT}/v1/${OPERATION_RESOURCE_NAME}`; return requestHandler.createIosApp(PROJECT_RESOURCE_NAME, BUNDLE_ID, DISPLAY_NAME) - .then((result) => { - expect(result).to.equal(expectedJsonResponse); - expect(stub) - .to.have.been.calledThrice - .and.calledWith({ - method: 'POST', - url: initialUrl, - data: initialData, - headers: expectedHeaders, - timeout: 10000, - }) - .and.calledWith({ - method: 'GET', - url: pollingUrl, - data: null, - headers: expectedHeaders, - timeout: 10000, - }); - }); + .then((result) => { + expect(result).to.equal(expectedJsonResponse); + expect(stub) + .to.have.been.calledThrice + .and.calledWith({ + method: 'POST', + url: initialUrl, + data: initialData, + headers: expectedHeaders, + timeout: 10000, + }) + .and.calledWith({ + method: 'GET', + url: pollingUrl, + data: null, + headers: expectedHeaders, + timeout: 10000, + }); + }); }); }); @@ -423,7 +423,7 @@ describe('ProjectManagementRequestHandler', () => { const newDisplayName = 'test-new-display-name'; testHttpErrors( - () => requestHandler.setDisplayName(ANDROID_APP_RESOURCE_NAME, newDisplayName)); + () => requestHandler.setDisplayName(ANDROID_APP_RESOURCE_NAME, newDisplayName)); it('should succeed', () => { const stub = sinon.stub(HttpClient.prototype, 'send').resolves(utils.responseFrom({})); @@ -435,15 +435,15 @@ describe('ProjectManagementRequestHandler', () => { displayName: newDisplayName, }; return requestHandler.setDisplayName(ANDROID_APP_RESOURCE_NAME, newDisplayName) - .then(() => { - expect(stub).to.have.been.calledOnce.and.calledWith({ - method: 'PATCH', - url, - data: requestData, - headers: expectedHeaders, - timeout: 10000, - }); + .then(() => { + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'PATCH', + url, + data: requestData, + headers: expectedHeaders, + timeout: 10000, }); + }); }); }); @@ -454,21 +454,21 @@ describe('ProjectManagementRequestHandler', () => { const expectedResult: any = { certificates: [] }; const stub = sinon.stub(HttpClient.prototype, 'send') - .resolves(utils.responseFrom(expectedResult)); + .resolves(utils.responseFrom(expectedResult)); stubs.push(stub); const url = `https://${HOST}:${PORT}/v1beta1/${ANDROID_APP_RESOURCE_NAME}/sha`; return requestHandler.getAndroidShaCertificates(ANDROID_APP_RESOURCE_NAME) - .then((result) => { - expect(result).to.deep.equal(expectedResult); - expect(stub).to.have.been.calledOnce.and.calledWith({ - method: 'GET', - url, - data: null, - headers: expectedHeaders, - timeout: 10000, - }); + .then((result) => { + expect(result).to.deep.equal(expectedResult); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'GET', + url, + data: null, + headers: expectedHeaders, + timeout: 10000, }); + }); }); }); @@ -476,7 +476,7 @@ describe('ProjectManagementRequestHandler', () => { const certificateToAdd = new ShaCertificate(VALID_SHA_1_HASH); testHttpErrors( - () => requestHandler.addAndroidShaCertificate(ANDROID_APP_RESOURCE_NAME, certificateToAdd)); + () => requestHandler.addAndroidShaCertificate(ANDROID_APP_RESOURCE_NAME, certificateToAdd)); it('should succeed', () => { const stub = sinon.stub(HttpClient.prototype, 'send').resolves(utils.responseFrom({})); @@ -488,15 +488,15 @@ describe('ProjectManagementRequestHandler', () => { certType: 'SHA_1', }; return requestHandler.addAndroidShaCertificate(ANDROID_APP_RESOURCE_NAME, certificateToAdd) - .then(() => { - expect(stub).to.have.been.calledOnce.and.calledWith({ - method: 'POST', - url, - data: requestData, - headers: expectedHeaders, - timeout: 10000, - }); + .then(() => { + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url, + data: requestData, + headers: expectedHeaders, + timeout: 10000, }); + }); }); }); @@ -509,21 +509,21 @@ describe('ProjectManagementRequestHandler', () => { }; const stub = sinon.stub(HttpClient.prototype, 'send') - .resolves(utils.responseFrom(expectedResult)); + .resolves(utils.responseFrom(expectedResult)); stubs.push(stub); const url = `https://${HOST}:${PORT}/v1beta1/${ANDROID_APP_RESOURCE_NAME}/config`; return requestHandler.getConfig(ANDROID_APP_RESOURCE_NAME) - .then((result) => { - expect(result).to.deep.equal(expectedResult); - expect(stub).to.have.been.calledOnce.and.calledWith({ - method: 'GET', - url, - data: null, - headers: expectedHeaders, - timeout: 10000, - }); + .then((result) => { + expect(result).to.deep.equal(expectedResult); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'GET', + url, + data: null, + headers: expectedHeaders, + timeout: 10000, }); + }); }); }); @@ -536,21 +536,21 @@ describe('ProjectManagementRequestHandler', () => { const expectedResult = { success: true }; const stub = sinon.stub(HttpClient.prototype, 'send') - .resolves(utils.responseFrom(expectedResult)); + .resolves(utils.responseFrom(expectedResult)); stubs.push(stub); const url = `https://${HOST}:${PORT}/v1beta1/${resourceName}`; return requestHandler.getResource(resourceName) - .then((result) => { - expect(result).to.deep.equal(expectedResult); - expect(stub).to.have.been.calledOnce.and.calledWith({ - method: 'GET', - url, - data: null, - headers: expectedHeaders, - timeout: 10000, - }); + .then((result) => { + expect(result).to.deep.equal(expectedResult); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'GET', + url, + data: null, + headers: expectedHeaders, + timeout: 10000, }); + }); }); }); @@ -565,15 +565,15 @@ describe('ProjectManagementRequestHandler', () => { const url = `https://${HOST}:${PORT}/v1beta1/${resourceName}`; return requestHandler.deleteResource(resourceName) - .then(() => { - expect(stub).to.have.been.calledOnce.and.calledWith({ - method: 'DELETE', - url, - data: null, - headers: expectedHeaders, - timeout: 10000, - }); + .then(() => { + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'DELETE', + url, + data: null, + headers: expectedHeaders, + timeout: 10000, }); + }); }); }); }); diff --git a/test/unit/project-management/project-management.spec.ts b/test/unit/project-management/project-management.spec.ts index cbf391f141..bcd02f6d71 100644 --- a/test/unit/project-management/project-management.spec.ts +++ b/test/unit/project-management/project-management.spec.ts @@ -31,12 +31,12 @@ import { AppPlatform, AppMetadata } from '../../../src/project-management/app-me const expect = chai.expect; const APP_ID = 'test-app-id'; -const APP_ID_ANDROID: string = 'test-app-id-android'; -const APP_ID_IOS: string = 'test-app-id-ios'; +const APP_ID_ANDROID = 'test-app-id-android'; +const APP_ID_IOS = 'test-app-id-ios'; const PACKAGE_NAME = 'test-package-name'; const BUNDLE_ID = 'test-bundle-id'; -const DISPLAY_NAME_ANDROID: string = 'test-display-name-android'; -const DISPLAY_NAME_IOS: string = 'test-display-name-ios'; +const DISPLAY_NAME_ANDROID = 'test-display-name-android'; +const DISPLAY_NAME_IOS = 'test-display-name-ios'; const EXPECTED_ERROR = new FirebaseProjectManagementError('internal-error', 'message'); const RESOURCE_NAME = 'projects/test/resources-name'; const RESOURCE_NAME_ANDROID = 'projects/test/resources-name:android'; @@ -76,7 +76,7 @@ describe('ProjectManagement', () => { const projectManagementAny: any = ProjectManagement; return new projectManagementAny(invalidApp); }).to.throw( - 'First argument passed to admin.projectManagement() must be a valid Firebase app ' + 'First argument passed to admin.projectManagement() must be a valid Firebase app ' + 'instance.'); }); }); @@ -86,7 +86,7 @@ describe('ProjectManagement', () => { const projectManagementAny: any = ProjectManagement; return new projectManagementAny(); }).to.throw( - 'First argument passed to admin.projectManagement() must be a valid Firebase app ' + 'First argument passed to admin.projectManagement() must be a valid Firebase app ' + 'instance.'); }); @@ -116,48 +116,48 @@ describe('ProjectManagement', () => { describe('listAndroidApps', () => { it('should propagate API errors', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listAndroidApps') - .returns(Promise.reject(EXPECTED_ERROR)); + .stub(ProjectManagementRequestHandler.prototype, 'listAndroidApps') + .returns(Promise.reject(EXPECTED_ERROR)); stubs.push(stub); return projectManagement.listAndroidApps() - .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); + .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); }); it('should throw with null API response', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listAndroidApps') - .returns(Promise.resolve(null)); + .stub(ProjectManagementRequestHandler.prototype, 'listAndroidApps') + .returns(Promise.resolve(null)); stubs.push(stub); return projectManagement.listAndroidApps() - .should.eventually.be.rejected - .and.have.property( - 'message', - 'listAndroidApps()\'s responseData must be a non-null object. Response data: null'); + .should.eventually.be.rejected + .and.have.property( + 'message', + 'listAndroidApps()\'s responseData must be a non-null object. Response data: null'); }); it('should return empty array when API response missing "apps" field', () => { const partialApiResponse = {}; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listAndroidApps') - .returns(Promise.resolve(partialApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'listAndroidApps') + .returns(Promise.resolve(partialApiResponse)); stubs.push(stub); return projectManagement.listAndroidApps() - .should.eventually.deep.equal([]); + .should.eventually.deep.equal([]); }); it('should throw when API response has non-array "apps" field', () => { const partialApiResponse = { apps: 'none' }; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listAndroidApps') - .returns(Promise.resolve(partialApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'listAndroidApps') + .returns(Promise.resolve(partialApiResponse)); stubs.push(stub); return projectManagement.listAndroidApps() - .should.eventually.be.rejected - .and.have.property( - 'message', - '"apps" field must be present in the listAndroidApps() response data. Response data: ' + .should.eventually.be.rejected + .and.have.property( + 'message', + '"apps" field must be present in the listAndroidApps() response data. Response data: ' + JSON.stringify(partialApiResponse, null, 2)); }); @@ -167,14 +167,14 @@ describe('ProjectManagement', () => { }; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listAndroidApps') - .returns(Promise.resolve(partialApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'listAndroidApps') + .returns(Promise.resolve(partialApiResponse)); stubs.push(stub); return projectManagement.listAndroidApps() - .should.eventually.be.rejected - .and.have.property( - 'message', - '"apps[].appId" field must be present in the listAndroidApps() response data. ' + .should.eventually.be.rejected + .and.have.property( + 'message', + '"apps[].appId" field must be present in the listAndroidApps() response data. ' + `Response data: ${JSON.stringify(partialApiResponse, null, 2)}`); }); @@ -185,11 +185,11 @@ describe('ProjectManagement', () => { }; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listAndroidApps') - .returns(Promise.resolve(validListAndroidAppsApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'listAndroidApps') + .returns(Promise.resolve(validListAndroidAppsApiResponse)); stubs.push(stub); return projectManagement.listAndroidApps() - .should.eventually.deep.equal(validAndroidApps); + .should.eventually.deep.equal(validAndroidApps); }); }); @@ -200,48 +200,48 @@ describe('ProjectManagement', () => { it('should propagate API errors', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listIosApps') - .returns(Promise.reject(EXPECTED_ERROR)); + .stub(ProjectManagementRequestHandler.prototype, 'listIosApps') + .returns(Promise.reject(EXPECTED_ERROR)); stubs.push(stub); return projectManagement.listIosApps() - .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); + .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); }); it('should throw with null API response', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listIosApps') - .returns(Promise.resolve(null)); + .stub(ProjectManagementRequestHandler.prototype, 'listIosApps') + .returns(Promise.resolve(null)); stubs.push(stub); return projectManagement.listIosApps() - .should.eventually.be.rejected - .and.have.property( - 'message', - 'listIosApps()\'s responseData must be a non-null object. Response data: null'); + .should.eventually.be.rejected + .and.have.property( + 'message', + 'listIosApps()\'s responseData must be a non-null object. Response data: null'); }); it('should return empty array when API response missing "apps" field', () => { const partialApiResponse = {}; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listIosApps') - .returns(Promise.resolve(partialApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'listIosApps') + .returns(Promise.resolve(partialApiResponse)); stubs.push(stub); return projectManagement.listIosApps() - .should.eventually.deep.equal([]); + .should.eventually.deep.equal([]); }); it('should throw when API response has non-array "apps" field', () => { const partialApiResponse = { apps: 'none' }; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listIosApps') - .returns(Promise.resolve(partialApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'listIosApps') + .returns(Promise.resolve(partialApiResponse)); stubs.push(stub); return projectManagement.listIosApps() - .should.eventually.be.rejected - .and.have.property( - 'message', - '"apps" field must be present in the listIosApps() response data. Response data: ' + .should.eventually.be.rejected + .and.have.property( + 'message', + '"apps" field must be present in the listIosApps() response data. Response data: ' + JSON.stringify(partialApiResponse, null, 2)); }); @@ -251,14 +251,14 @@ describe('ProjectManagement', () => { }; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listIosApps') - .returns(Promise.resolve(partialApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'listIosApps') + .returns(Promise.resolve(partialApiResponse)); stubs.push(stub); return projectManagement.listIosApps() - .should.eventually.be.rejected - .and.have.property( - 'message', - '"apps[].appId" field must be present in the listIosApps() response data. ' + .should.eventually.be.rejected + .and.have.property( + 'message', + '"apps[].appId" field must be present in the listIosApps() response data. ' + `Response data: ${JSON.stringify(partialApiResponse, null, 2)}`); }); @@ -266,11 +266,11 @@ describe('ProjectManagement', () => { const validIosApps: IosApp[] = [projectManagement.iosApp(APP_ID)]; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listIosApps') - .returns(Promise.resolve(VALID_LIST_IOS_APPS_API_RESPONSE)); + .stub(ProjectManagementRequestHandler.prototype, 'listIosApps') + .returns(Promise.resolve(VALID_LIST_IOS_APPS_API_RESPONSE)); stubs.push(stub); return projectManagement.listIosApps() - .should.eventually.deep.equal(validIosApps); + .should.eventually.deep.equal(validIosApps); }); }); @@ -297,35 +297,35 @@ describe('ProjectManagement', () => { describe('createAndroidApp', () => { it('should propagate intial API response errors', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'createAndroidApp') - .returns(Promise.reject(EXPECTED_ERROR)); + .stub(ProjectManagementRequestHandler.prototype, 'createAndroidApp') + .returns(Promise.reject(EXPECTED_ERROR)); stubs.push(stub); return projectManagement.createAndroidApp(PACKAGE_NAME) - .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); + .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); }); it('should throw when initial API response is null', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'createAndroidApp') - .returns(Promise.resolve(null)); + .stub(ProjectManagementRequestHandler.prototype, 'createAndroidApp') + .returns(Promise.resolve(null)); stubs.push(stub); return projectManagement.createAndroidApp(PACKAGE_NAME) - .should.eventually.be.rejected - .and.have.property( - 'message', - 'createAndroidApp()\'s responseData must be a non-null object. Response data: null'); + .should.eventually.be.rejected + .and.have.property( + 'message', + 'createAndroidApp()\'s responseData must be a non-null object. Response data: null'); }); it('should throw when initial API response.appId is undefined', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'createAndroidApp') - .returns(Promise.resolve({})); + .stub(ProjectManagementRequestHandler.prototype, 'createAndroidApp') + .returns(Promise.resolve({})); stubs.push(stub); return projectManagement.createAndroidApp(PACKAGE_NAME) - .should.eventually.be.rejected - .and.have.property( - 'message', - '"responseData.appId" field must be present in createAndroidApp()\'s response data. ' + .should.eventually.be.rejected + .and.have.property( + 'message', + '"responseData.appId" field must be present in createAndroidApp()\'s response data. ' + 'Response data: {}'); }); @@ -334,46 +334,46 @@ describe('ProjectManagement', () => { const validCreateAppResponse = { appId: APP_ID }; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'createAndroidApp') - .returns(Promise.resolve(validCreateAppResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'createAndroidApp') + .returns(Promise.resolve(validCreateAppResponse)); stubs.push(stub); return projectManagement.createAndroidApp(PACKAGE_NAME) - .should.eventually.deep.equal(createdAndroidApp); + .should.eventually.deep.equal(createdAndroidApp); }); }); describe('createIosApp', () => { it('should propagate intial API response errors', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'createIosApp') - .returns(Promise.reject(EXPECTED_ERROR)); + .stub(ProjectManagementRequestHandler.prototype, 'createIosApp') + .returns(Promise.reject(EXPECTED_ERROR)); stubs.push(stub); return projectManagement.createIosApp(BUNDLE_ID) - .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); + .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); }); it('should throw when initial API response is null', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'createIosApp') - .returns(Promise.resolve(null)); + .stub(ProjectManagementRequestHandler.prototype, 'createIosApp') + .returns(Promise.resolve(null)); stubs.push(stub); return projectManagement.createIosApp(BUNDLE_ID) - .should.eventually.be.rejected - .and.have.property( - 'message', - 'createIosApp()\'s responseData must be a non-null object. Response data: null'); + .should.eventually.be.rejected + .and.have.property( + 'message', + 'createIosApp()\'s responseData must be a non-null object. Response data: null'); }); it('should throw when initial API response.appId is undefined', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'createIosApp') - .returns(Promise.resolve({})); + .stub(ProjectManagementRequestHandler.prototype, 'createIosApp') + .returns(Promise.resolve({})); stubs.push(stub); return projectManagement.createIosApp(BUNDLE_ID) - .should.eventually.be.rejected - .and.have.property( - 'message', - '"responseData.appId" field must be present in createIosApp()\'s response data. ' + .should.eventually.be.rejected + .and.have.property( + 'message', + '"responseData.appId" field must be present in createIosApp()\'s response data. ' + 'Response data: {}'); }); @@ -382,11 +382,11 @@ describe('ProjectManagement', () => { const validCreateAppResponse = { appId: APP_ID }; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'createIosApp') - .returns(Promise.resolve(validCreateAppResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'createIosApp') + .returns(Promise.resolve(validCreateAppResponse)); stubs.push(stub); return projectManagement.createIosApp(BUNDLE_ID) - .should.eventually.deep.equal(createdIosApp); + .should.eventually.deep.equal(createdIosApp); }); }); @@ -415,48 +415,48 @@ describe('ProjectManagement', () => { it('should propagate API errors', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') - .returns(Promise.reject(EXPECTED_ERROR)); + .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') + .returns(Promise.reject(EXPECTED_ERROR)); stubs.push(stub); return projectManagement.listAppMetadata() - .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); + .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); }); it('should throw with null API response', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') - .returns(Promise.resolve(null)); + .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') + .returns(Promise.resolve(null)); stubs.push(stub); return projectManagement.listAppMetadata() - .should.eventually.be.rejected - .and.have.property( - 'message', - 'listAppMetadata()\'s responseData must be a non-null object. Response data: null'); + .should.eventually.be.rejected + .and.have.property( + 'message', + 'listAppMetadata()\'s responseData must be a non-null object. Response data: null'); }); it('should return empty array when API response missing "apps" field', () => { const partialApiResponse = {}; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') - .returns(Promise.resolve(partialApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') + .returns(Promise.resolve(partialApiResponse)); stubs.push(stub); return projectManagement.listAppMetadata() - .should.eventually.deep.equal([]); + .should.eventually.deep.equal([]); }); it('should throw when API response has non-array "apps" field', () => { const partialApiResponse = { apps: 'none' }; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') - .returns(Promise.resolve(partialApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') + .returns(Promise.resolve(partialApiResponse)); stubs.push(stub); return projectManagement.listAppMetadata() - .should.eventually.be.rejected - .and.have.property( - 'message', - '"apps" field must be present in the listAppMetadata() response data. Response data: ' + .should.eventually.be.rejected + .and.have.property( + 'message', + '"apps" field must be present in the listAppMetadata() response data. Response data: ' + JSON.stringify(partialApiResponse, null, 2)); }); @@ -466,14 +466,14 @@ describe('ProjectManagement', () => { }; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') - .returns(Promise.resolve(partialApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') + .returns(Promise.resolve(partialApiResponse)); stubs.push(stub); return projectManagement.listAppMetadata() - .should.eventually.be.rejected - .and.have.property( - 'message', - '"apps[].appId" field must be present in the listAppMetadata() response data. ' + .should.eventually.be.rejected + .and.have.property( + 'message', + '"apps[].appId" field must be present in the listAppMetadata() response data. ' + `Response data: ${JSON.stringify(partialApiResponse, null, 2)}`); }); @@ -485,14 +485,14 @@ describe('ProjectManagement', () => { }; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') - .returns(Promise.resolve(missingPlatformApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') + .returns(Promise.resolve(missingPlatformApiResponse)); stubs.push(stub); return projectManagement.listAppMetadata() - .should.eventually.be.rejected - .and.have.property( - 'message', - '"apps[].platform" field must be present in the listAppMetadata() response data. ' + .should.eventually.be.rejected + .and.have.property( + 'message', + '"apps[].platform" field must be present in the listAppMetadata() response data. ' + `Response data: ${JSON.stringify(missingPlatformApiResponse, null, 2)}`); }); @@ -520,11 +520,11 @@ describe('ProjectManagement', () => { }, ]; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') - .returns(Promise.resolve(VALID_LIST_APP_METADATA_API_RESPONSE)); + .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') + .returns(Promise.resolve(VALID_LIST_APP_METADATA_API_RESPONSE)); stubs.push(stub); return projectManagement.listAppMetadata() - .should.eventually.deep.equal(expectedAppMetadata); + .should.eventually.deep.equal(expectedAppMetadata); }); it('should resolve with "apps[].platform" to be "PLATFORM_UNKNOWN" for web app', () => { @@ -543,28 +543,28 @@ describe('ProjectManagement', () => { }]; const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') - .returns(Promise.resolve(webPlatformApiResponse)); + .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') + .returns(Promise.resolve(webPlatformApiResponse)); stubs.push(stub); return projectManagement.listAppMetadata() - .should.eventually.deep.equal(expectedAppMetadata); + .should.eventually.deep.equal(expectedAppMetadata); }); }); describe('setDisplayName', () => { it('should propagate API errors', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'setDisplayName') - .returns(Promise.reject(EXPECTED_ERROR)); + .stub(ProjectManagementRequestHandler.prototype, 'setDisplayName') + .returns(Promise.reject(EXPECTED_ERROR)); stubs.push(stub); return projectManagement.setDisplayName(DISPLAY_NAME_ANDROID) - .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); + .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); }); it('should resolve on success', () => { const stub = sinon - .stub(ProjectManagementRequestHandler.prototype, 'setDisplayName') - .returns(Promise.resolve()); + .stub(ProjectManagementRequestHandler.prototype, 'setDisplayName') + .returns(Promise.resolve()); stubs.push(stub); return projectManagement.setDisplayName(DISPLAY_NAME_ANDROID).should.eventually.be.fulfilled; }); diff --git a/test/unit/security-rules/security-rules.spec.ts b/test/unit/security-rules/security-rules.spec.ts index 63c5cf12f5..45e1f98a87 100644 --- a/test/unit/security-rules/security-rules.spec.ts +++ b/test/unit/security-rules/security-rules.spec.ts @@ -36,9 +36,9 @@ describe('SecurityRules', () => { // to allow easier use from within the tests. An improvement would be to // alter this into a helper that creates customized RulesetResponses based // on the needs of the test, as that would ensure type-safety. - name: string, - createTime: string, - source: object | null, + name: string; + createTime: string; + source: object | null; } = { name: 'projects/test-project/rulesets/foo', createTime: '2019-03-08T23:45:23.288047Z', @@ -64,7 +64,7 @@ describe('SecurityRules', () => { 'Bucket name not specified or invalid. Specify a default bucket name via the ' + 'storageBucket option when initializing the app, or specify the bucket name ' + 'explicitly when calling the rules API.', - ); + ); const INVALID_BUCKET_NAMES: any[] = [null, '', true, false, 1, 0, {}, []]; const INVALID_SOURCES: any[] = [null, undefined, '', 1, true, {}, []]; @@ -114,7 +114,7 @@ describe('SecurityRules', () => { const securityRulesAny: any = SecurityRules; return new securityRulesAny(invalidApp); }).to.throw( - 'First argument passed to admin.securityRules() must be a valid Firebase app ' + 'First argument passed to admin.securityRules() must be a valid Firebase app ' + 'instance.'); }); }); @@ -124,7 +124,7 @@ describe('SecurityRules', () => { const securityRulesAny: any = SecurityRules; return new securityRulesAny(); }).to.throw( - 'First argument passed to admin.securityRules() must be a valid Firebase app ' + 'First argument passed to admin.securityRules() must be a valid Firebase app ' + 'instance.'); }); @@ -202,8 +202,8 @@ describe('SecurityRules', () => { const response = deepCopy(FIRESTORE_RULESET_RESPONSE); response.source = null; const stub = sinon - .stub(SecurityRulesApiClient.prototype, 'getRuleset') - .resolves(response); + .stub(SecurityRulesApiClient.prototype, 'getRuleset') + .resolves(response); stubs.push(stub); return securityRules.getRuleset('foo') .should.eventually.be.rejected.and.have.property( diff --git a/test/unit/storage/storage.spec.ts b/test/unit/storage/storage.spec.ts index 4b95529ef3..55fc8ff6df 100644 --- a/test/unit/storage/storage.spec.ts +++ b/test/unit/storage/storage.spec.ts @@ -92,12 +92,12 @@ describe('Storage', () => { 'explicitly when calling the getBucket() method.'; const invalidNames = [null, NaN, 0, 1, true, false, '', [], [1, 'a'], {}, { a: 1}, _.noop]; invalidNames.forEach((invalidName) => { - it(`should throw given invalid bucket name: ${ JSON.stringify(invalidName) }`, () => { - expect(() => { - const bucketAny: any = storage.bucket; - bucketAny(invalidName); - }).to.throw(expectedError); - }); + it(`should throw given invalid bucket name: ${ JSON.stringify(invalidName) }`, () => { + expect(() => { + const bucketAny: any = storage.bucket; + bucketAny(invalidName); + }).to.throw(expectedError); + }); }); }); diff --git a/test/unit/utils.ts b/test/unit/utils.ts index 794b85bf46..12bc260b65 100644 --- a/test/unit/utils.ts +++ b/test/unit/utils.ts @@ -29,7 +29,7 @@ import { HttpError, HttpResponse } from '../../src/utils/api-request'; * @param {object} options The options for the FirebaseApp instance to create. * @return {FirebaseApp} A new FirebaseApp instance with the provided options. */ -export function createAppWithOptions(options: object) { +export function createAppWithOptions(options: object): FirebaseApp { const mockFirebaseNamespaceInternals = new FirebaseNamespace().INTERNAL; return new FirebaseApp(options as FirebaseAppOptions, mocks.appName, mockFirebaseNamespaceInternals); } @@ -71,7 +71,7 @@ export function stubGetAccessToken(accessToken?: string, app?: FirebaseApp): sin * @param {*=} headers HTTP headers to be included in the ersponse. * @return {HttpResponse} An HTTP response object. */ -export function responseFrom(data: object | string, status: number = 200, headers: any = {}): HttpResponse { +export function responseFrom(data: object | string, status = 200, headers: any = {}): HttpResponse { let responseData: any; let responseText: string; if (typeof data === 'object') { @@ -94,6 +94,6 @@ export function responseFrom(data: object | string, status: number = 200, header }; } -export function errorFrom(data: any, status: number = 500): HttpError { +export function errorFrom(data: any, status = 500): HttpError { return new HttpError(responseFrom(data, status)); } diff --git a/test/unit/utils/api-request.spec.ts b/test/unit/utils/api-request.spec.ts index 7811b236bb..c2798de455 100644 --- a/test/unit/utils/api-request.spec.ts +++ b/test/unit/utils/api-request.spec.ts @@ -66,7 +66,7 @@ function mockRequestWithHttpError( statusCode = 400, responseContentType = 'application/json', response: any = mockErrorResponse, -) { +): nock.Scope { if (responseContentType === 'text/html') { response = mockTextErrorResponse; } @@ -86,7 +86,7 @@ function mockRequestWithHttpError( * * @return {Object} A nock response object. */ -function mockRequestWithError(err: any) { +function mockRequestWithError(err: any): nock.Scope { return nock('https://' + mockHost) .get(mockPath) .replyWithError(err); @@ -346,6 +346,8 @@ describe('HttpClient', () => { mockedRequests.push(scope); const client = new HttpClient(); const httpAgent = new Agent(); + + // eslint-disable-next-line @typescript-eslint/no-var-requires const https = require('https'); transportSpy = sinon.spy(https, 'request'); return client.send({ @@ -378,9 +380,9 @@ describe('HttpClient', () => { 'My-Custom-Header': 'CustomValue', }, }).post(mockPath, reqData) - .reply(200, respData, { - 'content-type': 'application/json', - }); + .reply(200, respData, { + 'content-type': 'application/json', + }); mockedRequests.push(scope); const client = new HttpClient(); return client.send({ @@ -409,9 +411,9 @@ describe('HttpClient', () => { }, }, }).post(mockPath, reqData) - .reply(200, respData, { - 'content-type': 'application/json', - }); + .reply(200, respData, { + 'content-type': 'application/json', + }); mockedRequests.push(scope); const client = new HttpClient(); return client.send({ @@ -440,9 +442,9 @@ describe('HttpClient', () => { 'My-Custom-Header': 'CustomValue', }, }).post(mockPath, reqData) - .reply(200, {success: true}, { - 'content-type': 'application/json', - }); + .reply(200, {success: true}, { + 'content-type': 'application/json', + }); mockedRequests.push(scope); const client = new HttpClient(); const request: HttpRequestConfig = { @@ -470,10 +472,10 @@ describe('HttpClient', () => { 'My-Custom-Header': 'CustomValue', }, }).get(mockPath) - .query(reqData) - .reply(200, respData, { - 'content-type': 'application/json', - }); + .query(reqData) + .reply(200, respData, { + 'content-type': 'application/json', + }); mockedRequests.push(scope); const client = new HttpClient(); return client.send({ @@ -582,10 +584,10 @@ describe('HttpClient', () => { 'My-Custom-Header': 'CustomValue', }, }).head(mockPath) - .query(reqData) - .reply(200, respData, { - 'content-type': 'application/json', - }); + .query(reqData) + .reply(200, respData, { + 'content-type': 'application/json', + }); mockedRequests.push(scope); const client = new HttpClient(); return client.send({ @@ -1248,6 +1250,8 @@ describe('AuthorizedHttpClient', () => { beforeEach(() => { const options = mockApp.options; options.httpAgent = new Agent(); + + // eslint-disable-next-line @typescript-eslint/no-var-requires const https = require('https'); transportSpy = sinon.spy(https, 'request'); mockAppWithAgent = mocks.appWithOptions(options); @@ -1426,8 +1430,8 @@ describe('ApiSettings', () => { describe('with set properties', () => { const apiSettings: ApiSettings = new ApiSettings('getAccountInfo', 'GET'); // Set all apiSettings properties. - const requestValidator: ApiCallbackFunction = (request) => undefined; - const responseValidator: ApiCallbackFunction = (response) => undefined; + const requestValidator: ApiCallbackFunction = () => undefined; + const responseValidator: ApiCallbackFunction = () => undefined; apiSettings.setRequestValidator(requestValidator); apiSettings.setResponseValidator(responseValidator); it('should return the correct requestValidator', () => { diff --git a/test/unit/utils/error.spec.ts b/test/unit/utils/error.spec.ts index 0791b42769..41e977f725 100755 --- a/test/unit/utils/error.spec.ts +++ b/test/unit/utils/error.spec.ts @@ -80,7 +80,7 @@ describe('FirebaseAuthError', () => { const error = FirebaseAuthError.fromServerError('USER_NOT_FOUND'); expect(error.code).to.be.equal('auth/user-not-found'); expect(error.message).to.be.equal( - 'There is no user record corresponding to the provided identifier.'); + 'There is no user record corresponding to the provided identifier.'); }); it('should initialize an error from an unexpected server code', () => { @@ -100,22 +100,22 @@ describe('FirebaseAuthError', () => { describe('with message specified', () => { it('should initialize an error from an expected server code', () => { const error = FirebaseAuthError.fromServerError( - 'USER_NOT_FOUND', 'Invalid uid'); + 'USER_NOT_FOUND', 'Invalid uid'); expect(error.code).to.be.equal('auth/user-not-found'); expect(error.message).to.be.equal('Invalid uid'); }); it('should initialize an error from an unexpected server code', () => { const error = FirebaseAuthError.fromServerError( - 'UNEXPECTED_ERROR', 'An unexpected error occurred.'); + 'UNEXPECTED_ERROR', 'An unexpected error occurred.'); expect(error.code).to.be.equal('auth/internal-error'); expect(error.message).to.be.equal('An unexpected error occurred.'); }); it('should initialize an error from an expected server with server detailed message', () => { const error = FirebaseAuthError.fromServerError( - 'CONFIGURATION_NOT_FOUND : more details', - 'Ignored message'); + 'CONFIGURATION_NOT_FOUND : more details', + 'Ignored message'); expect(error.code).to.be.equal('auth/configuration-not-found'); expect(error.message).to.be.equal('more details'); }); @@ -131,14 +131,14 @@ describe('FirebaseAuthError', () => { it('should not include raw server response from an expected server code', () => { const error = FirebaseAuthError.fromServerError( - 'USER_NOT_FOUND', 'Invalid uid', mockRawServerResponse); + 'USER_NOT_FOUND', 'Invalid uid', mockRawServerResponse); expect(error.code).to.be.equal('auth/user-not-found'); expect(error.message).to.be.equal('Invalid uid'); }); it('should include raw server response from an unexpected server code', () => { const error = FirebaseAuthError.fromServerError( - 'UNEXPECTED_ERROR', 'An unexpected error occurred.', mockRawServerResponse); + 'UNEXPECTED_ERROR', 'An unexpected error occurred.', mockRawServerResponse); expect(error.code).to.be.equal('auth/internal-error'); expect(error.message).to.be.equal( 'An unexpected error occurred. Raw server response: "' + diff --git a/test/unit/utils/index.spec.ts b/test/unit/utils/index.spec.ts index 8413a8e979..2065e2fb49 100755 --- a/test/unit/utils/index.spec.ts +++ b/test/unit/utils/index.spec.ts @@ -48,7 +48,7 @@ describe('addReadonlyGetter()', () => { expect(() => { obj.foo = false; - }).to.throw(/Cannot assign to read only property \'foo\' of/); + }).to.throw(/Cannot assign to read only property 'foo' of/); }); it('should make the new property enumerable', () => { @@ -214,6 +214,63 @@ describe('findProjectId()', () => { }); }); +describe('findProjectId()', () => { + let googleCloudProject: string | undefined; + let gcloudProject: string | undefined; + + before(() => { + googleCloudProject = process.env.GOOGLE_CLOUD_PROJECT; + gcloudProject = process.env.GCLOUD_PROJECT; + }); + + after(() => { + if (isNonEmptyString(googleCloudProject)) { + process.env.GOOGLE_CLOUD_PROJECT = googleCloudProject; + } else { + delete process.env.GOOGLE_CLOUD_PROJECT; + } + + if (isNonEmptyString(gcloudProject)) { + process.env.GCLOUD_PROJECT = gcloudProject; + } else { + delete process.env.GCLOUD_PROJECT; + } + }); + + it('should return the explicitly specified project ID from app options', () => { + const options: FirebaseAppOptions = { + credential: new mocks.MockCredential(), + projectId: 'explicit-project-id', + }; + const app: FirebaseApp = mocks.appWithOptions(options); + return findProjectId(app).should.eventually.equal(options.projectId); + }); + + it('should return the project ID from service account', () => { + const app: FirebaseApp = mocks.app(); + return findProjectId(app).should.eventually.equal('project_id'); + }); + + it('should return the project ID set in GOOGLE_CLOUD_PROJECT environment variable', () => { + process.env.GOOGLE_CLOUD_PROJECT = 'env-var-project-id'; + const app: FirebaseApp = mocks.mockCredentialApp(); + return findProjectId(app).should.eventually.equal('env-var-project-id'); + }); + + it('should return the project ID set in GCLOUD_PROJECT environment variable', () => { + process.env.GCLOUD_PROJECT = 'env-var-project-id'; + const app: FirebaseApp = mocks.mockCredentialApp(); + return findProjectId(app).should.eventually.equal('env-var-project-id'); + }); + + it('should return null when project ID is not set', () => { + delete process.env.GOOGLE_CLOUD_PROJECT; + delete process.env.GCLOUD_PROJECT; + const app: FirebaseApp = mocks.mockCredentialApp(); + return findProjectId(app).should.eventually.be.null; + }); +}); + describe('formatString()', () => { it('should keep string as is if not parameters are provided', () => { const str = 'projects/{projectId}/{api}/path/api/projectId'; diff --git a/test/unit/utils/validator.spec.ts b/test/unit/utils/validator.spec.ts old mode 100644 new mode 100755 index 00a0ea6c2b..c8ab764f93 --- a/test/unit/utils/validator.spec.ts +++ b/test/unit/utils/validator.spec.ts @@ -22,6 +22,7 @@ import * as chaiAsPromised from 'chai-as-promised'; import { isArray, isNonEmptyArray, isBoolean, isNumber, isString, isNonEmptyString, isNonNullObject, isEmail, isPassword, isURL, isUid, isPhoneNumber, isObject, isBuffer, + isUTCDateString, isISODateString, } from '../../../src/utils/validator'; @@ -67,10 +68,12 @@ describe('isArray()', () => { }); it('should return true given an empty array created from Array constructor', () => { + // eslint-disable-next-line @typescript-eslint/no-array-constructor expect(isArray(new Array())).to.be.true; }); it('should return true given a non-empty array created from Array constructor', () => { + // eslint-disable-next-line @typescript-eslint/no-array-constructor expect(isArray(new Array(1, 2, 3))).to.be.true; }); }); @@ -96,10 +99,12 @@ describe('isNonEmptyArray()', () => { }); it('should return false given an empty array created from Array constructor', () => { + // eslint-disable-next-line @typescript-eslint/no-array-constructor expect(isNonEmptyArray(new Array())).to.be.false; }); it('should return true given a non-empty array created from Array constructor', () => { + // eslint-disable-next-line @typescript-eslint/no-array-constructor expect(isNonEmptyArray(new Array(1, 2, 3))).to.be.true; }); }); @@ -470,3 +475,57 @@ describe('isBuffer()', () => { expect(isBuffer(Buffer.from('I am a buffer'))).to.be.true; }); }); + +describe('isUTCDateString()', () => { + const validUTCDateString = 'Fri, 25 Oct 2019 04:01:21 GMT'; + it('should return false given no argument', () => { + expect(isUTCDateString(undefined as any)).to.be.false; + }); + + const nonUTCDateStrings = [ + null, NaN, 0, 1, true, false, [], ['a'], {}, { a: 1 }, _.noop, + new Date().getTime(), + new Date().getTime().toString(), + new Date().toISOString(), + 'Fri, 25 Oct 2019 04:01:21', + '25 Oct 2019', 'Fri, 25 Oct 2019', + '2019-10-25', '2019-10-25T04:07:34.036', + new Date().toDateString(), + ]; + nonUTCDateStrings.forEach((nonUTCDateString) => { + it('should return false given an invalid UTC date string: ' + JSON.stringify(nonUTCDateString), () => { + expect(isUTCDateString(nonUTCDateString as any)).to.be.false; + }); + }); + + it('should return true given a valid UTC date string', () => { + expect(isUTCDateString(validUTCDateString)).to.be.true; + }); +}); + +describe('isISODateString()', () => { + const validISODateString = '2019-10-25T04:07:34.036Z'; + it('should return false given no argument', () => { + expect(isISODateString(undefined as any)).to.be.false; + }); + + const nonISODateStrings = [ + null, NaN, 0, 1, true, false, [], ['a'], {}, { a: 1 }, _.noop, + new Date().getTime(), + new Date().getTime().toString(), + new Date().toUTCString(), + 'Fri, 25 Oct 2019 04:01:21', + '25 Oct 2019', 'Fri, 25 Oct 2019', + '2019-10-25', '2019-10-25T04:07:34.036', + new Date().toDateString(), + ]; + nonISODateStrings.forEach((nonISODateString) => { + it('should return false given an invalid ISO date string: ' + JSON.stringify(nonISODateString), () => { + expect(isISODateString(nonISODateString as any)).to.be.false; + }); + }); + + it('should return true given a valid ISO date string', () => { + expect(isISODateString(validISODateString)).to.be.true; + }); +}); diff --git a/tslint-test.json b/tslint-test.json deleted file mode 100644 index 27fa06e5bd..0000000000 --- a/tslint-test.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extends": "tslint:latest", - "rules": { - "interface-name": false, - "member-ordering": [true, - "public-before-private", - "static-before-instance", - "variables-before-functions" - ], - "prefer-object-spread": false, - "no-implicit-dependencies": [true, "dev", "optional"], - "max-classes-per-file": false, - "max-line-length": [true, 120], - "no-consecutive-blank-lines": false, - "object-literal-sort-keys": false, - "ordered-imports": false, - "quotemark": [true, "single", "avoid-escape"], - "no-unused-expression": false, - "variable-name": [true, "ban-keywords", "check-format", "allow-trailing-underscore"] - } -} diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 5626542a6e..0000000000 --- a/tslint.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extends": "tslint:latest", - "rules": { - "interface-name": false, - "member-ordering": [true, - "public-before-private", - "static-before-instance", - "variables-before-functions" - ], - "prefer-object-spread": false, - "no-implicit-dependencies": [true, "dev", "optional"], - "max-classes-per-file": false, - "max-line-length": [true, 120], - "no-consecutive-blank-lines": false, - "object-literal-sort-keys": false, - "ordered-imports": false, - "quotemark": [true, "single", "avoid-escape"], - "variable-name": [true, "ban-keywords", "check-format", "allow-trailing-underscore"] - } -}