diff --git a/.github/ISSUE_TEMPLATE/---feature-request.md b/.github/ISSUE_TEMPLATE/---feature-request.md
deleted file mode 100644
index 93f7d6de1..000000000
--- a/.github/ISSUE_TEMPLATE/---feature-request.md
+++ /dev/null
@@ -1,17 +0,0 @@
----
-name: "\U0001F4A1 Feature Request"
-about:
- Have a feature you'd like to see in the functions SDK? Request it through our
- support channel.
-title: ''
-labels: ''
-assignees: ''
----
-
-
-
-Great, we love hearing how we can improve our products! However, GitHub is not the place to submit them. Please submit your feature requests to:
-https://firebase.google.com/support/contact/bugs-features/
diff --git a/.github/ISSUE_TEMPLATE/---report-a-bug.md b/.github/ISSUE_TEMPLATE/---report-a-bug.md
index 694c4aedf..3ad82bf60 100644
--- a/.github/ISSUE_TEMPLATE/---report-a-bug.md
+++ b/.github/ISSUE_TEMPLATE/---report-a-bug.md
@@ -1,6 +1,6 @@
---
name: '⚠️ Report a Bug'
-about: Think you found a bug in the SDK? Report it here.
+about: Think you found a bug in the firebase-functions SDK? Report it here. Please do not use this form if your function is deployed successfully but not working as you expected.
title: ''
labels: ''
assignees: ''
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..918e205f9
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,8 @@
+blank_issues_enabled: false
+contact_links:
+ - name: 💻 Bug in the Firebase CLI
+ url: https://github.com/firebase/firebase-tools/issues/new/choose
+ about: Have you found a bug in the Firebase CLI?
+ - name: 🔥 Firebase Support
+ url: https://firebase.google.com/support/
+ about: If you have an issue with your functions in production, please contact support.
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
new file mode 100644
index 000000000..c56d65e12
--- /dev/null
+++ b/.github/workflows/test.yaml
@@ -0,0 +1,34 @@
+name: CI Tests
+
+on:
+ - pull_request
+ - push
+
+env:
+ CI: true
+
+jobs:
+ unit:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ node-version:
+ - 10.x
+ - 12.x
+ - 14.x
+ steps:
+ - uses: actions/checkout@v1
+ - uses: actions/setup-node@v1
+ with:
+ node-version: ${{ matrix.node-version }}
+
+ - name: Cache npm
+ uses: actions/cache@v1
+ with:
+ path: ~/.npm
+ key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
+
+ - run: npm install
+ - run: npm run lint
+ - run: npm run format
+ - run: npm run test
diff --git a/.npmrc b/.npmrc
deleted file mode 100644
index 43c97e719..000000000
--- a/.npmrc
+++ /dev/null
@@ -1 +0,0 @@
-package-lock=false
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 18dff4114..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-cache: npm
-jobs:
- include:
- - name: lint
- script: npm run lint
- stage: verify
- - name: format
- script: npm run format
- stage: verify
-language: node_js
-node_js:
- - '8'
- - '10'
-sudo: false
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f46f8b3f4..e69de29bb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1 +0,0 @@
-- Adds support for `vpcConnector` and `vpcConnectorEgressSettings` fields in `functions.runWith()`. **Must be used in conjunction with firebase-tools v8.9.0 or higher.** Thanks @pcboy! (#752)
diff --git a/docgen/content-sources/toc.yaml b/docgen/content-sources/toc.yaml
index 37093f7c2..e970fc80c 100644
--- a/docgen/content-sources/toc.yaml
+++ b/docgen/content-sources/toc.yaml
@@ -23,6 +23,20 @@ toc:
- title: 'config.Config'
path: /docs/reference/functions/config_.config.config.html
+ - title: 'functions.function-configuration'
+ path: /docs/reference/functions/function_configuration_.html
+ section:
+ - title: 'config.DeploymentOptions'
+ path: /docs/reference/functions/function_configuration_.deploymentoptions.html
+ - title: 'config.FailurePolicy'
+ path: /docs/reference/functions/function_configuration_.failurepolicy.html
+ - title: 'config.RuntimeOptions'
+ path: /docs/reference/functions/function_configuration_.runtimeoptions.html
+ - title: 'config.Schedule'
+ path: /docs/reference/functions/function_configuration_.schedule.html
+ - title: 'config.ScheduleRetryConfig'
+ path: /docs/reference/functions/function_configuration_.scheduleretryconfig.html
+
- title: 'functions.analytics'
path: /docs/reference/functions/providers_analytics_.html
section:
@@ -55,18 +69,6 @@ toc:
- title: 'UserRecord'
path: /docs/reference/functions/providers_auth_.html#userrecord
- - title: 'functions.crashlytics'
- path: /docs/reference/functions/providers_crashlytics_.html
- section:
- - title: 'Issue'
- path: /docs/reference/functions/providers_crashlytics_.issue.html
- - title: 'IssueBuilder'
- path: /docs/reference/functions/providers_crashlytics_.issuebuilder.html
- - title: 'AppInfo'
- path: /docs/reference/functions/providers_crashlytics_.appinfo.html
- - title: 'VelocityAlert'
- path: /docs/reference/functions/providers_crashlytics_.velocityalert.html
-
- title: 'functions.firestore'
path: /docs/reference/functions/providers_firestore_.html
section:
@@ -127,11 +129,6 @@ toc:
- title: 'ObjectMetadata'
path: /docs/reference/functions/providers_storage_.objectmetadata.html
- - title: 'functions.handler'
- path: /docs/reference/functions/handler_builder_.html
- section:
- - title: 'HandlerBuilder'
- path: /docs/reference/functions/handler_builder_.handlerbuilder.html
- title: 'functions.testLab'
path: /docs/reference/functions/providers_testlab_.html
section:
@@ -143,3 +140,9 @@ toc:
path: /docs/reference/functions/providers_testlab_.testmatrix.html
- title: 'testLab.testMatrixBuilder'
path: /docs/reference/functions/providers_testlab_.testmatrixbuilder.html
+
+ - title: 'functions.handler'
+ path: /docs/reference/functions/handler_builder_.html
+ section:
+ - title: 'HandlerBuilder'
+ path: /docs/reference/functions/handler_builder_.handlerbuilder.html
diff --git a/docgen/generate-docs.js b/docgen/generate-docs.js
index 01275ae2d..d36edb7e8 100644
--- a/docgen/generate-docs.js
+++ b/docgen/generate-docs.js
@@ -20,7 +20,6 @@ const fs = require('mz/fs');
const path = require('path');
const yargs = require('yargs');
const yaml = require('js-yaml');
-const _ = require('lodash');
const repoPath = path.resolve(`${__dirname}/..`);
@@ -72,59 +71,52 @@ function runTypedoc() {
* Moves files from subdir to root.
* @param {string} subdir Subdir to move files out of.
*/
-function moveFilesToRoot(subdir) {
- return exec(`mv ${docPath}/${subdir}/* ${docPath}`)
- .then(() => {
- exec(`rmdir ${docPath}/${subdir}`);
- })
- .catch(e => console.error(e));
+async function moveFilesToRoot(subdir) {
+ await exec(`mv ${docPath}/${subdir}/* ${docPath}`)
+ await exec(`rmdir ${docPath}/${subdir}`);
}
/**
* Renames files to remove the leading underscores.
* We need to do this because devsite hides these files.
- * Example:
+ * Example:
* _cloud_functions_.resource.html => cloud_functions_.resource.html
*/
-function renameFiles() {
- return fs.readdir(docPath).then(files => {
- console.log(files);
- files.forEach(file => {
- let newFileName = file;
- if (_.startsWith(file, "_") && _.endsWith(file, "html")) {
- newFileName = _.trimStart(file, "_");
- fs.rename(`${docPath}/${file}`, `${docPath}/${newFileName}`, (err) => {
- if (err) console.log(err)
- });
- }
- })
- })
+async function renameFiles() {
+ const files = await fs.readdir(docPath);
+ const renames = [];
+ for (const file of files) {
+ if (file.startsWith("_") && file.endsWith("html")) {
+ let newFileName = file.substring(1);
+ renames.push(fs.rename(`${docPath}/${file}`, `${docPath}/${newFileName}`));
+ }
+ }
+ await Promise.all(renames);
}
/**
* Reformat links to match flat structure.
* @param {string} file File to fix links in.
*/
-function fixLinks(file) {
- return fs.readFile(file, 'utf8').then(data => {
- data = addTypeAliasLinks(data);
- const flattenedLinks = data
- .replace(/\.\.\//g, '')
- .replace(/(modules|interfaces|classes)\//g, '')
- .replace(/\"_/g, '"');
- let caseFixedLinks = flattenedLinks;
- for (const lower in lowerToUpperLookup) {
- const re = new RegExp(lower, 'g');
- caseFixedLinks = caseFixedLinks.replace(re, lowerToUpperLookup[lower]);
- }
- return fs.writeFile(file, caseFixedLinks);
- });
+async function fixLinks(file) {
+ let data = await fs.readFile(file, 'utf8');
+ data = addTypeAliasLinks(data);
+ const flattenedLinks = data
+ .replace(/\.\.\//g, '')
+ .replace(/(modules|interfaces|classes)\//g, '')
+ .replace(/\"_/g, '"');
+ let caseFixedLinks = flattenedLinks;
+ for (const lower in lowerToUpperLookup) {
+ const re = new RegExp(lower, 'g');
+ caseFixedLinks = caseFixedLinks.replace(re, lowerToUpperLookup[lower]);
+ }
+ return fs.writeFile(file, caseFixedLinks);
}
-/**
+/**
* Adds links to external documentation for type aliases that
* reference an external library.
- *
+ *
* @param data File data to add external library links to.
*/
function addTypeAliasLinks(data) {
@@ -136,22 +128,20 @@ function addTypeAliasLinks(data) {
const fileTags = htmlDom.window.document.querySelectorAll(".tsd-signature-type");
fileTags.forEach(tag => {
const mapping = typeMap[tag.textContent];
- if (mapping) {
- console.log('Adding link to '+tag.textContent+" documentation.");
-
- // Add the corresponding document link to this type
- const linkChild = htmlDom.window.document.createElement('a');
- linkChild.setAttribute('href', mapping);
- linkChild.textContent = tag.textContent;
- tag.textContent = null;
- tag.appendChild(linkChild);
- }
- });
+ if (mapping) {
+ console.log('Adding link to '+tag.textContent+" documentation.");
+
+ // Add the corresponding document link to this type
+ const linkChild = htmlDom.window.document.createElement('a');
+ linkChild.setAttribute('href', mapping);
+ linkChild.textContent = tag.textContent;
+ tag.textContent = null;
+ tag.appendChild(linkChild);
+ }
+ });
return htmlDom.serialize();
}
-let tocText = '';
-
/**
* Generates temporary markdown file that will be sourced by Typedoc to
* create index.html.
@@ -182,37 +172,33 @@ const lowerToUpperLookup = {};
* Checks to see if any files listed in toc.yaml were not generated.
* If files exist, fixes filename case to match toc.yaml version.
*/
-function checkForMissingFilesAndFixFilenameCase() {
+async function checkForMissingFilesAndFixFilenameCase(tocText) {
// Get filenames from toc.yaml.
const filenames = tocText
.split('\n')
.filter(line => line.includes('path:'))
.map(line => line.split(devsitePath)[1]);
// Logs warning to console if a file from TOC is not found.
- // console.log(filenames);
- const fileCheckPromises = filenames.map(filename => {
+ const fileCheckPromises = filenames.map(async filename => {
// Warns if file does not exist, fixes filename case if it does.
// Preferred filename for devsite should be capitalized and taken from
// toc.yaml.
const tocFilePath = `${docPath}/${filename}`;
// Generated filename from Typedoc will be lowercase.
const generatedFilePath = `${docPath}/${filename.toLowerCase()}`;
- return fs.exists(generatedFilePath).then(exists => {
- if (exists) {
- // Store in a lookup table for link fixing.
- lowerToUpperLookup[
- `${filename.toLowerCase()}`
- ] = `${filename}`;
- return fs.rename(generatedFilePath, tocFilePath);
- } else {
- console.warn(
- `Missing file: ${filename} requested ` +
- `in toc.yaml but not found in ${docPath}`
- );
- }
- });
+ if (await fs.exists(generatedFilePath)) {
+ // Store in a lookup table for link fixing.
+ lowerToUpperLookup[filename.toLowerCase()] = filename;
+ return fs.rename(generatedFilePath, tocFilePath);
+ } else {
+ console.warn(
+ `Missing file: ${filename} requested ` +
+ `in toc.yaml but not found in ${docPath}`
+ );
+ }
});
- return Promise.all(fileCheckPromises).then(() => filenames);
+ await Promise.all(fileCheckPromises);
+ return filenames;
}
/**
@@ -223,40 +209,31 @@ function checkForMissingFilesAndFixFilenameCase() {
* @param {Array} filenamesFromToc Filenames pulled from toc.yaml
* @param {boolean} shouldRemove Should just remove the file
*/
-function checkForUnlistedFiles(filenamesFromToc, shouldRemove) {
- return fs.readdir(docPath).then(files => {
- const htmlFiles = files
- .filter(filename => filename.slice(-4) === 'html');
- const removePromises = [];
- htmlFiles.forEach(filename => {
- if (
- !filenamesFromToc.includes(filename) &&
- filename !== 'index' &&
- filename !== 'globals'
- ) {
- if (shouldRemove) {
- console.log(
- `REMOVING ${docPath}/${filename} - not listed in toc.yaml.`
- );
- removePromises.push(fs.unlink(`${docPath}/${filename}`));
- } else {
- // This is just a warning, it doesn't need to finish before
- // the process continues.
- console.warn(
- `Unlisted file: ${filename} generated ` +
- `but not listed in toc.yaml.`
- );
- }
- }
- });
- if (shouldRemove) {
- return Promise.all(removePromises).then(() =>
- htmlFiles.filter(filename => filenamesFromToc.includes(filename))
- );
- } else {
- return htmlFiles;
- }
- });
+async function checkForUnlistedFiles(filenamesFromToc, shouldRemove) {
+ const files = await fs.readdir(docPath);
+ const htmlFiles = files
+ .filter(filename => filename.slice(-4) === 'html');
+ const removePromises = [];
+ const filesToRemove = htmlFiles
+ .filter(filename => !filenamesFromToc.includes(filename))
+ .filter(filename => filename !== 'index' && filename != 'globals');
+ if (filesToRemove.length && !shouldRemove) {
+ // This is just a warning, it doesn't need to finish before
+ // the process continues.
+ console.warn(
+ `Unlisted files: ${filesToRemove.join(", ")} generated ` +
+ `but not listed in toc.yaml.`
+ );
+ return htmlFiles;
+ }
+
+ await Promise.all(filesToRemove.map(filename => {
+ console.log(
+ `REMOVING ${docPath}/${filename} - not listed in toc.yaml.`
+ );
+ return fs.unlink(`${docPath}/${filename})`);
+ }));
+ return htmlFiles.filter(filename => filenamesFromToc.includes(filename))
}
/**
@@ -265,7 +242,7 @@ function checkForUnlistedFiles(filenamesFromToc, shouldRemove) {
*
* @param {Array} htmlFiles List of html files found in generated dir.
*/
-function writeGeneratedFileList(htmlFiles) {
+async function writeGeneratedFileList(htmlFiles) {
const fileList = htmlFiles.map(filename => {
return {
title: filename,
@@ -273,9 +250,8 @@ function writeGeneratedFileList(htmlFiles) {
};
});
const generatedTocYAML = yaml.safeDump({ toc: fileList });
- return fs
- .writeFile(`${docPath}/_toc_autogenerated.yaml`, generatedTocYAML)
- .then(() => htmlFiles);
+ await fs.writeFile(`${docPath}/_toc_autogenerated.yaml`, generatedTocYAML);
+ return htmlFiles;
}
/**
@@ -305,69 +281,72 @@ function fixAllLinks(htmlFiles) {
* links as needed.
* 5) Check for mismatches between TOC list and generated file list.
*/
-Promise.all([
- fs.readFile(`${contentPath}/toc.yaml`, 'utf8'),
- fs.readFile(`${contentPath}/HOME.md`, 'utf8')
-])
- // Read TOC and homepage text and assemble a homepage markdown file.
- // This file will be sourced by Typedoc to generate index.html.
- .then(([tocRaw, homeRaw]) => {
- tocText = tocRaw;
- return generateTempHomeMdFile(tocRaw, homeRaw);
- })
- // Run main Typedoc process (uses index.d.ts and generated temp file above).
- .then(runTypedoc)
- .then(output => {
+(async function() {
+ try {
+ const [tocRaw, homeRaw] = await Promise.all([
+ fs.readFile(`${contentPath}/toc.yaml`, 'utf8'),
+ fs.readFile(`${contentPath}/HOME.md`, 'utf8')
+ ])
+
+ // Run main Typedoc process (uses index.d.ts and generated temp file above).
+ await generateTempHomeMdFile(tocRaw, homeRaw);
+ const output = await runTypedoc();
+
// Typedoc output.
console.log(output.stdout);
- // Clean up temp home markdown file. (Nothing needs to wait for this.)
- fs.unlink(tempHomePath);
- // Devsite doesn't like css.map files.
- return fs.unlink(`${docPath}/assets/css/main.css.map`);
- })
- // Write out TOC file. Do this after Typedoc step to prevent Typedoc
- // erroring when it finds an unexpected file in the target dir.
- .then(() => fs.writeFile(`${docPath}/_toc.yaml`, tocText))
- // Flatten file structure. These categories don't matter to us and it makes
- // it easier to manage the docs directory.
- .then(() => {
- return Promise.all([
+ await Promise.all([
+ // Clean up temp home markdown file. (Nothing needs to wait for this.)
+ fs.unlink(tempHomePath),
+
+ // Devsite doesn't like css.map files.
+ // NOTE: This doesn't seem to actually get generated anymore, but we'll keep this here just in case.
+ async () => {
+ const cssMap = `${docPath}/assets/css/main.css.map`;
+ if (await fs.exists(cssMap)) {
+ await fs.unlink();
+ }
+ },
+
+ // Write out TOC file. Do this after Typedoc step to prevent Typedoc
+ // erroring when it finds an unexpected file in the target dir.
+ fs.writeFile(`${docPath}/_toc.yaml`, tocRaw),
+ ]);
+
+ // Flatten file structure. These categories don't matter to us and it makes
+ // it easier to manage the docs directory.
+ await Promise.all([
moveFilesToRoot('classes'),
moveFilesToRoot('modules'),
moveFilesToRoot('interfaces'),
]);
- })
- // Rename files to remove the underscores since devsite hides those.
- .then(renameFiles)
- // Check for files listed in TOC that are missing and warn if so.
- // Not blocking.
- .then(checkForMissingFilesAndFixFilenameCase)
- // Check for files that exist but aren't listed in the TOC and warn.
- // (If API is node, actually remove the file.)
- // Removal is blocking, warnings aren't.
- .then(filenamesFromToc =>
- checkForUnlistedFiles(filenamesFromToc, false)
- )
- // Write a _toc_autogenerated.yaml to record what files were created.
- .then(htmlFiles => writeGeneratedFileList(htmlFiles))
- // Correct the links in all the generated html files now that files have
- // all been moved to top level.
- // .then(removeUnderscores)
- .then(fixAllLinks)
- .then(() => {
- fs.readFile(`${docPath}/index.html`, 'utf8').then(data => {
- // String to include devsite local variables.
- const localVariablesIncludeString = `{% include "docs/web/_local_variables.html" %}\n`;
- return fs.writeFile(
- `${docPath}/index.html`,
- localVariablesIncludeString + data
- );
- });
- })
- .catch(e => {
- if (e.stdout) {
- console.error(e.stdout);
- } else {
- console.error(e);
- }
- });
+ // Rename files to remove the underscores since devsite hides those.
+ await renameFiles();
+
+ // Check for files listed in TOC that are missing and warn if so.
+ // Not blocking.
+ const filenamesFromToc = await checkForMissingFilesAndFixFilenameCase(tocRaw);
+
+ // Check for files that exist but aren't listed in the TOC and warn.
+ // (If API is node, actually remove the file.)
+ // Removal is blocking, warnings aren't.
+ const htmlFiles = await checkForUnlistedFiles(filenamesFromToc, false);
+
+ // Write a _toc_autogenerated.yaml to record what files were created.
+ const fileList = await writeGeneratedFileList(htmlFiles);
+
+ // Correct the links in all the generated html files now that files have
+ // all been moved to top level.
+ await fixAllLinks(fileList);
+ const data = await fs.readFile(`${docPath}/index.html`, 'utf8');
+ // String to include devsite local variables.
+ const localVariablesIncludeString = `{% include "docs/web/_local_variables.html" %}\n`;
+ await fs.writeFile(`${docPath}/index.html`, localVariablesIncludeString + data);
+ } catch (err) {
+ if (err.stdout) {
+ console.error(err.stdout);
+ } else {
+ console.error(err);
+ }
+}
+})();
+
diff --git a/docgen/theme/layouts/default.hbs b/docgen/theme/layouts/default.hbs
index 72111fb4e..17106e8d8 100644
--- a/docgen/theme/layouts/default.hbs
+++ b/docgen/theme/layouts/default.hbs
@@ -7,6 +7,8 @@
+
+
{{#ifCond model.name '==' project.name}}{{project.name}}{{else}}{{model.name}} | {{project.name}}{{/ifCond}}
diff --git a/integration_test/firestore.rules b/integration_test/firestore.rules
index e8f8d7997..d9df6d5d1 100644
--- a/integration_test/firestore.rules
+++ b/integration_test/firestore.rules
@@ -1,3 +1,5 @@
+rules_version = "2";
+
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
diff --git a/integration_test/functions/src/auth-tests.ts b/integration_test/functions/src/auth-tests.ts
index 2c1e1d4e6..c97c907bc 100644
--- a/integration_test/functions/src/auth-tests.ts
+++ b/integration_test/functions/src/auth-tests.ts
@@ -3,68 +3,82 @@ import * as functions from 'firebase-functions';
import { expectEq, TestSuite } from './testing';
import UserMetadata = admin.auth.UserRecord;
-export const createUserTests: any = functions.auth.user().onCreate((u, c) => {
- const testId: string = u.displayName;
- console.log(`testId is ${testId}`);
+const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1';
- return new TestSuite('auth user onCreate')
- .it('should have a project as resource', (user, context) =>
- expectEq(context.resource.name, `projects/${process.env.GCLOUD_PROJECT}`)
- )
+export const createUserTests: any = functions
+ .region(REGION)
+ .auth.user()
+ .onCreate((u, c) => {
+ const testId: string = u.displayName;
+ console.log(`testId is ${testId}`);
- .it('should not have a path', (user, context) =>
- expectEq((context as any).path, undefined)
- )
+ return new TestSuite('auth user onCreate')
+ .it('should have a project as resource', (user, context) =>
+ expectEq(
+ context.resource.name,
+ `projects/${process.env.GCLOUD_PROJECT}`
+ )
+ )
- .it('should have the correct eventType', (user, context) =>
- expectEq(context.eventType, 'google.firebase.auth.user.create')
- )
+ .it('should not have a path', (user, context) =>
+ expectEq((context as any).path, undefined)
+ )
- .it('should have an eventId', (user, context) => context.eventId)
+ .it('should have the correct eventType', (user, context) =>
+ expectEq(context.eventType, 'google.firebase.auth.user.create')
+ )
- .it('should have a timestamp', (user, context) => context.timestamp)
+ .it('should have an eventId', (user, context) => context.eventId)
- .it('should not have auth', (user, context) =>
- expectEq((context as any).auth, undefined)
- )
+ .it('should have a timestamp', (user, context) => context.timestamp)
- .it('should not have action', (user, context) =>
- expectEq((context as any).action, undefined)
- )
+ .it('should not have auth', (user, context) =>
+ expectEq((context as any).auth, undefined)
+ )
- .it('should have properly defined meta', (user, context) => user.metadata)
+ .it('should not have action', (user, context) =>
+ expectEq((context as any).action, undefined)
+ )
- .run(testId, u, c);
-});
+ .it('should have properly defined meta', (user, context) => user.metadata)
-export const deleteUserTests: any = functions.auth.user().onDelete((u, c) => {
- const testId: string = u.displayName;
- console.log(`testId is ${testId}`);
+ .run(testId, u, c);
+ });
- return new TestSuite('auth user onDelete')
- .it('should have a project as resource', (user, context) =>
- expectEq(context.resource.name, `projects/${process.env.GCLOUD_PROJECT}`)
- )
+export const deleteUserTests: any = functions
+ .region(REGION)
+ .auth.user()
+ .onDelete((u, c) => {
+ const testId: string = u.displayName;
+ console.log(`testId is ${testId}`);
- .it('should not have a path', (user, context) =>
- expectEq((context as any).path, undefined)
- )
+ return new TestSuite('auth user onDelete')
+ .it('should have a project as resource', (user, context) =>
+ expectEq(
+ context.resource.name,
+ `projects/${process.env.GCLOUD_PROJECT}`
+ )
+ )
- .it('should have the correct eventType', (user, context) =>
- expectEq(context.eventType, 'google.firebase.auth.user.delete')
- )
+ .it('should not have a path', (user, context) =>
+ expectEq((context as any).path, undefined)
+ )
- .it('should have an eventId', (user, context) => context.eventId)
+ .it('should have the correct eventType', (user, context) =>
+ expectEq(context.eventType, 'google.firebase.auth.user.delete')
+ )
- .it('should have a timestamp', (user, context) => context.timestamp)
+ .it('should have an eventId', (user, context) => context.eventId)
- .it('should not have auth', (user, context) =>
- expectEq((context as any).auth, undefined)
- )
+ .it('should have a timestamp', (user, context) => context.timestamp)
- .it('should not have action', (user, context) =>
- expectEq((context as any).action, undefined)
- )
+ .it('should not have auth', (user, context) =>
+ expectEq((context as any).auth, undefined)
+ )
- .run(testId, u, c);
-});
+ .it('should not have action', (user, context) =>
+ expectEq((context as any).action, undefined)
+ )
+
+ .run(testId, u, c);
+ });
diff --git a/integration_test/functions/src/database-tests.ts b/integration_test/functions/src/database-tests.ts
index de3030d3a..568d26bf4 100644
--- a/integration_test/functions/src/database-tests.ts
+++ b/integration_test/functions/src/database-tests.ts
@@ -4,9 +4,11 @@ import { expectEq, expectMatches, TestSuite } from './testing';
import DataSnapshot = admin.database.DataSnapshot;
const testIdFieldName = 'testId';
+const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1';
-export const databaseTests: any = functions.database
- .ref('dbTests/{testId}/start')
+export const databaseTests: any = functions
+ .region(REGION)
+ .database.ref('dbTests/{testId}/start')
.onWrite((ch, ctx) => {
if (ch.after.val() === null) {
console.log(
diff --git a/integration_test/functions/src/firestore-tests.ts b/integration_test/functions/src/firestore-tests.ts
index 49d1ae919..97daadea5 100644
--- a/integration_test/functions/src/firestore-tests.ts
+++ b/integration_test/functions/src/firestore-tests.ts
@@ -4,11 +4,13 @@ import { expectDeepEq, expectEq, TestSuite } from './testing';
import DocumentSnapshot = admin.firestore.DocumentSnapshot;
const testIdFieldName = 'documentId';
+const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1';
export const firestoreTests: any = functions
.runWith({
timeoutSeconds: 540,
})
+ .region(REGION)
.firestore.document('tests/{documentId}')
.onCreate((s, c) => {
return new TestSuite('firestore document onWrite')
diff --git a/integration_test/functions/src/https-tests.ts b/integration_test/functions/src/https-tests.ts
index 55c7df983..18972bd47 100644
--- a/integration_test/functions/src/https-tests.ts
+++ b/integration_test/functions/src/https-tests.ts
@@ -1,11 +1,12 @@
import * as functions from 'firebase-functions';
-import * as _ from 'lodash';
import { expectEq, TestSuite } from './testing';
-export const callableTests: any = functions.https.onCall((d) => {
+const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1';
+
+export const callableTests: any = functions.region(REGION).https.onCall((d) => {
return new TestSuite('https onCall')
- .it('should have the correct data', (data) =>
- expectEq(_.get(data, 'foo'), 'bar')
+ .it('should have the correct data', (data: any) =>
+ expectEq(data?.foo, 'bar')
)
.run(d.testId, d);
});
diff --git a/integration_test/functions/src/index.ts b/integration_test/functions/src/index.ts
index 9988fe636..6865d7dd5 100644
--- a/integration_test/functions/src/index.ts
+++ b/integration_test/functions/src/index.ts
@@ -1,3 +1,4 @@
+import { PubSub } from '@google-cloud/pubsub';
import { Request, Response } from 'express';
import * as admin from 'firebase-admin';
import * as functions from 'firebase-functions';
@@ -18,15 +19,17 @@ import * as utils from './test-utils';
import * as testLab from './testLab-utils';
import 'firebase-functions'; // temporary shim until process.env.FIREBASE_CONFIG available natively in GCF(BUG 63586213)
+import { config } from 'firebase-functions';
const firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG);
admin.initializeApp();
+const REGION = functions.config().functions.test_region;
// TODO(klimt): Get rid of this once the JS client SDK supports callable triggers.
function callHttpsTrigger(name: string, data: any, baseUrl) {
return utils.makeRequest(
{
method: 'POST',
- host: 'us-central1-' + firebaseConfig.projectId + '.' + baseUrl,
+ host: REGION + '-' + firebaseConfig.projectId + '.' + baseUrl,
path: '/' + name,
headers: {
'Content-Type': 'application/json',
@@ -36,43 +39,59 @@ function callHttpsTrigger(name: string, data: any, baseUrl) {
);
}
-function callScheduleTrigger(functionName: string, region: string) {
- return new Promise((resolve, reject) => {
+async function callScheduleTrigger(functionName: string, region: string) {
+ const accessToken = await admin.credential
+ .applicationDefault()
+ .getAccessToken();
+ return new Promise((resolve, reject) => {
const request = https.request(
{
method: 'POST',
host: 'cloudscheduler.googleapis.com',
- path: `projects/${firebaseConfig.projectId}/locations/us-central1/jobs/firebase-schedule-${functionName}-${region}:run`,
+ path: `/v1/projects/${firebaseConfig.projectId}/locations/us-central1/jobs/firebase-schedule-${functionName}-${region}:run`,
headers: {
'Content-Type': 'application/json',
+ Authorization: `Bearer ${accessToken.access_token}`,
},
},
(response) => {
+ if (response.statusCode! / 100 != 2) {
+ reject(
+ new Error('Failed request with status ' + response.statusCode!)
+ );
+ return;
+ }
let body = '';
response.on('data', (chunk) => {
body += chunk;
});
- response.on('end', () => resolve(body));
+ response.on('end', () => {
+ console.log(`Successfully scheduled function ${functionName}`);
+ resolve(body);
+ });
}
);
- request.on('error', reject);
+ request.on('error', (err) => {
+ console.error('Failed to schedule cloud scheduler job with error', err);
+ reject(err);
+ });
request.write('{}');
request.end();
});
}
export const integrationTests: any = functions
+ .region(REGION)
.runWith({
timeoutSeconds: 540,
})
- .https.onRequest((req: Request, resp: Response) => {
+ .https.onRequest(async (req: Request, resp: Response) => {
// We take the base url for our https call (cloudfunctions.net, txckloud.net, etc) from the request
// so that it changes with the environment that the tests are run in
const baseUrl = req.hostname
.split('.')
.slice(1)
.join('.');
- const pubsub: any = require('@google-cloud/pubsub')();
const testId = admin
.database()
.ref()
@@ -81,114 +100,107 @@ export const integrationTests: any = functions
.database()
.ref(`testRuns/${testId}/timestamp`)
.set(Date.now());
+ const testIdRef = admin.database().ref(`testRuns/${testId}`);
console.log('testId is: ', testId);
fs.writeFile('/tmp/' + testId + '.txt', 'test', () => {});
- return Promise.all([
- // A database write to trigger the Firebase Realtime Database tests.
- admin
- .database()
- .ref(`dbTests/${testId}/start`)
- .set({ '.sv': 'timestamp' }),
- // A Pub/Sub publish to trigger the Cloud Pub/Sub tests.
- pubsub
- .topic('pubsubTests')
- .publisher()
- .publish(Buffer.from(JSON.stringify({ testId }))),
- // A user creation to trigger the Firebase Auth user creation tests.
- admin
- .auth()
- .createUser({
- email: `${testId}@fake.com`,
- password: 'secret',
- displayName: `${testId}`,
- })
- .then((userRecord) => {
- // A user deletion to trigger the Firebase Auth user deletion tests.
- admin.auth().deleteUser(userRecord.uid);
- }),
- // A firestore write to trigger the Cloud Firestore tests.
- admin
- .firestore()
- .collection('tests')
- .doc(testId)
- .set({ test: testId }),
- // Invoke a callable HTTPS trigger.
- callHttpsTrigger('callableTests', { foo: 'bar', testId }, baseUrl),
- // A Remote Config update to trigger the Remote Config tests.
- admin.credential
- .applicationDefault()
- .getAccessToken()
- .then((accessToken) => {
- const options = {
- hostname: 'firebaseremoteconfig.googleapis.com',
- path: `/v1/projects/${firebaseConfig.projectId}/remoteConfig`,
- method: 'PUT',
- headers: {
- Authorization: 'Bearer ' + accessToken.access_token,
- 'Content-Type': 'application/json; UTF-8',
- 'Accept-Encoding': 'gzip',
- 'If-Match': '*',
- },
- };
- const request = https.request(options, (resp) => {});
- request.write(JSON.stringify({ version: { description: testId } }));
- request.end();
- }),
- // A storage upload to trigger the Storage tests
- admin
- .storage()
- .bucket()
- .upload('/tmp/' + testId + '.txt'),
- testLab.startTestRun(firebaseConfig.projectId, testId),
- // Invoke the schedule for our scheduled function to fire
- callScheduleTrigger('schedule', 'us-central1'),
- ])
- .then(() => {
- // On test completion, check that all tests pass and reply "PASS", or provide further details.
- console.log('Waiting for all tests to report they pass...');
- const ref = admin.database().ref(`testRuns/${testId}`);
- return new Promise((resolve, reject) => {
- let testsExecuted = 0;
- ref.on('child_added', (snapshot) => {
- testsExecuted += 1;
- if (snapshot.key != 'timestamp' && !snapshot.val().passed) {
- reject(
- new Error(
- `test ${snapshot.key} failed; see database for details.`
- )
- );
- return;
- }
- console.log(
- `${snapshot.key} passed (${testsExecuted} of ${numTests})`
- );
- if (testsExecuted < numTests) {
- // Not all tests have completed. Wait longer.
- return;
- }
- // All tests have passed!
- resolve();
- });
- })
- .then(() => {
- ref.off(); // No more need to listen.
- return Promise.resolve();
+ try {
+ await Promise.all([
+ // A database write to trigger the Firebase Realtime Database tests.
+ admin
+ .database()
+ .ref(`dbTests/${testId}/start`)
+ .set({ '.sv': 'timestamp' }),
+ // A Pub/Sub publish to trigger the Cloud Pub/Sub tests.
+ new PubSub()
+ .topic('pubsubTests')
+ .publish(Buffer.from(JSON.stringify({ testId }))),
+ // A user creation to trigger the Firebase Auth user creation tests.
+ admin
+ .auth()
+ .createUser({
+ email: `${testId}@fake.com`,
+ password: 'secret',
+ displayName: `${testId}`,
})
- .catch((err) => {
- ref.off(); // No more need to listen.
- return Promise.reject(err);
- });
- })
- .then(() => {
- console.log('All tests pass!');
- resp.status(200).send('PASS \n');
- })
- .catch((err) => {
- console.log(`Some tests failed: ${err}`);
- resp
- .status(500)
- .send(
- `FAIL - details at https://${process.env.GCLOUD_PROJECT}.firebaseio.com/testRuns/${testId}`
+ .then((userRecord) => {
+ // A user deletion to trigger the Firebase Auth user deletion tests.
+ admin.auth().deleteUser(userRecord.uid);
+ }),
+ // A firestore write to trigger the Cloud Firestore tests.
+ admin
+ .firestore()
+ .collection('tests')
+ .doc(testId)
+ .set({ test: testId }),
+ // Invoke a callable HTTPS trigger.
+ callHttpsTrigger('callableTests', { foo: 'bar', testId }, baseUrl),
+ // A Remote Config update to trigger the Remote Config tests.
+ admin.credential
+ .applicationDefault()
+ .getAccessToken()
+ .then((accessToken) => {
+ const options = {
+ hostname: 'firebaseremoteconfig.googleapis.com',
+ path: `/v1/projects/${firebaseConfig.projectId}/remoteConfig`,
+ method: 'PUT',
+ headers: {
+ Authorization: 'Bearer ' + accessToken.access_token,
+ 'Content-Type': 'application/json; UTF-8',
+ 'Accept-Encoding': 'gzip',
+ 'If-Match': '*',
+ },
+ };
+ const request = https.request(options, (resp) => {});
+ request.write(JSON.stringify({ version: { description: testId } }));
+ request.end();
+ }),
+ // A storage upload to trigger the Storage tests
+ admin
+ .storage()
+ .bucket()
+ .upload('/tmp/' + testId + '.txt'),
+ testLab.startTestRun(firebaseConfig.projectId, testId),
+ // Invoke the schedule for our scheduled function to fire
+ callScheduleTrigger('schedule', 'us-central1'),
+ ]);
+
+ // On test completion, check that all tests pass and reply "PASS", or provide further details.
+ console.log('Waiting for all tests to report they pass...');
+ await new Promise((resolve, reject) => {
+ setTimeout(() => reject(new Error('Timeout')), 5 * 60 * 1000);
+ let testsExecuted = 0;
+ testIdRef.on('child_added', (snapshot) => {
+ testsExecuted += 1;
+ if (snapshot.key != 'timestamp' && !snapshot.val().passed) {
+ reject(
+ new Error(
+ `test ${snapshot.key} failed; see database for details.`
+ )
+ );
+ return;
+ }
+ console.log(
+ `${snapshot.key} passed (${testsExecuted} of ${numTests})`
);
+ if (testsExecuted < numTests) {
+ // Not all tests have completed. Wait longer.
+ return;
+ }
+ // All tests have passed!
+ resolve();
+ });
});
+ console.log('All tests pass!');
+ resp.status(200).send('PASS \n');
+ } catch (err) {
+ console.log(`Some tests failed: ${err}`);
+ resp
+ .status(500)
+ .send(
+ `FAIL - details at ${functions.firebaseConfig()
+ .databaseURL!}/testRuns/${testId}`
+ );
+ } finally {
+ testIdRef.off('child_added');
+ }
});
diff --git a/integration_test/functions/src/pubsub-tests.ts b/integration_test/functions/src/pubsub-tests.ts
index 3c1b1ecc7..a21c2011a 100644
--- a/integration_test/functions/src/pubsub-tests.ts
+++ b/integration_test/functions/src/pubsub-tests.ts
@@ -3,10 +3,13 @@ import * as functions from 'firebase-functions';
import { evaluate, expectEq, success, TestSuite } from './testing';
import PubsubMessage = functions.pubsub.Message;
+const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1';
+
// TODO(inlined) use multiple queues to run inline.
// Expected message data: {"hello": "world"}
-export const pubsubTests: any = functions.pubsub
- .topic('pubsubTests')
+export const pubsubTests: any = functions
+ .region(REGION)
+ .pubsub.topic('pubsubTests')
.onPublish((m, c) => {
let testId: string;
try {
@@ -59,23 +62,19 @@ export const pubsubTests: any = functions.pubsub
.run(testId, m, c);
});
-export const schedule: any = functions.pubsub
- .schedule('every 10 hours') // This is a dummy schedule, since we need to put a valid one in.
+export const schedule: any = functions
+ .region(REGION)
+ .pubsub.schedule('every 10 hours') // This is a dummy schedule, since we need to put a valid one in.
// For the test, the job is triggered by the jobs:run api
- .onRun((context) => {
- let testId;
+ .onRun(async (context) => {
const db = admin.database();
- return new Promise(async (resolve, reject) => {
- await db
- .ref('testRuns')
- .orderByChild('timestamp')
- .limitToLast(1)
- .on('value', (snap) => {
- testId = Object.keys(snap.val())[0];
- new TestSuite('pubsub scheduleOnRun')
- .it('should trigger when the scheduler fires', () => success())
- .run(testId, null);
- });
- resolve();
- });
+ const snap = await db
+ .ref('testRuns')
+ .orderByChild('timestamp')
+ .limitToLast(1)
+ .once('value');
+ const testId = Object.keys(snap.val())[0];
+ return new TestSuite('pubsub scheduleOnRun')
+ .it('should trigger when the scheduler fires', () => success())
+ .run(testId, null);
});
diff --git a/integration_test/functions/src/remoteConfig-tests.ts b/integration_test/functions/src/remoteConfig-tests.ts
index 3474e1fc2..3f2cc8993 100644
--- a/integration_test/functions/src/remoteConfig-tests.ts
+++ b/integration_test/functions/src/remoteConfig-tests.ts
@@ -2,8 +2,11 @@ import * as functions from 'firebase-functions';
import { expectEq, TestSuite } from './testing';
import TemplateVersion = functions.remoteConfig.TemplateVersion;
-export const remoteConfigTests: any = functions.remoteConfig.onUpdate(
- (v, c) => {
+const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1';
+
+export const remoteConfigTests: any = functions
+ .region(REGION)
+ .remoteConfig.onUpdate((v, c) => {
return new TestSuite('remoteConfig onUpdate')
.it('should have a project as resource', (version, context) =>
expectEq(
@@ -25,5 +28,4 @@ export const remoteConfigTests: any = functions.remoteConfig.onUpdate(
)
.run(v.description, v, c);
- }
-);
+ });
diff --git a/integration_test/functions/src/storage-tests.ts b/integration_test/functions/src/storage-tests.ts
index 43cdef5e2..df3032f78 100644
--- a/integration_test/functions/src/storage-tests.ts
+++ b/integration_test/functions/src/storage-tests.ts
@@ -2,10 +2,13 @@ import * as functions from 'firebase-functions';
import { expectEq, TestSuite } from './testing';
import ObjectMetadata = functions.storage.ObjectMetadata;
+const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1';
+
export const storageTests: any = functions
.runWith({
timeoutSeconds: 540,
})
+ .region(REGION)
.storage.bucket()
.object()
.onFinalize((s, c) => {
diff --git a/integration_test/functions/src/testLab-tests.ts b/integration_test/functions/src/testLab-tests.ts
index a6350c57d..8e064928a 100644
--- a/integration_test/functions/src/testLab-tests.ts
+++ b/integration_test/functions/src/testLab-tests.ts
@@ -1,12 +1,13 @@
import * as functions from 'firebase-functions';
-import * as _ from 'lodash';
-import { TestSuite, expectEq } from './testing';
+import { expectEq, TestSuite } from './testing';
import TestMatrix = functions.testLab.TestMatrix;
+const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1';
export const testLabTests: any = functions
.runWith({
timeoutSeconds: 540,
})
+ .region(REGION)
.testLab.testMatrix()
.onComplete((matrix, context) => {
return new TestSuite('test matrix complete')
@@ -22,5 +23,5 @@ export const testLabTests: any = functions
expectEq(matrix.state, 'INVALID')
)
- .run(_.get(matrix, 'clientInfo.details.testId'), matrix, context);
+ .run(matrix?.clientInfo?.details?.testId, matrix, context);
});
diff --git a/integration_test/functions/src/testLab-utils.ts b/integration_test/functions/src/testLab-utils.ts
index 2f457e70d..3dbb7b763 100644
--- a/integration_test/functions/src/testLab-utils.ts
+++ b/integration_test/functions/src/testLab-utils.ts
@@ -1,7 +1,6 @@
+import * as admin from 'firebase-admin';
import * as http from 'http';
import * as https from 'https';
-import * as admin from 'firebase-admin';
-import * as _ from 'lodash';
import * as utils from './test-utils';
interface AndroidDevice {
@@ -35,7 +34,7 @@ async function fetchDefaultDevice(
requestOptions(accessToken, 'GET', '/v1/testEnvironmentCatalog/ANDROID')
);
const data = JSON.parse(response);
- const models = _.get(data, 'androidDeviceCatalog.models', []);
+ const models = data?.androidDeviceCatalog?.models || [];
const defaultModels = models.filter(
(m) =>
m.tags !== undefined &&
@@ -51,12 +50,12 @@ async function fetchDefaultDevice(
const model = defaultModels[0];
const versions = model.supportedVersionIds;
- return {
+ return {
androidModelId: model.id,
androidVersionId: versions[versions.length - 1],
locale: 'en',
orientation: 'portrait',
- };
+ } as AndroidDevice;
}
function createTestMatrix(
@@ -71,7 +70,7 @@ function createTestMatrix(
'/v1/projects/' + projectId + '/testMatrices'
);
const body = {
- projectId: projectId,
+ projectId,
testSpecification: {
androidRoboTest: {
appApk: {
@@ -106,9 +105,9 @@ function requestOptions(
path: string
): https.RequestOptions {
return {
- method: method,
+ method,
hostname: TESTING_API_SERVICE_NAME,
- path: path,
+ path,
headers: {
Authorization: 'Bearer ' + accessToken.access_token,
'Content-Type': 'application/json',
diff --git a/integration_test/functions/src/testing.ts b/integration_test/functions/src/testing.ts
index 43d3e69d5..1cb9f7819 100644
--- a/integration_test/functions/src/testing.ts
+++ b/integration_test/functions/src/testing.ts
@@ -1,6 +1,5 @@
import * as firebase from 'firebase-admin';
import { EventContext } from 'firebase-functions';
-import * as _ from 'lodash';
export type TestCase = (data: T, context?: EventContext) => any;
export interface TestCaseMap {
@@ -66,43 +65,70 @@ function failure(reason: string) {
return Promise.reject(reason);
}
-export function evaluate(value, errMsg) {
+export function evaluate(value: boolean, errMsg: string) {
if (value) {
return success();
}
return failure(errMsg);
}
-export function expectEq(left, right) {
+export function expectEq(left: any, right: any) {
return evaluate(
- left === right,
+ left == right,
JSON.stringify(left) + ' does not equal ' + JSON.stringify(right)
);
}
-export function expectDeepEq(left, right) {
+function deepEq(left: any, right: any) {
+ if (left === right) {
+ return true;
+ }
+
+ if (!(left instanceof Object && right instanceof Object)) {
+ return false;
+ }
+
+ if (Object.keys(left).length != Object.keys(right).length) {
+ return false;
+ }
+
+ for (const key in left) {
+ if (!right.hasOwnProperty(key)) {
+ return false;
+ }
+ if (!deepEq(left[key], right[key])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+export function expectDeepEq(left: any, right: any) {
return evaluate(
- _.isEqual(left, right),
- JSON.stringify(left) + ' does not equal ' + JSON.stringify(right)
+ deepEq(left, right),
+ `${JSON.stringify(left)} does not deep equal ${JSON.stringify(right)}`
);
}
-export function expectMatches(input: string, regexp) {
+export function expectMatches(input: string, regexp: RegExp) {
return evaluate(
- input.match(regexp),
+ input.match(regexp) !== null,
"Input '" + input + "' did not match regexp '" + regexp + "'"
);
}
-export function expectReject(f) {
- return function(event) {
- return Promise.resolve()
- .then(() => f(event))
- .then(
- () => {
- throw new Error('Test should have returned a rejected promise');
- },
- () => true // A rejection is what we expected, and so is a positive result.
- );
+export function expectReject(f: (e: EventType) => Promise) {
+ return async (event: EventType) => {
+ let rejected = false;
+ try {
+ await f(event);
+ } catch {
+ rejected = true;
+ }
+
+ if (!rejected) {
+ throw new Error('Test should have returned a rejected promise');
+ }
};
}
diff --git a/integration_test/package.node8.json b/integration_test/package.json.template
similarity index 55%
rename from integration_test/package.node8.json
rename to integration_test/package.json.template
index 8803f2fdc..5bcd8761b 100644
--- a/integration_test/package.node8.json
+++ b/integration_test/package.json.template
@@ -5,19 +5,17 @@
"build": "./node_modules/.bin/tsc"
},
"dependencies": {
- "@google-cloud/pubsub": "~0.19.0",
- "@types/google-cloud__pubsub": "^0.18.0",
- "@types/lodash": "~4.14.41",
- "firebase-admin": "^8.0.0",
- "firebase-functions": "./firebase-functions.tgz",
+ "@google-cloud/pubsub": "^2.10.0",
+ "firebase-admin": "^9.1.0",
+ "firebase-functions": "__SDK_TARBALL__",
"lodash": "~4.17.2"
},
"main": "lib/index.js",
"devDependencies": {
- "typescript": "~3.6.0"
+ "typescript": "~4.2.2"
},
"engines": {
- "node": "8"
+ "node": "__NODE_VERSION__"
},
"private": true
}
diff --git a/integration_test/package.node10.json b/integration_test/package.node10.json
deleted file mode 100644
index cf5eeeb25..000000000
--- a/integration_test/package.node10.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "name": "functions",
- "description": "Integration test for the Firebase SDK for Google Cloud Functions",
- "scripts": {
- "build": "./node_modules/.bin/tsc"
- },
- "dependencies": {
- "@google-cloud/pubsub": "~0.19.0",
- "@types/google-cloud__pubsub": "^0.18.0",
- "@types/lodash": "~4.14.41",
- "firebase-admin": "^8.0.0",
- "firebase-functions": "./firebase-functions.tgz",
- "lodash": "~4.17.2"
- },
- "main": "lib/index.js",
- "devDependencies": {
- "typescript": "~3.6.0"
- },
- "engines": {
- "node": "10"
- },
- "private": true
-}
diff --git a/integration_test/run_tests.sh b/integration_test/run_tests.sh
index 903936b13..100e24a32 100755
--- a/integration_test/run_tests.sh
+++ b/integration_test/run_tests.sh
@@ -38,21 +38,36 @@ function build_sdk {
mv firebase-functions-*.tgz "integration_test/functions/firebase-functions-${TIMESTAMP}.tgz"
}
-function pick_node8 {
- cd "${DIR}"
- cp package.node8.json functions/package.json
- # we have to do the -e flag here so that it work both on linux and mac os, but that creates an extra
- # backup file called package.json-e that we should clean up afterwards.
- sed -i -e "s/firebase-functions.tgz/firebase-functions-${TIMESTAMP}.tgz/g" functions/package.json
- rm -f functions/package.json-e
+function set_region {
+ if [[ "${FIREBASE_FUNCTIONS_TEST_REGION}" == "" ]]; then
+ FIREBASE_FUNCTIONS_TEST_REGION="us-central1"
+ fi
+ if [[ "${TOKEN}" == "" ]]; then
+ firebase functions:config:set functions.test_region=$FIREBASE_FUNCTIONS_TEST_REGION --project=$PROJECT_ID
+ else
+ firebase functions:config:set functions.test_region=$FIREBASE_FUNCTIONS_TEST_REGION --project=$PROJECT_ID --token=$TOKEN
+ fi
+ announce "Set region to ${FIREBASE_FUNCTIONS_TEST_REGION}"
}
-function pick_node10 {
+function unset_region {
+ if [[ "${TOKEN}" == "" ]]; then
+ firebase functions:config:unset functions.test_region --project=$PROJECT_ID
+ else
+ firebase functions:config:unset functions.test_region --project=$PROJECT_ID --token=$TOKEN
+ fi
+}
+
+# Creates a Package.json from package.json.template
+# @param timestmap of the current SDK build
+# @param Node version to test under
+function create_package_json {
cd "${DIR}"
- cp package.node10.json functions/package.json
+ cp package.json.template functions/package.json
# we have to do the -e flag here so that it work both on linux and mac os, but that creates an extra
# backup file called package.json-e that we should clean up afterwards.
- sed -i -e "s/firebase-functions.tgz/firebase-functions-${TIMESTAMP}.tgz/g" functions/package.json
+ sed -i -e "s/__SDK_TARBALL__/firebase-functions-$1.tgz/g" functions/package.json
+ sed -i -e "s/__NODE_VERSION__/$2/g" functions/package.json
rm -f functions/package.json-e
}
@@ -98,7 +113,10 @@ function run_tests {
if [[ "${FIREBASE_FUNCTIONS_URL}" == "https://preprod-cloudfunctions.sandbox.googleapis.com" ]]; then
TEST_DOMAIN="txcloud.net"
fi
- TEST_URL="https://us-central1-${PROJECT_ID}.${TEST_DOMAIN}/integrationTests"
+ if [[ "${FIREBASE_FUNCTIONS_TEST_REGION}" == "" ]]; then
+ FIREBASE_FUNCTIONS_TEST_REGION="us-central1"
+ fi
+ TEST_URL="https://${FIREBASE_FUNCTIONS_TEST_REGION}-${PROJECT_ID}.${TEST_DOMAIN}/integrationTests"
echo "${TEST_URL}"
curl --fail "${TEST_URL}"
@@ -107,6 +125,7 @@ function run_tests {
function cleanup {
announce "Performing cleanup..."
delete_all_functions
+ unset_region
rm "${DIR}/functions/firebase-functions-${TIMESTAMP}.tgz"
rm "${DIR}/functions/package.json"
rm -f "${DIR}/functions/firebase-debug.log"
@@ -117,20 +136,15 @@ function cleanup {
# Setup
build_sdk
delete_all_functions
-
-# Node 8 tests
-pick_node8
-install_deps
-announce "Deploying functions to Node 8 runtime ..."
-deploy
-run_tests
-
-# Node 10 tests
-pick_node10
-install_deps
-announce "Re-deploying the same functions to Node 10 runtime ..."
-deploy
-run_tests
+set_region
+
+for version in 10 12 14; do
+ create_package_json $TIMESTAMP $version
+ install_deps
+ announce "Re-deploying the same functions to Node $version runtime ..."
+ deploy
+ run_tests
+done
# Cleanup
cleanup
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 000000000..8a4fd6be8
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,4727 @@
+{
+ "name": "firebase-functions",
+ "version": "3.13.2",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@firebase/app-types": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.6.2.tgz",
+ "integrity": "sha512-2VXvq/K+n8XMdM4L2xy5bYp2ZXMawJXluUIDzUBvMthVR+lhxK4pfFiqr1mmDbv9ydXvEAuFsD+6DpcZuJcSSw==",
+ "dev": true
+ },
+ "@firebase/auth-interop-types": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.6.tgz",
+ "integrity": "sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g==",
+ "dev": true
+ },
+ "@firebase/component": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.0.tgz",
+ "integrity": "sha512-v18csWtXb0ri+3m7wuGLY/UDgcb89vuMlZGQ//+7jEPLIQeLbylvZhol1uzW9WzoOpxMxOS2W5qyVGX36wZvEA==",
+ "dev": true,
+ "requires": {
+ "@firebase/util": "1.1.0",
+ "tslib": "^2.1.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
+ "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==",
+ "dev": true
+ }
+ }
+ },
+ "@firebase/database": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.10.0.tgz",
+ "integrity": "sha512-GsHvuES83Edtboij2h3txKg+yV/TD4b5Owc01SgXEQtvj1lulkHt4Ufmd9OZz1WreWQJMIqKpbVowIDHjlkZJQ==",
+ "dev": true,
+ "requires": {
+ "@firebase/auth-interop-types": "0.1.6",
+ "@firebase/component": "0.5.0",
+ "@firebase/database-types": "0.7.2",
+ "@firebase/logger": "0.2.6",
+ "@firebase/util": "1.1.0",
+ "faye-websocket": "0.11.3",
+ "tslib": "^2.1.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
+ "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==",
+ "dev": true
+ }
+ }
+ },
+ "@firebase/database-types": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.7.2.tgz",
+ "integrity": "sha512-cdAd/dgwvC0r3oLEDUR+ULs1vBsEvy0b27nlzKhU6LQgm9fCDzgaH9nFGv8x+S9dly4B0egAXkONkVoWcOAisg==",
+ "dev": true,
+ "requires": {
+ "@firebase/app-types": "0.6.2"
+ }
+ },
+ "@firebase/logger": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.2.6.tgz",
+ "integrity": "sha512-KIxcUvW/cRGWlzK9Vd2KB864HlUnCfdTH0taHE0sXW5Xl7+W68suaeau1oKNEqmc3l45azkd4NzXTCWZRZdXrw==",
+ "dev": true
+ },
+ "@firebase/util": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.1.0.tgz",
+ "integrity": "sha512-lfuSASuPKNdfebuFR8rjFamMQUPH9iiZHcKS755Rkm/5gRT0qC7BMhCh3ZkHf7NVbplzIc/GhmX2jM+igDRCag==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.1.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
+ "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==",
+ "dev": true
+ }
+ }
+ },
+ "@google-cloud/common": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.6.0.tgz",
+ "integrity": "sha512-aHIFTqJZmeTNO9md8XxV+ywuvXF3xBm5WNmgWeeCK+XN5X+kGW0WEX94wGwj+/MdOnrVf4dL2RvSIt9J5yJG6Q==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "@google-cloud/projectify": "^2.0.0",
+ "@google-cloud/promisify": "^2.0.0",
+ "arrify": "^2.0.1",
+ "duplexify": "^4.1.1",
+ "ent": "^2.2.0",
+ "extend": "^3.0.2",
+ "google-auth-library": "^7.0.2",
+ "retry-request": "^4.1.1",
+ "teeny-request": "^7.0.0"
+ }
+ },
+ "@google-cloud/firestore": {
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.11.0.tgz",
+ "integrity": "sha512-Do9WJzEkFBBB+zVFvFfrrrIFEz086lrdgKQic7XsdoTgtYtq0yMu2u3kGLyxMbdasl2c2yf49FE4YvO3AYjQMQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "functional-red-black-tree": "^1.0.1",
+ "google-gax": "^2.9.2",
+ "protobufjs": "^6.8.6"
+ }
+ },
+ "@google-cloud/paginator": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.5.tgz",
+ "integrity": "sha512-N4Uk4BT1YuskfRhKXBs0n9Lg2YTROZc6IMpkO/8DIHODtm5s3xY8K5vVBo23v/2XulY3azwITQlYWgT4GdLsUw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "arrify": "^2.0.0",
+ "extend": "^3.0.2"
+ }
+ },
+ "@google-cloud/projectify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz",
+ "integrity": "sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@google-cloud/promisify": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz",
+ "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==",
+ "dev": true,
+ "optional": true
+ },
+ "@google-cloud/storage": {
+ "version": "5.8.5",
+ "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.8.5.tgz",
+ "integrity": "sha512-i0gB9CRwQeOBYP7xuvn14M40LhHCwMjceBjxE4CTvsqL519sVY5yVKxLiAedHWGwUZHJNRa7Q2CmNfkdRwVNPg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "@google-cloud/common": "^3.6.0",
+ "@google-cloud/paginator": "^3.0.0",
+ "@google-cloud/promisify": "^2.0.0",
+ "arrify": "^2.0.0",
+ "async-retry": "^1.3.1",
+ "compressible": "^2.0.12",
+ "date-and-time": "^1.0.0",
+ "duplexify": "^4.0.0",
+ "extend": "^3.0.2",
+ "gaxios": "^4.0.0",
+ "gcs-resumable-upload": "^3.1.4",
+ "get-stream": "^6.0.0",
+ "hash-stream-validation": "^0.2.2",
+ "mime": "^2.2.0",
+ "mime-types": "^2.0.8",
+ "onetime": "^5.1.0",
+ "p-limit": "^3.0.1",
+ "pumpify": "^2.0.0",
+ "snakeize": "^0.1.0",
+ "stream-events": "^1.0.1",
+ "xdg-basedir": "^4.0.0"
+ },
+ "dependencies": {
+ "mime": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz",
+ "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==",
+ "dev": true,
+ "optional": true
+ },
+ "p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "yocto-queue": "^0.1.0"
+ }
+ }
+ }
+ },
+ "@grpc/grpc-js": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.3.0.tgz",
+ "integrity": "sha512-fiL7ZaGg2HBiFtmv6m34d5jEgEtNXfctjzB3f7b3iuT7olBX4mHLMOqOBmGTTSOTfNRQJH5+vsyk6mEz3I0Q7Q==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "@types/node": ">=12.12.47"
+ },
+ "dependencies": {
+ "@types/node": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz",
+ "integrity": "sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "@grpc/proto-loader": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.1.tgz",
+ "integrity": "sha512-4DIvEOZhw5nGj3RQngIoiMXRsre3InEH136krZTcirs/G2em3WMXdtx4Lqlnb4E2ertbWGs5gPeVDKU5BHffXw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "@types/long": "^4.0.1",
+ "lodash.camelcase": "^4.3.0",
+ "long": "^4.0.0",
+ "protobufjs": "^6.10.0",
+ "yargs": "^16.1.1"
+ },
+ "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,
+ "optional": true
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "optional": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "optional": 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,
+ "optional": true
+ },
+ "string-width": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+ "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "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,
+ "optional": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "optional": true
+ },
+ "yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "20.2.7",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz",
+ "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "@panva/asn1.js": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz",
+ "integrity": "sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==",
+ "dev": true
+ },
+ "@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=",
+ "dev": true,
+ "optional": true
+ },
+ "@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+ "dev": true,
+ "optional": true
+ },
+ "@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
+ "dev": true,
+ "optional": true
+ },
+ "@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=",
+ "dev": true,
+ "optional": true
+ },
+ "@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=",
+ "dev": true,
+ "optional": true
+ },
+ "@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=",
+ "dev": true,
+ "optional": true
+ },
+ "@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=",
+ "dev": true,
+ "optional": true
+ },
+ "@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=",
+ "dev": true,
+ "optional": true
+ },
+ "@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=",
+ "dev": true,
+ "optional": true
+ },
+ "@sinonjs/commons": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz",
+ "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==",
+ "dev": true,
+ "requires": {
+ "type-detect": "4.0.8"
+ }
+ },
+ "@sinonjs/formatio": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz",
+ "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==",
+ "dev": true,
+ "requires": {
+ "@sinonjs/commons": "^1",
+ "@sinonjs/samsam": "^3.1.0"
+ }
+ },
+ "@sinonjs/samsam": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz",
+ "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==",
+ "dev": true,
+ "requires": {
+ "@sinonjs/commons": "^1.3.0",
+ "array-from": "^2.1.1",
+ "lodash": "^4.17.15"
+ }
+ },
+ "@sinonjs/text-encoding": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz",
+ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
+ "dev": true
+ },
+ "@tootallnate/once": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "dev": true,
+ "optional": true
+ },
+ "@types/body-parser": {
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
+ "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
+ "requires": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/chai": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.12.tgz",
+ "integrity": "sha512-aN5IAC8QNtSUdQzxu7lGBgYAOuU1tmRU4c9dIq5OKGf/SBVjXo+ffM2wEjudAWbgpOhy60nLoAGH1xm8fpCKFQ==",
+ "dev": true
+ },
+ "@types/chai-as-promised": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.3.tgz",
+ "integrity": "sha512-FQnh1ohPXJELpKhzjuDkPLR2BZCAqed+a6xV4MI/T3XzHfd2FlarfUGUdZYgqYe8oxkYn0fchHEeHfHqdZ96sg==",
+ "dev": true,
+ "requires": {
+ "@types/chai": "*"
+ }
+ },
+ "@types/color-name": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
+ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
+ "dev": true
+ },
+ "@types/connect": {
+ "version": "3.4.33",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
+ "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-GmK8AKu8i+s+EChK/uZ5IbrXPcPaQKWaNSGevDT/7o3gFObwSUQwqb1jMqxuo+YPvj0ckGzINI+EO7EHcmJjKg==",
+ "dev": true,
+ "requires": {
+ "@types/express": "*"
+ }
+ },
+ "@types/express": {
+ "version": "4.17.3",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.3.tgz",
+ "integrity": "sha512-I8cGRJj3pyOLs/HndoP+25vOqhqWkAZsWMEmq1qXy/b/M3ppufecUwaK2/TVDVxcV61/iSdhykUjQQ2DLSrTdg==",
+ "requires": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "@types/express-jwt": {
+ "version": "0.0.42",
+ "resolved": "https://registry.npmjs.org/@types/express-jwt/-/express-jwt-0.0.42.tgz",
+ "integrity": "sha512-WszgUddvM1t5dPpJ3LhWNH8kfNN8GPIBrAGxgIYXVCEGx6Bx4A036aAuf/r5WH9DIEdlmp7gHOYvSM6U87B0ag==",
+ "dev": true,
+ "requires": {
+ "@types/express": "*",
+ "@types/express-unless": "*"
+ }
+ },
+ "@types/express-serve-static-core": {
+ "version": "4.17.12",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.12.tgz",
+ "integrity": "sha512-EaEdY+Dty1jEU7U6J4CUWwxL+hyEGMkO5jan5gplfegUgCUsIUWqXxqw47uGjimeT4Qgkz/XUfwoau08+fgvKA==",
+ "requires": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*"
+ }
+ },
+ "@types/express-unless": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.1.tgz",
+ "integrity": "sha512-5fuvg7C69lemNgl0+v+CUxDYWVPSfXHhJPst4yTLcqi4zKJpORCxnDrnnilk3k0DTq/WrAUdvXFs01+vUqUZHw==",
+ "dev": true,
+ "requires": {
+ "@types/express": "*"
+ }
+ },
+ "@types/jsonwebtoken": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.3.2.tgz",
+ "integrity": "sha512-Mkjljd9DTpkPlrmGfTJvcP4aBU7yO2QmW7wNVhV4/6AEUxYoacqU7FJU/N0yFEHTsIrE4da3rUrjrR5ejicFmA==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/lodash": {
+ "version": "4.14.161",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.161.tgz",
+ "integrity": "sha512-EP6O3Jkr7bXvZZSZYlsgt5DIjiGr0dXP1/jVEwVLTFgg0d+3lWVQkRavYVQszV7dYUwvg0B8R0MBDpcmXg7XIA==",
+ "dev": true
+ },
+ "@types/long": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
+ "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==",
+ "dev": true,
+ "optional": true
+ },
+ "@types/mime": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz",
+ "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q=="
+ },
+ "@types/mocha": {
+ "version": "5.2.7",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
+ "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==",
+ "dev": true
+ },
+ "@types/mock-require": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@types/mock-require/-/mock-require-2.0.0.tgz",
+ "integrity": "sha512-nOgjoE5bBiDeiA+z41i95makyHUSMWQMOPocP+J67Pqx/68HAXaeWN1NFtrAYYV6LrISIZZ8vKHm/a50k0f6Sg==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/nock": {
+ "version": "10.0.3",
+ "resolved": "https://registry.npmjs.org/@types/nock/-/nock-10.0.3.tgz",
+ "integrity": "sha512-OthuN+2FuzfZO3yONJ/QVjKmLEuRagS9TV9lEId+WHL9KhftYG+/2z+pxlr0UgVVXSpVD8woie/3fzQn8ft/Ow==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/node": {
+ "version": "8.10.63",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.63.tgz",
+ "integrity": "sha512-g+nSkeHFDd2WOQChfmy9SAXLywT47WZBrGS/NC5ym5PJ8c8RC6l4pbGaUW/X0+eZJnXw6/AVNEouXWhV4iz72Q=="
+ },
+ "@types/qs": {
+ "version": "6.9.4",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz",
+ "integrity": "sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ=="
+ },
+ "@types/range-parser": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
+ "integrity": "sha1-fuMwunyq+5gJC+zoal7kQRWQTCw="
+ },
+ "@types/serve-static": {
+ "version": "1.13.5",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.5.tgz",
+ "integrity": "sha512-6M64P58N+OXjU432WoLLBQxbA0LRGBCRm7aAGQJ+SMC1IMl0dgRVi9EFfoDcS2a7Xogygk/eGN94CfwU9UF7UQ==",
+ "requires": {
+ "@types/express-serve-static-core": "*",
+ "@types/mime": "*"
+ }
+ },
+ "@types/sinon": {
+ "version": "7.5.2",
+ "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.5.2.tgz",
+ "integrity": "sha512-T+m89VdXj/eidZyejvmoP9jivXgBDdkOSBVQjU9kF349NEx10QdPNGxHeZUaj1IlJ32/ewdyXJjnJxyxJroYwg==",
+ "dev": true
+ },
+ "abab": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
+ "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
+ "dev": true
+ },
+ "abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "event-target-shim": "^5.0.0"
+ }
+ },
+ "accepts": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+ "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+ "requires": {
+ "mime-types": "~2.1.24",
+ "negotiator": "0.6.2"
+ }
+ },
+ "acorn": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
+ "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==",
+ "dev": true
+ },
+ "acorn-globals": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz",
+ "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==",
+ "dev": true,
+ "requires": {
+ "acorn": "^7.1.1",
+ "acorn-walk": "^7.1.1"
+ }
+ },
+ "acorn-walk": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
+ "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
+ "dev": true
+ },
+ "agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "debug": "4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+ "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "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,
+ "optional": true
+ }
+ }
+ },
+ "ajv": {
+ "version": "6.12.5",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
+ "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-colors": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
+ "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "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"
+ }
+ },
+ "any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=",
+ "dev": true
+ },
+ "arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+ },
+ "array-from": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz",
+ "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=",
+ "dev": true
+ },
+ "arrify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+ "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+ "dev": true,
+ "optional": true
+ },
+ "asn1": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+ "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=",
+ "dev": true,
+ "requires": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "asn1.js": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
+ "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0",
+ "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=",
+ "dev": true
+ },
+ "assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha1-5gtrDo8wG9l+U3UhW9pAbIURjAs=",
+ "dev": true
+ },
+ "async-retry": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz",
+ "integrity": "sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "retry": "0.12.0"
+ }
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+ "dev": true
+ },
+ "at-least-node": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "dev": true
+ },
+ "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=",
+ "dev": true
+ },
+ "aws4": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz",
+ "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "optional": true
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+ "dev": true,
+ "requires": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "bignumber.js": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
+ "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==",
+ "dev": true,
+ "optional": true
+ },
+ "bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true
+ },
+ "body-parser": {
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+ "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+ "requires": {
+ "bytes": "3.1.0",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "http-errors": "1.7.2",
+ "iconv-lite": "0.4.24",
+ "on-finished": "~2.3.0",
+ "qs": "6.7.0",
+ "raw-body": "2.4.0",
+ "type-is": "~1.6.17"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
+ "dev": true
+ },
+ "browser-process-hrtime": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
+ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==",
+ "dev": true
+ },
+ "browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha1-uqVZ7hTO1zRSIputcyZGfGH6vWA=",
+ "dev": true
+ },
+ "buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=",
+ "dev": true
+ },
+ "buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=",
+ "dev": true
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+ "dev": true
+ },
+ "bytes": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+ "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+ "dev": true
+ },
+ "chai": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
+ "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
+ "dev": true,
+ "requires": {
+ "assertion-error": "^1.1.0",
+ "check-error": "^1.0.2",
+ "deep-eql": "^3.0.1",
+ "get-func-name": "^2.0.0",
+ "pathval": "^1.1.0",
+ "type-detect": "^4.0.5"
+ }
+ },
+ "chai-as-promised": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz",
+ "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==",
+ "dev": true,
+ "requires": {
+ "check-error": "^1.0.2"
+ }
+ },
+ "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"
+ },
+ "dependencies": {
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "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"
+ }
+ }
+ }
+ },
+ "check-error": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
+ "dev": true
+ },
+ "child-process-promise": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/child-process-promise/-/child-process-promise-2.2.1.tgz",
+ "integrity": "sha1-RzChHvYQ+tRQuPIjx50x172tgHQ=",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^4.0.2",
+ "node-version": "^1.0.0",
+ "promise-polyfill": "^6.0.1"
+ }
+ },
+ "cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.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
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "commander": {
+ "version": "2.17.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
+ "integrity": "sha1-vXerfebelCBc6sxy8XFtKfIKd78=",
+ "dev": true
+ },
+ "compressible": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+ "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "mime-db": ">= 1.43.0 < 2"
+ },
+ "dependencies": {
+ "mime-db": {
+ "version": "1.47.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz",
+ "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "configstore": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz",
+ "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "dot-prop": "^5.2.0",
+ "graceful-fs": "^4.1.2",
+ "make-dir": "^3.0.0",
+ "unique-string": "^2.0.0",
+ "write-file-atomic": "^3.0.0",
+ "xdg-basedir": "^4.0.0"
+ }
+ },
+ "content-disposition": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
+ "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+ "requires": {
+ "safe-buffer": "5.1.2"
+ }
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js="
+ },
+ "cookie": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
+ "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
+ },
+ "cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+ },
+ "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=",
+ "dev": true
+ },
+ "cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha1-6sEdpRWS3Ya58G9uesKTs9+HXSk=",
+ "requires": {
+ "object-assign": "^4",
+ "vary": "^1"
+ }
+ },
+ "cross-spawn": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz",
+ "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^4.0.1",
+ "which": "^1.2.9"
+ }
+ },
+ "crypto-random-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
+ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
+ "dev": true,
+ "optional": true
+ },
+ "cssom": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
+ "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==",
+ "dev": true
+ },
+ "cssstyle": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
+ "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
+ "dev": true,
+ "requires": {
+ "cssom": "~0.3.6"
+ },
+ "dependencies": {
+ "cssom": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
+ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
+ "dev": true
+ }
+ }
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "data-urls": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz",
+ "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==",
+ "dev": true,
+ "requires": {
+ "abab": "^2.0.3",
+ "whatwg-mimetype": "^2.3.0",
+ "whatwg-url": "^8.0.0"
+ }
+ },
+ "date-and-time": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-1.0.0.tgz",
+ "integrity": "sha512-477D7ypIiqlXBkxhU7YtG9wWZJEQ+RUpujt2quTfgf4+E8g5fNUkB0QIL0bVyP5/TKBg8y55Hfa1R/c4bt3dEw==",
+ "dev": true,
+ "optional": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true
+ },
+ "decimal.js": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz",
+ "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==",
+ "dev": true
+ },
+ "deep-eql": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
+ "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
+ "dev": true,
+ "requires": {
+ "type-detect": "^4.0.0"
+ }
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha1-z4jabL7ib+bbcJT2HYcMvYTO6fE=",
+ "dev": true,
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "dev": true
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+ },
+ "destroy": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+ },
+ "dicer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz",
+ "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==",
+ "dev": true,
+ "requires": {
+ "streamsearch": "0.1.2"
+ }
+ },
+ "diff": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
+ "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=",
+ "dev": true
+ },
+ "domexception": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz",
+ "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==",
+ "dev": true,
+ "requires": {
+ "webidl-conversions": "^5.0.0"
+ },
+ "dependencies": {
+ "webidl-conversions": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz",
+ "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==",
+ "dev": true
+ }
+ }
+ },
+ "dot-prop": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
+ "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "is-obj": "^2.0.0"
+ }
+ },
+ "duplexify": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz",
+ "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "end-of-stream": "^1.4.1",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1",
+ "stream-shift": "^1.0.0"
+ }
+ },
+ "ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+ "dev": true,
+ "requires": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+ },
+ "elliptic": {
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
+ "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.11.9",
+ "brorand": "^1.1.0",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.1",
+ "inherits": "^2.0.4",
+ "minimalistic-assert": "^1.0.1",
+ "minimalistic-crypto-utils": "^1.0.1"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ }
+ }
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+ },
+ "end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "once": "^1.4.0"
+ }
+ },
+ "ent": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz",
+ "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=",
+ "dev": true,
+ "optional": true
+ },
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true,
+ "optional": true
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "eslint-plugin-prettier": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.7.0.tgz",
+ "integrity": "sha512-CStQYJgALoQBw3FsBzH0VOVDRnJ/ZimUlpLm226U8qgqYJfPOY/CPK6wyRInMxh73HSKg5wyRwdS4BVYYHwokA==",
+ "dev": true,
+ "requires": {
+ "fast-diff": "^1.1.1",
+ "jest-docblock": "^21.0.0"
+ }
+ },
+ "estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
+ "dev": true
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+ },
+ "event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "dev": true,
+ "optional": true
+ },
+ "express": {
+ "version": "4.17.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
+ "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
+ "requires": {
+ "accepts": "~1.3.7",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.19.0",
+ "content-disposition": "0.5.3",
+ "content-type": "~1.0.4",
+ "cookie": "0.4.0",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.1.2",
+ "fresh": "0.5.2",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.5",
+ "qs": "6.7.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.1.2",
+ "send": "0.17.1",
+ "serve-static": "1.14.1",
+ "setprototypeof": "1.1.1",
+ "statuses": "~1.5.0",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ }
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=",
+ "dev": true
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+ "dev": true
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "fast-diff": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "fast-text-encoding": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz",
+ "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==",
+ "dev": true,
+ "optional": true
+ },
+ "faye-websocket": {
+ "version": "0.11.3",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz",
+ "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==",
+ "dev": true,
+ "requires": {
+ "websocket-driver": ">=0.5.1"
+ }
+ },
+ "finalhandler": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "statuses": "~1.5.0",
+ "unpipe": "~1.0.0"
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "firebase-admin": {
+ "version": "9.8.0",
+ "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-9.8.0.tgz",
+ "integrity": "sha512-v8B1qU8McZZT2hlLZ018TKz2FoKlfFkZq9mOIyzN7wJUOlAywqQX0JyqNpVGyPeU+B+77ojlvmkGTNXt2OFkgw==",
+ "dev": true,
+ "requires": {
+ "@firebase/database": "^0.10.0",
+ "@firebase/database-types": "^0.7.2",
+ "@google-cloud/firestore": "^4.5.0",
+ "@google-cloud/storage": "^5.3.0",
+ "@types/node": ">=12.12.47",
+ "dicer": "^0.3.0",
+ "jsonwebtoken": "^8.5.1",
+ "jwks-rsa": "^2.0.2",
+ "node-forge": "^0.10.0"
+ },
+ "dependencies": {
+ "@types/node": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz",
+ "integrity": "sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==",
+ "dev": true
+ }
+ }
+ },
+ "flat": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
+ "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "~2.0.3"
+ },
+ "dependencies": {
+ "is-buffer": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+ "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
+ "dev": true
+ }
+ }
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+ "dev": true
+ },
+ "form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "forwarded": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+ },
+ "fs-extra": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
+ "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==",
+ "dev": true,
+ "requires": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^1.0.0"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true,
+ "optional": true
+ },
+ "gaxios": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.2.1.tgz",
+ "integrity": "sha512-s+rTywpw6CmfB8r9TXYkpix7YFeuRjnR/AqhaJrQqsNhsAqej+IAiCc3hadzQH3gHyWth30tvYjxH8EVjQt/8Q==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "abort-controller": "^3.0.0",
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^5.0.0",
+ "is-stream": "^2.0.0",
+ "node-fetch": "^2.3.0"
+ }
+ },
+ "gcp-metadata": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz",
+ "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "gaxios": "^4.0.0",
+ "json-bigint": "^1.0.0"
+ }
+ },
+ "gcs-resumable-upload": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-3.1.4.tgz",
+ "integrity": "sha512-5dyDfHrrVcIskiw/cPssVD4HRiwoHjhk1Nd6h5W3pQ/qffDvhfy4oNCr1f3ZXFPwTnxkCbibsB+73oOM+NvmJQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "abort-controller": "^3.0.0",
+ "configstore": "^5.0.0",
+ "extend": "^3.0.2",
+ "gaxios": "^4.0.0",
+ "google-auth-library": "^7.0.0",
+ "pumpify": "^2.0.0",
+ "stream-events": "^1.0.4"
+ }
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "get-func-name": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
+ "dev": true
+ },
+ "get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true,
+ "optional": true
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha1-OWCDLT8VdBCDQtr9OmezMsCWnfE=",
+ "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"
+ }
+ },
+ "google-auth-library": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.0.4.tgz",
+ "integrity": "sha512-o8irYyeijEiecTXeoEe8UKNEzV1X+uhR4b2oNdapDMZixypp0J+eHimGOyx5Joa3UAeokGngdtDLXtq9vDqG2Q==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "arrify": "^2.0.0",
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "fast-text-encoding": "^1.0.0",
+ "gaxios": "^4.0.0",
+ "gcp-metadata": "^4.2.0",
+ "gtoken": "^5.0.4",
+ "jws": "^4.0.0",
+ "lru-cache": "^6.0.0"
+ },
+ "dependencies": {
+ "jwa": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+ "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jws": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+ "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "jwa": "^2.0.0",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "google-gax": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.12.0.tgz",
+ "integrity": "sha512-UDx4ZZx85vXBe6GZ0sdRSzuegLrRQdRjCxlauX+U7i5YwOoSgcSaIM71BhcdHwGPhEkvO/SSHrEfc1wpL/J6JA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "@grpc/grpc-js": "~1.3.0",
+ "@grpc/proto-loader": "^0.6.1",
+ "@types/long": "^4.0.0",
+ "abort-controller": "^3.0.0",
+ "duplexify": "^4.0.0",
+ "fast-text-encoding": "^1.0.3",
+ "google-auth-library": "^7.0.2",
+ "is-stream-ended": "^0.1.4",
+ "node-fetch": "^2.6.1",
+ "object-hash": "^2.1.1",
+ "protobufjs": "^6.10.2",
+ "retry-request": "^4.0.0"
+ }
+ },
+ "google-p12-pem": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz",
+ "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "node-forge": "^0.10.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+ "dev": true
+ },
+ "growl": {
+ "version": "1.10.5",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+ "integrity": "sha1-8nNdwig2dPpnR4sQGBBZNVw2nl4=",
+ "dev": true
+ },
+ "gtoken": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.2.1.tgz",
+ "integrity": "sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "gaxios": "^4.0.0",
+ "google-p12-pem": "^3.0.3",
+ "jws": "^4.0.0"
+ },
+ "dependencies": {
+ "jwa": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+ "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jws": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+ "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "jwa": "^2.0.0",
+ "safe-buffer": "^5.0.1"
+ }
+ }
+ }
+ },
+ "har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+ "dev": true
+ },
+ "har-validator": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+ "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.3",
+ "har-schema": "^2.0.0"
+ }
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-symbols": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+ "dev": true
+ },
+ "hash-stream-validation": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/hash-stream-validation/-/hash-stream-validation-0.2.4.tgz",
+ "integrity": "sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ==",
+ "dev": true,
+ "optional": true
+ },
+ "hash.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true
+ },
+ "highlight.js": {
+ "version": "10.4.1",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.4.1.tgz",
+ "integrity": "sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg==",
+ "dev": true
+ },
+ "hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+ "dev": true,
+ "requires": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "html-encoding-sniffer": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz",
+ "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==",
+ "dev": true,
+ "requires": {
+ "whatwg-encoding": "^1.0.5"
+ }
+ },
+ "http-errors": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+ "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.1",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.0"
+ }
+ },
+ "http-parser-js": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz",
+ "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==",
+ "dev": true
+ },
+ "http-proxy-agent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "@tootallnate/once": "1",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+ "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "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,
+ "optional": true
+ }
+ }
+ },
+ "http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+ "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "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,
+ "optional": true
+ }
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true,
+ "optional": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ },
+ "interpret": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
+ "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
+ "dev": true
+ },
+ "ip-regex": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
+ "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
+ "dev": true
+ },
+ "ipaddr.js": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
+ "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
+ },
+ "is-arguments": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
+ "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
+ "dev": true
+ },
+ "is-callable": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.1.tgz",
+ "integrity": "sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg==",
+ "dev": true
+ },
+ "is-date-object": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+ "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
+ "dev": true
+ },
+ "is-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
+ },
+ "is-negative-zero": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
+ "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=",
+ "dev": true
+ },
+ "is-obj": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+ "dev": true,
+ "optional": true
+ },
+ "is-potential-custom-element-name": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz",
+ "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
+ "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "is-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
+ "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
+ "dev": true,
+ "optional": true
+ },
+ "is-stream-ended": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz",
+ "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==",
+ "dev": true,
+ "optional": true
+ },
+ "is-symbol": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+ "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+ "dev": true
+ },
+ "jest-docblock": {
+ "version": "21.2.0",
+ "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.2.0.tgz",
+ "integrity": "sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw==",
+ "dev": true
+ },
+ "jose": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.5.tgz",
+ "integrity": "sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA==",
+ "dev": true,
+ "requires": {
+ "@panva/asn1.js": "^1.0.0"
+ }
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
+ "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "dependencies": {
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=",
+ "dev": true
+ }
+ }
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+ "dev": true
+ },
+ "jsdom": {
+ "version": "16.4.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz",
+ "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==",
+ "dev": true,
+ "requires": {
+ "abab": "^2.0.3",
+ "acorn": "^7.1.1",
+ "acorn-globals": "^6.0.0",
+ "cssom": "^0.4.4",
+ "cssstyle": "^2.2.0",
+ "data-urls": "^2.0.0",
+ "decimal.js": "^10.2.0",
+ "domexception": "^2.0.1",
+ "escodegen": "^1.14.1",
+ "html-encoding-sniffer": "^2.0.1",
+ "is-potential-custom-element-name": "^1.0.0",
+ "nwsapi": "^2.2.0",
+ "parse5": "5.1.1",
+ "request": "^2.88.2",
+ "request-promise-native": "^1.0.8",
+ "saxes": "^5.0.0",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^3.0.1",
+ "w3c-hr-time": "^1.0.2",
+ "w3c-xmlserializer": "^2.0.0",
+ "webidl-conversions": "^6.1.0",
+ "whatwg-encoding": "^1.0.5",
+ "whatwg-mimetype": "^2.3.0",
+ "whatwg-url": "^8.0.0",
+ "ws": "^7.2.3",
+ "xml-name-validator": "^3.0.0"
+ },
+ "dependencies": {
+ "escodegen": {
+ "version": "1.14.3",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
+ "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+ "dev": true,
+ "requires": {
+ "esprima": "^4.0.1",
+ "estraverse": "^4.2.0",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1",
+ "source-map": "~0.6.1"
+ }
+ },
+ "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
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "json-bigint": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+ "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "bignumber.js": "^9.0.0"
+ }
+ },
+ "json-schema": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=",
+ "dev": true
+ },
+ "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=",
+ "dev": true
+ },
+ "jsonfile": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz",
+ "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6",
+ "universalify": "^1.0.0"
+ }
+ },
+ "jsonwebtoken": {
+ "version": "8.5.1",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
+ "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+ "dev": true,
+ "requires": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^5.6.0"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "jsprim": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+ "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.2.3",
+ "verror": "1.10.0"
+ }
+ },
+ "just-extend": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.1.tgz",
+ "integrity": "sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA==",
+ "dev": true
+ },
+ "jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "dev": true,
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jwk-to-pem": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/jwk-to-pem/-/jwk-to-pem-2.0.5.tgz",
+ "integrity": "sha512-L90jwellhO8jRKYwbssU9ifaMVqajzj3fpRjDKcsDzrslU9syRbFqfkXtT4B89HYAap+xsxNcxgBSB09ig+a7A==",
+ "dev": true,
+ "requires": {
+ "asn1.js": "^5.3.0",
+ "elliptic": "^6.5.4",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jwks-rsa": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.0.3.tgz",
+ "integrity": "sha512-/rkjXRWAp0cS00tunsHResw68P5iTQru8+jHufLNv3JHc4nObFEndfEUSuPugh09N+V9XYxKUqi7QrkmCHSSSg==",
+ "dev": true,
+ "requires": {
+ "@types/express-jwt": "0.0.42",
+ "debug": "^4.1.0",
+ "jose": "^2.0.5",
+ "limiter": "^1.1.5",
+ "lru-memoizer": "^2.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+ "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "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
+ }
+ }
+ },
+ "jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "dev": true,
+ "requires": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ }
+ },
+ "limiter": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
+ "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==",
+ "dev": true
+ },
+ "lines-and-columns": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
+ "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
+ "dev": true
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
+ },
+ "lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=",
+ "dev": true,
+ "optional": true
+ },
+ "lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
+ "dev": true
+ },
+ "lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=",
+ "dev": true
+ },
+ "lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=",
+ "dev": true
+ },
+ "lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=",
+ "dev": true
+ },
+ "lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=",
+ "dev": true
+ },
+ "lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
+ "dev": true
+ },
+ "lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=",
+ "dev": true
+ },
+ "lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=",
+ "dev": true
+ },
+ "lodash.sortby": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
+ "dev": true
+ },
+ "log-symbols": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
+ "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.1"
+ }
+ },
+ "lolex": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz",
+ "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==",
+ "dev": true
+ },
+ "long": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
+ "dev": true,
+ "optional": true
+ },
+ "lru-cache": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+ "integrity": "sha1-i75Q6oW+1ZvJ4z3KuCNe6bz0Q80=",
+ "dev": true,
+ "requires": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "lru-memoizer": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.1.4.tgz",
+ "integrity": "sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ==",
+ "dev": true,
+ "requires": {
+ "lodash.clonedeep": "^4.5.0",
+ "lru-cache": "~4.0.0"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz",
+ "integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=",
+ "dev": true,
+ "requires": {
+ "pseudomap": "^1.0.1",
+ "yallist": "^2.0.0"
+ }
+ }
+ }
+ },
+ "lunr": {
+ "version": "2.3.9",
+ "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
+ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
+ "dev": true
+ },
+ "make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "semver": "^6.0.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true
+ },
+ "marked": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-1.1.1.tgz",
+ "integrity": "sha512-mJzT8D2yPxoPh7h0UXkB+dBj4FykPJ2OIfxAWeIHrvoHDkFxukV/29QxoFQoPM6RLEwhIFdJpmKBlqVM3s2ZIw==",
+ "dev": true
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+ },
+ "merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+ },
+ "methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+ },
+ "mime-db": {
+ "version": "1.40.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
+ "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
+ },
+ "mime-types": {
+ "version": "2.1.24",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
+ "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
+ "requires": {
+ "mime-db": "1.40.0"
+ }
+ },
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "optional": true
+ },
+ "minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true
+ },
+ "minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ }
+ }
+ },
+ "mocha": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.3.tgz",
+ "integrity": "sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "3.2.3",
+ "browser-stdout": "1.3.1",
+ "debug": "3.2.6",
+ "diff": "3.5.0",
+ "escape-string-regexp": "1.0.5",
+ "find-up": "3.0.0",
+ "glob": "7.1.3",
+ "growl": "1.10.5",
+ "he": "1.2.0",
+ "js-yaml": "3.13.1",
+ "log-symbols": "2.2.0",
+ "minimatch": "3.0.4",
+ "mkdirp": "0.5.4",
+ "ms": "2.1.1",
+ "node-environment-flags": "1.0.5",
+ "object.assign": "4.1.0",
+ "strip-json-comments": "2.0.1",
+ "supports-color": "6.0.0",
+ "which": "1.3.1",
+ "wide-align": "1.1.3",
+ "yargs": "13.3.2",
+ "yargs-parser": "13.1.2",
+ "yargs-unparser": "1.6.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
+ },
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "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
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz",
+ "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ },
+ "object.assign": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "function-bind": "^1.1.1",
+ "has-symbols": "^1.0.0",
+ "object-keys": "^1.0.11"
+ }
+ },
+ "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"
+ }
+ },
+ "supports-color": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
+ "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "yargs": {
+ "version": "13.3.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+ "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.2"
+ }
+ }
+ }
+ },
+ "mock-require": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/mock-require/-/mock-require-3.0.3.tgz",
+ "integrity": "sha512-lLzfLHcyc10MKQnNUCv7dMcoY/2Qxd6wJfbqCcVk3LDb8An4hF6ohk5AztrvgKhJCqj36uyzi/p5se+tvyD+Wg==",
+ "dev": true,
+ "requires": {
+ "get-caller-file": "^1.0.2",
+ "normalize-path": "^2.1.1"
+ },
+ "dependencies": {
+ "get-caller-file": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
+ "dev": true
+ }
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ },
+ "mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dev": true,
+ "requires": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "negotiator": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
+ },
+ "neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true
+ },
+ "nise": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz",
+ "integrity": "sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ==",
+ "dev": true,
+ "requires": {
+ "@sinonjs/formatio": "^3.2.1",
+ "@sinonjs/text-encoding": "^0.7.1",
+ "just-extend": "^4.0.2",
+ "lolex": "^5.0.1",
+ "path-to-regexp": "^1.7.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ },
+ "lolex": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz",
+ "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==",
+ "dev": true,
+ "requires": {
+ "@sinonjs/commons": "^1.7.0"
+ }
+ },
+ "path-to-regexp": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
+ "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
+ "dev": true,
+ "requires": {
+ "isarray": "0.0.1"
+ }
+ }
+ }
+ },
+ "nock": {
+ "version": "10.0.6",
+ "resolved": "https://registry.npmjs.org/nock/-/nock-10.0.6.tgz",
+ "integrity": "sha512-b47OWj1qf/LqSQYnmokNWM8D88KvUl2y7jT0567NB3ZBAZFz2bWp2PC81Xn7u8F2/vJxzkzNZybnemeFa7AZ2w==",
+ "dev": true,
+ "requires": {
+ "chai": "^4.1.2",
+ "debug": "^4.1.0",
+ "deep-equal": "^1.0.0",
+ "json-stringify-safe": "^5.0.1",
+ "lodash": "^4.17.5",
+ "mkdirp": "^0.5.0",
+ "propagate": "^1.0.0",
+ "qs": "^6.5.1",
+ "semver": "^5.5.0"
+ },
+ "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"
+ }
+ },
+ "deep-equal": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
+ "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
+ "dev": true,
+ "requires": {
+ "is-arguments": "^1.0.4",
+ "is-date-object": "^1.0.1",
+ "is-regex": "^1.0.4",
+ "object-is": "^1.0.1",
+ "object-keys": "^1.1.1",
+ "regexp.prototype.flags": "^1.2.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
+ }
+ }
+ },
+ "node-environment-flags": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz",
+ "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==",
+ "dev": true,
+ "requires": {
+ "object.getownpropertydescriptors": "^2.0.3",
+ "semver": "^5.7.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "node-fetch": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
+ "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
+ "dev": true,
+ "optional": true
+ },
+ "node-forge": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+ "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==",
+ "dev": true
+ },
+ "node-version": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/node-version/-/node-version-1.2.0.tgz",
+ "integrity": "sha512-ma6oU4Sk0qOoKEAymVoTvk8EdXEobdS7m/mAGhDJ8Rouugho48crHBORAmy5BoOcv8wraPM6xumapQp5hl4iIQ==",
+ "dev": true
+ },
+ "normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+ "dev": true,
+ "requires": {
+ "remove-trailing-separator": "^1.0.1"
+ }
+ },
+ "nwsapi": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
+ "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==",
+ "dev": true
+ },
+ "oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+ },
+ "object-hash": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.1.1.tgz",
+ "integrity": "sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==",
+ "dev": true,
+ "optional": true
+ },
+ "object-inspect": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
+ "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
+ "dev": true
+ },
+ "object-is": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz",
+ "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true
+ },
+ "object.assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
+ "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.0",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.18.0-next.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz",
+ "integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
+ }
+ },
+ "object.getownpropertydescriptors": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz",
+ "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1"
+ }
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "optionator": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
+ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+ "dev": true,
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.4",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "wordwrap": "~1.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
+ "parse5": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
+ "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
+ "dev": true
+ },
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+ },
+ "pathval": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
+ "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
+ "dev": true
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+ "dev": true
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true
+ },
+ "prettier": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
+ "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
+ "dev": true
+ },
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true
+ },
+ "promise-polyfill": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz",
+ "integrity": "sha1-36lpQ+qcEh/KTem1hoyznTRy4Fc=",
+ "dev": true
+ },
+ "propagate": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/propagate/-/propagate-1.0.0.tgz",
+ "integrity": "sha1-AMLa7t2iDofjeCs0Stuhzd1q1wk=",
+ "dev": true
+ },
+ "protobufjs": {
+ "version": "6.11.2",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz",
+ "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/long": "^4.0.1",
+ "@types/node": ">=13.7.0",
+ "long": "^4.0.0"
+ },
+ "dependencies": {
+ "@types/node": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz",
+ "integrity": "sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "proxy-addr": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
+ "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
+ "requires": {
+ "forwarded": "~0.1.2",
+ "ipaddr.js": "1.9.0"
+ }
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+ "dev": true
+ },
+ "psl": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
+ "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
+ "dev": true
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "pumpify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz",
+ "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "duplexify": "^4.1.1",
+ "inherits": "^2.0.3",
+ "pump": "^3.0.0"
+ }
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
+ },
+ "range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+ },
+ "raw-body": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+ "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+ "requires": {
+ "bytes": "3.1.0",
+ "http-errors": "1.7.2",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "rechoir": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+ "dev": true,
+ "requires": {
+ "resolve": "^1.1.6"
+ }
+ },
+ "regexp.prototype.flags": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz",
+ "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1"
+ }
+ },
+ "remove-trailing-separator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+ "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
+ "dev": true
+ },
+ "request": {
+ "version": "2.88.2",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+ "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+ "dev": true,
+ "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"
+ },
+ "dependencies": {
+ "qs": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+ "dev": true
+ },
+ "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==",
+ "dev": true,
+ "requires": {
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ }
+ },
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true
+ }
+ }
+ },
+ "request-promise-core": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
+ "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.19"
+ }
+ },
+ "request-promise-native": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz",
+ "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==",
+ "dev": true,
+ "requires": {
+ "request-promise-core": "1.1.4",
+ "stealthy-require": "^1.1.1",
+ "tough-cookie": "^2.3.3"
+ },
+ "dependencies": {
+ "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==",
+ "dev": true,
+ "requires": {
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ }
+ }
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+ "dev": true
+ },
+ "retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
+ "dev": true,
+ "optional": true
+ },
+ "retry-request": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.1.3.tgz",
+ "integrity": "sha512-QnRZUpuPNgX0+D1xVxul6DbJ9slvo4Rm6iV/dn63e048MvGbUZiKySVt6Tenp04JqmchxjiLltGerOJys7kJYQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "debug": "^4.1.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+ "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "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,
+ "optional": true
+ }
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0="
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo="
+ },
+ "saxes": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
+ "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==",
+ "dev": true,
+ "requires": {
+ "xmlchars": "^2.2.0"
+ }
+ },
+ "semver": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+ "integrity": "sha1-fnQlb7qknHWqfHogXMInmcrIAAQ=",
+ "dev": true
+ },
+ "send": {
+ "version": "0.17.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
+ "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "~1.7.2",
+ "mime": "1.6.0",
+ "ms": "2.1.1",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.1",
+ "statuses": "~1.5.0"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+ }
+ }
+ },
+ "serve-static": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
+ "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+ "requires": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.17.1"
+ }
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+ "dev": true
+ },
+ "setprototypeof": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+ },
+ "shelljs": {
+ "version": "0.8.4",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz",
+ "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.0.0",
+ "interpret": "^1.0.0",
+ "rechoir": "^0.6.2"
+ }
+ },
+ "signal-exit": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
+ "dev": true,
+ "optional": true
+ },
+ "sinon": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz",
+ "integrity": "sha512-AoD0oJWerp0/rY9czP/D6hDTTUYGpObhZjMpd7Cl/A6+j0xBE+ayL/ldfggkBXUs0IkvIiM1ljM8+WkOc5k78Q==",
+ "dev": true,
+ "requires": {
+ "@sinonjs/commons": "^1.4.0",
+ "@sinonjs/formatio": "^3.2.1",
+ "@sinonjs/samsam": "^3.3.3",
+ "diff": "^3.5.0",
+ "lolex": "^4.2.0",
+ "nise": "^1.5.2",
+ "supports-color": "^5.5.0"
+ },
+ "dependencies": {
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "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"
+ }
+ }
+ }
+ },
+ "snakeize": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/snakeize/-/snakeize-0.1.0.tgz",
+ "integrity": "sha1-EMCI2LWOsHazIpu1oE4jLOEmQi0=",
+ "dev": true,
+ "optional": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.5.19",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+ "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "sshpk": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+ "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+ "dev": true,
+ "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"
+ }
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+ },
+ "stealthy-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
+ "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
+ "dev": true
+ },
+ "stream-events": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
+ "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "stubs": "^3.0.0"
+ }
+ },
+ "stream-shift": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
+ "dev": true,
+ "optional": true
+ },
+ "streamsearch": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
+ "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
+ "string.prototype.trimend": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
+ "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "string.prototype.trimstart": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
+ "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safe-buffer": "~5.2.0"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+ "dev": true
+ },
+ "stubs": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
+ "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=",
+ "dev": true,
+ "optional": true
+ },
+ "symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+ "dev": true
+ },
+ "teeny-request": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz",
+ "integrity": "sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "http-proxy-agent": "^4.0.0",
+ "https-proxy-agent": "^5.0.0",
+ "node-fetch": "^2.6.1",
+ "stream-events": "^1.0.5",
+ "uuid": "^8.0.0"
+ }
+ },
+ "thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "dev": true,
+ "requires": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
+ "dev": true,
+ "requires": {
+ "thenify": ">= 3.1.0 < 4"
+ }
+ },
+ "toidentifier": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+ },
+ "tough-cookie": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
+ "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==",
+ "dev": true,
+ "requires": {
+ "ip-regex": "^2.1.0",
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ }
+ },
+ "tr46": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz",
+ "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.1"
+ }
+ },
+ "ts-node": {
+ "version": "8.10.2",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz",
+ "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==",
+ "dev": true,
+ "requires": {
+ "arg": "^4.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "source-map-support": "^0.5.17",
+ "yn": "3.1.1"
+ },
+ "dependencies": {
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true
+ }
+ }
+ },
+ "tslib": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
+ "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
+ "dev": true
+ },
+ "tslint": {
+ "version": "5.20.1",
+ "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz",
+ "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "builtin-modules": "^1.1.1",
+ "chalk": "^2.3.0",
+ "commander": "^2.12.1",
+ "diff": "^4.0.1",
+ "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": {
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+ "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ }
+ }
+ },
+ "tslint-config-prettier": {
+ "version": "1.18.0",
+ "resolved": "https://registry.npmjs.org/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz",
+ "integrity": "sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg==",
+ "dev": true
+ },
+ "tslint-no-unused-expression-chai": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/tslint-no-unused-expression-chai/-/tslint-no-unused-expression-chai-0.1.4.tgz",
+ "integrity": "sha512-frEWKNTcq7VsaWKgUxMDOB2N/cmQadVkUtUGIut+2K4nv/uFXPfgJyPjuNC/cHyfUVqIkHMAvHOCL+d/McU3nQ==",
+ "dev": true,
+ "requires": {
+ "tsutils": "^3.0.0"
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "tslint-plugin-prettier": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tslint-plugin-prettier/-/tslint-plugin-prettier-2.3.0.tgz",
+ "integrity": "sha512-F9e4K03yc9xuvv+A0v1EmjcnDwpz8SpCD8HzqSDe0eyg34cBinwn9JjmnnRrNAs4HdleRQj7qijp+P/JTxt4vA==",
+ "dev": true,
+ "requires": {
+ "eslint-plugin-prettier": "^2.2.0",
+ "lines-and-columns": "^1.1.6",
+ "tslib": "^1.7.1"
+ }
+ },
+ "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",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "dev": true,
+ "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=",
+ "dev": true
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ },
+ "type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true
+ },
+ "type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ }
+ },
+ "typedarray-to-buffer": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
+ "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "is-typedarray": "^1.0.0"
+ }
+ },
+ "typedoc": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.19.1.tgz",
+ "integrity": "sha512-EqZpRJQUnkwHA1yBhaDExEXUZIiWKddkrDXhRcfUzpnu6pizxNmVTw5IZ3mu682Noa4zQCniE0YNjaAwHQodrA==",
+ "dev": true,
+ "requires": {
+ "fs-extra": "^9.0.1",
+ "handlebars": "^4.7.6",
+ "highlight.js": "^10.0.0",
+ "lodash": "^4.17.20",
+ "lunr": "^2.3.9",
+ "marked": "^1.1.1",
+ "minimatch": "^3.0.0",
+ "progress": "^2.0.3",
+ "semver": "^7.3.2",
+ "shelljs": "^0.8.4",
+ "typedoc-default-themes": "^0.11.1"
+ },
+ "dependencies": {
+ "handlebars": {
+ "version": "4.7.6",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
+ "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5",
+ "neo-async": "^2.6.0",
+ "source-map": "^0.6.1",
+ "uglify-js": "^3.1.4",
+ "wordwrap": "^1.0.0"
+ }
+ },
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ },
+ "semver": {
+ "version": "7.3.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
+ "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
+ "dev": true
+ }
+ }
+ },
+ "typedoc-default-themes": {
+ "version": "0.11.3",
+ "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.11.3.tgz",
+ "integrity": "sha512-SwyN188QGNA2iFS5mdWYTGzohKqJ1PWAXVmGolKnVc2NnpX234FEPF2nUvEg+O9jjwAu7ZSVZ5UrZri0raJOjQ==",
+ "dev": true
+ },
+ "typescript": {
+ "version": "3.9.7",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
+ "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
+ "dev": true
+ },
+ "uglify-js": {
+ "version": "3.4.9",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
+ "integrity": "sha1-rwLxgMEgfXZDLkc+0koo9KeCuuM=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "commander": "~2.17.1",
+ "source-map": "~0.6.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "unique-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
+ "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "crypto-random-string": "^2.0.0"
+ }
+ },
+ "universalify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz",
+ "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==",
+ "dev": true
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+ },
+ "uri-js": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
+ "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true,
+ "optional": true
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+ },
+ "uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "dev": true,
+ "optional": true
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "w3c-hr-time": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
+ "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==",
+ "dev": true,
+ "requires": {
+ "browser-process-hrtime": "^1.0.0"
+ }
+ },
+ "w3c-xmlserializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz",
+ "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==",
+ "dev": true,
+ "requires": {
+ "xml-name-validator": "^3.0.0"
+ }
+ },
+ "webidl-conversions": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
+ "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==",
+ "dev": true
+ },
+ "websocket-driver": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+ "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+ "dev": true,
+ "requires": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ }
+ },
+ "websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+ "dev": true
+ },
+ "whatwg-encoding": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
+ "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
+ "dev": true,
+ "requires": {
+ "iconv-lite": "0.4.24"
+ }
+ },
+ "whatwg-mimetype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
+ "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==",
+ "dev": true
+ },
+ "whatwg-url": {
+ "version": "8.2.2",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.2.2.tgz",
+ "integrity": "sha512-PcVnO6NiewhkmzV0qn7A+UZ9Xx4maNTI+O+TShmfE4pqjoCMwUMjkvoNhNHPTvgR7QH9Xt3R13iHuWy2sToFxQ==",
+ "dev": true,
+ "requires": {
+ "lodash.sortby": "^4.7.0",
+ "tr46": "^2.0.2",
+ "webidl-conversions": "^6.1.0"
+ }
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+ "dev": true
+ },
+ "wide-align": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.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
+ },
+ "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"
+ }
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "write-file-atomic": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
+ "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "imurmurhash": "^0.1.4",
+ "is-typedarray": "^1.0.0",
+ "signal-exit": "^3.0.2",
+ "typedarray-to-buffer": "^3.1.5"
+ }
+ },
+ "ws": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
+ "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==",
+ "dev": true
+ },
+ "xdg-basedir": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
+ "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==",
+ "dev": true,
+ "optional": true
+ },
+ "xml-name-validator": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
+ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
+ "dev": true
+ },
+ "xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true
+ },
+ "y18n": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+ "dev": true
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+ "dev": true
+ },
+ "yargs": {
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "dev": true,
+ "requires": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ },
+ "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": "4.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+ "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "dev": true,
+ "requires": {
+ "@types/color-name": "^1.1.1",
+ "color-convert": "^2.0.1"
+ }
+ },
+ "cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "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
+ },
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "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"
+ }
+ },
+ "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"
+ }
+ },
+ "wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ },
+ "yargs-unparser": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz",
+ "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==",
+ "dev": true,
+ "requires": {
+ "flat": "^4.1.0",
+ "lodash": "^4.17.15",
+ "yargs": "^13.3.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
+ },
+ "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"
+ }
+ },
+ "yargs": {
+ "version": "13.3.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+ "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.2"
+ }
+ }
+ }
+ },
+ "yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true
+ },
+ "yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "optional": true
+ }
+ }
+}
diff --git a/package.json b/package.json
index 4eb5986d8..fa00d759d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "firebase-functions",
- "version": "3.11.0",
+ "version": "3.14.0",
"description": "Firebase SDK for Cloud Functions",
"keywords": [
"firebase",
@@ -31,6 +31,7 @@
"build:pack": "rm -rf lib && npm install && tsc -p tsconfig.release.json && npm pack",
"build:release": "npm install --production && npm install --no-save typescript firebase-admin && tsc -p tsconfig.release.json",
"build": "tsc -p tsconfig.release.json",
+ "build:watch": "npm run build -- -w",
"format": "prettier --check '**/*.{json,md,ts,yml,yaml}'",
"format:fix": "prettier --write '**/*.{json,md,ts,yml,yaml}'",
"lint": "tslint --config tslint.json --project tsconfig.json ",
@@ -57,11 +58,11 @@
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"child-process-promise": "^2.2.1",
- "firebase-admin": "^8.2.0",
- "istanbul": "^0.4.5",
+ "firebase-admin": "^9.8.0",
"js-yaml": "^3.13.1",
"jsdom": "^16.2.1",
"jsonwebtoken": "^8.5.1",
+ "jwk-to-pem": "^2.0.5",
"mocha": "^6.1.4",
"mock-require": "^3.0.3",
"mz": "^2.7.0",
@@ -73,7 +74,7 @@
"tslint-config-prettier": "^1.18.0",
"tslint-no-unused-expression-chai": "^0.1.4",
"tslint-plugin-prettier": "^2.0.1",
- "typedoc": "0.14.2",
+ "typedoc": "^0.19.1",
"typescript": "^3.8.3",
"yargs": "^15.3.1"
},
diff --git a/scripts/publish-container/Dockerfile b/scripts/publish-container/Dockerfile
index c8ba24c12..02b15bf11 100644
--- a/scripts/publish-container/Dockerfile
+++ b/scripts/publish-container/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:8
+FROM node:14
# Install dependencies
RUN apt-get update && \
diff --git a/scripts/publish.sh b/scripts/publish.sh
index a1498460e..d187e9f45 100644
--- a/scripts/publish.sh
+++ b/scripts/publish.sh
@@ -87,7 +87,7 @@ npm run build:release
echo "Ran publish build."
echo "Making a $VERSION version..."
-npm version $VERSION
+npm version --no-git-tag-version $VERSION
NEW_VERSION=$(jq -r ".version" package.json)
echo "Made a $VERSION version."
diff --git a/spec/fixtures/credential/jwk.json b/spec/fixtures/credential/jwk.json
new file mode 100644
index 000000000..cde44767e
--- /dev/null
+++ b/spec/fixtures/credential/jwk.json
@@ -0,0 +1,14 @@
+{
+ "p": "9cTVRzGXbDfhIMQW9gXtWveDW0u_Hvwnbjx7TRPgSfawZ0MjgKfSbnyHTDXiqM1ifcN_Nk58KJ-PG9eZ7V7_mfTUnPv2puDaecn-kgHobnTJMoBR9hpzyyMpyNJuMvX4kqE7Qh8iFMBK_-p8ICiW15gK5WykswIKfIOkUZc52XM",
+ "kty": "RSA",
+ "q": "pYdUNL244sCoc4XrONKlu787AiHrjFFLHdTjoFLbvxSpszXM8iSjoiFAM_MCF-uWks2iBVDw9wlG4MB7MfNf_fD0i1wqyknSOtfMxknU7D4eU_Sp6tI99Jl8f_GAzODK__k_0MpqqXgZmJbUvYuIXMiha-5lddz8ENa4pYpbr7M",
+ "d": "MpkXqjmjvzwfmlq3o0uZAXjeeAnBlYQSNaSllBWKepgPjg4FxFIt_BlXex1NeP0npNy_oCgaM_x7NiALaaPhwPK52lhYThc-xomCic1KDkyPecODTPXi4Iw94Q_gp442SYMWz2ZktS-2DgXc3599fGHkY80u0rHNSO8ptdk8SUDUIZ82ZQ3pBhClF_uY3c1jZLuqVgCwKksInZmNPnv3ge088wmQC26t0Ph5u1HU6lISgaqZ8ol23iNWJPf4UEi8Twy1a73nphQS-y1yK9UC3c5Knk-WI2TMmjlxqC02ZjKqnRDxElTj9kpodasPRHRV_KJI8rTaStgxd7peMFODzQ",
+ "e": "AQAB",
+ "use": "sig",
+ "kid": "a12KBE",
+ "qi": "aJCrZVWeOjxYmMBTTI7aJhxcvvfS3I5Q7wwN4Oyb1rJZ4fgGYjDohlzeZz_3fNantPAgcDbzJfa3XS327sHJGaAVqvDugZUgyHeLZGzXGs-_mlL72wzcfvTa1C9_lIndLNZJle5_mg3xJAqRKV0s7kymSdYt0wL5fDaqo5SDNqQ",
+ "dp": "haBk2hWzoApt5HPZjCDC4g_rosr3enBdPAm0fL8O1whC95JAjmYw-xPIOH6f42nwYDLYSv23chr3I4tBTRe2382HgGdav3dIMqnKOTbCWrQy5LtyVN4jEVLoGCGZ-ylT4t25K4Vj8WZwIN8saAvJoCUx33YHwrCcZQDqadZQhNM",
+ "alg": "RS256",
+ "dq": "j6NdeN7hnzMbehPNyGNSmhcZd4JDymGI03w3gpokQi4GDJM1IzKUJE7CTdIkEOnIod97Jy3TzCrqrIGa5f-RXuVG79-s6hkhKxq0gaTz9YT6AFShVjnWtXizRrskz6SJw5JgxCfCYwjq_TR1q313eTxIh0Y6GQsIWPxbApuLcG0",
+ "n": "nunJGpOcPvVsP3q-NLgf3H6OycPhnXUxywMR2_H_JJP7BUIDSsYcOGBTFe7OphHYfyb1Gs14yAER243swndpNbQkuDJhj9a9kK6dJZmPGmvCySk_E5URj6MimZg1MBbwhsVAbRp2uerESZuoRrfdTdV87E3pGyg6Irl0IXRjy5w9SsFjjIi7E-Qxpf3TcNNjfVRLj9V2bSzmS7hlsPKBhDon0tWecuNKoNNMiGI46mz_MSUa2y1lPV6Cqhf1su_TRd7N7u9eP7xWArr7wqtqHiFTZ3qp1xoA_dr_xv_Ao2kBtohZiAFLV-PQShprSN5fafztRZFkSEF0m2tUkvmoaQ"
+}
diff --git a/spec/fixtures/mockrequest.ts b/spec/fixtures/mockrequest.ts
index fff2ce1a9..68827610b 100644
--- a/spec/fixtures/mockrequest.ts
+++ b/spec/fixtures/mockrequest.ts
@@ -1,7 +1,9 @@
import * as jwt from 'jsonwebtoken';
+import * as jwkToPem from 'jwk-to-pem';
import * as _ from 'lodash';
import * as nock from 'nock';
-import * as mocks from '../fixtures/credential/key.json';
+import * as mockJWK from '../fixtures/credential/jwk.json';
+import * as mockKey from '../fixtures/credential/key.json';
// MockRequest mocks an https.Request.
export class MockRequest {
@@ -26,6 +28,7 @@ export function mockRequest(
context: {
authorization?: string;
instanceIdToken?: string;
+ appCheckToken?: string;
} = {}
) {
const body: any = {};
@@ -37,6 +40,7 @@ export function mockRequest(
'content-type': contentType,
authorization: context.authorization,
'firebase-instance-id-token': context.instanceIdToken,
+ 'x-firebase-appcheck': context.appCheckToken,
origin: 'example.com',
};
@@ -53,7 +57,7 @@ export const expectedResponseHeaders = {
* verifying an id token.
*/
export function mockFetchPublicKeys(): nock.Scope {
- const mockedResponse = { [mocks.key_id]: mocks.public_key };
+ const mockedResponse = { [mockKey.key_id]: mockKey.public_key };
const headers = {
'cache-control': 'public, max-age=1, must-revalidate, no-transform',
};
@@ -72,11 +76,48 @@ export function generateIdToken(projectId: string): string {
audience: projectId,
expiresIn: 60 * 60, // 1 hour in seconds
issuer: 'https://securetoken.google.com/' + projectId,
- subject: mocks.user_id,
+ subject: mockKey.user_id,
algorithm: 'RS256',
header: {
- kid: mocks.key_id,
+ kid: mockKey.key_id,
},
};
- return jwt.sign(claims, mocks.private_key, options);
+ return jwt.sign(claims, mockKey.private_key, options);
+}
+
+/**
+ * Mocks out the http request used by the firebase-admin SDK to get the jwks for
+ * verifying an AppCheck token.
+ */
+export function mockFetchAppCheckPublicJwks(): nock.Scope {
+ const { kty, use, alg, kid, n, e } = mockJWK;
+ const mockedResponse = {
+ keys: [{ kty, use, alg, kid, n, e }],
+ };
+
+ return nock('https://firebaseappcheck.googleapis.com:443')
+ .get('/v1beta/jwks')
+ .reply(200, mockedResponse);
+}
+
+/**
+ * Generates a mocked AppCheck token.
+ */
+export function generateAppCheckToken(
+ projectId: string,
+ appId: string
+): string {
+ const claims = {};
+ const options: jwt.SignOptions = {
+ audience: [`projects/${projectId}`],
+ expiresIn: 60 * 60, // 1 hour in seconds
+ issuer: `https://firebaseappcheck.googleapis.com/${projectId}`,
+ subject: appId,
+ header: {
+ alg: 'RS256',
+ typ: 'JWT',
+ kid: mockJWK.kid,
+ },
+ };
+ return jwt.sign(claims, jwkToPem(mockJWK, { private: true }), options);
}
diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts
index eec531fdf..28cf35c3a 100644
--- a/spec/function-builder.spec.ts
+++ b/spec/function-builder.spec.ts
@@ -200,6 +200,27 @@ describe('FunctionBuilder', () => {
}).to.throw(Error, 'at least one region');
});
+ it('should allow a ingressSettings to be set', () => {
+ const fn = functions
+ .runWith({ ingressSettings: 'ALLOW_INTERNAL_ONLY' })
+ .https.onRequest(() => {});
+
+ expect(fn.__trigger.ingressSettings).to.equal('ALLOW_INTERNAL_ONLY');
+ });
+
+ it('should throw an error if user chooses an invalid ingressSettings', () => {
+ expect(() => {
+ return functions.runWith({
+ ingressSettings: 'INVALID_OPTION',
+ } as any);
+ }).to.throw(
+ Error,
+ `The only valid ingressSettings values are: ${functions.INGRESS_SETTINGS_OPTIONS.join(
+ ','
+ )}`
+ );
+ });
+
it('should allow a vpcConnector to be set', () => {
const fn = functions
.runWith({
@@ -238,4 +259,64 @@ describe('FunctionBuilder', () => {
)}`
);
});
+
+ it('should allow a serviceAccount to be set as-is', () => {
+ const serviceAccount = 'test-service-account@test.iam.gserviceaccount.com';
+ const fn = functions
+ .runWith({
+ serviceAccount,
+ })
+ .auth.user()
+ .onCreate((user) => user);
+
+ expect(fn.__trigger.serviceAccountEmail).to.equal(serviceAccount);
+ });
+
+ it('should allow a serviceAccount to be set with generated service account email', () => {
+ const serviceAccount = 'test-service-account@';
+ const projectId = process.env.GCLOUD_PROJECT;
+ const fn = functions
+ .runWith({
+ serviceAccount,
+ })
+ .auth.user()
+ .onCreate((user) => user);
+
+ expect(fn.__trigger.serviceAccountEmail).to.equal(
+ `test-service-account@${projectId}.iam.gserviceaccount.com`
+ );
+ });
+
+ it('should not set a serviceAccountEmail if service account is set to `default`', () => {
+ const serviceAccount = 'default';
+ const fn = functions
+ .runWith({
+ serviceAccount,
+ })
+ .auth.user()
+ .onCreate((user) => user);
+
+ expect(fn.__trigger.serviceAccountEmail).to.be.undefined;
+ });
+
+ it('should throw an error if serviceAccount is set to an invalid value', () => {
+ const serviceAccount = 'test-service-account';
+ expect(() => {
+ functions.runWith({
+ serviceAccount,
+ });
+ }).to.throw();
+ });
+
+ it('should allow setting 4GB memory option', () => {
+ const fn = functions
+ .runWith({
+ memory: '4GB',
+ })
+ .region('europe-west1')
+ .auth.user()
+ .onCreate((user) => user);
+
+ expect(fn.__trigger.availableMemoryMb).to.deep.equal(4096);
+ });
});
diff --git a/spec/logger.spec.ts b/spec/logger.spec.ts
index c6859b60a..e32765d10 100644
--- a/spec/logger.spec.ts
+++ b/spec/logger.spec.ts
@@ -1,5 +1,4 @@
import { expect } from 'chai';
-import * as sinon from 'sinon';
import * as logger from '../src/logger';
const SUPPORTS_STRUCTURED_LOGS =
@@ -8,52 +7,51 @@ const SUPPORTS_STRUCTURED_LOGS =
describe(`logger (${
SUPPORTS_STRUCTURED_LOGS ? 'structured' : 'unstructured'
})`, () => {
- let sandbox: sinon.SinonSandbox;
- let stdoutStub: sinon.SinonStub;
- let stderrStub: sinon.SinonStub;
+ const stdoutWrite = process.stdout.write.bind(process.stdout);
+ const stderrWrite = process.stderr.write.bind(process.stderr);
+ let lastOut: string;
+ let lastErr: string;
beforeEach(() => {
- sandbox = sinon.createSandbox();
- stdoutStub = sandbox.stub(process.stdout, 'write');
- stderrStub = sandbox.stub(process.stderr, 'write');
+ process.stdout.write = (msg: Buffer | string, cb?: any): boolean => {
+ lastOut = msg as string;
+ return stdoutWrite(msg, cb);
+ };
+ process.stderr.write = (msg: Buffer | string, cb?: any): boolean => {
+ lastErr = msg as string;
+ return stderrWrite(msg, cb);
+ };
});
- function expectOutput(stdStub: sinon.SinonStub, entry: any) {
+ afterEach(() => {
+ process.stdout.write = stdoutWrite;
+ process.stderr.write = stderrWrite;
+ });
+
+ function expectOutput(last: string, entry: any) {
if (SUPPORTS_STRUCTURED_LOGS) {
- return expect(
- JSON.parse((stdStub.getCalls()[0].args[0] as string).trim())
- ).to.deep.eq(entry);
+ return expect(JSON.parse(last.trim())).to.deep.eq(entry);
} else {
// legacy logging is not structured, but do a sanity check
- return expect(stdStub.getCalls()[0].args[0]).to.include(entry.message);
+ return expect(last).to.include(entry.message);
}
}
function expectStdout(entry: any) {
- return expectOutput(stdoutStub, entry);
+ return expectOutput(lastOut, entry);
}
function expectStderr(entry: any) {
- return expectOutput(stderrStub, entry);
+ return expectOutput(lastErr, entry);
}
describe('logging methods', () => {
- let writeStub: sinon.SinonStub;
- beforeEach(() => {
- writeStub = sinon.stub(logger, 'write');
- });
-
- afterEach(() => {
- writeStub.restore();
- });
-
it('should coalesce arguments into the message', () => {
logger.log('hello', { middle: 'obj' }, 'end message');
expectStdout({
severity: 'INFO',
message: "hello { middle: 'obj' } end message",
});
- sandbox.restore(); // to avoid swallowing test runner output
});
it('should merge structured data from the last argument', () => {
@@ -63,7 +61,6 @@ describe(`logger (${
message: 'hello world',
additional: 'context',
});
- sandbox.restore(); // to avoid swallowing test runner output
});
it('should not recognize null as a structured logging object', () => {
@@ -72,22 +69,70 @@ describe(`logger (${
severity: 'INFO',
message: 'hello world null',
});
- sandbox.restore(); // to avoid swallowing test runner output
});
});
describe('write', () => {
describe('structured logging', () => {
describe('write', () => {
+ it('should remove circular references', () => {
+ const circ: any = { b: 'foo' };
+ circ.circ = circ;
+
+ const entry: logger.LogEntry = {
+ severity: 'ERROR',
+ message: 'testing circular',
+ circ,
+ };
+ logger.write(entry);
+ expectStderr({
+ severity: 'ERROR',
+ message: 'testing circular',
+ circ: { b: 'foo', circ: '[Circular]' },
+ });
+ });
+
+ it('should remove circular references in arrays', () => {
+ const circ: any = { b: 'foo' };
+ circ.circ = [circ];
+
+ const entry: logger.LogEntry = {
+ severity: 'ERROR',
+ message: 'testing circular',
+ circ,
+ };
+ logger.write(entry);
+ expectStderr({
+ severity: 'ERROR',
+ message: 'testing circular',
+ circ: { b: 'foo', circ: ['[Circular]'] },
+ });
+ });
+
+ it('should not alter parameters that are logged', () => {
+ const circ: any = { b: 'foo' };
+ circ.array = [circ];
+ circ.object = circ;
+ const entry: logger.LogEntry = {
+ severity: 'ERROR',
+ message: 'testing circular',
+ circ,
+ };
+ logger.write(entry);
+
+ expect(circ.array[0].b).to.equal('foo');
+ expect(circ.object.b).to.equal('foo');
+ expect(circ.object.array[0].object.array[0].b).to.equal('foo');
+ });
+
for (const severity of ['DEBUG', 'INFO', 'NOTICE']) {
it(`should output ${severity} severity to stdout`, () => {
- let entry: logger.LogEntry = {
+ const entry: logger.LogEntry = {
severity: severity as logger.LogSeverity,
message: 'test',
};
logger.write(entry);
expectStdout(entry);
- sandbox.restore(); // to avoid swallowing test runner output
});
}
@@ -99,13 +144,12 @@ describe(`logger (${
'EMERGENCY',
]) {
it(`should output ${severity} severity to stderr`, () => {
- let entry: logger.LogEntry = {
+ const entry: logger.LogEntry = {
severity: severity as logger.LogSeverity,
message: 'test',
};
logger.write(entry);
expectStderr(entry);
- sandbox.restore(); // to avoid swallowing test runner output
});
}
});
diff --git a/spec/providers/crashlytics.spec.ts b/spec/providers/crashlytics.spec.ts
deleted file mode 100644
index 59e745dfa..000000000
--- a/spec/providers/crashlytics.spec.ts
+++ /dev/null
@@ -1,157 +0,0 @@
-// The MIT License (MIT)
-//
-// Copyright (c) 2017 Firebase
-//
-// 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.
-
-import { expect } from 'chai';
-
-import { apps as appsNamespace } from '../../src/apps';
-import * as functions from '../../src/index';
-import * as crashlytics from '../../src/providers/crashlytics';
-
-describe('Crashlytics Functions', () => {
- describe('Issue Builder', () => {
- before(() => {
- appsNamespace.init();
- process.env.GCLOUD_PROJECT = 'project1';
- });
-
- after(() => {
- delete appsNamespace.singleton;
- delete process.env.GCLOUD_PROJECT;
- });
-
- it('should allow both region and runtime options to be set', () => {
- const fn = functions
- .region('us-east1')
- .runWith({
- timeoutSeconds: 90,
- memory: '256MB',
- })
- .crashlytics.issue()
- .onNew((issue) => issue);
-
- expect(fn.__trigger.regions).to.deep.equal(['us-east1']);
- expect(fn.__trigger.availableMemoryMb).to.deep.equal(256);
- expect(fn.__trigger.timeout).to.deep.equal('90s');
- });
-
- describe('#onNew', () => {
- it('should return a TriggerDefinition with appropriate values', () => {
- const cloudFunction = crashlytics.issue().onNew((data) => null);
- expect(cloudFunction.__trigger).to.deep.equal({
- eventTrigger: {
- eventType: 'providers/firebase.crashlytics/eventTypes/issue.new',
- resource: 'projects/project1',
- service: 'fabric.io',
- },
- });
- });
- });
-
- describe('#onRegressed', () => {
- it('should return a TriggerDefinition with appropriate values', () => {
- const cloudFunction = crashlytics.issue().onRegressed((data) => null);
- expect(cloudFunction.__trigger).to.deep.equal({
- eventTrigger: {
- eventType:
- 'providers/firebase.crashlytics/eventTypes/issue.regressed',
- resource: 'projects/project1',
- service: 'fabric.io',
- },
- });
- });
- });
-
- describe('#onVelocityAlert', () => {
- it('should return a TriggerDefinition with appropriate values', () => {
- const cloudFunction = crashlytics
- .issue()
- .onVelocityAlert((data) => null);
- expect(cloudFunction.__trigger).to.deep.equal({
- eventTrigger: {
- eventType:
- 'providers/firebase.crashlytics/eventTypes/issue.velocityAlert',
- resource: 'projects/project1',
- service: 'fabric.io',
- },
- });
- });
- });
-
- describe('HandlerBuilder', () => {
- describe('#onNew', () => {
- it('should return a CloudFunction with appropriate values', () => {
- const cloudFunction = functions.handler.crashlytics.issue.onNew(
- (testIssue) => {
- return (
- testIssue.issueId + testIssue.issueTitle + testIssue.createTime
- );
- }
- );
- expect(cloudFunction.__trigger).to.deep.equal({});
- });
- });
-
- describe('#onRegressed', () => {
- it('should return a CloudFunction with appropriate values', () => {
- const cloudFunction = functions.handler.crashlytics.issue.onRegressed(
- (testIssue) => {
- return (
- testIssue.issueId + testIssue.issueTitle + testIssue.createTime
- );
- }
- );
- expect(cloudFunction.__trigger).to.deep.equal({});
- });
- });
-
- describe('#onVelocityAlert', () => {
- it('should return a CloudFunction with appropriate values', () => {
- const cloudFunction = functions.handler.crashlytics.issue.onVelocityAlert(
- (testIssue) => {
- return (
- testIssue.issueId + testIssue.issueTitle + testIssue.createTime
- );
- }
- );
- expect(cloudFunction.__trigger).to.deep.equal({});
- });
- });
- });
- });
-
- describe('process.env.GCLOUD_PROJECT not set', () => {
- it('should not throw if __trigger is not accessed', () => {
- expect(() => crashlytics.issue().onNew(() => null)).to.not.throw(Error);
- });
-
- it('should throw if __trigger is accessed', () => {
- expect(() => crashlytics.issue().onNew(() => null).__trigger).to.throw(
- Error
- );
- });
-
- it('should not throw when #run is called', () => {
- const cf = crashlytics.issue().onNew(() => null);
- expect(cf.run).to.not.throw(Error);
- });
- });
-});
diff --git a/spec/providers/database.spec.ts b/spec/providers/database.spec.ts
index b764a7ea0..bf7e54b6b 100644
--- a/spec/providers/database.spec.ts
+++ b/spec/providers/database.spec.ts
@@ -472,6 +472,17 @@ describe('Database Functions', () => {
);
}).to.throw(Error);
});
+
+ it('should use the emulator host when present', () => {
+ process.env.FIREBASE_DATABASE_EMULATOR_HOST = 'localhost:1234';
+ const [instance, path] = database.extractInstanceAndPath(
+ 'projects/_/instances/foo/refs/bar',
+ 'firebaseio-staging.com'
+ );
+ expect(instance).to.equal('http://localhost:1234/?ns=foo');
+ expect(path).to.equal('/bar');
+ delete process.env.FIREBASE_DATABASE_EMULATOR_HOST;
+ });
});
describe('DataSnapshot', () => {
diff --git a/spec/providers/https.spec.ts b/spec/providers/https.spec.ts
index f858d5923..128ea0058 100644
--- a/spec/providers/https.spec.ts
+++ b/spec/providers/https.spec.ts
@@ -30,7 +30,9 @@ import * as https from '../../src/providers/https';
import * as mocks from '../fixtures/credential/key.json';
import {
expectedResponseHeaders,
+ generateAppCheckToken,
generateIdToken,
+ mockFetchAppCheckPublicJwks,
mockFetchPublicKeys,
MockRequest,
mockRequest,
@@ -414,6 +416,58 @@ describe('callable.FunctionBuilder', () => {
});
});
+ it('should handle AppCheck token', async () => {
+ const mock = mockFetchAppCheckPublicJwks();
+ const projectId = appsNamespace().admin.options.projectId;
+ const appId = '1:65211879909:web:3ae38ef1cdcb2e01fe5f0c';
+ const appCheckToken = generateAppCheckToken(projectId, appId);
+ await runTest({
+ httpRequest: mockRequest(null, 'application/json', { appCheckToken }),
+ expectedData: null,
+ callableFunction: (data, context) => {
+ expect(context.app).to.not.be.undefined;
+ expect(context.app).to.not.be.null;
+ expect(context.app.appId).to.equal(appId);
+ expect(context.app.token.app_id).to.be.equal(appId);
+ expect(context.app.token.sub).to.be.equal(appId);
+ expect(context.app.token.aud).to.be.deep.equal([
+ `projects/${projectId}`,
+ ]);
+ expect(context.auth).to.be.undefined;
+ expect(context.instanceIdToken).to.be.undefined;
+ return null;
+ },
+ expectedHttpResponse: {
+ status: 200,
+ headers: expectedResponseHeaders,
+ body: { result: null },
+ },
+ });
+ mock.done();
+ });
+
+ it('should reject bad AppCheck token', async () => {
+ await runTest({
+ httpRequest: mockRequest(null, 'application/json', {
+ appCheckToken: 'FAKE',
+ }),
+ expectedData: null,
+ callableFunction: (data, context) => {
+ return;
+ },
+ expectedHttpResponse: {
+ status: 401,
+ headers: expectedResponseHeaders,
+ body: {
+ error: {
+ status: 'UNAUTHENTICATED',
+ message: 'Unauthenticated',
+ },
+ },
+ },
+ });
+ });
+
it('should handle instance id', async () => {
await runTest({
httpRequest: mockRequest(null, 'application/json', {
diff --git a/spec/providers/testLab.spec.ts b/spec/providers/testLab.spec.ts
index bf74a6fd1..8a70cf680 100644
--- a/spec/providers/testLab.spec.ts
+++ b/spec/providers/testLab.spec.ts
@@ -66,23 +66,23 @@ describe('Test Lab Functions', () => {
resource: {},
},
};
- const expected = {
+ const expected = {
testMatrixId: 'matrix-375mfeu9mnw8t',
state: 'INVALID',
createTime: '2019-04-15T17:43:32.538Z',
outcomeSummary: undefined,
invalidMatrixDetails: 'INVALID_INPUT_APK',
- resultStorage: {
+ resultStorage: {
gcsPath: 'gs://test.appspot.com',
resultsUrl: undefined,
toolResultsHistoryId: undefined,
toolResultsExecutionId: undefined,
- },
- clientInfo: {
+ } as testLab.ResultStorage,
+ clientInfo: {
name: 'test',
details: {},
- },
- };
+ } as testLab.ClientInfo,
+ } as testLab.TestMatrix;
const func = testLab.testMatrix().onComplete((matrix) => matrix);
return expect(func(event.data, event.context)).to.eventually.deep.equal(
expected
@@ -119,23 +119,23 @@ describe('Test Lab Functions', () => {
resource: {},
},
};
- const expected = {
+ const expected = {
testMatrixId: 'matrix-tsgjk8pnvxhya',
state: 'FINISHED',
createTime: '2019-04-15T18:03:11.115Z',
outcomeSummary: 'FAILURE',
invalidMatrixDetails: undefined,
- resultStorage: {
+ resultStorage: {
gcsPath: 'gs://test.appspot.com',
toolResultsHistoryId: 'bh.9b6f4dac24d3049',
toolResultsExecutionId: '6352915701487950333',
resultsUrl: 'https://path/to/results',
- },
- clientInfo: {
+ } as testLab.ResultStorage,
+ clientInfo: {
name: 'test',
details: {},
- },
- };
+ } as testLab.ClientInfo,
+ } as testLab.TestMatrix;
const func = testLab.testMatrix().onComplete((matrix) => matrix);
return expect(func(event.data, event.context)).to.eventually.deep.equal(
expected
@@ -161,7 +161,7 @@ describe('Test Lab Functions', () => {
describe('TestMatrix', () => {
describe('constructor', () => {
it('should populate basic fields', () => {
- const expected = {
+ const expected = {
testMatrixId: 'id1',
createTime: '2019-02-08T18:50:32.178Z',
state: 'FINISHED',
@@ -169,7 +169,7 @@ describe('Test Lab Functions', () => {
invalidMatrixDetails: 'DETAILS_UNAVAILABLE',
resultStorage: new testLab.ResultStorage(),
clientInfo: new testLab.ClientInfo(),
- };
+ } as testLab.TestMatrix;
const actual = new testLab.TestMatrix({
testMatrixId: 'id1',
timestamp: '2019-02-08T18:50:32.178Z',
@@ -185,10 +185,10 @@ describe('Test Lab Functions', () => {
describe('ClientInfo', () => {
describe('constructor', () => {
it('should populate basic fields', () => {
- const expected = {
+ const expected = {
name: 'client',
details: {},
- };
+ } as testLab.ClientInfo;
const actual = new testLab.ClientInfo({
name: 'client',
});
@@ -196,13 +196,13 @@ describe('Test Lab Functions', () => {
});
it('should populate key/value details', () => {
- const expected = {
+ const expected = {
name: 'client',
details: {
k0: 'v0',
k1: '',
},
- };
+ } as testLab.ClientInfo;
const actual = new testLab.ClientInfo({
name: 'client',
clientInfoDetails: [
@@ -223,12 +223,12 @@ describe('Test Lab Functions', () => {
describe('ResultStorage', () => {
describe('constructor', () => {
it('should populate basic fields', () => {
- const expected = {
+ const expected = {
gcsPath: 'path',
toolResultsHistoryId: 'h1',
toolResultsExecutionId: 'e2',
resultsUrl: 'http://example.com/',
- };
+ } as testLab.ResultStorage;
const actual = new testLab.ResultStorage({
googleCloudStorage: {
gcsPath: 'path',
diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts
index 109a1ad05..93377c822 100644
--- a/src/cloud-functions.ts
+++ b/src/cloud-functions.ts
@@ -22,13 +22,13 @@
import { Request, Response } from 'express';
import * as _ from 'lodash';
-import { warn } from './logger';
import {
DEFAULT_FAILURE_POLICY,
DeploymentOptions,
FailurePolicy,
Schedule,
} from './function-configuration';
+import { warn } from './logger';
export { Request, Response };
/** @hidden */
@@ -99,9 +99,6 @@ export interface EventContext {
* * `google.analytics.event.log`
* * `google.firebase.auth.user.create`
* * `google.firebase.auth.user.delete`
- * * `google.firebase.crashlytics.issue.new`
- * * `google.firebase.crashlytics.issue.regressed`
- * * `google.firebase.crashlytics.issue.velocityAlert`
* * `google.firebase.database.ref.write`
* * `google.firebase.database.ref.create`
* * `google.firebase.database.ref.update`
@@ -206,7 +203,7 @@ export namespace Change {
json: ChangeJson,
customizer: (x: any) => T = reinterpretCast
): Change {
- let before = _.assign({}, json.before);
+ let before = { ...json.before };
if (json.fieldMask) {
before = applyFieldMask(before, json.after, json.fieldMask);
}
@@ -223,7 +220,7 @@ export namespace Change {
after: any,
fieldMask: string
) {
- const before = _.assign({}, after);
+ const before = { ...after };
const masks = fieldMask.split(',');
masks.forEach((mask) => {
@@ -272,6 +269,8 @@ export interface TriggerAnnotated {
timeout?: string;
vpcConnector?: string;
vpcConnectorEgressSettings?: string;
+ serviceAccountEmail?: string;
+ ingressSettings?: string;
};
}
@@ -506,6 +505,8 @@ export function optionsToTrigger(options: DeploymentOptions) {
'512MB': 512,
'1GB': 1024,
'2GB': 2048,
+ '4GB': 4096,
+ '8GB': 8192,
};
trigger.availableMemoryMb = _.get(memoryLookup, options.memory);
}
@@ -513,10 +514,18 @@ export function optionsToTrigger(options: DeploymentOptions) {
trigger.schedule = options.schedule;
}
+ if (options.minInstances) {
+ trigger.minInstances = options.minInstances;
+ }
+
if (options.maxInstances) {
trigger.maxInstances = options.maxInstances;
}
+ if (options.ingressSettings) {
+ trigger.ingressSettings = options.ingressSettings;
+ }
+
if (options.vpcConnector) {
trigger.vpcConnector = options.vpcConnector;
}
@@ -525,5 +534,24 @@ export function optionsToTrigger(options: DeploymentOptions) {
trigger.vpcConnectorEgressSettings = options.vpcConnectorEgressSettings;
}
+ if (options.serviceAccount) {
+ if (options.serviceAccount === 'default') {
+ // Do nothing, since this is equivalent to not setting serviceAccount.
+ } else if (options.serviceAccount.endsWith('@')) {
+ if (!process.env.GCLOUD_PROJECT) {
+ throw new Error(
+ `Unable to determine email for service account '${options.serviceAccount}' because process.env.GCLOUD_PROJECT is not set.`
+ );
+ }
+ trigger.serviceAccountEmail = `${options.serviceAccount}${process.env.GCLOUD_PROJECT}.iam.gserviceaccount.com`;
+ } else if (options.serviceAccount.includes('@')) {
+ trigger.serviceAccountEmail = options.serviceAccount;
+ } else {
+ throw new Error(
+ `Invalid option for serviceAccount: '${options.serviceAccount}'. Valid options are 'default', a service account email, or '{serviceAccountName}@'`
+ );
+ }
+ }
+
return trigger;
}
diff --git a/src/function-builder.ts b/src/function-builder.ts
index 919f9f09b..f08b16117 100644
--- a/src/function-builder.ts
+++ b/src/function-builder.ts
@@ -26,6 +26,7 @@ import * as _ from 'lodash';
import { CloudFunction, EventContext } from './cloud-functions';
import {
DeploymentOptions,
+ INGRESS_SETTINGS_OPTIONS,
MAX_TIMEOUT_SECONDS,
RuntimeOptions,
SUPPORTED_REGIONS,
@@ -34,7 +35,6 @@ import {
} from './function-configuration';
import * as analytics from './providers/analytics';
import * as auth from './providers/auth';
-import * as crashlytics from './providers/crashlytics';
import * as database from './providers/database';
import * as firestore from './providers/firestore';
import * as https from './providers/https';
@@ -68,6 +68,17 @@ function assertRuntimeOptionsValid(runtimeOptions: RuntimeOptions): boolean {
);
}
+ if (
+ runtimeOptions.ingressSettings &&
+ !_.includes(INGRESS_SETTINGS_OPTIONS, runtimeOptions.ingressSettings)
+ ) {
+ throw new Error(
+ `The only valid ingressSettings values are: ${INGRESS_SETTINGS_OPTIONS.join(
+ ','
+ )}`
+ );
+ }
+
if (
runtimeOptions.vpcConnectorEgressSettings &&
!_.includes(
@@ -99,6 +110,16 @@ function assertRuntimeOptionsValid(runtimeOptions: RuntimeOptions): boolean {
}
}
}
+
+ if (
+ runtimeOptions.serviceAccount &&
+ runtimeOptions.serviceAccount !== 'default' &&
+ !_.includes(runtimeOptions.serviceAccount, '@')
+ ) {
+ throw new Error(
+ `serviceAccount must be set to 'default', a service account email, or '{serviceAccountName}@'`
+ );
+ }
return true;
}
@@ -133,15 +154,18 @@ export function region(
/**
* Configure runtime options for the function.
* @param runtimeOptions Object with optional fields:
- * 1. memory: amount of memory to allocate to the function, possible values
- * are: '128MB', '256MB', '512MB', '1GB', and '2GB'.
- * 2. timeoutSeconds: timeout for the function in seconds, possible values are
+ * 1. `memory`: amount of memory to allocate to the function, possible values
+ * are: '128MB', '256MB', '512MB', '1GB', '2GB', and '4GB'.
+ * 2. `timeoutSeconds`: timeout for the function in seconds, possible values are
* 0 to 540.
- * 3. failurePolicy: failure policy of the function, with boolean `true` being
+ * 3. `failurePolicy`: failure policy of the function, with boolean `true` being
* equivalent to providing an empty retry object.
- * 4. vpcConnector: id of a VPC connector in same project and region
- * 5. vpcConnectorEgressSettings: when a vpcConnector is set, control which
+ * 4. `vpcConnector`: id of a VPC connector in same project and region.
+ * 5. `vpcConnectorEgressSettings`: when a vpcConnector is set, control which
* egress traffic is sent through the vpcConnector.
+ * 6. `serviceAccount`: Specific service account for the function.
+ * 7. `ingressSettings`: ingress settings for the function, which control where a HTTPS
+ * function can be called from.
*
* Value must not be null.
*/
@@ -173,13 +197,16 @@ export class FunctionBuilder {
/**
* Configure runtime options for the function.
- * @param runtimeOptions Object with three optional fields:
- * 1. failurePolicy: failure policy of the function, with boolean `true` being
+ * @param runtimeOptions Object with optional fields:
+ * 1. `memory`: amount of memory to allocate to the function, possible values
+ * are: '128MB', '256MB', '512MB', '1GB', '2GB', and '4GB'.
+ * 2. `timeoutSeconds`: timeout for the function in seconds, possible values are
+ * 0 to 540.
+ * 3. `failurePolicy`: failure policy of the function, with boolean `true` being
* equivalent to providing an empty retry object.
- * 2. memory: amount of memory to allocate to the function, with possible
- * values being '128MB', '256MB', '512MB', '1GB', and '2GB'.
- * 3. timeoutSeconds: timeout for the function in seconds, with possible
- * values being 0 to 540.
+ * 4. `vpcConnector`: id of a VPC connector in the same project and region
+ * 5. `vpcConnectorEgressSettings`: when a `vpcConnector` is set, control which
+ * egress traffic is sent through the `vpcConnector`.
*
* Value must not be null.
*/
@@ -283,16 +310,6 @@ export class FunctionBuilder {
};
}
- get crashlytics() {
- return {
- /**
- * Handle events related to Crashlytics issues. An issue in Crashlytics is
- * an aggregation of crashes which have a shared root cause.
- */
- issue: () => crashlytics._issueWithOptions(this.options),
- };
- }
-
get analytics() {
return {
/**
diff --git a/src/function-configuration.ts b/src/function-configuration.ts
index 558895148..852482241 100644
--- a/src/function-configuration.ts
+++ b/src/function-configuration.ts
@@ -8,6 +8,7 @@ export const SUPPORTED_REGIONS = [
'us-west2',
'us-west3',
'us-west4',
+ 'europe-central2',
'europe-west1',
'europe-west2',
'europe-west3',
@@ -42,6 +43,8 @@ export const VALID_MEMORY_OPTIONS = [
'512MB',
'1GB',
'2GB',
+ '4GB',
+ '8GB',
] as const;
/**
@@ -53,6 +56,16 @@ export const VPC_EGRESS_SETTINGS_OPTIONS = [
'ALL_TRAFFIC',
] as const;
+/**
+ * List of available options for IngressSettings.
+ */
+export const INGRESS_SETTINGS_OPTIONS = [
+ 'INGRESS_SETTINGS_UNSPECIFIED',
+ 'ALLOW_ALL',
+ 'ALLOW_INTERNAL_ONLY',
+ 'ALLOW_INTERNAL_AND_GCLB',
+] as const;
+
/**
* Scheduler retry options. Applies only to scheduled functions.
*/
@@ -96,6 +109,20 @@ export interface RuntimeOptions {
*/
timeoutSeconds?: number;
+ /**
+ * Min number of actual instances allowed to be running in parallel
+ * Instances will be billed while idle.
+ * @hidden
+ */
+ minInstances?: number;
+
+ /**
+ * Which version of the internal contract between the CLI and the SDK are
+ * we using? For internal testing only.
+ * @hidden
+ */
+ apiVersion?: 1 | 2;
+
/**
* Max number of actual instances allowed to be running in parallel
*/
@@ -110,6 +137,16 @@ export interface RuntimeOptions {
* Egress settings for VPC connector
*/
vpcConnectorEgressSettings?: typeof VPC_EGRESS_SETTINGS_OPTIONS[number];
+
+ /**
+ * Specific service account for the function to run as
+ */
+ serviceAccount?: 'default' | string;
+
+ /**
+ * Ingress settings
+ */
+ ingressSettings?: typeof INGRESS_SETTINGS_OPTIONS[number];
}
export interface DeploymentOptions extends RuntimeOptions {
diff --git a/src/handler-builder.ts b/src/handler-builder.ts
index ea0f4af74..ad4cd1541 100644
--- a/src/handler-builder.ts
+++ b/src/handler-builder.ts
@@ -26,7 +26,6 @@ import { apps } from './apps';
import { CloudFunction, EventContext, HttpsFunction } from './cloud-functions';
import * as analytics from './providers/analytics';
import * as auth from './providers/auth';
-import * as crashlytics from './providers/crashlytics';
import * as database from './providers/database';
import * as firestore from './providers/firestore';
import * as https from './providers/https';
@@ -182,41 +181,6 @@ export class HandlerBuilder {
};
}
- /**
- * Create a handler for Firebase Crashlytics events.
-
- * `issue.onNew` handles events where the app experiences an issue for the first time.
-
- * @example
- * ```javascript
- * exports.myFunction = functions.handler.crashlytics.issue.onNew((issue) => { ... })
- * ```
-
- * `issue.onRegressed` handles events where an issue reoccurs after it
- * is closed in Crashlytics.
- *
- * @example
- * ```javascript
- * exports.myFunction = functions.handler.crashlytics.issue.onRegressed((issue) => { ... })
- * ```
-
- * `issue.onVelocityAlert` handles events where a statistically significant number
- * of sessions in a given build crash.
- *
- * @example
- * ```javascript
- * exports.myFunction = functions.handler.crashlytics.issue.onVelocityAlert((issue) => { ... })
- * ```
-
- */
- get crashlytics() {
- return {
- get issue() {
- return new crashlytics.IssueBuilder(() => null, {});
- },
- };
- }
-
/**
* Create a handler for Firebase Remote Config events.
diff --git a/src/index.ts b/src/index.ts
index dcca98172..dbb42003c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -23,7 +23,6 @@
// Providers:
import * as analytics from './providers/analytics';
import * as auth from './providers/auth';
-import * as crashlytics from './providers/crashlytics';
import * as database from './providers/database';
import * as firestore from './providers/firestore';
import * as https from './providers/https';
@@ -33,8 +32,8 @@ import * as storage from './providers/storage';
import * as testLab from './providers/testLab';
import * as apps from './apps';
-import * as logger from './logger';
import { handler } from './handler-builder';
+import * as logger from './logger';
import { setup } from './setup';
const app = apps.apps();
@@ -43,7 +42,6 @@ export {
analytics,
app,
auth,
- crashlytics,
database,
firestore,
handler,
diff --git a/src/logger.ts b/src/logger.ts
index 7958ca457..1dd109179 100644
--- a/src/logger.ts
+++ b/src/logger.ts
@@ -1,8 +1,8 @@
import { format } from 'util';
import {
- SUPPORTS_STRUCTURED_LOGS,
CONSOLE_SEVERITY,
+ SUPPORTS_STRUCTURED_LOGS,
UNPATCHED_CONSOLE,
} from './logger/common';
@@ -30,13 +30,40 @@ export interface LogEntry {
[key: string]: any;
}
+function removeCircular(obj: any, refs: any[] = []): any {
+ if (typeof obj !== 'object' || !obj) {
+ return obj;
+ }
+ if (refs.includes(obj)) {
+ return '[Circular]';
+ } else {
+ refs.push(obj);
+ }
+ let returnObj: any;
+ if (Array.isArray(obj)) {
+ returnObj = new Array(obj.length);
+ } else {
+ returnObj = {};
+ }
+ for (const k in obj) {
+ if (refs.includes(obj[k])) {
+ returnObj[k] = '[Circular]';
+ } else {
+ returnObj[k] = removeCircular(obj[k], refs);
+ }
+ }
+ return returnObj;
+}
+
/**
* Writes a `LogEntry` to `stdout`/`stderr` (depending on severity).
* @param entry The `LogEntry` including severity, message, and any additional structured metadata.
*/
export function write(entry: LogEntry) {
if (SUPPORTS_STRUCTURED_LOGS) {
- UNPATCHED_CONSOLE[CONSOLE_SEVERITY[entry.severity]](JSON.stringify(entry));
+ UNPATCHED_CONSOLE[CONSOLE_SEVERITY[entry.severity]](
+ JSON.stringify(removeCircular(entry))
+ );
return;
}
@@ -50,7 +77,11 @@ export function write(entry: LogEntry) {
}
}
if (jsonKeyCount > 0) {
- message = `${message} ${JSON.stringify(jsonPayload, null, 2)}`;
+ message = `${message} ${JSON.stringify(
+ removeCircular(jsonPayload),
+ null,
+ 2
+ )}`;
}
UNPATCHED_CONSOLE[CONSOLE_SEVERITY[entry.severity]](message);
}
diff --git a/src/logger/compat.ts b/src/logger/compat.ts
index a7c27bd16..4239221f0 100644
--- a/src/logger/compat.ts
+++ b/src/logger/compat.ts
@@ -1,9 +1,9 @@
+import { format } from 'util';
import {
+ CONSOLE_SEVERITY,
SUPPORTS_STRUCTURED_LOGS,
UNPATCHED_CONSOLE,
- CONSOLE_SEVERITY,
} from './common';
-import { format } from 'util';
/** @hidden */
function patchedConsole(severity: string): (data: any, ...args: any[]) => void {
diff --git a/src/providers/crashlytics.ts b/src/providers/crashlytics.ts
deleted file mode 100644
index 54fc26469..000000000
--- a/src/providers/crashlytics.ts
+++ /dev/null
@@ -1,197 +0,0 @@
-// The MIT License (MIT)
-//
-// Copyright (c) 2017 Firebase
-//
-// 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.
-
-import {
- CloudFunction,
- EventContext,
- makeCloudFunction,
-} from '../cloud-functions';
-import { DeploymentOptions } from '../function-configuration';
-
-/** @hidden */
-export const provider = 'google.firebase.crashlytics';
-/** @hidden */
-export const service = 'fabric.io';
-
-/**
- * Registers a Cloud Function to handle Crashlytics issue events.
- *
- * @returns Crashlytics issue event builder interface.
- */
-export function issue() {
- return _issueWithOptions({});
-}
-
-/** @hidden */
-export function _issueWithOptions(options: DeploymentOptions) {
- return new IssueBuilder(() => {
- if (!process.env.GCLOUD_PROJECT) {
- throw new Error('process.env.GCLOUD_PROJECT is not set.');
- }
- return 'projects/' + process.env.GCLOUD_PROJECT;
- }, options);
-}
-
-/** The Firebase Crashlytics issue builder interface. */
-export class IssueBuilder {
- /** @hidden */
- constructor(
- private triggerResource: () => string,
- private options: DeploymentOptions
- ) {}
-
- /** @hidden */
- onNewDetected(handler: any): Error {
- throw new Error('"onNewDetected" is now deprecated, please use "onNew"');
- }
-
- /**
- * Event handler that fires every time a new issue occurs in a project.
- *
- * @param handler Event handler that fires every time a new issue event occurs.
- * @example
- * ```javascript
- * exports.postOnNewIssue = functions.crashlytics.issue().onNew(event => {
- * const { data } = event;
- * issueId = data.issueId;
- * issueTitle = data.issueTitle;
- * const slackMessage = ` There's a new issue (${issueId}) ` +
- * `in your app - ${issueTitle}`;
- * return notifySlack(slackMessage).then(() => {
- * functions.logger.info(`Posted new issue ${issueId} successfully to Slack`);
- * });
- * });
- * ```
- */
- onNew(
- handler: (issue: Issue, context: EventContext) => PromiseLike | any
- ): CloudFunction {
- return this.onEvent(handler, 'issue.new');
- }
-
- /**
- * Event handler that fires every time a regressed issue reoccurs in a project.
- *
- * @param handler Event handler that fires every time a regressed issue event occurs.
- */
- onRegressed(
- handler: (issue: Issue, context: EventContext) => PromiseLike | any
- ): CloudFunction {
- return this.onEvent(handler, 'issue.regressed');
- }
-
- /**
- * Event handler that fires every time a velocity alert occurs in a project.
- *
- * @param handler handler that fires every time a velocity alert issue event occurs.
- */
- onVelocityAlert(
- handler: (issue: Issue, context: EventContext) => PromiseLike | any
- ): CloudFunction {
- return this.onEvent(handler, 'issue.velocityAlert');
- }
-
- private onEvent(
- handler: (issue: Issue, context: EventContext) => PromiseLike | any,
- eventType: string
- ): CloudFunction {
- return makeCloudFunction({
- handler,
- provider,
- eventType,
- service,
- legacyEventType: `providers/firebase.crashlytics/eventTypes/${eventType}`,
- triggerResource: this.triggerResource,
- options: this.options,
- });
- }
-}
-
-/**
- * Interface representing a Firebase Crashlytics event that was logged for a specific issue.
- */
-export interface Issue {
- /** Crashlytics-provided issue ID. */
- issueId: string;
-
- /** Crashlytics-provided issue title. */
- issueTitle: string;
-
- /** AppInfo interface describing the App. */
- appInfo: AppInfo;
-
- /**
- * UTC when the issue occurred in ISO8601 standard representation.
- *
- * Example: 1970-01-17T10:52:15.661-08:00
- */
- createTime: string;
-
- /**
- * UTC When the issue was closed in ISO8601 standard representation.
- *
- * Example: 1970-01-17T10:52:15.661-08:00
- */
- resolvedTime?: string;
-
- /** Information about the velocity alert, like number of crashes and percentage of users affected by the issue. */
- velocityAlert?: VelocityAlert;
-}
-
-/** Interface representing Firebase Crashlytics VelocityAlert data. */
-export interface VelocityAlert {
- /**
- * The percentage of sessions which have been impacted by this issue.
- *
- * Example: .04
- */
- crashPercentage: number;
-
- /** The number of crashes that this issue has caused. */
- crashes: number;
-}
-
-/** Interface representing Firebase Crashlytics AppInfo data. */
-export interface AppInfo {
- /**
- * The app's name.
- *
- * Example: "My Awesome App".
- */
- appName: string;
-
- /** The app's platform.
- *
- * Examples: "android", "ios".
- */
- appPlatform: string;
-
- /** Unique application identifier within an app store, either the Android package name or the iOS bundle id. */
- appId: string;
-
- /**
- * The app's version name.
- *
- * Examples: "1.0", "4.3.1.1.213361", "2.3 (1824253)", "v1.8b22p6".
- */
- latestAppVersion: string;
-}
diff --git a/src/providers/database.ts b/src/providers/database.ts
index 2aa50b55a..76252d515 100644
--- a/src/providers/database.ts
+++ b/src/providers/database.ts
@@ -140,7 +140,7 @@ export function _refWithOptions(
);
}
- let instance = undefined;
+ let instance;
const prodMatch = databaseURL.match(databaseURLRegex);
if (prodMatch) {
instance = prodMatch[1];
@@ -334,8 +334,15 @@ export function extractInstanceAndPath(
`Expect project to be '_' in a Firebase Realtime Database event`
);
}
- const dbInstance = 'https://' + dbInstanceName + '.' + domain;
- return [dbInstance, path];
+
+ const emuHost = process.env.FIREBASE_DATABASE_EMULATOR_HOST;
+ if (emuHost) {
+ const dbInstance = `http://${emuHost}/?ns=${dbInstanceName}`;
+ return [dbInstance, path];
+ } else {
+ const dbInstance = 'https://' + dbInstanceName + '.' + domain;
+ return [dbInstance, path];
+ }
}
/**
diff --git a/src/providers/https.ts b/src/providers/https.ts
index cf64c9d90..c6167c50a 100644
--- a/src/providers/https.ts
+++ b/src/providers/https.ts
@@ -28,7 +28,7 @@ import * as _ from 'lodash';
import { apps } from '../apps';
import { HttpsFunction, optionsToTrigger, Runnable } from '../cloud-functions';
import { DeploymentOptions } from '../function-configuration';
-import { warn, error } from '../logger';
+import { error, info, warn } from '../logger';
/** @hidden */
export interface Request extends express.Request {
@@ -248,6 +248,67 @@ export class HttpsError extends Error {
* The interface for metadata for the API as passed to the handler.
*/
export interface CallableContext {
+ /**
+ * The result of decoding and verifying a Firebase AppCheck token.
+ */
+ app?: {
+ appId: string;
+
+ // This is actually a firebase.appCheck.DecodedAppCheckToken, but
+ // that type may not be available in some supported SDK versions.
+ // Declare as an inline type, which DecodedAppCheckToken will be
+ // able to merge with.
+ // TODO: Replace with the real type once we bump the min-version of
+ // the admin SDK
+ token: {
+ /**
+ * The issuer identifier for the issuer of the response.
+ *
+ * This value is a URL with the format
+ * `https://firebaseappcheck.googleapis.com/`, where `` is the
+ * same project number specified in the [`aud`](#aud) property.
+ */
+ iss: string;
+
+ /**
+ * The Firebase App ID corresponding to the app the token belonged to.
+ *
+ * As a convenience, this value is copied over to the [`app_id`](#app_id) property.
+ */
+ sub: string;
+
+ /**
+ * The audience for which this token is intended.
+ *
+ * This value is a JSON array of two strings, the first is the project number of your
+ * Firebase project, and the second is the project ID of the same project.
+ */
+ aud: string[];
+
+ /**
+ * The App Check token's expiration time, in seconds since the Unix epoch. That is, the
+ * time at which this App Check token expires and should no longer be considered valid.
+ */
+ exp: number;
+
+ /**
+ * The App Check token's issued-at time, in seconds since the Unix epoch. That is, the
+ * time at which this App Check token was issued and should start to be considered
+ * valid.
+ */
+ iat: number;
+
+ /**
+ * The App ID corresponding to the App the App Check token belonged to.
+ *
+ * This value is not actually one of the JWT token claims. It is added as a
+ * convenience, and is set as the value of the [`sub`](#sub) property.
+ */
+ app_id: string;
+ [key: string]: any;
+ };
+ };
+
/**
* The result of decoding and verifying a Firebase Auth ID token.
*/
@@ -411,6 +472,108 @@ export function decode(data: any): any {
return data;
}
+/**
+ * Be careful when changing token status values.
+ *
+ * Users are encouraged to setup log-based metric based on these values, and
+ * changing their values may cause their metrics to break.
+ *
+ */
+/** @hidden */
+type TokenStatus = 'MISSING' | 'VALID' | 'INVALID';
+
+/** @hidden */
+interface CallableTokenStatus {
+ app: TokenStatus;
+ auth: TokenStatus;
+}
+
+/**
+ * Check and verify tokens included in the requests. Once verified, tokens
+ * are injected into the callable context.
+ *
+ * @param {Request} req - Request sent to the Callable function.
+ * @param {CallableContext} ctx - Context to be sent to callable function handler.
+ * @return {CallableTokenStatus} Status of the token verifications.
+ */
+/** @hidden */
+async function checkTokens(
+ req: Request,
+ ctx: CallableContext
+): Promise {
+ const verifications: CallableTokenStatus = {
+ app: 'MISSING',
+ auth: 'MISSING',
+ };
+
+ const appCheck = req.header('X-Firebase-AppCheck');
+ if (appCheck) {
+ verifications.app = 'INVALID';
+ try {
+ if (!apps().admin.appCheck) {
+ throw new Error(
+ 'Cannot validate AppCheck token. Please update Firebase Admin SDK to >= v9.8.0'
+ );
+ }
+ const appCheckToken = await apps()
+ .admin.appCheck()
+ .verifyToken(appCheck);
+ ctx.app = {
+ appId: appCheckToken.appId,
+ token: appCheckToken.token,
+ };
+ verifications.app = 'VALID';
+ } catch (err) {
+ warn('Failed to validate AppCheck token.', err);
+ }
+ }
+
+ const authorization = req.header('Authorization');
+ if (authorization) {
+ verifications.auth = 'INVALID';
+ const match = authorization.match(/^Bearer (.*)$/);
+ if (match) {
+ const idToken = match[1];
+ try {
+ const authToken = await apps()
+ .admin.auth()
+ .verifyIdToken(idToken);
+
+ verifications.auth = 'VALID';
+ ctx.auth = {
+ uid: authToken.uid,
+ token: authToken,
+ };
+ } catch (err) {
+ warn('Failed to validate auth token.', err);
+ }
+ }
+ }
+
+ const logPayload = {
+ verifications,
+ 'logging.googleapis.com/labels': {
+ 'firebase-log-type': 'callable-request-verification',
+ },
+ };
+
+ const errs = [];
+ if (verifications.app === 'INVALID') {
+ errs.push('AppCheck token was rejected.');
+ }
+ if (verifications.auth === 'INVALID') {
+ errs.push('Auth token was rejected.');
+ }
+
+ if (errs.length == 0) {
+ info('Callable request verification passed', logPayload);
+ } else {
+ warn(`Callable request verification failed: ${errs.join(' ')}`, logPayload);
+ }
+
+ return verifications;
+}
+
/** @hidden */
const corsHandler = cors({ origin: true, methods: 'POST' });
@@ -427,25 +590,9 @@ export function _onCallWithOptions(
}
const context: CallableContext = { rawRequest: req };
-
- const authorization = req.header('Authorization');
- if (authorization) {
- const match = authorization.match(/^Bearer (.*)$/);
- if (!match) {
- throw new HttpsError('unauthenticated', 'Unauthenticated');
- }
- const idToken = match[1];
- try {
- const authToken = await apps()
- .admin.auth()
- .verifyIdToken(idToken);
- context.auth = {
- uid: authToken.uid,
- token: authToken,
- };
- } catch (err) {
- throw new HttpsError('unauthenticated', 'Unauthenticated');
- }
+ const tokenStatus = await checkTokens(req, context);
+ if (tokenStatus.app === 'INVALID' || tokenStatus.auth === 'INVALID') {
+ throw new HttpsError('unauthenticated', 'Unauthenticated');
}
const instanceId = req.header('Firebase-Instance-ID-Token');
diff --git a/src/setup.ts b/src/setup.ts
index d2935af15..6a9db702d 100644
--- a/src/setup.ts
+++ b/src/setup.ts
@@ -51,10 +51,10 @@ export function setup() {
);
process.env.FIREBASE_CONFIG = JSON.stringify({
databaseURL:
- `${process.env.DATABASE_URL}` ||
+ process.env.DATABASE_URL ||
`https://${process.env.GCLOUD_PROJECT}.firebaseio.com`,
storageBucket:
- `${process.env.STORAGE_BUCKET_URL}` ||
+ process.env.STORAGE_BUCKET_URL ||
`${process.env.GCLOUD_PROJECT}.appspot.com`,
projectId: process.env.GCLOUD_PROJECT,
});
diff --git a/tsconfig.release.json b/tsconfig.release.json
index a226eb290..1a45bd58b 100644
--- a/tsconfig.release.json
+++ b/tsconfig.release.json
@@ -1,13 +1,13 @@
{
"compilerOptions": {
"declaration": true,
- "lib": ["es2017"],
+ "lib": ["es2018"],
"module": "commonjs",
"noImplicitAny": false,
"noUnusedLocals": true,
"outDir": "lib",
"stripInternal": true,
- "target": "es2017",
+ "target": "es2018",
"typeRoots": ["./node_modules/@types"]
},
"files": ["./src/index.ts", "./src/logger.ts", "./src/logger/compat.ts"]