From 9df27f44c0c96b3dc83e2e30ee93ff45809e8374 Mon Sep 17 00:00:00 2001 From: Tina Liang Date: Mon, 13 Jan 2020 11:52:31 -0800 Subject: [PATCH 001/529] adding scheduled function to handler builder (#230) * adding scheduled function to handler builder --- spec/providers/pubsub.spec.ts | 106 +++++++++++++++++++++++----------- src/handler-builder.ts | 6 ++ src/providers/pubsub.ts | 99 ++++++++++++++++--------------- 3 files changed, 131 insertions(+), 80 deletions(-) diff --git a/spec/providers/pubsub.spec.ts b/spec/providers/pubsub.spec.ts index 53776c357..f64cd2119 100644 --- a/spec/providers/pubsub.spec.ts +++ b/spec/providers/pubsub.spec.ts @@ -311,45 +311,83 @@ describe('Pubsub Functions', () => { describe('handler namespace', () => { describe('#onPublish', () => { - it('should return an empty trigger', () => { - const result = functions.handler.pubsub.topic.onPublish(() => null); - expect(result.__trigger).to.deep.equal({}); - }); + describe('#topic', () => { + it('should return an empty trigger', () => { + const result = functions.handler.pubsub.topic.onPublish(() => null); + expect(result.__trigger).to.deep.equal({}); + }); - it('should properly handle a new-style event', () => { - const raw = new Buffer('{"hello":"world"}', 'utf8').toString('base64'); - const event = { - data: { - data: raw, - attributes: { - foo: 'bar', + it('should properly handle a new-style event', () => { + const raw = new Buffer('{"hello":"world"}', 'utf8').toString( + 'base64' + ); + const event = { + data: { + data: raw, + attributes: { + foo: 'bar', + }, }, - }, - context: { - eventId: '70172329041928', - timestamp: '2018-04-09T07:56:12.975Z', - eventType: 'google.pubsub.topic.publish', - resource: { - service: 'pubsub.googleapis.com', - name: 'projects/project1/topics/toppy', + context: { + eventId: '70172329041928', + timestamp: '2018-04-09T07:56:12.975Z', + eventType: 'google.pubsub.topic.publish', + resource: { + service: 'pubsub.googleapis.com', + name: 'projects/project1/topics/toppy', + }, }, - }, - }; - - const result = functions.handler.pubsub.topic.onPublish((data) => { - return { - raw: data.data, - json: data.json, - attributes: data.attributes, }; - }); - return expect( - result(event.data, event.context) - ).to.eventually.deep.equal({ - raw, - json: { hello: 'world' }, - attributes: { foo: 'bar' }, + const result = functions.handler.pubsub.topic.onPublish((data) => { + return { + raw: data.data, + json: data.json, + attributes: data.attributes, + }; + }); + + return expect( + result(event.data, event.context) + ).to.eventually.deep.equal({ + raw, + json: { hello: 'world' }, + attributes: { foo: 'bar' }, + }); + }); + }); + describe('#schedule', () => { + it('should return an empty trigger', () => { + const result = functions.handler.pubsub.schedule.onRun(() => null); + expect(result.__trigger).to.deep.equal({}); + }); + it('should return a handler with a proper event context', () => { + const raw = new Buffer('{"hello":"world"}', 'utf8').toString( + 'base64' + ); + const event = { + data: { + data: raw, + attributes: { + foo: 'bar', + }, + }, + context: { + eventId: '70172329041928', + timestamp: '2018-04-09T07:56:12.975Z', + eventType: 'google.pubsub.topic.publish', + resource: { + service: 'pubsub.googleapis.com', + name: 'projects/project1/topics/toppy', + }, + }, + }; + const result = functions.handler.pubsub.schedule.onRun( + (context) => context.eventId + ); + return expect(result(event.data, event.context)).to.eventually.equal( + '70172329041928' + ); }); }); }); diff --git a/src/handler-builder.ts b/src/handler-builder.ts index 22b2268aa..5d7c122d7 100644 --- a/src/handler-builder.ts +++ b/src/handler-builder.ts @@ -201,6 +201,12 @@ export class HandlerBuilder { get topic() { return new pubsub.TopicBuilder(() => null, {}); }, + /** + * Handle periodic events triggered by Cloud Scheduler. + */ + get schedule() { + return new pubsub.ScheduleBuilder(() => null, {}); + }, }; } diff --git a/src/providers/pubsub.ts b/src/providers/pubsub.ts index aee9e6c17..0f12f669c 100644 --- a/src/providers/pubsub.ts +++ b/src/providers/pubsub.ts @@ -63,52 +63,6 @@ export function _topicWithOptions( }, options); } -export function schedule(schedule: string): ScheduleBuilder { - return _scheduleWithOptions(schedule, {}); -} - -export class ScheduleBuilder { - /** @hidden */ - constructor(private options: DeploymentOptions) {} - - retryConfig(config: ScheduleRetryConfig): ScheduleBuilder { - this.options.schedule.retryConfig = config; - return this; - } - - timeZone(timeZone: string): ScheduleBuilder { - this.options.schedule.timeZone = timeZone; - return this; - } - - onRun(handler: (context: EventContext) => PromiseLike | any) { - const triggerResource = () => { - if (!process.env.GCLOUD_PROJECT) { - throw new Error('process.env.GCLOUD_PROJECT is not set.'); - } - return `projects/${process.env.GCLOUD_PROJECT}/topics`; // The CLI will append the correct topic name based on region and function name - }; - const cloudFunction = makeCloudFunction({ - contextOnlyHandler: handler, - provider, - service, - triggerResource, - eventType: 'topic.publish', - options: this.options, - labels: { 'deployment-scheduled': 'true' }, - }); - return cloudFunction; - } -} - -/** @hidden */ -export function _scheduleWithOptions( - schedule: string, - options: DeploymentOptions -): ScheduleBuilder { - return new ScheduleBuilder({ ...options, schedule: { schedule } }); -} - /** * The Google Cloud Pub/Sub topic builder. * @@ -144,6 +98,59 @@ export class TopicBuilder { } } +export function schedule(schedule: string): ScheduleBuilder { + return _scheduleWithOptions(schedule, {}); +} + +/** @hidden */ +export function _scheduleWithOptions( + schedule: string, + options: DeploymentOptions +): ScheduleBuilder { + const triggerResource = () => { + if (!process.env.GCLOUD_PROJECT) { + throw new Error('process.env.GCLOUD_PROJECT is not set.'); + } + // The CLI will append the correct topic name based on region and function name + return `projects/${process.env.GCLOUD_PROJECT}/topics`; + }; + return new ScheduleBuilder(triggerResource, { + ...options, + schedule: { schedule }, + }); +} + +export class ScheduleBuilder { + /** @hidden */ + constructor( + private triggerResource: () => string, + private options: DeploymentOptions + ) {} + + retryConfig(config: ScheduleRetryConfig): ScheduleBuilder { + this.options.schedule.retryConfig = config; + return this; + } + + timeZone(timeZone: string): ScheduleBuilder { + this.options.schedule.timeZone = timeZone; + return this; + } + + onRun(handler: (context: EventContext) => PromiseLike | any) { + const cloudFunction = makeCloudFunction({ + contextOnlyHandler: handler, + provider, + service, + triggerResource: this.triggerResource, + eventType: 'topic.publish', + options: this.options, + labels: { 'deployment-scheduled': 'true' }, + }); + return cloudFunction; + } +} + /** * Interface representing a Google Cloud Pub/Sub message. * From 2940a4b5230e6469ebca3102b11d45915a3a8939 Mon Sep 17 00:00:00 2001 From: Lauren Long Date: Thu, 19 Mar 2020 10:40:58 -0700 Subject: [PATCH 002/529] Use process.env.PWD for finding .runtimeconfig.json (#634) --- CHANGELOG.md | 4 ++++ changelog.txt | 0 spec/config.spec.ts | 12 +++++++++--- src/config.ts | 15 +++++++++------ 4 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 changelog.txt diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..69ffc7120 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +**IMPORTANT: Please update to this version of `firebase-functions` if you are using Node.js 10 functions, otherwise your functions will break in production.** + +- Prevents deployment and runtime issues caused by upcoming changes to Cloud Functions infrastructure for Node.js 10 functions. (Issue #630) +- Adds support for writing scheduled functions under handler namespace. diff --git a/changelog.txt b/changelog.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/config.spec.ts b/spec/config.spec.ts index 64963be4e..b721dc3a5 100644 --- a/spec/config.spec.ts +++ b/spec/config.spec.ts @@ -25,6 +25,12 @@ import * as mockRequire from 'mock-require'; import { config, firebaseConfig } from '../src/config'; describe('config()', () => { + before(() => { + process.env.PWD = '/srv'; + }); + after(() => { + delete process.env.PWD; + }); afterEach(() => { mockRequire.stopAll(); delete config.singleton; @@ -33,19 +39,19 @@ describe('config()', () => { }); it('loads config values from .runtimeconfig.json', () => { - mockRequire('../../../.runtimeconfig.json', { foo: 'bar', firebase: {} }); + mockRequire('/srv/.runtimeconfig.json', { foo: 'bar', firebase: {} }); const loaded = config(); expect(loaded).to.not.have.property('firebase'); expect(loaded).to.have.property('foo', 'bar'); }); it('does not provide firebase config if .runtimeconfig.json not invalid', () => { - mockRequire('../../../.runtimeconfig.json', 'does-not-exist'); + mockRequire('/srv/.runtimeconfig.json', 'does-not-exist'); expect(firebaseConfig()).to.be.null; }); it('does not provide firebase config if .ruuntimeconfig.json has no firebase property', () => { - mockRequire('../../../.runtimeconfig.json', {}); + mockRequire('/srv/.runtimeconfig.json', {}); expect(firebaseConfig()).to.be.null; }); diff --git a/src/config.ts b/src/config.ts index 8cd4d3dd1..8925ff940 100644 --- a/src/config.ts +++ b/src/config.ts @@ -21,6 +21,7 @@ // SOFTWARE. import * as firebase from 'firebase-admin'; +import * as path from 'path'; export function config(): config.Config { if (typeof config.singleton === 'undefined') { @@ -68,9 +69,10 @@ export function firebaseConfig(): firebase.AppOptions | null { // Could have Runtime Config with Firebase in it as an ENV location or default. try { - const path = - process.env.CLOUD_RUNTIME_CONFIG || '../../../.runtimeconfig.json'; - const config = require(path); + const configPath = + process.env.CLOUD_RUNTIME_CONFIG || + path.join(process.env.PWD, '.runtimeconfig.json'); + const config = require(configPath); if (config.firebase) { return config.firebase; } @@ -92,9 +94,10 @@ function init() { } try { - const path = - process.env.CLOUD_RUNTIME_CONFIG || '../../../.runtimeconfig.json'; - const parsed = require(path); + const configPath = + process.env.CLOUD_RUNTIME_CONFIG || + path.join(process.env.PWD, '.runtimeconfig.json'); + const parsed = require(configPath); delete parsed.firebase; config.singleton = parsed; return; From 6873662f9814ed9262f0b2ac606bde1b3d1e0aea Mon Sep 17 00:00:00 2001 From: Bryan Kendall Date: Thu, 19 Mar 2020 11:02:30 -0700 Subject: [PATCH 003/529] New Publish Path (#231) --- package.json | 7 +- scripts/publish-container/Dockerfile | 12 ++ scripts/publish-container/cloudbuild.yaml | 4 + scripts/publish.sh | 135 ++++++++++++++++++++++ scripts/publish/cloudbuild.yaml | 114 ++++++++++++++++++ scripts/publish/deploy_key.enc | Bin 0 -> 3505 bytes scripts/publish/hub.enc | Bin 0 -> 191 bytes scripts/publish/npmrc.enc | Bin 0 -> 185 bytes scripts/publish/twitter.json.enc | Bin 0 -> 337 bytes scripts/tweet.js | 52 +++++++++ 10 files changed, 322 insertions(+), 2 deletions(-) create mode 100644 scripts/publish-container/Dockerfile create mode 100644 scripts/publish-container/cloudbuild.yaml create mode 100644 scripts/publish.sh create mode 100644 scripts/publish/cloudbuild.yaml create mode 100644 scripts/publish/deploy_key.enc create mode 100644 scripts/publish/hub.enc create mode 100644 scripts/publish/npmrc.enc create mode 100644 scripts/publish/twitter.json.enc create mode 100644 scripts/tweet.js diff --git a/package.json b/package.json index b9d7e31fc..35f0517b7 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/firebase/firebase-functions.git" + "url": "https://github.com/firebase/firebase-functions.git" }, "license": "MIT", "author": "Firebase Team", @@ -23,10 +23,13 @@ ], "main": "lib/index.js", "types": "lib/index.d.ts", + "publishConfig": { + "registry": "https://wombat-dressing-room.appspot.com" + }, "scripts": { "apidocs": "node docgen/generate-docs.js", "build:pack": "rm -rf lib && npm install && tsc -p tsconfig.release.json && npm pack", - "build:release": "npm install --production && npm install typescript firebase-admin && tsc -p tsconfig.release.json", + "build:release": "npm install --production && npm install --no-save typescript firebase-admin && tsc -p tsconfig.release.json", "build": "tsc -p tsconfig.release.json", "format": "prettier --check '**/*.{json,md,ts,yml,yaml}'", "format:fix": "prettier --write '**/*.{json,md,ts,yml,yaml}'", diff --git a/scripts/publish-container/Dockerfile b/scripts/publish-container/Dockerfile new file mode 100644 index 000000000..c8ba24c12 --- /dev/null +++ b/scripts/publish-container/Dockerfile @@ -0,0 +1,12 @@ +FROM node:8 + +# Install dependencies +RUN apt-get update && \ + apt-get install -y curl git jq + +# Install npm at latest. +RUN npm install --global npm@latest + +# Install hub +RUN curl -fsSL --output hub.tgz https://github.com/github/hub/releases/download/v2.13.0/hub-linux-amd64-2.13.0.tgz +RUN tar --strip-components=2 -C /usr/bin -xf hub.tgz hub-linux-amd64-2.13.0/bin/hub diff --git a/scripts/publish-container/cloudbuild.yaml b/scripts/publish-container/cloudbuild.yaml new file mode 100644 index 000000000..7a99a8cab --- /dev/null +++ b/scripts/publish-container/cloudbuild.yaml @@ -0,0 +1,4 @@ +steps: + - name: 'gcr.io/cloud-builders/docker' + args: ['build', '-t', 'gcr.io/$PROJECT_ID/package-builder', '.'] +images: ['gcr.io/$PROJECT_ID/package-builder'] diff --git a/scripts/publish.sh b/scripts/publish.sh new file mode 100644 index 000000000..a1498460e --- /dev/null +++ b/scripts/publish.sh @@ -0,0 +1,135 @@ +#!/bin/bash +set -e + +printusage() { + echo "publish.sh " + echo "REPOSITORY_ORG and REPOSITORY_NAME should be set in the environment." + echo "e.g. REPOSITORY_ORG=user, REPOSITORY_NAME=repo" + echo "" + echo "Arguments:" + echo " version: 'patch', 'minor', or 'major'." +} + +VERSION=$1 +if [[ $VERSION == "" ]]; then + printusage + exit 1 +elif [[ ! ($VERSION == "patch" || $VERSION == "minor" || $VERSION == "major") ]]; then + printusage + exit 1 +fi + +if [[ $REPOSITORY_ORG == "" ]]; then + printusage + exit 1 +fi +if [[ $REPOSITORY_NAME == "" ]]; then + printusage + exit 1 +fi + +WDIR=$(pwd) + +echo "Checking for commands..." +trap "echo 'Missing hub.'; exit 1" ERR +which hub &> /dev/null +trap - ERR + +trap "echo 'Missing node.'; exit 1" ERR +which node &> /dev/null +trap - ERR + +trap "echo 'Missing jq.'; exit 1" ERR +which jq &> /dev/null +trap - ERR +echo "Checked for commands." + +echo "Checking for Twitter credentials..." +trap "echo 'Missing Twitter credentials.'; exit 1" ERR +test -f "${WDIR}/scripts/twitter.json" +trap - ERR +echo "Checked for Twitter credentials..." + +echo "Checking for logged-in npm user..." +trap "echo 'Please login to npm using \`npm login --registry https://wombat-dressing-room.appspot.com\`'; exit 1" ERR +npm whoami --registry https://wombat-dressing-room.appspot.com +trap - ERR +echo "Checked for logged-in npm user." + +echo "Moving to temporary directory.." +TEMPDIR=$(mktemp -d) +echo "[DEBUG] ${TEMPDIR}" +cd "${TEMPDIR}" +echo "Moved to temporary directory." + +echo "Cloning repository..." +git clone "git@github.com:${REPOSITORY_ORG}/${REPOSITORY_NAME}.git" +cd "${REPOSITORY_NAME}" +echo "Cloned repository." + +echo "Making sure there is a changelog..." +if [ ! -s CHANGELOG.md ]; then + echo "CHANGELOG.md is empty. aborting." + exit 1 +fi +echo "Made sure there is a changelog." + +echo "Running npm install..." +npm install +echo "Ran npm install." + +echo "Running tests..." +npm test +echo "Ran tests." + +echo "Running publish build..." +npm run build:release +echo "Ran publish build." + +echo "Making a $VERSION version..." +npm version $VERSION +NEW_VERSION=$(jq -r ".version" package.json) +echo "Made a $VERSION version." + +echo "Making the release notes..." +RELEASE_NOTES_FILE=$(mktemp) +echo "[DEBUG] ${RELEASE_NOTES_FILE}" +echo "v${NEW_VERSION}" >> "${RELEASE_NOTES_FILE}" +echo "" >> "${RELEASE_NOTES_FILE}" +cat CHANGELOG.md >> "${RELEASE_NOTES_FILE}" +echo "Made the release notes." + +echo "Publishing to npm..." +if [[ $DRY_RUN == "" ]]; then + npm publish +else + echo "DRY RUN: running publish with --dry-run" + npm publish --dry-run +fi +echo "Published to npm." + +if [[ $DRY_RUN != "" ]]; then + echo "All other commands are mutations, and we are doing a dry run." + echo "Terminating." + exit +fi + +echo "Cleaning up release notes..." +rm CHANGELOG.md +touch CHANGELOG.md +git commit -m "[firebase-release] Removed change log and reset repo after ${NEW_VERSION} release" CHANGELOG.md +echo "Cleaned up release notes." + +echo "Pushing to GitHub..." +git push origin master --tags +echo "Pushed to GitHub." + +echo "Publishing release notes..." +hub release create --file "${RELEASE_NOTES_FILE}" "v${NEW_VERSION}" +echo "Published release notes." + +echo "Making the tweet..." +npm install --no-save twitter@1.7.1 +cp -v "${WDIR}/scripts/twitter.json" "${TEMPDIR}/${REPOSITORY_NAME}/scripts/" +node ./scripts/tweet.js ${NEW_VERSION} +echo "Made the tweet." diff --git a/scripts/publish/cloudbuild.yaml b/scripts/publish/cloudbuild.yaml new file mode 100644 index 000000000..d04fec25c --- /dev/null +++ b/scripts/publish/cloudbuild.yaml @@ -0,0 +1,114 @@ +steps: + # Decrypt the SSH key. + - name: 'gcr.io/cloud-builders/gcloud' + args: + [ + 'kms', + 'decrypt', + '--ciphertext-file=deploy_key.enc', + '--plaintext-file=/root/.ssh/id_rsa', + '--location=global', + '--keyring=${_KEY_RING}', + '--key=${_KEY_NAME}', + ] + + # Decrypt the Twitter credentials. + - name: 'gcr.io/cloud-builders/gcloud' + args: + [ + 'kms', + 'decrypt', + '--ciphertext-file=twitter.json.enc', + '--plaintext-file=twitter.json', + '--location=global', + '--keyring=${_KEY_RING}', + '--key=${_KEY_NAME}', + ] + + # Decrypt the npm credentials. + - name: 'gcr.io/cloud-builders/gcloud' + args: + [ + 'kms', + 'decrypt', + '--ciphertext-file=npmrc.enc', + '--plaintext-file=npmrc', + '--location=global', + '--keyring=${_KEY_RING}', + '--key=${_KEY_NAME}', + ] + + # Decrypt the hub (GitHub) credentials. + - name: 'gcr.io/cloud-builders/gcloud' + args: + [ + 'kms', + 'decrypt', + '--ciphertext-file=hub.enc', + '--plaintext-file=hub', + '--location=global', + '--keyring=${_KEY_RING}', + '--key=${_KEY_NAME}', + ] + + # Set up git with key and domain. + - name: 'gcr.io/cloud-builders/git' + entrypoint: 'bash' + args: + - '-c' + - | + chmod 600 /root/.ssh/id_rsa + cat </root/.ssh/config + Hostname github.com + IdentityFile /root/.ssh/id_rsa + EOF + ssh-keyscan github.com >> /root/.ssh/known_hosts + + # Clone the repository. + - name: 'gcr.io/cloud-builders/git' + args: ['clone', 'git@github.com:${_REPOSITORY_ORG}/${_REPOSITORY_NAME}'] + + # Set up the Git configuration. + - name: 'gcr.io/cloud-builders/git' + dir: '${_REPOSITORY_NAME}' + args: ['config', '--global', 'user.email', 'firebase-oss-bot@google.com'] + - name: 'gcr.io/cloud-builders/git' + dir: '${_REPOSITORY_NAME}' + args: ['config', '--global', 'user.name', 'Google Open Source Bot'] + + # Set up the Twitter credentials. + - name: 'gcr.io/$PROJECT_ID/package-builder' + entrypoint: 'cp' + args: ['-v', 'twitter.json', '${_REPOSITORY_NAME}/scripts/twitter.json'] + + # Set up the npm credentials. + - name: 'gcr.io/$PROJECT_ID/package-builder' + entrypoint: 'bash' + args: ['-c', 'cp -v npmrc ~/.npmrc'] + + # Set up the hub credentials for package-builder. + - name: 'gcr.io/$PROJECT_ID/package-builder' + entrypoint: 'bash' + args: ['-c', 'mkdir -vp ~/.config && cp -v hub ~/.config/hub'] + + # Publish the package. + - name: 'gcr.io/$PROJECT_ID/package-builder' + dir: '${_REPOSITORY_NAME}' + args: ['bash', './scripts/publish.sh', '${_VERSION}'] + env: + - 'REPOSITORY_ORG=${_REPOSITORY_ORG}' + - 'REPOSITORY_NAME=${_REPOSITORY_NAME}' + - 'DRY_RUN=${_DRY_RUN}' + +options: + volumes: + - name: 'ssh' + path: /root/.ssh + +substitutions: + _VERSION: '' + _DRY_RUN: '' + _KEY_RING: 'npm-publish-keyring' + _KEY_NAME: 'publish' + _REPOSITORY_ORG: 'firebase' + _REPOSITORY_NAME: 'firebase-functions' diff --git a/scripts/publish/deploy_key.enc b/scripts/publish/deploy_key.enc new file mode 100644 index 0000000000000000000000000000000000000000..127551f08a284b04645c1979c5e7582ff6d87cc2 GIT binary patch literal 3505 zcmV;i4Nme3Bmgb?`&t@CANF6;R;o>u7LKpmI5M&PT05}Z%Fvpli)}500uqQD0I@1* zqHJtSSX}$k?Rd7LKh&;a=vnd$={xB*q> zA&%j))nL-jB=2Pj(Wz1seZ^pKpHctdh-YHXB0r^La?C?~Mf9%}Ccsy^HCF zOb#!mG@W>%81NyFz!emyLQSG!Uddt)!yBRud|uy3``LlrL%;ZxRwax>PWc|VwwAFv z9OMa-Z9qqPEP|7LJu|tL`CKbh#O-KYZ2NW|tK~YPfT})dHaPHK#q>)nGZvxUV3?sg z(y{|s@DGBI4I2S0?@HJA*}p?tQT_02T_90NWwFME7-!aglgC0xKGiF?>tLU`mY$-@ zYl4i49l`mxevU$MQ51F82-l~~^2ci>=1!x1&Gr~KL&r69Z&ci^AJR^Rug*fdq8!F2 zvak|`Mi^klfVOdMIZH`>QPOo@2m^$C6=PwK|1+NC=iMZ}fMqrR;#b{dcw~YygKR_x zJ-NodT=TW8sb1C()1P?fZh6N9F^w|7@#8UifxZqLNsdPq@%;0u^K(Ae;M>JnAL1Sj zw%WcTocg@=05m^ixPm)pc~|YYLG8>kpRiejk7Z7^jnhVJF#h`%a~m>sMTZwih(#a^ zFy6_w{alNDyjwd9!NG@h#_pnsrgt&olf_5f3jkB4JPKk2HlEp%P@REPmRRLoYIQh$ zq(W3tr#&hus>2>|8z`zMxir(_zRWt6o+r(01v4P9bZRLJR`MGQMhHxZ8|L!jY`!~y zup{RtT&1eO^D`$qDX*%l1f8gaozR}|M+W}PSDVT)UkYCPMPMn*VbHK-?JeQO#e72- zi(o|L&s1b;-A8_)o$c#5Dn~m|tc67XXD9p{KCw6KabQCa6n{~4!%!OFGi$RER`}lb zp7wu)GL#sd79Z+Fj4lZ~cBhBAyV$=p)w1BnpIv1Y%+8{j*y>p;TQxlo9XbJ-dO$(z{FE2P6Z4-A2afjbAhPH&+>TXEfKrAxn)J0 zU>>UZyy@&BtJvRa8UOnOKlI9jQ*q3aSOy3OvZL2$dK z%PrOHR3^w^AO}8zlJz&VnII*NcS|L5AUq^QCb72fo!+YJ3!TD2pPk72$j}k+F)30CK5yHBPVm5Qu*Z`iKlVd4I+_+Z< zMjr6TP;kDgh5E+lkVIRzO>Y^L+}uv_WSmd1|*{CSVxYAM8o1ce0Gg3FU^@i zW5?gDOW3=>oI{o&@rcAZj+bwetel^sIqlf5vMInpHVA zA%ag{Tag1RdhuFY;9%^WbI*XZnel2VjPR=Sn@rH@Ap9?ODfff)c?i_YXE$w&i(f0l zwc*&T=X0E6eea)*?yo9@;bM=S!#GUs&lGUOaprtq;M^}kG<>l{3pT%o(f+C?Z!K4% zH%@F=RfezqRr=pJXw(x=puymA-n%mrFKx&ciEu}GE{yK0zAUsX4!h!?(%|D?cqW%D zaI)ZgAOg|&m$RSDQ0&wFmq#m%^6@7iSK#DmB76DU5nR3H6d!XyGF^j^ue7!~nOguMEA=l6h@uAsSL_S$%w`)dABi;=Q)a?I##7)Gx=$0(#Yz^@-2 zP}wb;T_!c>lO!ob%$VdSVSU$KE(~6vxAPJ~j`9I`$z4%%W!$TdVL@;qr7T6S!S@9= zNl;;06mDnka*i}jPE+TDX%%r=SE8CYW|ek`0Cpchp;Hm`>uyH!YNEhvbsze$S2=yp z8s=oVffyvYx+W>-{)@+dI_l-($R5pZWs~mL#^u7B;Gs$WSCc(wg`V#=seLPw6WYZ) zYUIJ1CfeJ^4+LM&!aaDDAP}?wt5w!OrzWfiJ}5Od){VA<#=G~0^9*JV%Ni9__nHB2 zkQ^%e*>!@ADxVe?f&xrC;_zUa`8BmXoJ?k_@?R0us*4s~m{(*lpf+#dO&k<^r~n*< z&<|y!znIMP#nt$PXi^*)<+;YHFSz=^w~JMwY;sL1k%Trl>;-*2C%Shwlv5V*L(W&5 zXZ-|v=@#Ye(O|~3?y{zGxa8oPt62rI5fC+S3*`2r|9LDt78?Z1m~^xYa<;VxzSGTY zM3S4T^Y@)f3!tL)?;FEB8ZkSg`;qQfwe7=xZ2?gJ0LL2xSk5ridpLYex&D;fYiViq zn+v(hY+#sHFYH2Dg$J}Fg1DI-)?be{T)hS+69w!*k@cOHuFI;!Unr)S`lV;iy1w=M zscVhlRjt0FKRnUSLc`2sRIZlBtqdpjTB1dPVsNGpW`c=Uv>%bFjVEppPHOqhH~-l^ zOIyetr4Qa~BW3IlHn(es;M5$k28lm1D#B-7>!g^ROUx7vCO*Z!>9H3KR))r9#Ez05 z9jDV8==JApZeN`V(M^%TyGfccf42us*()JpzY%OO{Vj=OoWK$yyzJA=c;5pG3R_ea z?f^pQ(rx3efxj@J0KHk-YVE@?K^L&uXiu5zcq(}?BZu5pIQ2y!MUx^t-SZJ8oB+s> z*j`FmRguK31g^$7Ln0D6Pp&|@4q0`qy%R)>YbY&31G5EPZ#~3JRBKB^_L1Kj_ f4Ub|Hgeu)7h18+LKzH?pr3a9uFy{Epm(_|36Y1*QWJQfJT0MJ9zwMi%Kd3wn#dG_QbRhG-GIt!<8_$jzFOm|VQ-rR?2l}i2{p^L{_ tc<&{%BIKp@wGJNlQ#WkDi)Ew#{#^Qs@9VYVi;<-_|HPcgwp_ON(Ql0_WTgNA literal 0 HcmV?d00001 diff --git a/scripts/publish/npmrc.enc b/scripts/publish/npmrc.enc new file mode 100644 index 0000000000000000000000000000000000000000..da8ea49bb39b447cf82e476e2fbe5cbea9c73ab2 GIT binary patch literal 185 zcmV;q07m}`Bmgb?`&x`B3vTk#mH`EO%n0vbM?y@JnAJ literal 0 HcmV?d00001 diff --git a/scripts/publish/twitter.json.enc b/scripts/publish/twitter.json.enc new file mode 100644 index 0000000000000000000000000000000000000000..82123a04d0ff081fb0e72b4ef55875715afc4a65 GIT binary patch literal 337 zcmV-X0j~ZEBmgb?`&w^yETddISLST9*Q73S3M0HD5u5QbgL|PqvifMB4-%*X0I@1* zqFru1Q`}3f;Ld){D@!s0@HGRmE3-;7)M2ircF{PyxjTknwRwMPZ9!f~Sxs)bbsh+J zE$e6}?hEB5or0?@>f=G5N#*klB1NbA$$(9y$E;0PSaEG%6=El1c4m^(o=8eGQkwhE zbx=AC2;_=16pQ2uAPJH6pLMi|Bf6za61;86Z|Mun0Z4f%*&wt5QAvta;5 jz>LyJ^e{Dl?7J^g|EMlTm%vy}Hf(R(%iC3z{0i>Dxt*iy literal 0 HcmV?d00001 diff --git a/scripts/tweet.js b/scripts/tweet.js new file mode 100644 index 000000000..be6229574 --- /dev/null +++ b/scripts/tweet.js @@ -0,0 +1,52 @@ +"use strict"; + +const fs = require("fs"); +const Twitter = require("twitter"); + +function printUsage() { + console.error( + ` +Usage: tweet.js + +Credentials must be stored in "twitter.json" in this directory. + +Arguments: + - version: Version of module that was released. e.g. "1.2.3" +` + ); + process.exit(1); +} + +function getUrl(version) { + return `https://github.com/firebase/firebase-functions/releases/tag/v${version}`; +} + +if (process.argv.length !== 3) { + console.error("Missing arguments."); + printUsage(); +} + +const version = process.argv.pop(); +if (!version.match(/^\d+\.\d+\.\d+$/)) { + console.error(`Version "${version}" not a version number.`); + printUsage(); +} + +if (!fs.existsSync(`${__dirname}/twitter.json`)) { + console.error("Missing credentials."); + printUsage(); +} +const creds = require("./twitter.json"); + +const client = new Twitter(creds); + +client.post( + "statuses/update", + { status: `v${version} of @Firebase SDK for Cloud Functions is available. Release notes: ${getUrl(version)}` }, + (err) => { + if (err) { + console.error(err); + process.exit(1); + } + } +); From f41bdaa9bd4adc479015ed9df2102a3c1bafc8bf Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 19 Mar 2020 18:10:16 +0000 Subject: [PATCH 004/529] 3.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 35f0517b7..3fe70ac72 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.3.0", + "version": "3.4.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 76bfc9c7563dc81dc65b9a3d90edbac9c0198ecf Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 19 Mar 2020 18:10:21 +0000 Subject: [PATCH 005/529] [firebase-release] Removed change log and reset repo after 3.4.0 release --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69ffc7120..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +0,0 @@ -**IMPORTANT: Please update to this version of `firebase-functions` if you are using Node.js 10 functions, otherwise your functions will break in production.** - -- Prevents deployment and runtime issues caused by upcoming changes to Cloud Functions infrastructure for Node.js 10 functions. (Issue #630) -- Adds support for writing scheduled functions under handler namespace. From bf52fa3b85ef4210e04074afcd401c943879cdac Mon Sep 17 00:00:00 2001 From: efi shtain Date: Thu, 19 Mar 2020 22:50:58 +0200 Subject: [PATCH 006/529] add support for maxInstances in RuntimeOptions (#624) Co-authored-by: joehan --- src/cloud-functions.ts | 4 ++++ src/function-configuration.ts | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 1309aba61..ec2d64779 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -486,5 +486,9 @@ export function optionsToTrigger(options: DeploymentOptions) { if (options.schedule) { trigger.schedule = options.schedule; } + + if (options.maxInstances) { + trigger.maxInstances = options.maxInstances; + } return trigger; } diff --git a/src/function-configuration.ts b/src/function-configuration.ts index ec1695ccd..215ea81a8 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -61,6 +61,11 @@ export interface RuntimeOptions { * Timeout for the function in seconds, possible values are 0 to 540. */ timeoutSeconds?: number; + + /** + * Max number of actual instances allowed to be running in parallel + */ + maxInstances?: number; } export interface DeploymentOptions extends RuntimeOptions { From 9e05b7f4cf663e104c059e56b33ee0174d8dc086 Mon Sep 17 00:00:00 2001 From: joehan Date: Thu, 19 Mar 2020 15:14:55 -0700 Subject: [PATCH 007/529] Add entry for maxInstances (#636) * add entry for maxInstances * prettier --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..166198e9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Adds support for maxInstances. From df543dc04d14441ce65e82d342f1377f859ca758 Mon Sep 17 00:00:00 2001 From: Lauren Long Date: Fri, 20 Mar 2020 10:49:45 -0700 Subject: [PATCH 008/529] Update dependencies to fix TS build issue (#638) --- CHANGELOG.md | 12 +++++++++++- package.json | 8 ++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 166198e9a..c459e5afc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,11 @@ -- Adds support for maxInstances. +- Adds support for defining max number of instances for a function. Example: + +``` +functions.runWith({ + maxInstances: 10 +}).https.onRequest(...); +``` + +Learn more about max instances in the [Google Cloud documentation.](https://cloud.google.com/functions/docs/max-instances) + +- Fixes TypeScript build error when `package-lock.json` is present by updating dependencies (Issue #637). diff --git a/package.json b/package.json index 3fe70ac72..20f7c8c38 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "test": "mocha" }, "dependencies": { - "@types/express": "^4.17.0", + "@types/express": "^4.17.3", "cors": "^2.8.5", "express": "^4.17.1", "jsonwebtoken": "^8.5.1", @@ -61,7 +61,7 @@ "firebase-admin": "^8.2.0", "istanbul": "^0.4.5", "js-yaml": "^3.13.1", - "jsdom": "^15.2.0", + "jsdom": "^16.2.1", "mocha": "^6.1.4", "mock-require": "^3.0.3", "mz": "^2.7.0", @@ -73,9 +73,9 @@ "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.17.1", "typescript": "^3.5.2", - "yargs": "^13.2.4" + "yargs": "^15.3.1" }, "peerDependencies": { "firebase-admin": "^8.0.0" From 15bf0da3b489c4a31f97bc171aa9b2ef251c4abd Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Fri, 20 Mar 2020 11:16:18 -0700 Subject: [PATCH 009/529] Update CHANGELOG (#639) --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c459e5afc..5b7b4bfe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,11 @@ - Adds support for defining max number of instances for a function. Example: -``` -functions.runWith({ - maxInstances: 10 -}).https.onRequest(...); -``` + ``` + functions.runWith({ + maxInstances: 10 + }).https.onRequest(...); + ``` -Learn more about max instances in the [Google Cloud documentation.](https://cloud.google.com/functions/docs/max-instances) + Learn more about max instances in the [Google Cloud documentation.](https://cloud.google.com/functions/docs/max-instances) - Fixes TypeScript build error when `package-lock.json` is present by updating dependencies (Issue #637). From 1ed73456250333274653f874246433f3df89e7d4 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 20 Mar 2020 18:18:21 +0000 Subject: [PATCH 010/529] 3.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 20f7c8c38..0884f7af2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.4.0", + "version": "3.5.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 0921c78ea48c4aee260fa687b8e9c96baa916120 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 20 Mar 2020 18:18:26 +0000 Subject: [PATCH 011/529] [firebase-release] Removed change log and reset repo after 3.5.0 release --- CHANGELOG.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b7b4bfe2..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +0,0 @@ -- Adds support for defining max number of instances for a function. Example: - - ``` - functions.runWith({ - maxInstances: 10 - }).https.onRequest(...); - ``` - - Learn more about max instances in the [Google Cloud documentation.](https://cloud.google.com/functions/docs/max-instances) - -- Fixes TypeScript build error when `package-lock.json` is present by updating dependencies (Issue #637). From 5250110912dbe9e5ff00debe542e33e30089f1a8 Mon Sep 17 00:00:00 2001 From: Martin H Date: Mon, 23 Mar 2020 21:32:01 +0100 Subject: [PATCH 012/529] Add support for europe-west3 region. (#627) --- spec/function-builder.spec.ts | 2 ++ src/function-configuration.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts index b0c4d4c28..0044e5661 100644 --- a/spec/function-builder.spec.ts +++ b/spec/function-builder.spec.ts @@ -59,6 +59,7 @@ describe('FunctionBuilder', () => { 'us-east4', 'europe-west1', 'europe-west2', + 'europe-west3', 'asia-east2', 'asia-northeast1' ) @@ -71,6 +72,7 @@ describe('FunctionBuilder', () => { 'us-east4', 'europe-west1', 'europe-west2', + 'europe-west3', 'asia-east2', 'asia-northeast1', ]); diff --git a/src/function-configuration.ts b/src/function-configuration.ts index 215ea81a8..05d4517a8 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -7,6 +7,7 @@ export const SUPPORTED_REGIONS = [ 'us-east4', 'europe-west1', 'europe-west2', + 'europe-west3', 'asia-east2', 'asia-northeast1', ] as const; From 468455d6e6b9c7712e0615f667592ff207a6df69 Mon Sep 17 00:00:00 2001 From: egilmorez Date: Wed, 25 Mar 2020 16:16:33 -0700 Subject: [PATCH 013/529] Updating docs TOC with Testlab paths. (#643) --- docgen/content-sources/toc.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docgen/content-sources/toc.yaml b/docgen/content-sources/toc.yaml index 378af2eef..7b0b70347 100644 --- a/docgen/content-sources/toc.yaml +++ b/docgen/content-sources/toc.yaml @@ -116,3 +116,15 @@ toc: path: /docs/reference/functions/providers_storage_.objectbuilder.html - title: 'ObjectMetadata' path: /docs/reference/functions/providers_storage_.objectmetadata.html + + - title: 'functions.testLab' + path: /docs/reference/functions/providers_testlab_.html + section: + - title: 'testLab.clientInfo' + path: /docs/reference/functions/providers_testlab_.clientinfo.html + - title: 'testLab.resultStorage' + path: /docs/reference/functions/providers_testlab_.resultstorage.html + - title: 'testLab.testMatrix' + path: /docs/reference/functions/providers_testlab_.testmatrix.html + - title: 'testLab.testMatrixBuilder' + path: /docs/reference/functions/providers_testlab_.testmatrixbuilder.html From e1df8236604c37cee7486209c08f0eb9c291ab77 Mon Sep 17 00:00:00 2001 From: egilmorez Date: Tue, 31 Mar 2020 14:23:10 -0700 Subject: [PATCH 014/529] Adding testlab event to eventTypes list (#649) --- src/cloud-functions.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index ec2d64779..ea4acd7a2 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -104,11 +104,12 @@ export interface EventContext { * * `providers/cloud.firestore/eventTypes/document.update` * * `providers/cloud.firestore/eventTypes/document.delete` * * `google.pubsub.topic.publish` + * * `google.firebase.remoteconfig.update` * * `google.storage.object.finalize` * * `google.storage.object.archive` * * `google.storage.object.delete` * * `google.storage.object.metadataUpdate` - * * `google.firebase.remoteconfig.update` + * * `google.testing.testMatrix.complete` */ eventType: string; From 7f4c9572cb96881f9b0be7521799bcf6e73e42a1 Mon Sep 17 00:00:00 2001 From: Sebastian Kreft Date: Tue, 31 Mar 2020 18:29:20 -0300 Subject: [PATCH 015/529] Enable users to define async HTTP functions (#651) Fixes #606 --- src/providers/https.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/https.ts b/src/providers/https.ts index d941f67c8..36077affd 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -60,7 +60,7 @@ export function _onRequestWithOptions( ): HttpsFunction { // lets us add __trigger without altering handler: const cloudFunction: any = (req: Request, res: express.Response) => { - handler(req, res); + return handler(req, res); }; cloudFunction.__trigger = _.assign(optionsToTrigger(options), { httpsTrigger: {}, From 95d4a4a5b3850ddd69b14284f6823ec7cd66c36a Mon Sep 17 00:00:00 2001 From: Lauren Long Date: Tue, 31 Mar 2020 15:56:48 -0700 Subject: [PATCH 016/529] Update CHANGELOG.md (#640) --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..9b9881949 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,2 @@ +- Adds support for europe-west3 region (e.g. `functions.region("europe-west3")`). +- Adds support for async HTTP functions (Issue #606). From c9a3a0e06545171f3cc83361526773de44166a95 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Tue, 31 Mar 2020 23:02:56 +0000 Subject: [PATCH 017/529] 3.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0884f7af2..3fab11e33 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.5.0", + "version": "3.6.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From d3e8951e151102e2b1ccbee7e826a31c014712ed Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Tue, 31 Mar 2020 23:03:02 +0000 Subject: [PATCH 018/529] [firebase-release] Removed change log and reset repo after 3.6.0 release --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b9881949..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +0,0 @@ -- Adds support for europe-west3 region (e.g. `functions.region("europe-west3")`). -- Adds support for async HTTP functions (Issue #606). From 2784d5dad6d48215e024aae4678a99a317f6e3dd Mon Sep 17 00:00:00 2001 From: Lauren Long Date: Fri, 24 Apr 2020 10:20:17 -0700 Subject: [PATCH 019/529] Update TypeScript dependency to v3.8 to fix build issues (Issue #667) (#668) --- CHANGELOG.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..e5a92e2f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Update TypeScript dependency to v.3.8 to fix build issues (Issue #667) diff --git a/package.json b/package.json index 3fab11e33..13c1e10c2 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "tslint-no-unused-expression-chai": "^0.1.4", "tslint-plugin-prettier": "^2.0.1", "typedoc": "^0.17.1", - "typescript": "^3.5.2", + "typescript": "^3.8.3", "yargs": "^15.3.1" }, "peerDependencies": { From 1fb57c58c15b3af9b14e6a0dab6678ebde35aa6b Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 24 Apr 2020 17:29:31 +0000 Subject: [PATCH 020/529] 3.6.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 13c1e10c2..2f45c7dc7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.6.0", + "version": "3.6.1", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 1dde3dbc9a7c2d7caae945e43cb11b602c968331 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 24 Apr 2020 17:29:37 +0000 Subject: [PATCH 021/529] [firebase-release] Removed change log and reset repo after 3.6.1 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5a92e2f7..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Update TypeScript dependency to v.3.8 to fix build issues (Issue #667) From df35c1b4b18b7f2cd6c2d0d21090793b3b153e8b Mon Sep 17 00:00:00 2001 From: Sebastian Kreft Date: Mon, 27 Apr 2020 17:48:07 -0400 Subject: [PATCH 022/529] fix: onCreate, onUpdate and onDelete receive a DocumentQuerySnapshopt (#670) The arguments of Firestore triggers `onCreate`, `onUpdate` and `onDelete` cannot have an undefined data by definition, so we changed the arguments of those handlers to reflect that by using DocumentQuerySnapshot. fixes #659 --- src/providers/firestore.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/providers/firestore.ts b/src/providers/firestore.ts index 291d7d7f9..f91e17306 100644 --- a/src/providers/firestore.ts +++ b/src/providers/firestore.ts @@ -42,6 +42,7 @@ export const service = 'firestore.googleapis.com'; export const defaultDatabase = '(default)'; let firestoreInstance: any; export type DocumentSnapshot = firebase.firestore.DocumentSnapshot; +export type QueryDocumentSnapshot = firebase.firestore.QueryDocumentSnapshot; /** * Select the Firestore document to listen to for events. @@ -204,30 +205,30 @@ export class DocumentBuilder { /** Respond only to document updates. */ onUpdate( handler: ( - change: Change, + change: Change, context: EventContext ) => PromiseLike | any - ): CloudFunction> { + ): CloudFunction> { return this.onOperation(handler, 'document.update', changeConstructor); } /** Respond only to document creations. */ onCreate( handler: ( - snapshot: DocumentSnapshot, + snapshot: QueryDocumentSnapshot, context: EventContext ) => PromiseLike | any - ): CloudFunction { + ): CloudFunction { return this.onOperation(handler, 'document.create', snapshotConstructor); } /** Respond only to document deletions. */ onDelete( handler: ( - snapshot: DocumentSnapshot, + snapshot: QueryDocumentSnapshot, context: EventContext ) => PromiseLike | any - ): CloudFunction { + ): CloudFunction { return this.onOperation( handler, 'document.delete', From 5c18afeb2d93881c90ab3bc91cab075dcd3b3a85 Mon Sep 17 00:00:00 2001 From: Rich Hodgkins Date: Mon, 27 Apr 2020 22:57:07 +0100 Subject: [PATCH 023/529] Modify return type of DataSnapshot.forEach to `boolean | void` (#666) This is to match the Admin SDK: https://github.com/firebase/firebase-js-sdk/blob/37b98e9271c494a0fb58ca1960f8fcfaec49ade9/packages/database/src/api/DataSnapshot.ts#L137 Co-authored-by: joehan Co-authored-by: Lauren Long --- src/providers/database.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/database.ts b/src/providers/database.ts index e648be20e..78614a152 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -476,7 +476,7 @@ export class DataSnapshot { * @return `true` if enumeration was canceled due to your callback * returning `true`. */ - forEach(action: (a: DataSnapshot) => boolean): boolean { + forEach(action: (a: DataSnapshot) => boolean | void): boolean { const val = this.val(); if (_.isPlainObject(val)) { return _.some( From b1f9b5ad3d7227132cff82542c6222b6b5edcea0 Mon Sep 17 00:00:00 2001 From: Lauren Long Date: Fri, 1 May 2020 10:20:04 -0700 Subject: [PATCH 024/529] Revise docs for handler namespace (#680) * Adding top-level comment and TOC for functions.handler. (#633) * Adding top-level comment and TOC for functions.handler. * Adding edits from feedback. * Refer specifically to Firebase CLI. * Adding top-level comment and TOC for functions.handler. (#633) * Adding top-level comment and TOC for functions.handler. * Adding edits from feedback. * Refer specifically to Firebase CLI. * Pin "typedoc" version to 0.14.2 (#655) * Add docstrings for handler.firestore, handler.database, and handler.https (#652) * Document a few providers * Remove extra sentence in http function docstring * Crashlytics handler details and example formatting (#656) * Adding comments for Crashlytics handler. * Removing parens for parameters per feedback. * Eg moar handlers (#657) * Adding RC and Analytics handlers. * Adding Storage object handler. * Adding Pub/Sub and Test Lab handlers. * Adding auth handler. * Fixing typo. * Removing async to be consistent among examples. (#658) * handler functions: copy-edits in comments only (#675) * Removing async syntax from Storage handlers. (#678) Co-authored-by: egilmorez Co-authored-by: rachelsaunders <52258509+rachelsaunders@users.noreply.github.com> --- deploy_key.enc | Bin 0 -> 3497 bytes docgen/content-sources/toc.yaml | 5 + package.json | 2 +- src/handler-builder.ts | 280 ++++++++++++++++++++++++-------- 4 files changed, 215 insertions(+), 72 deletions(-) create mode 100644 deploy_key.enc diff --git a/deploy_key.enc b/deploy_key.enc new file mode 100644 index 0000000000000000000000000000000000000000..4451e042b005ebc9eda67d58b62167f4c49daf6a GIT binary patch literal 3497 zcmV;a4Oa3BBmmVf@Zrdr)Xk)=bRAGj!{RH@SFgr^td|4+9tfBW^Z~L71QLK70H3|R zajlkRxfa@rcX>I9C~NhK{A6SLG=zTUY?Gzdo%dt4TzAs$G$%Eg94vVf6hVeHEUTRE z*3NSh~uGCn|R=X)&pZv zf?JJM(&zim_<}4u;>Hix(K|O{>gBe(_wgXp%R@{hrQ+rV1!o*KY{VK7t2b;&OO%0b zmaYzO>8gq1TJ?ijsfxS#HZp~m;M*N_*G2_GihAVKTbe<7ydAJ8h^=n59%!DAR`SO9 zI@%%3U4cW>uR!+LO^cgy?Wr5k9c0wZa`$%2mq_rksc_jcK#Gg0rzlv|%?n*j^8r=* z=1h@esuK?)h)e?Js&g0w9&cLyrb(2iv8E7NpVAxHa`ULDhM7cnp|+=Smq6(1kbA?h z)<00(wd_RMcR$nxp~F0an9CGc(l({P@_*@(?19xjFA8Ogqe=}f3=KRIYMSWzIjf+mz1Z;z;`Cqg7A5G(f34LOI%u8> zMSWL|1YP0t{PxWvP4EFE8&>1UH$R*G@S4))k_+vJ3L?a-poZq&k1%3(_iDGzPaC&` zKK&yBq`IUt!!f8nKY6|X^k43jD@_TTn%fEZA?-JMa3YF$H9GE_t5FTJlu-p$xaIX_ zO#I&Yw9uDWnpp2TOFR=GTal0yJmPiQ56t=aGbJfMlpU)V(byVZv~6;_(P^0w7(4u} z6h6tr0d9i?^s8jb6Dd);+od;stGFyV4mA}EW5zNh{`njzaZZ)|jl8Ajwdo(Qv4FG1 zcnz52kZD+HR~zpox)Hr$2AJXf7mBk*&YBUN`hO0E|rGF4v#)`yhU0*o5E|UTop&t;F|cB-mt_q zI_a%0>Z>Z;+8&vzbyX28=;)gK=rb&QZ0I)kEB_TzSZ8{`;*q+#9%Tn&Zn%r*(UDfd z+GN$ahV#aSZluYmq_2#y?lpZWv|-(Bm*1Bt4t@5uQw zolniyAwn|r^qROUaFl4My9AlN_Zx7$V7Rki6=?~u1aj(kw54qpqs(i4@fxA^07=jk z^brWB_a~^XTj2E!g&bR)we}HwiKv3QhaWr6Gn%jy!b$a{bNd{TKG(Rk6|6-0r>HAm zrZlXuf)$7`%3B~i$VN;M>|~dn`=H1w5I+@!Ov46I8R-#`&5Z>poMVE9?|Ev8S0RGd z=22d>MEY>&f~^<3V6FT#y_UXD0*c9t*u=hx$o?GoIgcnnM$cdH))S?ai zrO&XTnsn?~=A7?WEnw|oPpcRYNYgMMr7nzn5!==*vK-1gu&{e+&!H4DqHd&g$N9F}{(}T_X zvd#2Q6K{eY9gM%E&=vA?ys*1Ecb{K@%&G>=W}lGbjI2rA!(~pPCKE_w zV9W{U&WNX%s!Y85W-`P~q3QhJlXkma!!ap@goj79Aj1sQZ6G>>q z&uRQNoXZ;g2HVCIy>!Wg$BK|;5lnw1b2N&#Y{^+zA$&%Vm2su`Y}^i%9x1MIH%Q0L zR&*8olK>jMSc~pFNm%Z8ks|e@O-qUwW)hocP1PFN9MadNy&w@?^`W8>Ew7+QljLeJ zsc^5%Wik4LycK}31v7!^PAXN6JpIj!_le_{qK1TxXRPP)01O!NM=O2lCrCDQTTKuC zFzsX!U;gS9k;hb!avA32c<6idpD3X z@B!9=11BW7g@_2as9@|=T{<2}#HZe#f)nE&;_ZB;$bm4WF)#95kBCA3^)t04 z;BONYb#RcC5)3Zvjs@<5sum)Yva+mV)8ha{~fjjduoPowBXJyfc^g=u#;Fm!@XL?P|0d= z-*9rNZ5|D6m_3N?LktGD0^&LxVZ#1!QH3_1AIO_Q80GGSD2nbPImcJ6qEO5bW6>05jnYPFF79=9tO}g)WU$Z~IUt-| z`Dq201@Xr=S~#DeHK%`Aj}Sru5U4ox=$8a@wA_qS4>(a#+KxYG zSmwf2+K3EvWR#+X$qXg9*=MXnse)npW!mTIza@5!9sZ(bHo+Syi~~-rYhBB)BM*>d zp^P`{TNB}XUSnS@u|?t&H61_vtnv@lMSU$uV*#QUT#K9p_AW77-?6rAN~K$f;+(DM z?k?H)S2hNDJ5yC8+gZHk^iB%>R_jFwG`QlvZ0%Lt0HLR2INg*+D+L;M7L)-!^RN6` zWJ`5Ok=RO3ie$X(J52NZGB_;WdyZu2F~$zTs%9B(ECuiK z$w2MYk-bEC&pv=xF`h8Lt3ZKQuzwqsGf8fkB;DmLNVx2h4?lFfg$9C|%9##GZ-j4WaYIL$( z^tuu2Vy@cd#f_B+yg&0mpor5N*na)M;C2hta0t=Eu>^K>6ICTb=)R~a20LWz+l%_s z)cp&MypEH3kaMsW(1gzvAAGBdT1>66{zBWsESk3qV$k>h>jjA1ir3m;f!t+_p8e{( z6aTXDX%5B`=kKo$Nzmo3)JzCm2zR+#X_I_20RGp5W&H|Ywk_aV5jxQ58Ny= z_6#3#5}8uYJ{WAIx)XK|s!CR@yM`mZk8_2IsZCBST&kWrMt?W$BSg8vF*Qp1ao7cO<(Jj!2v1 zi8dHEQhkN;x2t&`LMK#rDr43TXg<+IeF-PqQXNVEUgR_k0my%5l-t-JsPeQtD*zL` z7*5tEMlMy`EyyDB$(A)!6K+RnohyHU { ... }) + * ``` + * + * `onCall` declares a callable function for clients to call using a Firebase SDK. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.https.onCall((data, context) => { ... }) + * ``` + */ get https() { return { - /** - * Handle HTTP requests. - * @param handler A function that takes a request and response object, - * same signature as an Express app. - */ onRequest: ( handler: (req: express.Request, resp: express.Response) => void ): HttpsFunction => { @@ -52,10 +73,6 @@ export class HandlerBuilder { func.__trigger = {}; return func; }, - /** - * Declares a callable method for clients to call using a Firebase SDK. - * @param handler A method that takes a data and context and returns a value. - */ onCall: ( handler: ( data: any, @@ -69,12 +86,40 @@ export class HandlerBuilder { }; } + /** + * Create a handler for Firebase Realtime Database events. + * + * `ref.onCreate` handles the creation of new data. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.database.ref.onCreate((snap, context) => { ... }) + * ``` + * + * `ref.onUpdate` handles updates to existing data. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.database.ref.onUpdate((change, context) => { ... }) + * ``` + + * `ref.onDelete` handles the deletion of existing data. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.database.ref.onDelete((snap, context) => { ... }) + * ``` + + * `ref.onWrite` handles the creation, update, or deletion of data. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.database.ref.onWrite((change, context) => { ... }) + * ``` + */ get database() { return { - /** - * Selects a database instance that will trigger the function. - * If omitted, will pick the default database for your project. - */ + /** @hidden */ get instance() { return { get ref() { @@ -82,41 +127,47 @@ export class HandlerBuilder { }, }; }, - - /** - * Select Firebase Realtime Database Reference to listen to. - * - * This method behaves very similarly to the method of the same name in the - * client and Admin Firebase SDKs. Any change to the Database that affects the - * data at or below the provided `path` will fire an event in Cloud Functions. - * - * There are three important differences between listening to a Realtime - * Database event in Cloud Functions and using the Realtime Database in the - * client and Admin SDKs: - * 1. Cloud Functions allows wildcards in the `path` name. Any `path` component - * in curly brackets (`{}`) is a wildcard that matches all strings. The value - * that matched a certain invocation of a Cloud Function is returned as part - * of the `context.params` object. For example, `ref("messages/{messageId}")` - * matches changes at `/messages/message1` or `/messages/message2`, resulting - * in `context.params.messageId` being set to `"message1"` or `"message2"`, - * respectively. - * 2. Cloud Functions do not fire an event for data that already existed before - * the Cloud Function was deployed. - * 3. Cloud Function events have access to more information, including information - * about the user who triggered the Cloud Function. - */ get ref() { return new database.RefBuilder(apps(), () => null, {}); }, }; } + /** + * Create a handler for Cloud Firestore events. + * + * `document.onCreate` handles the creation of new documents. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.firestore.document.onCreate((snap, context) => { ... }) + * ``` + + * `document.onUpdate` handles updates to existing documents. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.firestore.document.onUpdate((change, context) => { ... }) + * ``` + + * `document.onDelete` handles the deletion of existing documents. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.firestore.document.onDelete((snap, context) => + * { ... }) + * ``` + + * `document.onWrite` handles the creation, update, or deletion of documents. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.firestore.document.onWrite((change, context) => + * { ... }) + * ``` + */ get firestore() { return { - /** - * Listen for events on a Firestore document. A Firestore document contains a set of - * key-value pairs and may contain subcollections and nested objects. - */ get document() { return new firestore.DocumentBuilder(() => null, {}); }, @@ -131,26 +182,53 @@ 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 { - /** - * Handle events related to Crashlytics issues. An issue in Crashlytics is an - * aggregation of crashes which have a shared root cause. - */ get issue() { return new crashlytics.IssueBuilder(() => null, {}); }, }; } + /** + * Create a handler for Firebase Remote Config events. + + * `remoteConfig.onUpdate` handles events that update a Remote Config template. + + * @example + * ```javascript + * exports.myFunction = functions.handler.remoteConfig.onUpdate() => { ... }) + * ``` + */ get remoteConfig() { return { - /** - * Handle all updates (including rollbacks) that affect a Remote Config - * project. - * @param handler A function that takes the updated Remote Config template - * version metadata as an argument. - */ onUpdate: ( handler: ( version: remoteConfig.TemplateVersion, @@ -162,67 +240,127 @@ export class HandlerBuilder { }; } + /** + * Create a handler for Google Analytics events. + + * `event.onLog` handles the logging of Analytics conversion events. + + * @example + * ```javascript + * exports.myFunction = functions.handler.analytics.event.onLog((event) => { ... }) + * ``` + */ get analytics() { return { - /** - * Select analytics events to listen to for events. - */ get event() { return new analytics.AnalyticsEventBuilder(() => null, {}); }, }; } + /** + * Create a handler for Cloud Storage for Firebase events. + * + * `object.onArchive` handles the archiving of Storage objects. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.storage.object.onArchive((object) => { ... }) + * ``` + + * `object.onDelete` handles the deletion of Storage objects. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.storage.object.onDelete((object) => { ... }) + * ``` + + * `object.onFinalize` handles the creation of Storage objects. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.storage.object.onFinalize((object) => + * { ... }) + * ``` + + * `object.onMetadataUpdate` handles changes to the metadata of existing Storage objects. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.storage.object.onMetadataUpdate((object) => + * { ... }) + * ``` + */ get storage() { return { - /** - * The optional bucket function allows you to choose which buckets' events to handle. - * This step can be bypassed by calling object() directly, which will use the default - * Cloud Storage for Firebase bucket. - */ get bucket() { return new storage.BucketBuilder(() => null, {}).object(); }, - /** - * Handle events related to Cloud Storage objects. - */ get object() { return new storage.ObjectBuilder(() => null, {}); }, }; } + /** + * Create a handler for Cloud Pub/Sub events. + + * `pubsub.onPublish` handles the publication of messages to a topic. + + * @example + * ```javascript + * exports.myFunction = functions.handler.pubsub.topic.onPublish((message) => { ... }) + * ``` + */ get pubsub() { return { - /** - * Select Cloud Pub/Sub topic to listen to. - */ get topic() { return new pubsub.TopicBuilder(() => null, {}); }, - /** - * Handle periodic events triggered by Cloud Scheduler. - */ get schedule() { return new pubsub.ScheduleBuilder(() => null, {}); }, }; } + /** + * Create a handler for Firebase Authentication events. + * + * `user.onCreate` handles the creation of users. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.auth.user.onCreate((user) => { ... }) + * ``` + + * `user.onDelete` handles the deletion of users. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.auth.user.onDelete((user => { ... }) + * ``` + + */ get auth() { return { - /** - * Handle events related to Firebase authentication users. - */ get user() { return new auth.UserBuilder(() => null, {}); }, }; } + /** + * Create a handler for Firebase Test Lab events. + + * `testMatrix.onComplete` handles the completion of a test matrix. + + * @example + * ```javascript + * exports.myFunction = functions.handler.testLab.testMatrix.onComplete((testMatrix) => { ... }) + * ``` + */ get testLab() { - /** Handle events related to Test Lab test matrices. */ return { get testMatrix() { return new testLab.TestMatrixBuilder(() => null, {}); From 1bd2736befdc0de87c0c00e36fe0a81b50d5c5b5 Mon Sep 17 00:00:00 2001 From: egilmorez Date: Wed, 27 May 2020 12:29:29 -0700 Subject: [PATCH 025/529] Fixes to reference doc generation for functions.https (#690) --- docgen/content-sources/HOME.md | 7 ++++++- docgen/content-sources/toc.yaml | 2 ++ src/providers/https.ts | 14 ++++++++++++-- src/setup.ts | 1 + src/utilities/assertions.ts | 2 +- src/utilities/path.ts | 2 +- src/utils.ts | 1 + 7 files changed, 24 insertions(+), 5 deletions(-) diff --git a/docgen/content-sources/HOME.md b/docgen/content-sources/HOME.md index c89df0a57..00d2090a0 100644 --- a/docgen/content-sources/HOME.md +++ b/docgen/content-sources/HOME.md @@ -1,3 +1,8 @@ # Firebase Functions SDK Reference -Functions SDK!!! +The `firebase-functions` package provides an SDK for defining Cloud Functions for Firebase. + +To get started using Cloud Functions, see +[Get started: write, test, and deploy your first functions](/docs/functions/get-started). + +For source code, see the [Cloud Functions for Firebase GitHub repo](https://github.com/firebase/firebase-functions). diff --git a/docgen/content-sources/toc.yaml b/docgen/content-sources/toc.yaml index 856b12640..91544d4f6 100644 --- a/docgen/content-sources/toc.yaml +++ b/docgen/content-sources/toc.yaml @@ -90,6 +90,8 @@ toc: section: - title: 'HttpsError' path: /docs/reference/functions/providers_https_.httpserror.html + - title: 'CallableContext' + path: /docs/reference/functions/providers_https_.callablecontext.html - title: 'functions.pubsub' path: /docs/reference/functions/providers_pubsub_.html diff --git a/src/providers/https.ts b/src/providers/https.ts index 36077affd..605c633f0 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -28,6 +28,7 @@ import { apps } from '../apps'; import { HttpsFunction, optionsToTrigger, Runnable } from '../cloud-functions'; import { DeploymentOptions } from '../function-configuration'; +/** @hidden */ export interface Request extends express.Request { rawBody: Buffer; } @@ -127,6 +128,7 @@ export type FunctionsErrorCode = | 'data-loss' | 'unauthenticated'; +/** @hidden */ export type CanonicalErrorCodeName = | 'OK' | 'CANCELLED' @@ -146,6 +148,7 @@ export type CanonicalErrorCodeName = | 'UNAVAILABLE' | 'DATA_LOSS'; +/** @hidden */ interface HttpErrorCode { canonicalName: CanonicalErrorCodeName; status: number; @@ -180,6 +183,7 @@ const errorCodeMap: { [name in FunctionsErrorCode]: HttpErrorCode } = { 'data-loss': { canonicalName: 'DATA_LOSS', status: 500 }, }; +/** @hidden */ interface HttpErrorWireFormat { details?: unknown; message: string; @@ -261,19 +265,22 @@ export interface CallableContext { rawRequest: Request; } -// The allowed interface for an http request for a callable function. +// The allowed interface for an HTTP request to a Callable function. +/** @hidden*/ interface HttpRequest extends Request { body: { data: any; }; } -// The format for the http body response to a callable function. +/** @hidden */ +// The format for an HTTP body response from a Callable function. interface HttpResponseBody { result?: any; error?: HttpsError; } +/** @hidden */ // Returns true if req is a properly formatted callable request. function isValidRequest(req: Request): req is HttpRequest { // The body must not be empty. @@ -317,7 +324,9 @@ function isValidRequest(req: Request): req is HttpRequest { return true; } +/** @hidden */ const LONG_TYPE = 'type.googleapis.com/google.protobuf.Int64Value'; +/** @hidden */ const UNSIGNED_LONG_TYPE = 'type.googleapis.com/google.protobuf.UInt64Value'; /** @@ -400,6 +409,7 @@ export function decode(data: any): any { return data; } +/** @hidden */ const corsHandler = cors({ origin: true, methods: 'POST' }); /** @hidden */ diff --git a/src/setup.ts b/src/setup.ts index 6a0796348..b30fa6226 100644 --- a/src/setup.ts +++ b/src/setup.ts @@ -20,6 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +/** @hidden */ import { firebaseConfig } from './config'; // Set up for config and vars diff --git a/src/utilities/assertions.ts b/src/utilities/assertions.ts index 49ff5e36d..982ca304d 100644 --- a/src/utilities/assertions.ts +++ b/src/utilities/assertions.ts @@ -1,4 +1,4 @@ -/** +/** @hidden * @file Provides common assertion helpers which can be used to improve * strictness of both type checking and runtime. */ diff --git a/src/utilities/path.ts b/src/utilities/path.ts index 40ab8f047..33e187f9e 100644 --- a/src/utilities/path.ts +++ b/src/utilities/path.ts @@ -1,4 +1,4 @@ -/** +/** @hidden * Removes leading and trailing slashes from a path. * * @param path A path to normalize, in POSIX format. diff --git a/src/utils.ts b/src/utils.ts index b52c1e54f..e41b1e1fb 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -20,6 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +/** @hidden */ import * as _ from 'lodash'; export function applyChange(src: any, dest: any) { From 8d0a6c230984a058b798a3b58201930b87b06786 Mon Sep 17 00:00:00 2001 From: Adam Montgomery <1934358+montasaurus@users.noreply.github.com> Date: Wed, 27 May 2020 15:10:18 -0500 Subject: [PATCH 026/529] pin @types/express version (#686) --- CHANGELOG.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..b39bddd01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Pin `@types/express` version to 4.17.3 to fix type definition issue (#685) diff --git a/package.json b/package.json index ed26f220b..27bba5b72 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "test": "mocha" }, "dependencies": { - "@types/express": "^4.17.3", + "@types/express": "4.17.3", "cors": "^2.8.5", "express": "^4.17.1", "jsonwebtoken": "^8.5.1", From 9431102c8cd47432c091d873ef08aa45138f7f27 Mon Sep 17 00:00:00 2001 From: Sebastian Kreft Date: Wed, 27 May 2020 16:11:14 -0400 Subject: [PATCH 027/529] fix: move jsonwebtoken to dev dependencies (#677) jsonwebtoken was only used in tests and not present in the release output. ``` [0] firebase-functions$ ag jsonwebtoken spec/fixtures/mockrequest.ts 1:import * as jwt from 'jsonwebtoken'; package.json 50: "@types/jsonwebtoken": "^8.3.2", 64: "jsonwebtoken": "^8.5.1", [0] firebase-functions$ grep jsonwebtoken -R lib [1] firebase-functions$ ``` Co-authored-by: Lauren Long --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 27bba5b72..020998793 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,6 @@ "@types/express": "4.17.3", "cors": "^2.8.5", "express": "^4.17.1", - "jsonwebtoken": "^8.5.1", "lodash": "^4.17.14" }, "devDependencies": { @@ -62,6 +61,7 @@ "istanbul": "^0.4.5", "js-yaml": "^3.13.1", "jsdom": "^16.2.1", + "jsonwebtoken": "^8.5.1", "mocha": "^6.1.4", "mock-require": "^3.0.3", "mz": "^2.7.0", From dbf9b5e29d1bc1c31345151ce29c9ea13dc55847 Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Wed, 27 May 2020 13:26:20 -0700 Subject: [PATCH 028/529] Use `domain` in context to override RTDB domain #683 (#235) --- spec/providers/database.spec.ts | 56 +++++++++++++++++++++++---------- src/cloud-functions.ts | 1 + src/providers/database.ts | 34 +++++++++++++------- 3 files changed, 64 insertions(+), 27 deletions(-) diff --git a/spec/providers/database.spec.ts b/spec/providers/database.spec.ts index 25293ac11..b764a7ea0 100644 --- a/spec/providers/database.spec.ts +++ b/spec/providers/database.spec.ts @@ -32,7 +32,7 @@ describe('Database Functions', () => { before(() => { process.env.FIREBASE_CONFIG = JSON.stringify({ - databaseURL: 'https://subdomain.firebaseio.com', + databaseURL: 'https://subdomain.apse.firebasedatabase.app', }); appsNamespace.init(); }); @@ -424,29 +424,51 @@ describe('Database Functions', () => { }); }); - describe('resourceToInstanceAndPath', () => { - it('should return the correct instance and path strings', () => { - const [instance, path] = database.resourceToInstanceAndPath( - 'projects/_/instances/foo/refs/bar' + describe('extractInstanceAndPath', () => { + it('should return correct us-central prod instance and path strings if domain is missing', () => { + const [instance, path] = database.extractInstanceAndPath( + 'projects/_/instances/foo/refs/bar', + undefined ); expect(instance).to.equal('https://foo.firebaseio.com'); expect(path).to.equal('/bar'); }); + it('should return the correct staging instance and path strings if domain is present', () => { + const [instance, path] = database.extractInstanceAndPath( + 'projects/_/instances/foo/refs/bar', + 'firebaseio-staging.com' + ); + expect(instance).to.equal('https://foo.firebaseio-staging.com'); + expect(path).to.equal('/bar'); + }); + + it('should return the correct multi-region instance and path strings if domain is present', () => { + const [instance, path] = database.extractInstanceAndPath( + 'projects/_/instances/foo/refs/bar', + 'euw1.firebasedatabase.app' + ); + expect(instance).to.equal('https://foo.euw1.firebasedatabase.app'); + expect(path).to.equal('/bar'); + }); + it('should throw an error if the given instance name contains anything except alphanumerics and dashes', () => { expect(() => { - return database.resourceToInstanceAndPath( - 'projects/_/instances/a.bad.name/refs/bar' + return database.extractInstanceAndPath( + 'projects/_/instances/a.bad.name/refs/bar', + undefined ); }).to.throw(Error); expect(() => { - return database.resourceToInstanceAndPath( - 'projects/_/instances/a_different_bad_name/refs/bar' + return database.extractInstanceAndPath( + 'projects/_/instances/a_different_bad_name/refs/bar', + undefined ); }).to.throw(Error); expect(() => { - return database.resourceToInstanceAndPath( - 'projects/_/instances/BAD!!!!/refs/bar' + return database.extractInstanceAndPath( + 'projects/_/instances/BAD!!!!/refs/bar', + undefined ); }).to.throw(Error); }); @@ -457,8 +479,9 @@ describe('Database Functions', () => { const apps = new appsNamespace.Apps(); const populate = (data: any) => { - const [instance, path] = database.resourceToInstanceAndPath( - 'projects/_/instances/other-subdomain/refs/foo' + const [instance, path] = database.extractInstanceAndPath( + 'projects/_/instances/other-subdomain/refs/foo', + 'firebaseio-staging.com' ); subject = new database.DataSnapshot(data, path, apps.admin, instance); }; @@ -467,7 +490,7 @@ describe('Database Functions', () => { it('should return a ref for correct instance, not the default instance', () => { populate({}); expect(subject.ref.toJSON()).to.equal( - 'https://other-subdomain.firebaseio.com/foo' + 'https://other-subdomain.firebaseio-staging.com/foo' ); }); }); @@ -648,8 +671,9 @@ describe('Database Functions', () => { }); it('should return null for the root', () => { - const [instance, path] = database.resourceToInstanceAndPath( - 'projects/_/instances/foo/refs/' + const [instance, path] = database.extractInstanceAndPath( + 'projects/_/instances/foo/refs/', + undefined ); const snapshot = new database.DataSnapshot( null, diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index ea4acd7a2..f8b1b1c1e 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -39,6 +39,7 @@ export interface Event { timestamp: string; eventType: string; resource: Resource; + domain?: string; }; data: any; } diff --git a/src/providers/database.ts b/src/providers/database.ts index 78614a152..8c8de4ec6 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -40,8 +40,7 @@ export const provider = 'google.firebase.database'; /** @hidden */ export const service = 'firebaseio.com'; -// NOTE(inlined): Should we relax this a bit to allow staging or alternate implementations of our API? -const databaseURLRegex = new RegExp('https://([^.]+).firebaseio.com'); +const databaseURLRegex = new RegExp('^https://([^.]+).'); /** * Registers a function that triggers on events from a specific @@ -215,8 +214,9 @@ export class RefBuilder { ) => PromiseLike | any ): CloudFunction { const dataConstructor = (raw: Event) => { - const [dbInstance, path] = resourceToInstanceAndPath( - raw.context.resource.name + const [dbInstance, path] = extractInstanceAndPath( + raw.context.resource.name, + raw.context.domain ); return new DataSnapshot( raw.data.delta, @@ -243,8 +243,9 @@ export class RefBuilder { ) => PromiseLike | any ): CloudFunction { const dataConstructor = (raw: Event) => { - const [dbInstance, path] = resourceToInstanceAndPath( - raw.context.resource.name + const [dbInstance, path] = extractInstanceAndPath( + raw.context.resource.name, + raw.context.domain ); return new DataSnapshot(raw.data.data, path, this.apps.admin, dbInstance); }; @@ -271,8 +272,9 @@ export class RefBuilder { } private changeConstructor = (raw: Event): Change => { - const [dbInstance, path] = resourceToInstanceAndPath( - raw.context.resource.name + const [dbInstance, path] = extractInstanceAndPath( + raw.context.resource.name, + raw.context.domain ); const before = new DataSnapshot( raw.data.data, @@ -293,9 +295,19 @@ export class RefBuilder { }; } -/* Utility function to extract database reference from resource string */ +/** + * Utility function to extract database reference from resource string + * + * @param optional database domain override for the original of the source database. + * It defaults to `firebaseio.com`. + * Multi-region RTDB will be served from different domains. + * Since region is not part of the resource name, it is provided through context. + */ /** @hidden */ -export function resourceToInstanceAndPath(resource: string) { +export function extractInstanceAndPath( + resource: string, + domain = 'firebaseio.com' +) { const resourceRegex = `projects/([^/]+)/instances/([a-zA-Z0-9\-^/]+)/refs(/.+)?`; const match = resource.match(new RegExp(resourceRegex)); if (!match) { @@ -310,7 +322,7 @@ export function resourceToInstanceAndPath(resource: string) { `Expect project to be '_' in a Firebase Realtime Database event` ); } - const dbInstance = 'https://' + dbInstanceName + '.firebaseio.com'; + const dbInstance = 'https://' + dbInstanceName + '.' + domain; return [dbInstance, path]; } From f907d68e9e3d6500a357fbfd4722e6fc201e1e56 Mon Sep 17 00:00:00 2001 From: Lauren Long Date: Thu, 28 May 2020 10:25:58 -0700 Subject: [PATCH 029/529] Update CHANGELOG.md (#694) --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b39bddd01..ecf68561e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,3 @@ -- Pin `@types/express` version to 4.17.3 to fix type definition issue (#685) +- Pin `@types/express` version to 4.17.3 to fix type definition issue (Issue #685). +- Firestore onCreate, onUpdate, and onDelete now receive a `QueryDocumentSnapshot` instead of `DocumentSnapshot`, which guarantees that data is not undefined (Issue #659). +- Modify return type of `DataSnapshot.forEach` to `boolean | void` match `firebase-admin` SDK. From 4fdf9dbdfc29eeb98c2c46f455ea17f353591dec Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 28 May 2020 17:28:56 +0000 Subject: [PATCH 030/529] 3.6.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 020998793..c0403aafa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.6.1", + "version": "3.6.2", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From bb6f038f76c35dd64f48e8293a7e1b3e464999bb Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 28 May 2020 17:29:01 +0000 Subject: [PATCH 031/529] [firebase-release] Removed change log and reset repo after 3.6.2 release --- CHANGELOG.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecf68561e..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +0,0 @@ -- Pin `@types/express` version to 4.17.3 to fix type definition issue (Issue #685). -- Firestore onCreate, onUpdate, and onDelete now receive a `QueryDocumentSnapshot` instead of `DocumentSnapshot`, which guarantees that data is not undefined (Issue #659). -- Modify return type of `DataSnapshot.forEach` to `boolean | void` match `firebase-admin` SDK. From bb92d38238dda02e376e585b3e80af2e68008e68 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Tue, 2 Jun 2020 09:31:35 -0700 Subject: [PATCH 032/529] Adds structured logging API. (#665) --- CHANGELOG.md | 18 ++++++ spec/logger.spec.ts | 105 ++++++++++++++++++++++++++++++++ src/index.ts | 2 + src/logger.ts | 135 ++++++++++++++++++++++++++++++++++++++++++ src/logger/compat.ts | 8 +++ tsconfig.release.json | 2 +- 6 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 spec/logger.spec.ts create mode 100644 src/logger.ts create mode 100644 src/logger/compat.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..f85fd69b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,18 @@ +- Adds `functions.logger` SDK to enable structured logging in the Node.js 10 runtime. For example: + + ```js + const functions = require('firebase-functions'); + + functions.logger.debug('example log with structured data', { + uid: user.uid, + authorized: true, + }); + ``` + + The logger can also override default behavior of `console.*` methods through a special require: + + ```js + require('firebase-functions/logger/compat'); + ``` + + In older runtimes, logger prints to the console, and no structured data is saved. diff --git a/spec/logger.spec.ts b/spec/logger.spec.ts new file mode 100644 index 000000000..517f50f1d --- /dev/null +++ b/spec/logger.spec.ts @@ -0,0 +1,105 @@ +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import * as logger from '../src/logger'; + +const SUPPORTS_STRUCTURED_LOGS = + parseInt(process.versions?.node?.split('.')?.[0] || '8', 10) >= 10; + +describe(`logger (${ + SUPPORTS_STRUCTURED_LOGS ? 'structured' : 'unstructured' +})`, () => { + let sandbox: sinon.SinonSandbox; + let stdoutStub: sinon.SinonStub; + let stderrStub: sinon.SinonStub; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + stdoutStub = sandbox.stub(process.stdout, 'write'); + stderrStub = sandbox.stub(process.stderr, 'write'); + }); + + function expectOutput(stdStub: sinon.SinonStub, entry: any) { + if (SUPPORTS_STRUCTURED_LOGS) { + return expect( + JSON.parse((stdStub.getCalls()[0].args[0] as string).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); + } + } + + function expectStdout(entry: any) { + return expectOutput(stdoutStub, entry); + } + + function expectStderr(entry: any) { + return expectOutput(stderrStub, 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', () => { + logger.log('hello', 'world', { additional: 'context' }); + expectStdout({ + severity: 'INFO', + message: 'hello world', + additional: 'context', + }); + sandbox.restore(); // to avoid swallowing test runner output + }); + }); + + describe('write', () => { + describe('structured logging', () => { + describe('write', () => { + for (const severity of ['DEBUG', 'INFO', 'NOTICE']) { + it(`should output ${severity} severity to stdout`, () => { + let entry: logger.LogEntry = { + severity: severity as logger.LogSeverity, + message: 'test', + }; + logger.write(entry); + expectStdout(entry); + sandbox.restore(); // to avoid swallowing test runner output + }); + } + + for (const severity of [ + 'WARNING', + 'ERROR', + 'CRITICAL', + 'ALERT', + 'EMERGENCY', + ]) { + it(`should output ${severity} severity to stderr`, () => { + let 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/src/index.ts b/src/index.ts index d4951c512..dcca98172 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,6 +33,7 @@ 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 { setup } from './setup'; @@ -51,6 +52,7 @@ export { remoteConfig, storage, testLab, + logger, }; // Exported root types: diff --git a/src/logger.ts b/src/logger.ts new file mode 100644 index 000000000..7d4c91cbf --- /dev/null +++ b/src/logger.ts @@ -0,0 +1,135 @@ +import { format } from 'util'; + +// safely preserve unpatched console.* methods in case of compat require +const unpatchedConsole = { + debug: console.debug, + info: console.info, + log: console.log, + warn: console.warn, + error: console.error, +}; + +// Determine if structured logs are supported (node >= 10). If something goes wrong, +// assume no since unstructured is safer. +const SUPPORTS_STRUCTURED_LOGS = + parseInt(process.versions?.node?.split('.')?.[0] || '8', 10) >= 10; + +// Map LogSeverity types to their equivalent `console.*` method. +const CONSOLE_SEVERITY: { + [severity: string]: 'debug' | 'info' | 'warn' | 'error'; +} = { + DEBUG: 'debug', + INFO: 'info', + NOTICE: 'info', + WARNING: 'warn', + ERROR: 'error', + CRITICAL: 'error', + ALERT: 'error', + EMERGENCY: 'error', +}; + +/** + * `LogSeverity` indicates the detailed severity of the log entry. See [LogSeverity](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity) for more. + */ +export type LogSeverity = + | 'DEBUG' + | 'INFO' + | 'NOTICE' + | 'WARNING' + | 'ERROR' + | 'CRITICAL' + | 'ALERT' + | 'EMERGENCY'; + +/** + * `LogEntry` represents a structured Cloud Logging entry. All keys aside from `severity` and `message` are + * included in the `jsonPayload` of the logged entry. + */ +export interface LogEntry { + severity: LogSeverity; + message?: string; + [key: string]: any; +} + +/** + * 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) { + unpatchedConsole[CONSOLE_SEVERITY[entry.severity]](JSON.stringify(entry)); + return; + } + + let message = entry.message || ''; + const jsonPayload: { [key: string]: any } = {}; + let jsonKeyCount = 0; + for (const k in entry) { + if (!['severity', 'message'].includes(k)) { + jsonKeyCount++; + jsonPayload[k] = entry[k]; + } + } + if (jsonKeyCount > 0) { + message = `${message} ${JSON.stringify(jsonPayload, null, 2)}`; + } + unpatchedConsole[CONSOLE_SEVERITY[entry.severity]](message); +} + +/** + * Writes a `DEBUG` severity log. If the last argument provided is a plain object, + * it will be added to the `jsonPayload` in the Cloud Logging entry. + * @param args Arguments, concatenated into the log message with space separators. + */ +export function debug(...args: any[]) { + write(entryFromArgs('DEBUG', args)); +} + +/** + * Writes an `INFO` severity log. If the last argument provided is a plain object, + * it will be added to the `jsonPayload` in the Cloud Logging entry. + * @param args Arguments, concatenated into the log message with space separators. + */ +export function log(...args: any[]) { + write(entryFromArgs('INFO', args)); +} + +/** + * Writes an `INFO` severity log. If the last argument provided is a plain object, + * it will be added to the `jsonPayload` in the Cloud Logging entry. + * @param args Arguments, concatenated into the log message with space separators. + */ +export function info(...args: any[]) { + write(entryFromArgs('INFO', args)); +} + +/** + * Writes a `WARNING` severity log. If the last argument provided is a plain object, + * it will be added to the `jsonPayload` in the Cloud Logging entry. + * @param args Arguments, concatenated into the log message with space separators. + */ +export function warn(...args: any[]) { + write(entryFromArgs('WARNING', args)); +} + +/** + * Writes an `ERROR` severity log. If the last argument provided is a plain object, + * it will be added to the `jsonPayload` in the Cloud Logging entry. + * @param args Arguments, concatenated into the log message with space separators. + */ +export function error(...args: any[]) { + write(entryFromArgs('ERROR', args)); +} + +function entryFromArgs(severity: LogSeverity, args: any[]): LogEntry { + let entry = {}; + const lastArg = args[args.length - 1]; + if (typeof lastArg == 'object' && lastArg.constructor == Object) { + entry = args.pop(); + } + return Object.assign({}, entry, { + severity, + // mimic `console.*` behavior, see https://nodejs.org/api/console.html#console_console_log_data_args + message: format.apply(null, args), + }); +} diff --git a/src/logger/compat.ts b/src/logger/compat.ts new file mode 100644 index 000000000..6f6eda03f --- /dev/null +++ b/src/logger/compat.ts @@ -0,0 +1,8 @@ +import { debug, info, warn, error } from '../logger'; + +// IMPORTANT -- "../logger" must be imported before monkeypatching! +console.debug = debug; +console.info = info; +console.log = info; +console.warn = warn; +console.error = error; diff --git a/tsconfig.release.json b/tsconfig.release.json index e93d5d4b6..a226eb290 100644 --- a/tsconfig.release.json +++ b/tsconfig.release.json @@ -10,5 +10,5 @@ "target": "es2017", "typeRoots": ["./node_modules/@types"] }, - "files": ["./src/index.ts"] + "files": ["./src/index.ts", "./src/logger.ts", "./src/logger/compat.ts"] } From e447949d0d6095b130ca5551e3396b45ff5d589a Mon Sep 17 00:00:00 2001 From: Sebastian Kreft Date: Tue, 9 Jun 2020 17:34:43 -0400 Subject: [PATCH 033/529] fix: onRequest type definitions (#696) --- src/cloud-functions.ts | 2 +- src/providers/https.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index f8b1b1c1e..f249bf7f9 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -281,7 +281,7 @@ export interface Runnable { * arguments. */ export type HttpsFunction = TriggerAnnotated & - ((req: Request, resp: Response) => void); + ((req: Request, resp: Response) => void | Promise); /** * The Cloud Function type for all non-HTTPS triggers. This should be exported diff --git a/src/providers/https.ts b/src/providers/https.ts index 605c633f0..cd9f1745d 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -39,7 +39,7 @@ export interface Request extends express.Request { * same signature as an Express app. */ export function onRequest( - handler: (req: Request, resp: express.Response) => void + handler: (req: Request, resp: express.Response) => void | Promise ): HttpsFunction { return _onRequestWithOptions(handler, {}); } @@ -56,7 +56,7 @@ export function onCall( /** @hidden */ export function _onRequestWithOptions( - handler: (req: Request, resp: express.Response) => void, + handler: (req: Request, resp: express.Response) => void | Promise, options: DeploymentOptions ): HttpsFunction { // lets us add __trigger without altering handler: From c26ed9e6bee56f285118975e8afa6680657af5f3 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Tue, 9 Jun 2020 15:10:47 -0700 Subject: [PATCH 034/529] Makes "logger/compat" more focused on compatibility. Fixes #697 (#701) --- CHANGELOG.md | 4 ++-- src/logger.ts | 36 +++++++----------------------------- src/logger/common.ts | 27 +++++++++++++++++++++++++++ src/logger/compat.ts | 30 ++++++++++++++++++++++++------ 4 files changed, 60 insertions(+), 37 deletions(-) create mode 100644 src/logger/common.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index f85fd69b3..1b87ad8b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,10 @@ }); ``` - The logger can also override default behavior of `console.*` methods through a special require: +- Adds a special require that mimics Node.js 8 runtime logging in Node.js 10 and later runtimes: ```js require('firebase-functions/logger/compat'); ``` - In older runtimes, logger prints to the console, and no structured data is saved. + In newer runtimes, requiring this will emit text logs with multi-line support and appropriate severity. In the Node.js 8 runtime, the `compat` module has no effect. diff --git a/src/logger.ts b/src/logger.ts index 7d4c91cbf..258dbbdff 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,32 +1,10 @@ import { format } from 'util'; -// safely preserve unpatched console.* methods in case of compat require -const unpatchedConsole = { - debug: console.debug, - info: console.info, - log: console.log, - warn: console.warn, - error: console.error, -}; - -// Determine if structured logs are supported (node >= 10). If something goes wrong, -// assume no since unstructured is safer. -const SUPPORTS_STRUCTURED_LOGS = - parseInt(process.versions?.node?.split('.')?.[0] || '8', 10) >= 10; - -// Map LogSeverity types to their equivalent `console.*` method. -const CONSOLE_SEVERITY: { - [severity: string]: 'debug' | 'info' | 'warn' | 'error'; -} = { - DEBUG: 'debug', - INFO: 'info', - NOTICE: 'info', - WARNING: 'warn', - ERROR: 'error', - CRITICAL: 'error', - ALERT: 'error', - EMERGENCY: 'error', -}; +import { + SUPPORTS_STRUCTURED_LOGS, + CONSOLE_SEVERITY, + UNPATCHED_CONSOLE, +} from './logger/common'; /** * `LogSeverity` indicates the detailed severity of the log entry. See [LogSeverity](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity) for more. @@ -57,7 +35,7 @@ export interface LogEntry { */ export function write(entry: LogEntry) { if (SUPPORTS_STRUCTURED_LOGS) { - unpatchedConsole[CONSOLE_SEVERITY[entry.severity]](JSON.stringify(entry)); + UNPATCHED_CONSOLE[CONSOLE_SEVERITY[entry.severity]](JSON.stringify(entry)); return; } @@ -73,7 +51,7 @@ export function write(entry: LogEntry) { if (jsonKeyCount > 0) { message = `${message} ${JSON.stringify(jsonPayload, null, 2)}`; } - unpatchedConsole[CONSOLE_SEVERITY[entry.severity]](message); + UNPATCHED_CONSOLE[CONSOLE_SEVERITY[entry.severity]](message); } /** diff --git a/src/logger/common.ts b/src/logger/common.ts new file mode 100644 index 000000000..f8b1f0ed9 --- /dev/null +++ b/src/logger/common.ts @@ -0,0 +1,27 @@ +// Determine if structured logs are supported (node >= 10). If something goes wrong, +// assume no since unstructured is safer. +export const SUPPORTS_STRUCTURED_LOGS = + parseInt(process.versions?.node?.split('.')?.[0] || '8', 10) >= 10; + +// Map LogSeverity types to their equivalent `console.*` method. +export const CONSOLE_SEVERITY: { + [severity: string]: 'debug' | 'info' | 'warn' | 'error'; +} = { + DEBUG: 'debug', + INFO: 'info', + NOTICE: 'info', + WARNING: 'warn', + ERROR: 'error', + CRITICAL: 'error', + ALERT: 'error', + EMERGENCY: 'error', +}; + +// safely preserve unpatched console.* methods in case of compat require +export const UNPATCHED_CONSOLE = { + debug: console.debug, + info: console.info, + log: console.log, + warn: console.warn, + error: console.error, +}; diff --git a/src/logger/compat.ts b/src/logger/compat.ts index 6f6eda03f..48647f5e3 100644 --- a/src/logger/compat.ts +++ b/src/logger/compat.ts @@ -1,8 +1,26 @@ -import { debug, info, warn, error } from '../logger'; +import { + SUPPORTS_STRUCTURED_LOGS, + UNPATCHED_CONSOLE, + CONSOLE_SEVERITY, +} from './common'; +import { format } from 'util'; + +function patchedConsole(severity: string): (data: any, ...args: any[]) => void { + return function(data: any, ...args: any[]): void { + if (SUPPORTS_STRUCTURED_LOGS) { + UNPATCHED_CONSOLE[CONSOLE_SEVERITY[severity]]( + JSON.stringify({ severity, message: format(data, ...args) }) + ); + return; + } + + UNPATCHED_CONSOLE[CONSOLE_SEVERITY[severity]](data, ...args); + }; +} // IMPORTANT -- "../logger" must be imported before monkeypatching! -console.debug = debug; -console.info = info; -console.log = info; -console.warn = warn; -console.error = error; +console.debug = patchedConsole('DEBUG'); +console.info = patchedConsole('INFO'); +console.log = patchedConsole('INFO'); +console.warn = patchedConsole('WARNING'); +console.error = patchedConsole('ERROR'); From 0c684322f91410cdb8d77c97de174f9dbb17a909 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Tue, 9 Jun 2020 16:06:49 -0700 Subject: [PATCH 035/529] Fix compat path in CHANGELOG (#702) --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b87ad8b8..248ab7792 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,9 @@ - Adds a special require that mimics Node.js 8 runtime logging in Node.js 10 and later runtimes: ```js - require('firebase-functions/logger/compat'); + require('firebase-functions/lib/logger/compat'); ``` In newer runtimes, requiring this will emit text logs with multi-line support and appropriate severity. In the Node.js 8 runtime, the `compat` module has no effect. + +- Fixes `https.onRequest` type signature to allow Promises for `async` functions. From 505f357b1c57338e55ae5da16e62e4398d3dc746 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Tue, 9 Jun 2020 23:18:37 +0000 Subject: [PATCH 036/529] 3.7.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c0403aafa..6e198c416 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.6.2", + "version": "3.7.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From c23a8c11299f93f32a59507009f88999851082b6 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Tue, 9 Jun 2020 23:18:44 +0000 Subject: [PATCH 037/529] [firebase-release] Removed change log and reset repo after 3.7.0 release --- CHANGELOG.md | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 248ab7792..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,20 +0,0 @@ -- Adds `functions.logger` SDK to enable structured logging in the Node.js 10 runtime. For example: - - ```js - const functions = require('firebase-functions'); - - functions.logger.debug('example log with structured data', { - uid: user.uid, - authorized: true, - }); - ``` - -- Adds a special require that mimics Node.js 8 runtime logging in Node.js 10 and later runtimes: - - ```js - require('firebase-functions/lib/logger/compat'); - ``` - - In newer runtimes, requiring this will emit text logs with multi-line support and appropriate severity. In the Node.js 8 runtime, the `compat` module has no effect. - -- Fixes `https.onRequest` type signature to allow Promises for `async` functions. From f9b24e03a5b9da08866f990304f6623a4b909be3 Mon Sep 17 00:00:00 2001 From: egilmorez Date: Tue, 23 Jun 2020 12:56:52 -0700 Subject: [PATCH 038/529] Adding logger SDK to reference, with some edits. (#714) * Adding logger SDK to reference, with some edits. * Adding link to structured logging details per feedback. --- docgen/content-sources/toc.yaml | 6 ++++++ src/logger.ts | 18 ++++++++++-------- src/logger/common.ts | 3 +++ src/logger/compat.ts | 1 + 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docgen/content-sources/toc.yaml b/docgen/content-sources/toc.yaml index 91544d4f6..a394975df 100644 --- a/docgen/content-sources/toc.yaml +++ b/docgen/content-sources/toc.yaml @@ -93,6 +93,12 @@ toc: - title: 'CallableContext' path: /docs/reference/functions/providers_https_.callablecontext.html + - title: 'functions.logger' + path: /docs/reference/functions/logger_.html + section: + - title: 'LogEntry' + path: /docs/reference/functions/logger_.logentry.html + - title: 'functions.pubsub' path: /docs/reference/functions/providers_pubsub_.html section: diff --git a/src/logger.ts b/src/logger.ts index 258dbbdff..4bc851cec 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -7,7 +7,7 @@ import { } from './logger/common'; /** - * `LogSeverity` indicates the detailed severity of the log entry. See [LogSeverity](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity) for more. + * `LogSeverity` indicates the detailed severity of the log entry. See [LogSeverity](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity). */ export type LogSeverity = | 'DEBUG' @@ -20,7 +20,8 @@ export type LogSeverity = | 'EMERGENCY'; /** - * `LogEntry` represents a structured Cloud Logging entry. All keys aside from `severity` and `message` are + * `LogEntry` represents a [structured Cloud Logging](https://cloud.google.com/logging/docs/structured-logging) + * entry. All keys aside from `severity` and `message` are * included in the `jsonPayload` of the logged entry. */ export interface LogEntry { @@ -31,7 +32,7 @@ export interface LogEntry { /** * Writes a `LogEntry` to `stdout`/`stderr` (depending on severity). - * @param entry The LogEntry including severity, message, and any additional structured metadata. + * @param entry The `LogEntry` including severity, message, and any additional structured metadata. */ export function write(entry: LogEntry) { if (SUPPORTS_STRUCTURED_LOGS) { @@ -56,7 +57,7 @@ export function write(entry: LogEntry) { /** * Writes a `DEBUG` severity log. If the last argument provided is a plain object, - * it will be added to the `jsonPayload` in the Cloud Logging entry. + * it is added to the `jsonPayload` in the Cloud Logging entry. * @param args Arguments, concatenated into the log message with space separators. */ export function debug(...args: any[]) { @@ -65,7 +66,7 @@ export function debug(...args: any[]) { /** * Writes an `INFO` severity log. If the last argument provided is a plain object, - * it will be added to the `jsonPayload` in the Cloud Logging entry. + * it is added to the `jsonPayload` in the Cloud Logging entry. * @param args Arguments, concatenated into the log message with space separators. */ export function log(...args: any[]) { @@ -74,7 +75,7 @@ export function log(...args: any[]) { /** * Writes an `INFO` severity log. If the last argument provided is a plain object, - * it will be added to the `jsonPayload` in the Cloud Logging entry. + * it is added to the `jsonPayload` in the Cloud Logging entry. * @param args Arguments, concatenated into the log message with space separators. */ export function info(...args: any[]) { @@ -83,7 +84,7 @@ export function info(...args: any[]) { /** * Writes a `WARNING` severity log. If the last argument provided is a plain object, - * it will be added to the `jsonPayload` in the Cloud Logging entry. + * it is added to the `jsonPayload` in the Cloud Logging entry. * @param args Arguments, concatenated into the log message with space separators. */ export function warn(...args: any[]) { @@ -92,13 +93,14 @@ export function warn(...args: any[]) { /** * Writes an `ERROR` severity log. If the last argument provided is a plain object, - * it will be added to the `jsonPayload` in the Cloud Logging entry. + * it is added to the `jsonPayload` in the Cloud Logging entry. * @param args Arguments, concatenated into the log message with space separators. */ export function error(...args: any[]) { write(entryFromArgs('ERROR', args)); } +/** @hidden */ function entryFromArgs(severity: LogSeverity, args: any[]): LogEntry { let entry = {}; const lastArg = args[args.length - 1]; diff --git a/src/logger/common.ts b/src/logger/common.ts index f8b1f0ed9..f7ff0de78 100644 --- a/src/logger/common.ts +++ b/src/logger/common.ts @@ -1,9 +1,11 @@ // Determine if structured logs are supported (node >= 10). If something goes wrong, // assume no since unstructured is safer. +/** @hidden */ export const SUPPORTS_STRUCTURED_LOGS = parseInt(process.versions?.node?.split('.')?.[0] || '8', 10) >= 10; // Map LogSeverity types to their equivalent `console.*` method. +/** @hidden */ export const CONSOLE_SEVERITY: { [severity: string]: 'debug' | 'info' | 'warn' | 'error'; } = { @@ -18,6 +20,7 @@ export const CONSOLE_SEVERITY: { }; // safely preserve unpatched console.* methods in case of compat require +/** @hidden */ export const UNPATCHED_CONSOLE = { debug: console.debug, info: console.info, diff --git a/src/logger/compat.ts b/src/logger/compat.ts index 48647f5e3..a7c27bd16 100644 --- a/src/logger/compat.ts +++ b/src/logger/compat.ts @@ -5,6 +5,7 @@ import { } from './common'; import { format } from 'util'; +/** @hidden */ function patchedConsole(severity: string): (data: any, ...args: any[]) => void { return function(data: any, ...args: any[]): void { if (SUPPORTS_STRUCTURED_LOGS) { From d06bcbee7b147843e3de85363d6dd090767064c9 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Wed, 24 Jun 2020 10:51:39 -0700 Subject: [PATCH 039/529] Handles `null` in structured logging (Fixes #716) (#717) --- spec/logger.spec.ts | 9 +++++++++ src/logger.ts | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/spec/logger.spec.ts b/spec/logger.spec.ts index 517f50f1d..c6859b60a 100644 --- a/spec/logger.spec.ts +++ b/spec/logger.spec.ts @@ -65,6 +65,15 @@ describe(`logger (${ }); sandbox.restore(); // to avoid swallowing test runner output }); + + it('should not recognize null as a structured logging object', () => { + logger.log('hello', 'world', null); + expectStdout({ + severity: 'INFO', + message: 'hello world null', + }); + sandbox.restore(); // to avoid swallowing test runner output + }); }); describe('write', () => { diff --git a/src/logger.ts b/src/logger.ts index 4bc851cec..7958ca457 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -104,7 +104,7 @@ export function error(...args: any[]) { function entryFromArgs(severity: LogSeverity, args: any[]): LogEntry { let entry = {}; const lastArg = args[args.length - 1]; - if (typeof lastArg == 'object' && lastArg.constructor == Object) { + if (lastArg && typeof lastArg == 'object' && lastArg.constructor == Object) { entry = args.pop(); } return Object.assign({}, entry, { From 5f7c7acc9deb4e7c9ab1ab32c660ae035a63f93c Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Thu, 25 Jun 2020 08:25:42 -0700 Subject: [PATCH 040/529] Changelog update (#718) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..43975c685 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Fixes error when last argument to logger methods is `null`. (#716) From b6e611baab000303b55958b55bdcf6655069f221 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Tue, 7 Jul 2020 13:23:23 -0700 Subject: [PATCH 041/529] Adds newly available GCF regions. (#722) --- CHANGELOG.md | 2 ++ scripts/fetch-regions | 7 +++++++ spec/function-builder.spec.ts | 30 ------------------------------ src/function-builder.ts | 11 ++++------- src/function-configuration.ts | 6 +++++- 5 files changed, 18 insertions(+), 38 deletions(-) create mode 100755 scripts/fetch-regions diff --git a/CHANGELOG.md b/CHANGELOG.md index 43975c685..edab83fdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,3 @@ - Fixes error when last argument to logger methods is `null`. (#716) +- Adds newly available locations `us-west3`, `europe-west6`, `northamerica-northeast1`, and `australia-southeast1`. +- No longer throw errors for unrecognized regions (deploy will error instead). diff --git a/scripts/fetch-regions b/scripts/fetch-regions new file mode 100755 index 000000000..d529cdf9b --- /dev/null +++ b/scripts/fetch-regions @@ -0,0 +1,7 @@ +#!/bin/bash + +if [ -z $1 ]; then + echo "Must provide a project id as first argument." && exit 1 +fi; + +gcloud functions regions list --project $1 --format=json | jq 'map(.locationId)' \ No newline at end of file diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts index 0044e5661..0ad4265ed 100644 --- a/spec/function-builder.spec.ts +++ b/spec/function-builder.spec.ts @@ -121,14 +121,6 @@ describe('FunctionBuilder', () => { expect(fn.__trigger.timeout).to.deep.equal('90s'); }); - it('should fail if valid runtime options but unsupported region are set (reverse order)', () => { - expect(() => { - functions - .runWith({ timeoutSeconds: 90, memory: '256MB' }) - .region('unsupported' as any); - }).to.throw(Error, 'region'); - }); - it('should fail if supported region but invalid runtime options are set (reverse order)', () => { expect(() => { functions @@ -165,28 +157,6 @@ describe('FunctionBuilder', () => { }).to.throw(Error, 'TimeoutSeconds'); }); - it('should throw an error if user chooses an invalid region', () => { - expect(() => { - return functions.region('unsupported' as any); - }).to.throw(Error, 'region'); - - expect(() => { - return functions.region('unsupported' as any).runWith({ - timeoutSeconds: 500, - } as any); - }).to.throw(Error, 'region'); - - expect(() => { - return functions.region('unsupported' as any, 'us-east1'); - }).to.throw(Error, 'region'); - - expect(() => { - return functions.region('unsupported' as any, 'us-east1').runWith({ - timeoutSeconds: 500, - } as any); - }).to.throw(Error, 'region'); - }); - it('should throw an error if user chooses no region when using .region()', () => { expect(() => { return functions.region(); diff --git a/src/function-builder.ts b/src/function-builder.ts index 580e0498b..76c4b6c9d 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -78,11 +78,6 @@ function assertRegionsAreValid(regions: string[]): boolean { if (!regions.length) { throw new Error('You must specify at least one region'); } - if (_.difference(regions, SUPPORTED_REGIONS).length) { - throw new Error( - `The only valid regions are: ${SUPPORTED_REGIONS.join(', ')}` - ); - } return true; } @@ -95,7 +90,7 @@ function assertRegionsAreValid(regions: string[]): boolean { * functions.region('us-east1', 'us-central1') */ export function region( - ...regions: Array + ...regions: Array ): FunctionBuilder { if (assertRegionsAreValid(regions)) { return new FunctionBuilder({ regions }); @@ -127,7 +122,9 @@ export class FunctionBuilder { * @example * functions.region('us-east1', 'us-central1') */ - region(...regions: Array): FunctionBuilder { + region( + ...regions: Array + ): FunctionBuilder { if (assertRegionsAreValid(regions)) { this.options.regions = regions; return this; diff --git a/src/function-configuration.ts b/src/function-configuration.ts index 05d4517a8..2ec102fda 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -5,11 +5,15 @@ export const SUPPORTED_REGIONS = [ 'us-central1', 'us-east1', 'us-east4', + 'us-west3', 'europe-west1', 'europe-west2', 'europe-west3', + 'europe-west6', 'asia-east2', 'asia-northeast1', + 'northamerica-northeast1', + 'australia-southeast1', ] as const; /** @@ -70,6 +74,6 @@ export interface RuntimeOptions { } export interface DeploymentOptions extends RuntimeOptions { - regions?: Array; + regions?: Array; schedule?: Schedule; } From ab3ae2af275a0037eff905804a49d031f1734f93 Mon Sep 17 00:00:00 2001 From: Sam Stern Date: Wed, 8 Jul 2020 15:02:58 -0400 Subject: [PATCH 042/529] Fix issues with .ref in Database functions (#727) --- CHANGELOG.md | 1 + src/providers/database.ts | 25 ++++++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edab83fdb..35049e3ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ - Fixes error when last argument to logger methods is `null`. (#716) - Adds newly available locations `us-west3`, `europe-west6`, `northamerica-northeast1`, and `australia-southeast1`. - No longer throw errors for unrecognized regions (deploy will error instead). +- Fixes error where `snap.ref` in database functions did not work when using the Emulator Suite (#726) diff --git a/src/providers/database.ts b/src/providers/database.ts index 8c8de4ec6..2aa50b55a 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -41,6 +41,7 @@ export const provider = 'google.firebase.database'; export const service = 'firebaseio.com'; const databaseURLRegex = new RegExp('^https://([^.]+).'); +const emulatorDatabaseURLRegex = new RegExp('^http://.*ns=([^&]+)'); /** * Registers a function that triggers on events from a specific @@ -138,14 +139,25 @@ export function _refWithOptions( '\n If you are unit testing, please set process.env.FIREBASE_CONFIG' ); } - const match = databaseURL.match(databaseURLRegex); - if (!match) { + + let instance = undefined; + const prodMatch = databaseURL.match(databaseURLRegex); + if (prodMatch) { + instance = prodMatch[1]; + } else { + const emulatorMatch = databaseURL.match(emulatorDatabaseURLRegex); + if (emulatorMatch) { + instance = emulatorMatch[1]; + } + } + + if (!instance) { throw new Error( 'Invalid value for config firebase.databaseURL: ' + databaseURL ); } - const subdomain = match[1]; - return `projects/_/instances/${subdomain}/refs/${normalized}`; + + return `projects/_/instances/${instance}/refs/${normalized}`; }; return new RefBuilder(apps(), resourceGetter, options); @@ -350,7 +362,10 @@ export class DataSnapshot { private app?: firebase.app.App, instance?: string ) { - if (instance) { + if (app && app.options.databaseURL.startsWith('http:')) { + // In this case we're dealing with an emulator + this.instance = app.options.databaseURL; + } else if (instance) { // SDK always supplies instance, but user's unit tests may not this.instance = instance; } else if (app) { From ad144e41c82e551fe323d473919d7e8426d11d84 Mon Sep 17 00:00:00 2001 From: Lauren Long Date: Thu, 9 Jul 2020 17:09:42 -0700 Subject: [PATCH 043/529] Update ref docs for handler SDK to include scheduled functions. (#731) --- src/handler-builder.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/handler-builder.ts b/src/handler-builder.ts index 81232df59..ea0f4af74 100644 --- a/src/handler-builder.ts +++ b/src/handler-builder.ts @@ -305,13 +305,20 @@ export class HandlerBuilder { /** * Create a handler for Cloud Pub/Sub events. - - * `pubsub.onPublish` handles the publication of messages to a topic. - + * + * `topic.onPublish` handles messages published to a Pub/Sub topic from SDKs, Cloud Console, or gcloud CLI. + * * @example * ```javascript * exports.myFunction = functions.handler.pubsub.topic.onPublish((message) => { ... }) * ``` + + * `schedule.onPublish` handles messages published to a Pub/Sub topic on a schedule. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.pubsub.schedule.onPublish((message) => { ... }) + * ``` */ get pubsub() { return { From 858cb9befd9468a9f1e1f2fb0d6f339cce3dec35 Mon Sep 17 00:00:00 2001 From: egilmorez Date: Fri, 10 Jul 2020 11:01:11 -0700 Subject: [PATCH 044/529] Eg pubsub fixes (#729) * Adding logger SDK to reference, with some edits. * Adding link to structured logging details per feedback. * Adding ScheduleBuilder class to TOC and adding minimal commenting. * Improving wording and format per feedback. * I think we need to document both the schedule function and the builder class this way. * Last edits from feedback. --- docgen/content-sources/toc.yaml | 2 ++ src/providers/pubsub.ts | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/docgen/content-sources/toc.yaml b/docgen/content-sources/toc.yaml index a394975df..37093f7c2 100644 --- a/docgen/content-sources/toc.yaml +++ b/docgen/content-sources/toc.yaml @@ -106,6 +106,8 @@ toc: path: /docs/reference/functions/providers_pubsub_.message.html - title: 'TopicBuilder' path: /docs/reference/functions/providers_pubsub_.topicbuilder.html + - title: 'ScheduleBuilder' + path: /docs/reference/functions/providers_pubsub_.schedulebuilder.html - title: 'functions.remoteconfig' path: /docs/reference/functions/providers_remoteconfig_.html diff --git a/src/providers/pubsub.ts b/src/providers/pubsub.ts index 0f12f669c..264ef9480 100644 --- a/src/providers/pubsub.ts +++ b/src/providers/pubsub.ts @@ -98,6 +98,12 @@ export class TopicBuilder { } } +/** + * Registers a Cloud Function to run at specified times. + * + * @param schedule The schedule, in Unix Crontab or AppEngine syntax. + * @return ScheduleBuilder interface. + */ export function schedule(schedule: string): ScheduleBuilder { return _scheduleWithOptions(schedule, {}); } @@ -120,6 +126,15 @@ export function _scheduleWithOptions( }); } +/** + * The builder for scheduled functions, which are powered by + * Google Pub/Sub and Cloud Scheduler. Describes the Cloud Scheduler + * job that is deployed to trigger a scheduled function at the provided + * frequency. For more information, see + * [Schedule functions](/docs/functions/schedule-functions). + * + * Access via [`functions.pubsub.schedule()`](providers_pubsub_.html#schedule). + */ export class ScheduleBuilder { /** @hidden */ constructor( @@ -137,6 +152,14 @@ export class ScheduleBuilder { return this; } + /** + * Event handler for scheduled functions. Triggered whenever the associated + * scheduler job sends a Pub/Sub message. + * + * @param handler Handler that fires whenever the associated + * scheduler job sends a Pub/Sub message. + * @return A Cloud Function that you can export and deploy. + */ onRun(handler: (context: EventContext) => PromiseLike | any) { const cloudFunction = makeCloudFunction({ contextOnlyHandler: handler, From b424fd3ccb4284a663213993fe2349eaef057992 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Fri, 10 Jul 2020 15:21:09 -0700 Subject: [PATCH 045/529] Adds four new regions launched July 10. (#733) --- CHANGELOG.md | 10 +++++++++- src/function-configuration.ts | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35049e3ca..d85f674fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ - Fixes error when last argument to logger methods is `null`. (#716) -- Adds newly available locations `us-west3`, `europe-west6`, `northamerica-northeast1`, and `australia-southeast1`. +- Adds eight new available regions: + - `us-west2` + - `us-west3` + - `us-west4` + - `europe-west6` + - `asia-northeast2` + - `northamerica-northeast1` + - `southamerica-east1` + - `australia-southeast1` - No longer throw errors for unrecognized regions (deploy will error instead). - Fixes error where `snap.ref` in database functions did not work when using the Emulator Suite (#726) diff --git a/src/function-configuration.ts b/src/function-configuration.ts index 2ec102fda..0ab2f9c31 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -5,14 +5,18 @@ export const SUPPORTED_REGIONS = [ 'us-central1', 'us-east1', 'us-east4', + 'us-west2', 'us-west3', + 'us-west4', 'europe-west1', 'europe-west2', 'europe-west3', 'europe-west6', 'asia-east2', 'asia-northeast1', + 'asia-northeast2', 'northamerica-northeast1', + 'southamerica-east1', 'australia-southeast1', ] as const; From 1e4de88ed658289f6c7d4e94ac24732726da04c0 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Tue, 14 Jul 2020 00:00:43 +0000 Subject: [PATCH 046/529] 3.8.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6e198c416..bef1bb506 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.7.0", + "version": "3.8.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From a07d6efeb39600a6b8b31a583eef8fc4a694df50 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Tue, 14 Jul 2020 00:00:49 +0000 Subject: [PATCH 047/529] [firebase-release] Removed change log and reset repo after 3.8.0 release --- CHANGELOG.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d85f674fa..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +0,0 @@ -- Fixes error when last argument to logger methods is `null`. (#716) -- Adds eight new available regions: - - `us-west2` - - `us-west3` - - `us-west4` - - `europe-west6` - - `asia-northeast2` - - `northamerica-northeast1` - - `southamerica-east1` - - `australia-southeast1` -- No longer throw errors for unrecognized regions (deploy will error instead). -- Fixes error where `snap.ref` in database functions did not work when using the Emulator Suite (#726) From 8bc84dda6ed1b9dc485c0f462aec2d9066453442 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Fri, 31 Jul 2020 15:27:46 -0700 Subject: [PATCH 048/529] Updates logging in https callable functions. (#745) --- README.md | 2 +- src/cloud-functions.ts | 3 ++- src/providers/crashlytics.ts | 2 +- src/providers/https.ts | 34 ++++++++++++++++++---------------- src/setup.ts | 5 +++-- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 59c5c0936..33fbaa746 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ const notifyUsers = require('./notify-users'); exports.newPost = functions.database .ref('/posts/{postId}') .onCreate((snapshot, context) => { - console.log('Received new post with ID:', context.params.postId); + functions.logger.info('Received new post with ID:', context.params.postId); return notifyUsers(snapshot.val()); }); ``` diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index f249bf7f9..b97c4ad7e 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -23,6 +23,7 @@ import { Request, Response } from 'express'; import * as _ from 'lodash'; import { DeploymentOptions, Schedule } from './function-configuration'; +import { warn } from './logger'; export { Request, Response }; /** @hidden */ @@ -379,7 +380,7 @@ export function makeCloudFunction({ promise = handler(dataOrChange, context); } if (typeof promise === 'undefined') { - console.warn('Function returned undefined, expected Promise or value'); + warn('Function returned undefined, expected Promise or value'); } return Promise.resolve(promise) .then((result) => { diff --git a/src/providers/crashlytics.ts b/src/providers/crashlytics.ts index e7a6fe882..54fc26469 100644 --- a/src/providers/crashlytics.ts +++ b/src/providers/crashlytics.ts @@ -77,7 +77,7 @@ export class IssueBuilder { * const slackMessage = ` There's a new issue (${issueId}) ` + * `in your app - ${issueTitle}`; * return notifySlack(slackMessage).then(() => { - * console.log(`Posted new issue ${issueId} successfully to Slack`); + * functions.logger.info(`Posted new issue ${issueId} successfully to Slack`); * }); * }); * ``` diff --git a/src/providers/https.ts b/src/providers/https.ts index cd9f1745d..ec397fc64 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -24,9 +24,11 @@ import * as cors from 'cors'; import * as express from 'express'; import * as firebase from 'firebase-admin'; 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'; /** @hidden */ export interface Request extends express.Request { @@ -285,13 +287,13 @@ interface HttpResponseBody { function isValidRequest(req: Request): req is HttpRequest { // The body must not be empty. if (!req.body) { - console.warn('Request is missing body.'); + warn('Request is missing body.'); return false; } // Make sure it's a POST. if (req.method !== 'POST') { - console.warn('Request has invalid method.', req.method); + warn('Request has invalid method.', req.method); return false; } @@ -303,13 +305,13 @@ function isValidRequest(req: Request): req is HttpRequest { contentType = contentType.substr(0, semiColon).trim(); } if (contentType !== 'application/json') { - console.warn('Request has incorrect Content-Type.', contentType); + warn('Request has incorrect Content-Type.', contentType); return false; } // The body must have data. if (_.isUndefined(req.body.data)) { - console.warn('Request body is missing data.', req.body); + warn('Request body is missing data.', req.body); return false; } @@ -318,7 +320,7 @@ function isValidRequest(req: Request): req is HttpRequest { // Verify that the body does not have any extra fields. const extras = _.omit(req.body, 'data'); if (!_.isEmpty(extras)) { - console.warn('Request body has extra fields.', extras); + warn('Request body has extra fields.', extras); return false; } return true; @@ -363,7 +365,7 @@ export function encode(data: any): any { return _.mapValues(data, encode); } // If we got this far, the data is not encodable. - console.error('Data cannot be encoded in JSON.', data); + error('Data cannot be encoded in JSON.', data); throw new Error('Data cannot be encoded in JSON: ' + data); } @@ -386,13 +388,13 @@ export function decode(data: any): any { // worth all the extra code to detect that case. const value = parseFloat(data.value); if (_.isNaN(value)) { - console.error('Data cannot be decoded from JSON.', data); + error('Data cannot be decoded from JSON.', data); throw new Error('Data cannot be decoded from JSON: ' + data); } return value; } default: { - console.error('Data cannot be decoded from JSON.', data); + error('Data cannot be decoded from JSON.', data); throw new Error('Data cannot be decoded from JSON: ' + data); } } @@ -420,7 +422,7 @@ export function _onCallWithOptions( const func = async (req: Request, res: express.Response) => { try { if (!isValidRequest(req)) { - console.error('Invalid request', req); + error('Invalid request, unable to process.'); throw new HttpsError('invalid-argument', 'Bad Request'); } @@ -441,7 +443,7 @@ export function _onCallWithOptions( uid: authToken.uid, token: authToken, }; - } catch (e) { + } catch (err) { throw new HttpsError('unauthenticated', 'Unauthenticated'); } } @@ -464,15 +466,15 @@ export function _onCallWithOptions( // If there was some result, encode it in the body. const responseBody: HttpResponseBody = { result }; res.status(200).send(responseBody); - } catch (error) { - if (!(error instanceof HttpsError)) { + } catch (err) { + if (!(err instanceof HttpsError)) { // This doesn't count as an 'explicit' error. - console.error('Unhandled error', error); - error = new HttpsError('internal', 'INTERNAL'); + error('Unhandled error', error); + err = new HttpsError('internal', 'INTERNAL'); } - const { status } = error.httpErrorCode; - const body = { error: error.toJSON() }; + const { status } = err.httpErrorCode; + const body = { error: err.toJSON() }; res.status(status).send(body); } diff --git a/src/setup.ts b/src/setup.ts index b30fa6226..d2935af15 100644 --- a/src/setup.ts +++ b/src/setup.ts @@ -22,6 +22,7 @@ /** @hidden */ import { firebaseConfig } from './config'; +import { warn } from './logger'; // Set up for config and vars export function setup() { @@ -45,7 +46,7 @@ export function setup() { // If FIREBASE_CONFIG is still not found, try using GCLOUD_PROJECT to estimate if (!process.env.FIREBASE_CONFIG) { if (process.env.GCLOUD_PROJECT) { - console.warn( + warn( 'Warning, estimating Firebase Config based on GCLOUD_PROJECT. Initializing firebase-admin may fail' ); process.env.FIREBASE_CONFIG = JSON.stringify({ @@ -58,7 +59,7 @@ export function setup() { projectId: process.env.GCLOUD_PROJECT, }); } else { - console.warn( + warn( 'Warning, FIREBASE_CONFIG and GCLOUD_PROJECT environment variables are missing. Initializing firebase-admin will fail' ); } From e4f2eba2367fd6863e340617e49b55b214d4d8b6 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Fri, 31 Jul 2020 15:34:06 -0700 Subject: [PATCH 049/529] Adds support for three new regions. (#750) --- CHANGELOG.md | 2 ++ src/function-configuration.ts | 3 +++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..3c25216f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,2 @@ +- Updates HTTP callable functions to use structured logging for Node 10+ environments. +- Adds type hints for new Cloud Functions regions `asia-northeast3`, `asia-south1`, and `asia-southeast2`. diff --git a/src/function-configuration.ts b/src/function-configuration.ts index 0ab2f9c31..a7d87b726 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -15,6 +15,9 @@ export const SUPPORTED_REGIONS = [ 'asia-east2', 'asia-northeast1', 'asia-northeast2', + 'asia-northeast3', + 'asia-south1', + 'asia-southeast2', 'northamerica-northeast1', 'southamerica-east1', 'australia-southeast1', From 0ae7c3c8b8fd985fe669c2768df7df3ce847e5f3 Mon Sep 17 00:00:00 2001 From: p-young <35713583+p-young@users.noreply.github.com> Date: Fri, 31 Jul 2020 15:48:04 -0700 Subject: [PATCH 050/529] Fix onRequest handler return type to allow promises (async) (#705) Co-authored-by: Michael Bleigh --- src/function-builder.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/function-builder.ts b/src/function-builder.ts index 76c4b6c9d..27fcd821a 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -154,7 +154,10 @@ export class FunctionBuilder { * same signature as an Express app. */ onRequest: ( - handler: (req: https.Request, resp: express.Response) => void + handler: ( + req: https.Request, + resp: express.Response + ) => void | Promise ) => https._onRequestWithOptions(handler, this.options), /** * Declares a callable method for clients to call using a Firebase SDK. From 78fb92f7943ccb5e244ae4b5a164cfa302faa719 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Fri, 31 Jul 2020 16:11:18 -0700 Subject: [PATCH 051/529] Update CHANGELOG.md (#751) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c25216f0..3b20c6064 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,3 @@ - Updates HTTP callable functions to use structured logging for Node 10+ environments. - Adds type hints for new Cloud Functions regions `asia-northeast3`, `asia-south1`, and `asia-southeast2`. +- Updates type definition of `https.onRequest` to allow for promises (async functions). From e91d16a0c7d39c8894a7226e3f79700d6c6f1c01 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 31 Jul 2020 23:20:59 +0000 Subject: [PATCH 052/529] 3.9.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bef1bb506..cec716c20 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.8.0", + "version": "3.9.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 02f61cf1dc3828cb8af842ed15c1cbab3466b074 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 31 Jul 2020 23:21:05 +0000 Subject: [PATCH 053/529] [firebase-release] Removed change log and reset repo after 3.9.0 release --- CHANGELOG.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b20c6064..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +0,0 @@ -- Updates HTTP callable functions to use structured logging for Node 10+ environments. -- Adds type hints for new Cloud Functions regions `asia-northeast3`, `asia-south1`, and `asia-southeast2`. -- Updates type definition of `https.onRequest` to allow for promises (async functions). From c69c974f18431f27244f03abfb67955718b4efb7 Mon Sep 17 00:00:00 2001 From: Lauren Long Date: Fri, 7 Aug 2020 14:17:48 -0700 Subject: [PATCH 054/529] Update reference docs for EventContext.EventType (#743) --- src/cloud-functions.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index b97c4ad7e..1850b0d62 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -89,22 +89,22 @@ export interface EventContext { eventId: string; /** - * Type of event. Valid values are: + * Type of event. Possible values are: * - * * `providers/google.firebase.analytics/eventTypes/event.log` - * * `providers/firebase.auth/eventTypes/user.create` - * * `providers/firebase.auth/eventTypes/user.delete` - * * `providers/firebase.crashlytics/eventTypes/issue.new` - * * `providers/firebase.crashlytics/eventTypes/issue.regressed` - * * `providers/firebase.crashlytics/eventTypes/issue.velocityAlert` - * * `providers/google.firebase.database/eventTypes/ref.write` - * * `providers/google.firebase.database/eventTypes/ref.create` - * * `providers/google.firebase.database/eventTypes/ref.update` - * * `providers/google.firebase.database/eventTypes/ref.delete` - * * `providers/cloud.firestore/eventTypes/document.write` - * * `providers/cloud.firestore/eventTypes/document.create` - * * `providers/cloud.firestore/eventTypes/document.update` - * * `providers/cloud.firestore/eventTypes/document.delete` + * * `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` + * * `google.firebase.database.ref.delete` + * * `google.firestore.document.write` + * * `google.firestore.document.create` + * * `google.firestore.document.update` + * * `google.firestore.document.delete` * * `google.pubsub.topic.publish` * * `google.firebase.remoteconfig.update` * * `google.storage.object.finalize` From d1432c50863dec4b27834241279180f12f6148d5 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Mon, 10 Aug 2020 12:44:09 -0700 Subject: [PATCH 055/529] Updates firebase-admin peerDependency (#756) --- CHANGELOG.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..bbf6a741b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Updates `firebase-admin` dependency to support `^9.0.0` in addition to `^8.0.0`. Note that `firebase-admin` no longer supports Node.js 8.x as of `9.0.0`. diff --git a/package.json b/package.json index cec716c20..8cb004a34 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "yargs": "^15.3.1" }, "peerDependencies": { - "firebase-admin": "^8.0.0" + "firebase-admin": "^8.0.0 || ^9.0.0" }, "engines": { "node": "^8.13.0 || >=10.10.0" From 84433df63147d61a5330f9b8d3e5c5e4837ad658 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Wed, 12 Aug 2020 10:27:37 -0700 Subject: [PATCH 056/529] Fixes logging of unhandled exceptions in callable functions. (#759) --- CHANGELOG.md | 1 + src/providers/https.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbf6a741b..6a558f5e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ - Updates `firebase-admin` dependency to support `^9.0.0` in addition to `^8.0.0`. Note that `firebase-admin` no longer supports Node.js 8.x as of `9.0.0`. +- Fixes logging of unexpected errors in `https.onCall()` functions. diff --git a/src/providers/https.ts b/src/providers/https.ts index ec397fc64..cf64c9d90 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -469,7 +469,7 @@ export function _onCallWithOptions( } catch (err) { if (!(err instanceof HttpsError)) { // This doesn't count as an 'explicit' error. - error('Unhandled error', error); + error('Unhandled error', err); err = new HttpsError('internal', 'INTERNAL'); } From ae9e856f0be28e68ed8bf68a23f581eccfd7aed3 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 12 Aug 2020 19:58:53 +0000 Subject: [PATCH 057/529] 3.9.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8cb004a34..6560a8d07 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.9.0", + "version": "3.9.1", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From b7514295540b0e872e9be46656fb63b158fa9484 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 12 Aug 2020 19:58:58 +0000 Subject: [PATCH 058/529] [firebase-release] Removed change log and reset repo after 3.9.1 release --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a558f5e0..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +0,0 @@ -- Updates `firebase-admin` dependency to support `^9.0.0` in addition to `^8.0.0`. Note that `firebase-admin` no longer supports Node.js 8.x as of `9.0.0`. -- Fixes logging of unexpected errors in `https.onCall()` functions. From 5e287286c7aa6bbf66f36d21e49db5a7d5e06e20 Mon Sep 17 00:00:00 2001 From: Sam Stern Date: Mon, 17 Aug 2020 05:44:48 -0400 Subject: [PATCH 059/529] Restore failurePolicy (#760) --- CHANGELOG.md | 1 + spec/function-builder.spec.ts | 31 +++++++++++++++++++++++ src/cloud-functions.ts | 25 +++++++++++++++++-- src/function-builder.ts | 47 +++++++++++++++++++++++++++++------ src/function-configuration.ts | 13 ++++++++++ 5 files changed, 107 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..7c0e3b45c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Adds support for functions failure policies (#482) diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts index 0ad4265ed..1f9122061 100644 --- a/spec/function-builder.spec.ts +++ b/spec/function-builder.spec.ts @@ -82,6 +82,7 @@ describe('FunctionBuilder', () => { const fn = functions .runWith({ timeoutSeconds: 90, + failurePolicy: { retry: {} }, memory: '256MB', }) .auth.user() @@ -89,6 +90,20 @@ describe('FunctionBuilder', () => { expect(fn.__trigger.availableMemoryMb).to.deep.equal(256); expect(fn.__trigger.timeout).to.deep.equal('90s'); + expect(fn.__trigger.failurePolicy).to.deep.equal({ retry: {} }); + }); + + it("should apply a default failure policy if it's aliased with `true`", () => { + const fn = functions + .runWith({ + failurePolicy: true, + memory: '256MB', + timeoutSeconds: 90, + }) + .auth.user() + .onCreate((user) => user); + + expect(fn.__trigger.failurePolicy).to.deep.equal({ retry: {} }); }); it('should allow both supported region and valid runtime options to be set', () => { @@ -129,6 +144,22 @@ describe('FunctionBuilder', () => { }).to.throw(Error, 'TimeoutSeconds'); }); + it('should throw an error if user chooses a failurePolicy which is neither an object nor a boolean', () => { + expect(() => + functions.runWith({ + failurePolicy: (1234 as unknown) as functions.RuntimeOptions['failurePolicy'], + }) + ).to.throw(Error, 'failurePolicy must be a boolean or an object'); + }); + + it('should throw an error if user chooses a failurePolicy.retry which is not an object', () => { + expect(() => + functions.runWith({ + failurePolicy: { retry: (1234 as unknown) as object }, + }) + ).to.throw(Error, 'failurePolicy.retry'); + }); + it('should throw an error if user chooses an invalid memory allocation', () => { expect(() => { return functions.runWith({ diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 1850b0d62..387953bdc 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -22,8 +22,13 @@ import { Request, Response } from 'express'; import * as _ from 'lodash'; -import { DeploymentOptions, Schedule } from './function-configuration'; import { warn } from './logger'; +import { + DEFAULT_FAILURE_POLICY, + DeploymentOptions, + FailurePolicy, + Schedule, +} from './function-configuration'; export { Request, Response }; /** @hidden */ @@ -205,6 +210,7 @@ export namespace Change { if (json.fieldMask) { before = applyFieldMask(before, json.after, json.fieldMask); } + return Change.fromObjects( customizer(before || {}), customizer(json.after || {}) @@ -219,7 +225,8 @@ export namespace Change { ) { const before = _.assign({}, after); const masks = fieldMask.split(','); - _.forEach(masks, (mask) => { + + masks.forEach((mask) => { const val = _.get(sparseBefore, mask); if (typeof val === 'undefined') { _.unset(before, mask); @@ -227,6 +234,7 @@ export namespace Change { _.set(before, mask, val); } }); + return before; } } @@ -256,6 +264,7 @@ export interface TriggerAnnotated { resource: string; service: string; }; + failurePolicy?: FailurePolicy; httpsTrigger?: {}; labels?: { [key: string]: string }; regions?: string[]; @@ -473,6 +482,18 @@ export function optionsToTrigger(options: DeploymentOptions) { if (options.regions) { trigger.regions = options.regions; } + if (options.failurePolicy !== undefined) { + switch (options.failurePolicy) { + case false: + trigger.failurePolicy = undefined; + break; + case true: + trigger.failurePolicy = DEFAULT_FAILURE_POLICY; + break; + default: + trigger.failurePolicy = options.failurePolicy; + } + } if (options.timeoutSeconds) { trigger.timeout = options.timeoutSeconds.toString() + 's'; } diff --git a/src/function-builder.ts b/src/function-builder.ts index 27fcd821a..6050927ea 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -66,6 +66,23 @@ function assertRuntimeOptionsValid(runtimeOptions: RuntimeOptions): boolean { `TimeoutSeconds must be between 0 and ${MAX_TIMEOUT_SECONDS}` ); } + if (runtimeOptions.failurePolicy !== undefined) { + if ( + _.isBoolean(runtimeOptions.failurePolicy) === false && + _.isObjectLike(runtimeOptions.failurePolicy) === false + ) { + throw new Error(`failurePolicy must be a boolean or an object.`); + } + + if (typeof runtimeOptions.failurePolicy === 'object') { + if ( + _.isObjectLike(runtimeOptions.failurePolicy.retry) === false || + _.isEmpty(runtimeOptions.failurePolicy.retry) === false + ) { + throw new Error('failurePolicy.retry must be an empty object.'); + } + } + } return true; } @@ -100,10 +117,14 @@ export function region( /** * Configure runtime options for the function. * @param runtimeOptions Object with three 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 - * 0 to 540. + * 1. 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. + * + * Value must not be null. */ export function runWith(runtimeOptions: RuntimeOptions): FunctionBuilder { if (assertRuntimeOptionsValid(runtimeOptions)) { @@ -134,10 +155,14 @@ export class FunctionBuilder { /** * Configure runtime options for the function. * @param runtimeOptions Object with three 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 - * 0 to 540. + * 1. 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. + * + * Value must not be null. */ runWith(runtimeOptions: RuntimeOptions): FunctionBuilder { if (assertRuntimeOptionsValid(runtimeOptions)) { @@ -147,6 +172,12 @@ export class FunctionBuilder { } get https() { + if (this.options.failurePolicy !== undefined) { + console.warn( + 'RuntimeOptions.failurePolicy is not supported in https functions.' + ); + } + return { /** * Handle HTTP requests. diff --git a/src/function-configuration.ts b/src/function-configuration.ts index a7d87b726..31c708f3f 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -64,7 +64,20 @@ export interface Schedule { retryConfig?: ScheduleRetryConfig; } +export interface FailurePolicy { + retry: {}; +} + +export const DEFAULT_FAILURE_POLICY: FailurePolicy = { + retry: {}, +}; + export interface RuntimeOptions { + /** + * Failure policy of the function, with boolean `true` being equivalent to + * providing an empty retry object. + */ + failurePolicy?: FailurePolicy | boolean; /** * Amount of memory to allocate to the function. */ From 7bd795e2fac0a87d9c3c57b31594b67cfc59ad8c Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 20 Aug 2020 15:59:59 +0000 Subject: [PATCH 060/529] 3.10.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6560a8d07..48a71e881 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.9.1", + "version": "3.10.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From bc9d8caef0e44a84b7a1a4ca749268467bbe80df Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 20 Aug 2020 16:00:07 +0000 Subject: [PATCH 061/529] [firebase-release] Removed change log and reset repo after 3.10.0 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c0e3b45c..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Adds support for functions failure policies (#482) From 6b15ff7da69721bc1a1ecd8e44f9c76481061213 Mon Sep 17 00:00:00 2001 From: David Hagege Date: Fri, 21 Aug 2020 08:28:36 +0900 Subject: [PATCH 062/529] Add support for VPC connectors in `functions.runWith` (#752) --- spec/function-builder.spec.ts | 39 +++++++++++++++++++++++++++++++++++ src/cloud-functions.ts | 11 ++++++++++ src/function-builder.ts | 31 ++++++++++++++++++++++------ src/function-configuration.ts | 19 +++++++++++++++++ 4 files changed, 94 insertions(+), 6 deletions(-) diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts index 1f9122061..eec531fdf 100644 --- a/spec/function-builder.spec.ts +++ b/spec/function-builder.spec.ts @@ -199,4 +199,43 @@ describe('FunctionBuilder', () => { } as any); }).to.throw(Error, 'at least one region'); }); + + it('should allow a vpcConnector to be set', () => { + const fn = functions + .runWith({ + vpcConnector: 'test-connector', + }) + .auth.user() + .onCreate((user) => user); + + expect(fn.__trigger.vpcConnector).to.equal('test-connector'); + }); + + it('should allow a vpcConnectorEgressSettings to be set', () => { + const fn = functions + .runWith({ + vpcConnector: 'test-connector', + vpcConnectorEgressSettings: 'PRIVATE_RANGES_ONLY', + }) + .auth.user() + .onCreate((user) => user); + + expect(fn.__trigger.vpcConnectorEgressSettings).to.equal( + 'PRIVATE_RANGES_ONLY' + ); + }); + + it('should throw an error if user chooses an invalid vpcConnectorEgressSettings', () => { + expect(() => { + return functions.runWith({ + vpcConnector: 'test-connector', + vpcConnectorEgressSettings: 'INCORRECT_OPTION', + } as any); + }).to.throw( + Error, + `The only valid vpcConnectorEgressSettings values are: ${functions.VPC_EGRESS_SETTINGS_OPTIONS.join( + ',' + )}` + ); + }); }); diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 387953bdc..109a1ad05 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -270,6 +270,8 @@ export interface TriggerAnnotated { regions?: string[]; schedule?: Schedule; timeout?: string; + vpcConnector?: string; + vpcConnectorEgressSettings?: string; }; } @@ -514,5 +516,14 @@ export function optionsToTrigger(options: DeploymentOptions) { if (options.maxInstances) { trigger.maxInstances = options.maxInstances; } + + if (options.vpcConnector) { + trigger.vpcConnector = options.vpcConnector; + } + + if (options.vpcConnectorEgressSettings) { + trigger.vpcConnectorEgressSettings = options.vpcConnectorEgressSettings; + } + return trigger; } diff --git a/src/function-builder.ts b/src/function-builder.ts index 6050927ea..919f9f09b 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -30,6 +30,7 @@ import { RuntimeOptions, SUPPORTED_REGIONS, VALID_MEMORY_OPTIONS, + VPC_EGRESS_SETTINGS_OPTIONS, } from './function-configuration'; import * as analytics from './providers/analytics'; import * as auth from './providers/auth'; @@ -66,6 +67,21 @@ function assertRuntimeOptionsValid(runtimeOptions: RuntimeOptions): boolean { `TimeoutSeconds must be between 0 and ${MAX_TIMEOUT_SECONDS}` ); } + + if ( + runtimeOptions.vpcConnectorEgressSettings && + !_.includes( + VPC_EGRESS_SETTINGS_OPTIONS, + runtimeOptions.vpcConnectorEgressSettings + ) + ) { + throw new Error( + `The only valid vpcConnectorEgressSettings values are: ${VPC_EGRESS_SETTINGS_OPTIONS.join( + ',' + )}` + ); + } + if (runtimeOptions.failurePolicy !== undefined) { if ( _.isBoolean(runtimeOptions.failurePolicy) === false && @@ -116,13 +132,16 @@ export function region( /** * 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', and '2GB'. + * 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 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. */ diff --git a/src/function-configuration.ts b/src/function-configuration.ts index 31c708f3f..558895148 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -44,6 +44,15 @@ export const VALID_MEMORY_OPTIONS = [ '2GB', ] as const; +/** + * List of available options for VpcConnectorEgressSettings. + */ +export const VPC_EGRESS_SETTINGS_OPTIONS = [ + 'VPC_CONNECTOR_EGRESS_SETTINGS_UNSPECIFIED', + 'PRIVATE_RANGES_ONLY', + 'ALL_TRAFFIC', +] as const; + /** * Scheduler retry options. Applies only to scheduled functions. */ @@ -91,6 +100,16 @@ export interface RuntimeOptions { * Max number of actual instances allowed to be running in parallel */ maxInstances?: number; + + /** + * Connect cloud function to specified VPC connector + */ + vpcConnector?: string; + + /** + * Egress settings for VPC connector + */ + vpcConnectorEgressSettings?: typeof VPC_EGRESS_SETTINGS_OPTIONS[number]; } export interface DeploymentOptions extends RuntimeOptions { From f4faaad9c914f7744ae15d61f1ee88a1e8d3ffad Mon Sep 17 00:00:00 2001 From: Lauren Long Date: Thu, 20 Aug 2020 17:00:18 -0700 Subject: [PATCH 063/529] Update CHANGELOG.md for 3.11.0 (#764) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..f46f8b3f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- 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) From f9d71aab9278b375761d28b5584916f2da2fe66b Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 21 Aug 2020 16:10:08 +0000 Subject: [PATCH 064/529] 3.11.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 48a71e881..4eb5986d8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.10.0", + "version": "3.11.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 85af5e1a1a3792ce67c638fc98f522f6b3644923 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 21 Aug 2020 16:10:15 +0000 Subject: [PATCH 065/529] [firebase-release] Removed change log and reset repo after 3.11.0 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) 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) From 7e2c0ec13e781d829bd37e66efc61c6ee74191a2 Mon Sep 17 00:00:00 2001 From: joehan Date: Mon, 14 Sep 2020 13:36:00 -0700 Subject: [PATCH 066/529] Update tests to change region based on env variable FIREBASE_FUNCTIONS_TEST_REGION (#780) * update tests to change region based on environment variable FIREBASE_FUNCTIONS_TEST_REGION * formats --- integration_test/functions/src/auth-tests.ts | 108 ++++++++++-------- .../functions/src/database-tests.ts | 6 +- .../functions/src/firestore-tests.ts | 2 + integration_test/functions/src/https-tests.ts | 4 +- integration_test/functions/src/index.ts | 4 +- .../functions/src/pubsub-tests.ts | 12 +- .../functions/src/remoteConfig-tests.ts | 10 +- .../functions/src/storage-tests.ts | 3 + .../functions/src/testLab-tests.ts | 2 + integration_test/run_tests.sh | 27 ++++- 10 files changed, 118 insertions(+), 60 deletions(-) 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..6af0f9fac 100644 --- a/integration_test/functions/src/https-tests.ts +++ b/integration_test/functions/src/https-tests.ts @@ -2,7 +2,9 @@ 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') diff --git a/integration_test/functions/src/index.ts b/integration_test/functions/src/index.ts index 9988fe636..2ac16e5fa 100644 --- a/integration_test/functions/src/index.ts +++ b/integration_test/functions/src/index.ts @@ -20,13 +20,14 @@ import * as testLab from './testLab-utils'; import 'firebase-functions'; // temporary shim until process.env.FIREBASE_CONFIG available natively in GCF(BUG 63586213) 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', @@ -62,6 +63,7 @@ function callScheduleTrigger(functionName: string, region: string) { } export const integrationTests: any = functions + .region(REGION) .runWith({ timeoutSeconds: 540, }) diff --git a/integration_test/functions/src/pubsub-tests.ts b/integration_test/functions/src/pubsub-tests.ts index 3c1b1ecc7..e2a3f1bd0 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,8 +62,9 @@ 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; 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..cf4b2f062 100644 --- a/integration_test/functions/src/testLab-tests.ts +++ b/integration_test/functions/src/testLab-tests.ts @@ -2,11 +2,13 @@ import * as functions from 'firebase-functions'; import * as _ from 'lodash'; import { TestSuite, expectEq } 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') diff --git a/integration_test/run_tests.sh b/integration_test/run_tests.sh index 903936b13..ba567536a 100755 --- a/integration_test/run_tests.sh +++ b/integration_test/run_tests.sh @@ -38,6 +38,26 @@ function build_sdk { mv firebase-functions-*.tgz "integration_test/functions/firebase-functions-${TIMESTAMP}.tgz" } +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 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 +} + function pick_node8 { cd "${DIR}" cp package.node8.json functions/package.json @@ -98,7 +118,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 +130,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,6 +141,7 @@ function cleanup { # Setup build_sdk delete_all_functions +set_region # Node 8 tests pick_node8 From f59ff8cde4de1cf2335fbc9525a123aa740ef1dd Mon Sep 17 00:00:00 2001 From: egilmorez Date: Mon, 2 Nov 2020 07:30:50 -0800 Subject: [PATCH 067/529] Adding required tags to page template. (#804) * Adding required tags to page template. * Adding formatting and fixing typo discovered in internal review. * More fixes/additions discovered in internal review. * Fixing format of toc.yaml file. --- docgen/content-sources/toc.yaml | 25 ++++++++++++++++++++----- docgen/theme/layouts/default.hbs | 2 ++ src/function-builder.ts | 27 +++++++++++++++------------ 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/docgen/content-sources/toc.yaml b/docgen/content-sources/toc.yaml index 37093f7c2..5758eb74e 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: @@ -127,11 +141,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 +152,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/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/src/function-builder.ts b/src/function-builder.ts index 919f9f09b..9b5f5660b 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -133,15 +133,15 @@ 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 + * 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 + * 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 - * egress traffic is sent through the vpcConnector. + * 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. */ @@ -173,13 +173,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', and '2GB'. + * 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. */ From 2988a2b3b1f17674446504c3b24c5c10335aa728 Mon Sep 17 00:00:00 2001 From: joehan Date: Wed, 18 Nov 2020 09:54:18 -0800 Subject: [PATCH 068/529] Adds 4GB as a memory option. (#814) --- src/function-configuration.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/function-configuration.ts b/src/function-configuration.ts index 558895148..6743d463e 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -42,6 +42,7 @@ export const VALID_MEMORY_OPTIONS = [ '512MB', '1GB', '2GB', + '4GB', ] as const; /** From ecfefd1bcfd26311e7a4de4665f3d02b49f7dc94 Mon Sep 17 00:00:00 2001 From: Marcel Goya <3046751+marcelgoya@users.noreply.github.com> Date: Wed, 25 Nov 2020 22:22:07 +0100 Subject: [PATCH 069/529] Add ingress settings support (#815) * Add IngressSettings support * Add IngressSettings support * Format Co-authored-by: Marcel Goya Co-authored-by: joehan --- src/function-configuration.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/function-configuration.ts b/src/function-configuration.ts index 6743d463e..10098a745 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -54,6 +54,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. */ @@ -111,6 +121,11 @@ export interface RuntimeOptions { * Egress settings for VPC connector */ vpcConnectorEgressSettings?: typeof VPC_EGRESS_SETTINGS_OPTIONS[number]; + + /** + * Ingress settings + */ + ingressSettings?: typeof INGRESS_SETTINGS_OPTIONS[number]; } export interface DeploymentOptions extends RuntimeOptions { From c11e5b3b2a6cce9bd87674e68cc3a7239a99489a Mon Sep 17 00:00:00 2001 From: Bryan Kendall Date: Mon, 30 Nov 2020 08:05:36 -0800 Subject: [PATCH 070/529] introduce `package-lock.json` (#781) * initial package-lock * remove npmrc * update package-lock * update typedoc * remove istanbul; audit fixes Co-authored-by: joehan --- .npmrc | 1 - package-lock.json | 4688 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 +- 3 files changed, 4689 insertions(+), 3 deletions(-) delete mode 100644 .npmrc create mode 100644 package-lock.json 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/package-lock.json b/package-lock.json new file mode 100644 index 000000000..17b67b6d0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4688 @@ +{ + "name": "firebase-functions", + "version": "3.11.0", + "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.1", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.6.1.tgz", + "integrity": "sha512-L/ZnJRAq7F++utfuoTKX4CLBG5YR7tFO3PLzG1/oXXKEezJ0kRL3CMRoueBEmTCzVb/6SIs2Qlaw++uDgi5Xyg==", + "dev": true + }, + "@firebase/auth-interop-types": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.5.tgz", + "integrity": "sha512-88h74TMQ6wXChPA6h9Q3E1Jg6TkTHep2+k63OWg3s0ozyGVMeY+TTOti7PFPzq5RhszQPQOoCi59es4MaRvgCw==", + "dev": true + }, + "@firebase/component": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.1.19.tgz", + "integrity": "sha512-L0S3g8eqaerg8y0zox3oOHSTwn/FE8RbcRHiurnbESvDViZtP5S5WnhuAPd7FnFxa8ElWK0z1Tr3ikzWDv1xdQ==", + "dev": true, + "requires": { + "@firebase/util": "0.3.2", + "tslib": "^1.11.1" + } + }, + "@firebase/database": { + "version": "0.6.12", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.6.12.tgz", + "integrity": "sha512-OLUxp8TkXiML4X5LWM5IACsSDvo3fcf4mTbTe5RF+N6TRFv0Svzlet5OgGIa3ET1dQvNiisrMX7zzRa0OTLs7Q==", + "dev": true, + "requires": { + "@firebase/auth-interop-types": "0.1.5", + "@firebase/component": "0.1.19", + "@firebase/database-types": "0.5.2", + "@firebase/logger": "0.2.6", + "@firebase/util": "0.3.2", + "faye-websocket": "0.11.3", + "tslib": "^1.11.1" + } + }, + "@firebase/database-types": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.5.2.tgz", + "integrity": "sha512-ap2WQOS3LKmGuVFKUghFft7RxXTyZTDr0Xd8y2aqmWsbJVjgozi0huL/EUMgTjGFrATAjcf2A7aNs8AKKZ2a8g==", + "dev": true, + "requires": { + "@firebase/app-types": "0.6.1" + } + }, + "@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": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.3.2.tgz", + "integrity": "sha512-Dqs00++c8rwKky6KCKLLY2T1qYO4Q+X5t+lF7DInXDNF4ae1Oau35bkD+OpJ9u7l1pEv7KHowP6CUKuySCOc8g==", + "dev": true, + "requires": { + "tslib": "^1.11.1" + } + }, + "@google-cloud/common": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-2.4.0.tgz", + "integrity": "sha512-zWFjBS35eI9leAHhjfeOYlK5Plcuj/77EzstnrJIZbKgF/nkqjcQuGiMCpzCwOfPyUbz8ZaEOYgbHa759AKbjg==", + "dev": true, + "optional": true, + "requires": { + "@google-cloud/projectify": "^1.0.0", + "@google-cloud/promisify": "^1.0.0", + "arrify": "^2.0.0", + "duplexify": "^3.6.0", + "ent": "^2.2.0", + "extend": "^3.0.2", + "google-auth-library": "^5.5.0", + "retry-request": "^4.0.0", + "teeny-request": "^6.0.0" + } + }, + "@google-cloud/firestore": { + "version": "3.8.6", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-3.8.6.tgz", + "integrity": "sha512-ox80NbrM1MLJgvAAUd1quFLx/ie/nSjrk1PtscSicpoYDlKb9e6j7pHrVpbopBMyliyfNl3tLJWaDh+x+uCXqw==", + "dev": true, + "optional": true, + "requires": { + "deep-equal": "^2.0.0", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^1.15.3", + "readable-stream": "^3.4.0", + "through2": "^3.0.0" + } + }, + "@google-cloud/paginator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-2.0.3.tgz", + "integrity": "sha512-kp/pkb2p/p0d8/SKUu4mOq8+HGwF8NPzHWkj+VKrIPQPyMRw8deZtrO/OcSiy9C/7bpfU5Txah5ltUNfPkgEXg==", + "dev": true, + "optional": true, + "requires": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + } + }, + "@google-cloud/projectify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-1.0.4.tgz", + "integrity": "sha512-ZdzQUN02eRsmTKfBj9FDL0KNDIFNjBn/d6tHQmA/+FImH5DO6ZV8E7FzxMgAUiVAUq41RFAkb25p1oHOZ8psfg==", + "dev": true, + "optional": true + }, + "@google-cloud/promisify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-1.0.4.tgz", + "integrity": "sha512-VccZDcOql77obTnFh0TbNED/6ZbbmHDf8UMNnzO1d5g9V0Htfm4k5cllY8P1tJsRKC3zWYGRLaViiupcgVjBoQ==", + "dev": true, + "optional": true + }, + "@google-cloud/storage": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-4.7.0.tgz", + "integrity": "sha512-f0guAlbeg7Z0m3gKjCfBCu7FG9qS3M3oL5OQQxlvGoPtK7/qg3+W+KQV73O2/sbuS54n0Kh2mvT5K2FWzF5vVQ==", + "dev": true, + "optional": true, + "requires": { + "@google-cloud/common": "^2.1.1", + "@google-cloud/paginator": "^2.0.0", + "@google-cloud/promisify": "^1.0.0", + "arrify": "^2.0.0", + "compressible": "^2.0.12", + "concat-stream": "^2.0.0", + "date-and-time": "^0.13.0", + "duplexify": "^3.5.0", + "extend": "^3.0.2", + "gaxios": "^3.0.0", + "gcs-resumable-upload": "^2.2.4", + "hash-stream-validation": "^0.2.2", + "mime": "^2.2.0", + "mime-types": "^2.0.8", + "onetime": "^5.1.0", + "p-limit": "^2.2.0", + "pumpify": "^2.0.0", + "readable-stream": "^3.4.0", + "snakeize": "^0.1.0", + "stream-events": "^1.0.1", + "through2": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "gaxios": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-3.2.0.tgz", + "integrity": "sha512-+6WPeVzPvOshftpxJwRi2Ozez80tn/hdtOUag7+gajDHRJvAblKxTFSSMPtr2hmnLy7p0mvYz0rMXLBl8pSO7Q==", + "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" + } + }, + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "dev": true, + "optional": true + } + } + }, + "@grpc/grpc-js": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.0.5.tgz", + "integrity": "sha512-Hm+xOiqAhcpT9RYM8lc15dbQD7aQurM7ZU8ulmulepiPlN7iwBXXwP3vSBUimoFoApRqz7pSIisXU8pZaCB4og==", + "dev": true, + "optional": true, + "requires": { + "semver": "^6.2.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 + } + } + }, + "@grpc/proto-loader": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.5.tgz", + "integrity": "sha512-WwN9jVNdHRQoOBo9FDH7qU+mgfjPc8GygPYms3M+y3fbQLfnCe/Kv/E01t7JRgnrsOHH8euvSbed3mIalXhwqQ==", + "dev": true, + "optional": true, + "requires": { + "lodash.camelcase": "^4.3.0", + "protobufjs": "^6.8.6" + } + }, + "@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": "sha1-TIVzDlm5ofHzSQR9vyQpYDS7JzU=", + "dev": true, + "optional": true + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha1-fvN/DQEPsCitGtWXIuUG2SYoFcs=", + "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-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/fs-extra": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.1.tgz", + "integrity": "sha512-TcUlBem321DFQzBNuz8p0CLLKp0VvF/XH9E4KHNmgwyp4E3AfgI5cjiIVZWlbfThBop2qxFIh4+LeY6hVWWZ2w==", + "dev": true, + "optional": true, + "requires": { + "@types/node": "*" + } + }, + "@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": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" + }, + "@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.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.1.tgz", + "integrity": "sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg==", + "dev": true, + "optional": true, + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "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-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", + "dev": true, + "optional": true + }, + "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": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "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 + }, + "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 + }, + "available-typed-arrays": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", + "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", + "dev": true, + "optional": true, + "requires": { + "array-filter": "^1.0.0" + } + }, + "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.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "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.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==", + "dev": true, + "optional": 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" + } + }, + "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.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "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 + }, + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "optional": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "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": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "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": "0.13.1", + "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-0.13.1.tgz", + "integrity": "sha512-/Uge9DJAT+s+oAcDxtBhyR8+sKjUnZbYmyhbmWjTHNtX7B7oWD8YyYdeXcBRbwSj6hVvj+IQegJam7m7czhbFw==", + "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-equal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.3.tgz", + "integrity": "sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA==", + "dev": true, + "optional": true, + "requires": { + "es-abstract": "^1.17.5", + "es-get-iterator": "^1.1.0", + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.2", + "is-regex": "^1.0.5", + "isarray": "^2.0.5", + "object-is": "^1.1.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "regexp.prototype.flags": "^1.3.0", + "side-channel": "^1.0.2", + "which-boxed-primitive": "^1.0.1", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.2" + } + }, + "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": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "optional": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true, + "optional": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + } + } + }, + "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=" + }, + "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-get-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", + "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", + "dev": true, + "optional": true, + "requires": { + "es-abstract": "^1.17.4", + "has-symbols": "^1.0.1", + "is-arguments": "^1.0.4", + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + } + }, + "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" + } + }, + "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": "8.13.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-8.13.0.tgz", + "integrity": "sha512-krXj5ncWMJBhCpXSn9UFY6zmDWjFjqgx+1e9ATXKFYndEjmKtNBuJzqdrAdDh7aTUR7X6+0TPx4Hbc08kd0lwQ==", + "dev": true, + "requires": { + "@firebase/database": "^0.6.0", + "@google-cloud/firestore": "^3.0.0", + "@google-cloud/storage": "^4.1.2", + "@types/node": "^8.10.59", + "dicer": "^0.3.0", + "jsonwebtoken": "^8.5.1", + "node-forge": "^0.7.6" + } + }, + "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 + } + } + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true, + "optional": 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": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "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": "2.3.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.3.4.tgz", + "integrity": "sha512-US8UMj8C5pRnao3Zykc4AAVr+cffoNKRTg9Rsf2GiuZCW69vgJj38VK2PzlPuQU73FZ/nTk9/Av6/JGcE1N9vA==", + "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": "3.5.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.5.0.tgz", + "integrity": "sha512-ZQf+DLZ5aKcRpLzYUyBS3yo3N0JSa82lNDO8rj3nMSlovLcz2riKFBsYgDzeXcv75oo5eqB2lx+B14UvPoCRnA==", + "dev": true, + "optional": true, + "requires": { + "gaxios": "^2.1.0", + "json-bigint": "^0.3.0" + } + }, + "gcs-resumable-upload": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-2.3.3.tgz", + "integrity": "sha512-sf896I5CC/1AxeaGfSFg3vKMjUq/r+A3bscmVzZm10CElyRanN0XwPu/MxeIO4LSP+9uF6yKzXvNsaTsMXUG6Q==", + "dev": true, + "optional": true, + "requires": { + "abort-controller": "^3.0.0", + "configstore": "^5.0.0", + "gaxios": "^2.0.0", + "google-auth-library": "^5.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 + }, + "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": "5.10.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.1.tgz", + "integrity": "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg==", + "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": "^2.1.0", + "gcp-metadata": "^3.4.0", + "gtoken": "^4.1.0", + "jws": "^4.0.0", + "lru-cache": "^5.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": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "optional": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "optional": true + } + } + }, + "google-gax": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-1.15.3.tgz", + "integrity": "sha512-3JKJCRumNm3x2EksUTw4P1Rad43FTpqrtW9jzpf3xSMYXx+ogaqTM1vGo7VixHB4xkAyATXVIa3OcNSh8H9zsQ==", + "dev": true, + "optional": true, + "requires": { + "@grpc/grpc-js": "~1.0.3", + "@grpc/proto-loader": "^0.5.1", + "@types/fs-extra": "^8.0.1", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^3.6.0", + "google-auth-library": "^5.0.0", + "is-stream-ended": "^0.1.4", + "lodash.at": "^4.6.0", + "lodash.has": "^4.5.2", + "node-fetch": "^2.6.0", + "protobufjs": "^6.8.9", + "retry-request": "^4.0.0", + "semver": "^6.0.0", + "walkdir": "^0.4.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 + } + } + }, + "google-p12-pem": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.4.tgz", + "integrity": "sha512-S4blHBQWZRnEW44OcR7TL9WR+QCqByRvhNDZ/uuQfpxywfupikf/miba8js1jZi6ZOGv5slgSuoshCWh6EMDzg==", + "dev": true, + "optional": true, + "requires": { + "node-forge": "^0.9.0" + }, + "dependencies": { + "node-forge": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.2.tgz", + "integrity": "sha512-naKSScof4Wn+aoHU6HBsifh92Zeicm1GDQKd1vp3Y/kOi8ub0DozCa9KpvYNCXslFHYRmLNiqRopGdTGwNLpNw==", + "dev": true, + "optional": true + } + } + }, + "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": "4.1.4", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.4.tgz", + "integrity": "sha512-VxirzD0SWoFUo5p8RDP8Jt2AGyOmyYcT/pOUgDKJCK+iSw0TMqwrVfY37RXTNmoKwrzmDHSk0GMT9FsgVmnVSA==", + "dev": true, + "optional": true, + "requires": { + "gaxios": "^2.1.0", + "google-p12-pem": "^2.0.0", + "jws": "^4.0.0", + "mime": "^2.2.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" + } + }, + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "dev": true, + "optional": true + } + } + }, + "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 + }, + "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.2.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.2.0.tgz", + "integrity": "sha512-OryzPiqqNCfO/wtFo619W+nPYALM6u7iCQkum4bqRmmlcTikOkmlL06i009QelynBPAlNByTQU6cBB2cOBQtCw==", + "dev": true + }, + "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.2", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz", + "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==", + "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.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "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.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "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-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.0.tgz", + "integrity": "sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==", + "dev": true, + "optional": true + }, + "is-boolean-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz", + "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==", + "dev": true, + "optional": 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-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", + "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", + "dev": true, + "optional": 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-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", + "dev": true, + "optional": 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-set": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", + "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", + "dev": true, + "optional": true + }, + "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": "sha1-9QIk6V4GvODjVtRApIJ801smfto=", + "dev": true, + "optional": true + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true, + "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-typed-array": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz", + "integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==", + "dev": true, + "optional": true, + "requires": { + "available-typed-arrays": "^1.0.0", + "es-abstract": "^1.17.4", + "foreach": "^2.0.5", + "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 + }, + "is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "dev": true, + "optional": true + }, + "is-weakset": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", + "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==", + "dev": true, + "optional": true + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "optional": 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 + }, + "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": "0.3.1", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.1.tgz", + "integrity": "sha512-DGWnSzmusIreWlEupsUelHrhwmPPE+FiQvg+drKfk2p+bdEYa5mp4PJ8JsCWqae0M2jQNb0HPvnwvf1qOTThzQ==", + "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": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "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" + } + }, + "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" + } + }, + "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.at": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.at/-/lodash.at-4.6.0.tgz", + "integrity": "sha1-k83OZk8KGZTqM9181A4jr9EbD/g=", + "dev": true, + "optional": true + }, + "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.has": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz", + "integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=", + "dev": true, + "optional": 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": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg=", + "dev": true, + "optional": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "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 + }, + "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.7.6", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz", + "integrity": "sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==", + "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": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "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-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 + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "optional": 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.10.1", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.1.tgz", + "integrity": "sha512-pb8kTchL+1Ceg4lFd5XUpK8PdWacbvV5SK2ULH2ebrYtl4GjJmS24m6CKME67jzV53tbJxHlnNOSqQHbTsR9JQ==", + "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": "13.13.20", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.20.tgz", + "integrity": "sha512-1kx55tU3AvGX2Cjk2W4GMBxbgIz892V+X10S2gUreIAq8qCWgaQH+tZBOWc0bi2BKFhQt+CX0BTx28V9QPNa+A==", + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "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-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.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "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" + } + }, + "side-channel": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.3.tgz", + "integrity": "sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==", + "dev": true, + "optional": true, + "requires": { + "es-abstract": "^1.18.0-next.0", + "object-inspect": "^1.8.0" + }, + "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, + "optional": 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" + } + } + } + }, + "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.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "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": "6.0.3", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-6.0.3.tgz", + "integrity": "sha512-TZG/dfd2r6yeji19es1cUIwAlVD8y+/svB1kAC2Y0bjEyysrfbO8EZvJBRwIE6WkwmUoB7uvWLwTIhJbMXZ1Dw==", + "dev": true, + "optional": true, + "requires": { + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.2.0", + "stream-events": "^1.0.5", + "uuid": "^7.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" + } + }, + "through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "optional": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + }, + "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, + "optional": true + } + } + }, + "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": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true, + "optional": true + }, + "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": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", + "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" + } + }, + "walkdir": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz", + "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==", + "dev": true, + "optional": true + }, + "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-boxed-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz", + "integrity": "sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==", + "dev": true, + "optional": true, + "requires": { + "is-bigint": "^1.0.0", + "is-boolean-object": "^1.0.0", + "is-number-object": "^1.0.3", + "is-string": "^1.0.4", + "is-symbol": "^1.0.2" + } + }, + "which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dev": true, + "optional": true, + "requires": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + } + }, + "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 + }, + "which-typed-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.2.tgz", + "integrity": "sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==", + "dev": true, + "optional": true, + "requires": { + "available-typed-arrays": "^1.0.2", + "es-abstract": "^1.17.5", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.3" + } + }, + "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 + } + } +} diff --git a/package.json b/package.json index 4eb5986d8..72a217fb7 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,6 @@ "chai-as-promised": "^7.1.1", "child-process-promise": "^2.2.1", "firebase-admin": "^8.2.0", - "istanbul": "^0.4.5", "js-yaml": "^3.13.1", "jsdom": "^16.2.1", "jsonwebtoken": "^8.5.1", @@ -73,7 +72,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" }, From 93047d5046e589f6d0ae4d321e546c0e85a769a4 Mon Sep 17 00:00:00 2001 From: joehan Date: Mon, 30 Nov 2020 13:49:32 -0800 Subject: [PATCH 071/529] Switches to Github Actions from travis, and adds CHANGELOG (#818) * Switches to Github Actions over travis, and adds CHNAGELOG entries for recent PRs * formats --- .github/workflows/test.yaml | 35 +++++++++++++++++++++++++++++++++++ .travis.yml | 14 -------------- CHANGELOG.md | 2 ++ 3 files changed, 37 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/test.yaml delete mode 100644 .travis.yml diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 000000000..9a31678c2 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,35 @@ +name: CI Tests + +on: + - pull_request + - push + +env: + CI: true + +jobs: + unit: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: + - 8.x + - 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/.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 e69de29bb..e0c94613b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,2 @@ +- Adds `4GB` as a `memory` option for `runWith()`. +- Adds support for choosing `ingressSettings` via `runWith()`. From 3932876901d977d7957b57a69b6f2b0b9b2b94a5 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Mon, 30 Nov 2020 21:54:33 +0000 Subject: [PATCH 072/529] 3.12.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 17b67b6d0..2ff1ff5cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.11.0", + "version": "3.12.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 72a217fb7..a27384f60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.11.0", + "version": "3.12.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 9055e0fc9f3bd8fb72c4cc8702f8ea1a1051ee70 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Mon, 30 Nov 2020 21:54:38 +0000 Subject: [PATCH 073/529] [firebase-release] Removed change log and reset repo after 3.12.0 release --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0c94613b..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +0,0 @@ -- Adds `4GB` as a `memory` option for `runWith()`. -- Adds support for choosing `ingressSettings` via `runWith()`. From 07139c8d9cdaff5dccbe790fd3ebc66a717ae8ac Mon Sep 17 00:00:00 2001 From: Egor Miasnikov Date: Tue, 8 Dec 2020 01:05:26 +0300 Subject: [PATCH 074/529] Add support for service account in `functions.runWith` (#770) * Add support for service account in `functions.runWith` * Add email validation and email generation by project id * Changing to serviceAccount, and adding default as an option * adds a test case for default * refactoring to checxk for @ at the end of service account, and throw erros earlier when service account is set to something invalid * gets rid of repeated @ for generated service account emails Co-authored-by: joehan --- spec/function-builder.spec.ts | 48 +++++++++++++++++++++++++++++++++++ src/cloud-functions.ts | 20 +++++++++++++++ src/function-builder.ts | 19 +++++++++++--- src/function-configuration.ts | 5 ++++ 4 files changed, 89 insertions(+), 3 deletions(-) diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts index eec531fdf..c31a82d4a 100644 --- a/spec/function-builder.spec.ts +++ b/spec/function-builder.spec.ts @@ -238,4 +238,52 @@ 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(); + }); }); diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 109a1ad05..50d674022 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -272,6 +272,7 @@ export interface TriggerAnnotated { timeout?: string; vpcConnector?: string; vpcConnectorEgressSettings?: string; + serviceAccountEmail?: string; }; } @@ -525,5 +526,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 9b5f5660b..7411f72e2 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -99,6 +99,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; } @@ -139,9 +149,12 @@ export function region( * 0 to 540. * 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 the same project and region - * 5. `vpcConnectorEgressSettings`: when a `vpcConnector` is set, control which - * egress traffic is sent through the `vpcConnector`. + * 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. */ diff --git a/src/function-configuration.ts b/src/function-configuration.ts index 10098a745..f5a325b41 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -122,6 +122,11 @@ export interface RuntimeOptions { */ vpcConnectorEgressSettings?: typeof VPC_EGRESS_SETTINGS_OPTIONS[number]; + /** + * Specific service account for the function to run as + */ + serviceAccount?: 'default' | string; + /** * Ingress settings */ From 07ca97f68bcf3e724a81c4da62807a51e5712619 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Dec 2020 14:14:49 -0800 Subject: [PATCH 075/529] Bump highlight.js from 10.2.0 to 10.4.1 (#823) Bumps [highlight.js](https://github.com/highlightjs/highlight.js) from 10.2.0 to 10.4.1. - [Release notes](https://github.com/highlightjs/highlight.js/releases) - [Changelog](https://github.com/highlightjs/highlight.js/blob/master/CHANGES.md) - [Commits](https://github.com/highlightjs/highlight.js/compare/10.2.0...10.4.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: joehan --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2ff1ff5cd..167df1e2b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1919,9 +1919,9 @@ "dev": true }, "highlight.js": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.2.0.tgz", - "integrity": "sha512-OryzPiqqNCfO/wtFo619W+nPYALM6u7iCQkum4bqRmmlcTikOkmlL06i009QelynBPAlNByTQU6cBB2cOBQtCw==", + "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 }, "html-encoding-sniffer": { From df592717075a9bbb1a17dd5e55c77a076b328cf9 Mon Sep 17 00:00:00 2001 From: joehan Date: Mon, 7 Dec 2020 15:43:39 -0800 Subject: [PATCH 076/529] Adds changelog entries for v3.13.0 (#824) * adds changelog entries for 3.13.0 * formats * Update CHANGELOG.md Co-authored-by: Sam Stern * Update CHANGELOG.md Co-authored-by: Sam Stern * Update CHANGELOG.md Co-authored-by: Sam Stern * formats * formats Co-authored-by: Sam Stern --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..c9cb1adff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,18 @@ +- Adds `serviceAccount` option to `runtimeOptions` to specify which service account Cloud Function should use at runtime. For example: + +``` +const functions = require('firebase-functions'); + +exports.myFunction = functions.runWith({ + serviceAccount: 'test-sa@project.iam.gserviceaccount.com' + // OR + // serviceAcount: 'test-sa@" + // OR + // serviceAccount: 'default' + }) + +``` + +Requires firebase-tools@8.18.0 or later. Thanks @egor-miasnikov! + +- Upgrades `highlight.js` to `10.4.1` to fix a vulnerability. From 21d2c0bb458bd2aa6628f8cf4e2648d51d8a0f13 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Mon, 7 Dec 2020 23:48:48 +0000 Subject: [PATCH 077/529] 3.13.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 167df1e2b..7a1981a69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.12.0", + "version": "3.13.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index a27384f60..8f4bb09c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.12.0", + "version": "3.13.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From c69c9bc083a2ef55a3ea152839d6027b4bb20352 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Mon, 7 Dec 2020 23:48:54 +0000 Subject: [PATCH 078/529] [firebase-release] Removed change log and reset repo after 3.13.0 release --- CHANGELOG.md | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9cb1adff..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +0,0 @@ -- Adds `serviceAccount` option to `runtimeOptions` to specify which service account Cloud Function should use at runtime. For example: - -``` -const functions = require('firebase-functions'); - -exports.myFunction = functions.runWith({ - serviceAccount: 'test-sa@project.iam.gserviceaccount.com' - // OR - // serviceAcount: 'test-sa@" - // OR - // serviceAccount: 'default' - }) - -``` - -Requires firebase-tools@8.18.0 or later. Thanks @egor-miasnikov! - -- Upgrades `highlight.js` to `10.4.1` to fix a vulnerability. From 9b8fe654d090cb6f4721c9725aa0f0707afc7433 Mon Sep 17 00:00:00 2001 From: Sam Stern Date: Mon, 11 Jan 2021 17:37:14 +0000 Subject: [PATCH 079/529] Fix emulated database URL parse issue (#838) --- package.json | 1 + spec/providers/database.spec.ts | 11 +++++++++++ src/providers/database.ts | 11 +++++++++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8f4bb09c5..9b2c3cd8c 100644 --- a/package.json +++ b/package.json @@ -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 ", 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/src/providers/database.ts b/src/providers/database.ts index 2aa50b55a..0e4393d05 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -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]; + } } /** From 2f72c33ab47a57dce7175eb4449dca00073d46cc Mon Sep 17 00:00:00 2001 From: Leon Radley Date: Wed, 13 Jan 2021 20:46:14 +0100 Subject: [PATCH 080/529] Add support for 4GB memory option (#842) * Add support for 4GB memory option The 4GB memory option was not added everywhere. Fixes #834 * Added test for 4GB memory option Co-authored-by: Leon Radley Co-authored-by: joehan --- spec/function-builder.spec.ts | 12 ++++++++++++ src/cloud-functions.ts | 1 + src/function-builder.ts | 4 ++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts index c31a82d4a..209d63d60 100644 --- a/spec/function-builder.spec.ts +++ b/spec/function-builder.spec.ts @@ -286,4 +286,16 @@ describe('FunctionBuilder', () => { }); }).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/src/cloud-functions.ts b/src/cloud-functions.ts index 50d674022..aef850da4 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -507,6 +507,7 @@ export function optionsToTrigger(options: DeploymentOptions) { '512MB': 512, '1GB': 1024, '2GB': 2048, + '4GB': 4096, }; trigger.availableMemoryMb = _.get(memoryLookup, options.memory); } diff --git a/src/function-builder.ts b/src/function-builder.ts index 7411f72e2..8d06b2142 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -144,7 +144,7 @@ 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'. + * 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 @@ -188,7 +188,7 @@ export class FunctionBuilder { * 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'. + * 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 From f508951c651f755734dea6c8202c8a7ced034e2a Mon Sep 17 00:00:00 2001 From: huangjeff5 <64040981+huangjeff5@users.noreply.github.com> Date: Fri, 15 Jan 2021 10:31:54 -0800 Subject: [PATCH 081/529] Remove circular dependencies when logging. Fixes #737 (#844) * Remove circular dependencies when logging. Fixes #737 * Fix bug in binding the output logger * modify removeCircular to return a copy of the original object, rather than mutate * Modify removeCircular to return a new object, rather than mutating in place Co-authored-by: Michael Bleigh --- spec/logger.spec.ts | 84 ++++++++++++++++++++++++++++++--------------- src/logger.ts | 35 +++++++++++++++++-- 2 files changed, 89 insertions(+), 30 deletions(-) diff --git a/spec/logger.spec.ts b/spec/logger.spec.ts index c6859b60a..dda16087c 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; + let stdoutWrite = process.stdout.write.bind(process.stdout); + let 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,13 +69,46 @@ 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]'] }, + }); + }); + for (const severity of ['DEBUG', 'INFO', 'NOTICE']) { it(`should output ${severity} severity to stdout`, () => { let entry: logger.LogEntry = { @@ -87,7 +117,6 @@ describe(`logger (${ }; logger.write(entry); expectStdout(entry); - sandbox.restore(); // to avoid swallowing test runner output }); } @@ -105,7 +134,6 @@ describe(`logger (${ }; logger.write(entry); expectStderr(entry); - sandbox.restore(); // to avoid swallowing test runner output }); } }); diff --git a/src/logger.ts b/src/logger.ts index 7958ca457..d6c0d2220 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -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); } From e920b01d1d2f0ba4fe528936196537e90d48ee4a Mon Sep 17 00:00:00 2001 From: joehan Date: Fri, 15 Jan 2021 10:41:17 -0800 Subject: [PATCH 082/529] Add changelog entry for #842 (#845) * add changelog for memory options * formats * Update CHANGELOG.md * Update CHANGELOG.md Co-authored-by: huangjeff5 <64040981+huangjeff5@users.noreply.github.com> --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..dba1ded30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,2 @@ +- Fixes a bug that prevented Functions from being deployed with `availableMemoryMb` set to `4GB`. +- Fixes bug where `functions.logger.log` crashes function if circular dependencies are passed in From daeda1dd38904c6c4c6c1dc2b09d3c2a275ccc4e Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 15 Jan 2021 18:47:18 +0000 Subject: [PATCH 083/529] 3.13.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7a1981a69..f1e9dc457 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.13.0", + "version": "3.13.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 9b2c3cd8c..f6f9ae1c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.13.0", + "version": "3.13.1", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 61dc5a16cab32a9e671d6fcee383ef1af0d25420 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 15 Jan 2021 18:47:23 +0000 Subject: [PATCH 084/529] [firebase-release] Removed change log and reset repo after 3.13.1 release --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dba1ded30..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +0,0 @@ -- Fixes a bug that prevented Functions from being deployed with `availableMemoryMb` set to `4GB`. -- Fixes bug where `functions.logger.log` crashes function if circular dependencies are passed in From 5f64797555e150ae8de93445edb304e6ea81fda2 Mon Sep 17 00:00:00 2001 From: takaaa220 Date: Wed, 20 Jan 2021 07:41:55 +0900 Subject: [PATCH 085/529] Fix IngressSettings (#827) Co-authored-by: joehan --- spec/function-builder.spec.ts | 21 +++++++++++++++++++++ src/cloud-functions.ts | 5 +++++ src/function-builder.ts | 12 ++++++++++++ 3 files changed, 38 insertions(+) diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts index 209d63d60..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({ diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index aef850da4..126997c1d 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -273,6 +273,7 @@ export interface TriggerAnnotated { vpcConnector?: string; vpcConnectorEgressSettings?: string; serviceAccountEmail?: string; + ingressSettings?: string; }; } @@ -519,6 +520,10 @@ export function optionsToTrigger(options: DeploymentOptions) { trigger.maxInstances = options.maxInstances; } + if (options.ingressSettings) { + trigger.ingressSettings = options.ingressSettings; + } + if (options.vpcConnector) { trigger.vpcConnector = options.vpcConnector; } diff --git a/src/function-builder.ts b/src/function-builder.ts index 8d06b2142..6f50e6bde 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -31,6 +31,7 @@ import { SUPPORTED_REGIONS, VALID_MEMORY_OPTIONS, VPC_EGRESS_SETTINGS_OPTIONS, + INGRESS_SETTINGS_OPTIONS, } from './function-configuration'; import * as analytics from './providers/analytics'; import * as auth from './providers/auth'; @@ -68,6 +69,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( From 88691699d875f47ea7dc5db382f3624c68dc3148 Mon Sep 17 00:00:00 2001 From: Reza Rahmati Date: Tue, 19 Jan 2021 17:45:42 -0500 Subject: [PATCH 086/529] Fixing issue reading env.DATABASE_URL and process.env.STORAGE_BUCKET_URL (#840) * #829 fixing issue reading env.DATABASE_URL and env.STORAGE_BUCKET_URL * #829 revert space changes Co-authored-by: joehan --- src/setup.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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, }); From 0e2e95c8540a6e7e136e5bf718ccacaf319f8587 Mon Sep 17 00:00:00 2001 From: joehan Date: Thu, 4 Feb 2021 06:52:32 -0800 Subject: [PATCH 087/529] Adds changelog for #829 and #827. (#848) --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..06d7cfb4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,2 @@ +- Fixes issue where DATABASE_URL and STORAGE_BUCKET_URL could not be set to undefined. (#829) +- Fixes a bug where ingressSettings could not be set. (#827) From 2d81a6be9e31b610f9d66c7ab7f1172762698ed7 Mon Sep 17 00:00:00 2001 From: Sam Stern Date: Thu, 4 Feb 2021 16:56:10 +0000 Subject: [PATCH 088/529] Update issue templates (#857) --- .github/ISSUE_TEMPLATE/---feature-request.md | 17 ----------------- .github/ISSUE_TEMPLATE/---report-a-bug.md | 2 +- .github/ISSUE_TEMPLATE/config.yml | 8 ++++++++ 3 files changed, 9 insertions(+), 18 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/---feature-request.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml 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. From 0c382ed0f7a94f3cc270b13111d635fe4caccd79 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Mon, 22 Feb 2021 18:01:36 +0000 Subject: [PATCH 089/529] 3.13.2 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f1e9dc457..9f26affdd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.13.1", + "version": "3.13.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index f6f9ae1c6..11a4dfb39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.13.1", + "version": "3.13.2", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From fc4788e61012306eda385428f25291094fce624d Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Mon, 22 Feb 2021 18:01:44 +0000 Subject: [PATCH 090/529] [firebase-release] Removed change log and reset repo after 3.13.2 release --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06d7cfb4c..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +0,0 @@ -- Fixes issue where DATABASE_URL and STORAGE_BUCKET_URL could not be set to undefined. (#829) -- Fixes a bug where ingressSettings could not be set. (#827) From e2c9475372d6eab47d038f29ebc386ec18029784 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Mon, 1 Mar 2021 13:28:43 -0800 Subject: [PATCH 091/529] Remove crashlyitcs (#866) --- package-lock.json | 18 +-- spec/providers/crashlytics.spec.ts | 157 ----------------------- src/function-builder.ts | 11 -- src/handler-builder.ts | 36 ------ src/index.ts | 2 - src/providers/crashlytics.ts | 197 ----------------------------- 6 files changed, 9 insertions(+), 412 deletions(-) delete mode 100644 spec/providers/crashlytics.spec.ts delete mode 100644 src/providers/crashlytics.ts diff --git a/package-lock.json b/package-lock.json index 9f26affdd..f1f20b89a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -490,7 +490,7 @@ "@types/range-parser": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", - "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" + "integrity": "sha1-fuMwunyq+5gJC+zoal7kQRWQTCw=" }, "@types/serve-static": { "version": "1.13.5", @@ -665,7 +665,7 @@ "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=", "dev": true, "requires": { "safer-buffer": "~2.1.0" @@ -1042,7 +1042,7 @@ "cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "integrity": "sha1-6sEdpRWS3Ya58G9uesKTs9+HXSk=", "requires": { "object-assign": "^4", "vary": "^1" @@ -1581,7 +1581,7 @@ "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -2356,7 +2356,7 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", "dev": true }, "json-stringify-safe": { @@ -2565,7 +2565,7 @@ "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "integrity": "sha1-i75Q6oW+1ZvJ4z3KuCNe6bz0Q80=", "dev": true, "requires": { "pseudomap": "^1.0.2", @@ -3012,7 +3012,7 @@ "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=", "dev": true }, "object-assign": { @@ -3327,7 +3327,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", "dev": true }, "qs": { @@ -3745,7 +3745,7 @@ "stream-events": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", - "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "integrity": "sha1-u8iY7E3zOkkC2JIzPUfam/HEBtU=", "dev": true, "optional": true, "requires": { 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/src/function-builder.ts b/src/function-builder.ts index 6f50e6bde..9d016d817 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -35,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'; @@ -311,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/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..9d49414e8 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'; @@ -43,7 +42,6 @@ export { analytics, app, auth, - crashlytics, database, firestore, handler, 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; -} From f2435b1eefeaba80c0bbd1578d6dbd3650630996 Mon Sep 17 00:00:00 2001 From: joehan Date: Tue, 2 Mar 2021 14:30:10 -0800 Subject: [PATCH 092/529] Cleaning up crashlytics trigger (#868) --- src/cloud-functions.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 126997c1d..272920c20 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -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` From ee793c77f1931d44adb3500bc2eed37d4a674370 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 3 Mar 2021 13:44:43 -0800 Subject: [PATCH 093/529] Update integration test so that it works again. (#869) * Updated node versions under test * Updated node dependencies to modern versions * Removed lodash dependency * Minor updates to TypeScript annotations/style * Fix URL and authentication when invoking a cloud schedule * Fix Cloud Schedule test to not finish until tests are complete --- integration_test/firestore.rules | 2 + integration_test/functions/src/https-tests.ts | 5 +- integration_test/functions/src/index.ts | 234 +++++++++--------- .../functions/src/pubsub-tests.ts | 25 +- .../functions/src/testLab-tests.ts | 3 +- .../functions/src/testLab-utils.ts | 3 +- integration_test/functions/src/testing.ts | 64 +++-- ...ckage.node8.json => package.json.template} | 12 +- integration_test/package.node10.json | 23 -- integration_test/run_tests.sh | 39 ++- 10 files changed, 202 insertions(+), 208 deletions(-) rename integration_test/{package.node8.json => package.json.template} (55%) delete mode 100644 integration_test/package.node10.json 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/https-tests.ts b/integration_test/functions/src/https-tests.ts index 6af0f9fac..18972bd47 100644 --- a/integration_test/functions/src/https-tests.ts +++ b/integration_test/functions/src/https-tests.ts @@ -1,13 +1,12 @@ import * as functions from 'firebase-functions'; -import * as _ from 'lodash'; import { expectEq, TestSuite } from './testing'; 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 2ac16e5fa..0a980aa06 100644 --- a/integration_test/functions/src/index.ts +++ b/integration_test/functions/src/index.ts @@ -3,6 +3,7 @@ import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; import * as fs from 'fs'; import * as https from 'https'; +import { PubSub } from '@google-cloud/pubsub'; export * from './pubsub-tests'; export * from './database-tests'; @@ -18,6 +19,7 @@ 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; @@ -37,26 +39,42 @@ 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(); }); @@ -67,14 +85,13 @@ export const integrationTests: any = functions .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() @@ -83,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 e2a3f1bd0..a21c2011a 100644 --- a/integration_test/functions/src/pubsub-tests.ts +++ b/integration_test/functions/src/pubsub-tests.ts @@ -66,20 +66,15 @@ 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/testLab-tests.ts b/integration_test/functions/src/testLab-tests.ts index cf4b2f062..586c3a0cb 100644 --- a/integration_test/functions/src/testLab-tests.ts +++ b/integration_test/functions/src/testLab-tests.ts @@ -1,5 +1,4 @@ import * as functions from 'firebase-functions'; -import * as _ from 'lodash'; import { TestSuite, expectEq } from './testing'; import TestMatrix = functions.testLab.TestMatrix; const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1'; @@ -24,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..6f86b0a1f 100644 --- a/integration_test/functions/src/testLab-utils.ts +++ b/integration_test/functions/src/testLab-utils.ts @@ -1,7 +1,6 @@ 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 && diff --git a/integration_test/functions/src/testing.ts b/integration_test/functions/src/testing.ts index 43d3e69d5..228203d31 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 (let 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 ba567536a..100e24a32 100755 --- a/integration_test/run_tests.sh +++ b/integration_test/run_tests.sh @@ -58,21 +58,16 @@ function unset_region { fi } -function pick_node8 { +# 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.node8.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 - rm -f functions/package.json-e -} - -function pick_node10 { - cd "${DIR}" - cp package.node10.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 + 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 } @@ -143,19 +138,13 @@ build_sdk delete_all_functions set_region -# 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 +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 From d7d455a40f1a0f3a25de08e85175e27ee7e17f7c Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Fri, 5 Mar 2021 13:30:52 -0800 Subject: [PATCH 094/529] Stop running Node 8 tests in GitHub (#870) --- .github/workflows/test.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 9a31678c2..c56d65e12 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -13,7 +13,6 @@ jobs: strategy: matrix: node-version: - - 8.x - 10.x - 12.x - 14.x From 92916aa42b8b8e85fe7416ee0795744206ef9afa Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 17 Mar 2021 10:05:33 -0700 Subject: [PATCH 095/529] Add tests to verify logger does not alter its parameters (#873) --- spec/logger.spec.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/spec/logger.spec.ts b/spec/logger.spec.ts index dda16087c..48258ffac 100644 --- a/spec/logger.spec.ts +++ b/spec/logger.spec.ts @@ -109,6 +109,22 @@ describe(`logger (${ }); }); + 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 = { From a4019540dc0ced5abadb0a3c5ddaf21f7881f79e Mon Sep 17 00:00:00 2001 From: joehan Date: Fri, 19 Mar 2021 10:43:32 -0700 Subject: [PATCH 096/529] Remove crashlytics from toc.yaml, and dont try to delete css.map files if they dont exist (#871) --- docgen/content-sources/toc.yaml | 12 ------------ docgen/generate-docs.js | 4 +++- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/docgen/content-sources/toc.yaml b/docgen/content-sources/toc.yaml index 5758eb74e..e970fc80c 100644 --- a/docgen/content-sources/toc.yaml +++ b/docgen/content-sources/toc.yaml @@ -69,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: diff --git a/docgen/generate-docs.js b/docgen/generate-docs.js index 01275ae2d..1ce7ff910 100644 --- a/docgen/generate-docs.js +++ b/docgen/generate-docs.js @@ -323,7 +323,9 @@ Promise.all([ // 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`); + if (fs.existsSync(`${docPath}/assets/css/main.css.map`)) { + 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. From 98bf467aa436f7e27969a8ad3887abe0345f66d3 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Tue, 23 Mar 2021 10:34:24 -0700 Subject: [PATCH 097/529] Update generate-docs to modern JS (#874) Update generate-docs to modern JS. Fixes a few places where promises were left dangling. --- docgen/generate-docs.js | 313 +++++++++++++++++++--------------------- 1 file changed, 145 insertions(+), 168 deletions(-) diff --git a/docgen/generate-docs.js b/docgen/generate-docs.js index 1ce7ff910..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,71 +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. - if (fs.existsSync(`${docPath}/assets/css/main.css.map`)) { - 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); + } +} +})(); + From 29a55af955450d7758ddaa0cf3871836a2849c13 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Tue, 4 May 2021 18:46:29 -0700 Subject: [PATCH 098/529] Updating runWith enums (#884) Updating runWith enums --- CHANGELOG.md | 3 +++ src/cloud-functions.ts | 9 +++++++-- src/function-configuration.ts | 8 ++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..adc2430a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +- Functions may now be deployed with 8GB RAM +- Functions may now be deployed to europe-central2 (Warsaw) +- Functions may now reserve a minimum number of instances (updated CLI required) diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 272920c20..500e27144 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -203,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); } @@ -220,7 +220,7 @@ export namespace Change { after: any, fieldMask: string ) { - const before = _.assign({}, after); + const before = { ...after }; const masks = fieldMask.split(','); masks.forEach((mask) => { @@ -506,6 +506,7 @@ export function optionsToTrigger(options: DeploymentOptions) { '1GB': 1024, '2GB': 2048, '4GB': 4096, + '8GB': 8192, }; trigger.availableMemoryMb = _.get(memoryLookup, options.memory); } @@ -513,6 +514,10 @@ export function optionsToTrigger(options: DeploymentOptions) { trigger.schedule = options.schedule; } + if (options.minInstances) { + trigger.minInstances = options.minInstances; + } + if (options.maxInstances) { trigger.maxInstances = options.maxInstances; } diff --git a/src/function-configuration.ts b/src/function-configuration.ts index f5a325b41..8b2b28b81 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', @@ -43,6 +44,7 @@ export const VALID_MEMORY_OPTIONS = [ '1GB', '2GB', '4GB', + '8GB', ] as const; /** @@ -107,6 +109,12 @@ export interface RuntimeOptions { */ timeoutSeconds?: number; + /** + * Min number of actual instances allowed to be running in parallel + * Instances will be billed while idle. + */ + minInstances?: number; + /** * Max number of actual instances allowed to be running in parallel */ From 09120b64a931d41fc500f2c88e3cff0a5a72bee2 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Tue, 11 May 2021 20:16:37 -0700 Subject: [PATCH 099/529] Redactions (#886) --- CHANGELOG.md | 1 - src/function-configuration.ts | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index adc2430a1..d952d8984 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,2 @@ - Functions may now be deployed with 8GB RAM - Functions may now be deployed to europe-central2 (Warsaw) -- Functions may now reserve a minimum number of instances (updated CLI required) diff --git a/src/function-configuration.ts b/src/function-configuration.ts index 8b2b28b81..2d9925ac5 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -112,6 +112,7 @@ export interface RuntimeOptions { /** * Min number of actual instances allowed to be running in parallel * Instances will be billed while idle. + * @hidden */ minInstances?: number; From c29a8a518aba91aabe9bd02dd6d3a72718033ae7 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 12 May 2021 09:49:52 -0700 Subject: [PATCH 100/529] Support verification of AppCheck token in Callable Functions (#885) Callable Functions will now verify the AppCheck token included in the X-Firebase-AppCheck request header. Similar to auth, Callable Function will return 401 Unauthorized if the AppCheck token is invalid. --- CHANGELOG.md | 1 + package-lock.json | 1197 +++++++++++++++-------------- package.json | 3 +- spec/fixtures/credential/jwk.json | 14 + spec/fixtures/mockrequest.ts | 51 +- spec/providers/https.spec.ts | 54 ++ src/providers/https.ts | 134 +++- tsconfig.release.json | 4 +- 8 files changed, 851 insertions(+), 607 deletions(-) create mode 100644 spec/fixtures/credential/jwk.json diff --git a/CHANGELOG.md b/CHANGELOG.md index d952d8984..4ee96fbef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,3 @@ - Functions may now be deployed with 8GB RAM - Functions may now be deployed to europe-central2 (Warsaw) +- Add support for validating AppCheck tokens for Callable Functions diff --git a/package-lock.json b/package-lock.json index f1f20b89a..8a4fd6be8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,49 +31,65 @@ } }, "@firebase/app-types": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.6.1.tgz", - "integrity": "sha512-L/ZnJRAq7F++utfuoTKX4CLBG5YR7tFO3PLzG1/oXXKEezJ0kRL3CMRoueBEmTCzVb/6SIs2Qlaw++uDgi5Xyg==", + "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.5", - "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.5.tgz", - "integrity": "sha512-88h74TMQ6wXChPA6h9Q3E1Jg6TkTHep2+k63OWg3s0ozyGVMeY+TTOti7PFPzq5RhszQPQOoCi59es4MaRvgCw==", + "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.1.19", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.1.19.tgz", - "integrity": "sha512-L0S3g8eqaerg8y0zox3oOHSTwn/FE8RbcRHiurnbESvDViZtP5S5WnhuAPd7FnFxa8ElWK0z1Tr3ikzWDv1xdQ==", + "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": "0.3.2", - "tslib": "^1.11.1" + "@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.6.12", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.6.12.tgz", - "integrity": "sha512-OLUxp8TkXiML4X5LWM5IACsSDvo3fcf4mTbTe5RF+N6TRFv0Svzlet5OgGIa3ET1dQvNiisrMX7zzRa0OTLs7Q==", + "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.5", - "@firebase/component": "0.1.19", - "@firebase/database-types": "0.5.2", + "@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": "0.3.2", + "@firebase/util": "1.1.0", "faye-websocket": "0.11.3", - "tslib": "^1.11.1" + "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.5.2", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.5.2.tgz", - "integrity": "sha512-ap2WQOS3LKmGuVFKUghFft7RxXTyZTDr0Xd8y2aqmWsbJVjgozi0huL/EUMgTjGFrATAjcf2A7aNs8AKKZ2a8g==", + "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.1" + "@firebase/app-types": "0.6.2" } }, "@firebase/logger": { @@ -83,50 +99,57 @@ "dev": true }, "@firebase/util": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.3.2.tgz", - "integrity": "sha512-Dqs00++c8rwKky6KCKLLY2T1qYO4Q+X5t+lF7DInXDNF4ae1Oau35bkD+OpJ9u7l1pEv7KHowP6CUKuySCOc8g==", + "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": "^1.11.1" + "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": "2.4.0", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-2.4.0.tgz", - "integrity": "sha512-zWFjBS35eI9leAHhjfeOYlK5Plcuj/77EzstnrJIZbKgF/nkqjcQuGiMCpzCwOfPyUbz8ZaEOYgbHa759AKbjg==", + "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": "^1.0.0", - "@google-cloud/promisify": "^1.0.0", - "arrify": "^2.0.0", - "duplexify": "^3.6.0", + "@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": "^5.5.0", - "retry-request": "^4.0.0", - "teeny-request": "^6.0.0" + "google-auth-library": "^7.0.2", + "retry-request": "^4.1.1", + "teeny-request": "^7.0.0" } }, "@google-cloud/firestore": { - "version": "3.8.6", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-3.8.6.tgz", - "integrity": "sha512-ox80NbrM1MLJgvAAUd1quFLx/ie/nSjrk1PtscSicpoYDlKb9e6j7pHrVpbopBMyliyfNl3tLJWaDh+x+uCXqw==", + "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": { - "deep-equal": "^2.0.0", + "fast-deep-equal": "^3.1.1", "functional-red-black-tree": "^1.0.1", - "google-gax": "^1.15.3", - "readable-stream": "^3.4.0", - "through2": "^3.0.0" + "google-gax": "^2.9.2", + "protobufjs": "^6.8.6" } }, "@google-cloud/paginator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-2.0.3.tgz", - "integrity": "sha512-kp/pkb2p/p0d8/SKUu4mOq8+HGwF8NPzHWkj+VKrIPQPyMRw8deZtrO/OcSiy9C/7bpfU5Txah5ltUNfPkgEXg==", + "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": { @@ -135,103 +158,233 @@ } }, "@google-cloud/projectify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-1.0.4.tgz", - "integrity": "sha512-ZdzQUN02eRsmTKfBj9FDL0KNDIFNjBn/d6tHQmA/+FImH5DO6ZV8E7FzxMgAUiVAUq41RFAkb25p1oHOZ8psfg==", + "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": "1.0.4", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-1.0.4.tgz", - "integrity": "sha512-VccZDcOql77obTnFh0TbNED/6ZbbmHDf8UMNnzO1d5g9V0Htfm4k5cllY8P1tJsRKC3zWYGRLaViiupcgVjBoQ==", + "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": "4.7.0", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-4.7.0.tgz", - "integrity": "sha512-f0guAlbeg7Z0m3gKjCfBCu7FG9qS3M3oL5OQQxlvGoPtK7/qg3+W+KQV73O2/sbuS54n0Kh2mvT5K2FWzF5vVQ==", + "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": "^2.1.1", - "@google-cloud/paginator": "^2.0.0", - "@google-cloud/promisify": "^1.0.0", + "@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", - "concat-stream": "^2.0.0", - "date-and-time": "^0.13.0", - "duplexify": "^3.5.0", + "date-and-time": "^1.0.0", + "duplexify": "^4.0.0", "extend": "^3.0.2", - "gaxios": "^3.0.0", - "gcs-resumable-upload": "^2.2.4", + "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": "^2.2.0", + "p-limit": "^3.0.1", "pumpify": "^2.0.0", - "readable-stream": "^3.4.0", "snakeize": "^0.1.0", "stream-events": "^1.0.1", - "through2": "^3.0.0", "xdg-basedir": "^4.0.0" }, "dependencies": { - "gaxios": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-3.2.0.tgz", - "integrity": "sha512-+6WPeVzPvOshftpxJwRi2Ozez80tn/hdtOUag7+gajDHRJvAblKxTFSSMPtr2hmnLy7p0mvYz0rMXLBl8pSO7Q==", + "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": { - "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" + "yocto-queue": "^0.1.0" } - }, - "mime": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", - "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", - "dev": true, - "optional": true } } }, "@grpc/grpc-js": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.0.5.tgz", - "integrity": "sha512-Hm+xOiqAhcpT9RYM8lc15dbQD7aQurM7ZU8ulmulepiPlN7iwBXXwP3vSBUimoFoApRqz7pSIisXU8pZaCB4og==", + "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": { - "semver": "^6.2.0" + "@types/node": ">=12.12.47" }, "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "@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.5.5", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.5.tgz", - "integrity": "sha512-WwN9jVNdHRQoOBo9FDH7qU+mgfjPc8GygPYms3M+y3fbQLfnCe/Kv/E01t7JRgnrsOHH8euvSbed3mIalXhwqQ==", + "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", - "protobufjs": "^6.8.6" + "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", @@ -242,14 +395,14 @@ "@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha1-TIVzDlm5ofHzSQR9vyQpYDS7JzU=", + "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": "sha1-fvN/DQEPsCitGtWXIuUG2SYoFcs=", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", "dev": true, "optional": true }, @@ -406,6 +559,16 @@ "@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", @@ -416,14 +579,13 @@ "@types/range-parser": "*" } }, - "@types/fs-extra": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.1.tgz", - "integrity": "sha512-TcUlBem321DFQzBNuz8p0CLLKp0VvF/XH9E4KHNmgwyp4E3AfgI5cjiIVZWlbfThBop2qxFIh4+LeY6hVWWZ2w==", + "@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, - "optional": true, "requires": { - "@types/node": "*" + "@types/express": "*" } }, "@types/jsonwebtoken": { @@ -555,9 +717,9 @@ "dev": true }, "agent-base": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.1.tgz", - "integrity": "sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg==", + "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": { @@ -565,13 +727,13 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "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.1" + "ms": "2.1.2" } }, "ms": { @@ -637,13 +799,6 @@ "sprintf-js": "~1.0.2" } }, - "array-filter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", - "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", - "dev": true, - "optional": true - }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -671,6 +826,18 @@ "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", @@ -683,6 +850,16 @@ "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", @@ -695,16 +872,6 @@ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true }, - "available-typed-arrays": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", - "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", - "dev": true, - "optional": true, - "requires": { - "array-filter": "^1.0.0" - } - }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -724,9 +891,9 @@ "dev": true }, "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "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 }, @@ -740,12 +907,18 @@ } }, "bignumber.js": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", - "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==", + "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", @@ -773,6 +946,12 @@ "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", @@ -968,9 +1147,9 @@ }, "dependencies": { "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "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 } @@ -982,19 +1161,6 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "dev": true, - "optional": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, "configstore": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", @@ -1109,9 +1275,9 @@ } }, "date-and-time": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-0.13.1.tgz", - "integrity": "sha512-/Uge9DJAT+s+oAcDxtBhyR8+sKjUnZbYmyhbmWjTHNtX7B7oWD8YyYdeXcBRbwSj6hVvj+IQegJam7m7czhbFw==", + "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 }, @@ -1144,29 +1310,6 @@ "type-detect": "^4.0.0" } }, - "deep-equal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.3.tgz", - "integrity": "sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA==", - "dev": true, - "optional": true, - "requires": { - "es-abstract": "^1.17.5", - "es-get-iterator": "^1.1.0", - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.2", - "is-regex": "^1.0.5", - "isarray": "^2.0.5", - "object-is": "^1.1.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.2", - "which-boxed-primitive": "^1.0.1", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.2" - } - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -1241,41 +1384,16 @@ } }, "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "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.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", "stream-shift": "^1.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - } } }, "ecc-jsbn": { @@ -1302,6 +1420,29 @@ "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", @@ -1349,22 +1490,6 @@ "string.prototype.trimstart": "^1.0.1" } }, - "es-get-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", - "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", - "dev": true, - "optional": true, - "requires": { - "es-abstract": "^1.17.4", - "has-symbols": "^1.0.1", - "is-arguments": "^1.0.4", - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-string": "^1.0.5", - "isarray": "^2.0.5" - } - }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -1376,6 +1501,13 @@ "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", @@ -1534,18 +1666,28 @@ } }, "firebase-admin": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-8.13.0.tgz", - "integrity": "sha512-krXj5ncWMJBhCpXSn9UFY6zmDWjFjqgx+1e9ATXKFYndEjmKtNBuJzqdrAdDh7aTUR7X6+0TPx4Hbc08kd0lwQ==", + "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.6.0", - "@google-cloud/firestore": "^3.0.0", - "@google-cloud/storage": "^4.1.2", - "@types/node": "^8.10.59", + "@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", - "node-forge": "^0.7.6" + "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": { @@ -1565,13 +1707,6 @@ } } }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true, - "optional": true - }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -1631,9 +1766,9 @@ "optional": true }, "gaxios": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.3.4.tgz", - "integrity": "sha512-US8UMj8C5pRnao3Zykc4AAVr+cffoNKRTg9Rsf2GiuZCW69vgJj38VK2PzlPuQU73FZ/nTk9/Av6/JGcE1N9vA==", + "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": { @@ -1645,27 +1780,28 @@ } }, "gcp-metadata": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.5.0.tgz", - "integrity": "sha512-ZQf+DLZ5aKcRpLzYUyBS3yo3N0JSa82lNDO8rj3nMSlovLcz2riKFBsYgDzeXcv75oo5eqB2lx+B14UvPoCRnA==", + "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": "^2.1.0", - "json-bigint": "^0.3.0" + "gaxios": "^4.0.0", + "json-bigint": "^1.0.0" } }, "gcs-resumable-upload": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-2.3.3.tgz", - "integrity": "sha512-sf896I5CC/1AxeaGfSFg3vKMjUq/r+A3bscmVzZm10CElyRanN0XwPu/MxeIO4LSP+9uF6yKzXvNsaTsMXUG6Q==", + "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", - "gaxios": "^2.0.0", - "google-auth-library": "^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" } @@ -1682,6 +1818,13 @@ "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", @@ -1706,9 +1849,9 @@ } }, "google-auth-library": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.1.tgz", - "integrity": "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg==", + "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": { @@ -1716,11 +1859,11 @@ "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "fast-text-encoding": "^1.0.0", - "gaxios": "^2.1.0", - "gcp-metadata": "^3.4.0", - "gtoken": "^4.1.0", + "gaxios": "^4.0.0", + "gcp-metadata": "^4.2.0", + "gtoken": "^5.0.4", "jws": "^4.0.0", - "lru-cache": "^5.0.0" + "lru-cache": "^6.0.0" }, "dependencies": { "jwa": { @@ -1747,74 +1890,53 @@ } }, "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "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": "^3.0.2" + "yallist": "^4.0.0" } }, "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "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": "1.15.3", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-1.15.3.tgz", - "integrity": "sha512-3JKJCRumNm3x2EksUTw4P1Rad43FTpqrtW9jzpf3xSMYXx+ogaqTM1vGo7VixHB4xkAyATXVIa3OcNSh8H9zsQ==", + "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.0.3", - "@grpc/proto-loader": "^0.5.1", - "@types/fs-extra": "^8.0.1", + "@grpc/grpc-js": "~1.3.0", + "@grpc/proto-loader": "^0.6.1", "@types/long": "^4.0.0", "abort-controller": "^3.0.0", - "duplexify": "^3.6.0", - "google-auth-library": "^5.0.0", + "duplexify": "^4.0.0", + "fast-text-encoding": "^1.0.3", + "google-auth-library": "^7.0.2", "is-stream-ended": "^0.1.4", - "lodash.at": "^4.6.0", - "lodash.has": "^4.5.2", - "node-fetch": "^2.6.0", - "protobufjs": "^6.8.9", - "retry-request": "^4.0.0", - "semver": "^6.0.0", - "walkdir": "^0.4.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 - } + "node-fetch": "^2.6.1", + "object-hash": "^2.1.1", + "protobufjs": "^6.10.2", + "retry-request": "^4.0.0" } }, "google-p12-pem": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.4.tgz", - "integrity": "sha512-S4blHBQWZRnEW44OcR7TL9WR+QCqByRvhNDZ/uuQfpxywfupikf/miba8js1jZi6ZOGv5slgSuoshCWh6EMDzg==", + "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.9.0" - }, - "dependencies": { - "node-forge": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.2.tgz", - "integrity": "sha512-naKSScof4Wn+aoHU6HBsifh92Zeicm1GDQKd1vp3Y/kOi8ub0DozCa9KpvYNCXslFHYRmLNiqRopGdTGwNLpNw==", - "dev": true, - "optional": true - } + "node-forge": "^0.10.0" } }, "graceful-fs": { @@ -1830,16 +1952,15 @@ "dev": true }, "gtoken": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.4.tgz", - "integrity": "sha512-VxirzD0SWoFUo5p8RDP8Jt2AGyOmyYcT/pOUgDKJCK+iSw0TMqwrVfY37RXTNmoKwrzmDHSk0GMT9FsgVmnVSA==", + "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": "^2.1.0", - "google-p12-pem": "^2.0.0", - "jws": "^4.0.0", - "mime": "^2.2.0" + "gaxios": "^4.0.0", + "google-p12-pem": "^3.0.3", + "jws": "^4.0.0" }, "dependencies": { "jwa": { @@ -1864,13 +1985,6 @@ "jwa": "^2.0.0", "safe-buffer": "^5.0.1" } - }, - "mime": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", - "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", - "dev": true, - "optional": true } } }, @@ -1912,6 +2026,16 @@ "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", @@ -1924,6 +2048,17 @@ "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", @@ -1946,9 +2081,9 @@ } }, "http-parser-js": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz", - "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==", + "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": { @@ -1964,13 +2099,13 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "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.1" + "ms": "2.1.2" } }, "ms": { @@ -2005,13 +2140,13 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "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.1" + "ms": "2.1.2" } }, "ms": { @@ -2076,20 +2211,6 @@ "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", "dev": true }, - "is-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.0.tgz", - "integrity": "sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==", - "dev": true, - "optional": true - }, - "is-boolean-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz", - "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==", - "dev": true, - "optional": true - }, "is-callable": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.1.tgz", @@ -2108,26 +2229,12 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, - "is-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", - "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", - "dev": true, - "optional": 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-number-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", - "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", - "dev": true, - "optional": true - }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -2150,13 +2257,6 @@ "has-symbols": "^1.0.1" } }, - "is-set": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", - "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", - "dev": true, - "optional": true - }, "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", @@ -2167,14 +2267,7 @@ "is-stream-ended": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", - "integrity": "sha1-9QIk6V4GvODjVtRApIJ801smfto=", - "dev": true, - "optional": true - }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==", "dev": true, "optional": true }, @@ -2187,46 +2280,12 @@ "has-symbols": "^1.0.1" } }, - "is-typed-array": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz", - "integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==", - "dev": true, - "optional": true, - "requires": { - "available-typed-arrays": "^1.0.0", - "es-abstract": "^1.17.4", - "foreach": "^2.0.5", - "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 }, - "is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true, - "optional": true - }, - "is-weakset": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", - "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==", - "dev": true, - "optional": true - }, - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "optional": true - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2245,6 +2304,15 @@ "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", @@ -2338,9 +2406,9 @@ } }, "json-bigint": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.1.tgz", - "integrity": "sha512-DGWnSzmusIreWlEupsUelHrhwmPPE+FiQvg+drKfk2p+bdEYa5mp4PJ8JsCWqae0M2jQNb0HPvnwvf1qOTThzQ==", + "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": { @@ -2430,6 +2498,47 @@ "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", @@ -2450,6 +2559,12 @@ "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", @@ -2471,13 +2586,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, - "lodash.at": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.at/-/lodash.at-4.6.0.tgz", - "integrity": "sha1-k83OZk8KGZTqM9181A4jr9EbD/g=", - "dev": true, - "optional": true - }, "lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -2485,12 +2593,11 @@ "dev": true, "optional": true }, - "lodash.has": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz", - "integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=", - "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", @@ -2558,7 +2665,7 @@ "long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg=", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", "dev": true, "optional": true }, @@ -2572,6 +2679,28 @@ "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", @@ -2649,6 +2778,18 @@ "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", @@ -2983,9 +3124,9 @@ "optional": true }, "node-forge": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz", - "integrity": "sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==", + "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": { @@ -3020,6 +3161,13 @@ "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", @@ -3209,13 +3357,6 @@ "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", "dev": true }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true, - "optional": true - }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -3235,9 +3376,9 @@ "dev": true }, "protobufjs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.1.tgz", - "integrity": "sha512-pb8kTchL+1Ceg4lFd5XUpK8PdWacbvV5SK2ULH2ebrYtl4GjJmS24m6CKME67jzV53tbJxHlnNOSqQHbTsR9JQ==", + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", + "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", "dev": true, "optional": true, "requires": { @@ -3252,14 +3393,14 @@ "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/long": "^4.0.1", - "@types/node": "^13.7.0", + "@types/node": ">=13.7.0", "long": "^4.0.0" }, "dependencies": { "@types/node": { - "version": "13.13.20", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.20.tgz", - "integrity": "sha512-1kx55tU3AvGX2Cjk2W4GMBxbgIz892V+X10S2gUreIAq8qCWgaQH+tZBOWc0bi2BKFhQt+CX0BTx28V9QPNa+A==", + "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 } @@ -3307,21 +3448,6 @@ "duplexify": "^4.1.1", "inherits": "^2.0.3", "pump": "^3.0.0" - }, - "dependencies": { - "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" - } - } } }, "punycode": { @@ -3490,6 +3616,13 @@ "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", @@ -3501,13 +3634,13 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "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.1" + "ms": "2.1.2" } }, "ms": { @@ -3604,40 +3737,6 @@ "rechoir": "^0.6.2" } }, - "side-channel": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.3.tgz", - "integrity": "sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==", - "dev": true, - "optional": true, - "requires": { - "es-abstract": "^1.18.0-next.0", - "object-inspect": "^1.8.0" - }, - "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, - "optional": 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" - } - } - } - }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -3745,7 +3844,7 @@ "stream-events": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", - "integrity": "sha1-u8iY7E3zOkkC2JIzPUfam/HEBtU=", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", "dev": true, "optional": true, "requires": { @@ -3796,13 +3895,22 @@ } }, "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "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.1.0" + "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": { @@ -3834,17 +3942,17 @@ "dev": true }, "teeny-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-6.0.3.tgz", - "integrity": "sha512-TZG/dfd2r6yeji19es1cUIwAlVD8y+/svB1kAC2Y0bjEyysrfbO8EZvJBRwIE6WkwmUoB7uvWLwTIhJbMXZ1Dw==", + "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.2.0", + "node-fetch": "^2.6.1", "stream-events": "^1.0.5", - "uuid": "^7.0.0" + "uuid": "^8.0.0" } }, "thenify": { @@ -3865,26 +3973,6 @@ "thenify": ">= 3.1.0 < 4" } }, - "through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", - "dev": true, - "optional": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" - }, - "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, - "optional": true - } - } - }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -4060,13 +4148,6 @@ "mime-types": "~2.1.24" } }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true, - "optional": true - }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -4198,9 +4279,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", + "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 }, @@ -4238,13 +4319,6 @@ "xml-name-validator": "^3.0.0" } }, - "walkdir": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz", - "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==", - "dev": true, - "optional": true - }, "webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", @@ -4303,54 +4377,12 @@ "isexe": "^2.0.0" } }, - "which-boxed-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz", - "integrity": "sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==", - "dev": true, - "optional": true, - "requires": { - "is-bigint": "^1.0.0", - "is-boolean-object": "^1.0.0", - "is-number-object": "^1.0.3", - "is-string": "^1.0.4", - "is-symbol": "^1.0.2" - } - }, - "which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "dev": true, - "optional": true, - "requires": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - } - }, "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 }, - "which-typed-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.2.tgz", - "integrity": "sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==", - "dev": true, - "optional": true, - "requires": { - "available-typed-arrays": "^1.0.2", - "es-abstract": "^1.17.5", - "foreach": "^2.0.5", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.1", - "is-typed-array": "^1.1.3" - } - }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -4683,6 +4715,13 @@ "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 11a4dfb39..24ebbbcb4 100644 --- a/package.json +++ b/package.json @@ -58,10 +58,11 @@ "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "child-process-promise": "^2.2.1", - "firebase-admin": "^8.2.0", + "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", 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..8766ab06a 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 mockKey from '../fixtures/credential/key.json'; +import * as mockJWK from '../fixtures/credential/jwk.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/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/src/providers/https.ts b/src/providers/https.ts index cf64c9d90..99f465215 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,14 @@ 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; + token: firebase.appCheck.DecodedAppCheckToken; + }; + /** * The result of decoding and verifying a Firebase Auth ID token. */ @@ -411,6 +419,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 uupdate 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 +537,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/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"] From 4ed7b7615302404bb0205c68ee49b1134aae5449 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 12 May 2021 13:03:08 -0700 Subject: [PATCH 101/529] Update release script to build packages using Node 14 (#889) * Update node version in package-builder imaage. * Update publish script to ignore local changes while updating package version. --- scripts/publish-container/Dockerfile | 2 +- scripts/publish.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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." From f74adaa86a8a4ccd43ca6e0e4f59e7c1bbb19b8f Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 12 May 2021 20:09:26 +0000 Subject: [PATCH 102/529] [firebase-release] Removed change log and reset repo after 3.14.0 release --- CHANGELOG.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ee96fbef..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +0,0 @@ -- Functions may now be deployed with 8GB RAM -- Functions may now be deployed to europe-central2 (Warsaw) -- Add support for validating AppCheck tokens for Callable Functions From 8e4a446ad0872297b873cfcd21f6847a9182b359 Mon Sep 17 00:00:00 2001 From: "Masayuki Ono (mono)" Date: Fri, 14 May 2021 04:29:10 +0900 Subject: [PATCH 103/529] Fix typo (#890) --- src/providers/https.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/https.ts b/src/providers/https.ts index 99f465215..2af2117f3 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -459,7 +459,7 @@ async function checkTokens( try { if (!apps().admin.appCheck) { throw new Error( - 'Cannot validate AppCheck token. Please uupdate Firebase Admin SDK to >= v9.8.0' + 'Cannot validate AppCheck token. Please update Firebase Admin SDK to >= v9.8.0' ); } const appCheckToken = await apps() From e94f1c029f837bfb0738fd477fd8b6a0c78245d4 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Mon, 17 May 2021 10:54:13 -0700 Subject: [PATCH 104/529] Inline DecodedAppCheck definition (#891) Fixes an issue where old (but supported) versions of firebase-admin didn't expose AppCheck and then TSC failed. --- integration_test/functions/src/index.ts | 2 +- .../functions/src/testLab-tests.ts | 2 +- .../functions/src/testLab-utils.ts | 12 ++-- integration_test/functions/src/testing.ts | 2 +- spec/fixtures/mockrequest.ts | 2 +- spec/logger.spec.ts | 8 +-- spec/providers/testLab.spec.ts | 40 +++++++------- src/cloud-functions.ts | 2 +- src/function-builder.ts | 2 +- src/function-configuration.ts | 7 +++ src/index.ts | 2 +- src/logger.ts | 2 +- src/logger/compat.ts | 4 +- src/providers/database.ts | 2 +- src/providers/https.ts | 55 ++++++++++++++++++- 15 files changed, 102 insertions(+), 42 deletions(-) diff --git a/integration_test/functions/src/index.ts b/integration_test/functions/src/index.ts index 0a980aa06..6865d7dd5 100644 --- a/integration_test/functions/src/index.ts +++ b/integration_test/functions/src/index.ts @@ -1,9 +1,9 @@ +import { PubSub } from '@google-cloud/pubsub'; import { Request, Response } from 'express'; import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; import * as fs from 'fs'; import * as https from 'https'; -import { PubSub } from '@google-cloud/pubsub'; export * from './pubsub-tests'; export * from './database-tests'; diff --git a/integration_test/functions/src/testLab-tests.ts b/integration_test/functions/src/testLab-tests.ts index 586c3a0cb..8e064928a 100644 --- a/integration_test/functions/src/testLab-tests.ts +++ b/integration_test/functions/src/testLab-tests.ts @@ -1,5 +1,5 @@ import * as functions from 'firebase-functions'; -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'; diff --git a/integration_test/functions/src/testLab-utils.ts b/integration_test/functions/src/testLab-utils.ts index 6f86b0a1f..3dbb7b763 100644 --- a/integration_test/functions/src/testLab-utils.ts +++ b/integration_test/functions/src/testLab-utils.ts @@ -1,6 +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 utils from './test-utils'; interface AndroidDevice { @@ -50,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( @@ -70,7 +70,7 @@ function createTestMatrix( '/v1/projects/' + projectId + '/testMatrices' ); const body = { - projectId: projectId, + projectId, testSpecification: { androidRoboTest: { appApk: { @@ -105,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 228203d31..1cb9f7819 100644 --- a/integration_test/functions/src/testing.ts +++ b/integration_test/functions/src/testing.ts @@ -92,7 +92,7 @@ function deepEq(left: any, right: any) { return false; } - for (let key in left) { + for (const key in left) { if (!right.hasOwnProperty(key)) { return false; } diff --git a/spec/fixtures/mockrequest.ts b/spec/fixtures/mockrequest.ts index 8766ab06a..68827610b 100644 --- a/spec/fixtures/mockrequest.ts +++ b/spec/fixtures/mockrequest.ts @@ -2,8 +2,8 @@ import * as jwt from 'jsonwebtoken'; import * as jwkToPem from 'jwk-to-pem'; import * as _ from 'lodash'; import * as nock from 'nock'; -import * as mockKey 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 { diff --git a/spec/logger.spec.ts b/spec/logger.spec.ts index 48258ffac..e32765d10 100644 --- a/spec/logger.spec.ts +++ b/spec/logger.spec.ts @@ -7,8 +7,8 @@ const SUPPORTS_STRUCTURED_LOGS = describe(`logger (${ SUPPORTS_STRUCTURED_LOGS ? 'structured' : 'unstructured' })`, () => { - let stdoutWrite = process.stdout.write.bind(process.stdout); - let stderrWrite = process.stderr.write.bind(process.stderr); + const stdoutWrite = process.stdout.write.bind(process.stdout); + const stderrWrite = process.stderr.write.bind(process.stderr); let lastOut: string; let lastErr: string; @@ -127,7 +127,7 @@ describe(`logger (${ 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', }; @@ -144,7 +144,7 @@ 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', }; 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 500e27144..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 */ diff --git a/src/function-builder.ts b/src/function-builder.ts index 9d016d817..f08b16117 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -26,12 +26,12 @@ import * as _ from 'lodash'; import { CloudFunction, EventContext } from './cloud-functions'; import { DeploymentOptions, + INGRESS_SETTINGS_OPTIONS, MAX_TIMEOUT_SECONDS, RuntimeOptions, SUPPORTED_REGIONS, VALID_MEMORY_OPTIONS, VPC_EGRESS_SETTINGS_OPTIONS, - INGRESS_SETTINGS_OPTIONS, } from './function-configuration'; import * as analytics from './providers/analytics'; import * as auth from './providers/auth'; diff --git a/src/function-configuration.ts b/src/function-configuration.ts index 2d9925ac5..852482241 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -116,6 +116,13 @@ export interface RuntimeOptions { */ 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 */ diff --git a/src/index.ts b/src/index.ts index 9d49414e8..dbb42003c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,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(); diff --git a/src/logger.ts b/src/logger.ts index d6c0d2220..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'; 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/database.ts b/src/providers/database.ts index 0e4393d05..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]; diff --git a/src/providers/https.ts b/src/providers/https.ts index 2af2117f3..c6167c50a 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -253,7 +253,60 @@ export interface CallableContext { */ app?: { appId: string; - token: firebase.appCheck.DecodedAppCheckToken; + + // 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; + }; }; /** From ba2cd3be03041828d3e0b83e8a8f45d38302ee02 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Mon, 17 May 2021 13:08:02 -0700 Subject: [PATCH 105/529] Add relnotes (#892) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..f5d3834e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Fixes a bug where typescript would fail to compile with old (but supported) versions of firebase-admin From 2b3b15d5be1f17a166175e4f5d5cc5ffc7a7040a Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Mon, 17 May 2021 20:35:15 +0000 Subject: [PATCH 106/529] [firebase-release] Removed change log and reset repo after 3.13.3 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5d3834e9..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Fixes a bug where typescript would fail to compile with old (but supported) versions of firebase-admin From 4053ee19e33add8418f5483caf8ea62b64b2253f Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Mon, 17 May 2021 14:04:34 -0700 Subject: [PATCH 107/529] Prep release 3.14.1 (#893) --- package.json | 2 +- scripts/publish/CHANGELOG.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 scripts/publish/CHANGELOG.md diff --git a/package.json b/package.json index 24ebbbcb4..fa00d759d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.13.2", + "version": "3.14.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", diff --git a/scripts/publish/CHANGELOG.md b/scripts/publish/CHANGELOG.md new file mode 100644 index 000000000..510e7e4e0 --- /dev/null +++ b/scripts/publish/CHANGELOG.md @@ -0,0 +1,2 @@ +- Fixes a bug where typescript would fail to compile with old (but supported) versions of firebase-admin +- Replaces 3.13.3 which was an inappropriately numbered version From 242f4215668f2deb1931a74f006d9d8ef9f24330 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Mon, 17 May 2021 14:10:51 -0700 Subject: [PATCH 108/529] Fix changelog being in the wrong location (#894) --- CHANGELOG.md | 2 ++ scripts/publish/CHANGELOG.md | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 scripts/publish/CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..510e7e4e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,2 @@ +- Fixes a bug where typescript would fail to compile with old (but supported) versions of firebase-admin +- Replaces 3.13.3 which was an inappropriately numbered version diff --git a/scripts/publish/CHANGELOG.md b/scripts/publish/CHANGELOG.md deleted file mode 100644 index 510e7e4e0..000000000 --- a/scripts/publish/CHANGELOG.md +++ /dev/null @@ -1,2 +0,0 @@ -- Fixes a bug where typescript would fail to compile with old (but supported) versions of firebase-admin -- Replaces 3.13.3 which was an inappropriately numbered version From 1c2cc9c367ed1a979ae0039bebb8be37f0a79f37 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Mon, 17 May 2021 21:12:48 +0000 Subject: [PATCH 109/529] [firebase-release] Removed change log and reset repo after 3.14.1 release --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 510e7e4e0..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +0,0 @@ -- Fixes a bug where typescript would fail to compile with old (but supported) versions of firebase-admin -- Replaces 3.13.3 which was an inappropriately numbered version From f369a2c35e65f2e484a1f6c770f4dfa1dbe1baae Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Thu, 20 May 2021 09:50:50 -0700 Subject: [PATCH 110/529] Update package.json to the latest release (#895) * Update package.json to the latest release * Bump version in package-lock.json --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8a4fd6be8..e199a60c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.13.2", + "version": "3.14.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index fa00d759d..a9c86bf87 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.14.0", + "version": "3.14.1", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From f7bcdabc1a1d67c962072fc8cd229372a1349ffc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 May 2021 08:51:16 -0700 Subject: [PATCH 111/529] Bump y18n from 4.0.0 to 4.0.3 (#888) Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.3. - [Release notes](https://github.com/yargs/y18n/releases) - [Changelog](https://github.com/yargs/y18n/blob/y18n-v4.0.3/CHANGELOG.md) - [Commits](https://github.com/yargs/y18n/compare/v4.0.0...y18n-v4.0.3) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index e199a60c2..41abc0d4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -347,13 +347,6 @@ "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", @@ -2946,6 +2939,12 @@ "has-flag": "^3.0.0" } }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, "yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", @@ -4482,10 +4481,11 @@ "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 + "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 }, "yallist": { "version": "2.1.2", @@ -4631,6 +4631,12 @@ "strip-ansi": "^6.0.0" } }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, "yargs-parser": { "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", @@ -4690,6 +4696,12 @@ "ansi-regex": "^4.1.0" } }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, "yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", From 7e26ae36a8fb6f44b86279826068d63f366e568f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 May 2021 08:53:54 -0700 Subject: [PATCH 112/529] Bump lodash from 4.17.20 to 4.17.21 (#887) Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: joehan --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 41abc0d4b..16606db37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2575,9 +2575,9 @@ } }, "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.camelcase": { "version": "4.3.0", From 02a97a19b81a2e40f25f23907b6ae3f585820978 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 27 May 2021 15:41:30 -0700 Subject: [PATCH 113/529] Fix publish script to commit changes to version info in package{-lock}.json. (#896) * Fix publish script to commit changes to version info in package.json and package-lock.json. * Add debug message to make it clear what's going on. Co-authored-by: Thomas Bouldin --- scripts/publish.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/publish.sh b/scripts/publish.sh index d187e9f45..9246562ae 100644 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -87,7 +87,11 @@ npm run build:release echo "Ran publish build." echo "Making a $VERSION version..." -npm version --no-git-tag-version $VERSION +# TODO: Remove the following command. +# npm version command had previously failed claiming unclean git repo, and we don't know why. +echo "DEBUG: Running git status to show dirty files..." +git status +npm version $VERSION NEW_VERSION=$(jq -r ".version" package.json) echo "Made a $VERSION version." From 2e718c74a382655a9756c0ff781278c0e8a9146b Mon Sep 17 00:00:00 2001 From: joehan Date: Thu, 27 May 2021 15:51:45 -0700 Subject: [PATCH 114/529] Adds support for setting user labels on functions (#899) * Adds support for runWith labels * Adds CHANGELOG entry and extra test cases * Minor style fixes * pr fixes * minor typo fix --- CHANGELOG.md | 1 + spec/function-builder.spec.ts | 120 ++++++++++++++++++++++++++++++++++ src/cloud-functions.ts | 6 +- src/function-builder.ts | 72 ++++++++++++++++++++ src/function-configuration.ts | 17 +++-- 5 files changed, 210 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..5283950c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Adds support for setting user labels on functions via `runWith()`. diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts index 28cf35c3a..106f874ea 100644 --- a/spec/function-builder.spec.ts +++ b/spec/function-builder.spec.ts @@ -319,4 +319,124 @@ describe('FunctionBuilder', () => { expect(fn.__trigger.availableMemoryMb).to.deep.equal(4096); }); + + it('should allow labels to be set', () => { + const fn = functions + .runWith({ + labels: { + 'valid-key': 'valid-value', + }, + }) + .auth.user() + .onCreate((user) => user); + + expect(fn.__trigger.labels).to.deep.equal({ + 'valid-key': 'valid-value', + }); + }); + + it('should throw an error if more than 58 labels are set', () => { + const labels = {}; + for (let i = 0; i < 59; i++) { + labels[`label${i}`] = 'value'; + } + + expect(() => + functions.runWith({ + labels, + }) + ).to.throw(); + }); + + it('should throw an error if labels has a key that is too long', () => { + expect(() => + functions.runWith({ + labels: { + 'a-very-long-key-that-is-more-than-the-maximum-allowed-length-for-keys': + 'value', + }, + }) + ).to.throw(); + }); + + it('should throw an error if labels has key that is too short', () => { + expect(() => + functions.runWith({ + labels: { '': 'value' }, + }) + ).to.throw(); + }); + + it('should throw an error if labels has a value that is too long', () => { + expect(() => + functions.runWith({ + labels: { + key: + 'a-very-long-value-that-is-more-than-the-maximum-allowed-length-for-values', + }, + }) + ).to.throw(); + }); + + it('should throw an error if labels has a key that contains invalid characters', () => { + expect(() => + functions.runWith({ + labels: { + Key: 'value', + }, + }) + ).to.throw(); + + expect(() => + functions.runWith({ + labels: { + 'key ': 'value', + }, + }) + ).to.throw(); + + expect(() => + functions.runWith({ + labels: { + '1key': 'value', + }, + }) + ).to.throw(); + }); + + it('should throw an error if labels has a value that contains invalid characters', () => { + expect(() => + functions.runWith({ + labels: { + key: 'Value', + }, + }) + ).to.throw(); + + expect(() => + functions.runWith({ + labels: { + 'key ': 'va lue', + }, + }) + ).to.throw(); + }); + + it('should throw an error if a label key starts with a reserved namespace', () => { + expect(() => + functions.runWith({ + labels: { + 'firebase-foo': 'value', + }, + }) + ).to.throw(); + + expect(() => + functions.runWith({ + labels: { + 'deployment-bar': 'value', + }, + }) + ).to.throw(); + }); }); diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 93377c822..2a6c770e6 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -417,7 +417,7 @@ export function makeCloudFunction({ }, }); if (!_.isEmpty(labels)) { - trigger.labels = labels; + trigger.labels = { ...trigger.labels, ...labels }; } return trigger; }, @@ -553,5 +553,9 @@ export function optionsToTrigger(options: DeploymentOptions) { } } + if (options.labels) { + trigger.labels = options.labels; + } + return trigger; } diff --git a/src/function-builder.ts b/src/function-builder.ts index f08b16117..b421668d4 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -27,6 +27,7 @@ import { CloudFunction, EventContext } from './cloud-functions'; import { DeploymentOptions, INGRESS_SETTINGS_OPTIONS, + MAX_NUMBER_USER_LABELS, MAX_TIMEOUT_SECONDS, RuntimeOptions, SUPPORTED_REGIONS, @@ -120,6 +121,77 @@ function assertRuntimeOptionsValid(runtimeOptions: RuntimeOptions): boolean { `serviceAccount must be set to 'default', a service account email, or '{serviceAccountName}@'` ); } + + if (runtimeOptions.labels) { + // Labels must follow the rules listed in + // https://cloud.google.com/resource-manager/docs/creating-managing-labels#requirements + + if (Object.keys(runtimeOptions.labels).length > MAX_NUMBER_USER_LABELS) { + throw new Error( + `A function must not have more than ${MAX_NUMBER_USER_LABELS} user-defined labels.` + ); + } + + // We reserve the 'deployment' and 'firebase' namespaces for future feature development. + const reservedKeys = Object.keys(runtimeOptions.labels).filter( + (key) => key.startsWith('deployment') || key.startsWith('firebase') + ); + if (reservedKeys.length) { + throw new Error( + `Invalid labels: ${reservedKeys.join( + ', ' + )}. Labels may not start with reserved names 'deployment' or 'firebase'` + ); + } + + const invalidLengthKeys = Object.keys(runtimeOptions.labels).filter( + (key) => key.length < 1 || key.length > 63 + ); + if (invalidLengthKeys.length > 0) { + throw new Error( + `Invalid labels: ${invalidLengthKeys.join( + ', ' + )}. Label keys must be between 1 and 63 characters in length.` + ); + } + + const invalidLengthValues = Object.values(runtimeOptions.labels).filter( + (value) => value.length > 63 + ); + if (invalidLengthValues.length > 0) { + throw new Error( + `Invalid labels: ${invalidLengthValues.join( + ', ' + )}. Label values must be less than 64 charcters.` + ); + } + + // Keys can contain lowercase letters, foreign characters, numbers, _ or -. They must start with a letter. + const validKeyPattern = /^[\p{Ll}\p{Lo}][\p{Ll}\p{Lo}\p{N}_-]{0,62}$/u; + const invalidKeys = Object.keys(runtimeOptions.labels).filter( + (key) => !validKeyPattern.test(key) + ); + if (invalidKeys.length > 0) { + throw new Error( + `Invalid labels: ${invalidKeys.join( + ', ' + )}. Label keys can only contain lowercase letters, international characters, numbers, _ or -, and must start with a letter.` + ); + } + + // Values can contain lowercase letters, foreign characters, numbers, _ or -. + const validValuePattern = /^[\p{Ll}\p{Lo}\p{N}_-]{0,63}$/u; + const invalidValues = Object.values(runtimeOptions.labels).filter( + (value) => !validValuePattern.test(value) + ); + if (invalidValues.length > 0) { + throw new Error( + `Invalid labels: ${invalidValues.join( + ', ' + )}. Label values can only contain lowercase letters, international characters, numbers, _ or -.` + ); + } + } return true; } diff --git a/src/function-configuration.ts b/src/function-configuration.ts index 852482241..6faa7aa42 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -94,6 +94,8 @@ export const DEFAULT_FAILURE_POLICY: FailurePolicy = { retry: {}, }; +export const MAX_NUMBER_USER_LABELS = 58; + export interface RuntimeOptions { /** * Failure policy of the function, with boolean `true` being equivalent to @@ -124,29 +126,34 @@ export interface RuntimeOptions { apiVersion?: 1 | 2; /** - * Max number of actual instances allowed to be running in parallel + * Max number of actual instances allowed to be running in parallel. */ maxInstances?: number; /** - * Connect cloud function to specified VPC connector + * Connect cloud function to specified VPC connector. */ vpcConnector?: string; /** - * Egress settings for VPC connector + * Egress settings for VPC connector. */ vpcConnectorEgressSettings?: typeof VPC_EGRESS_SETTINGS_OPTIONS[number]; /** - * Specific service account for the function to run as + * Specific service account for the function to run as. */ serviceAccount?: 'default' | string; /** - * Ingress settings + * Ingress settings which control where this function can be called from. */ ingressSettings?: typeof INGRESS_SETTINGS_OPTIONS[number]; + + /** + * User labels to set on the function. + */ + labels?: Record; } export interface DeploymentOptions extends RuntimeOptions { From 88589371f69ec98723d0e0d8bcb58c6ef23a11e5 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Thu, 24 Jun 2021 13:26:13 -0700 Subject: [PATCH 115/529] Support documented case where FIREBASE_CONFIG is a json file. (#905) Support documented case where FIREBASE_CONFIG is a json file. Per https://firebase.google.com/docs/admin/setup#initialize-without-parameters the FIREBASE_CONFIG environment variable can be a name of a JSON file. Because this now requires fs.readFileSync to keep API compliance, firebaseConfg() now uses caching. --- CHANGELOG.md | 1 + spec/config.spec.ts | 50 ++++++++++++++++++++++++++------- spec/providers/database.spec.ts | 7 +++-- spec/providers/storage.spec.ts | 12 ++++++-- src/config.ts | 29 +++++++++++++++---- 5 files changed, 78 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5283950c7..900cd7d55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ - Adds support for setting user labels on functions via `runWith()`. +- Adds support for FIREBASE_CONFIG env as the name of a JSON file diff --git a/spec/config.spec.ts b/spec/config.spec.ts index b721dc3a5..1f93ea10e 100644 --- a/spec/config.spec.ts +++ b/spec/config.spec.ts @@ -21,55 +21,85 @@ // SOFTWARE. import { expect } from 'chai'; +import * as fs from 'fs'; import * as mockRequire from 'mock-require'; -import { config, firebaseConfig } from '../src/config'; +import Sinon = require('sinon'); + +import * as config from '../src/config'; describe('config()', () => { + let readFileSync: Sinon.SinonStub; + before(() => { + readFileSync = Sinon.stub(fs, 'readFileSync'); + readFileSync.throws('Unexpected call'); process.env.PWD = '/srv'; }); + after(() => { delete process.env.PWD; + Sinon.verifyAndRestore(); }); + afterEach(() => { mockRequire.stopAll(); - delete config.singleton; + delete config.config.singleton; + (config as any).firebaseConfigCache = null; delete process.env.FIREBASE_CONFIG; delete process.env.CLOUD_RUNTIME_CONFIG; }); it('loads config values from .runtimeconfig.json', () => { mockRequire('/srv/.runtimeconfig.json', { foo: 'bar', firebase: {} }); - const loaded = config(); + const loaded = config.config(); expect(loaded).to.not.have.property('firebase'); expect(loaded).to.have.property('foo', 'bar'); }); it('does not provide firebase config if .runtimeconfig.json not invalid', () => { mockRequire('/srv/.runtimeconfig.json', 'does-not-exist'); - expect(firebaseConfig()).to.be.null; + expect(config.firebaseConfig()).to.be.null; }); it('does not provide firebase config if .ruuntimeconfig.json has no firebase property', () => { mockRequire('/srv/.runtimeconfig.json', {}); - expect(firebaseConfig()).to.be.null; + expect(config.firebaseConfig()).to.be.null; }); it('loads Firebase configs from FIREBASE_CONFIG env variable', () => { process.env.FIREBASE_CONFIG = JSON.stringify({ databaseURL: 'foo@firebaseio.com', }); - expect(firebaseConfig()).to.have.property( + expect(config.firebaseConfig()).to.have.property( 'databaseURL', 'foo@firebaseio.com' ); }); + it('loads Firebase configs from FIREBASE_CONFIG env variable pointing to a file', () => { + const oldEnv = process.env; + process.env = { + ...oldEnv, + FIREBASE_CONFIG: '.firebaseconfig.json', + }; + try { + readFileSync.returns( + Buffer.from('{"databaseURL": "foo@firebaseio.com"}') + ); + expect(config.firebaseConfig()).to.have.property( + 'databaseURL', + 'foo@firebaseio.com' + ); + } finally { + process.env = oldEnv; + } + }); + it('accepts alternative locations for config file', () => { process.env.CLOUD_RUNTIME_CONFIG = 'another.json'; mockRequire('another.json', { foo: 'bar', firebase: {} }); - expect(firebaseConfig()).to.not.be.null; - expect(config()).to.have.property('foo', 'bar'); + expect(config.firebaseConfig()).to.not.be.null; + expect(config.config()).to.have.property('foo', 'bar'); }); it('accepts full JSON in env.CLOUD_RUNTIME_CONFIG', () => { @@ -77,7 +107,7 @@ describe('config()', () => { foo: 'bar', firebase: {}, }); - expect(firebaseConfig()).to.not.be.null; - expect(config()).to.have.property('foo', 'bar'); + expect(config.firebaseConfig()).to.not.be.null; + expect(config.config()).to.have.property('foo', 'bar'); }); }); diff --git a/spec/providers/database.spec.ts b/spec/providers/database.spec.ts index bf7e54b6b..10e1b0efb 100644 --- a/spec/providers/database.spec.ts +++ b/spec/providers/database.spec.ts @@ -22,6 +22,7 @@ import { expect } from 'chai'; import { apps as appsNamespace } from '../../src/apps'; +import * as config from '../../src/config'; import * as functions from '../../src/index'; import * as database from '../../src/providers/database'; import { applyChange } from '../../src/utils'; @@ -31,14 +32,14 @@ describe('Database Functions', () => { // TODO add tests for building a data or change based on the type of operation before(() => { - process.env.FIREBASE_CONFIG = JSON.stringify({ + (config as any).firebaseConfigCache = { databaseURL: 'https://subdomain.apse.firebasedatabase.app', - }); + }; appsNamespace.init(); }); after(() => { - delete process.env.FIREBASE_CONFIG; + (config as any).firebaseConfigCache = null; delete appsNamespace.singleton; }); diff --git a/spec/providers/storage.spec.ts b/spec/providers/storage.spec.ts index 18326eab8..c104a076f 100644 --- a/spec/providers/storage.spec.ts +++ b/spec/providers/storage.spec.ts @@ -21,6 +21,7 @@ // SOFTWARE. import { expect } from 'chai'; +import * as config from '../../src/config'; import { Event, EventContext } from '../../src/index'; import * as functions from '../../src/index'; import * as storage from '../../src/providers/storage'; @@ -28,13 +29,13 @@ import * as storage from '../../src/providers/storage'; describe('Storage Functions', () => { describe('ObjectBuilder', () => { before(() => { - process.env.FIREBASE_CONFIG = JSON.stringify({ + (config as any).firebaseConfigCache = { storageBucket: 'bucket', - }); + }; }); after(() => { - delete process.env.FIREBASE_CONFIG; + (config as any).firebaseConfigcache = null; }); it('should allow both region and runtime options to be set', () => { @@ -542,6 +543,11 @@ describe('Storage Functions', () => { }); describe('process.env.FIREBASE_CONFIG not set', () => { + beforeEach(() => { + (config as any).firebaseConfigCache = null; + delete process.env.FIREBASE_CONFIG; + }); + it('should not throw if __trigger is not accessed', () => { expect(() => storage.object().onArchive(() => null)).to.not.throw(Error); }); diff --git a/src/config.ts b/src/config.ts index 8925ff940..c49e7d03f 100644 --- a/src/config.ts +++ b/src/config.ts @@ -20,9 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -import * as firebase from 'firebase-admin'; +import * as fs from 'fs'; import * as path from 'path'; +import * as firebase from 'firebase-admin'; + export function config(): config.Config { if (typeof config.singleton === 'undefined') { init(); @@ -50,18 +52,34 @@ export namespace config { export let singleton: config.Config; } +/** @hidden */ +export let firebaseConfigCache: firebase.AppOptions | null = null; + /** @hidden */ export function firebaseConfig(): firebase.AppOptions | null { - const env = process.env.FIREBASE_CONFIG; + if (firebaseConfigCache) { + return firebaseConfigCache; + } + + let env = process.env.FIREBASE_CONFIG; if (env) { - return JSON.parse(env); + // Firebase Tools will always use a JSON blob in prod, but docs + // explicitly state that the user can set the env to a file: + // https://firebase.google.com/docs/admin/setup#initialize-without-parameters + if (!env.startsWith('{')) { + env = fs.readFileSync(path.join(process.env.PWD, env)).toString('utf8'); + } + + firebaseConfigCache = JSON.parse(env); + return firebaseConfigCache; } // Could have Runtime Config with Firebase in it as an ENV value. try { const config = JSON.parse(process.env.CLOUD_RUNTIME_CONFIG); if (config.firebase) { - return config.firebase; + firebaseConfigCache = config.firebase; + return firebaseConfigCache; } } catch (e) { // Do nothing @@ -74,7 +92,8 @@ export function firebaseConfig(): firebase.AppOptions | null { path.join(process.env.PWD, '.runtimeconfig.json'); const config = require(configPath); if (config.firebase) { - return config.firebase; + firebaseConfigCache = config.firebase; + return firebaseConfigCache; } } catch (e) { // Do nothing From 4028acc07d11cd81cd56ee816bf16f025c0d10df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joel=20Nordstr=C3=B6m?= Date: Thu, 24 Jun 2021 22:51:15 +0200 Subject: [PATCH 116/529] Fall back to updateTime if no readTime exists (#843) Fixes #599 --- spec/providers/firestore.spec.ts | 13 +++++++++++++ src/providers/firestore.ts | 5 ++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/spec/providers/firestore.spec.ts b/spec/providers/firestore.spec.ts index 42cdf97f7..271701108 100644 --- a/spec/providers/firestore.spec.ts +++ b/spec/providers/firestore.spec.ts @@ -566,6 +566,7 @@ describe('Firestore Functions', () => { describe('Other DocumentSnapshot methods', () => { let snapshot: FirebaseFirestore.DocumentSnapshot; + let newSnapshot: FirebaseFirestore.DocumentSnapshot; before(() => { snapshot = firestore.snapshotConstructor( @@ -579,6 +580,16 @@ describe('Firestore Functions', () => { }, }) ); + newSnapshot = firestore.snapshotConstructor( + makeEvent({ + value: { + fields: { key: { integerValue: '2' } }, + createTime: '2017-06-17T14:45:17.876479Z', + updateTime: '2017-06-17T14:45:17.876479Z', + name: 'projects/pid/databases/(default)/documents/collection/124', + }, + }) + ); }); it('should support #exists', () => { @@ -606,6 +617,8 @@ describe('Firestore Functions', () => { it('should support #readTime', () => { expect(snapshot.readTime.seconds).to.be.a('number'); expect(snapshot.readTime.nanoseconds).to.be.a('number'); + expect(newSnapshot.readTime.seconds).to.be.a('number'); + expect(newSnapshot.readTime.nanoseconds).to.be.a('number'); }); }); diff --git a/src/providers/firestore.ts b/src/providers/firestore.ts index f91e17306..58a6de925 100644 --- a/src/providers/firestore.ts +++ b/src/providers/firestore.ts @@ -155,7 +155,10 @@ export function snapshotConstructor(event: Event): DocumentSnapshot { event.context.resource.name, 'value' ); - const readTime = dateToTimestampProto(_.get(event, 'data.value.readTime')); + const timeString = + _.get(event, 'data.value.readTime') ?? + _.get(event, 'data.value.updateTime'); + const readTime = dateToTimestampProto(timeString); return firestoreInstance.snapshot_(valueProto, readTime, 'json'); } From 33a68eaa5b228ac39c1f0b3a9610dc7457d4e921 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Thu, 8 Jul 2021 14:52:26 -0700 Subject: [PATCH 117/529] Create package exports (#906) * Create version packages * Changelog * Make exports list always use explicit file names --- CHANGELOG.md | 1 + package.json | 8 +++ spec/{ => v1}/apps.spec.ts | 2 +- spec/{ => v1}/cloud-functions.spec.ts | 2 +- spec/{ => v1}/config.spec.ts | 2 +- spec/{ => v1}/function-builder.spec.ts | 2 +- .../providers/analytics.spec.input.ts | 2 +- spec/{ => v1}/providers/analytics.spec.ts | 6 +- spec/{ => v1}/providers/auth.spec.ts | 10 ++- spec/{ => v1}/providers/database.spec.ts | 10 +-- spec/{ => v1}/providers/firestore.spec.ts | 4 +- spec/{ => v1}/providers/https.spec.ts | 10 +-- spec/{ => v1}/providers/pubsub.spec.ts | 6 +- spec/{ => v1}/providers/remoteConfig.spec.ts | 6 +- spec/{ => v1}/providers/storage.spec.ts | 8 +-- spec/{ => v1}/providers/testLab.spec.ts | 2 +- spec/{ => v1}/setup.spec.ts | 2 +- spec/{ => v1}/utils.spec.ts | 2 +- src/index.ts | 63 +------------------ src/{logger.ts => logger/index.ts} | 2 +- src/{ => v1}/apps.ts | 0 src/{ => v1}/cloud-functions.ts | 2 +- src/{ => v1}/config.ts | 0 src/{ => v1}/encoder.ts | 0 src/{ => v1}/function-builder.ts | 0 src/{ => v1}/function-configuration.ts | 0 src/{ => v1}/handler-builder.ts | 0 src/v1/index.ts | 62 ++++++++++++++++++ src/{ => v1}/providers/analytics.ts | 0 src/{ => v1}/providers/auth.ts | 0 src/{ => v1}/providers/database.ts | 2 +- src/{ => v1}/providers/firestore.ts | 0 src/{ => v1}/providers/https.ts | 2 +- src/{ => v1}/providers/pubsub.ts | 0 src/{ => v1}/providers/remoteConfig.ts | 0 src/{ => v1}/providers/storage.ts | 0 src/{ => v1}/providers/testLab.ts | 0 src/{ => v1}/setup.ts | 2 +- src/{ => v1}/utils.ts | 0 tsconfig.release.json | 2 +- 40 files changed, 118 insertions(+), 104 deletions(-) rename spec/{ => v1}/apps.spec.ts (98%) rename spec/{ => v1}/cloud-functions.spec.ts (99%) rename spec/{ => v1}/config.spec.ts (98%) rename spec/{ => v1}/function-builder.spec.ts (99%) rename spec/{ => v1}/providers/analytics.spec.input.ts (98%) rename spec/{ => v1}/providers/analytics.spec.ts (98%) rename spec/{ => v1}/providers/auth.spec.ts (97%) rename spec/{ => v1}/providers/database.spec.ts (98%) rename spec/{ => v1}/providers/firestore.spec.ts (99%) rename spec/{ => v1}/providers/https.spec.ts (98%) rename spec/{ => v1}/providers/pubsub.spec.ts (98%) rename spec/{ => v1}/providers/remoteConfig.spec.ts (96%) rename spec/{ => v1}/providers/storage.spec.ts (98%) rename spec/{ => v1}/providers/testLab.spec.ts (99%) rename spec/{ => v1}/setup.spec.ts (97%) rename spec/{ => v1}/utils.spec.ts (97%) rename src/{logger.ts => logger/index.ts} (99%) rename src/{ => v1}/apps.ts (100%) rename src/{ => v1}/cloud-functions.ts (99%) rename src/{ => v1}/config.ts (100%) rename src/{ => v1}/encoder.ts (100%) rename src/{ => v1}/function-builder.ts (100%) rename src/{ => v1}/function-configuration.ts (100%) rename src/{ => v1}/handler-builder.ts (100%) create mode 100644 src/v1/index.ts rename src/{ => v1}/providers/analytics.ts (100%) rename src/{ => v1}/providers/auth.ts (100%) rename src/{ => v1}/providers/database.ts (99%) rename src/{ => v1}/providers/firestore.ts (100%) rename src/{ => v1}/providers/https.ts (99%) rename src/{ => v1}/providers/pubsub.ts (100%) rename src/{ => v1}/providers/remoteConfig.ts (100%) rename src/{ => v1}/providers/storage.ts (100%) rename src/{ => v1}/providers/testLab.ts (100%) rename src/{ => v1}/setup.ts (98%) rename src/{ => v1}/utils.ts (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 900cd7d55..adfc05c3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,3 @@ - Adds support for setting user labels on functions via `runWith()`. - Adds support for FIREBASE_CONFIG env as the name of a JSON file +- Formalize module exports. Loggers can now be accessed at 'firebase-functions/logger' and 'firebase-functions/logger/compat' diff --git a/package.json b/package.json index a9c86bf87..413a81c45 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,14 @@ ], "main": "lib/index.js", "types": "lib/index.d.ts", + "exports": { + ".": "./lib/index.js", + "./v1": "./lib/v1/index.js", + "./logger": "./lib/logger/index.js", + "./logger/compat": "./lib/logger/compat.js", + "./lib/logger": "./lib/logger/index.js", + "./lib/logger/compat": "./lib/logger/compat.js" + }, "publishConfig": { "registry": "https://wombat-dressing-room.appspot.com" }, diff --git a/spec/apps.spec.ts b/spec/v1/apps.spec.ts similarity index 98% rename from spec/apps.spec.ts rename to spec/v1/apps.spec.ts index 9fff98c71..33c263f4f 100644 --- a/spec/apps.spec.ts +++ b/spec/v1/apps.spec.ts @@ -21,7 +21,7 @@ // SOFTWARE. import { expect } from 'chai'; -import { apps as appsNamespace } from '../src/apps'; +import { apps as appsNamespace } from '../../src/v1/apps'; import * as firebase from 'firebase-admin'; import * as _ from 'lodash'; diff --git a/spec/cloud-functions.spec.ts b/spec/v1/cloud-functions.spec.ts similarity index 99% rename from spec/cloud-functions.spec.ts rename to spec/v1/cloud-functions.spec.ts index ab17fbc3e..48ecb3072 100644 --- a/spec/cloud-functions.spec.ts +++ b/spec/v1/cloud-functions.spec.ts @@ -29,7 +29,7 @@ import { EventContext, makeCloudFunction, MakeCloudFunctionArgs, -} from '../src/cloud-functions'; +} from '../../src/v1/cloud-functions'; describe('makeCloudFunction', () => { const cloudFunctionArgs: MakeCloudFunctionArgs = { diff --git a/spec/config.spec.ts b/spec/v1/config.spec.ts similarity index 98% rename from spec/config.spec.ts rename to spec/v1/config.spec.ts index 1f93ea10e..8a40fe523 100644 --- a/spec/config.spec.ts +++ b/spec/v1/config.spec.ts @@ -25,7 +25,7 @@ import * as fs from 'fs'; import * as mockRequire from 'mock-require'; import Sinon = require('sinon'); -import * as config from '../src/config'; +import * as config from '../../src/v1/config'; describe('config()', () => { let readFileSync: Sinon.SinonStub; diff --git a/spec/function-builder.spec.ts b/spec/v1/function-builder.spec.ts similarity index 99% rename from spec/function-builder.spec.ts rename to spec/v1/function-builder.spec.ts index 106f874ea..431d018ac 100644 --- a/spec/function-builder.spec.ts +++ b/spec/v1/function-builder.spec.ts @@ -22,7 +22,7 @@ import { expect } from 'chai'; -import * as functions from '../src/index'; +import * as functions from '../../src/v1'; describe('FunctionBuilder', () => { before(() => { diff --git a/spec/providers/analytics.spec.input.ts b/spec/v1/providers/analytics.spec.input.ts similarity index 98% rename from spec/providers/analytics.spec.input.ts rename to spec/v1/providers/analytics.spec.input.ts index bd9768b16..74ad65a93 100644 --- a/spec/providers/analytics.spec.input.ts +++ b/spec/v1/providers/analytics.spec.input.ts @@ -21,7 +21,7 @@ // SOFTWARE. /* tslint:disable:max-line-length */ -import { AnalyticsEvent } from '../../src/providers/analytics'; +import { AnalyticsEvent } from '../../../src/v1/providers/analytics'; // A payload, as it might arrive over the wire. Every possible field is filled out at least once. export const fullPayload = JSON.parse(`{ diff --git a/spec/providers/analytics.spec.ts b/spec/v1/providers/analytics.spec.ts similarity index 98% rename from spec/providers/analytics.spec.ts rename to spec/v1/providers/analytics.spec.ts index 4a5e84a0c..a6e6b0af6 100644 --- a/spec/providers/analytics.spec.ts +++ b/spec/v1/providers/analytics.spec.ts @@ -22,9 +22,9 @@ import { expect } from 'chai'; -import { Event, EventContext } from '../../src/cloud-functions'; -import * as functions from '../../src/index'; -import * as analytics from '../../src/providers/analytics'; +import * as functions from '../../../src/v1'; +import { Event, EventContext } from '../../../src/v1/cloud-functions'; +import * as analytics from '../../../src/v1/providers/analytics'; import * as analytics_spec_input from './analytics.spec.input'; describe('Analytics Functions', () => { diff --git a/spec/providers/auth.spec.ts b/spec/v1/providers/auth.spec.ts similarity index 97% rename from spec/providers/auth.spec.ts rename to spec/v1/providers/auth.spec.ts index bb2ab7761..df0db01f7 100644 --- a/spec/providers/auth.spec.ts +++ b/spec/v1/providers/auth.spec.ts @@ -23,9 +23,13 @@ import { expect } from 'chai'; import * as firebase from 'firebase-admin'; -import { CloudFunction, Event, EventContext } from '../../src/cloud-functions'; -import * as functions from '../../src/index'; -import * as auth from '../../src/providers/auth'; +import * as functions from '../../../src/index'; +import { + CloudFunction, + Event, + EventContext, +} from '../../../src/v1/cloud-functions'; +import * as auth from '../../../src/v1/providers/auth'; describe('Auth Functions', () => { const event: Event = { diff --git a/spec/providers/database.spec.ts b/spec/v1/providers/database.spec.ts similarity index 98% rename from spec/providers/database.spec.ts rename to spec/v1/providers/database.spec.ts index 10e1b0efb..27d5854c6 100644 --- a/spec/providers/database.spec.ts +++ b/spec/v1/providers/database.spec.ts @@ -21,11 +21,11 @@ // SOFTWARE. import { expect } from 'chai'; -import { apps as appsNamespace } from '../../src/apps'; -import * as config from '../../src/config'; -import * as functions from '../../src/index'; -import * as database from '../../src/providers/database'; -import { applyChange } from '../../src/utils'; +import { apps as appsNamespace } from '../../../src/v1/apps'; +import * as config from '../../../src/v1/config'; +import * as functions from '../../../src/v1/index'; +import * as database from '../../../src/v1/providers/database'; +import { applyChange } from '../../../src/v1/utils'; describe('Database Functions', () => { describe('DatabaseBuilder', () => { diff --git a/spec/providers/firestore.spec.ts b/spec/v1/providers/firestore.spec.ts similarity index 99% rename from spec/providers/firestore.spec.ts rename to spec/v1/providers/firestore.spec.ts index 271701108..07014aeb0 100644 --- a/spec/providers/firestore.spec.ts +++ b/spec/v1/providers/firestore.spec.ts @@ -24,8 +24,8 @@ import { expect } from 'chai'; import * as admin from 'firebase-admin'; import * as _ from 'lodash'; -import * as functions from '../../src/index'; -import * as firestore from '../../src/providers/firestore'; +import * as functions from '../../../src/index'; +import * as firestore from '../../../src/v1/providers/firestore'; describe('Firestore Functions', () => { function constructValue(fields: any) { diff --git a/spec/providers/https.spec.ts b/spec/v1/providers/https.spec.ts similarity index 98% rename from spec/providers/https.spec.ts rename to spec/v1/providers/https.spec.ts index 128ea0058..b34e29e6e 100644 --- a/spec/providers/https.spec.ts +++ b/spec/v1/providers/https.spec.ts @@ -24,10 +24,10 @@ import { expect } from 'chai'; import * as express from 'express'; import * as firebase from 'firebase-admin'; import * as _ from 'lodash'; -import { apps as appsNamespace } from '../../src/apps'; -import * as functions from '../../src/index'; -import * as https from '../../src/providers/https'; -import * as mocks from '../fixtures/credential/key.json'; +import * as functions from '../../../src/index'; +import { apps as appsNamespace } from '../../../src/v1/apps'; +import * as https from '../../../src/v1/providers/https'; +import * as mocks from '../../fixtures/credential/key.json'; import { expectedResponseHeaders, generateAppCheckToken, @@ -36,7 +36,7 @@ import { mockFetchPublicKeys, MockRequest, mockRequest, -} from '../fixtures/mockrequest'; +} from '../../fixtures/mockrequest'; describe('CloudHttpsBuilder', () => { describe('#onRequest', () => { diff --git a/spec/providers/pubsub.spec.ts b/spec/v1/providers/pubsub.spec.ts similarity index 98% rename from spec/providers/pubsub.spec.ts rename to spec/v1/providers/pubsub.spec.ts index f64cd2119..3d0569057 100644 --- a/spec/providers/pubsub.spec.ts +++ b/spec/v1/providers/pubsub.spec.ts @@ -21,9 +21,9 @@ // SOFTWARE. import { expect } from 'chai'; -import { Event } from '../../src/index'; -import * as functions from '../../src/index'; -import * as pubsub from '../../src/providers/pubsub'; +import { Event } from '../../../src/index'; +import * as functions from '../../../src/index'; +import * as pubsub from '../../../src/v1/providers/pubsub'; describe('Pubsub Functions', () => { describe('pubsub.Message', () => { diff --git a/spec/providers/remoteConfig.spec.ts b/spec/v1/providers/remoteConfig.spec.ts similarity index 96% rename from spec/providers/remoteConfig.spec.ts rename to spec/v1/providers/remoteConfig.spec.ts index ef6d68572..2ccfe41fd 100644 --- a/spec/providers/remoteConfig.spec.ts +++ b/spec/v1/providers/remoteConfig.spec.ts @@ -22,14 +22,14 @@ import { expect } from 'chai'; import * as _ from 'lodash'; +import * as functions from '../../../src/index'; import { CloudFunction, Event, EventContext, TriggerAnnotated, -} from '../../src/cloud-functions'; -import * as functions from '../../src/index'; -import * as remoteConfig from '../../src/providers/remoteConfig'; +} from '../../../src/v1/cloud-functions'; +import * as remoteConfig from '../../../src/v1/providers/remoteConfig'; describe('RemoteConfig Functions', () => { function constructVersion() { diff --git a/spec/providers/storage.spec.ts b/spec/v1/providers/storage.spec.ts similarity index 98% rename from spec/providers/storage.spec.ts rename to spec/v1/providers/storage.spec.ts index c104a076f..aee2d72a9 100644 --- a/spec/providers/storage.spec.ts +++ b/spec/v1/providers/storage.spec.ts @@ -21,10 +21,10 @@ // SOFTWARE. import { expect } from 'chai'; -import * as config from '../../src/config'; -import { Event, EventContext } from '../../src/index'; -import * as functions from '../../src/index'; -import * as storage from '../../src/providers/storage'; +import { Event, EventContext } from '../../../src/v1'; +import * as functions from '../../../src/v1'; +import * as config from '../../../src/v1/config'; +import * as storage from '../../../src/v1/providers/storage'; describe('Storage Functions', () => { describe('ObjectBuilder', () => { diff --git a/spec/providers/testLab.spec.ts b/spec/v1/providers/testLab.spec.ts similarity index 99% rename from spec/providers/testLab.spec.ts rename to spec/v1/providers/testLab.spec.ts index 8a70cf680..5584bfbf7 100644 --- a/spec/providers/testLab.spec.ts +++ b/spec/v1/providers/testLab.spec.ts @@ -22,7 +22,7 @@ import { expect } from 'chai'; -import * as testLab from '../../src/providers/testLab'; +import * as testLab from '../../../src/v1/providers/testLab'; describe('Test Lab Functions', () => { describe('#onComplete', () => { diff --git a/spec/setup.spec.ts b/spec/v1/setup.spec.ts similarity index 97% rename from spec/setup.spec.ts rename to spec/v1/setup.spec.ts index 289b90fc1..20ca26d12 100644 --- a/spec/setup.spec.ts +++ b/spec/v1/setup.spec.ts @@ -21,7 +21,7 @@ // SOFTWARE. import { expect } from 'chai'; -import { setup } from '../src/setup'; +import { setup } from '../../src/v1/setup'; describe('setup()', () => { afterEach(() => { diff --git a/spec/utils.spec.ts b/spec/v1/utils.spec.ts similarity index 97% rename from spec/utils.spec.ts rename to spec/v1/utils.spec.ts index 51a8478fa..119d203f6 100644 --- a/spec/utils.spec.ts +++ b/spec/v1/utils.spec.ts @@ -21,7 +21,7 @@ // SOFTWARE. import { expect } from 'chai'; -import { applyChange } from '../src/utils'; +import { applyChange } from '../../src/v1/utils'; describe('utils', () => { describe('.applyChange(from: any, to: any): any', () => { diff --git a/src/index.ts b/src/index.ts index dbb42003c..5b98253d9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,62 +1 @@ -// 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. - -// Providers: -import * as analytics from './providers/analytics'; -import * as auth from './providers/auth'; -import * as database from './providers/database'; -import * as firestore from './providers/firestore'; -import * as https from './providers/https'; -import * as pubsub from './providers/pubsub'; -import * as remoteConfig from './providers/remoteConfig'; -import * as storage from './providers/storage'; -import * as testLab from './providers/testLab'; - -import * as apps from './apps'; -import { handler } from './handler-builder'; -import * as logger from './logger'; -import { setup } from './setup'; - -const app = apps.apps(); - -export { - analytics, - app, - auth, - database, - firestore, - handler, - https, - pubsub, - remoteConfig, - storage, - testLab, - logger, -}; - -// Exported root types: -export * from './cloud-functions'; -export * from './config'; -export * from './function-builder'; -export * from './function-configuration'; - -setup(); +export * from './v1'; diff --git a/src/logger.ts b/src/logger/index.ts similarity index 99% rename from src/logger.ts rename to src/logger/index.ts index 1dd109179..24483087c 100644 --- a/src/logger.ts +++ b/src/logger/index.ts @@ -4,7 +4,7 @@ import { CONSOLE_SEVERITY, SUPPORTS_STRUCTURED_LOGS, UNPATCHED_CONSOLE, -} from './logger/common'; +} from './common'; /** * `LogSeverity` indicates the detailed severity of the log entry. See [LogSeverity](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity). diff --git a/src/apps.ts b/src/v1/apps.ts similarity index 100% rename from src/apps.ts rename to src/v1/apps.ts diff --git a/src/cloud-functions.ts b/src/v1/cloud-functions.ts similarity index 99% rename from src/cloud-functions.ts rename to src/v1/cloud-functions.ts index 2a6c770e6..ef59c0422 100644 --- a/src/cloud-functions.ts +++ b/src/v1/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 */ diff --git a/src/config.ts b/src/v1/config.ts similarity index 100% rename from src/config.ts rename to src/v1/config.ts diff --git a/src/encoder.ts b/src/v1/encoder.ts similarity index 100% rename from src/encoder.ts rename to src/v1/encoder.ts diff --git a/src/function-builder.ts b/src/v1/function-builder.ts similarity index 100% rename from src/function-builder.ts rename to src/v1/function-builder.ts diff --git a/src/function-configuration.ts b/src/v1/function-configuration.ts similarity index 100% rename from src/function-configuration.ts rename to src/v1/function-configuration.ts diff --git a/src/handler-builder.ts b/src/v1/handler-builder.ts similarity index 100% rename from src/handler-builder.ts rename to src/v1/handler-builder.ts diff --git a/src/v1/index.ts b/src/v1/index.ts new file mode 100644 index 000000000..ec83eeab5 --- /dev/null +++ b/src/v1/index.ts @@ -0,0 +1,62 @@ +// 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. + +// Providers: +import * as analytics from './providers/analytics'; +import * as auth from './providers/auth'; +import * as database from './providers/database'; +import * as firestore from './providers/firestore'; +import * as https from './providers/https'; +import * as pubsub from './providers/pubsub'; +import * as remoteConfig from './providers/remoteConfig'; +import * as storage from './providers/storage'; +import * as testLab from './providers/testLab'; + +import * as logger from '../logger'; +import * as apps from './apps'; +import { handler } from './handler-builder'; +import { setup } from './setup'; + +const app = apps.apps(); + +export { + analytics, + app, + auth, + database, + firestore, + handler, + https, + pubsub, + remoteConfig, + storage, + testLab, + logger, +}; + +// Exported root types: +export * from './cloud-functions'; +export * from './config'; +export * from './function-builder'; +export * from './function-configuration'; + +setup(); diff --git a/src/providers/analytics.ts b/src/v1/providers/analytics.ts similarity index 100% rename from src/providers/analytics.ts rename to src/v1/providers/analytics.ts diff --git a/src/providers/auth.ts b/src/v1/providers/auth.ts similarity index 100% rename from src/providers/auth.ts rename to src/v1/providers/auth.ts diff --git a/src/providers/database.ts b/src/v1/providers/database.ts similarity index 99% rename from src/providers/database.ts rename to src/v1/providers/database.ts index 76252d515..372419169 100644 --- a/src/providers/database.ts +++ b/src/v1/providers/database.ts @@ -22,6 +22,7 @@ import * as firebase from 'firebase-admin'; import * as _ from 'lodash'; +import { joinPath, normalizePath, pathParts } from '../../utilities/path'; import { apps } from '../apps'; import { Change, @@ -32,7 +33,6 @@ import { } from '../cloud-functions'; import { firebaseConfig } from '../config'; import { DeploymentOptions } from '../function-configuration'; -import { joinPath, normalizePath, pathParts } from '../utilities/path'; import { applyChange } from '../utils'; /** @hidden */ diff --git a/src/providers/firestore.ts b/src/v1/providers/firestore.ts similarity index 100% rename from src/providers/firestore.ts rename to src/v1/providers/firestore.ts diff --git a/src/providers/https.ts b/src/v1/providers/https.ts similarity index 99% rename from src/providers/https.ts rename to src/v1/providers/https.ts index c6167c50a..c6d5cc8a0 100644 --- a/src/providers/https.ts +++ b/src/v1/providers/https.ts @@ -25,10 +25,10 @@ import * as express from 'express'; import * as firebase from 'firebase-admin'; import * as _ from 'lodash'; +import { error, info, warn } from '../../logger'; import { apps } from '../apps'; import { HttpsFunction, optionsToTrigger, Runnable } from '../cloud-functions'; import { DeploymentOptions } from '../function-configuration'; -import { error, info, warn } from '../logger'; /** @hidden */ export interface Request extends express.Request { diff --git a/src/providers/pubsub.ts b/src/v1/providers/pubsub.ts similarity index 100% rename from src/providers/pubsub.ts rename to src/v1/providers/pubsub.ts diff --git a/src/providers/remoteConfig.ts b/src/v1/providers/remoteConfig.ts similarity index 100% rename from src/providers/remoteConfig.ts rename to src/v1/providers/remoteConfig.ts diff --git a/src/providers/storage.ts b/src/v1/providers/storage.ts similarity index 100% rename from src/providers/storage.ts rename to src/v1/providers/storage.ts diff --git a/src/providers/testLab.ts b/src/v1/providers/testLab.ts similarity index 100% rename from src/providers/testLab.ts rename to src/v1/providers/testLab.ts diff --git a/src/setup.ts b/src/v1/setup.ts similarity index 98% rename from src/setup.ts rename to src/v1/setup.ts index 6a9db702d..6b0eb3506 100644 --- a/src/setup.ts +++ b/src/v1/setup.ts @@ -21,8 +21,8 @@ // SOFTWARE. /** @hidden */ +import { warn } from '../logger'; import { firebaseConfig } from './config'; -import { warn } from './logger'; // Set up for config and vars export function setup() { diff --git a/src/utils.ts b/src/v1/utils.ts similarity index 100% rename from src/utils.ts rename to src/v1/utils.ts diff --git a/tsconfig.release.json b/tsconfig.release.json index 1a45bd58b..b8f2632fd 100644 --- a/tsconfig.release.json +++ b/tsconfig.release.json @@ -10,5 +10,5 @@ "target": "es2018", "typeRoots": ["./node_modules/@types"] }, - "files": ["./src/index.ts", "./src/logger.ts", "./src/logger/compat.ts"] + "files": ["./src/index.ts", "./src/logger/index.ts", "./src/logger/compat.ts"] } From 57ca58a1e3b8a4db652bc31e477c8b95fa5899a3 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Mon, 12 Jul 2021 11:27:20 -0700 Subject: [PATCH 118/529] RemoteConfig can be loaded in windows CMD.exe (#913) Favors using process.cwd over process.env.PWD as the latter is POSIX only (I might need to go check some of my code in firebase-tools!). While I was at it, I fixed a probably useless but nasty vulnerability where a malformed .runtimeconfig.json file would allow arbitrary code execution. --- CHANGELOG.md | 1 + spec/v1/config.spec.ts | 29 +++++++++++++++++++---------- src/v1/config.ts | 10 ++++++---- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index adfc05c3a..45844cd0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ - Adds support for setting user labels on functions via `runWith()`. - Adds support for FIREBASE_CONFIG env as the name of a JSON file - Formalize module exports. Loggers can now be accessed at 'firebase-functions/logger' and 'firebase-functions/logger/compat' +- Fixes an issue where Remote Config coiuld not be emulated in Windows machines on the classic Command Prompt. diff --git a/spec/v1/config.spec.ts b/spec/v1/config.spec.ts index 8a40fe523..d1deea4fd 100644 --- a/spec/v1/config.spec.ts +++ b/spec/v1/config.spec.ts @@ -22,27 +22,27 @@ import { expect } from 'chai'; import * as fs from 'fs'; -import * as mockRequire from 'mock-require'; +import * as process from 'process'; import Sinon = require('sinon'); import * as config from '../../src/v1/config'; describe('config()', () => { let readFileSync: Sinon.SinonStub; + let cwdStub: Sinon.SinonStub; before(() => { readFileSync = Sinon.stub(fs, 'readFileSync'); readFileSync.throws('Unexpected call'); - process.env.PWD = '/srv'; + cwdStub = Sinon.stub(process, 'cwd'); + cwdStub.returns('/srv'); }); after(() => { - delete process.env.PWD; Sinon.verifyAndRestore(); }); afterEach(() => { - mockRequire.stopAll(); delete config.config.singleton; (config as any).firebaseConfigCache = null; delete process.env.FIREBASE_CONFIG; @@ -50,19 +50,27 @@ describe('config()', () => { }); it('loads config values from .runtimeconfig.json', () => { - mockRequire('/srv/.runtimeconfig.json', { foo: 'bar', firebase: {} }); + const json = JSON.stringify({ + foo: 'bar', + firebase: {}, + }); + readFileSync + .withArgs('/srv/.runtimeconfig.json') + .returns(Buffer.from(json)); const loaded = config.config(); expect(loaded).to.not.have.property('firebase'); expect(loaded).to.have.property('foo', 'bar'); }); it('does not provide firebase config if .runtimeconfig.json not invalid', () => { - mockRequire('/srv/.runtimeconfig.json', 'does-not-exist'); + readFileSync.withArgs('/srv/.runtimeconfig.json').returns('invalid JSON'); expect(config.firebaseConfig()).to.be.null; }); it('does not provide firebase config if .ruuntimeconfig.json has no firebase property', () => { - mockRequire('/srv/.runtimeconfig.json', {}); + readFileSync + .withArgs('/srv/.runtimeconfig.json') + .returns(Buffer.from('{}')); expect(config.firebaseConfig()).to.be.null; }); @@ -78,7 +86,7 @@ describe('config()', () => { it('loads Firebase configs from FIREBASE_CONFIG env variable pointing to a file', () => { const oldEnv = process.env; - process.env = { + (process as any).env = { ...oldEnv, FIREBASE_CONFIG: '.firebaseconfig.json', }; @@ -91,13 +99,14 @@ describe('config()', () => { 'foo@firebaseio.com' ); } finally { - process.env = oldEnv; + (process as any).env = oldEnv; } }); it('accepts alternative locations for config file', () => { process.env.CLOUD_RUNTIME_CONFIG = 'another.json'; - mockRequire('another.json', { foo: 'bar', firebase: {} }); + const json = JSON.stringify({ foo: 'bar', firebase: {} }); + readFileSync.withArgs('another.json').returns(Buffer.from(json)); expect(config.firebaseConfig()).to.not.be.null; expect(config.config()).to.have.property('foo', 'bar'); }); diff --git a/src/v1/config.ts b/src/v1/config.ts index c49e7d03f..f0793957d 100644 --- a/src/v1/config.ts +++ b/src/v1/config.ts @@ -89,8 +89,9 @@ export function firebaseConfig(): firebase.AppOptions | null { try { const configPath = process.env.CLOUD_RUNTIME_CONFIG || - path.join(process.env.PWD, '.runtimeconfig.json'); - const config = require(configPath); + path.join(process.cwd(), '.runtimeconfig.json'); + const contents = fs.readFileSync(configPath); + const config = JSON.parse(contents.toString('utf8')); if (config.firebase) { firebaseConfigCache = config.firebase; return firebaseConfigCache; @@ -115,8 +116,9 @@ function init() { try { const configPath = process.env.CLOUD_RUNTIME_CONFIG || - path.join(process.env.PWD, '.runtimeconfig.json'); - const parsed = require(configPath); + path.join(process.cwd(), '.runtimeconfig.json'); + const contents = fs.readFileSync(configPath); + const parsed = JSON.parse(contents.toString('utf8')); delete parsed.firebase; config.singleton = parsed; return; From 5e7fb2cf68821b26fcb8519f88a270577bfe6f11 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Tue, 13 Jul 2021 16:19:17 -0700 Subject: [PATCH 119/529] Fixes docgen after file renames. (#914) --- .gitignore | 1 + docgen/content-sources/toc.yaml | 120 +- docgen/generate-docs.js | 95 +- docgen/theme/helpers/cleanBreadcrumb.js | 4 + docgen/theme/partials/breadcrumb.hbs | 2 +- docgen/theme/partials/header.hbs | 2 +- package-lock.json | 5923 ++++++++++++++++++++++- src/logger/index.ts | 1 + 8 files changed, 6020 insertions(+), 128 deletions(-) create mode 100644 docgen/theme/helpers/cleanBreadcrumb.js diff --git a/.gitignore b/.gitignore index ba3cdeec5..b5a890408 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ node_modules npm-debug.log typings yarn.lock +.DS_Store diff --git a/docgen/content-sources/toc.yaml b/docgen/content-sources/toc.yaml index e970fc80c..ac430151b 100644 --- a/docgen/content-sources/toc.yaml +++ b/docgen/content-sources/toc.yaml @@ -1,148 +1,148 @@ toc: - title: 'functions' - path: /docs/reference/functions/cloud_functions_.html + path: /docs/reference/functions/v1_cloud_functions_.html section: - title: 'CloudFunction' - path: /docs/reference/functions/cloud_functions_.html#cloudfunction + path: /docs/reference/functions/v1_cloud_functions_.html#cloudfunction - title: 'HttpsFunction' - path: /docs/reference/functions/cloud_functions_.html#httpsfunction + path: /docs/reference/functions/v1_cloud_functions_.html#httpsfunction - title: 'EventContext' - path: /docs/reference/functions/cloud_functions_.eventcontext.html + path: /docs/reference/functions/v1_cloud_functions_.eventcontext.html - title: 'FunctionBuilder' - path: /docs/reference/functions/function_builder_.functionbuilder.html + path: /docs/reference/functions/v1_function_builder_.functionbuilder.html - title: 'Change' - path: /docs/reference/functions/cloud_functions_.change.html + path: /docs/reference/functions/v1_cloud_functions_.change.html - title: 'ChangeJson' - path: /docs/reference/functions/cloud_functions_.changejson.html + path: /docs/reference/functions/v1_cloud_functions_.changejson.html - title: 'functions.config' - path: /docs/reference/functions/config_.html + path: /docs/reference/functions/v1_config_.html section: - title: 'Config' - path: /docs/reference/functions/config_.config.html + path: /docs/reference/functions/v1_config_.config.html - title: 'config.Config' - path: /docs/reference/functions/config_.config.config.html + path: /docs/reference/functions/v1_config_.config.config.html - title: 'functions.function-configuration' - path: /docs/reference/functions/function_configuration_.html + path: /docs/reference/functions/v1_function_configuration_.html section: - title: 'config.DeploymentOptions' - path: /docs/reference/functions/function_configuration_.deploymentoptions.html + path: /docs/reference/functions/v1_function_configuration_.deploymentoptions.html - title: 'config.FailurePolicy' - path: /docs/reference/functions/function_configuration_.failurepolicy.html + path: /docs/reference/functions/v1_function_configuration_.failurepolicy.html - title: 'config.RuntimeOptions' - path: /docs/reference/functions/function_configuration_.runtimeoptions.html + path: /docs/reference/functions/v1_function_configuration_.runtimeoptions.html - title: 'config.Schedule' - path: /docs/reference/functions/function_configuration_.schedule.html + path: /docs/reference/functions/v1_function_configuration_.schedule.html - title: 'config.ScheduleRetryConfig' - path: /docs/reference/functions/function_configuration_.scheduleretryconfig.html + path: /docs/reference/functions/v1_function_configuration_.scheduleretryconfig.html - title: 'functions.analytics' - path: /docs/reference/functions/providers_analytics_.html + path: /docs/reference/functions/v1_providers_analytics_.html section: - title: 'AnalyticsEvent' - path: /docs/reference/functions/providers_analytics_.analyticsevent.html + path: /docs/reference/functions/v1_providers_analytics_.analyticsevent.html - title: 'AnalyticsEventBuilder' - path: /docs/reference/functions/providers_analytics_.analyticseventbuilder.html + path: /docs/reference/functions/v1_providers_analytics_.analyticseventbuilder.html - title: 'AppInfo' - path: /docs/reference/functions/providers_analytics_.appinfo.html + path: /docs/reference/functions/v1_providers_analytics_.appinfo.html - title: 'DeviceInfo' - path: /docs/reference/functions/providers_analytics_.deviceinfo.html + path: /docs/reference/functions/v1_providers_analytics_.deviceinfo.html - title: 'ExportBundleInfo' - path: /docs/reference/functions/providers_analytics_.exportbundleinfo.html + path: /docs/reference/functions/v1_providers_analytics_.exportbundleinfo.html - title: 'GeoInfo' - path: /docs/reference/functions/providers_analytics_.geoinfo.html + path: /docs/reference/functions/v1_providers_analytics_.geoinfo.html - title: 'UserDimensions' - path: /docs/reference/functions/providers_analytics_.userdimensions.html + path: /docs/reference/functions/v1_providers_analytics_.userdimensions.html - title: 'UserPropertyValue' - path: /docs/reference/functions/providers_analytics_.userpropertyvalue.html + path: /docs/reference/functions/v1_providers_analytics_.userpropertyvalue.html - title: 'functions.auth' - path: /docs/reference/functions/providers_auth_.html + path: /docs/reference/functions/v1_providers_auth_.html section: - title: 'UserBuilder' - path: /docs/reference/functions/providers_auth_.userbuilder.html + path: /docs/reference/functions/v1_providers_auth_.userbuilder.html - title: 'UserInfo' - path: /docs/reference/functions/providers_auth_.html#userinfo + path: /docs/reference/functions/v1_providers_auth_.html#userinfo - title: 'UserRecordMetadata' - path: /docs/reference/functions/providers_auth_.userrecordmetadata.html + path: /docs/reference/functions/v1_providers_auth_.userrecordmetadata.html - title: 'UserRecord' - path: /docs/reference/functions/providers_auth_.html#userrecord + path: /docs/reference/functions/v1_providers_auth_.html#userrecord - title: 'functions.firestore' - path: /docs/reference/functions/providers_firestore_.html + path: /docs/reference/functions/v1_providers_firestore_.html section: - title: 'DocumentBuilder' - path: /docs/reference/functions/providers_firestore_.documentbuilder.html + path: /docs/reference/functions/v1_providers_firestore_.documentbuilder.html - title: 'DocumentSnapshot' - path: /docs/reference/functions/providers_firestore_.html#documentsnapshot + path: /docs/reference/functions/v1_providers_firestore_.html#documentsnapshot - title: 'functions.database' - path: /docs/reference/functions/providers_database_.html + path: /docs/reference/functions/v1_providers_database_.html section: - title: 'DataSnapshot' - path: /docs/reference/functions/providers_database_.datasnapshot.html + path: /docs/reference/functions/v1_providers_database_.datasnapshot.html - title: 'RefBuilder' - path: /docs/reference/functions/providers_database_.refbuilder.html + path: /docs/reference/functions/v1_providers_database_.refbuilder.html - title: 'InstanceBuilder' - path: /docs/reference/functions/providers_database_.instancebuilder.html + path: /docs/reference/functions/v1_providers_database_.instancebuilder.html - title: 'functions.https' - path: /docs/reference/functions/providers_https_.html + path: /docs/reference/functions/v1_providers_https_.html section: - title: 'HttpsError' - path: /docs/reference/functions/providers_https_.httpserror.html + path: /docs/reference/functions/v1_providers_https_.httpserror.html - title: 'CallableContext' - path: /docs/reference/functions/providers_https_.callablecontext.html + path: /docs/reference/functions/v1_providers_https_.callablecontext.html - title: 'functions.logger' - path: /docs/reference/functions/logger_.html + path: /docs/reference/functions/logger_index_.html section: - title: 'LogEntry' - path: /docs/reference/functions/logger_.logentry.html + path: /docs/reference/functions/logger_index_.logentry.html - title: 'functions.pubsub' - path: /docs/reference/functions/providers_pubsub_.html + path: /docs/reference/functions/v1_providers_pubsub_.html section: - title: 'Message' - path: /docs/reference/functions/providers_pubsub_.message.html + path: /docs/reference/functions/v1_providers_pubsub_.message.html - title: 'TopicBuilder' - path: /docs/reference/functions/providers_pubsub_.topicbuilder.html + path: /docs/reference/functions/v1_providers_pubsub_.topicbuilder.html - title: 'ScheduleBuilder' - path: /docs/reference/functions/providers_pubsub_.schedulebuilder.html + path: /docs/reference/functions/v1_providers_pubsub_.schedulebuilder.html - title: 'functions.remoteconfig' - path: /docs/reference/functions/providers_remoteconfig_.html + path: /docs/reference/functions/v1_providers_remoteconfig_.html section: - title: 'RemoteConfigUser' - path: /docs/reference/functions/providers_remoteconfig_.remoteconfiguser.html + path: /docs/reference/functions/v1_providers_remoteconfig_.remoteconfiguser.html - title: 'TemplateVersion' - path: /docs/reference/functions/providers_remoteconfig_.templateversion.html + path: /docs/reference/functions/v1_providers_remoteconfig_.templateversion.html - title: 'functions.storage' - path: /docs/reference/functions/providers_storage_.html + path: /docs/reference/functions/v1_providers_storage_.html section: - title: 'BucketBuilder' - path: /docs/reference/functions/providers_storage_.bucketbuilder.html + path: /docs/reference/functions/v1_providers_storage_.bucketbuilder.html - title: 'ObjectBuilder' - path: /docs/reference/functions/providers_storage_.objectbuilder.html + path: /docs/reference/functions/v1_providers_storage_.objectbuilder.html - title: 'ObjectMetadata' - path: /docs/reference/functions/providers_storage_.objectmetadata.html + path: /docs/reference/functions/v1_providers_storage_.objectmetadata.html - title: 'functions.testLab' - path: /docs/reference/functions/providers_testlab_.html + path: /docs/reference/functions/v1_providers_testlab_.html section: - title: 'testLab.clientInfo' - path: /docs/reference/functions/providers_testlab_.clientinfo.html + path: /docs/reference/functions/v1_providers_testlab_.clientinfo.html - title: 'testLab.resultStorage' - path: /docs/reference/functions/providers_testlab_.resultstorage.html + path: /docs/reference/functions/v1_providers_testlab_.resultstorage.html - title: 'testLab.testMatrix' - path: /docs/reference/functions/providers_testlab_.testmatrix.html + path: /docs/reference/functions/v1_providers_testlab_.testmatrix.html - title: 'testLab.testMatrixBuilder' - path: /docs/reference/functions/providers_testlab_.testmatrixbuilder.html + path: /docs/reference/functions/v1_providers_testlab_.testmatrixbuilder.html - title: 'functions.handler' - path: /docs/reference/functions/handler_builder_.html + path: /docs/reference/functions/v1_handler_builder_.html section: - title: 'HandlerBuilder' - path: /docs/reference/functions/handler_builder_.handlerbuilder.html + path: /docs/reference/functions/v1_handler_builder_.handlerbuilder.html diff --git a/docgen/generate-docs.js b/docgen/generate-docs.js index d36edb7e8..2f8b9abb0 100644 --- a/docgen/generate-docs.js +++ b/docgen/generate-docs.js @@ -26,9 +26,9 @@ const repoPath = path.resolve(`${__dirname}/..`); // Command-line options. const { source: sourceFile } = yargs .option('source', { - default: `${repoPath}/src`, + default: `${repoPath}/src/{v1,logger}`, describe: 'Typescript source file(s)', - type: 'string' + type: 'string', }) .version(false) .help().argv; @@ -38,7 +38,7 @@ const contentPath = path.resolve(`${__dirname}/content-sources`); const tempHomePath = path.resolve(`${contentPath}/HOME_TEMP.md`); const devsitePath = `/docs/reference/functions/`; -const { JSDOM } = require("jsdom"); +const { JSDOM } = require('jsdom'); const typeMap = require('./type-aliases.json'); @@ -72,7 +72,7 @@ function runTypedoc() { * @param {string} subdir Subdir to move files out of. */ async function moveFilesToRoot(subdir) { - await exec(`mv ${docPath}/${subdir}/* ${docPath}`) + await exec(`mv ${docPath}/${subdir}/* ${docPath}`); await exec(`rmdir ${docPath}/${subdir}`); } @@ -86,9 +86,11 @@ async function renameFiles() { const files = await fs.readdir(docPath); const renames = []; for (const file of files) { - if (file.startsWith("_") && file.endsWith("html")) { + if (file.startsWith('_') && file.endsWith('html')) { let newFileName = file.substring(1); - renames.push(fs.rename(`${docPath}/${file}`, `${docPath}/${newFileName}`)); + renames.push( + fs.rename(`${docPath}/${file}`, `${docPath}/${newFileName}`) + ); } } await Promise.all(renames); @@ -125,11 +127,13 @@ function addTypeAliasLinks(data) { * Select .tsd-signature-type because all potential external * links will have this identifier. */ - const fileTags = htmlDom.window.document.querySelectorAll(".tsd-signature-type"); - fileTags.forEach(tag => { + const fileTags = htmlDom.window.document.querySelectorAll( + '.tsd-signature-type' + ); + for (const tag of fileTags) { const mapping = typeMap[tag.textContent]; if (mapping) { - console.log('Adding link to '+tag.textContent+" documentation."); + console.log('Adding link to ' + tag.textContent + ' documentation.'); // Add the corresponding document link to this type const linkChild = htmlDom.window.document.createElement('a'); @@ -138,7 +142,7 @@ function addTypeAliasLinks(data) { tag.textContent = null; tag.appendChild(linkChild); } - }); + } return htmlDom.serialize(); } @@ -152,13 +156,13 @@ function addTypeAliasLinks(data) { function generateTempHomeMdFile(tocRaw, homeRaw) { const { toc } = yaml.safeLoad(tocRaw); let tocPageLines = [homeRaw, '# API Reference']; - toc.forEach(group => { + for (const group of toc) { tocPageLines.push(`\n## [${group.title}](${stripPath(group.path)})`); const section = group.section || []; - section.forEach(item => { + for (const item of section) { tocPageLines.push(`- [${item.title}](${stripPath(item.path)})`); - }); - }); + } + } return fs.writeFile(tempHomePath, tocPageLines.join('\n')); } @@ -176,10 +180,10 @@ 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]); + .filter((line) => line.includes('path:')) + .map((line) => line.split(devsitePath)[1].replace(/#.*$/, '')); // Logs warning to console if a file from TOC is not found. - const fileCheckPromises = filenames.map(async 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. @@ -211,29 +215,28 @@ async function checkForMissingFilesAndFixFilenameCase(tocText) { */ async function checkForUnlistedFiles(filenamesFromToc, shouldRemove) { const files = await fs.readdir(docPath); - const htmlFiles = files - .filter(filename => filename.slice(-4) === 'html'); + 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'); + .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 ` + + `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)) + 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)); } /** @@ -243,10 +246,10 @@ async function checkForUnlistedFiles(filenamesFromToc, shouldRemove) { * @param {Array} htmlFiles List of html files found in generated dir. */ async function writeGeneratedFileList(htmlFiles) { - const fileList = htmlFiles.map(filename => { + const fileList = htmlFiles.map((filename) => { return { title: filename, - path: `${devsitePath}${filename}` + path: `${devsitePath}${filename}`, }; }); const generatedTocYAML = yaml.safeDump({ toc: fileList }); @@ -262,10 +265,10 @@ async function writeGeneratedFileList(htmlFiles) { */ function fixAllLinks(htmlFiles) { const writePromises = []; - htmlFiles.forEach(file => { + for (const file of htmlFiles) { // Update links in each html file to match flattened file structure. writePromises.push(fixLinks(`${docPath}/${file}`)); - }); + } return Promise.all(writePromises); } @@ -281,12 +284,12 @@ function fixAllLinks(htmlFiles) { * links as needed. * 5) Check for mismatches between TOC list and generated file list. */ -(async function() { +(async function () { try { const [tocRaw, homeRaw] = await Promise.all([ fs.readFile(`${contentPath}/toc.yaml`, 'utf8'), - fs.readFile(`${contentPath}/HOME.md`, 'utf8') - ]) + fs.readFile(`${contentPath}/HOME.md`, 'utf8'), + ]); // Run main Typedoc process (uses index.d.ts and generated temp file above). await generateTempHomeMdFile(tocRaw, homeRaw); @@ -324,7 +327,9 @@ function fixAllLinks(htmlFiles) { // Check for files listed in TOC that are missing and warn if so. // Not blocking. - const filenamesFromToc = await checkForMissingFilesAndFixFilenameCase(tocRaw); + 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.) @@ -340,13 +345,15 @@ function fixAllLinks(htmlFiles) { 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); + await fs.writeFile( + `${docPath}/index.html`, + localVariablesIncludeString + data + ); } catch (err) { - if (err.stdout) { - console.error(err.stdout); - } else { - console.error(err); + if (err.stdout) { + console.error(err.stdout); + } else { + console.error(err); + } } -} })(); - diff --git a/docgen/theme/helpers/cleanBreadcrumb.js b/docgen/theme/helpers/cleanBreadcrumb.js new file mode 100644 index 000000000..ad52e64a7 --- /dev/null +++ b/docgen/theme/helpers/cleanBreadcrumb.js @@ -0,0 +1,4 @@ +exports.cleanBreadcrumb = function (value) { + const parts = value.replace(/"/g, '').split('/'); + return parts[parts.length - 1]; +}; diff --git a/docgen/theme/partials/breadcrumb.hbs b/docgen/theme/partials/breadcrumb.hbs index db115163f..6a2147724 100644 --- a/docgen/theme/partials/breadcrumb.hbs +++ b/docgen/theme/partials/breadcrumb.hbs @@ -3,7 +3,7 @@ {{#with parent}}{{> breadcrumb}}{{/with}}