From 50dcd827acf15060a6b800c01fffe24bdc1cc8f5 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 1 Sep 2021 13:31:37 -0700 Subject: [PATCH 001/370] Release minInstances feature (#966) * Release minInstances feature * Lint fixes * PR feedback --- CHANGELOG.md | 1 + src/function-configuration.ts | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..5e4da0ced 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Make the minInstances feature public. diff --git a/src/function-configuration.ts b/src/function-configuration.ts index c4ba01f9c..9ec182a60 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -123,7 +123,6 @@ export interface RuntimeOptions { * Min number of actual instances to be running at a given time. * Instances will be billed for memory allocation and 10% of CPU allocation * while idle. - * @hidden */ minInstances?: number; From c5beb045620534ebc40a266bf43519cfc56e6f61 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 1 Sep 2021 20:35:37 +0000 Subject: [PATCH 002/370] 3.15.5 --- 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 4ca5bc357..57b0ebe0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.15.4", + "version": "3.15.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 2e6b69771..dedee3974 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.15.4", + "version": "3.15.5", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From d46ec6191e61f560f3f21f13333e0f3285d3de90 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 1 Sep 2021 20:35:41 +0000 Subject: [PATCH 003/370] [firebase-release] Removed change log and reset repo after 3.15.5 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e4da0ced..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Make the minInstances feature public. From 5ced4a45df56ebf265f61dbda1054e27a12c2533 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Wed, 15 Sep 2021 10:15:15 -0700 Subject: [PATCH 004/370] Add missing type annotations (#974) * adding config options for cors * update changelog * fix changelog * newline --- CHANGELOG.md | 1 + src/v2/providers/https.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..b22b63fe5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Add missing type annotations diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 612352bc8..f462d270e 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -40,7 +40,7 @@ export interface HttpsOptions extends Omit { | options.SupportedRegion | string | Array; - cors?: string | boolean; + cors?: string | boolean | RegExp | (string | RegExp)[]; } export type HttpsFunction = (( From 6cee06e961b48610d858585d249f15cdca20c6b2 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 15 Sep 2021 19:31:11 +0000 Subject: [PATCH 005/370] 3.15.6 --- 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 57b0ebe0e..eb7037071 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.15.5", + "version": "3.15.6", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index dedee3974..56861df06 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.15.5", + "version": "3.15.6", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From b6cb79776dcc57656fcdcbc072f804142541ab6b Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 15 Sep 2021 19:31:15 +0000 Subject: [PATCH 006/370] [firebase-release] Removed change log and reset repo after 3.15.6 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b22b63fe5..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Add missing type annotations From 0a40a56a2be1a2853703e77e0a1139bfd4063b88 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Mon, 20 Sep 2021 11:34:07 -0700 Subject: [PATCH 007/370] Adjust acceptable options values (#980) --- CHANGELOG.md | 1 + src/v2/options.ts | 2 +- src/v2/providers/https.ts | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..f107824a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Adjust acceptable runtime options values diff --git a/src/v2/options.ts b/src/v2/options.ts index f7a679589..076cb8532 100644 --- a/src/v2/options.ts +++ b/src/v2/options.ts @@ -33,7 +33,7 @@ import { ParamSpec } from './params/types'; /** * List of all regions supported by Cloud Functions v2 */ -export const SUPPORTED_REGIONS = ['us-west1'] as const; +export const SUPPORTED_REGIONS = ['us-west1', 'europe-west4'] as const; /** * A region known to be supported by CloudFunctions v2 diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index f462d270e..54f1a1b36 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -40,7 +40,7 @@ export interface HttpsOptions extends Omit { | options.SupportedRegion | string | Array; - cors?: string | boolean | RegExp | (string | RegExp)[]; + cors?: string | boolean | RegExp | Array; } export type HttpsFunction = (( From 347a381b3300e99aaa94687cb2d4ba8f06b8f42b Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Mon, 20 Sep 2021 18:37:45 +0000 Subject: [PATCH 008/370] 3.15.7 --- 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 eb7037071..b46f581ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.15.6", + "version": "3.15.7", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 56861df06..b2b31acca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.15.6", + "version": "3.15.7", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 10650154ef00187a5ef4ae569cf25f35c769292f Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Mon, 20 Sep 2021 18:37:50 +0000 Subject: [PATCH 009/370] [firebase-release] Removed change log and reset repo after 3.15.7 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f107824a1..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Adjust acceptable runtime options values From 0479817fd00b2c889063748d7f7cb1dfa73ac4dc Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Thu, 30 Sep 2021 11:19:25 -0700 Subject: [PATCH 010/370] GCS Enhancement (#981) * inital gcs work * removing bucket path * adding tests * cleaning up and fixing pr issues * clean up comment * removing redundant code * fixing undefined argument bug * format:fix * removing apiVersion, changing hidden to internal, and cleaning up tests * format * changelog --- CHANGELOG.md | 1 + package.json | 6 +- spec/v2/providers/storage.spec.ts | 489 ++++++++++++++++++++++++++++++ src/v2/index.ts | 3 +- src/v2/providers/storage.ts | 373 +++++++++++++++++++++++ v2/storage.js | 26 ++ 6 files changed, 896 insertions(+), 2 deletions(-) create mode 100644 spec/v2/providers/storage.spec.ts create mode 100644 src/v2/providers/storage.ts create mode 100644 v2/storage.js diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..881256402 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- GCS Enhancement diff --git a/package.json b/package.json index b2b31acca..e1755efa1 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,8 @@ "./v2/options": "./lib/v2/options.js", "./v2/https": "./lib/v2/providers/https.js", "./v2/params": "./lib/v2/params/index.js", - "./v2/pubsub": "./lib/v2/providers/pubsub.js" + "./v2/pubsub": "./lib/v2/providers/pubsub.js", + "./v2/storage": "./lib/v2/providers/storage.js" }, "typesVersions": { "*": { @@ -110,6 +111,9 @@ ], "v2/pubsub": [ "lib/v2/providers/pubsub" + ], + "v2/storage": [ + "lib/v2/providers/storage" ] } }, diff --git a/spec/v2/providers/storage.spec.ts b/spec/v2/providers/storage.spec.ts new file mode 100644 index 000000000..601b5c024 --- /dev/null +++ b/spec/v2/providers/storage.spec.ts @@ -0,0 +1,489 @@ +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import * as config from '../../../src/config'; +import * as options from '../../../src/v2/options'; +import * as storage from '../../../src/v2/providers/storage'; +import { FULL_OPTIONS } from './helpers'; + +const EVENT_TRIGGER = { + eventType: 'event-type', + resource: 'some-bucket', +}; + +describe('v2/storage', () => { + describe('getOptsAndBucket', () => { + it('should return the default bucket with empty opts', () => { + const configStub = sinon + .stub(config, 'firebaseConfig') + .returns({ storageBucket: 'default-bucket' }); + + const [opts, bucket] = storage.getOptsAndBucket({}); + + configStub.restore(); + expect(opts).to.deep.equal({}); + expect(bucket).to.eq('default-bucket'); + }); + + it('should return the default bucket with opts param', () => { + const configStub = sinon + .stub(config, 'firebaseConfig') + .returns({ storageBucket: 'default-bucket' }); + + const [opts, bucket] = storage.getOptsAndBucket({ region: 'us-west1' }); + + configStub.restore(); + expect(opts).to.deep.equal({ region: 'us-west1' }); + expect(bucket).to.eq('default-bucket'); + }); + + it('should return the given bucket', () => { + const [opts, bucket] = storage.getOptsAndBucket('my-bucket'); + + expect(opts).to.deep.equal({}); + expect(bucket).to.eq('my-bucket'); + }); + + it('should return the given bucket and opts', () => { + const [opts, bucket] = storage.getOptsAndBucket({ + bucket: 'my-bucket', + region: 'us-west1', + }); + + expect(opts).to.deep.equal({ region: 'us-west1' }); + expect(bucket).to.eq('my-bucket'); + }); + }); + + describe('onOperation', () => { + let configStub: sinon.SinonStub; + + beforeEach(() => { + process.env.GCLOUD_PROJECT = 'aProject'; + configStub = sinon.stub(config, 'firebaseConfig'); + }); + + afterEach(() => { + options.setGlobalOptions({}); + delete process.env.GCLOUD_PROJECT; + configStub.restore(); + }); + + it('should create a minimal trigger with bucket', () => { + const result = storage.onOperation('event-type', 'some-bucket', () => 42); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: EVENT_TRIGGER, + }); + }); + + it('should create a minimal trigger with opts', () => { + configStub.returns({ storageBucket: 'default-bucket' }); + + const result = storage.onOperation( + 'event-type', + { region: 'us-west1' }, + () => 42 + ); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...EVENT_TRIGGER, + resource: 'default-bucket', + }, + regions: ['us-west1'], + }); + }); + + it('should create a minimal trigger with bucket with opts and bucket', () => { + const result = storage.onOperation( + 'event-type', + { bucket: 'some-bucket' }, + () => 42 + ); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: EVENT_TRIGGER, + }); + }); + + it('should create a complex trigger with appropriate values', () => { + const result = storage.onOperation( + 'event-type', + { + ...FULL_OPTIONS, + bucket: 'some-bucket', + }, + () => 42 + ); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + regions: ['us-west1'], + availableMemoryMb: 512, + timeout: '60s', + minInstances: 1, + maxInstances: 3, + concurrency: 20, + vpcConnector: 'aConnector', + vpcConnectorEgressSettings: 'ALL_TRAFFIC', + serviceAccountEmail: 'root@aProject.iam.gserviceaccount.com', + ingressSettings: 'ALLOW_ALL', + labels: { + hello: 'world', + }, + eventTrigger: EVENT_TRIGGER, + }); + }); + + it('should merge options and globalOptions', () => { + options.setGlobalOptions({ + concurrency: 20, + region: 'europe-west1', + minInstances: 1, + }); + + const result = storage.onOperation( + 'event-type', + { + bucket: 'some-bucket', + region: 'us-west1', + minInstances: 3, + }, + () => 42 + ); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + concurrency: 20, + minInstances: 3, + regions: ['us-west1'], + labels: {}, + eventTrigger: EVENT_TRIGGER, + }); + }); + }); + + describe('onObjectArchived', () => { + const ARCHIVED_TRIGGER = { + ...EVENT_TRIGGER, + eventType: storage.archivedEvent, + }; + let configStub: sinon.SinonStub; + + beforeEach(() => { + configStub = sinon.stub(config, 'firebaseConfig'); + }); + + afterEach(() => { + configStub.restore(); + }); + + it('should accept only handler', () => { + configStub.returns({ storageBucket: 'default-bucket' }); + + const result = storage.onObjectArchived(() => 42); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ARCHIVED_TRIGGER, + resource: 'default-bucket', + }, + }); + }); + + it('should accept bucket and handler', () => { + const result = storage.onObjectArchived('my-bucket', () => 42); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ARCHIVED_TRIGGER, + resource: 'my-bucket', + }, + }); + }); + + it('should accept opts and handler', () => { + const result = storage.onObjectArchived( + { bucket: 'my-bucket', region: 'us-west1' }, + () => 42 + ); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ARCHIVED_TRIGGER, + resource: 'my-bucket', + }, + regions: ['us-west1'], + }); + }); + + it('should accept opts and handler, default bucket', () => { + configStub.returns({ storageBucket: 'default-bucket' }); + + const result = storage.onObjectArchived({ region: 'us-west1' }, () => 42); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ARCHIVED_TRIGGER, + resource: 'default-bucket', + }, + regions: ['us-west1'], + }); + }); + }); + + describe('onObjectFinalized', () => { + const FINALIZED_TRIGGER = { + ...EVENT_TRIGGER, + eventType: storage.finalizedEvent, + }; + let configStub: sinon.SinonStub; + + beforeEach(() => { + configStub = sinon.stub(config, 'firebaseConfig'); + }); + + afterEach(() => { + configStub.restore(); + }); + + it('should accept only handler', () => { + configStub.returns({ storageBucket: 'default-bucket' }); + + const result = storage.onObjectFinalized(() => 42); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...FINALIZED_TRIGGER, + resource: 'default-bucket', + }, + }); + }); + + it('should accept bucket and handler', () => { + const result = storage.onObjectFinalized('my-bucket', () => 42); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...FINALIZED_TRIGGER, + resource: 'my-bucket', + }, + }); + }); + + it('should accept opts and handler', () => { + const result = storage.onObjectFinalized( + { bucket: 'my-bucket', region: 'us-west1' }, + () => 42 + ); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...FINALIZED_TRIGGER, + resource: 'my-bucket', + }, + regions: ['us-west1'], + }); + }); + + it('should accept opts and handler, default bucket', () => { + configStub.returns({ storageBucket: 'default-bucket' }); + + const result = storage.onObjectFinalized( + { region: 'us-west1' }, + () => 42 + ); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...FINALIZED_TRIGGER, + resource: 'default-bucket', + }, + regions: ['us-west1'], + }); + }); + }); + + describe('onObjectDeleted', () => { + const DELETED_TRIGGER = { + ...EVENT_TRIGGER, + eventType: storage.deletedEvent, + }; + let configStub: sinon.SinonStub; + + beforeEach(() => { + configStub = sinon.stub(config, 'firebaseConfig'); + }); + + afterEach(() => { + configStub.restore(); + }); + + it('should accept only handler', () => { + configStub.returns({ storageBucket: 'default-bucket' }); + + const result = storage.onObjectDeleted(() => 42); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...DELETED_TRIGGER, + resource: 'default-bucket', + }, + }); + + configStub.restore(); + }); + + it('should accept bucket and handler', () => { + const result = storage.onObjectDeleted('my-bucket', () => 42); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...DELETED_TRIGGER, + resource: 'my-bucket', + }, + }); + }); + + it('should accept opts and handler', () => { + const result = storage.onObjectDeleted( + { bucket: 'my-bucket', region: 'us-west1' }, + () => 42 + ); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...DELETED_TRIGGER, + resource: 'my-bucket', + }, + regions: ['us-west1'], + }); + }); + + it('should accept opts and handler, default bucket', () => { + configStub.returns({ storageBucket: 'default-bucket' }); + + const result = storage.onObjectDeleted({ region: 'us-west1' }, () => 42); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...DELETED_TRIGGER, + resource: 'default-bucket', + }, + regions: ['us-west1'], + }); + }); + }); + + describe('onObjectMetadataUpdated', () => { + const METADATA_TRIGGER = { + ...EVENT_TRIGGER, + eventType: storage.metadataUpdatedEvent, + }; + let configStub: sinon.SinonStub; + + beforeEach(() => { + configStub = sinon.stub(config, 'firebaseConfig'); + }); + + afterEach(() => { + configStub.restore(); + }); + + it('should accept only handler', () => { + configStub.returns({ storageBucket: 'default-bucket' }); + + const result = storage.onObjectMetadataUpdated(() => 42); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...METADATA_TRIGGER, + resource: 'default-bucket', + }, + }); + + configStub.restore(); + }); + + it('should accept bucket and handler', () => { + const result = storage.onObjectMetadataUpdated('my-bucket', () => 42); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...METADATA_TRIGGER, + resource: 'my-bucket', + }, + }); + }); + + it('should accept opts and handler', () => { + const result = storage.onObjectMetadataUpdated( + { bucket: 'my-bucket', region: 'us-west1' }, + () => 42 + ); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...METADATA_TRIGGER, + resource: 'my-bucket', + }, + regions: ['us-west1'], + }); + }); + + it('should accept opts and handler, default bucket', () => { + configStub.returns({ storageBucket: 'default-bucket' }); + + const result = storage.onObjectMetadataUpdated( + { region: 'us-west1' }, + () => 42 + ); + + expect(result.__trigger).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...METADATA_TRIGGER, + resource: 'default-bucket', + }, + regions: ['us-west1'], + }); + }); + }); +}); diff --git a/src/v2/index.ts b/src/v2/index.ts index 679df995a..5c99a4678 100644 --- a/src/v2/index.ts +++ b/src/v2/index.ts @@ -24,8 +24,9 @@ import * as logger from '../logger'; import * as params from './params'; import * as https from './providers/https'; import * as pubsub from './providers/pubsub'; +import * as storage from './providers/storage'; -export { https, pubsub, logger, params }; +export { https, pubsub, storage, logger, params }; export { setGlobalOptions, GlobalOptions } from './options'; diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts new file mode 100644 index 000000000..4240ae9a9 --- /dev/null +++ b/src/v2/providers/storage.ts @@ -0,0 +1,373 @@ +// 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 { firebaseConfig } from '../../config'; +import { CloudEvent, CloudFunction } from '../core'; +import * as options from '../options'; + +/** + * An object within Google Cloud Storage. + * Ref: https://github.com/googleapis/google-cloudevents-nodejs/blob/main/cloud/storage/v1/StorageObjectData.ts + */ +export interface StorageObjectData { + /** + * The name of the bucket containing this object. + */ + bucket?: string; + /** + * Cache-Control directive for the object data, matching + * [https://tools.ietf.org/html/rfc7234#section-5.2"][RFC 7234 §5.2]. + */ + cacheControl?: string; + /** + * Number of underlying components that make up this object. Components are + * accumulated by compose operations. + * Attempting to set this field will result in an error. + */ + componentCount?: number; + /** + * Content-Disposition of the object data, matching + * [https://tools.ietf.org/html/rfc6266][RFC 6266]. + */ + contentDisposition?: string; + /** + * Content-Encoding of the object data, matching + * [https://tools.ietf.org/html/rfc7231#section-3.1.2.2][RFC 7231 §3.1.2.2] + */ + contentEncoding?: string; + /** + * Content-Language of the object data, matching + * [https://tools.ietf.org/html/rfc7231#section-3.1.3.2][RFC 7231 §3.1.3.2]. + */ + contentLanguage?: string; + /** + * Content-Type of the object data, matching + * [https://tools.ietf.org/html/rfc7231#section-3.1.1.5][RFC 7231 §3.1.1.5]. + * If an object is stored without a Content-Type, it is served as + * `application/octet-stream`. + */ + contentType?: string; + /** + * CRC32c checksum. For more information about using the CRC32c + * checksum, see + * [https://cloud.google.com/storage/docs/hashes-etags#_JSONAPI][Hashes and + * ETags: Best Practices]. + */ + crc32c?: string; + /** + * Metadata of customer-supplied encryption key, if the object is encrypted by + * such a key. + */ + customerEncryption?: CustomerEncryption; + /** + * HTTP 1.1 Entity tag for the object. See + * [https://tools.ietf.org/html/rfc7232#section-2.3][RFC 7232 §2.3]. + */ + etag?: string; + /** + * The content generation of this object. Used for object versioning. + * Attempting to set this field will result in an error. + */ + generation?: number; + /** + * The ID of the object, including the bucket name, object name, and + * generation number. + */ + id?: string; + /** + * The kind of item this is. For objects, this is always "storage#object". + */ + kind?: string; + /** + * MD5 hash of the data; encoded using base64 as per + * [https://tools.ietf.org/html/rfc4648#section-4][RFC 4648 §4]. For more + * information about using the MD5 hash, see + * [https://cloud.google.com/storage/docs/hashes-etags#_JSONAPI][Hashes and + * ETags: Best Practices]. + */ + md5Hash?: string; + /** + * Media download link. + */ + mediaLink?: string; + /** + * User-provided metadata, in key/value pairs. + */ + metadata?: { [key: string]: string }; + /** + * The version of the metadata for this object at this generation. Used for + * preconditions and for detecting changes in metadata. A metageneration + * number is only meaningful in the context of a particular generation of a + * particular object. + */ + metageneration?: number; + /** + * The name of the object. + */ + name?: string; + /** + * The link to this object. + */ + selfLink?: string; + /** + * Content-Length of the object data in bytes, matching + * [https://tools.ietf.org/html/rfc7230#section-3.3.2][RFC 7230 §3.3.2]. + */ + size?: number; + /** + * Storage class of the object. + */ + storageClass?: string; + /** + * The creation time of the object. + * Attempting to set this field will result in an error. + */ + timeCreated?: Date | string; + /** + * The deletion time of the object. Will be returned if and only if this + * version of the object has been deleted. + */ + timeDeleted?: Date | string; + /** + * The time at which the object's storage class was last changed. + */ + timeStorageClassUpdated?: Date | string; + /** + * The modification time of the object metadata. + */ + updated?: Date | string; +} + +/** + * Metadata of customer-supplied encryption key, if the object is encrypted by + * such a key. + */ +export interface CustomerEncryption { + /** + * The encryption algorithm. + */ + encryptionAlgorithm?: string; + /** + * SHA256 hash value of the encryption key. + */ + keySha256?: string; +} + +/** @internal */ +export const archivedEvent = 'google.cloud.storage.object.v1.archived'; +/** @internal */ +export const finalizedEvent = 'google.cloud.storage.object.v1.finalized'; +/** @internal */ +export const deletedEvent = 'google.cloud.storage.object.v1.deleted'; +/** @internal */ +export const metadataUpdatedEvent = + 'google.cloud.storage.object.v1.metadataUpdated'; + +/** StorageOptions extend EventHandlerOptions with a bucket name */ +export interface StorageOptions extends options.EventHandlerOptions { + bucket?: string; +} + +/** Handle a storage object archived */ +export function onObjectArchived( + handler: (event: CloudEvent) => any | Promise +): CloudFunction; + +export function onObjectArchived( + bucket: string, + handler: (event: CloudEvent) => any | Promise +): CloudFunction; + +export function onObjectArchived( + opts: StorageOptions, + handler: (event: CloudEvent) => any | Promise +): CloudFunction; + +export function onObjectArchived( + buketOrOptsOrHandler: + | string + | StorageOptions + | ((event: CloudEvent) => any | Promise), + handler?: (event: CloudEvent) => any | Promise +): CloudFunction { + return onOperation(archivedEvent, buketOrOptsOrHandler, handler); +} + +/** Handle a storage object finalized */ +export function onObjectFinalized( + handler: (event: CloudEvent) => any | Promise +): CloudFunction; + +export function onObjectFinalized( + bucket: string, + handler: (event: CloudEvent) => any | Promise +): CloudFunction; + +export function onObjectFinalized( + opts: StorageOptions, + handler: (event: CloudEvent) => any | Promise +): CloudFunction; + +export function onObjectFinalized( + buketOrOptsOrHandler: + | string + | StorageOptions + | ((event: CloudEvent) => any | Promise), + handler?: (event: CloudEvent) => any | Promise +): CloudFunction { + return onOperation(finalizedEvent, buketOrOptsOrHandler, handler); +} + +/** Handle a storage object deleted */ +export function onObjectDeleted( + handler: (event: CloudEvent) => any | Promise +): CloudFunction; + +export function onObjectDeleted( + bucket: string, + handler: (event: CloudEvent) => any | Promise +): CloudFunction; + +export function onObjectDeleted( + opts: StorageOptions, + handler: (event: CloudEvent) => any | Promise +): CloudFunction; + +export function onObjectDeleted( + buketOrOptsOrHandler: + | string + | StorageOptions + | ((event: CloudEvent) => any | Promise), + handler?: (event: CloudEvent) => any | Promise +): CloudFunction { + return onOperation(deletedEvent, buketOrOptsOrHandler, handler); +} + +/** Handle a storage object metadata updated */ +export function onObjectMetadataUpdated( + handler: (event: CloudEvent) => any | Promise +): CloudFunction; + +export function onObjectMetadataUpdated( + bucket: string, + handler: (event: CloudEvent) => any | Promise +): CloudFunction; + +export function onObjectMetadataUpdated( + opts: StorageOptions, + handler: (event: CloudEvent) => any | Promise +): CloudFunction; + +export function onObjectMetadataUpdated( + buketOrOptsOrHandler: + | string + | StorageOptions + | ((event: CloudEvent) => any | Promise), + handler?: (event: CloudEvent) => any | Promise +): CloudFunction { + return onOperation(metadataUpdatedEvent, buketOrOptsOrHandler, handler); +} + +/** @internal */ +export function onOperation( + eventType: string, + bucketOrOptsOrHandler: + | string + | StorageOptions + | ((event: CloudEvent) => any | Promise), + handler: (event: CloudEvent) => any | Promise +): CloudFunction { + if (typeof bucketOrOptsOrHandler === 'function') { + handler = bucketOrOptsOrHandler as ( + event: CloudEvent + ) => any | Promise; + bucketOrOptsOrHandler = {}; + } + + const [opts, bucket] = getOptsAndBucket( + bucketOrOptsOrHandler as string | StorageOptions + ); + + const func = (raw: CloudEvent) => { + return handler(raw as CloudEvent); + }; + + func.run = handler; + + // TypeScript doesn't recongize defineProperty as adding a property and complains + // that __trigger doesn't exist. We can either cast to any and lose all type safety + // or we can just assign a meaningless value before calling defineProperty. + func.__trigger = 'silence the transpiler'; + + Object.defineProperty(func, '__trigger', { + get: () => { + const baseOpts = options.optionsToTriggerAnnotations( + options.getGlobalOptions() + ); + const specificOpts = options.optionsToTriggerAnnotations(opts); + + return { + platform: 'gcfv2', + ...baseOpts, + ...specificOpts, + labels: { + ...baseOpts?.labels, + ...specificOpts?.labels, + }, + eventTrigger: { + eventType: eventType, + resource: bucket, // TODO(colerogers): replace with 'bucket,' eventually + }, + }; + }, + }); + + return func; +} + +/** @internal */ +export function getOptsAndBucket( + bucketOrOpts: string | StorageOptions +): [options.EventHandlerOptions, string] { + let bucket: string; + let opts: options.EventHandlerOptions; + if (typeof bucketOrOpts === 'string') { + bucket = bucketOrOpts; + opts = {}; + } else { + bucket = bucketOrOpts.bucket || firebaseConfig().storageBucket; + opts = { ...bucketOrOpts }; + delete (opts as any).bucket; + } + + if (!bucket) { + throw new Error( + 'Missing bucket name. If you are unit testing, please provide a bucket name' + + ' by providing bucket name directly in the event handler or by setting process.env.FIREBASE_CONFIG.' + ); + } + if (!/^[a-z\d][a-z\d\\._-]{1,230}[a-z\d]$/.test(bucket)) { + throw new Error(`Invalid bucket name ${bucket}`); + } + + return [opts, bucket]; +} diff --git a/v2/storage.js b/v2/storage.js new file mode 100644 index 000000000..7d725acc3 --- /dev/null +++ b/v2/storage.js @@ -0,0 +1,26 @@ +// The MIT License (MIT) +// +// Copyright (c) 2021 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. + +// This file is not part of the firebase-functions SDK. It is used to silence the +// imports eslint plugin until it can understand import paths defined by node +// package exports. +// For more information, see github.com/import-js/eslint-plugin-import/issues/1810 From 63bd14d08c1f1f5640e2124ab3a54e93c5a6dea2 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 14 Oct 2021 15:21:55 -0700 Subject: [PATCH 011/370] Add an option to disable rejection of requests with invalid App Check token for callable functions. (#989) Since releasing App Check integration for Callable Functions, we've received several requests from our users to make it possible turn App Check enforcement off. By default, if a request includes an App Check token, callable functions will verify the token, and - if the token is invalid - reject the request. This makes it hard for developers to onboard to App Check, especially for developers that want to "soft launch" App Check integration to measure the App Check enforcement would have on its users. The change here adds a `runWith` option to allow requests with invalid App check token to continue to user code execution, e.g. ```js exports.yourCallableFunction = functions. .runWith({ allowInvalidAppCheckToken: true // Opt-out: Invalid App Check token cont. to user code. }). .https.onCall( (data, context) => { // Requests with an invalid App Check token are not rejected. // // context.app will be undefined if the request: // 1) Does not include an App Check token // 2) Includes an invalid App Check token if (context.app == undefined) { // Users can manually inspect raw request header to check whether an App Check // token was provided in the request. const rawToken = context.rawRequest.header['X-Firebase-AppCheck']; if (rawToken == undefined) { throw new functions.https.HttpsError( 'failed-precondition', 'The function must be called from an App Check verified app.' ); } else { throw new functions.https.HttpsError( 'unauthenticated', 'Provided App Check token failed to validate.' ); } }, } ); ``` --- CHANGELOG.md | 1 + spec/common/providers/https.spec.ts | 31 ++++++++++++++++++++++++++++- src/common/providers/https.ts | 18 +++++++++++++---- src/function-configuration.ts | 5 +++++ src/providers/https.ts | 8 +++++++- src/v2/providers/https.ts | 5 ++++- 6 files changed, 61 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 881256402..eae642ee0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ - GCS Enhancement +- Add option to allow callable functions to ignore invalid App Check tokens. diff --git a/spec/common/providers/https.spec.ts b/spec/common/providers/https.spec.ts index b8cc9546b..78d4c0705 100644 --- a/spec/common/providers/https.spec.ts +++ b/spec/common/providers/https.spec.ts @@ -40,6 +40,8 @@ interface CallTest { callableFunction2: (request: https.CallableRequest) => any; + callableOption?: https.CallableOptions; + // The expected shape of the http response returned to the callable SDK. expectedHttpResponse: RunHandlerResult; } @@ -104,7 +106,10 @@ function runHandler( // Runs a CallTest test. async function runTest(test: CallTest): Promise { - const opts = { origin: true, methods: 'POST' }; + const opts = { + cors: { origin: true, methods: 'POST' }, + ...test.callableOption, + }; const callableFunctionV1 = https.onCallHandler(opts, (data, context) => { expect(data).to.deep.equal(test.expectedData); return test.callableFunction(data, context); @@ -470,6 +475,30 @@ describe('onCallHandler', () => { }); }); + it('should handle bad AppCheck token with callable option', async () => { + await runTest({ + httpRequest: mockRequest(null, 'application/json', { + appCheckToken: 'FAKE', + }), + expectedData: null, + callableFunction: (data, context) => { + return; + }, + callableFunction2: (request) => { + return; + }, + callableOption: { + cors: { origin: true, methods: 'POST' }, + allowInvalidAppCheckToken: true, + }, + expectedHttpResponse: { + status: 200, + headers: expectedResponseHeaders, + body: { result: null }, + }, + }); + }); + it('should handle instance id', async () => { await runTest({ httpRequest: mockRequest(null, 'application/json', { diff --git a/src/common/providers/https.ts b/src/common/providers/https.ts index 67c63613f..9599be76c 100644 --- a/src/common/providers/https.ts +++ b/src/common/providers/https.ts @@ -592,16 +592,22 @@ async function checkTokens( type v1Handler = (data: any, context: CallableContext) => any | Promise; type v2Handler = (request: CallableRequest) => Res; +/** @hidden **/ +export interface CallableOptions { + cors: cors.CorsOptions; + allowInvalidAppCheckToken?: boolean; +} + /** @hidden */ export function onCallHandler( - options: cors.CorsOptions, + options: CallableOptions, handler: v1Handler | v2Handler ): (req: Request, res: express.Response) => Promise { - const wrapped = wrapOnCallHandler(handler); + const wrapped = wrapOnCallHandler(options, handler); return (req: Request, res: express.Response) => { return new Promise((resolve) => { res.on('finish', resolve); - cors(options)(req, res, () => { + cors(options.cors)(req, res, () => { resolve(wrapped(req, res)); }); }); @@ -610,6 +616,7 @@ export function onCallHandler( /** @internal */ function wrapOnCallHandler( + options: CallableOptions, handler: v1Handler | v2Handler ): (req: Request, res: express.Response) => Promise { return async (req: Request, res: express.Response): Promise => { @@ -621,7 +628,10 @@ function wrapOnCallHandler( const context: CallableContext = { rawRequest: req }; const tokenStatus = await checkTokens(req, context); - if (tokenStatus.app === 'INVALID' || tokenStatus.auth === 'INVALID') { + if (tokenStatus.auth === 'INVALID') { + throw new HttpsError('unauthenticated', 'Unauthenticated'); + } + if (tokenStatus.app === 'INVALID' && !options.allowInvalidAppCheckToken) { throw new HttpsError('unauthenticated', 'Unauthenticated'); } diff --git a/src/function-configuration.ts b/src/function-configuration.ts index 9ec182a60..5aa6177fd 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -160,6 +160,11 @@ export interface RuntimeOptions { * Invoker to set access control on https functions. */ invoker?: 'public' | 'private' | string | string[]; + + /* + * Allow requests with invalid App Check tokens on callable functions. + */ + allowInvalidAppCheckToken?: boolean; } export interface DeploymentOptions extends RuntimeOptions { diff --git a/src/providers/https.ts b/src/providers/https.ts index 074a7fbff..8a48f41fe 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -90,7 +90,13 @@ export function _onCallWithOptions( // in another handler to avoid accidentally triggering the v2 API const fixedLen = (data: any, context: CallableContext) => handler(data, context); - const func: any = onCallHandler({ origin: true, methods: 'POST' }, fixedLen); + const func: any = onCallHandler( + { + allowInvalidAppCheckToken: options.allowInvalidAppCheckToken, + cors: { origin: true, methods: 'POST' }, + }, + fixedLen + ); func.__trigger = { labels: {}, diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 54f1a1b36..9e4c466e4 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -157,7 +157,10 @@ export function onCall>( // onCallHandler sniffs the function length to determine which API to present. // fix the length to prevent api versions from being mismatched. const fixedLen = (req: CallableRequest) => handler(req); - const func: any = onCallHandler({ origin, methods: 'POST' }, fixedLen); + const func: any = onCallHandler( + { cors: { origin, methods: 'POST' } }, + fixedLen + ); Object.defineProperty(func, '__trigger', { get: () => { From ede96e66c56580bbca0a2efdcc8512d58712ca8f Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Mon, 18 Oct 2021 15:38:59 -0700 Subject: [PATCH 012/370] Add debug mode (#992) Debug mode is intended to be used by the Functions Emulator to enable/disable certain aspect of the Functions SDK. Debug mode allows the Functions SDK to operate in development friendly way without the need to monkey-patch the Functions SDK. For example, auth or App Check token verification can be disabled during development to make Functions SDK compatible with the Auth Emulator. This feature is intended for internal use-cases and won't expose any public interfaces. No one outside the Firebase team should use it, and if they do, we can't promise any stability or feature support. --- src/common/debug.ts | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/common/debug.ts diff --git a/src/common/debug.ts b/src/common/debug.ts new file mode 100644 index 000000000..c3cbd86e1 --- /dev/null +++ b/src/common/debug.ts @@ -0,0 +1,52 @@ +// The MIT License (MIT) +// +// Copyright (c) 2021 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. + +// Do NOT turn on a debug feature in production. +const debugMode = process.env.FIREBASE_DEBUG_MODE === 'true'; + +interface DebugFeatures { + skipCallableTokenVerification?: boolean; +} + +function loadDebugFeatures(): DebugFeatures { + if (!debugMode) return {}; + try { + const obj = JSON.parse(process.env.FIREBASE_DEBUG_FEATURES); + if (typeof obj !== 'object') { + return {}; + } + return obj as DebugFeatures; + } catch (e) { + return {}; + } +} + +/* @internal */ +export function debugFeatureValue(feat: keyof DebugFeatures): unknown { + if (!debugMode) return; + return loadDebugFeatures()[feat]; +} + +/* @internal */ +export function isDebugFeatureEnabled(feat: keyof DebugFeatures): boolean { + return debugMode && !!debugFeatureValue(feat); +} From 11e045ab698651ee295fd09e0e79fb9786308ec9 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 19 Oct 2021 08:58:41 -0700 Subject: [PATCH 013/370] Fix docgen script (#990) --- docgen/content-sources/v1/toc.yaml | 126 +++++++++++++++-------------- docgen/generate-docs.js | 7 +- docgen/theme/layouts/default.hbs | 4 +- package-lock.json | 43 +++++----- package.json | 2 +- 5 files changed, 94 insertions(+), 88 deletions(-) diff --git a/docgen/content-sources/v1/toc.yaml b/docgen/content-sources/v1/toc.yaml index ac430151b..c4091f15d 100644 --- a/docgen/content-sources/v1/toc.yaml +++ b/docgen/content-sources/v1/toc.yaml @@ -1,148 +1,154 @@ toc: - title: 'functions' - path: /docs/reference/functions/v1_cloud_functions_.html + path: /docs/reference/functions/cloud_functions.html section: - title: 'CloudFunction' - path: /docs/reference/functions/v1_cloud_functions_.html#cloudfunction + path: /docs/reference/functions/cloud_functions.html#cloudfunction - title: 'HttpsFunction' - path: /docs/reference/functions/v1_cloud_functions_.html#httpsfunction + path: /docs/reference/functions/cloud_functions.html#httpsfunction - title: 'EventContext' - path: /docs/reference/functions/v1_cloud_functions_.eventcontext.html + path: /docs/reference/functions/cloud_functions.eventcontext.html - title: 'FunctionBuilder' - path: /docs/reference/functions/v1_function_builder_.functionbuilder.html + path: /docs/reference/functions/function_builder.functionbuilder.html - title: 'Change' - path: /docs/reference/functions/v1_cloud_functions_.change.html + path: /docs/reference/functions/cloud_functions.change.html - title: 'ChangeJson' - path: /docs/reference/functions/v1_cloud_functions_.changejson.html + path: /docs/reference/functions/cloud_functions.changejson.html - title: 'functions.config' - path: /docs/reference/functions/v1_config_.html + path: /docs/reference/functions/config.html section: - title: 'Config' - path: /docs/reference/functions/v1_config_.config.html + path: /docs/reference/functions/config.config-1.html - title: 'config.Config' - path: /docs/reference/functions/v1_config_.config.config.html + path: /docs/reference/functions/config.config-1.config.html - title: 'functions.function-configuration' - path: /docs/reference/functions/v1_function_configuration_.html + path: /docs/reference/functions/function_configuration.html section: - title: 'config.DeploymentOptions' - path: /docs/reference/functions/v1_function_configuration_.deploymentoptions.html + path: /docs/reference/functions/function_configuration.deploymentoptions.html - title: 'config.FailurePolicy' - path: /docs/reference/functions/v1_function_configuration_.failurepolicy.html + path: /docs/reference/functions/function_configuration.failurepolicy.html - title: 'config.RuntimeOptions' - path: /docs/reference/functions/v1_function_configuration_.runtimeoptions.html + path: /docs/reference/functions/function_configuration.runtimeoptions.html - title: 'config.Schedule' - path: /docs/reference/functions/v1_function_configuration_.schedule.html + path: /docs/reference/functions/function_configuration.schedule.html - title: 'config.ScheduleRetryConfig' - path: /docs/reference/functions/v1_function_configuration_.scheduleretryconfig.html + path: /docs/reference/functions/function_configuration.scheduleretryconfig.html - title: 'functions.analytics' - path: /docs/reference/functions/v1_providers_analytics_.html + path: /docs/reference/functions/providers_analytics.html section: - title: 'AnalyticsEvent' - path: /docs/reference/functions/v1_providers_analytics_.analyticsevent.html + path: /docs/reference/functions/providers_analytics.analyticsevent.html - title: 'AnalyticsEventBuilder' - path: /docs/reference/functions/v1_providers_analytics_.analyticseventbuilder.html + path: /docs/reference/functions/providers_analytics.analyticseventbuilder.html - title: 'AppInfo' - path: /docs/reference/functions/v1_providers_analytics_.appinfo.html + path: /docs/reference/functions/providers_analytics.appinfo.html - title: 'DeviceInfo' - path: /docs/reference/functions/v1_providers_analytics_.deviceinfo.html + path: /docs/reference/functions/providers_analytics.deviceinfo.html - title: 'ExportBundleInfo' - path: /docs/reference/functions/v1_providers_analytics_.exportbundleinfo.html + path: /docs/reference/functions/providers_analytics.exportbundleinfo.html - title: 'GeoInfo' - path: /docs/reference/functions/v1_providers_analytics_.geoinfo.html + path: /docs/reference/functions/providers_analytics.geoinfo.html - title: 'UserDimensions' - path: /docs/reference/functions/v1_providers_analytics_.userdimensions.html + path: /docs/reference/functions/providers_analytics.userdimensions.html - title: 'UserPropertyValue' - path: /docs/reference/functions/v1_providers_analytics_.userpropertyvalue.html + path: /docs/reference/functions/providers_analytics.userpropertyvalue.html - title: 'functions.auth' - path: /docs/reference/functions/v1_providers_auth_.html + path: /docs/reference/functions/providers_auth.html section: - title: 'UserBuilder' - path: /docs/reference/functions/v1_providers_auth_.userbuilder.html + path: /docs/reference/functions/providers_auth.userbuilder.html - title: 'UserInfo' - path: /docs/reference/functions/v1_providers_auth_.html#userinfo + path: /docs/reference/functions/providers_auth.html#userinfo - title: 'UserRecordMetadata' - path: /docs/reference/functions/v1_providers_auth_.userrecordmetadata.html + path: /docs/reference/functions/providers_auth.userrecordmetadata.html - title: 'UserRecord' - path: /docs/reference/functions/v1_providers_auth_.html#userrecord + path: /docs/reference/functions/providers_auth.html#userrecord - title: 'functions.firestore' - path: /docs/reference/functions/v1_providers_firestore_.html + path: /docs/reference/functions/providers_firestore.html section: - title: 'DocumentBuilder' - path: /docs/reference/functions/v1_providers_firestore_.documentbuilder.html + path: /docs/reference/functions/providers_firestore.documentbuilder.html - title: 'DocumentSnapshot' - path: /docs/reference/functions/v1_providers_firestore_.html#documentsnapshot + path: /docs/reference/functions/providers_firestore.html#documentsnapshot - title: 'functions.database' - path: /docs/reference/functions/v1_providers_database_.html + path: /docs/reference/functions/providers_database.html section: - title: 'DataSnapshot' - path: /docs/reference/functions/v1_providers_database_.datasnapshot.html + path: /docs/reference/functions/providers_database.datasnapshot.html - title: 'RefBuilder' - path: /docs/reference/functions/v1_providers_database_.refbuilder.html + path: /docs/reference/functions/providers_database.refbuilder.html - title: 'InstanceBuilder' - path: /docs/reference/functions/v1_providers_database_.instancebuilder.html + path: /docs/reference/functions/providers_database.instancebuilder.html - title: 'functions.https' - path: /docs/reference/functions/v1_providers_https_.html + path: /docs/reference/functions/providers_https.html section: - title: 'HttpsError' - path: /docs/reference/functions/v1_providers_https_.httpserror.html + path: /docs/reference/functions/common_providers_https.httpserror.html + - title: 'CallableRequest' + path: /docs/reference/functions/common_providers_https.callablerequest.html - title: 'CallableContext' - path: /docs/reference/functions/v1_providers_https_.callablecontext.html + path: /docs/reference/functions/common_providers_https.callablecontext.html + - title: 'AuthData' + path: /docs/reference/functions/common_providers_https.authdata.html + - title: 'AppCheckData' + path: /docs/reference/functions/common_providers_https.appcheckdata.html - title: 'functions.logger' - path: /docs/reference/functions/logger_index_.html + path: /docs/reference/functions/logger.html section: - title: 'LogEntry' - path: /docs/reference/functions/logger_index_.logentry.html + path: /docs/reference/functions/logger.logentry.html - title: 'functions.pubsub' - path: /docs/reference/functions/v1_providers_pubsub_.html + path: /docs/reference/functions/providers_pubsub.html section: - title: 'Message' - path: /docs/reference/functions/v1_providers_pubsub_.message.html + path: /docs/reference/functions/providers_pubsub.message.html - title: 'TopicBuilder' - path: /docs/reference/functions/v1_providers_pubsub_.topicbuilder.html + path: /docs/reference/functions/providers_pubsub.topicbuilder.html - title: 'ScheduleBuilder' - path: /docs/reference/functions/v1_providers_pubsub_.schedulebuilder.html + path: /docs/reference/functions/providers_pubsub.schedulebuilder.html - title: 'functions.remoteconfig' - path: /docs/reference/functions/v1_providers_remoteconfig_.html + path: /docs/reference/functions/providers_remoteconfig.html section: - title: 'RemoteConfigUser' - path: /docs/reference/functions/v1_providers_remoteconfig_.remoteconfiguser.html + path: /docs/reference/functions/providers_remoteconfig.remoteconfiguser.html - title: 'TemplateVersion' - path: /docs/reference/functions/v1_providers_remoteconfig_.templateversion.html + path: /docs/reference/functions/providers_remoteconfig.templateversion.html - title: 'functions.storage' - path: /docs/reference/functions/v1_providers_storage_.html + path: /docs/reference/functions/providers_storage.html section: - title: 'BucketBuilder' - path: /docs/reference/functions/v1_providers_storage_.bucketbuilder.html + path: /docs/reference/functions/providers_storage.bucketbuilder.html - title: 'ObjectBuilder' - path: /docs/reference/functions/v1_providers_storage_.objectbuilder.html + path: /docs/reference/functions/providers_storage.objectbuilder.html - title: 'ObjectMetadata' - path: /docs/reference/functions/v1_providers_storage_.objectmetadata.html + path: /docs/reference/functions/providers_storage.objectmetadata.html - title: 'functions.testLab' - path: /docs/reference/functions/v1_providers_testlab_.html + path: /docs/reference/functions/providers_testlab.html section: - title: 'testLab.clientInfo' - path: /docs/reference/functions/v1_providers_testlab_.clientinfo.html + path: /docs/reference/functions/providers_testlab.clientinfo.html - title: 'testLab.resultStorage' - path: /docs/reference/functions/v1_providers_testlab_.resultstorage.html + path: /docs/reference/functions/providers_testlab.resultstorage.html - title: 'testLab.testMatrix' - path: /docs/reference/functions/v1_providers_testlab_.testmatrix.html + path: /docs/reference/functions/providers_testlab.testmatrix.html - title: 'testLab.testMatrixBuilder' - path: /docs/reference/functions/v1_providers_testlab_.testmatrixbuilder.html + path: /docs/reference/functions/providers_testlab.testmatrixbuilder.html - title: 'functions.handler' - path: /docs/reference/functions/v1_handler_builder_.html + path: /docs/reference/functions/handler_builder.html section: - title: 'HandlerBuilder' - path: /docs/reference/functions/v1_handler_builder_.handlerbuilder.html + path: /docs/reference/functions/handler_builder.handlerbuilder.html diff --git a/docgen/generate-docs.js b/docgen/generate-docs.js index ab7b128a7..bf07f3881 100644 --- a/docgen/generate-docs.js +++ b/docgen/generate-docs.js @@ -33,15 +33,17 @@ const { api: apiVersion } = yargs .version(false) .help().argv; -let sourceFile, devsitePath; +let sourceFile, devsitePath, exclude; switch (apiVersion) { case 'v1': - sourceFile = `${repoPath}/src/{v1,logger}`; + sourceFile = `${repoPath}/src`; devsitePath = '/docs/reference/functions/'; + exclude = ['"**/v2/**/*.ts"', '"src/index.ts"']; break; case 'v2': sourceFile = `${repoPath}/src/{v2,logger}`; devsitePath = '/docs/functions/alpha/'; + exclude = []; break; default: throw new Error( @@ -75,6 +77,7 @@ function stripPath(path) { function runTypedoc() { const command = `${repoPath}/node_modules/.bin/typedoc ${sourceFile} \ --out ${docPath} \ + ${exclude.map(ex => "--exclude " + ex).join(" ")} \ --readme ${tempHomePath} \ --options ${__dirname}/typedoc.js \ --theme ${__dirname}/theme`; diff --git a/docgen/theme/layouts/default.hbs b/docgen/theme/layouts/default.hbs index 5e749dd87..17106e8d8 100644 --- a/docgen/theme/layouts/default.hbs +++ b/docgen/theme/layouts/default.hbs @@ -5,8 +5,8 @@ - - + + {{#ifCond model.name '==' project.name}}{{project.name}}{{else}}{{model.name}} | {{project.name}}{{/ifCond}} diff --git a/package-lock.json b/package-lock.json index b46f581ef..e32576e6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -537,7 +537,6 @@ "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": "*" } @@ -645,7 +644,7 @@ "@types/range-parser": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", - "integrity": "sha1-fuMwunyq+5gJC+zoal7kQRWQTCw=" + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" }, "@types/serve-static": { "version": "1.13.5", @@ -2410,14 +2409,11 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, - "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } + "jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true }, "jsonwebtoken": { "version": "8.5.1", @@ -3726,12 +3722,12 @@ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, "shiki": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.6.tgz", - "integrity": "sha512-h2y5Uq9QEWsEmi97n+BOdPOVxkOUdVunl+jVIzU9EqJ6/QbIX+U6F7TsrWZQ2xqwPgvvQaC9r7/zeegi1b48dQ==", + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.12.tgz", + "integrity": "sha512-VXcROdldv0/Qu0w2XvzU4IrvTeBNs/Kj/FCmtcEXGz7Tic/veQzliJj6tEiAgoKianhQstpYmbPDStHU5Opqcw==", "dev": true, "requires": { - "json5": "^2.2.0", + "jsonc-parser": "^3.0.0", "onigasm": "^2.2.5", "vscode-textmate": "5.2.0" } @@ -4158,13 +4154,14 @@ } }, "typedoc": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.5.tgz", - "integrity": "sha512-uRDRmYheE5Iju9Zz0X50pTASTpBorIHFt02F5Y8Dt4eBt55h3mwk1CBSY2+EfwBxY16N4Xm7f8KXhnfFZ0AmBw==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.2.tgz", + "integrity": "sha512-SR1ByJB3USg+jxoxwzMRP07g/0f/cQUE5t7gOh1iTUyjTPyJohu9YSKRlK+MSXXqlhIq+m0jkEHEG5HoY7/Adg==", "dev": true, "requires": { "glob": "^7.1.7", "handlebars": "^4.7.7", + "lodash": "^4.17.21", "lunr": "^2.3.9", "marked": "^2.1.1", "minimatch": "^3.0.0", @@ -4174,9 +4171,9 @@ }, "dependencies": { "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -4202,9 +4199,9 @@ "dev": true }, "uglify-js": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.1.tgz", - "integrity": "sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz", + "integrity": "sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==", "dev": true, "optional": true }, diff --git a/package.json b/package.json index e1755efa1..708831ccc 100644 --- a/package.json +++ b/package.json @@ -168,7 +168,7 @@ "tslint-config-prettier": "^1.18.0", "tslint-no-unused-expression-chai": "^0.1.4", "tslint-plugin-prettier": "^2.0.1", - "typedoc": "^0.21.5", + "typedoc": "0.21.2", "typescript": "^4.3.5", "yargs": "^15.3.1" }, From 43e68afbef70adee15c1cc9d2835f6b82f2987a6 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Mon, 25 Oct 2021 16:38:26 -0700 Subject: [PATCH 014/370] Allow firebase-admin v10. Add integration tests for Node 16 (#998) * Allow firebase-admin v10. Add integration tests for Node 16 * Add changelog. Run formatter * rerun formatter --- CHANGELOG.md | 1 + integration_test/package.json.template | 2 +- integration_test/run_tests.sh | 10 +- package-lock.json | 5919 +++++++++++++++++++++++- package.json | 4 +- src/common/debug.ts | 8 +- src/v2/providers/storage.ts | 2 +- 7 files changed, 5875 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eae642ee0..8c1bb5b7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,3 @@ - GCS Enhancement - Add option to allow callable functions to ignore invalid App Check tokens. +- Add firebase-admin v10 as an allowed peer dependency diff --git a/integration_test/package.json.template b/integration_test/package.json.template index 5bcd8761b..a5259aeb6 100644 --- a/integration_test/package.json.template +++ b/integration_test/package.json.template @@ -6,7 +6,7 @@ }, "dependencies": { "@google-cloud/pubsub": "^2.10.0", - "firebase-admin": "^9.1.0", + "firebase-admin": "__FIREBASE_ADMIN__", "firebase-functions": "__SDK_TARBALL__", "lodash": "~4.17.2" }, diff --git a/integration_test/run_tests.sh b/integration_test/run_tests.sh index 100e24a32..498a9b0a0 100755 --- a/integration_test/run_tests.sh +++ b/integration_test/run_tests.sh @@ -68,6 +68,7 @@ function create_package_json { # backup file called package.json-e that we should clean up afterwards. 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 + sed -i -e "s/__FIREBASE_ADMIN__/$3/g" functions/package.json rm -f functions/package.json-e } @@ -138,8 +139,13 @@ build_sdk delete_all_functions set_region -for version in 10 12 14; do - create_package_json $TIMESTAMP $version +for version in 10 12 14 16; do + if [[ "$version" -eq 10 ]]; then + admin_sdk="^9.12.0" + else + admin_sdk="^10.0.0" + fi + create_package_json $TIMESTAMP $version $admin_sdk install_deps announce "Re-deploying the same functions to Node $version runtime ..." deploy diff --git a/package-lock.json b/package-lock.json index e32576e6b..c0b6fe545 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,8 +1,5705 @@ { "name": "firebase-functions", "version": "3.15.7", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "version": "3.15.7", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.5", + "@types/express": "4.17.3", + "cors": "^2.8.5", + "express": "^4.17.1", + "lodash": "^4.17.14" + }, + "devDependencies": { + "@types/chai": "^4.1.7", + "@types/chai-as-promised": "^7.1.0", + "@types/jsonwebtoken": "^8.3.2", + "@types/lodash": "^4.14.135", + "@types/mocha": "^5.2.7", + "@types/mock-require": "^2.0.0", + "@types/nock": "^10.0.3", + "@types/node": "^8.10.50", + "@types/sinon": "^7.0.13", + "chai": "^4.2.0", + "chai-as-promised": "^7.1.1", + "child-process-promise": "^2.2.1", + "firebase-admin": "10.0.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", + "nock": "^10.0.6", + "prettier": "^1.18.2", + "sinon": "^7.3.2", + "ts-node": "^8.3.0", + "tslint": "^5.18.0", + "tslint-config-prettier": "^1.18.0", + "tslint-no-unused-expression-chai": "^0.1.4", + "tslint-plugin-prettier": "^2.0.1", + "typedoc": "0.21.2", + "typescript": "^4.3.5", + "yargs": "^15.3.1" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + }, + "peerDependencies": { + "firebase-admin": "^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/@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, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@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 + }, + "node_modules/@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, + "dependencies": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "node_modules/@firebase/app": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.7.4.tgz", + "integrity": "sha512-XBrZb60m7N1XqmRhSJWADDD3J/0j9wM2VhxC5KUEtFA9SWfTn9Z3EWGtRGz7ahrMkgPJsmo0fXpvUh6cY8pQvQ==", + "dev": true, + "peer": true, + "dependencies": { + "@firebase/component": "0.5.7", + "@firebase/logger": "0.3.0", + "@firebase/util": "1.4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-compat": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.5.tgz", + "integrity": "sha512-GJURp5Nn8dEm72/y13Z+XMvWmMomsYViNxw6VKYqVH9f9VKnJ46Q8zYtx2ePvOuj7pAqsfwNkvAdAFYcveTe9g==", + "dev": true, + "peer": true, + "dependencies": { + "@firebase/app": "0.7.4", + "@firebase/component": "0.5.7", + "@firebase/logger": "0.3.0", + "@firebase/util": "1.4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-compat/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true, + "peer": true + }, + "node_modules/@firebase/app-types": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.7.0.tgz", + "integrity": "sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg==", + "dev": true + }, + "node_modules/@firebase/app/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true, + "peer": true + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.6.tgz", + "integrity": "sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g==", + "dev": true, + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.7.tgz", + "integrity": "sha512-CiAHUPXh2hn/lpzMShNmfAxHNQhKQwmQUJSYMPCjf2bCCt4Z2vLGpS+UWEuNFm9Zf8LNmkS+Z+U/s4Obi5carg==", + "dev": true, + "dependencies": { + "@firebase/util": "1.4.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/component/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/@firebase/database": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.12.2.tgz", + "integrity": "sha512-Y1LZR1LIQM8YKMkeUPpAq3/e53hcfcXO+JEZ6vCzBeD6xRawqmpw6B5/DzePdCNNvjcqheXzSaR7T39eRZo/wA==", + "dev": true, + "dependencies": { + "@firebase/auth-interop-types": "0.1.6", + "@firebase/component": "0.5.7", + "@firebase/logger": "0.3.0", + "@firebase/util": "1.4.0", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.1.2.tgz", + "integrity": "sha512-sV32QIRSNIBj/6OYtpmPzA/SfQz1/NBZbhxg9dIhGaSt9e5HaMxXRuz2lImudX0Sd/v8DKdExrxa++K6rKrRtA==", + "dev": true, + "dependencies": { + "@firebase/component": "0.5.7", + "@firebase/database": "0.12.2", + "@firebase/database-types": "0.9.1", + "@firebase/logger": "0.3.0", + "@firebase/util": "1.4.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/database-compat/node_modules/@firebase/database-types": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.1.tgz", + "integrity": "sha512-RUixK/YrbpxbfdE+nYP0wMcEsz1xPTnafP0q3UlSS/+fW744OITKtR1J0cMRaXbvY7EH0wUVTNVkrtgxYY8IgQ==", + "dev": true, + "dependencies": { + "@firebase/app-types": "0.7.0", + "@firebase/util": "1.4.0" + } + }, + "node_modules/@firebase/database-compat/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/@firebase/database-types": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.7.3.tgz", + "integrity": "sha512-dSOJmhKQ0nL8O4EQMRNGpSExWCXeHtH57gGg0BfNAdWcKhC8/4Y+qfKLfWXzyHvrSecpLmO0SmAi/iK2D5fp5A==", + "dev": true, + "dependencies": { + "@firebase/app-types": "0.6.3" + } + }, + "node_modules/@firebase/database-types/node_modules/@firebase/app-types": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.6.3.tgz", + "integrity": "sha512-/M13DPPati7FQHEQ9Minjk1HGLm/4K4gs9bR4rzLCWJg64yGtVC0zNg9gDpkw9yc2cvol/mNFxqTtd4geGrwdw==", + "dev": true + }, + "node_modules/@firebase/database/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/@firebase/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-7oQ+TctqekfgZImWkKuda50JZfkmAKMgh5qY4aR4pwRyqZXuJXN1H/BKkHvN1y0S4XWtF0f/wiCLKHhyi1ppPA==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/logger/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/@firebase/util": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.4.0.tgz", + "integrity": "sha512-Qn58d+DVi1nGn0bA9RV89zkz0zcbt6aUcRdyiuub/SuEvjKYstWmHcHwh1C0qmE1wPf9a3a+AuaRtduaGaRT7A==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/util/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/@google-cloud/common": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.6.0.tgz", + "integrity": "sha512-aHIFTqJZmeTNO9md8XxV+ywuvXF3xBm5WNmgWeeCK+XN5X+kGW0WEX94wGwj+/MdOnrVf4dL2RvSIt9J5yJG6Q==", + "dev": true, + "optional": true, + "dependencies": { + "@google-cloud/projectify": "^2.0.0", + "@google-cloud/promisify": "^2.0.0", + "arrify": "^2.0.1", + "duplexify": "^4.1.1", + "ent": "^2.2.0", + "extend": "^3.0.2", + "google-auth-library": "^7.0.2", + "retry-request": "^4.1.1", + "teeny-request": "^7.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@google-cloud/firestore": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.11.0.tgz", + "integrity": "sha512-Do9WJzEkFBBB+zVFvFfrrrIFEz086lrdgKQic7XsdoTgtYtq0yMu2u3kGLyxMbdasl2c2yf49FE4YvO3AYjQMQ==", + "dev": true, + "optional": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^2.9.2", + "protobufjs": "^6.8.6" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@google-cloud/paginator": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.5.tgz", + "integrity": "sha512-N4Uk4BT1YuskfRhKXBs0n9Lg2YTROZc6IMpkO/8DIHODtm5s3xY8K5vVBo23v/2XulY3azwITQlYWgT4GdLsUw==", + "dev": true, + "optional": true, + "dependencies": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@google-cloud/projectify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz", + "integrity": "sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@google-cloud/promisify": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz", + "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==", + "dev": true, + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@google-cloud/storage": { + "version": "5.8.5", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.8.5.tgz", + "integrity": "sha512-i0gB9CRwQeOBYP7xuvn14M40LhHCwMjceBjxE4CTvsqL519sVY5yVKxLiAedHWGwUZHJNRa7Q2CmNfkdRwVNPg==", + "dev": true, + "optional": true, + "dependencies": { + "@google-cloud/common": "^3.6.0", + "@google-cloud/paginator": "^3.0.0", + "@google-cloud/promisify": "^2.0.0", + "arrify": "^2.0.0", + "async-retry": "^1.3.1", + "compressible": "^2.0.12", + "date-and-time": "^1.0.0", + "duplexify": "^4.0.0", + "extend": "^3.0.2", + "gaxios": "^4.0.0", + "gcs-resumable-upload": "^3.1.4", + "get-stream": "^6.0.0", + "hash-stream-validation": "^0.2.2", + "mime": "^2.2.0", + "mime-types": "^2.0.8", + "onetime": "^5.1.0", + "p-limit": "^3.0.1", + "pumpify": "^2.0.0", + "snakeize": "^0.1.0", + "stream-events": "^1.0.1", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@google-cloud/storage/node_modules/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, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@google-cloud/storage/node_modules/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, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.3.0.tgz", + "integrity": "sha512-fiL7ZaGg2HBiFtmv6m34d5jEgEtNXfctjzB3f7b3iuT7olBX4mHLMOqOBmGTTSOTfNRQJH5+vsyk6mEz3I0Q7Q==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/grpc-js/node_modules/@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 + }, + "node_modules/@grpc/proto-loader": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.1.tgz", + "integrity": "sha512-4DIvEOZhw5nGj3RQngIoiMXRsre3InEH136krZTcirs/G2em3WMXdtx4Lqlnb4E2ertbWGs5gPeVDKU5BHffXw==", + "dev": true, + "optional": true, + "dependencies": { + "@types/long": "^4.0.1", + "lodash.camelcase": "^4.3.0", + "long": "^4.0.0", + "protobufjs": "^6.10.0", + "yargs": "^16.1.1" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@grpc/proto-loader/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@grpc/proto-loader/node_modules/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, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/@grpc/proto-loader/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@grpc/proto-loader/node_modules/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 + }, + "node_modules/@grpc/proto-loader/node_modules/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 + }, + "node_modules/@grpc/proto-loader/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@grpc/proto-loader/node_modules/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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@grpc/proto-loader/node_modules/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, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@grpc/proto-loader/node_modules/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, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@grpc/proto-loader/node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@grpc/proto-loader/node_modules/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, + "engines": { + "node": ">=10" + } + }, + "node_modules/@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, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@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 + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true, + "optional": true + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "dev": true, + "optional": true + }, + "node_modules/@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 + }, + "node_modules/@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, + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@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 + }, + "node_modules/@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 + }, + "node_modules/@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 + }, + "node_modules/@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 + }, + "node_modules/@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 + }, + "node_modules/@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, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@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, + "dependencies": { + "@sinonjs/commons": "^1", + "@sinonjs/samsam": "^3.1.0" + } + }, + "node_modules/@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, + "dependencies": { + "@sinonjs/commons": "^1.3.0", + "array-from": "^2.1.1", + "lodash": "^4.17.15" + } + }, + "node_modules/@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 + }, + "node_modules/@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, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@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==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@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 + }, + "node_modules/@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, + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@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 + }, + "node_modules/@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==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@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==", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@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==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@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, + "dependencies": { + "@types/express": "*", + "@types/express-unless": "*" + } + }, + "node_modules/@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==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@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, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@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, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@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 + }, + "node_modules/@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 + }, + "node_modules/@types/mime": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", + "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==" + }, + "node_modules/@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 + }, + "node_modules/@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, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@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, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@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==" + }, + "node_modules/@types/qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ==" + }, + "node_modules/@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==" + }, + "node_modules/@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==", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/mime": "*" + } + }, + "node_modules/@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 + }, + "node_modules/abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/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, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "optional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/agent-base/node_modules/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 + }, + "node_modules/ajv": { + "version": "6.12.5", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", + "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/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 + }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true, + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=", + "dev": true, + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/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, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/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, + "engines": { + "node": "*" + } + }, + "node_modules/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, + "dependencies": { + "retry": "0.12.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "node_modules/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, + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", + "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "optional": true + }, + "node_modules/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, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bignumber.js": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", + "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==", + "dev": true, + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/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 + }, + "node_modules/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dependencies": { + "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" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha1-uqVZ7hTO1zRSIputcyZGfGH6vWA=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "node_modules/chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "check-error": "^1.0.2" + } + }, + "node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/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, + "dependencies": { + "cross-spawn": "^4.0.2", + "node-version": "^1.0.0", + "promise-polyfill": "^6.0.1" + } + }, + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/cliui/node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/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, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/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, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/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, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha1-vXerfebelCBc6sxy8XFtKfIKd78=", + "dev": true + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "optional": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compressible/node_modules/mime-db": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", + "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==", + "dev": true, + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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 + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "optional": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/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 + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha1-6sEdpRWS3Ya58G9uesKTs9+HXSk=", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "dev": true, + "dependencies": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/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, + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/date-and-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-1.0.0.tgz", + "integrity": "sha512-477D7ypIiqlXBkxhU7YtG9wWZJEQ+RUpujt2quTfgf4+E8g5fNUkB0QIL0bVyP5/TKBg8y55Hfa1R/c4bt3dEw==", + "dev": true, + "optional": true + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/dicer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", + "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", + "dev": true, + "dependencies": { + "streamsearch": "0.1.2" + }, + "engines": { + "node": ">=4.5.0" + } + }, + "node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexify": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", + "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", + "dev": true, + "optional": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/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, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/elliptic/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true, + "optional": true + }, + "node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/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, + "dependencies": { + "fast-diff": "^1.1.1", + "jest-docblock": "^21.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dependencies": { + "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" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=", + "dev": true + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dependencies": { + "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" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/firebase-admin": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-10.0.0.tgz", + "integrity": "sha512-EOAk5ZaqXhBBvx9ZyXd28kw8glMTt3xl0g3BepGRCy0RSSUPGOzfAqjGhc65guSKgFOpT5mAUycYcJbqullKUQ==", + "dev": true, + "dependencies": { + "@firebase/database-compat": "^0.1.1", + "@firebase/database-types": "^0.7.2", + "@types/node": ">=12.12.47", + "dicer": "^0.3.0", + "jsonwebtoken": "^8.5.1", + "jwks-rsa": "^2.0.2", + "node-forge": "^0.10.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "optionalDependencies": { + "@google-cloud/firestore": "^4.5.0", + "@google-cloud/storage": "^5.3.0" + } + }, + "node_modules/firebase-admin/node_modules/@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 + }, + "node_modules/flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "dev": true, + "dependencies": { + "is-buffer": "~2.0.3" + }, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/gaxios": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.2.1.tgz", + "integrity": "sha512-s+rTywpw6CmfB8r9TXYkpix7YFeuRjnR/AqhaJrQqsNhsAqej+IAiCc3hadzQH3gHyWth30tvYjxH8EVjQt/8Q==", + "dev": true, + "optional": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gcp-metadata": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz", + "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==", + "dev": true, + "optional": true, + "dependencies": { + "gaxios": "^4.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gcs-resumable-upload": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-3.1.4.tgz", + "integrity": "sha512-5dyDfHrrVcIskiw/cPssVD4HRiwoHjhk1Nd6h5W3pQ/qffDvhfy4oNCr1f3ZXFPwTnxkCbibsB+73oOM+NvmJQ==", + "dev": true, + "optional": true, + "dependencies": { + "abort-controller": "^3.0.0", + "configstore": "^5.0.0", + "extend": "^3.0.2", + "gaxios": "^4.0.0", + "google-auth-library": "^7.0.0", + "pumpify": "^2.0.0", + "stream-events": "^1.0.4" + }, + "bin": { + "gcs-upload": "build/src/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/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, + "engines": { + "node": "*" + } + }, + "node_modules/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, + "engines": { + "node": ">=10" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha1-OWCDLT8VdBCDQtr9OmezMsCWnfE=", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": "*" + } + }, + "node_modules/google-auth-library": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.0.4.tgz", + "integrity": "sha512-o8irYyeijEiecTXeoEe8UKNEzV1X+uhR4b2oNdapDMZixypp0J+eHimGOyx5Joa3UAeokGngdtDLXtq9vDqG2Q==", + "dev": true, + "optional": true, + "dependencies": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^4.0.0", + "gcp-metadata": "^4.2.0", + "gtoken": "^5.0.4", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/google-auth-library/node_modules/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, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/google-auth-library/node_modules/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, + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/google-auth-library/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/google-auth-library/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "optional": true + }, + "node_modules/google-gax": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.12.0.tgz", + "integrity": "sha512-UDx4ZZx85vXBe6GZ0sdRSzuegLrRQdRjCxlauX+U7i5YwOoSgcSaIM71BhcdHwGPhEkvO/SSHrEfc1wpL/J6JA==", + "dev": true, + "optional": true, + "dependencies": { + "@grpc/grpc-js": "~1.3.0", + "@grpc/proto-loader": "^0.6.1", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "fast-text-encoding": "^1.0.3", + "google-auth-library": "^7.0.2", + "is-stream-ended": "^0.1.4", + "node-fetch": "^2.6.1", + "object-hash": "^2.1.1", + "protobufjs": "^6.10.2", + "retry-request": "^4.0.0" + }, + "bin": { + "compileProtos": "build/tools/compileProtos.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/google-p12-pem": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz", + "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==", + "dev": true, + "optional": true, + "dependencies": { + "node-forge": "^0.10.0" + }, + "bin": { + "gp12-pem": "build/src/bin/gp12-pem.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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, + "optional": true + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha1-8nNdwig2dPpnR4sQGBBZNVw2nl4=", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/gtoken": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.2.1.tgz", + "integrity": "sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw==", + "dev": true, + "optional": true, + "dependencies": { + "gaxios": "^4.0.0", + "google-p12-pem": "^3.0.3", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gtoken/node_modules/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, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/gtoken/node_modules/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, + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/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, + "bin": { + "he": "bin/he" + } + }, + "node_modules/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, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/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, + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", + "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "optional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/http-proxy-agent/node_modules/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 + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/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, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "optional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/https-proxy-agent/node_modules/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 + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/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==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream-ended": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", + "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==", + "dev": true, + "optional": true + }, + "node_modules/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, + "dependencies": { + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/jose": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.5.tgz", + "integrity": "sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA==", + "dev": true, + "dependencies": { + "@panva/asn1.js": "^1.0.0" + }, + "engines": { + "node": ">=10.13.0 < 13 || >=13.7.0" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/js-yaml/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "node_modules/jsdom": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", + "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsdom/node_modules/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, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/jsdom/node_modules/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, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jsdom/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dev": true, + "optional": true, + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jsonwebtoken/node_modules/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_modules/jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/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, + "dependencies": { + "asn1.js": "^5.3.0", + "elliptic": "^6.5.4", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/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, + "dependencies": { + "@types/express-jwt": "0.0.42", + "debug": "^4.1.0", + "jose": "^2.0.5", + "limiter": "^1.1.5", + "lru-memoizer": "^2.1.2" + }, + "engines": { + "node": ">=10 < 13 || >=14" + } + }, + "node_modules/jwks-rsa/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/jwks-rsa/node_modules/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_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, + "node_modules/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, + "dependencies": { + "chalk": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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 + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "dev": true, + "optional": true + }, + "node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha1-i75Q6oW+1ZvJ4z3KuCNe6bz0Q80=", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/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, + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "~4.0.0" + } + }, + "node_modules/lru-memoizer/node_modules/lru-cache": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", + "integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.1", + "yallist": "^2.0.0" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/make-dir/node_modules/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, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/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 + }, + "node_modules/marked": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz", + "integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==", + "dev": true, + "bin": { + "marked": "bin/marked" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "dependencies": { + "mime-db": "1.40.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp/node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "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" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/mocha/node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/mocha/node_modules/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, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/mocha/node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/mocha/node_modules/mkdirp": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", + "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mocha/node_modules/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 + }, + "node_modules/mocha/node_modules/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, + "dependencies": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mocha/node_modules/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, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/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, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/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 + }, + "node_modules/mocha/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "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" + } + }, + "node_modules/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, + "dependencies": { + "get-caller-file": "^1.0.2", + "normalize-path": "^2.1.1" + }, + "engines": { + "node": ">=4.3.0" + } + }, + "node_modules/mock-require/node_modules/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 + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "@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" + } + }, + "node_modules/nise/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "node_modules/nise/node_modules/lolex": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", + "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/nise/node_modules/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, + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/nock": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/nock/-/nock-10.0.6.tgz", + "integrity": "sha512-b47OWj1qf/LqSQYnmokNWM8D88KvUl2y7jT0567NB3ZBAZFz2bWp2PC81Xn7u8F2/vJxzkzNZybnemeFa7AZ2w==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 6.0" + } + }, + "node_modules/nock/node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/nock/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/nock/node_modules/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_modules/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, + "dependencies": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + } + }, + "node_modules/node-environment-flags/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/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, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/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, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">= 6" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign/node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/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, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/onigasm": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", + "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", + "dev": true, + "dependencies": { + "lru-cache": "^5.1.1" + } + }, + "node_modules/onigasm/node_modules/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, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/onigasm/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/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 + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + }, + "node_modules/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=" + }, + "node_modules/pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/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 + }, + "node_modules/propagate": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-1.0.0.tgz", + "integrity": "sha1-AMLa7t2iDofjeCs0Stuhzd1q1wk=", + "dev": true, + "engines": [ + "node >= 0.8.1" + ] + }, + "node_modules/protobufjs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", + "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", + "dev": true, + "optional": true, + "dependencies": { + "@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" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, + "node_modules/protobufjs/node_modules/@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 + }, + "node_modules/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==", + "dependencies": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", + "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", + "dev": true, + "optional": true, + "dependencies": { + "duplexify": "^4.1.1", + "inherits": "^2.0.3", + "pump": "^3.0.0" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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==", + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/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, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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 + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/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, + "dependencies": { + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/request-promise-native/node_modules/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, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/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, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/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, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true, + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/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, + "dependencies": { + "debug": "^4.1.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/retry-request/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "optional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/retry-request/node_modules/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 + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha1-fnQlb7qknHWqfHogXMInmcrIAAQ=", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dependencies": { + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/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==" + }, + "node_modules/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==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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 + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "node_modules/shiki": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.12.tgz", + "integrity": "sha512-VXcROdldv0/Qu0w2XvzU4IrvTeBNs/Kj/FCmtcEXGz7Tic/veQzliJj6tEiAgoKianhQstpYmbPDStHU5Opqcw==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.0.0", + "onigasm": "^2.2.5", + "vscode-textmate": "5.2.0" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "@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" + } + }, + "node_modules/sinon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/sinon/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/snakeize": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/snakeize/-/snakeize-0.1.0.tgz", + "integrity": "sha1-EMCI2LWOsHazIpu1oE4jLOEmQi0=", + "dev": true, + "optional": true + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/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 + }, + "node_modules/streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "optional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/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 + }, + "node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/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, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=", + "dev": true, + "optional": true + }, + "node_modules/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 + }, + "node_modules/teeny-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz", + "integrity": "sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==", + "dev": true, + "optional": true, + "dependencies": { + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "stream-events": "^1.0.5", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/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, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/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, + "dependencies": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/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 + }, + "node_modules/tslint": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", + "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", + "dev": true, + "dependencies": { + "@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" + }, + "bin": { + "tslint": "bin/tslint" + }, + "engines": { + "node": ">=4.8.0" + } + }, + "node_modules/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, + "bin": { + "tslint-config-prettier-check": "bin/check.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/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, + "dependencies": { + "tsutils": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslint-no-unused-expression-chai/node_modules/tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/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, + "dependencies": { + "eslint-plugin-prettier": "^2.2.0", + "lines-and-columns": "^1.1.6", + "tslib": "^1.7.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/tslint/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslint/node_modules/resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "dependencies": { + "path-parse": "^1.0.6" + } + }, + "node_modules/tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typedoc": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.2.tgz", + "integrity": "sha512-SR1ByJB3USg+jxoxwzMRP07g/0f/cQUE5t7gOh1iTUyjTPyJohu9YSKRlK+MSXXqlhIq+m0jkEHEG5HoY7/Adg==", + "dev": true, + "dependencies": { + "glob": "^7.1.7", + "handlebars": "^4.7.7", + "lodash": "^4.17.21", + "lunr": "^2.3.9", + "marked": "^2.1.1", + "minimatch": "^3.0.0", + "progress": "^2.0.3", + "shiki": "^0.9.3", + "typedoc-default-themes": "^0.12.10" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 12.20.0" + } + }, + "node_modules/typedoc-default-themes": { + "version": "0.12.10", + "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", + "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/typedoc/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": "*" + } + }, + "node_modules/typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uglify-js": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz", + "integrity": "sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/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, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/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 + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/vscode-textmate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", + "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/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, + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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, + "engines": { + "node": ">=10.4" + } + }, + "node_modules/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, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/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, + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^2.0.2", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/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, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/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, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/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, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "dev": true, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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 + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/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, + "dependencies": { + "flat": "^4.1.0", + "lodash": "^4.17.15", + "yargs": "^13.3.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs-unparser/node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs-unparser/node_modules/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, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs-unparser/node_modules/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, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs-unparser/node_modules/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 + }, + "node_modules/yargs-unparser/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "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" + } + }, + "node_modules/yargs/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/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, + "dependencies": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/yargs/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/yargs/node_modules/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 + }, + "node_modules/yargs/node_modules/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 + }, + "node_modules/yargs/node_modules/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, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/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, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/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, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/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, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/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, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/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 + }, + "node_modules/yargs/node_modules/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, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "engines": { + "node": ">=10" + } + } + }, "dependencies": { "@babel/code-frame": { "version": "7.10.4", @@ -30,87 +5727,183 @@ "js-tokens": "^4.0.0" } }, + "@firebase/app": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.7.4.tgz", + "integrity": "sha512-XBrZb60m7N1XqmRhSJWADDD3J/0j9wM2VhxC5KUEtFA9SWfTn9Z3EWGtRGz7ahrMkgPJsmo0fXpvUh6cY8pQvQ==", + "dev": true, + "peer": true, + "requires": { + "@firebase/component": "0.5.7", + "@firebase/logger": "0.3.0", + "@firebase/util": "1.4.0", + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true, + "peer": true + } + } + }, + "@firebase/app-compat": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.5.tgz", + "integrity": "sha512-GJURp5Nn8dEm72/y13Z+XMvWmMomsYViNxw6VKYqVH9f9VKnJ46Q8zYtx2ePvOuj7pAqsfwNkvAdAFYcveTe9g==", + "dev": true, + "peer": true, + "requires": { + "@firebase/app": "0.7.4", + "@firebase/component": "0.5.7", + "@firebase/logger": "0.3.0", + "@firebase/util": "1.4.0", + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true, + "peer": true + } + } + }, "@firebase/app-types": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.6.2.tgz", - "integrity": "sha512-2VXvq/K+n8XMdM4L2xy5bYp2ZXMawJXluUIDzUBvMthVR+lhxK4pfFiqr1mmDbv9ydXvEAuFsD+6DpcZuJcSSw==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.7.0.tgz", + "integrity": "sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg==", "dev": true }, "@firebase/auth-interop-types": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.6.tgz", "integrity": "sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g==", - "dev": true + "dev": true, + "requires": {} }, "@firebase/component": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.0.tgz", - "integrity": "sha512-v18csWtXb0ri+3m7wuGLY/UDgcb89vuMlZGQ//+7jEPLIQeLbylvZhol1uzW9WzoOpxMxOS2W5qyVGX36wZvEA==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.7.tgz", + "integrity": "sha512-CiAHUPXh2hn/lpzMShNmfAxHNQhKQwmQUJSYMPCjf2bCCt4Z2vLGpS+UWEuNFm9Zf8LNmkS+Z+U/s4Obi5carg==", "dev": true, "requires": { - "@firebase/util": "1.1.0", + "@firebase/util": "1.4.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==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "dev": true } } }, "@firebase/database": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.10.0.tgz", - "integrity": "sha512-GsHvuES83Edtboij2h3txKg+yV/TD4b5Owc01SgXEQtvj1lulkHt4Ufmd9OZz1WreWQJMIqKpbVowIDHjlkZJQ==", + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.12.2.tgz", + "integrity": "sha512-Y1LZR1LIQM8YKMkeUPpAq3/e53hcfcXO+JEZ6vCzBeD6xRawqmpw6B5/DzePdCNNvjcqheXzSaR7T39eRZo/wA==", "dev": true, "requires": { "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.0", - "@firebase/database-types": "0.7.2", - "@firebase/logger": "0.2.6", - "@firebase/util": "1.1.0", - "faye-websocket": "0.11.3", + "@firebase/component": "0.5.7", + "@firebase/logger": "0.3.0", + "@firebase/util": "1.4.0", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + } + } + }, + "@firebase/database-compat": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.1.2.tgz", + "integrity": "sha512-sV32QIRSNIBj/6OYtpmPzA/SfQz1/NBZbhxg9dIhGaSt9e5HaMxXRuz2lImudX0Sd/v8DKdExrxa++K6rKrRtA==", + "dev": true, + "requires": { + "@firebase/component": "0.5.7", + "@firebase/database": "0.12.2", + "@firebase/database-types": "0.9.1", + "@firebase/logger": "0.3.0", + "@firebase/util": "1.4.0", "tslib": "^2.1.0" }, "dependencies": { + "@firebase/database-types": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.1.tgz", + "integrity": "sha512-RUixK/YrbpxbfdE+nYP0wMcEsz1xPTnafP0q3UlSS/+fW744OITKtR1J0cMRaXbvY7EH0wUVTNVkrtgxYY8IgQ==", + "dev": true, + "requires": { + "@firebase/app-types": "0.7.0", + "@firebase/util": "1.4.0" + } + }, "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "dev": true } } }, "@firebase/database-types": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.7.2.tgz", - "integrity": "sha512-cdAd/dgwvC0r3oLEDUR+ULs1vBsEvy0b27nlzKhU6LQgm9fCDzgaH9nFGv8x+S9dly4B0egAXkONkVoWcOAisg==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.7.3.tgz", + "integrity": "sha512-dSOJmhKQ0nL8O4EQMRNGpSExWCXeHtH57gGg0BfNAdWcKhC8/4Y+qfKLfWXzyHvrSecpLmO0SmAi/iK2D5fp5A==", "dev": true, "requires": { - "@firebase/app-types": "0.6.2" + "@firebase/app-types": "0.6.3" + }, + "dependencies": { + "@firebase/app-types": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.6.3.tgz", + "integrity": "sha512-/M13DPPati7FQHEQ9Minjk1HGLm/4K4gs9bR4rzLCWJg64yGtVC0zNg9gDpkw9yc2cvol/mNFxqTtd4geGrwdw==", + "dev": true + } } }, "@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 + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-7oQ+TctqekfgZImWkKuda50JZfkmAKMgh5qY4aR4pwRyqZXuJXN1H/BKkHvN1y0S4XWtF0f/wiCLKHhyi1ppPA==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + } + } }, "@firebase/util": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.1.0.tgz", - "integrity": "sha512-lfuSASuPKNdfebuFR8rjFamMQUPH9iiZHcKS755Rkm/5gRT0qC7BMhCh3ZkHf7NVbplzIc/GhmX2jM+igDRCag==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.4.0.tgz", + "integrity": "sha512-Qn58d+DVi1nGn0bA9RV89zkz0zcbt6aUcRdyiuub/SuEvjKYstWmHcHwh1C0qmE1wPf9a3a+AuaRtduaGaRT7A==", "dev": true, "requires": { "tslib": "^2.1.0" }, "dependencies": { "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "dev": true } } @@ -1620,9 +7413,9 @@ "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==", + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", "dev": true, "requires": { "websocket-driver": ">=0.5.1" @@ -1652,12 +7445,12 @@ } }, "firebase-admin": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-9.8.0.tgz", - "integrity": "sha512-v8B1qU8McZZT2hlLZ018TKz2FoKlfFkZq9mOIyzN7wJUOlAywqQX0JyqNpVGyPeU+B+77ojlvmkGTNXt2OFkgw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-10.0.0.tgz", + "integrity": "sha512-EOAk5ZaqXhBBvx9ZyXd28kw8glMTt3xl0g3BepGRCy0RSSUPGOzfAqjGhc65guSKgFOpT5mAUycYcJbqullKUQ==", "dev": true, "requires": { - "@firebase/database": "^0.10.0", + "@firebase/database-compat": "^0.1.1", "@firebase/database-types": "^0.7.2", "@google-cloud/firestore": "^4.5.0", "@google-cloud/storage": "^5.3.0", @@ -3859,6 +9652,25 @@ "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=", "dev": true }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "optional": true + } + } + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -3889,25 +9701,6 @@ "es-abstract": "^1.17.5" } }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "optional": true - } - } - }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", diff --git a/package.json b/package.json index 708831ccc..fd9747143 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,7 @@ "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "child-process-promise": "^2.2.1", - "firebase-admin": "^9.8.0", + "firebase-admin": "10.0.0", "js-yaml": "^3.13.1", "jsdom": "^16.2.1", "jsonwebtoken": "^8.5.1", @@ -173,7 +173,7 @@ "yargs": "^15.3.1" }, "peerDependencies": { - "firebase-admin": "^8.0.0 || ^9.0.0" + "firebase-admin": "^8.0.0 || ^9.0.0 || ^10.0.0" }, "engines": { "node": "^8.13.0 || >=10.10.0" diff --git a/src/common/debug.ts b/src/common/debug.ts index c3cbd86e1..33ce8e990 100644 --- a/src/common/debug.ts +++ b/src/common/debug.ts @@ -28,7 +28,9 @@ interface DebugFeatures { } function loadDebugFeatures(): DebugFeatures { - if (!debugMode) return {}; + if (!debugMode) { + return {}; + } try { const obj = JSON.parse(process.env.FIREBASE_DEBUG_FEATURES); if (typeof obj !== 'object') { @@ -42,7 +44,9 @@ function loadDebugFeatures(): DebugFeatures { /* @internal */ export function debugFeatureValue(feat: keyof DebugFeatures): unknown { - if (!debugMode) return; + if (!debugMode) { + return; + } return loadDebugFeatures()[feat]; } diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts index 4240ae9a9..e58405820 100644 --- a/src/v2/providers/storage.ts +++ b/src/v2/providers/storage.ts @@ -334,7 +334,7 @@ export function onOperation( ...specificOpts?.labels, }, eventTrigger: { - eventType: eventType, + eventType, resource: bucket, // TODO(colerogers): replace with 'bucket,' eventually }, }; From 87472f3142033cd564d95ade22dda4d93a7506d0 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 27 Oct 2021 23:10:39 -0700 Subject: [PATCH 015/370] Fix bug where onCall handler failed to encode returned value that contained a function (#1000) In https://github.com/firebase/firebase-functions/pull/915, we replaced use of `_.isObject(data)` with `typeof data === 'object'`. Turns out that `_.isObject` returns `true` for functions ([code](https://github.com/lodash/lodash/blob/2da024c3b4f9947a48517639de7560457cd4ec6c/isObject.js#L26)). We update the condition to match the previous behavior. Fixes https://github.com/firebase/firebase-functions/issues/964 --- CHANGELOG.md | 3 ++- spec/common/providers/https.spec.ts | 4 ++++ src/common/providers/https.ts | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c1bb5b7f..eafb8cd39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ - GCS Enhancement -- Add option to allow callable functions to ignore invalid App Check tokens. +- Add option to allow callable functions to ignore invalid App Check tokens - Add firebase-admin v10 as an allowed peer dependency +- Fix bug where onCall handler failed to encode returned value with functions diff --git a/spec/common/providers/https.spec.ts b/spec/common/providers/https.spec.ts index 78d4c0705..18f3fea50 100644 --- a/spec/common/providers/https.spec.ts +++ b/spec/common/providers/https.spec.ts @@ -665,4 +665,8 @@ describe('encoding/decoding', () => { baz: [1, 2, 1099511627776], }); }); + + it('encodes function as an empty object', () => { + expect(https.encode(() => 'foo')).to.deep.equal({}); + }); }); diff --git a/src/common/providers/https.ts b/src/common/providers/https.ts index 9599be76c..f35646a60 100644 --- a/src/common/providers/https.ts +++ b/src/common/providers/https.ts @@ -426,7 +426,7 @@ export function encode(data: any): any { if (Array.isArray(data)) { return data.map(encode); } - if (typeof data === 'object') { + if (typeof data === 'object' || typeof data === 'function') { // Sadly we don't have Object.fromEntries in Node 10, so we can't use a single // list comprehension const obj: Record = {}; From 9c2142b3faf1f198eb8cb23ebbea0b6f8a335236 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 27 Oct 2021 23:29:52 -0700 Subject: [PATCH 016/370] Allow callable functions to skip token verification in debug mode (#983) To replace monkey-patching of the Firebase Functions SDK in the Functions Emulator ([code](https://github.com/firebase/firebase-tools/blob/c2feb0836f6f64236e117f2906ef6083840e212b/src/emulator/functionsEmulatorRuntime.ts#L401-L496)), we provide native support for bypassing token verification for `onCall` handlers. Using the new debug mode introduced in https://github.com/firebase/firebase-functions/pull/992, Auth/App Check token included in the request will be decoded but no verified. --- spec/common/providers/https.spec.ts | 212 +++++++++++++++++++++++----- spec/fixtures/mockrequest.ts | 31 ++++ src/common/debug.ts | 2 +- src/common/providers/https.ts | 192 ++++++++++++++++--------- 4 files changed, 332 insertions(+), 105 deletions(-) diff --git a/spec/common/providers/https.spec.ts b/spec/common/providers/https.spec.ts index 18f3fea50..0e957a255 100644 --- a/spec/common/providers/https.spec.ts +++ b/spec/common/providers/https.spec.ts @@ -1,18 +1,28 @@ import { expect } from 'chai'; import * as express from 'express'; import * as firebase from 'firebase-admin'; +import * as sinon from 'sinon'; import { apps as appsNamespace } from '../../../src/apps'; import * as https from '../../../src/common/providers/https'; +import * as debug from '../../../src/common/debug'; import * as mocks from '../../fixtures/credential/key.json'; import { expectedResponseHeaders, generateAppCheckToken, generateIdToken, + generateUnsignedAppCheckToken, + generateUnsignedIdToken, mockFetchAppCheckPublicJwks, mockFetchPublicKeys, mockRequest, } from '../../fixtures/mockrequest'; +import { + CallableContext, + CallableRequest, + unsafeDecodeAppCheckToken, + unsafeDecodeIdToken, +} from '../../../src/common/providers/https'; /** * RunHandlerResult contains the data from an express.Response. @@ -133,6 +143,64 @@ async function runTest(test: CallTest): Promise { expect(responseV2.status).to.equal(test.expectedHttpResponse.status); } +function checkAuthContext( + context: CallableContext, + projectId: string, + userId: string +) { + expect(context.auth).to.not.be.undefined; + expect(context.auth).to.not.be.null; + expect(context.auth.uid).to.equal(userId); + expect(context.auth.token.uid).to.equal(userId); + expect(context.auth.token.sub).to.equal(userId); + expect(context.auth.token.aud).to.equal(projectId); + expect(context.instanceIdToken).to.be.undefined; +} + +function checkAppCheckContext( + context: CallableContext, + projectId: string, + appId: string +) { + 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; +} + +function checkAuthRequest( + request: CallableRequest, + projectId: string, + userId: string +) { + expect(request.auth).to.not.be.undefined; + expect(request.auth).to.not.be.null; + expect(request.auth.uid).to.equal(userId); + expect(request.auth.token.uid).to.equal(userId); + expect(request.auth.token.sub).to.equal(userId); + expect(request.auth.token.aud).to.equal(projectId); + expect(request.instanceIdToken).to.be.undefined; +} + +function checkAppCheckRequest( + request: CallableRequest, + projectId: string, + appId: string +) { + expect(request.app).to.not.be.undefined; + expect(request.app).to.not.be.null; + expect(request.app.appId).to.equal(appId); + expect(request.app.token.app_id).to.be.equal(appId); + expect(request.app.token.sub).to.be.equal(appId); + expect(request.app.token.aud).to.be.deep.equal([`projects/${projectId}`]); + expect(request.auth).to.be.undefined; + expect(request.instanceIdToken).to.be.undefined; +} + describe('onCallHandler', () => { let app: firebase.app.App; @@ -354,23 +422,11 @@ describe('onCallHandler', () => { }), expectedData: null, callableFunction: (data, context) => { - expect(context.auth).to.not.be.undefined; - expect(context.auth).to.not.be.null; - expect(context.auth.uid).to.equal(mocks.user_id); - expect(context.auth.token.uid).to.equal(mocks.user_id); - expect(context.auth.token.sub).to.equal(mocks.user_id); - expect(context.auth.token.aud).to.equal(projectId); - expect(context.instanceIdToken).to.be.undefined; + checkAuthContext(context, projectId, mocks.user_id); return null; }, callableFunction2: (request) => { - expect(request.auth).to.not.be.undefined; - expect(request.auth).to.not.be.null; - expect(request.auth.uid).to.equal(mocks.user_id); - expect(request.auth.token.uid).to.equal(mocks.user_id); - expect(request.auth.token.sub).to.equal(mocks.user_id); - expect(request.auth.token.aud).to.equal(projectId); - expect(request.instanceIdToken).to.be.undefined; + checkAuthRequest(request, projectId, mocks.user_id); return null; }, expectedHttpResponse: { @@ -383,9 +439,11 @@ describe('onCallHandler', () => { }); it('should reject bad auth', async () => { + const projectId = appsNamespace().admin.options.projectId; + const idToken = generateUnsignedIdToken(projectId); await runTest({ httpRequest: mockRequest(null, 'application/json', { - authorization: 'Bearer FAKE', + authorization: 'Bearer ' + idToken, }), expectedData: null, callableFunction: (data, context) => { @@ -410,35 +468,17 @@ describe('onCallHandler', () => { it('should handle AppCheck token', async () => { const mock = mockFetchAppCheckPublicJwks(); const projectId = appsNamespace().admin.options.projectId; - const appId = '1:65211879909:web:3ae38ef1cdcb2e01fe5f0c'; + const appId = '123:web:abc'; 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; + checkAppCheckContext(context, projectId, appId); return null; }, callableFunction2: (request) => { - expect(request.app).to.not.be.undefined; - expect(request.app).to.not.be.null; - expect(request.app.appId).to.equal(appId); - expect(request.app.token.app_id).to.be.equal(appId); - expect(request.app.token.sub).to.be.equal(appId); - expect(request.app.token.aud).to.be.deep.equal([ - `projects/${projectId}`, - ]); - expect(request.auth).to.be.undefined; - expect(request.instanceIdToken).to.be.undefined; + checkAppCheckRequest(request, projectId, appId); return null; }, expectedHttpResponse: { @@ -451,10 +491,11 @@ describe('onCallHandler', () => { }); it('should reject bad AppCheck token', async () => { + const projectId = appsNamespace().admin.options.projectId; + const appId = '123:web:abc'; + const appCheckToken = generateUnsignedAppCheckToken(projectId, appId); await runTest({ - httpRequest: mockRequest(null, 'application/json', { - appCheckToken: 'FAKE', - }), + httpRequest: mockRequest(null, 'application/json', { appCheckToken }), expectedData: null, callableFunction: (data, context) => { return; @@ -545,6 +586,66 @@ describe('onCallHandler', () => { }, }); }); + + describe('skip token verification debug mode support', () => { + before(() => { + sinon + .stub(debug, 'isDebugFeatureEnabled') + .withArgs('skipTokenVerification') + .returns(true); + }); + + after(() => { + sinon.verifyAndRestore(); + }); + + it('should skip auth token verification', async () => { + const projectId = appsNamespace().admin.options.projectId; + const idToken = generateUnsignedIdToken(projectId); + await runTest({ + httpRequest: mockRequest(null, 'application/json', { + authorization: 'Bearer ' + idToken, + }), + expectedData: null, + callableFunction: (data, context) => { + checkAuthContext(context, projectId, mocks.user_id); + return null; + }, + callableFunction2: (request) => { + checkAuthRequest(request, projectId, mocks.user_id); + return null; + }, + expectedHttpResponse: { + status: 200, + headers: expectedResponseHeaders, + body: { result: null }, + }, + }); + }); + + it('should skip app check token verification', async () => { + const projectId = appsNamespace().admin.options.projectId; + const appId = '123:web:abc'; + const appCheckToken = generateUnsignedAppCheckToken(projectId, appId); + await runTest({ + httpRequest: mockRequest(null, 'application/json', { appCheckToken }), + expectedData: null, + callableFunction: (data, context) => { + checkAppCheckContext(context, projectId, appId); + return null; + }, + callableFunction2: (request) => { + checkAppCheckRequest(request, projectId, appId); + return null; + }, + expectedHttpResponse: { + status: 200, + headers: expectedResponseHeaders, + body: { result: null }, + }, + }); + }); + }); }); describe('encoding/decoding', () => { @@ -670,3 +771,36 @@ describe('encoding/decoding', () => { expect(https.encode(() => 'foo')).to.deep.equal({}); }); }); + +describe('decode tokens', () => { + const projectId = 'myproject'; + const appId = '123:web:abc'; + + it('decodes valid Auth ID Token', () => { + const idToken = unsafeDecodeIdToken(generateIdToken(projectId)); + expect(idToken.uid).to.equal(mocks.user_id); + expect(idToken.sub).to.equal(mocks.user_id); + }); + + it('decodes invalid Auth ID Token', () => { + const idToken = unsafeDecodeIdToken(generateUnsignedIdToken(projectId)); + expect(idToken.uid).to.equal(mocks.user_id); + expect(idToken.sub).to.equal(mocks.user_id); + }); + + it('decodes valid App Check Token', () => { + const idToken = unsafeDecodeAppCheckToken( + generateAppCheckToken(projectId, appId) + ); + expect(idToken.app_id).to.equal(appId); + expect(idToken.sub).to.equal(appId); + }); + + it('decodes invalid App Check Token', () => { + const idToken = unsafeDecodeAppCheckToken( + generateUnsignedAppCheckToken(projectId, appId) + ); + expect(idToken.app_id).to.equal(appId); + expect(idToken.sub).to.equal(appId); + }); +}); diff --git a/spec/fixtures/mockrequest.ts b/spec/fixtures/mockrequest.ts index 68827610b..4a4a6df70 100644 --- a/spec/fixtures/mockrequest.ts +++ b/spec/fixtures/mockrequest.ts @@ -85,6 +85,20 @@ export function generateIdToken(projectId: string): string { return jwt.sign(claims, mockKey.private_key, options); } +/** + * Generates a mocked, unsigned Firebase ID token. + */ +export function generateUnsignedIdToken(projectId: string): string { + return [ + { alg: 'RS256', typ: 'JWT' }, + { aud: projectId, sub: mockKey.user_id }, + 'Invalid signature', + ] + .map((str) => JSON.stringify(str)) + .map((str) => Buffer.from(str).toString('base64')) + .join('.'); +} + /** * Mocks out the http request used by the firebase-admin SDK to get the jwks for * verifying an AppCheck token. @@ -121,3 +135,20 @@ export function generateAppCheckToken( }; return jwt.sign(claims, jwkToPem(mockJWK, { private: true }), options); } + +/** + * Generates a mocked, unsigned AppCheck token. + */ +export function generateUnsignedAppCheckToken( + projectId: string, + appId: string +): string { + return [ + { alg: 'RS256', typ: 'JWT' }, + { aud: [`projects/${projectId}`], sub: appId }, + 'Invalid signature', + ] + .map((component) => JSON.stringify(component)) + .map((str) => Buffer.from(str).toString('base64')) + .join('.'); +} diff --git a/src/common/debug.ts b/src/common/debug.ts index 33ce8e990..4e2fd2a8c 100644 --- a/src/common/debug.ts +++ b/src/common/debug.ts @@ -24,7 +24,7 @@ const debugMode = process.env.FIREBASE_DEBUG_MODE === 'true'; interface DebugFeatures { - skipCallableTokenVerification?: boolean; + skipTokenVerification?: boolean; } function loadDebugFeatures(): DebugFeatures { diff --git a/src/common/providers/https.ts b/src/common/providers/https.ts index f35646a60..fa64116cd 100644 --- a/src/common/providers/https.ts +++ b/src/common/providers/https.ts @@ -29,71 +29,75 @@ import * as logger from '../../logger'; // TODO(inlined): Decide whether we want to un-version apps or whether we want a // different strategy import { apps } from '../../apps'; +import { isDebugFeatureEnabled } from '../../common/debug'; + +const JWT_REGEX = /^[a-zA-Z0-9\-_=]+?\.[a-zA-Z0-9\-_=]+?\.([a-zA-Z0-9\-_=]+)?$/; /** @hidden */ export interface Request extends express.Request { rawBody: Buffer; } +// 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 +interface DecodedAppCheckToken { + /** + * The issuer identifier for the issuer of the response. + * + * This value is a URL with the format + * `https://firebaseappcheck.googleapis.com/`, where `` is the + * same project number specified in the [`aud`](#aud) property. + */ + iss: string; + + /** + * The Firebase App ID corresponding to the app the token belonged to. + * + * As a convenience, this value is copied over to the [`app_id`](#app_id) property. + */ + sub: string; + + /** + * The audience for which this token is intended. + * + * This value is a JSON array of two strings, the first is the project number of your + * Firebase project, and the second is the project ID of the same project. + */ + aud: string[]; + + /** + * The App Check token's expiration time, in seconds since the Unix epoch. That is, the + * time at which this App Check token expires and should no longer be considered valid. + */ + exp: number; + + /** + * The App Check token's issued-at time, in seconds since the Unix epoch. That is, the + * time at which this App Check token was issued and should start to be considered + * valid.; + */ + iat: number; + + /** + * The App ID corresponding to the App the App Check token belonged to. + * + * This value is not actually one of the JWT token claims. It is added as a + * convenience, and is set as the value of the [`sub`](#sub) property. + */ + app_id: string; + [key: string]: any; +} + /** * The interface for AppCheck tokens verified in Callable functions */ export interface AppCheckData { appId: string; - - // This is actually a firebase.appCheck.DecodedAppCheckToken, but - // that type may not be available in some supported SDK versions. - // Declare as an inline type, which DecodedAppCheckToken will be - // able to merge with. - // TODO: Replace with the real type once we bump the min-version of - // the admin SDK - token: { - /** - * The issuer identifier for the issuer of the response. - * - * This value is a URL with the format - * `https://firebaseappcheck.googleapis.com/`, where `` is the - * same project number specified in the [`aud`](#aud) property. - */ - iss: string; - - /** - * The Firebase App ID corresponding to the app the token belonged to. - * - * As a convenience, this value is copied over to the [`app_id`](#app_id) property. - */ - sub: string; - - /** - * The audience for which this token is intended. - * - * This value is a JSON array of two strings, the first is the project number of your - * Firebase project, and the second is the project ID of the same project. - */ - aud: string[]; - - /** - * The App Check token's expiration time, in seconds since the Unix epoch. That is, the - * time at which this App Check token expires and should no longer be considered valid. - */ - exp: number; - - /** - * The App Check token's issued-at time, in seconds since the Unix epoch. That is, the - * time at which this App Check token was issued and should start to be considered - * valid. - */ - iat: number; - - /** - * The App ID corresponding to the App the App Check token belonged to. - * - * This value is not actually one of the JWT token claims. It is added as a - * convenience, and is set as the value of the [`sub`](#sub) property. - */ - app_id: string; - [key: string]: any; - }; + token: DecodedAppCheckToken; } /** @@ -500,6 +504,55 @@ interface CallableTokenStatus { auth: TokenStatus; } +function unsafeDecodeToken(token: string): unknown { + if (!JWT_REGEX.test(token)) { + return {}; + } + const components = token + .split('.') + .map((s) => Buffer.from(s, 'base64').toString()); + let payload = components[1]; + if (typeof payload === 'string') { + try { + const obj = JSON.parse(payload); + if (typeof obj === 'object') { + payload = obj; + } + } catch (e) {} + } + return payload; +} + +/** + * Decode, but not verify, a Auth ID token. + * + * Do not use in production. Token should always be verified using the Admin SDK. + * + * This is exposed only for testing. + */ +/** @internal */ +export function unsafeDecodeIdToken( + token: string +): firebase.auth.DecodedIdToken { + const decoded = unsafeDecodeToken(token) as firebase.auth.DecodedIdToken; + decoded.uid = decoded.sub; + return decoded; +} + +/** + * Decode, but not verify, an App Check token. + * + * Do not use in production. Token should always be verified using the Admin SDK. + * + * This is exposed only for testing. + */ +/** @internal */ +export function unsafeDecodeAppCheckToken(token: string): DecodedAppCheckToken { + const decoded = unsafeDecodeToken(token) as DecodedAppCheckToken; + decoded.app_id = decoded.sub; + return decoded; +} + /** * Check and verify tokens included in the requests. Once verified, tokens * are injected into the callable context. @@ -518,6 +571,8 @@ async function checkTokens( auth: 'MISSING', }; + const skipTokenVerify = isDebugFeatureEnabled('skipTokenVerification'); + const appCheck = req.header('X-Firebase-AppCheck'); if (appCheck) { verifications.app = 'INVALID'; @@ -527,13 +582,16 @@ async function checkTokens( 'Cannot validate AppCheck token. Please update Firebase Admin SDK to >= v9.8.0' ); } - const appCheckToken = await apps() - .admin.appCheck() - .verifyToken(appCheck); - ctx.app = { - appId: appCheckToken.appId, - token: appCheckToken.token, - }; + let appCheckData; + if (skipTokenVerify) { + const decodedToken = unsafeDecodeAppCheckToken(appCheck); + appCheckData = { appId: decodedToken.app_id, token: decodedToken }; + } else { + appCheckData = await apps() + .admin.appCheck() + .verifyToken(appCheck); + } + ctx.app = appCheckData; verifications.app = 'VALID'; } catch (err) { logger.warn('Failed to validate AppCheck token.', err); @@ -547,10 +605,14 @@ async function checkTokens( if (match) { const idToken = match[1]; try { - const authToken = await apps() - .admin.auth() - .verifyIdToken(idToken); - + let authToken: firebase.auth.DecodedIdToken; + if (skipTokenVerify) { + authToken = unsafeDecodeIdToken(idToken); + } else { + authToken = await apps() + .admin.auth() + .verifyIdToken(idToken); + } verifications.auth = 'VALID'; ctx.auth = { uid: authToken.uid, From 24e4c20e0b9f321b5cec0403e86c79ab7d111a0b Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 28 Oct 2021 20:27:48 +0000 Subject: [PATCH 017/370] 3.16.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c0b6fe545..45768bc71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { "name": "firebase-functions", - "version": "3.15.7", + "version": "3.16.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "3.15.7", + "version": "3.16.0", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index fd9747143..50637bb74 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.15.7", + "version": "3.16.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From a3f5351bd1aa710cea24a8759c3cb3c1ef2f1480 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 28 Oct 2021 20:27:50 +0000 Subject: [PATCH 018/370] [firebase-release] Removed change log and reset repo after 3.16.0 release --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eafb8cd39..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +0,0 @@ -- GCS Enhancement -- Add option to allow callable functions to ignore invalid App Check tokens -- Add firebase-admin v10 as an allowed peer dependency -- Fix bug where onCall handler failed to encode returned value with functions From 38f1946df4804002be9c585f386c5ae61fd45b10 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Thu, 11 Nov 2021 09:20:37 -0800 Subject: [PATCH 019/370] Add TQ support (#1002) * Add TQ support * Adjust visibility labels & add doc * PR feedback --- CHANGELOG.md | 1 + spec/common/providers/https.spec.ts | 326 ++++++++++++++++++++++------ spec/v1/providers/https.spec.ts | 107 +++++++++ spec/v2/providers/https.spec.ts | 136 ++++++++++++ src/common/encoding.ts | 6 + src/common/providers/https.ts | 245 +++++++++++++++------ src/function-builder.ts | 9 + src/handler-builder.ts | 16 ++ src/providers/https.ts | 94 +++++++- src/v2/providers/https.ts | 104 ++++++++- 10 files changed, 916 insertions(+), 128 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..f41e7f57d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Parallelizes network calls that occur when validating authorization for onCall handlers. diff --git a/spec/common/providers/https.spec.ts b/spec/common/providers/https.spec.ts index 0e957a255..c0a1c7767 100644 --- a/spec/common/providers/https.spec.ts +++ b/spec/common/providers/https.spec.ts @@ -4,8 +4,16 @@ import * as firebase from 'firebase-admin'; import * as sinon from 'sinon'; import { apps as appsNamespace } from '../../../src/apps'; -import * as https from '../../../src/common/providers/https'; import * as debug from '../../../src/common/debug'; +import * as https from '../../../src/common/providers/https'; +import { + CallableContext, + CallableRequest, + TaskContext, + TaskRequest, + unsafeDecodeAppCheckToken, + unsafeDecodeIdToken, +} from '../../../src/common/providers/https'; import * as mocks from '../../fixtures/credential/key.json'; import { expectedResponseHeaders, @@ -17,12 +25,6 @@ import { mockFetchPublicKeys, mockRequest, } from '../../fixtures/mockrequest'; -import { - CallableContext, - CallableRequest, - unsafeDecodeAppCheckToken, - unsafeDecodeIdToken, -} from '../../../src/common/providers/https'; /** * RunHandlerResult contains the data from an express.Response. @@ -115,7 +117,7 @@ function runHandler( } // Runs a CallTest test. -async function runTest(test: CallTest): Promise { +async function runCallableTest(test: CallTest): Promise { const opts = { cors: { origin: true, methods: 'POST' }, ...test.callableOption, @@ -143,8 +145,50 @@ async function runTest(test: CallTest): Promise { expect(responseV2.status).to.equal(test.expectedHttpResponse.status); } +/** Represents a test case for a Task Queue Function */ +interface TaskTest { + // An http request, mocking a subset of https.Request. + httpRequest: any; + + // The expected format of the request passed to the handler. + expectedData: any; + + taskFunction?: ( + data: any, + context: https.TaskContext + ) => void | Promise; + + taskFunction2?: (request: https.TaskRequest) => void | Promise; + + // The expected shape of the http response returned to the callable SDK. + expectedStatus: number; +} + +// Runs a TaskTest test. +async function runTaskTest(test: TaskTest): Promise { + const taskQueueFunctionV1 = https.onEnqueueHandler((data, context) => { + expect(data).to.deep.equal(test.expectedData); + if (test.taskFunction) { + test.taskFunction(data, context); + } + }); + + const responseV1 = await runHandler(taskQueueFunctionV1, test.httpRequest); + expect(responseV1.status).to.equal(test.expectedStatus); + + const taskQueueFunctionV2 = https.onEnqueueHandler((request) => { + expect(request.data).to.deep.equal(test.expectedData); + if (test.taskFunction2) { + test.taskFunction2(request); + } + }); + + const responseV2 = await runHandler(taskQueueFunctionV2, test.httpRequest); + expect(responseV2.status).to.equal(test.expectedStatus); +} + function checkAuthContext( - context: CallableContext, + context: CallableContext | CallableRequest | TaskContext | TaskRequest, projectId: string, userId: string ) { @@ -154,11 +198,15 @@ function checkAuthContext( expect(context.auth.token.uid).to.equal(userId); expect(context.auth.token.sub).to.equal(userId); expect(context.auth.token.aud).to.equal(projectId); - expect(context.instanceIdToken).to.be.undefined; + + // TaskContext & TaskRequest don't have instanceIdToken + if ({}.hasOwnProperty.call(context, 'instanceIdToken')) { + expect((context as CallableContext).instanceIdToken).to.be.undefined; + } } function checkAppCheckContext( - context: CallableContext, + context: CallableContext | CallableRequest, projectId: string, appId: string ) { @@ -172,35 +220,6 @@ function checkAppCheckContext( expect(context.instanceIdToken).to.be.undefined; } -function checkAuthRequest( - request: CallableRequest, - projectId: string, - userId: string -) { - expect(request.auth).to.not.be.undefined; - expect(request.auth).to.not.be.null; - expect(request.auth.uid).to.equal(userId); - expect(request.auth.token.uid).to.equal(userId); - expect(request.auth.token.sub).to.equal(userId); - expect(request.auth.token.aud).to.equal(projectId); - expect(request.instanceIdToken).to.be.undefined; -} - -function checkAppCheckRequest( - request: CallableRequest, - projectId: string, - appId: string -) { - expect(request.app).to.not.be.undefined; - expect(request.app).to.not.be.null; - expect(request.app.appId).to.equal(appId); - expect(request.app.token.app_id).to.be.equal(appId); - expect(request.app.token.sub).to.be.equal(appId); - expect(request.app.token.aud).to.be.deep.equal([`projects/${projectId}`]); - expect(request.auth).to.be.undefined; - expect(request.instanceIdToken).to.be.undefined; -} - describe('onCallHandler', () => { let app: firebase.app.App; @@ -231,7 +250,7 @@ describe('onCallHandler', () => { }); it('should handle success', () => { - return runTest({ + return runCallableTest({ httpRequest: mockRequest({ foo: 'bar' }), expectedData: { foo: 'bar' }, callableFunction: (data, context) => ({ baz: 'qux' }), @@ -245,7 +264,7 @@ describe('onCallHandler', () => { }); it('should handle null data and return', () => { - return runTest({ + return runCallableTest({ httpRequest: mockRequest(null), expectedData: null, callableFunction: (data, context) => null, @@ -259,7 +278,7 @@ describe('onCallHandler', () => { }); it('should handle void return', () => { - return runTest({ + return runCallableTest({ httpRequest: mockRequest(null), expectedData: null, callableFunction: (data, context) => { @@ -279,7 +298,7 @@ describe('onCallHandler', () => { it('should reject bad method', () => { const req = mockRequest(null); req.method = 'GET'; - return runTest({ + return runCallableTest({ httpRequest: req, expectedData: null, callableFunction: (data, context) => { @@ -299,7 +318,7 @@ describe('onCallHandler', () => { }); it('should ignore charset', () => { - return runTest({ + return runCallableTest({ httpRequest: mockRequest(null, 'application/json; charset=utf-8'), expectedData: null, callableFunction: (data, context) => { @@ -317,7 +336,7 @@ describe('onCallHandler', () => { }); it('should reject bad content type', () => { - return runTest({ + return runCallableTest({ httpRequest: mockRequest(null, 'text/plain'), expectedData: null, callableFunction: (data, context) => { @@ -339,7 +358,7 @@ describe('onCallHandler', () => { it('should reject extra body fields', () => { const req = mockRequest(null); req.body.extra = 'bad'; - return runTest({ + return runCallableTest({ httpRequest: req, expectedData: null, callableFunction: (data, context) => { @@ -359,7 +378,7 @@ describe('onCallHandler', () => { }); it('should handle unhandled error', () => { - return runTest({ + return runCallableTest({ httpRequest: mockRequest(null), expectedData: null, callableFunction: (data, context) => { @@ -377,7 +396,7 @@ describe('onCallHandler', () => { }); it('should handle unknown error status', () => { - return runTest({ + return runCallableTest({ httpRequest: mockRequest(null), expectedData: null, callableFunction: (data, context) => { @@ -395,7 +414,7 @@ describe('onCallHandler', () => { }); it('should handle well-formed error', () => { - return runTest({ + return runCallableTest({ httpRequest: mockRequest(null), expectedData: null, callableFunction: (data, context) => { @@ -416,7 +435,7 @@ describe('onCallHandler', () => { const mock = mockFetchPublicKeys(); const projectId = appsNamespace().admin.options.projectId; const idToken = generateIdToken(projectId); - await runTest({ + await runCallableTest({ httpRequest: mockRequest(null, 'application/json', { authorization: 'Bearer ' + idToken, }), @@ -426,7 +445,7 @@ describe('onCallHandler', () => { return null; }, callableFunction2: (request) => { - checkAuthRequest(request, projectId, mocks.user_id); + checkAuthContext(request, projectId, mocks.user_id); return null; }, expectedHttpResponse: { @@ -441,7 +460,7 @@ describe('onCallHandler', () => { it('should reject bad auth', async () => { const projectId = appsNamespace().admin.options.projectId; const idToken = generateUnsignedIdToken(projectId); - await runTest({ + await runCallableTest({ httpRequest: mockRequest(null, 'application/json', { authorization: 'Bearer ' + idToken, }), @@ -470,7 +489,7 @@ describe('onCallHandler', () => { const projectId = appsNamespace().admin.options.projectId; const appId = '123:web:abc'; const appCheckToken = generateAppCheckToken(projectId, appId); - await runTest({ + await runCallableTest({ httpRequest: mockRequest(null, 'application/json', { appCheckToken }), expectedData: null, callableFunction: (data, context) => { @@ -478,7 +497,7 @@ describe('onCallHandler', () => { return null; }, callableFunction2: (request) => { - checkAppCheckRequest(request, projectId, appId); + checkAppCheckContext(request, projectId, appId); return null; }, expectedHttpResponse: { @@ -494,7 +513,7 @@ describe('onCallHandler', () => { const projectId = appsNamespace().admin.options.projectId; const appId = '123:web:abc'; const appCheckToken = generateUnsignedAppCheckToken(projectId, appId); - await runTest({ + await runCallableTest({ httpRequest: mockRequest(null, 'application/json', { appCheckToken }), expectedData: null, callableFunction: (data, context) => { @@ -517,7 +536,7 @@ describe('onCallHandler', () => { }); it('should handle bad AppCheck token with callable option', async () => { - await runTest({ + await runCallableTest({ httpRequest: mockRequest(null, 'application/json', { appCheckToken: 'FAKE', }), @@ -541,7 +560,7 @@ describe('onCallHandler', () => { }); it('should handle instance id', async () => { - await runTest({ + await runCallableTest({ httpRequest: mockRequest(null, 'application/json', { instanceIdToken: 'iid-token', }), @@ -566,7 +585,7 @@ describe('onCallHandler', () => { it('should expose raw request', async () => { const mockReq = mockRequest(null, 'application/json', {}); - await runTest({ + await runCallableTest({ httpRequest: mockReq, expectedData: null, callableFunction: (data, context) => { @@ -602,7 +621,7 @@ describe('onCallHandler', () => { it('should skip auth token verification', async () => { const projectId = appsNamespace().admin.options.projectId; const idToken = generateUnsignedIdToken(projectId); - await runTest({ + await runCallableTest({ httpRequest: mockRequest(null, 'application/json', { authorization: 'Bearer ' + idToken, }), @@ -612,7 +631,7 @@ describe('onCallHandler', () => { return null; }, callableFunction2: (request) => { - checkAuthRequest(request, projectId, mocks.user_id); + checkAuthContext(request, projectId, mocks.user_id); return null; }, expectedHttpResponse: { @@ -627,7 +646,7 @@ describe('onCallHandler', () => { const projectId = appsNamespace().admin.options.projectId; const appId = '123:web:abc'; const appCheckToken = generateUnsignedAppCheckToken(projectId, appId); - await runTest({ + await runCallableTest({ httpRequest: mockRequest(null, 'application/json', { appCheckToken }), expectedData: null, callableFunction: (data, context) => { @@ -635,7 +654,7 @@ describe('onCallHandler', () => { return null; }, callableFunction2: (request) => { - checkAppCheckRequest(request, projectId, appId); + checkAppCheckContext(request, projectId, appId); return null; }, expectedHttpResponse: { @@ -648,6 +667,187 @@ describe('onCallHandler', () => { }); }); +describe('onEnqueueHandler', () => { + let app: firebase.app.App; + + before(() => { + const credential = { + getAccessToken: () => { + return Promise.resolve({ + expires_in: 1000, + access_token: 'fake', + }); + }, + getCertificate: () => { + return { + projectId: 'aProjectId', + }; + }, + }; + app = firebase.initializeApp({ + projectId: 'aProjectId', + credential, + }); + Object.defineProperty(appsNamespace(), 'admin', { get: () => app }); + }); + + after(() => { + app.delete(); + delete appsNamespace.singleton; + }); + + it('should handle success', () => { + return runTaskTest({ + httpRequest: mockRequest({ foo: 'bar' }), + expectedData: { foo: 'bar' }, + expectedStatus: 204, + }); + }); + + it('should reject bad method', () => { + const req = mockRequest(null); + req.method = 'GET'; + return runTaskTest({ + httpRequest: req, + expectedData: null, + expectedStatus: 400, + }); + }); + + it('should ignore charset', () => { + return runTaskTest({ + httpRequest: mockRequest(null, 'application/json; charset=utf-8'), + expectedData: null, + expectedStatus: 204, + }); + }); + + it('should reject bad content type', () => { + return runTaskTest({ + httpRequest: mockRequest(null, 'text/plain'), + expectedData: null, + expectedStatus: 400, + }); + }); + + it('should reject extra body fields', () => { + const req = mockRequest(null); + req.body.extra = 'bad'; + return runTaskTest({ + httpRequest: req, + expectedData: null, + expectedStatus: 400, + }); + }); + + it('should handle unhandled error', () => { + return runTaskTest({ + httpRequest: mockRequest(null), + expectedData: null, + taskFunction: (data, context) => { + throw new Error(`ceci n'est pas une error`); + }, + taskFunction2: (request) => { + throw new Error(`cece n'est pas une error`); + }, + expectedStatus: 500, + }); + }); + + it('should handle unknown error status', () => { + return runTaskTest({ + httpRequest: mockRequest(null), + expectedData: null, + taskFunction: (data, context) => { + throw new https.HttpsError('THIS_IS_NOT_VALID' as any, 'nope'); + }, + taskFunction2: (request) => { + throw new https.HttpsError('THIS_IS_NOT_VALID' as any, 'nope'); + }, + expectedStatus: 500, + }); + }); + + it('should handle well-formed error', () => { + return runTaskTest({ + httpRequest: mockRequest(null), + expectedData: null, + taskFunction: (data, context) => { + throw new https.HttpsError('not-found', 'i am error'); + }, + taskFunction2: (request) => { + throw new https.HttpsError('not-found', 'i am error'); + }, + expectedStatus: 404, + }); + }); + + it('should handle auth', async () => { + const mock = mockFetchPublicKeys(); + const projectId = appsNamespace().admin.options.projectId; + const idToken = generateIdToken(projectId); + await runTaskTest({ + httpRequest: mockRequest(null, 'application/json', { + authorization: 'Bearer ' + idToken, + }), + expectedData: null, + taskFunction: (data, context) => { + checkAuthContext(context, projectId, mocks.user_id); + return null; + }, + taskFunction2: (request) => { + checkAuthContext(request, projectId, mocks.user_id); + return null; + }, + expectedStatus: 204, + }); + mock.done(); + }); + + it('should reject bad auth', async () => { + const projectId = appsNamespace().admin.options.projectId; + const idToken = generateUnsignedIdToken(projectId); + await runTaskTest({ + httpRequest: mockRequest(null, 'application/json', { + authorization: 'Bearer ' + idToken, + }), + expectedData: null, + expectedStatus: 401, + }); + }); + + describe('skip token verification debug mode support', () => { + before(() => { + sinon + .stub(debug, 'isDebugFeatureEnabled') + .withArgs('skipTokenVerification') + .returns(true); + }); + + after(() => { + sinon.verifyAndRestore(); + }); + + it('should skip auth token verification', async () => { + const projectId = appsNamespace().admin.options.projectId; + const idToken = generateUnsignedIdToken(projectId); + await runTaskTest({ + httpRequest: mockRequest(null, 'application/json', { + authorization: 'Bearer ' + idToken, + }), + expectedData: null, + taskFunction: (data, context) => { + checkAuthContext(context, projectId, mocks.user_id); + }, + taskFunction2: (request) => { + checkAuthContext(request, projectId, mocks.user_id); + }, + expectedStatus: 204, + }); + }); + }); +}); + describe('encoding/decoding', () => { it('encodes null', () => { expect(https.encode(null)).to.be.null; diff --git a/spec/v1/providers/https.spec.ts b/spec/v1/providers/https.spec.ts index 025c99a7a..e8c4785c6 100644 --- a/spec/v1/providers/https.spec.ts +++ b/spec/v1/providers/https.spec.ts @@ -135,6 +135,13 @@ describe('handler namespace', () => { expect(result.__trigger).to.deep.equal({}); }); }); + + describe('#onEnqueue', () => { + it('should return an empty trigger', () => { + const result = functions.handler.https.taskQueue.onEnqueue(() => null); + expect(result.__trigger).to.deep.equal({}); + }); + }); }); describe('#onCall', () => { @@ -201,6 +208,106 @@ describe('#onCall', () => { }); }); +describe('#onEnqueue', () => { + it('should return a Trigger with appropriate values', () => { + const result = https + .taskQueue({ + rateLimits: { + maxBurstSize: 20, + maxConcurrentDispatches: 30, + maxDispatchesPerSecond: 40, + }, + retryConfig: { + maxAttempts: 5, + maxBackoffSeconds: 20, + maxDoublings: 3, + minBackoffSeconds: 5, + }, + invoker: 'private', + }) + .onEnqueue(() => {}); + expect(result.__trigger).to.deep.equal({ + taskQueueTrigger: { + rateLimits: { + maxBurstSize: 20, + maxConcurrentDispatches: 30, + maxDispatchesPerSecond: 40, + }, + retryConfig: { + maxAttempts: 5, + maxBackoffSeconds: 20, + maxDoublings: 3, + minBackoffSeconds: 5, + }, + invoker: ['private'], + }, + }); + }); + + it('should allow both region and runtime options to be set', () => { + const fn = functions + .region('us-east1') + .runWith({ + timeoutSeconds: 90, + memory: '256MB', + }) + .https.taskQueue({ retryConfig: { maxAttempts: 5 } }) + .onEnqueue(() => null); + + expect(fn.__trigger).to.deep.equal({ + regions: ['us-east1'], + availableMemoryMb: 256, + timeout: '90s', + taskQueueTrigger: { + retryConfig: { + maxAttempts: 5, + }, + }, + }); + }); + + it('has a .run method', async () => { + const data = 'data'; + const context = { + auth: { + uid: 'abc', + token: 'token' as any, + }, + }; + let done = false; + const cf = https.taskQueue().onEnqueue((d, c) => { + expect(d).to.equal(data); + expect(c).to.deep.equal(context); + done = true; + }); + + await cf.run(data, context); + expect(done).to.be.true; + }); + + // Regression test for firebase-functions#947 + it('should lock to the v1 API even with function.length == 1', async () => { + let gotData: Record; + const func = https.taskQueue().onEnqueue((data) => { + gotData = data; + }); + + const req = new MockRequest( + { + data: { foo: 'bar' }, + }, + { + 'content-type': 'application/json', + } + ); + req.method = 'POST'; + + const response = await runHandler(func, req as any); + expect(response.status).to.equal(204); + expect(gotData).to.deep.equal({ foo: 'bar' }); + }); +}); + describe('callable CORS', () => { it('handles OPTIONS preflight', async () => { const func = https.onCall((data, context) => { diff --git a/spec/v2/providers/https.spec.ts b/spec/v2/providers/https.spec.ts index a28cb1c9c..d815d43e2 100644 --- a/spec/v2/providers/https.spec.ts +++ b/spec/v2/providers/https.spec.ts @@ -366,3 +366,139 @@ describe('onCall', () => { https.onCall((request: https.CallableRequest) => `Hello, ${request.data}`); }); }); + +describe('onTaskEnqueue', () => { + beforeEach(() => { + options.setGlobalOptions({}); + process.env.GCLOUD_PROJECT = 'aProject'; + }); + + afterEach(() => { + delete process.env.GCLOUD_PROJECT; + }); + + it('should return a minimal trigger with appropriate values', () => { + const result = https.onTaskEnqueue(() => {}); + expect(result.__trigger).to.deep.equal({ + apiVersion: 2, + platform: 'gcfv2', + taskQueueTrigger: {}, + labels: {}, + }); + }); + + it('should create a complex trigger with appropriate values', () => { + const result = https.onTaskEnqueue( + { + ...FULL_OPTIONS, + retryConfig: { + maxAttempts: 4, + maxDoublings: 3, + minBackoffSeconds: 1, + maxBackoffSeconds: 2, + }, + rateLimits: { + maxBurstSize: 10, + maxConcurrentDispatches: 5, + maxDispatchesPerSecond: 10, + }, + invoker: 'private', + }, + () => {} + ); + expect(result.__trigger).to.deep.equal({ + ...FULL_TRIGGER, + taskQueueTrigger: { + retryConfig: { + maxAttempts: 4, + maxDoublings: 3, + minBackoffSeconds: 1, + maxBackoffSeconds: 2, + }, + rateLimits: { + maxBurstSize: 10, + maxConcurrentDispatches: 5, + maxDispatchesPerSecond: 10, + }, + invoker: ['private'], + }, + }); + }); + + it('should merge options and globalOptions', () => { + options.setGlobalOptions({ + concurrency: 20, + region: 'europe-west1', + minInstances: 1, + }); + + const result = https.onTaskEnqueue( + { + region: 'us-west1', + minInstances: 3, + }, + (request) => {} + ); + + expect(result.__trigger).to.deep.equal({ + apiVersion: 2, + platform: 'gcfv2', + taskQueueTrigger: {}, + concurrency: 20, + minInstances: 3, + regions: ['us-west1'], + labels: {}, + }); + }); + + it('has a .run method', async () => { + const request: any = { + data: 'data', + auth: { + uid: 'abc', + token: 'token', + }, + }; + const cf = https.onTaskEnqueue((r) => { + expect(r.data).to.deep.equal(request.data); + expect(r.auth).to.deep.equal(request.auth); + }); + + await cf.run(request); + }); + + it('should be an express handler', async () => { + const func = https.onTaskEnqueue((request) => {}); + + const req = new MockRequest( + { + data: {}, + }, + { + 'content-type': 'application/json', + origin: 'example.com', + } + ); + req.method = 'POST'; + + const resp = await runHandler(func, req as any); + expect(resp.status).to.equal(204); + }); + + // These tests pass if the code transpiles + it('allows desirable syntax', () => { + https.onTaskEnqueue((request: https.TaskRequest) => { + // There should be no lint warnings that data is not a string. + console.log(`hello, ${request.data}`); + }); + https.onTaskEnqueue((request: https.TaskRequest) => { + console.log(`hello, ${request.data}`); + }); + https.onTaskEnqueue((request: https.TaskRequest) => { + console.log(`hello, ${request.data}`); + }); + https.onTaskEnqueue((request: https.TaskRequest) => { + console.log(`Hello, ${request.data}`); + }); + }); +}); diff --git a/src/common/encoding.ts b/src/common/encoding.ts index 0cbc2d50f..8960b5ddc 100644 --- a/src/common/encoding.ts +++ b/src/common/encoding.ts @@ -21,6 +21,9 @@ export function copyIfPresent( src: Src, ...fields: Array ) { + if (!src) { + return; + } for (const field of fields) { if (!Object.prototype.hasOwnProperty.call(src, field)) { continue; @@ -38,6 +41,9 @@ export function convertIfPresent( return from; } ) { + if (!src) { + return; + } if (!Object.prototype.hasOwnProperty.call(src, srcField)) { return; } diff --git a/src/common/providers/https.ts b/src/common/providers/https.ts index fa64116cd..0340bbce4 100644 --- a/src/common/providers/https.ts +++ b/src/common/providers/https.ts @@ -169,6 +169,56 @@ export interface CallableRequest { rawRequest: Request; } +/** How a task should be retried in the event of a non-2xx return. */ +export interface TaskRetryConfig { + // If left unspecified, will default to 3 + maxAttempts?: number; + + // If left unspecified will default to 1hr + maxBackoffSeconds?: number; + + // If left unspecified will default to 16 + maxDoublings?: number; + + // If left unspecified will default to 100ms + minBackoffSeconds?: number; +} + +/** How congestion control should be applied to the function. */ +export interface TaskRateLimits { + // If left unspecified, will default to 100 + maxBurstSize?: number; + + // If left unspecified, wild default to 1000 + maxConcurrentDispatches?: number; + + // If left unspecified, will default to 500 + maxDispatchesPerSecond?: number; +} + +/** Metadata about a call to a Task Queue function. */ +export interface TaskContext { + /** + * The result of decoding and verifying an ODIC token. + */ + auth?: AuthData; +} + +/** + * The request used to call a Task Queue function. + */ +export interface TaskRequest { + /** + * The parameters used by a client when calling this function. + */ + data: T; + + /** + * The result of decoding and verifying an ODIC token. + */ + auth?: AuthData; +} + /** * The set of Firebase Functions status codes. The codes are the same at the * ones exposed by gRPC here: @@ -561,68 +611,24 @@ export function unsafeDecodeAppCheckToken(token: string): DecodedAppCheckToken { * @param {CallableContext} ctx - Context to be sent to callable function handler. * @return {CallableTokenStatus} Status of the token verifications. */ -/** @hidden */ +/** @internal */ async function checkTokens( req: Request, ctx: CallableContext ): Promise { const verifications: CallableTokenStatus = { - app: 'MISSING', - auth: 'MISSING', + app: 'INVALID', + auth: 'INVALID', }; - const skipTokenVerify = isDebugFeatureEnabled('skipTokenVerification'); - - const appCheck = req.header('X-Firebase-AppCheck'); - if (appCheck) { - verifications.app = 'INVALID'; - try { - if (!apps().admin.appCheck) { - throw new Error( - 'Cannot validate AppCheck token. Please update Firebase Admin SDK to >= v9.8.0' - ); - } - let appCheckData; - if (skipTokenVerify) { - const decodedToken = unsafeDecodeAppCheckToken(appCheck); - appCheckData = { appId: decodedToken.app_id, token: decodedToken }; - } else { - appCheckData = await apps() - .admin.appCheck() - .verifyToken(appCheck); - } - ctx.app = appCheckData; - verifications.app = 'VALID'; - } catch (err) { - logger.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 { - let authToken: firebase.auth.DecodedIdToken; - if (skipTokenVerify) { - authToken = unsafeDecodeIdToken(idToken); - } else { - authToken = await apps() - .admin.auth() - .verifyIdToken(idToken); - } - verifications.auth = 'VALID'; - ctx.auth = { - uid: authToken.uid, - token: authToken, - }; - } catch (err) { - logger.warn('Failed to validate auth token.', err); - } - } - } + await Promise.all([ + Promise.resolve().then(async () => { + verifications.auth = await checkAuthToken(req, ctx); + }), + Promise.resolve().then(async () => { + verifications.app = await checkAppCheckToken(req, ctx); + }), + ]); const logPayload = { verifications, @@ -651,19 +657,89 @@ async function checkTokens( return verifications; } -type v1Handler = (data: any, context: CallableContext) => any | Promise; -type v2Handler = (request: CallableRequest) => Res; +/** @interanl */ +async function checkAuthToken( + req: Request, + ctx: CallableContext | TaskContext +): Promise { + const authorization = req.header('Authorization'); + if (!authorization) { + return 'MISSING'; + } + const match = authorization.match(/^Bearer (.*)$/); + if (match) { + const idToken = match[1]; + try { + let authToken: firebase.auth.DecodedIdToken; + if (isDebugFeatureEnabled('skipTokenVerification')) { + authToken = unsafeDecodeIdToken(idToken); + } else { + authToken = await apps() + .admin.auth() + .verifyIdToken(idToken); + } + ctx.auth = { + uid: authToken.uid, + token: authToken, + }; + return 'VALID'; + } catch (err) { + logger.warn('Failed to validate auth token.', err); + return 'INVALID'; + } + } +} + +/** @internal */ +async function checkAppCheckToken( + req: Request, + ctx: CallableContext +): Promise { + const appCheck = req.header('X-Firebase-AppCheck'); + if (!appCheck) { + return 'MISSING'; + } + try { + if (!apps().admin.appCheck) { + throw new Error( + 'Cannot validate AppCheck token. Please update Firebase Admin SDK to >= v9.8.0' + ); + } + let appCheckData; + if (isDebugFeatureEnabled('skipTokenVerification')) { + const decodedToken = unsafeDecodeAppCheckToken(appCheck); + appCheckData = { appId: decodedToken.app_id, token: decodedToken }; + } else { + appCheckData = await apps() + .admin.appCheck() + .verifyToken(appCheck); + } + ctx.app = appCheckData; + return 'VALID'; + } catch (err) { + logger.warn('Failed to validate AppCheck token.', err); + return 'INVALID'; + } +} + +type v1CallableHandler = ( + data: any, + context: CallableContext +) => any | Promise; +type v2CallableHandler = (request: CallableRequest) => Res; +type v1TaskHandler = (data: any, context: TaskContext) => void | Promise; +type v2TaskHandler = (request: TaskRequest) => void | Promise; -/** @hidden **/ +/** @internal **/ export interface CallableOptions { cors: cors.CorsOptions; allowInvalidAppCheckToken?: boolean; } -/** @hidden */ +/** @internal */ export function onCallHandler( options: CallableOptions, - handler: v1Handler | v2Handler + handler: v1CallableHandler | v2CallableHandler ): (req: Request, res: express.Response) => Promise { const wrapped = wrapOnCallHandler(options, handler); return (req: Request, res: express.Response) => { @@ -679,7 +755,7 @@ export function onCallHandler( /** @internal */ function wrapOnCallHandler( options: CallableOptions, - handler: v1Handler | v2Handler + handler: v1CallableHandler | v2CallableHandler ): (req: Request, res: express.Response) => Promise { return async (req: Request, res: express.Response): Promise => { try { @@ -740,3 +816,50 @@ function wrapOnCallHandler( } }; } + +/** @internal */ +export function onEnqueueHandler( + handler: v1TaskHandler | v2TaskHandler +): (req: Request, res: express.Response) => Promise { + return async (req: Request, res: express.Response): Promise => { + try { + if (!isValidRequest(req)) { + logger.error('Invalid request, unable to process.'); + throw new HttpsError('invalid-argument', 'Bad Request'); + } + + const context: TaskContext = {}; + const status = await checkAuthToken(req, context); + // Note: this should never happen since task queue functions are guarded by IAM. + if (status === 'INVALID') { + throw new HttpsError('unauthenticated', 'Unauthenticated'); + } + + const data: Req = decode(req.body.data); + if (handler.length === 2) { + await handler(data, context); + } else { + const arg: TaskRequest = { + ...context, + data, + }; + // For some reason the type system isn't picking up that the handler + // is a one argument function. + await (handler as any)(arg); + } + + res.status(204).end(); + } catch (err) { + if (!(err instanceof HttpsError)) { + // This doesn't count as an 'explicit' error. + logger.error('Unhandled error', err); + err = new HttpsError('internal', 'INTERNAL'); + } + + const { status } = err.httpErrorCode; + const body = { error: err.toJSON() }; + + res.status(status).send(body); + } + }; +} diff --git a/src/function-builder.ts b/src/function-builder.ts index ada3b724c..f8a6b19d2 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -355,6 +355,15 @@ export class FunctionBuilder { context: https.CallableContext ) => any | Promise ) => https._onCallWithOptions(handler, this.options), + + /** + * Declares a task queue function for clients to call using a Firebase Admin SDK. + * @param handler A method that takes a data and context and returns void or Promise. + */ + /** @hidden */ + taskQueue: (options?: https.TaskQueueOptions) => { + return new https.TaskQueueBuilder(options, this.options); + }, }; } diff --git a/src/handler-builder.ts b/src/handler-builder.ts index ad4cd1541..2f66d1572 100644 --- a/src/handler-builder.ts +++ b/src/handler-builder.ts @@ -82,6 +82,22 @@ export class HandlerBuilder { func.__trigger = {}; return func; }, + /** @hidden */ + get taskQueue() { + return { + onEnqueue( + handler: ( + data: any, + context: https.TaskContext + ) => void | Promise + ) { + const builder = new https.TaskQueueBuilder(); + const func = builder.onEnqueue(handler); + func.__trigger = {}; + return func; + }, + }; + }, }; } diff --git a/src/providers/https.ts b/src/providers/https.ts index 8a48f41fe..7039e37a0 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -23,17 +23,36 @@ import * as express from 'express'; import { HttpsFunction, optionsToTrigger, Runnable } from '../cloud-functions'; -import { convertIfPresent, convertInvoker } from '../common/encoding'; +import { + convertIfPresent, + convertInvoker, + copyIfPresent, +} from '../common/encoding'; import { CallableContext, FunctionsErrorCode, HttpsError, onCallHandler, + onEnqueueHandler, Request, + TaskContext, + TaskRateLimits, + TaskRetryConfig, } from '../common/providers/https'; import { DeploymentOptions } from '../function-configuration'; -export { Request, CallableContext, FunctionsErrorCode, HttpsError }; +export { + Request, + CallableContext, + FunctionsErrorCode, + HttpsError, + /** @hidden */ + TaskRetryConfig as TaskRetryPolicy, + /** @hidden */ + TaskRateLimits, + /** @hidden */ + TaskContext, +}; /** * Handle HTTP requests. @@ -56,6 +75,77 @@ export function onCall( return _onCallWithOptions(handler, {}); } +/** + * Configurations for Task Queue Functions. + * @hidden + */ +export interface TaskQueueOptions { + retryConfig?: TaskRetryConfig; + rateLimits?: TaskRateLimits; + + /** + * Who can enqueue tasks for this function. + * If left unspecified, only service accounts which have + * roles/cloudtasks.enqueuer and roles/cloudfunctions.invoker + * will have permissions. + */ + invoker?: 'private' | string | string[]; +} + +/** @hidden */ +export interface TaskQueueFunction { + (req: Request, res: express.Response): Promise; + __trigger: unknown; + run(data: any, context: TaskContext): void | Promise; +} + +/** @hidden */ +export class TaskQueueBuilder { + /** @internal */ + constructor( + private readonly tqOpts?: TaskQueueOptions, + private readonly depOpts?: DeploymentOptions + ) {} + + onEnqueue( + handler: (data: any, context: TaskContext) => void | Promise + ): TaskQueueFunction { + // onEnqueueHandler sniffs the function length of the passed-in callback + // and the user could have only tried to listen to data. Wrap their handler + // in another handler to avoid accidentally triggering the v2 API + const fixedLen = (data: any, context: TaskContext) => + handler(data, context); + const func: any = onEnqueueHandler(fixedLen); + + func.__trigger = { + ...optionsToTrigger(this.depOpts || {}), + taskQueueTrigger: {}, + }; + copyIfPresent(func.__trigger.taskQueueTrigger, this.tqOpts, 'retryConfig'); + copyIfPresent(func.__trigger.taskQueueTrigger, this.tqOpts, 'rateLimits'); + convertIfPresent( + func.__trigger.taskQueueTrigger, + this.tqOpts, + 'invoker', + 'invoker', + convertInvoker + ); + + func.run = handler; + + return func; + } +} + +/** + * Declares a function that can handle tasks enqueued using the Firebase Admin SDK. + * @param options Configuration for the Task Queue that feeds into this function. + * @hidden + */ +export function taskQueue(options?: TaskQueueOptions): TaskQueueBuilder { + return new TaskQueueBuilder(options); +} + /** @hidden */ export function _onRequestWithOptions( handler: (req: Request, resp: express.Response) => void | Promise, diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 9e4c466e4..6f6725e63 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -22,18 +22,34 @@ import * as cors from 'cors'; import * as express from 'express'; -import { convertIfPresent, convertInvoker } from '../../common/encoding'; +import { + convertIfPresent, + convertInvoker, + copyIfPresent, +} from '../../common/encoding'; import { CallableRequest, FunctionsErrorCode, HttpsError, onCallHandler, + onEnqueueHandler, Request, + TaskRateLimits, + TaskRequest, + TaskRetryConfig, } from '../../common/providers/https'; import * as options from '../options'; -export { Request, CallableRequest, FunctionsErrorCode, HttpsError }; +export { + Request, + CallableRequest, + FunctionsErrorCode, + HttpsError, + TaskRateLimits, + TaskRequest, + TaskRetryConfig as TaskRetryPolicy, +}; export interface HttpsOptions extends Omit { region?: @@ -43,6 +59,18 @@ export interface HttpsOptions extends Omit { cors?: string | boolean | RegExp | Array; } +export interface TaskQueueOptions extends options.GlobalOptions { + retryConfig?: TaskRetryConfig; + rateLimits?: TaskRateLimits; + /** + * Who can enqueue tasks for this function. + * If left unspecified, only service accounts which have + * roles/cloudtasks.enqueuer and roles/cloudfunctions.invoker + * will have permissions. + */ + invoker?: 'private' | string | string[]; +} + export type HttpsFunction = (( req: Request, res: express.Response @@ -50,6 +78,9 @@ export type HttpsFunction = (( export interface CallableFunction extends HttpsFunction { run(data: CallableRequest): Return; } +export interface TaskQueueFunction extends HttpsFunction { + run(data: TaskRequest): void | Promise; +} export function onRequest( opts: HttpsOptions, @@ -194,3 +225,72 @@ export function onCall>( func.run = handler; return func; } + +/** Handle a request sent to a Cloud Tasks queue. */ +export function onTaskEnqueue( + handler: (request: TaskRequest) => void | Promise +): TaskQueueFunction; + +/** Handle a request sent to a Cloud Tasks queue. */ +export function onTaskEnqueue( + options: TaskQueueOptions, + handler: (request: TaskRequest) => void | Promise +): TaskQueueFunction; + +export function onTaskEnqueue( + optsOrHandler: + | TaskQueueOptions + | ((request: TaskRequest) => void | Promise), + handler?: (request: TaskRequest) => void | Promise +): TaskQueueFunction { + let opts: TaskQueueOptions; + if (arguments.length == 1) { + opts = {}; + handler = optsOrHandler as ( + request: TaskRequest + ) => void | Promise; + } else { + opts = optsOrHandler as TaskQueueOptions; + } + + // onEnqueueHandler sniffs the function length to determine which API to present. + // fix the length to prevent api versions from being mismatched. + const fixedLen = (req: TaskRequest) => handler(req); + const func: any = onEnqueueHandler(fixedLen); + + Object.defineProperty(func, '__trigger', { + get: () => { + const baseOpts = options.optionsToTriggerAnnotations( + options.getGlobalOptions() + ); + // global options calls region a scalar and https allows it to be an array, + // but optionsToTriggerAnnotations handles both cases. + const specificOpts = options.optionsToTriggerAnnotations( + opts as options.GlobalOptions + ); + const taskQueueTrigger: Record = {}; + copyIfPresent(taskQueueTrigger, opts, 'retryConfig', 'rateLimits'); + convertIfPresent( + taskQueueTrigger, + opts, + 'invoker', + 'invoker', + convertInvoker + ); + return { + apiVersion: 2, + platform: 'gcfv2', + ...baseOpts, + ...specificOpts, + labels: { + ...baseOpts?.labels, + ...specificOpts?.labels, + }, + taskQueueTrigger, + }; + }, + }); + + func.run = handler; + return func; +} From c9bc7362aaea376e5209070551f52deb7ffe7ea4 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Mon, 15 Nov 2021 09:28:56 -0800 Subject: [PATCH 020/370] Rename enqueue to dispatch to match API proposal (#1004) * Rename enqueue to dispatch to match API proposal * Update src/common/providers/https.ts Co-authored-by: Daniel Lee * Update src/function-builder.ts Co-authored-by: Daniel Lee Co-authored-by: Daniel Lee --- spec/common/providers/https.spec.ts | 4 ++-- spec/v1/providers/https.spec.ts | 8 ++++---- spec/v2/providers/https.spec.ts | 18 +++++++++--------- src/common/providers/https.ts | 22 +++++++++++++++++----- src/function-builder.ts | 2 +- src/handler-builder.ts | 2 +- src/providers/https.ts | 6 +++--- src/v2/providers/https.ts | 10 +++++----- 8 files changed, 42 insertions(+), 30 deletions(-) diff --git a/spec/common/providers/https.spec.ts b/spec/common/providers/https.spec.ts index c0a1c7767..d9d8e63c4 100644 --- a/spec/common/providers/https.spec.ts +++ b/spec/common/providers/https.spec.ts @@ -166,7 +166,7 @@ interface TaskTest { // Runs a TaskTest test. async function runTaskTest(test: TaskTest): Promise { - const taskQueueFunctionV1 = https.onEnqueueHandler((data, context) => { + const taskQueueFunctionV1 = https.onDispatchHandler((data, context) => { expect(data).to.deep.equal(test.expectedData); if (test.taskFunction) { test.taskFunction(data, context); @@ -176,7 +176,7 @@ async function runTaskTest(test: TaskTest): Promise { const responseV1 = await runHandler(taskQueueFunctionV1, test.httpRequest); expect(responseV1.status).to.equal(test.expectedStatus); - const taskQueueFunctionV2 = https.onEnqueueHandler((request) => { + const taskQueueFunctionV2 = https.onDispatchHandler((request) => { expect(request.data).to.deep.equal(test.expectedData); if (test.taskFunction2) { test.taskFunction2(request); diff --git a/spec/v1/providers/https.spec.ts b/spec/v1/providers/https.spec.ts index e8c4785c6..2fbddcd26 100644 --- a/spec/v1/providers/https.spec.ts +++ b/spec/v1/providers/https.spec.ts @@ -225,7 +225,7 @@ describe('#onEnqueue', () => { }, invoker: 'private', }) - .onEnqueue(() => {}); + .onDispatch(() => {}); expect(result.__trigger).to.deep.equal({ taskQueueTrigger: { rateLimits: { @@ -252,7 +252,7 @@ describe('#onEnqueue', () => { memory: '256MB', }) .https.taskQueue({ retryConfig: { maxAttempts: 5 } }) - .onEnqueue(() => null); + .onDispatch(() => null); expect(fn.__trigger).to.deep.equal({ regions: ['us-east1'], @@ -275,7 +275,7 @@ describe('#onEnqueue', () => { }, }; let done = false; - const cf = https.taskQueue().onEnqueue((d, c) => { + const cf = https.taskQueue().onDispatch((d, c) => { expect(d).to.equal(data); expect(c).to.deep.equal(context); done = true; @@ -288,7 +288,7 @@ describe('#onEnqueue', () => { // Regression test for firebase-functions#947 it('should lock to the v1 API even with function.length == 1', async () => { let gotData: Record; - const func = https.taskQueue().onEnqueue((data) => { + const func = https.taskQueue().onDispatch((data) => { gotData = data; }); diff --git a/spec/v2/providers/https.spec.ts b/spec/v2/providers/https.spec.ts index d815d43e2..ef9efbc29 100644 --- a/spec/v2/providers/https.spec.ts +++ b/spec/v2/providers/https.spec.ts @@ -378,7 +378,7 @@ describe('onTaskEnqueue', () => { }); it('should return a minimal trigger with appropriate values', () => { - const result = https.onTaskEnqueue(() => {}); + const result = https.onTaskDispatched(() => {}); expect(result.__trigger).to.deep.equal({ apiVersion: 2, platform: 'gcfv2', @@ -388,7 +388,7 @@ describe('onTaskEnqueue', () => { }); it('should create a complex trigger with appropriate values', () => { - const result = https.onTaskEnqueue( + const result = https.onTaskDispatched( { ...FULL_OPTIONS, retryConfig: { @@ -432,7 +432,7 @@ describe('onTaskEnqueue', () => { minInstances: 1, }); - const result = https.onTaskEnqueue( + const result = https.onTaskDispatched( { region: 'us-west1', minInstances: 3, @@ -459,7 +459,7 @@ describe('onTaskEnqueue', () => { token: 'token', }, }; - const cf = https.onTaskEnqueue((r) => { + const cf = https.onTaskDispatched((r) => { expect(r.data).to.deep.equal(request.data); expect(r.auth).to.deep.equal(request.auth); }); @@ -468,7 +468,7 @@ describe('onTaskEnqueue', () => { }); it('should be an express handler', async () => { - const func = https.onTaskEnqueue((request) => {}); + const func = https.onTaskDispatched((request) => {}); const req = new MockRequest( { @@ -487,17 +487,17 @@ describe('onTaskEnqueue', () => { // These tests pass if the code transpiles it('allows desirable syntax', () => { - https.onTaskEnqueue((request: https.TaskRequest) => { + https.onTaskDispatched((request: https.TaskRequest) => { // There should be no lint warnings that data is not a string. console.log(`hello, ${request.data}`); }); - https.onTaskEnqueue((request: https.TaskRequest) => { + https.onTaskDispatched((request: https.TaskRequest) => { console.log(`hello, ${request.data}`); }); - https.onTaskEnqueue((request: https.TaskRequest) => { + https.onTaskDispatched((request: https.TaskRequest) => { console.log(`hello, ${request.data}`); }); - https.onTaskEnqueue((request: https.TaskRequest) => { + https.onTaskDispatched((request: https.TaskRequest) => { console.log(`Hello, ${request.data}`); }); }); diff --git a/src/common/providers/https.ts b/src/common/providers/https.ts index 0340bbce4..5cbc016a1 100644 --- a/src/common/providers/https.ts +++ b/src/common/providers/https.ts @@ -171,16 +171,28 @@ export interface CallableRequest { /** How a task should be retried in the event of a non-2xx return. */ export interface TaskRetryConfig { - // If left unspecified, will default to 3 + /** + * Maximum number of times a request should be attempted. + * If left unspecified, will default to 3. + */ maxAttempts?: number; - // If left unspecified will default to 1hr + /** + * The maximum amount of time to wait between attempts. + * If left unspecified will default to 1hr. + */ maxBackoffSeconds?: number; - // If left unspecified will default to 16 + /** + * The maximum number of times to double the backoff between + * retries. If left unspecified will default to 16. + */ maxDoublings?: number; - // If left unspecified will default to 100ms + /** + * The minimum time to wait between attempts. If left unspecified + * will default to 100ms. + */ minBackoffSeconds?: number; } @@ -818,7 +830,7 @@ function wrapOnCallHandler( } /** @internal */ -export function onEnqueueHandler( +export function onDispatchHandler( handler: v1TaskHandler | v2TaskHandler ): (req: Request, res: express.Response) => Promise { return async (req: Request, res: express.Response): Promise => { diff --git a/src/function-builder.ts b/src/function-builder.ts index f8a6b19d2..ef6f1ad16 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -358,7 +358,7 @@ export class FunctionBuilder { /** * Declares a task queue function for clients to call using a Firebase Admin SDK. - * @param handler A method that takes a data and context and returns void or Promise. + * @param options Configurations for the task queue function. */ /** @hidden */ taskQueue: (options?: https.TaskQueueOptions) => { diff --git a/src/handler-builder.ts b/src/handler-builder.ts index 2f66d1572..7ebf78f15 100644 --- a/src/handler-builder.ts +++ b/src/handler-builder.ts @@ -92,7 +92,7 @@ export class HandlerBuilder { ) => void | Promise ) { const builder = new https.TaskQueueBuilder(); - const func = builder.onEnqueue(handler); + const func = builder.onDispatch(handler); func.__trigger = {}; return func; }, diff --git a/src/providers/https.ts b/src/providers/https.ts index 7039e37a0..ecefd6c29 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -33,7 +33,7 @@ import { FunctionsErrorCode, HttpsError, onCallHandler, - onEnqueueHandler, + onDispatchHandler, Request, TaskContext, TaskRateLimits, @@ -107,7 +107,7 @@ export class TaskQueueBuilder { private readonly depOpts?: DeploymentOptions ) {} - onEnqueue( + onDispatch( handler: (data: any, context: TaskContext) => void | Promise ): TaskQueueFunction { // onEnqueueHandler sniffs the function length of the passed-in callback @@ -115,7 +115,7 @@ export class TaskQueueBuilder { // in another handler to avoid accidentally triggering the v2 API const fixedLen = (data: any, context: TaskContext) => handler(data, context); - const func: any = onEnqueueHandler(fixedLen); + const func: any = onDispatchHandler(fixedLen); func.__trigger = { ...optionsToTrigger(this.depOpts || {}), diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 6f6725e63..43556a1ba 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -33,7 +33,7 @@ import { FunctionsErrorCode, HttpsError, onCallHandler, - onEnqueueHandler, + onDispatchHandler, Request, TaskRateLimits, TaskRequest, @@ -227,17 +227,17 @@ export function onCall>( } /** Handle a request sent to a Cloud Tasks queue. */ -export function onTaskEnqueue( +export function onTaskDispatched( handler: (request: TaskRequest) => void | Promise ): TaskQueueFunction; /** Handle a request sent to a Cloud Tasks queue. */ -export function onTaskEnqueue( +export function onTaskDispatched( options: TaskQueueOptions, handler: (request: TaskRequest) => void | Promise ): TaskQueueFunction; -export function onTaskEnqueue( +export function onTaskDispatched( optsOrHandler: | TaskQueueOptions | ((request: TaskRequest) => void | Promise), @@ -256,7 +256,7 @@ export function onTaskEnqueue( // onEnqueueHandler sniffs the function length to determine which API to present. // fix the length to prevent api versions from being mismatched. const fixedLen = (req: TaskRequest) => handler(req); - const func: any = onEnqueueHandler(fixedLen); + const func: any = onDispatchHandler(fixedLen); Object.defineProperty(func, '__trigger', { get: () => { From 9016314de51c02691e2e43abe1707468f18e6074 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Thu, 2 Dec 2021 12:21:02 -0800 Subject: [PATCH 021/370] Add new region support (#1010) --- CHANGELOG.md | 1 + src/v2/options.ts | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f41e7f57d..2d6230214 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ - Parallelizes network calls that occur when validating authorization for onCall handlers. +- Adds new regions to V2 API diff --git a/src/v2/options.ts b/src/v2/options.ts index 076cb8532..0b2238a9f 100644 --- a/src/v2/options.ts +++ b/src/v2/options.ts @@ -33,7 +33,12 @@ import { ParamSpec } from './params/types'; /** * List of all regions supported by Cloud Functions v2 */ -export const SUPPORTED_REGIONS = ['us-west1', 'europe-west4'] as const; +export const SUPPORTED_REGIONS = [ + 'us-west1', + 'us-central1', + 'europe-west4', + 'asia-northeast1', +] as const; /** * A region known to be supported by CloudFunctions v2 From 4e48f0326d257b9e2cd27dd6d61fc4559d3514cf Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 19 Jan 2022 10:18:09 -0800 Subject: [PATCH 022/370] Container contract support for Firebase Functions SDK (#1014) * Annotate function triggers with __endpoint property (#999) In addition to annotating function triggers with `__trigger` property, we add `__endpoint` annotation. This property will be used by the to-be-developed functions runtime to generate/declare deployment manifest that the CLI will use to deploy the function. There are lots of code duplication between the utility functions for annotating the `__trigger` and `__endpoint` properties. I didn't try to refactor the common code since I expect that we will favor `__endpoint` property in the future. * Add missing import. * More of annotate endpoint property for functions (#1009) Follows up https://github.com/firebase/firebase-functions/pull/999 to annotate each funuctions with `__endpoint` property. Highlight of changes: * Extend unit test coverage for all v1 providers * Add `__endpoint` annotation to v1 task queue functions * Add `__requiredAPIs` annotation to task queue and scheduler functions * Explicitly set `__endpoint` to undefined in the handler namespace * No SDK-level label setting in the __endpoint annotation. --- spec/v1/cloud-functions.spec.ts | 108 +++++++++- spec/v1/providers/analytics.spec.ts | 22 +- spec/v1/providers/auth.spec.ts | 85 ++++++-- spec/v1/providers/database.spec.ts | 184 +++++++++++----- spec/v1/providers/firestore.spec.ts | 72 ++++--- spec/v1/providers/https.spec.ts | 62 +++++- spec/v1/providers/pubsub.spec.ts | 100 ++++++++- spec/v1/providers/remoteConfig.spec.ts | 43 ++-- spec/v1/providers/storage.spec.ts | 278 +++++++++++++++---------- spec/v1/providers/testLab.spec.ts | 21 +- spec/v2/providers/helpers.ts | 19 ++ spec/v2/providers/https.spec.ts | 59 +++++- spec/v2/providers/pubsub.spec.ts | 66 +++++- spec/v2/providers/storage.spec.ts | 267 +++++++++++++++++++++++- src/cloud-functions.ts | 102 ++++++++- src/common/manifest.ts | 85 ++++++++ src/handler-builder.ts | 68 +++--- src/providers/https.ts | 52 ++++- src/v2/core.ts | 5 +- src/v2/options.ts | 50 ++++- src/v2/providers/https.ts | 48 ++++- src/v2/providers/pubsub.ts | 29 ++- src/v2/providers/storage.ts | 42 +++- 23 files changed, 1564 insertions(+), 303 deletions(-) create mode 100644 src/common/manifest.ts diff --git a/spec/v1/cloud-functions.spec.ts b/spec/v1/cloud-functions.spec.ts index 05e80bd21..c894bfa23 100644 --- a/spec/v1/cloud-functions.spec.ts +++ b/spec/v1/cloud-functions.spec.ts @@ -41,7 +41,7 @@ describe('makeCloudFunction', () => { legacyEventType: 'providers/provider/eventTypes/event', }; - it('should put a __trigger on the returned CloudFunction', () => { + it('should put a __trigger/__endpoint on the returned CloudFunction', () => { const cf = makeCloudFunction({ provider: 'mock.provider', eventType: 'mock.event', @@ -49,6 +49,7 @@ describe('makeCloudFunction', () => { triggerResource: () => 'resource', handler: () => null, }); + expect(cf.__trigger).to.deep.equal({ eventTrigger: { eventType: 'mock.provider.mock.event', @@ -56,10 +57,23 @@ describe('makeCloudFunction', () => { service: 'service', }, }); + + expect(cf.__endpoint).to.deep.equal({ + platform: 'gcfv1', + eventTrigger: { + eventType: 'mock.provider.mock.event', + eventFilters: { + resource: 'resource', + }, + retry: false, + }, + labels: {}, + }); }); - it('should have legacy event type in __trigger if provided', () => { + it('should have legacy event type in __trigger/__endpoint if provided', () => { const cf = makeCloudFunction(cloudFunctionArgs); + expect(cf.__trigger).to.deep.equal({ eventTrigger: { eventType: 'providers/provider/eventTypes/event', @@ -67,6 +81,96 @@ describe('makeCloudFunction', () => { service: 'service', }, }); + + expect(cf.__endpoint).to.deep.equal({ + platform: 'gcfv1', + eventTrigger: { + eventType: 'providers/provider/eventTypes/event', + eventFilters: { + resource: 'resource', + }, + retry: false, + }, + labels: {}, + }); + }); + + it('should include converted options in __endpoint', () => { + const cf = makeCloudFunction({ + provider: 'mock.provider', + eventType: 'mock.event', + service: 'service', + triggerResource: () => 'resource', + handler: () => null, + options: { + timeoutSeconds: 10, + regions: ['us-central1'], + memory: '128MB', + serviceAccount: 'foo@google.com', + }, + }); + + expect(cf.__endpoint).to.deep.equal({ + platform: 'gcfv1', + timeoutSeconds: 10, + region: ['us-central1'], + availableMemoryMb: 128, + serviceAccountEmail: 'foo@google.com', + eventTrigger: { + eventType: 'mock.provider.mock.event', + eventFilters: { + resource: 'resource', + }, + retry: false, + }, + labels: {}, + }); + }); + + it('should set retry given failure policy in __endpoint', () => { + const cf = makeCloudFunction({ + provider: 'mock.provider', + eventType: 'mock.event', + service: 'service', + triggerResource: () => 'resource', + handler: () => null, + options: { failurePolicy: { retry: {} } }, + }); + + expect(cf.__endpoint).to.deep.equal({ + platform: 'gcfv1', + eventTrigger: { + eventType: 'mock.provider.mock.event', + eventFilters: { + resource: 'resource', + }, + retry: true, + }, + labels: {}, + }); + }); + + it('should setup a scheduleTrigger in __endpoint given a schedule', () => { + const schedule = { + schedule: 'every 5 minutes', + retryConfig: { retryCount: 3 }, + timeZone: 'America/New_York', + }; + const cf = makeCloudFunction({ + provider: 'mock.provider', + eventType: 'mock.event', + service: 'service', + triggerResource: () => 'resource', + handler: () => null, + options: { + schedule, + }, + }); + expect(cf.__endpoint).to.deep.equal({ + platform: 'gcfv1', + scheduleTrigger: schedule, + labels: {}, + }); }); it('should construct the right context for event', () => { diff --git a/spec/v1/providers/analytics.spec.ts b/spec/v1/providers/analytics.spec.ts index d92244439..24eb0a008 100644 --- a/spec/v1/providers/analytics.spec.ts +++ b/spec/v1/providers/analytics.spec.ts @@ -50,10 +50,14 @@ describe('Analytics Functions', () => { 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'); + + expect(fn.__endpoint.region).to.deep.equal(['us-east1']); + expect(fn.__endpoint.availableMemoryMb).to.deep.equal(256); + expect(fn.__endpoint.timeoutSeconds).to.deep.equal(90); }); describe('#onLog', () => { - it('should return a TriggerDefinition with appropriate values', () => { + it('should return a trigger/endpoint with appropriate values', () => { const cloudFunction = analytics.event('first_open').onLog(() => null); expect(cloudFunction.__trigger).to.deep.equal({ @@ -64,6 +68,19 @@ describe('Analytics Functions', () => { service: 'app-measurement.com', }, }); + + expect(cloudFunction.__endpoint).to.deep.equal({ + platform: 'gcfv1', + eventTrigger: { + eventFilters: { + resource: 'projects/project1/events/first_open', + }, + eventType: + 'providers/google.firebase.analytics/eventTypes/event.log', + retry: false, + }, + labels: {}, + }); }); }); @@ -305,11 +322,12 @@ describe('Analytics Functions', () => { describe('handler namespace', () => { describe('#onLog', () => { - it('should return an empty trigger', () => { + it('should return an empty trigger/endpoint', () => { const cloudFunction = functions.handler.analytics.event.onLog( () => null ); expect(cloudFunction.__trigger).to.deep.equal({}); + expect(cloudFunction.__endpoint).to.undefined; }); it('should handle an event with the appropriate fields', () => { diff --git a/spec/v1/providers/auth.spec.ts b/spec/v1/providers/auth.spec.ts index e22052573..6168cf767 100644 --- a/spec/v1/providers/auth.spec.ts +++ b/spec/v1/providers/auth.spec.ts @@ -51,12 +51,38 @@ describe('Auth Functions', () => { }; describe('AuthBuilder', () => { + function expectedTrigger(project: string, eventType: string) { + return { + eventTrigger: { + resource: `projects/${project}`, + eventType: `providers/firebase.auth/eventTypes/${eventType}`, + service: 'firebaseauth.googleapis.com', + }, + }; + } + + function expectedEndpoint(project: string, eventType: string) { + return { + platform: 'gcfv1', + eventTrigger: { + eventFilters: { + resource: `projects/${project}`, + }, + eventType: `providers/firebase.auth/eventTypes/${eventType}`, + retry: false, + }, + labels: {}, + }; + } + const handler = (user: firebase.auth.UserRecord) => { return Promise.resolve(); }; + const project = 'project1'; + before(() => { - process.env.GCLOUD_PROJECT = 'project1'; + process.env.GCLOUD_PROJECT = project; }); after(() => { @@ -76,31 +102,37 @@ describe('Auth Functions', () => { 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'); + + expect(fn.__endpoint.region).to.deep.equal(['us-east1']); + expect(fn.__endpoint.availableMemoryMb).to.deep.equal(256); + expect(fn.__endpoint.timeoutSeconds).to.deep.equal(90); }); describe('#onCreate', () => { - it('should return a TriggerDefinition with appropriate values', () => { + it('should return a trigger/endpoint with appropriate values', () => { const cloudFunction = auth.user().onCreate(() => null); - expect(cloudFunction.__trigger).to.deep.equal({ - eventTrigger: { - eventType: 'providers/firebase.auth/eventTypes/user.create', - resource: 'projects/project1', - service: 'firebaseauth.googleapis.com', - }, - }); + + expect(cloudFunction.__trigger).to.deep.equal( + expectedTrigger(project, 'user.create') + ); + + expect(cloudFunction.__endpoint).to.deep.equal( + expectedEndpoint(project, 'user.create') + ); }); }); describe('#onDelete', () => { - it('should return a TriggerDefinition with appropriate values', () => { + it('should return a trigger/endpoint with appropriate values', () => { const cloudFunction = auth.user().onDelete(handler); - expect(cloudFunction.__trigger).to.deep.equal({ - eventTrigger: { - eventType: 'providers/firebase.auth/eventTypes/user.delete', - resource: 'projects/project1', - service: 'firebaseauth.googleapis.com', - }, - }); + + expect(cloudFunction.__trigger).to.deep.equal( + expectedTrigger(project, 'user.delete') + ); + + expect(cloudFunction.__endpoint).to.deep.equal( + expectedEndpoint(project, 'user.delete') + ); }); }); @@ -198,6 +230,11 @@ describe('Auth Functions', () => { const cloudFunction = functions.handler.auth.user.onCreate(() => null); expect(cloudFunction.__trigger).to.deep.equal({}); }); + + it('should return an empty endpoint', () => { + const cloudFunction = functions.handler.auth.user.onCreate(() => null); + expect(cloudFunction.__endpoint).to.be.undefined; + }); }); describe('#onDelete', () => { @@ -206,13 +243,15 @@ describe('Auth Functions', () => { ); it('should return an empty trigger', () => { - const handler = (user: firebase.auth.UserRecord) => { - return Promise.resolve(); - }; - const cloudFunction = functions.handler.auth.user.onDelete(handler); + const cloudFunction = functions.handler.auth.user.onDelete(() => null); expect(cloudFunction.__trigger).to.deep.equal({}); }); + it('should return an empty endpoint', () => { + const cloudFunction = functions.handler.auth.user.onDelete(() => null); + expect(cloudFunction.__endpoint).to.be.undefined; + }); + it('should handle wire format as of v5.0.0 of firebase-admin', () => { return cloudFunctionDelete(event.data, event.context).then( (data: any) => { @@ -237,6 +276,10 @@ describe('Auth Functions', () => { expect(() => auth.user().onCreate(() => null).__trigger).to.throw(Error); }); + it('should throw when endpoint is accessed', () => { + expect(() => auth.user().onCreate(() => null).__endpoint).to.throw(Error); + }); + it('should not throw when #run is called', () => { const cf = auth.user().onCreate(() => null); expect(cf.run).to.not.throw(Error); diff --git a/spec/v1/providers/database.spec.ts b/spec/v1/providers/database.spec.ts index d49c27854..f932dd871 100644 --- a/spec/v1/providers/database.spec.ts +++ b/spec/v1/providers/database.spec.ts @@ -31,6 +31,30 @@ describe('Database Functions', () => { describe('DatabaseBuilder', () => { // TODO add tests for building a data or change based on the type of operation + function expectedTrigger(resource: string, eventType: string) { + return { + eventTrigger: { + resource, + eventType: `providers/google.firebase.database/eventTypes/${eventType}`, + service: 'firebaseio.com', + }, + }; + } + + function expectedEndpoint(resource: string, eventType: string) { + return { + platform: 'gcfv1', + eventTrigger: { + eventFilters: { + resource, + }, + eventType: `providers/google.firebase.database/eventTypes/${eventType}`, + retry: false, + }, + labels: {}, + }; + } + before(() => { (config as any).firebaseConfigCache = { databaseURL: 'https://subdomain.apse.firebasedatabase.app', @@ -56,21 +80,29 @@ describe('Database Functions', () => { 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'); + + expect(fn.__endpoint.region).to.deep.equal(['us-east1']); + expect(fn.__endpoint.availableMemoryMb).to.deep.equal(256); + expect(fn.__endpoint.timeoutSeconds).to.deep.equal(90); }); describe('#onWrite()', () => { - it('should return "ref.write" as the event type', () => { - const eventType = database.ref('foo').onWrite(() => null).__trigger - .eventTrigger.eventType; - expect(eventType).to.eq( - 'providers/google.firebase.database/eventTypes/ref.write' + it('should return a trigger/endpoint with appropriate values', () => { + const func = database.ref('foo').onWrite(() => null); + + expect(func.__trigger).to.deep.equal( + expectedTrigger( + 'projects/_/instances/subdomain/refs/foo', + 'ref.write' + ) ); - }); - it('should construct a proper resource path', () => { - const resource = database.ref('foo').onWrite(() => null).__trigger - .eventTrigger.resource; - expect(resource).to.eq('projects/_/instances/subdomain/refs/foo'); + expect(func.__endpoint).to.deep.equal( + expectedEndpoint( + 'projects/_/instances/subdomain/refs/foo', + 'ref.write' + ) + ); }); it('should let developers choose a database instance', () => { @@ -78,8 +110,14 @@ describe('Database Functions', () => { .instance('custom') .ref('foo') .onWrite(() => null); - const resource = func.__trigger.eventTrigger.resource; - expect(resource).to.eq('projects/_/instances/custom/refs/foo'); + + expect(func.__trigger).to.deep.equal( + expectedTrigger('projects/_/instances/custom/refs/foo', 'ref.write') + ); + + expect(func.__endpoint).to.deep.equal( + expectedEndpoint('projects/_/instances/custom/refs/foo', 'ref.write') + ); }); it('should return a handler that emits events with a proper DataSnapshot', () => { @@ -107,18 +145,22 @@ describe('Database Functions', () => { }); describe('#onCreate()', () => { - it('should return "ref.create" as the event type', () => { - const eventType = database.ref('foo').onCreate(() => null).__trigger - .eventTrigger.eventType; - expect(eventType).to.eq( - 'providers/google.firebase.database/eventTypes/ref.create' + it('should return a trigger/endpoint with appropriate values', () => { + const func = database.ref('foo').onCreate(() => null); + + expect(func.__trigger).to.deep.equal( + expectedTrigger( + 'projects/_/instances/subdomain/refs/foo', + 'ref.create' + ) ); - }); - it('should construct a proper resource path', () => { - const resource = database.ref('foo').onCreate(() => null).__trigger - .eventTrigger.resource; - expect(resource).to.eq('projects/_/instances/subdomain/refs/foo'); + expect(func.__endpoint).to.deep.equal( + expectedEndpoint( + 'projects/_/instances/subdomain/refs/foo', + 'ref.create' + ) + ); }); it('should let developers choose a database instance', () => { @@ -126,8 +168,14 @@ describe('Database Functions', () => { .instance('custom') .ref('foo') .onCreate(() => null); - const resource = func.__trigger.eventTrigger.resource; - expect(resource).to.eq('projects/_/instances/custom/refs/foo'); + + expect(func.__trigger).to.deep.equal( + expectedTrigger('projects/_/instances/custom/refs/foo', 'ref.create') + ); + + expect(func.__endpoint).to.deep.equal( + expectedEndpoint('projects/_/instances/custom/refs/foo', 'ref.create') + ); }); it('should return a handler that emits events with a proper DataSnapshot', () => { @@ -156,18 +204,22 @@ describe('Database Functions', () => { }); describe('#onUpdate()', () => { - it('should return "ref.update" as the event type', () => { - const eventType = database.ref('foo').onUpdate(() => null).__trigger - .eventTrigger.eventType; - expect(eventType).to.eq( - 'providers/google.firebase.database/eventTypes/ref.update' + it('should return a trigger/endpoint with appropriate values', () => { + const func = database.ref('foo').onUpdate(() => null); + + expect(func.__trigger).to.deep.equal( + expectedTrigger( + 'projects/_/instances/subdomain/refs/foo', + 'ref.update' + ) ); - }); - it('should construct a proper resource path', () => { - const resource = database.ref('foo').onUpdate(() => null).__trigger - .eventTrigger.resource; - expect(resource).to.eq('projects/_/instances/subdomain/refs/foo'); + expect(func.__endpoint).to.deep.equal( + expectedEndpoint( + 'projects/_/instances/subdomain/refs/foo', + 'ref.update' + ) + ); }); it('should let developers choose a database instance', () => { @@ -175,8 +227,14 @@ describe('Database Functions', () => { .instance('custom') .ref('foo') .onUpdate(() => null); - const resource = func.__trigger.eventTrigger.resource; - expect(resource).to.eq('projects/_/instances/custom/refs/foo'); + + expect(func.__trigger).to.deep.equal( + expectedTrigger('projects/_/instances/custom/refs/foo', 'ref.update') + ); + + expect(func.__endpoint).to.deep.equal( + expectedEndpoint('projects/_/instances/custom/refs/foo', 'ref.update') + ); }); it('should return a handler that emits events with a proper DataSnapshot', () => { @@ -205,18 +263,22 @@ describe('Database Functions', () => { }); describe('#onDelete()', () => { - it('should return "ref.delete" as the event type', () => { - const eventType = database.ref('foo').onDelete(() => null).__trigger - .eventTrigger.eventType; - expect(eventType).to.eq( - 'providers/google.firebase.database/eventTypes/ref.delete' + it('should return a trigger/endpoint with appropriate values', () => { + const func = database.ref('foo').onDelete(() => null); + + expect(func.__trigger).to.deep.equal( + expectedTrigger( + 'projects/_/instances/subdomain/refs/foo', + 'ref.delete' + ) ); - }); - it('should construct a proper resource path', () => { - const resource = database.ref('foo').onDelete(() => null).__trigger - .eventTrigger.resource; - expect(resource).to.eq('projects/_/instances/subdomain/refs/foo'); + expect(func.__endpoint).to.deep.equal( + expectedEndpoint( + 'projects/_/instances/subdomain/refs/foo', + 'ref.delete' + ) + ); }); it('should let developers choose a database instance', () => { @@ -224,8 +286,14 @@ describe('Database Functions', () => { .instance('custom') .ref('foo') .onDelete(() => null); - const resource = func.__trigger.eventTrigger.resource; - expect(resource).to.eq('projects/_/instances/custom/refs/foo'); + + expect(func.__trigger).to.deep.equal( + expectedTrigger('projects/_/instances/custom/refs/foo', 'ref.delete') + ); + + expect(func.__endpoint).to.deep.equal( + expectedEndpoint('projects/_/instances/custom/refs/foo', 'ref.delete') + ); }); it('should return a handler that emits events with a proper DataSnapshot', () => { @@ -259,6 +327,7 @@ describe('Database Functions', () => { it('correctly sets trigger to {}', () => { const cf = functions.handler.database.ref.onWrite(() => null); expect(cf.__trigger).to.deep.equal({}); + expect(cf.__endpoint).to.be.undefined; }); it('should be able to use the instance entry point', () => { @@ -266,6 +335,7 @@ describe('Database Functions', () => { () => null ); expect(func.__trigger).to.deep.equal({}); + expect(func.__endpoint).to.be.undefined; }); it('should return a handler that emits events with a proper DataSnapshot', () => { @@ -296,7 +366,8 @@ describe('Database Functions', () => { describe('#onCreate()', () => { it('correctly sets trigger to {}', () => { const cf = functions.handler.database.ref.onCreate(() => null); - return expect(cf.__trigger).to.deep.equal({}); + expect(cf.__trigger).to.deep.equal({}); + expect(cf.__endpoint).to.be.undefined; }); it('should be able to use the instance entry point', () => { @@ -304,6 +375,7 @@ describe('Database Functions', () => { () => null ); expect(func.__trigger).to.deep.equal({}); + expect(func.__endpoint).to.be.undefined; }); it('should return a handler that emits events with a proper DataSnapshot', () => { @@ -333,7 +405,8 @@ describe('Database Functions', () => { describe('#onUpdate()', () => { it('correctly sets trigger to {}', () => { const cf = functions.handler.database.ref.onUpdate(() => null); - return expect(cf.__trigger).to.deep.equal({}); + expect(cf.__trigger).to.deep.equal({}); + expect(cf.__endpoint).to.be.undefined; }); it('should be able to use the instance entry point', () => { @@ -341,6 +414,7 @@ describe('Database Functions', () => { () => null ); expect(func.__trigger).to.deep.equal({}); + expect(func.__endpoint).to.be.undefined; }); it('should return a handler that emits events with a proper DataSnapshot', () => { @@ -370,7 +444,8 @@ describe('Database Functions', () => { describe('#onDelete()', () => { it('correctly sets trigger to {}', () => { const cf = functions.handler.database.ref.onDelete(() => null); - return expect(cf.__trigger).to.deep.equal({}); + expect(cf.__trigger).to.deep.equal({}); + expect(cf.__endpoint).to.be.undefined; }); it('should be able to use the instance entry point', () => { @@ -378,6 +453,7 @@ describe('Database Functions', () => { () => null ); expect(func.__trigger).to.deep.equal({}); + expect(func.__endpoint).to.be.undefined; }); it('should return a handler that emits events with a proper DataSnapshot', () => { @@ -419,6 +495,12 @@ describe('Database Functions', () => { ).to.throw(Error); }); + it('should throw when endpoint is accessed', () => { + expect( + () => database.ref('/path').onWrite(() => null).__endpoint + ).to.throw(Error); + }); + it('should not throw when #run is called', () => { const cf = database.ref('/path').onWrite(() => null); expect(cf.run).to.not.throw(Error); diff --git a/spec/v1/providers/firestore.spec.ts b/spec/v1/providers/firestore.spec.ts index 25cffaf6c..3f9f07fbe 100644 --- a/spec/v1/providers/firestore.spec.ts +++ b/spec/v1/providers/firestore.spec.ts @@ -103,6 +103,20 @@ describe('Firestore Functions', () => { }; } + function expectedEndpoint(resource: string, eventType: string) { + return { + platform: 'gcfv1', + eventTrigger: { + eventFilters: { + resource, + }, + eventType: `providers/cloud.firestore/eventTypes/${eventType}`, + retry: false, + }, + labels: {}, + }; + } + before(() => { process.env.GCLOUD_PROJECT = 'project1'; }); @@ -117,9 +131,14 @@ describe('Firestore Functions', () => { const cloudFunction = firestore .document('users/{uid}') .onWrite(() => null); + expect(cloudFunction.__trigger).to.deep.equal( expectedTrigger(resource, 'document.write') ); + + expect(cloudFunction.__endpoint).to.deep.equal( + expectedEndpoint(resource, 'document.write') + ); }); it('should allow custom namespaces', () => { @@ -129,9 +148,14 @@ describe('Firestore Functions', () => { .namespace('v2') .document('users/{uid}') .onWrite(() => null); + expect(cloudFunction.__trigger).to.deep.equal( expectedTrigger(resource, 'document.write') ); + + expect(cloudFunction.__endpoint).to.deep.equal( + expectedEndpoint(resource, 'document.write') + ); }); it('should allow custom databases', () => { @@ -140,9 +164,14 @@ describe('Firestore Functions', () => { .database('myDB') .document('users/{uid}') .onWrite(() => null); + expect(cloudFunction.__trigger).to.deep.equal( expectedTrigger(resource, 'document.write') ); + + expect(cloudFunction.__endpoint).to.deep.equal( + expectedEndpoint(resource, 'document.write') + ); }); it('should allow both custom database and namespace', () => { @@ -153,9 +182,14 @@ describe('Firestore Functions', () => { .namespace('v2') .document('users/{uid}') .onWrite(() => null); + expect(cloudFunction.__trigger).to.deep.equal( expectedTrigger(resource, 'document.write') ); + + expect(cloudFunction.__endpoint).to.deep.equal( + expectedEndpoint(resource, 'document.write') + ); }); it('should allow both region and runtime options to be set', () => { @@ -171,36 +205,10 @@ describe('Firestore Functions', () => { 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'); - }); - - it('onCreate should have the "document.create" eventType', () => { - const resource = - 'projects/project1/databases/(default)/documents/users/{uid}'; - const eventType = firestore.document('users/{uid}').onCreate(() => null) - .__trigger.eventTrigger.eventType; - expect(eventType).to.eq( - expectedTrigger(resource, 'document.create').eventTrigger.eventType - ); - }); - it('onUpdate should have the "document.update" eventType', () => { - const resource = - 'projects/project1/databases/(default)/documents/users/{uid}'; - const eventType = firestore.document('users/{uid}').onUpdate(() => null) - .__trigger.eventTrigger.eventType; - expect(eventType).to.eq( - expectedTrigger(resource, 'document.update').eventTrigger.eventType - ); - }); - - it('onDelete should have the "document.delete" eventType', () => { - const resource = - 'projects/project1/databases/(default)/documents/users/{uid}'; - const eventType = firestore.document('users/{uid}').onDelete(() => null) - .__trigger.eventTrigger.eventType; - expect(eventType).to.eq( - expectedTrigger(resource, 'document.delete').eventTrigger.eventType - ); + expect(fn.__endpoint.region).to.deep.equal(['us-east1']); + expect(fn.__endpoint.availableMemoryMb).to.deep.equal(256); + expect(fn.__endpoint.timeoutSeconds).to.deep.equal(90); }); }); @@ -217,6 +225,12 @@ describe('Firestore Functions', () => { ).to.throw(Error); }); + it('should throw when endpoint is accessed', () => { + expect( + () => firestore.document('input').onCreate(() => null).__endpoint + ).to.throw(Error); + }); + it('should not throw when #run is called', () => { const cf = firestore.document('input').onCreate(() => null); expect(cf.run).to.not.throw(Error); diff --git a/spec/v1/providers/https.spec.ts b/spec/v1/providers/https.spec.ts index 2fbddcd26..22e6d52fd 100644 --- a/spec/v1/providers/https.spec.ts +++ b/spec/v1/providers/https.spec.ts @@ -22,7 +22,7 @@ import { expect } from 'chai'; import * as express from 'express'; -import * as _ from 'lodash'; + import * as functions from '../../../src/index'; import * as https from '../../../src/providers/https'; import { @@ -94,11 +94,15 @@ function runHandler( describe('CloudHttpsBuilder', () => { describe('#onRequest', () => { - it('should return a Trigger with appropriate values', () => { + it('should return a trigger with appropriate values', () => { const result = https.onRequest((req, resp) => { resp.send(200); }); expect(result.__trigger).to.deep.equal({ httpsTrigger: {} }); + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv1', + httpsTrigger: {}, + }); }); it('should allow both region and runtime options to be set', () => { @@ -115,6 +119,11 @@ describe('CloudHttpsBuilder', () => { expect(fn.__trigger.availableMemoryMb).to.deep.equal(256); expect(fn.__trigger.timeout).to.deep.equal('90s'); expect(fn.__trigger.httpsTrigger.invoker).to.deep.equal(['private']); + + expect(fn.__endpoint.region).to.deep.equal(['us-east1']); + expect(fn.__endpoint.availableMemoryMb).to.deep.equal(256); + expect(fn.__endpoint.timeoutSeconds).to.deep.equal(90); + expect(fn.__endpoint.httpsTrigger.invoker).to.deep.equal(['private']); }); }); }); @@ -126,6 +135,7 @@ describe('handler namespace', () => { res.send(200); }); expect(result.__trigger).to.deep.equal({}); + expect(result.__endpoint).to.be.undefined; }); }); @@ -133,6 +143,7 @@ describe('handler namespace', () => { it('should return an empty trigger', () => { const result = functions.handler.https.onCall(() => null); expect(result.__trigger).to.deep.equal({}); + expect(result.__endpoint).to.be.undefined; }); }); @@ -140,19 +151,27 @@ describe('handler namespace', () => { it('should return an empty trigger', () => { const result = functions.handler.https.taskQueue.onEnqueue(() => null); expect(result.__trigger).to.deep.equal({}); + expect(result.__endpoint).to.be.undefined; }); }); }); describe('#onCall', () => { - it('should return a Trigger with appropriate values', () => { + it('should return a trigger/endpoint with appropriate values', () => { const result = https.onCall((data) => { return 'response'; }); + expect(result.__trigger).to.deep.equal({ httpsTrigger: {}, labels: { 'deployment-callable': 'true' }, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv1', + callableTrigger: {}, + labels: {}, + }); }); it('should allow both region and runtime options to be set', () => { @@ -167,6 +186,10 @@ describe('#onCall', () => { 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'); + + expect(fn.__endpoint.region).to.deep.equal(['us-east1']); + expect(fn.__endpoint.availableMemoryMb).to.deep.equal(256); + expect(fn.__endpoint.timeoutSeconds).to.deep.equal(90); }); it('has a .run method', () => { @@ -209,7 +232,7 @@ describe('#onCall', () => { }); describe('#onEnqueue', () => { - it('should return a Trigger with appropriate values', () => { + it('should return a trigger/endpoint with appropriate values', () => { const result = https .taskQueue({ rateLimits: { @@ -226,6 +249,7 @@ describe('#onEnqueue', () => { invoker: 'private', }) .onDispatch(() => {}); + expect(result.__trigger).to.deep.equal({ taskQueueTrigger: { rateLimits: { @@ -242,6 +266,24 @@ describe('#onEnqueue', () => { invoker: ['private'], }, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv1', + taskQueueTrigger: { + rateLimits: { + maxBurstSize: 20, + maxConcurrentDispatches: 30, + maxDispatchesPerSecond: 40, + }, + retryConfig: { + maxAttempts: 5, + maxBackoffSeconds: 20, + maxDoublings: 3, + minBackoffSeconds: 5, + }, + invoker: ['private'], + }, + }); }); it('should allow both region and runtime options to be set', () => { @@ -264,6 +306,18 @@ describe('#onEnqueue', () => { }, }, }); + + expect(fn.__endpoint).to.deep.equal({ + platform: 'gcfv1', + region: ['us-east1'], + availableMemoryMb: 256, + timeoutSeconds: 90, + taskQueueTrigger: { + retryConfig: { + maxAttempts: 5, + }, + }, + }); }); it('has a .run method', async () => { diff --git a/spec/v1/providers/pubsub.spec.ts b/spec/v1/providers/pubsub.spec.ts index 1ab8c7ba4..5547515ab 100644 --- a/spec/v1/providers/pubsub.spec.ts +++ b/spec/v1/providers/pubsub.spec.ts @@ -85,12 +85,17 @@ describe('Pubsub Functions', () => { 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'); + + expect(fn.__endpoint.region).to.deep.equal(['us-east1']); + expect(fn.__endpoint.availableMemoryMb).to.deep.equal(256); + expect(fn.__endpoint.timeoutSeconds).to.deep.equal(90); }); describe('#onPublish', () => { - it('should return a TriggerDefinition with appropriate values', () => { + it('should return a trigger/endpoint with appropriate values', () => { // Pick up project from process.env.GCLOUD_PROJECT const result = pubsub.topic('toppy').onPublish(() => null); + expect(result.__trigger).to.deep.equal({ eventTrigger: { eventType: 'google.pubsub.topic.publish', @@ -98,6 +103,18 @@ describe('Pubsub Functions', () => { service: 'pubsub.googleapis.com', }, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv1', + eventTrigger: { + eventType: 'google.pubsub.topic.publish', + eventFilters: { + resource: 'projects/project1/topics/toppy', + }, + retry: false, + }, + labels: {}, + }); }); it('should throw with improperly formatted topics', () => { @@ -143,27 +160,38 @@ describe('Pubsub Functions', () => { }); describe('#schedule', () => { - it('should return a trigger with schedule', () => { + it('should return a trigger/endpoint with schedule', () => { const result = pubsub .schedule('every 5 minutes') .onRun((context) => null); + expect(result.__trigger.schedule).to.deep.equal({ schedule: 'every 5 minutes', }); + + expect(result.__endpoint.scheduleTrigger).to.deep.equal({ + schedule: 'every 5 minutes', + }); }); - it('should return a trigger with schedule and timeZone when one is chosen', () => { + it('should return a trigger/endpoint with schedule and timeZone when one is chosen', () => { const result = pubsub .schedule('every 5 minutes') .timeZone('America/New_York') .onRun((context) => null); + expect(result.__trigger.schedule).to.deep.equal({ schedule: 'every 5 minutes', timeZone: 'America/New_York', }); + + expect(result.__endpoint.scheduleTrigger).to.deep.equal({ + schedule: 'every 5 minutes', + timeZone: 'America/New_York', + }); }); - it('should return a trigger with schedule and retry config when called with retryConfig', () => { + it('should return a trigger/endpoint with schedule and retry config when called with retryConfig', () => { const retryConfig = { retryCount: 3, maxRetryDuration: '10 minutes', @@ -175,6 +203,7 @@ describe('Pubsub Functions', () => { .schedule('every 5 minutes') .retryConfig(retryConfig) .onRun(() => null); + expect(result.__trigger.schedule).to.deep.equal({ schedule: 'every 5 minutes', retryConfig, @@ -182,10 +211,16 @@ describe('Pubsub Functions', () => { expect(result.__trigger.labels).to.deep.equal({ 'deployment-scheduled': 'true', }); + + expect(result.__endpoint.scheduleTrigger).to.deep.equal({ + schedule: 'every 5 minutes', + retryConfig, + }); + expect(result.__endpoint.labels).to.be.empty; }); it( - 'should return a trigger with schedule, timeZone and retry config' + + 'should return a trigger/endpoint with schedule, timeZone and retry config' + 'when called with retryConfig and timeout', () => { const retryConfig = { @@ -200,6 +235,7 @@ describe('Pubsub Functions', () => { .timeZone('America/New_York') .retryConfig(retryConfig) .onRun(() => null); + expect(result.__trigger.schedule).to.deep.equal({ schedule: 'every 5 minutes', retryConfig, @@ -208,10 +244,17 @@ describe('Pubsub Functions', () => { expect(result.__trigger.labels).to.deep.equal({ 'deployment-scheduled': 'true', }); + + expect(result.__endpoint.scheduleTrigger).to.deep.equal({ + schedule: 'every 5 minutes', + retryConfig, + timeZone: 'America/New_York', + }); + expect(result.__endpoint.labels).to.be.empty; } ); - it('should return an appropriate trigger when called with region and options', () => { + it('should return an appropriate trigger/endpoint when called with region and options', () => { const result = functions .region('us-east1') .runWith({ @@ -226,9 +269,16 @@ describe('Pubsub Functions', () => { expect(result.__trigger.regions).to.deep.equal(['us-east1']); expect(result.__trigger.availableMemoryMb).to.deep.equal(256); expect(result.__trigger.timeout).to.deep.equal('90s'); + + expect(result.__endpoint.scheduleTrigger).to.deep.equal({ + schedule: 'every 5 minutes', + }); + expect(result.__endpoint.region).to.deep.equal(['us-east1']); + expect(result.__endpoint.availableMemoryMb).to.deep.equal(256); + expect(result.__endpoint.timeoutSeconds).to.deep.equal(90); }); - it('should return an appropriate trigger when called with region, timeZone, and options', () => { + it('should return an appropriate trigger/endpoint when called with region, timeZone, and options', () => { const result = functions .region('us-east1') .runWith({ @@ -245,9 +295,17 @@ describe('Pubsub Functions', () => { expect(result.__trigger.regions).to.deep.equal(['us-east1']); expect(result.__trigger.availableMemoryMb).to.deep.equal(256); expect(result.__trigger.timeout).to.deep.equal('90s'); + + expect(result.__endpoint.scheduleTrigger).to.deep.equal({ + schedule: 'every 5 minutes', + timeZone: 'America/New_York', + }); + expect(result.__endpoint.region).to.deep.equal(['us-east1']); + expect(result.__endpoint.availableMemoryMb).to.deep.equal(256); + expect(result.__endpoint.timeoutSeconds).to.deep.equal(90); }); - it('should return an appropriate trigger when called with region, options and retryConfig', () => { + it('should return an appropriate trigger/endpoint when called with region, options and retryConfig', () => { const retryConfig = { retryCount: 3, maxRetryDuration: '10 minutes', @@ -274,9 +332,17 @@ describe('Pubsub Functions', () => { expect(result.__trigger.regions).to.deep.equal(['us-east1']); expect(result.__trigger.availableMemoryMb).to.deep.equal(256); expect(result.__trigger.timeout).to.deep.equal('90s'); + + expect(result.__endpoint.scheduleTrigger).to.deep.equal({ + schedule: 'every 5 minutes', + retryConfig, + }); + expect(result.__endpoint.region).to.deep.equal(['us-east1']); + expect(result.__endpoint.availableMemoryMb).to.deep.equal(256); + expect(result.__endpoint.timeoutSeconds).to.deep.equal(90); }); - it('should return an appropriate trigger when called with region, options, retryConfig, and timeZone', () => { + it('should return an appropriate trigger/endpoint when called with region, options, retryConfig, and timeZone', () => { const retryConfig = { retryCount: 3, maxRetryDuration: '10 minutes', @@ -305,6 +371,15 @@ describe('Pubsub Functions', () => { expect(result.__trigger.regions).to.deep.equal(['us-east1']); expect(result.__trigger.availableMemoryMb).to.deep.equal(256); expect(result.__trigger.timeout).to.deep.equal('90s'); + + expect(result.__endpoint.scheduleTrigger).to.deep.equal({ + schedule: 'every 5 minutes', + timeZone: 'America/New_York', + retryConfig, + }); + expect(result.__endpoint.region).to.deep.equal(['us-east1']); + expect(result.__endpoint.availableMemoryMb).to.deep.equal(256); + expect(result.__endpoint.timeoutSeconds).to.deep.equal(90); }); }); }); @@ -315,6 +390,7 @@ describe('Pubsub Functions', () => { it('should return an empty trigger', () => { const result = functions.handler.pubsub.topic.onPublish(() => null); expect(result.__trigger).to.deep.equal({}); + expect(result.__endpoint).to.be.undefined; }); it('should properly handle a new-style event', () => { @@ -406,6 +482,12 @@ describe('Pubsub Functions', () => { ).to.throw(Error); }); + it('should throw when endpoint is accessed', () => { + expect( + () => pubsub.topic('toppy').onPublish(() => null).__endpoint + ).to.throw(Error); + }); + it('should not throw when #run is called', () => { const cf = pubsub.topic('toppy').onPublish(() => null); expect(cf.run).to.not.throw(Error); diff --git a/spec/v1/providers/remoteConfig.spec.ts b/spec/v1/providers/remoteConfig.spec.ts index f7221cf99..f3fde2043 100644 --- a/spec/v1/providers/remoteConfig.spec.ts +++ b/spec/v1/providers/remoteConfig.spec.ts @@ -20,13 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import { expect } from 'chai'; -import * as _ from 'lodash'; import { CloudFunction, Event, EventContext, - TriggerAnnotated, } from '../../../src/cloud-functions'; import * as functions from '../../../src/index'; import * as remoteConfig from '../../../src/providers/remoteConfig'; @@ -47,18 +45,6 @@ describe('RemoteConfig Functions', () => { } describe('#onUpdate', () => { - function expectedTrigger(): TriggerAnnotated { - return { - __trigger: { - eventTrigger: { - resource: 'projects/project1', - eventType: 'google.firebase.remoteconfig.update', - service: 'firebaseremoteconfig.googleapis.com', - }, - }, - }; - } - before(() => { process.env.GCLOUD_PROJECT = 'project1'; }); @@ -69,9 +55,26 @@ describe('RemoteConfig Functions', () => { it('should have the correct trigger', () => { const cloudFunction = remoteConfig.onUpdate(() => null); - expect(cloudFunction.__trigger).to.deep.equal( - expectedTrigger().__trigger - ); + + expect(cloudFunction.__trigger).to.deep.equal({ + eventTrigger: { + resource: 'projects/project1', + eventType: 'google.firebase.remoteconfig.update', + service: 'firebaseremoteconfig.googleapis.com', + }, + }); + + expect(cloudFunction.__endpoint).to.deep.equal({ + platform: 'gcfv1', + eventTrigger: { + eventType: 'google.firebase.remoteconfig.update', + eventFilters: { + resource: 'projects/project1', + }, + retry: false, + }, + labels: {}, + }); }); it('should allow both region and runtime options to be set', () => { @@ -86,6 +89,10 @@ describe('RemoteConfig Functions', () => { expect(cloudFunction.__trigger.regions).to.deep.equal(['us-east1']); expect(cloudFunction.__trigger.availableMemoryMb).to.deep.equal(256); expect(cloudFunction.__trigger.timeout).to.deep.equal('90s'); + + expect(cloudFunction.__endpoint.region).to.deep.equal(['us-east1']); + expect(cloudFunction.__endpoint.availableMemoryMb).to.deep.equal(256); + expect(cloudFunction.__endpoint.timeoutSeconds).to.deep.equal(90); }); }); @@ -135,7 +142,9 @@ describe('RemoteConfig Functions', () => { const cloudFunction = functions.handler.remoteConfig.onUpdate( () => null ); + expect(cloudFunction.__trigger).to.deep.equal({}); + expect(cloudFunction.__endpoint).to.be.undefined; }); it('should correctly unwrap the event', () => { diff --git a/spec/v1/providers/storage.spec.ts b/spec/v1/providers/storage.spec.ts index 02762630d..d96b131b9 100644 --- a/spec/v1/providers/storage.spec.ts +++ b/spec/v1/providers/storage.spec.ts @@ -28,9 +28,35 @@ import * as storage from '../../../src/providers/storage'; describe('Storage Functions', () => { describe('ObjectBuilder', () => { + function expectedTrigger(bucket: string, eventType: string) { + return { + eventTrigger: { + resource: `projects/_/buckets/${bucket}`, + eventType: `google.storage.object.${eventType}`, + service: 'storage.googleapis.com', + }, + }; + } + + function expectedEndpoint(bucket: string, eventType: string) { + return { + platform: 'gcfv1', + eventTrigger: { + eventFilters: { + resource: `projects/_/buckets/${bucket}`, + }, + eventType: `google.storage.object.${eventType}`, + retry: false, + }, + labels: {}, + }; + } + + const defaultBucket = 'bucket'; + before(() => { (config as any).firebaseConfigCache = { - storageBucket: 'bucket', + storageBucket: defaultBucket, }; }); @@ -51,6 +77,10 @@ describe('Storage Functions', () => { 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'); + + expect(fn.__endpoint.region).to.deep.equal(['us-east1']); + expect(fn.__endpoint.availableMemoryMb).to.deep.equal(256); + expect(fn.__endpoint.timeoutSeconds).to.deep.equal(90); }); describe('#onArchive', () => { @@ -59,24 +89,26 @@ describe('Storage Functions', () => { .bucket('bucky') .object() .onArchive(() => null); - expect(cloudFunction.__trigger).to.deep.equal({ - eventTrigger: { - eventType: 'google.storage.object.archive', - resource: 'projects/_/buckets/bucky', - service: 'storage.googleapis.com', - }, - }); + + expect(cloudFunction.__trigger).to.deep.equal( + expectedTrigger('bucky', 'archive') + ); + + expect(cloudFunction.__endpoint).to.deep.equal( + expectedEndpoint('bucky', 'archive') + ); }); it('should use the default bucket when none is provided', () => { const cloudFunction = storage.object().onArchive(() => null); - expect(cloudFunction.__trigger).to.deep.equal({ - eventTrigger: { - eventType: 'google.storage.object.archive', - resource: 'projects/_/buckets/bucket', - service: 'storage.googleapis.com', - }, - }); + + expect(cloudFunction.__trigger).to.deep.equal( + expectedTrigger(defaultBucket, 'archive') + ); + + expect(cloudFunction.__endpoint).to.deep.equal( + expectedEndpoint(defaultBucket, 'archive') + ); }); it('should allow fully qualified bucket names', () => { @@ -85,13 +117,14 @@ describe('Storage Functions', () => { {} ); const result = subjectQualified.onArchive(() => null); - expect(result.__trigger).to.deep.equal({ - eventTrigger: { - eventType: 'google.storage.object.archive', - resource: 'projects/_/buckets/bucky', - service: 'storage.googleapis.com', - }, - }); + + expect(result.__trigger).to.deep.equal( + expectedTrigger('bucky', 'archive') + ); + + expect(result.__endpoint).to.deep.equal( + expectedEndpoint('bucky', 'archive') + ); }); it('should throw with improperly formatted buckets', () => { @@ -102,6 +135,14 @@ describe('Storage Functions', () => { .object() .onArchive(() => null).__trigger ).to.throw(Error); + + expect( + () => + storage + .bucket('bad/bucket/format') + .object() + .onArchive(() => null).__endpoint + ).to.throw(Error); }); it('should not mess with media links using non-literal slashes', () => { @@ -139,24 +180,26 @@ describe('Storage Functions', () => { .bucket('bucky') .object() .onDelete(() => null); - expect(cloudFunction.__trigger).to.deep.equal({ - eventTrigger: { - eventType: 'google.storage.object.delete', - resource: 'projects/_/buckets/bucky', - service: 'storage.googleapis.com', - }, - }); + + expect(cloudFunction.__trigger).to.deep.equal( + expectedTrigger('bucky', 'delete') + ); + + expect(cloudFunction.__endpoint).to.deep.equal( + expectedEndpoint('bucky', 'delete') + ); }); it('should use the default bucket when none is provided', () => { const cloudFunction = storage.object().onDelete(() => null); - expect(cloudFunction.__trigger).to.deep.equal({ - eventTrigger: { - eventType: 'google.storage.object.delete', - resource: 'projects/_/buckets/bucket', - service: 'storage.googleapis.com', - }, - }); + + expect(cloudFunction.__trigger).to.deep.equal( + expectedTrigger(defaultBucket, 'delete') + ); + + expect(cloudFunction.__endpoint).to.deep.equal( + expectedEndpoint(defaultBucket, 'delete') + ); }); it('should allow fully qualified bucket names', () => { @@ -165,23 +208,25 @@ describe('Storage Functions', () => { {} ); const result = subjectQualified.onDelete(() => null); - expect(result.__trigger).to.deep.equal({ - eventTrigger: { - eventType: 'google.storage.object.delete', - resource: 'projects/_/buckets/bucky', - service: 'storage.googleapis.com', - }, - }); + + expect(result.__trigger).to.deep.equal( + expectedTrigger('bucky', 'delete') + ); + + expect(result.__endpoint).to.deep.equal( + expectedEndpoint('bucky', 'delete') + ); }); it('should throw with improperly formatted buckets', () => { - expect( - () => - storage - .bucket('bad/bucket/format') - .object() - .onDelete(() => null).__trigger - ).to.throw(Error); + const fn = storage + .bucket('bad/bucket/format') + .object() + .onDelete(() => null); + + expect(() => fn.__trigger).to.throw(Error); + + expect(() => fn.__endpoint).to.throw(Error); }); it('should not mess with media links using non-literal slashes', () => { @@ -219,24 +264,26 @@ describe('Storage Functions', () => { .bucket('bucky') .object() .onFinalize(() => null); - expect(cloudFunction.__trigger).to.deep.equal({ - eventTrigger: { - eventType: 'google.storage.object.finalize', - resource: 'projects/_/buckets/bucky', - service: 'storage.googleapis.com', - }, - }); + + expect(cloudFunction.__trigger).to.deep.equal( + expectedTrigger('bucky', 'finalize') + ); + + expect(cloudFunction.__endpoint).to.deep.equal( + expectedEndpoint('bucky', 'finalize') + ); }); it('should use the default bucket when none is provided', () => { const cloudFunction = storage.object().onFinalize(() => null); - expect(cloudFunction.__trigger).to.deep.equal({ - eventTrigger: { - eventType: 'google.storage.object.finalize', - resource: 'projects/_/buckets/bucket', - service: 'storage.googleapis.com', - }, - }); + + expect(cloudFunction.__trigger).to.deep.equal( + expectedTrigger(defaultBucket, 'finalize') + ); + + expect(cloudFunction.__endpoint).to.deep.equal( + expectedEndpoint(defaultBucket, 'finalize') + ); }); it('should allow fully qualified bucket names', () => { @@ -245,23 +292,25 @@ describe('Storage Functions', () => { {} ); const result = subjectQualified.onFinalize(() => null); - expect(result.__trigger).to.deep.equal({ - eventTrigger: { - eventType: 'google.storage.object.finalize', - resource: 'projects/_/buckets/bucky', - service: 'storage.googleapis.com', - }, - }); + + expect(result.__trigger).to.deep.equal( + expectedTrigger('bucky', 'finalize') + ); + + expect(result.__endpoint).to.deep.equal( + expectedEndpoint('bucky', 'finalize') + ); }); it('should throw with improperly formatted buckets', () => { - expect( - () => - storage - .bucket('bad/bucket/format') - .object() - .onFinalize(() => null).__trigger - ).to.throw(Error); + const fn = storage + .bucket('bad/bucket/format') + .object() + .onFinalize(() => null); + + expect(() => fn.__trigger).to.throw(Error); + + expect(() => fn.__endpoint).to.throw(Error); }); it('should not mess with media links using non-literal slashes', () => { @@ -299,24 +348,26 @@ describe('Storage Functions', () => { .bucket('bucky') .object() .onMetadataUpdate(() => null); - expect(cloudFunction.__trigger).to.deep.equal({ - eventTrigger: { - eventType: 'google.storage.object.metadataUpdate', - resource: 'projects/_/buckets/bucky', - service: 'storage.googleapis.com', - }, - }); + + expect(cloudFunction.__trigger).to.deep.equal( + expectedTrigger('bucky', 'metadataUpdate') + ); + + expect(cloudFunction.__endpoint).to.deep.equal( + expectedEndpoint('bucky', 'metadataUpdate') + ); }); it('should use the default bucket when none is provided', () => { const cloudFunction = storage.object().onMetadataUpdate(() => null); - expect(cloudFunction.__trigger).to.deep.equal({ - eventTrigger: { - eventType: 'google.storage.object.metadataUpdate', - resource: 'projects/_/buckets/bucket', - service: 'storage.googleapis.com', - }, - }); + + expect(cloudFunction.__trigger).to.deep.equal( + expectedTrigger(defaultBucket, 'metadataUpdate') + ); + + expect(cloudFunction.__endpoint).to.deep.equal( + expectedEndpoint(defaultBucket, 'metadataUpdate') + ); }); it('should allow fully qualified bucket names', () => { @@ -325,23 +376,24 @@ describe('Storage Functions', () => { {} ); const result = subjectQualified.onMetadataUpdate(() => null); - expect(result.__trigger).to.deep.equal({ - eventTrigger: { - eventType: 'google.storage.object.metadataUpdate', - resource: 'projects/_/buckets/bucky', - service: 'storage.googleapis.com', - }, - }); + + expect(result.__trigger).to.deep.equal( + expectedTrigger('bucky', 'metadataUpdate') + ); + + expect(result.__endpoint).to.deep.equal( + expectedEndpoint('bucky', 'metadataUpdate') + ); }); it('should throw with improperly formatted buckets', () => { - expect( - () => - storage - .bucket('bad/bucket/format') - .object() - .onMetadataUpdate(() => null).__trigger - ).to.throw(Error); + const fn = storage + .bucket('bad/bucket/format') + .object() + .onMetadataUpdate(() => null); + + expect(() => fn.__trigger).to.throw(Error); + expect(() => fn.__endpoint).to.throw(Error); }); it('should not mess with media links using non-literal slashes', () => { @@ -390,7 +442,9 @@ describe('Storage Functions', () => { const cloudFunction = functions.handler.storage.bucket.onArchive( () => null ); + expect(cloudFunction.__trigger).to.deep.equal({}); + expect(cloudFunction.__endpoint).to.be.undefined; }); it('should not mess with media links using non-literal slashes', () => { @@ -429,7 +483,9 @@ describe('Storage Functions', () => { const cloudFunction = functions.handler.storage.bucket.onDelete( () => null ); + expect(cloudFunction.__trigger).to.deep.equal({}); + expect(cloudFunction.__endpoint).to.be.undefined; }); it('should not mess with media links using non-literal slashes', () => { @@ -468,7 +524,9 @@ describe('Storage Functions', () => { const cloudFunction = functions.handler.storage.bucket.onFinalize( () => null ); + expect(cloudFunction.__trigger).to.deep.equal({}); + expect(cloudFunction.__endpoint).to.be.undefined; }); it('should not mess with media links using non-literal slashes', () => { @@ -507,7 +565,9 @@ describe('Storage Functions', () => { const cloudFunction = functions.handler.storage.bucket.onMetadataUpdate( () => null ); + expect(cloudFunction.__trigger).to.deep.equal({}); + expect(cloudFunction.__endpoint).to.be.undefined; }); it('should not mess with media links using non-literal slashes', () => { @@ -558,6 +618,12 @@ describe('Storage Functions', () => { ); }); + it('should throw when endpoint is accessed', () => { + expect(() => storage.object().onArchive(() => null).__endpoint).to.throw( + Error + ); + }); + it('should not throw when #run is called', () => { const cf = storage.object().onArchive(() => null); expect(cf.run).to.not.throw(Error); diff --git a/spec/v1/providers/testLab.spec.ts b/spec/v1/providers/testLab.spec.ts index 3dbe8c0f5..b3ba22d7f 100644 --- a/spec/v1/providers/testLab.spec.ts +++ b/spec/v1/providers/testLab.spec.ts @@ -35,8 +35,9 @@ describe('Test Lab Functions', () => { delete process.env.GCLOUD_PROJECT; }); - it('should return a TriggerDefinition with appropriate values', () => { + it('should return a trigger/endpoint with appropriate values', () => { const func = testLab.testMatrix().onComplete(() => null); + expect(func.__trigger).to.deep.equal({ eventTrigger: { service: 'testing.googleapis.com', @@ -44,6 +45,18 @@ describe('Test Lab Functions', () => { resource: 'projects/project1/testMatrices/{matrix}', }, }); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv1', + eventTrigger: { + eventType: 'google.testing.testMatrix.complete', + eventFilters: { + resource: 'projects/project1/testMatrices/{matrix}', + }, + retry: false, + }, + labels: {}, + }); }); it('should parse TestMatrix in "INVALID" state', () => { @@ -155,6 +168,12 @@ describe('Test Lab Functions', () => { () => testLab.testMatrix().onComplete(() => null).__trigger ).to.throw(Error); }); + + it('should throw when endpoint is accessed', () => { + expect( + () => testLab.testMatrix().onComplete(() => null).__endpoint + ).to.throw(Error); + }); }); }); diff --git a/spec/v2/providers/helpers.ts b/spec/v2/providers/helpers.ts index a654a4944..a499be69a 100644 --- a/spec/v2/providers/helpers.ts +++ b/spec/v2/providers/helpers.ts @@ -33,3 +33,22 @@ export const FULL_TRIGGER = { hello: 'world', }, }; + +export const FULL_ENDPOINT = { + platform: 'gcfv2', + region: ['us-west1'], + availableMemoryMb: 512, + timeoutSeconds: 60, + minInstances: 1, + maxInstances: 3, + concurrency: 20, + vpc: { + connector: 'aConnector', + egressSettings: 'ALL_TRAFFIC', + }, + serviceAccountEmail: 'root@', + ingressSettings: 'ALLOW_ALL', + labels: { + hello: 'world', + }, +}; diff --git a/spec/v2/providers/https.spec.ts b/spec/v2/providers/https.spec.ts index ef9efbc29..5f0f7eb74 100644 --- a/spec/v2/providers/https.spec.ts +++ b/spec/v2/providers/https.spec.ts @@ -7,7 +7,7 @@ import { expectedResponseHeaders, MockRequest, } from '../../fixtures/mockrequest'; -import { FULL_OPTIONS, FULL_TRIGGER } from './helpers'; +import { FULL_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from './helpers'; /** * RunHandlerResult contains the data from an express.Response. @@ -82,10 +82,11 @@ describe('onRequest', () => { delete process.env.GCLOUD_PROJECT; }); - it('should return a minimal trigger with appropriate values', () => { + it('should return a minimal trigger/endpoint with appropriate values', () => { const result = https.onRequest((req, res) => { res.send(200); }); + expect(result.__trigger).to.deep.equal({ apiVersion: 2, platform: 'gcfv2', @@ -94,9 +95,15 @@ describe('onRequest', () => { }, labels: {}, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + httpsTrigger: {}, + labels: {}, + }); }); - it('should create a complex trigger with appropriate values', () => { + it('should create a complex trigger/endpoint with appropriate values', () => { const result = https.onRequest( { ...FULL_OPTIONS, @@ -107,6 +114,7 @@ describe('onRequest', () => { res.send(200); } ); + expect(result.__trigger).to.deep.equal({ ...FULL_TRIGGER, httpsTrigger: { @@ -115,6 +123,14 @@ describe('onRequest', () => { }, regions: ['us-west1', 'us-central1'], }); + + expect(result.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + httpsTrigger: { + invoker: ['service-account1@', 'service-account2@'], + }, + region: ['us-west1', 'us-central1'], + }); }); it('should merge options and globalOptions', () => { @@ -148,6 +164,17 @@ describe('onRequest', () => { regions: ['us-west1', 'us-central1'], labels: {}, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + httpsTrigger: { + invoker: ['private'], + }, + concurrency: 20, + minInstances: 3, + region: ['us-west1', 'us-central1'], + labels: {}, + }); }); it('should be an express handler', async () => { @@ -209,8 +236,9 @@ describe('onCall', () => { delete process.env.GCLOUD_PROJECT; }); - it('should return a minimal trigger with appropriate values', () => { + it('should return a minimal trigger/endpoint with appropriate values', () => { const result = https.onCall((request) => 42); + expect(result.__trigger).to.deep.equal({ apiVersion: 2, platform: 'gcfv2', @@ -221,10 +249,17 @@ describe('onCall', () => { 'deployment-callable': 'true', }, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + callableTrigger: {}, + labels: {}, + }); }); - it('should create a complex trigger with appropriate values', () => { + it('should create a complex trigger/endpoint with appropriate values', () => { const result = https.onCall(FULL_OPTIONS, (request) => 42); + expect(result.__trigger).to.deep.equal({ ...FULL_TRIGGER, httpsTrigger: { @@ -235,6 +270,11 @@ describe('onCall', () => { 'deployment-callable': 'true', }, }); + + expect(result.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + callableTrigger: {}, + }); }); it('should merge options and globalOptions', () => { @@ -265,6 +305,15 @@ describe('onCall', () => { 'deployment-callable': 'true', }, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + callableTrigger: {}, + concurrency: 20, + minInstances: 3, + region: ['us-west1', 'us-central1'], + labels: {}, + }); }); it('has a .run method', () => { diff --git a/spec/v2/providers/pubsub.spec.ts b/spec/v2/providers/pubsub.spec.ts index 4fc338614..f48e10f72 100644 --- a/spec/v2/providers/pubsub.spec.ts +++ b/spec/v2/providers/pubsub.spec.ts @@ -3,13 +3,21 @@ import { expect } from 'chai'; import { CloudEvent } from '../../../src/v2/core'; import * as options from '../../../src/v2/options'; import * as pubsub from '../../../src/v2/providers/pubsub'; -import { FULL_OPTIONS, FULL_TRIGGER } from './helpers'; +import { FULL_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from './helpers'; const EVENT_TRIGGER = { eventType: 'google.cloud.pubsub.topic.v1.messagePublished', resource: 'projects/aProject/topics/topic', }; +const ENDPOINT_EVENT_TRIGGER = { + eventType: 'google.cloud.pubsub.topic.v1.messagePublished', + eventFilters: { + topic: 'topic', + }, + retry: false, +}; + describe('onMessagePublished', () => { beforeEach(() => { options.setGlobalOptions({}); @@ -20,25 +28,38 @@ describe('onMessagePublished', () => { delete process.env.GCLOUD_PROJECT; }); - it('should return a minimal trigger with appropriate values', () => { + it('should return a minimal trigger/endpoint with appropriate values', () => { const result = pubsub.onMessagePublished('topic', () => 42); + expect(result.__trigger).to.deep.equal({ apiVersion: 2, platform: 'gcfv2', eventTrigger: EVENT_TRIGGER, labels: {}, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + eventTrigger: ENDPOINT_EVENT_TRIGGER, + labels: {}, + }); }); - it('should create a complex trigger with appropriate values', () => { + it('should create a complex trigger/endpoint with appropriate values', () => { const result = pubsub.onMessagePublished( { ...FULL_OPTIONS, topic: 'topic' }, () => 42 ); + expect(result.__trigger).to.deep.equal({ ...FULL_TRIGGER, eventTrigger: EVENT_TRIGGER, }); + + expect(result.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: ENDPOINT_EVENT_TRIGGER, + }); }); it('should merge options and globalOptions', () => { @@ -66,6 +87,45 @@ describe('onMessagePublished', () => { labels: {}, eventTrigger: EVENT_TRIGGER, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + concurrency: 20, + minInstances: 3, + region: ['us-west1'], + labels: {}, + eventTrigger: ENDPOINT_EVENT_TRIGGER, + }); + }); + + it('should convert retry option if appropriate', () => { + const result = pubsub.onMessagePublished( + { + topic: 'topic', + region: 'us-west1', + minInstances: 3, + retry: true, + }, + () => 42 + ); + + expect(result.__trigger).to.deep.equal({ + apiVersion: 2, + platform: 'gcfv2', + minInstances: 3, + regions: ['us-west1'], + labels: {}, + eventTrigger: EVENT_TRIGGER, + failurePolicy: { retry: true }, + }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + minInstances: 3, + region: ['us-west1'], + labels: {}, + eventTrigger: { ...ENDPOINT_EVENT_TRIGGER, retry: true }, + }); }); it('should have a .run method', () => { diff --git a/spec/v2/providers/storage.spec.ts b/spec/v2/providers/storage.spec.ts index 601b5c024..8c2e25576 100644 --- a/spec/v2/providers/storage.spec.ts +++ b/spec/v2/providers/storage.spec.ts @@ -10,6 +10,14 @@ const EVENT_TRIGGER = { resource: 'some-bucket', }; +const ENDPOINT_EVENT_TRIGGER = { + eventType: 'event-type', + eventFilters: { + bucket: 'some-bucket', + }, + retry: false, +}; + describe('v2/storage', () => { describe('getOptsAndBucket', () => { it('should return the default bucket with empty opts', () => { @@ -68,7 +76,7 @@ describe('v2/storage', () => { configStub.restore(); }); - it('should create a minimal trigger with bucket', () => { + it('should create a minimal trigger/endpoint with bucket', () => { const result = storage.onOperation('event-type', 'some-bucket', () => 42); expect(result.__trigger).to.deep.equal({ @@ -76,9 +84,15 @@ describe('v2/storage', () => { labels: {}, eventTrigger: EVENT_TRIGGER, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: ENDPOINT_EVENT_TRIGGER, + }); }); - it('should create a minimal trigger with opts', () => { + it('should create a minimal trigger/endpoint with opts', () => { configStub.returns({ storageBucket: 'default-bucket' }); const result = storage.onOperation( @@ -96,6 +110,18 @@ describe('v2/storage', () => { }, regions: ['us-west1'], }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_EVENT_TRIGGER, + eventFilters: { + bucket: 'default-bucket', + }, + }, + region: ['us-west1'], + }); }); it('should create a minimal trigger with bucket with opts and bucket', () => { @@ -110,9 +136,15 @@ describe('v2/storage', () => { labels: {}, eventTrigger: EVENT_TRIGGER, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: ENDPOINT_EVENT_TRIGGER, + }); }); - it('should create a complex trigger with appropriate values', () => { + it('should create a complex trigger/endpoint with appropriate values', () => { const result = storage.onOperation( 'event-type', { @@ -139,6 +171,26 @@ describe('v2/storage', () => { }, eventTrigger: EVENT_TRIGGER, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + region: ['us-west1'], + availableMemoryMb: 512, + timeoutSeconds: 60, + minInstances: 1, + maxInstances: 3, + concurrency: 20, + vpc: { + connector: 'aConnector', + egressSettings: 'ALL_TRAFFIC', + }, + serviceAccountEmail: 'root@', + ingressSettings: 'ALLOW_ALL', + labels: { + hello: 'world', + }, + eventTrigger: ENDPOINT_EVENT_TRIGGER, + }); }); it('should merge options and globalOptions', () => { @@ -166,6 +218,15 @@ describe('v2/storage', () => { labels: {}, eventTrigger: EVENT_TRIGGER, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + concurrency: 20, + minInstances: 3, + region: ['us-west1'], + labels: {}, + eventTrigger: ENDPOINT_EVENT_TRIGGER, + }); }); }); @@ -174,6 +235,10 @@ describe('v2/storage', () => { ...EVENT_TRIGGER, eventType: storage.archivedEvent, }; + const ENDPOINT_ARCHIVED_TRIGGER = { + ...ENDPOINT_EVENT_TRIGGER, + eventType: storage.archivedEvent, + }; let configStub: sinon.SinonStub; beforeEach(() => { @@ -197,6 +262,17 @@ describe('v2/storage', () => { resource: 'default-bucket', }, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_ARCHIVED_TRIGGER, + eventFilters: { + bucket: 'default-bucket', + }, + }, + }); }); it('should accept bucket and handler', () => { @@ -210,6 +286,17 @@ describe('v2/storage', () => { resource: 'my-bucket', }, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_ARCHIVED_TRIGGER, + eventFilters: { + bucket: 'my-bucket', + }, + }, + }); }); it('should accept opts and handler', () => { @@ -227,6 +314,18 @@ describe('v2/storage', () => { }, regions: ['us-west1'], }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_ARCHIVED_TRIGGER, + eventFilters: { + bucket: 'my-bucket', + }, + }, + region: ['us-west1'], + }); }); it('should accept opts and handler, default bucket', () => { @@ -243,6 +342,18 @@ describe('v2/storage', () => { }, regions: ['us-west1'], }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_ARCHIVED_TRIGGER, + eventFilters: { + bucket: 'default-bucket', + }, + }, + region: ['us-west1'], + }); }); }); @@ -251,6 +362,10 @@ describe('v2/storage', () => { ...EVENT_TRIGGER, eventType: storage.finalizedEvent, }; + const ENDPOINT_FINALIZED_TRIGGER = { + ...ENDPOINT_EVENT_TRIGGER, + eventType: storage.finalizedEvent, + }; let configStub: sinon.SinonStub; beforeEach(() => { @@ -274,6 +389,17 @@ describe('v2/storage', () => { resource: 'default-bucket', }, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_FINALIZED_TRIGGER, + eventFilters: { + bucket: 'default-bucket', + }, + }, + }); }); it('should accept bucket and handler', () => { @@ -287,6 +413,17 @@ describe('v2/storage', () => { resource: 'my-bucket', }, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_FINALIZED_TRIGGER, + eventFilters: { + bucket: 'my-bucket', + }, + }, + }); }); it('should accept opts and handler', () => { @@ -304,6 +441,18 @@ describe('v2/storage', () => { }, regions: ['us-west1'], }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_FINALIZED_TRIGGER, + eventFilters: { + bucket: 'my-bucket', + }, + }, + region: ['us-west1'], + }); }); it('should accept opts and handler, default bucket', () => { @@ -323,6 +472,18 @@ describe('v2/storage', () => { }, regions: ['us-west1'], }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_FINALIZED_TRIGGER, + eventFilters: { + bucket: 'default-bucket', + }, + }, + region: ['us-west1'], + }); }); }); @@ -331,6 +492,10 @@ describe('v2/storage', () => { ...EVENT_TRIGGER, eventType: storage.deletedEvent, }; + const ENDPOINT_DELETED_TRIGGER = { + ...ENDPOINT_EVENT_TRIGGER, + eventType: storage.deletedEvent, + }; let configStub: sinon.SinonStub; beforeEach(() => { @@ -355,6 +520,17 @@ describe('v2/storage', () => { }, }); + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_DELETED_TRIGGER, + eventFilters: { + bucket: 'default-bucket', + }, + }, + }); + configStub.restore(); }); @@ -369,6 +545,17 @@ describe('v2/storage', () => { resource: 'my-bucket', }, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_DELETED_TRIGGER, + eventFilters: { + bucket: 'my-bucket', + }, + }, + }); }); it('should accept opts and handler', () => { @@ -386,6 +573,18 @@ describe('v2/storage', () => { }, regions: ['us-west1'], }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_DELETED_TRIGGER, + eventFilters: { + bucket: 'my-bucket', + }, + }, + region: ['us-west1'], + }); }); it('should accept opts and handler, default bucket', () => { @@ -402,6 +601,18 @@ describe('v2/storage', () => { }, regions: ['us-west1'], }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_DELETED_TRIGGER, + eventFilters: { + bucket: 'default-bucket', + }, + }, + region: ['us-west1'], + }); }); }); @@ -410,6 +621,10 @@ describe('v2/storage', () => { ...EVENT_TRIGGER, eventType: storage.metadataUpdatedEvent, }; + const ENDPOINT_METADATA_TRIGGER = { + ...ENDPOINT_EVENT_TRIGGER, + eventType: storage.metadataUpdatedEvent, + }; let configStub: sinon.SinonStub; beforeEach(() => { @@ -434,6 +649,17 @@ describe('v2/storage', () => { }, }); + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_METADATA_TRIGGER, + eventFilters: { + bucket: 'default-bucket', + }, + }, + }); + configStub.restore(); }); @@ -448,6 +674,17 @@ describe('v2/storage', () => { resource: 'my-bucket', }, }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_METADATA_TRIGGER, + eventFilters: { + bucket: 'my-bucket', + }, + }, + }); }); it('should accept opts and handler', () => { @@ -465,6 +702,18 @@ describe('v2/storage', () => { }, regions: ['us-west1'], }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_METADATA_TRIGGER, + eventFilters: { + bucket: 'my-bucket', + }, + }, + region: ['us-west1'], + }); }); it('should accept opts and handler, default bucket', () => { @@ -484,6 +733,18 @@ describe('v2/storage', () => { }, regions: ['us-west1'], }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_METADATA_TRIGGER, + eventFilters: { + bucket: 'default-bucket', + }, + }, + region: ['us-west1'], + }); }); }); }); diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 688c4eb78..d2de0504c 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -37,6 +37,7 @@ import { durationFromSeconds, serviceAccountFromShorthand, } from './common/encoding'; +import { ManifestEndpoint, ManifestRequiredAPI } from './common/manifest'; /** @hidden */ const WILDCARD_REGEX = new RegExp('{[^/{}]*}', 'g'); @@ -283,6 +284,15 @@ export interface TriggerAnnotated { }; } +/** + * @hidden + * EndpointAnnotated is used to generate the manifest that conforms to the container contract. + */ +export interface EndpointAnnotated { + __endpoint: ManifestEndpoint; + __requiredAPIs?: ManifestRequiredAPI[]; +} + /** * A Runnable has a `run` method which directly invokes the user-defined * function - useful for unit testing. @@ -301,6 +311,7 @@ export interface Runnable { * arguments. */ export type HttpsFunction = TriggerAnnotated & + EndpointAnnotated & ((req: Request, resp: Response) => void | Promise); /** @@ -312,6 +323,7 @@ export type HttpsFunction = TriggerAnnotated & */ export type CloudFunction = Runnable & TriggerAnnotated & + EndpointAnnotated & ((input: any, context?: any) => PromiseLike | any); /** @hidden */ @@ -322,9 +334,9 @@ export interface MakeCloudFunctionArgs { dataConstructor?: (raw: Event) => EventData; eventType: string; handler?: (data: EventData, context: EventContext) => PromiseLike | any; - labels?: { [key: string]: any }; + labels?: Record; legacyEventType?: string; - options?: { [key: string]: any }; + options?: DeploymentOptions; /* * TODO: should remove `provider` and require a fully qualified `eventType` * once all providers have migrated to new format. @@ -432,6 +444,47 @@ export function makeCloudFunction({ }, }); + Object.defineProperty(cloudFunction, '__endpoint', { + get: () => { + if (triggerResource() == null) { + return undefined; + } + + const endpoint: ManifestEndpoint = { + platform: 'gcfv1', + ...optionsToEndpoint(options), + }; + + if (options.schedule) { + endpoint.scheduleTrigger = options.schedule; + } else { + endpoint.eventTrigger = { + eventType: legacyEventType || provider + '.' + eventType, + eventFilters: { + resource: triggerResource(), + }, + retry: !!options.failurePolicy, + }; + } + + // Note: We intentionally don't make use of labels args here. + // labels is used to pass SDK-defined labels to the trigger, which isn't + // something we will do in the container contract world. + endpoint.labels = { ...endpoint.labels }; + + return endpoint; + }, + }); + + if (options.schedule) { + cloudFunction.__requiredAPIs = [ + { + api: 'cloudscheduler.googleapis.com', + reason: 'Needed for scheduled functions.', + }, + ]; + } + cloudFunction.run = handler || contextOnlyHandler; return cloudFunction; } @@ -545,3 +598,48 @@ export function optionsToTrigger(options: DeploymentOptions) { return trigger; } + +export function optionsToEndpoint( + options: DeploymentOptions +): ManifestEndpoint { + const endpoint: ManifestEndpoint = {}; + copyIfPresent( + endpoint, + options, + 'minInstances', + 'maxInstances', + 'ingressSettings', + 'labels', + 'timeoutSeconds' + ); + convertIfPresent(endpoint, options, 'region', 'regions'); + convertIfPresent( + endpoint, + options, + 'serviceAccountEmail', + 'serviceAccount', + (sa) => sa + ); + if (options?.vpcConnector) { + endpoint.vpc = { connector: options.vpcConnector }; + convertIfPresent( + endpoint.vpc, + options, + 'egressSettings', + 'vpcConnectorEgressSettings' + ); + } + convertIfPresent(endpoint, options, 'availableMemoryMb', 'memory', (mem) => { + const memoryLookup = { + '128MB': 128, + '256MB': 256, + '512MB': 512, + '1GB': 1024, + '2GB': 2048, + '4GB': 4096, + '8GB': 8192, + }; + return memoryLookup[mem]; + }); + return endpoint; +} diff --git a/src/common/manifest.ts b/src/common/manifest.ts new file mode 100644 index 000000000..379e61a4f --- /dev/null +++ b/src/common/manifest.ts @@ -0,0 +1,85 @@ +// The MIT License (MIT) +// +// Copyright (c) 2021 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. +/** + * @internal + * An definition of a function as appears in the Manifest. + */ +export interface ManifestEndpoint { + entryPoint?: string; + region?: string[]; + platform?: string; + availableMemoryMb?: number; + maxInstances?: number; + minInstances?: number; + concurrency?: number; + serviceAccountEmail?: string; + timeoutSeconds?: number; + vpc?: { + connector: string; + egressSettings?: string; + }; + labels?: Record; + ingressSettings?: string; + environmentVariables?: Record; + + httpsTrigger?: { + invoker?: string[]; + }; + + callableTrigger?: {}; + + eventTrigger?: { + eventFilters: Record; + eventType: string; + retry: boolean; + region?: string; + serviceAccountEmail?: string; + }; + + scheduleTrigger?: { + schedule?: string; + timezone?: string; + retryConfig?: { + retryCount?: number; + maxRetryDuration?: string; + minBackoffDuration?: string; + maxBackoffDuration?: string; + maxDoublings?: number; + }; + }; +} + +/* @internal */ +export interface ManifestRequiredAPI { + api: string; + reason: string; +} + +/** + * @internal + * An definition of a function deployment as appears in the Manifest. + **/ +export interface ManifestStack { + specVersion: 'v1alpha1'; + requiredAPIs: ManifestRequiredAPI[]; + endpoints: Record; +} diff --git a/src/handler-builder.ts b/src/handler-builder.ts index 7ebf78f15..c89f3c936 100644 --- a/src/handler-builder.ts +++ b/src/handler-builder.ts @@ -48,16 +48,16 @@ export class HandlerBuilder { /** * Create a handler for HTTPS events. - + * `onRequest` handles an HTTPS request and has the same signature as an Express app. * * @example * ```javascript * exports.myFunction = functions.handler.https.onRequest((req, res) => { ... }) * ``` - * + * * `onCall` declares a callable function for clients to call using a Firebase SDK. - * + * * @example * ```javascript * exports.myFunction = functions.handler.https.onCall((data, context) => { ... }) @@ -70,6 +70,8 @@ export class HandlerBuilder { ): HttpsFunction => { const func = https._onRequestWithOptions(handler, {}); func.__trigger = {}; + func.__endpoint = undefined; + func.__requiredAPIs = undefined; return func; }, onCall: ( @@ -80,6 +82,8 @@ export class HandlerBuilder { ): HttpsFunction => { const func = https._onCallWithOptions(handler, {}); func.__trigger = {}; + func.__endpoint = undefined; + func.__requiredAPIs = undefined; return func; }, /** @hidden */ @@ -94,6 +98,8 @@ export class HandlerBuilder { const builder = new https.TaskQueueBuilder(); const func = builder.onDispatch(handler); func.__trigger = {}; + func.__endpoint = undefined; + func.__requiredAPIs = undefined; return func; }, }; @@ -103,21 +109,21 @@ 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 @@ -150,21 +156,21 @@ export class HandlerBuilder { /** * 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 @@ -172,7 +178,7 @@ export class HandlerBuilder { * exports.myFunction = functions.handler.firestore.document.onDelete((snap, context) => * { ... }) * ``` - + * `document.onWrite` handles the creation, update, or deletion of documents. * * @example @@ -201,7 +207,7 @@ export class HandlerBuilder { * 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() => { ... }) @@ -222,9 +228,9 @@ 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) => { ... }) @@ -240,21 +246,21 @@ export class HandlerBuilder { /** * 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 @@ -262,7 +268,7 @@ export class HandlerBuilder { * exports.myFunction = functions.handler.storage.object.onFinalize((object) => * { ... }) * ``` - + * `object.onMetadataUpdate` handles changes to the metadata of existing Storage objects. * * @example @@ -285,16 +291,16 @@ export class HandlerBuilder { /** * Create a handler for Cloud Pub/Sub events. - * - * `topic.onPublish` handles messages published to a Pub/Sub topic from SDKs, Cloud Console, or gcloud CLI. - * + * + * `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) => { ... }) @@ -313,21 +319,21 @@ export class HandlerBuilder { /** * 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 { @@ -341,7 +347,7 @@ export class HandlerBuilder { * 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) => { ... }) diff --git a/src/providers/https.ts b/src/providers/https.ts index ecefd6c29..8ab072e78 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -22,12 +22,18 @@ import * as express from 'express'; -import { HttpsFunction, optionsToTrigger, Runnable } from '../cloud-functions'; +import { + HttpsFunction, + optionsToEndpoint, + optionsToTrigger, + Runnable, +} from '../cloud-functions'; import { convertIfPresent, convertInvoker, copyIfPresent, } from '../common/encoding'; +import { ManifestEndpoint, ManifestRequiredAPI } from '../common/manifest'; import { CallableContext, FunctionsErrorCode, @@ -96,6 +102,8 @@ export interface TaskQueueOptions { export interface TaskQueueFunction { (req: Request, res: express.Response): Promise; __trigger: unknown; + __endpoint: ManifestEndpoint; + __requiredAPIs?: ManifestRequiredAPI[]; run(data: any, context: TaskContext): void | Promise; } @@ -131,6 +139,28 @@ export class TaskQueueBuilder { convertInvoker ); + func.__endpoint = { + platform: 'gcfv1', + ...optionsToEndpoint(this.depOpts), + taskQueueTrigger: {}, + }; + copyIfPresent(func.__endpoint.taskQueueTrigger, this.tqOpts, 'retryConfig'); + copyIfPresent(func.__endpoint.taskQueueTrigger, this.tqOpts, 'rateLimits'); + convertIfPresent( + func.__endpoint.taskQueueTrigger, + this.tqOpts, + 'invoker', + 'invoker', + convertInvoker + ); + + func.__requiredAPIs = [ + { + api: 'cloudtasks.googleapis.com', + reason: 'Needed for v1 task queue functions', + }, + ]; + func.run = handler; return func; @@ -167,6 +197,19 @@ export function _onRequestWithOptions( convertInvoker ); // TODO parse the options + + cloudFunction.__endpoint = { + platform: 'gcfv1', + ...optionsToEndpoint(options), + httpsTrigger: {}, + }; + convertIfPresent( + cloudFunction.__endpoint.httpsTrigger, + options, + 'invoker', + 'invoker', + convertInvoker + ); return cloudFunction; } @@ -195,6 +238,13 @@ export function _onCallWithOptions( }; func.__trigger.labels['deployment-callable'] = 'true'; + func.__endpoint = { + platform: 'gcfv1', + labels: {}, + ...optionsToEndpoint(options), + callableTrigger: {}, + }; + func.run = handler; return func; diff --git a/src/v2/core.ts b/src/v2/core.ts index d471c107b..c790be7a8 100644 --- a/src/v2/core.ts +++ b/src/v2/core.ts @@ -20,6 +20,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +import { ManifestEndpoint } from '../common/manifest'; + /** @internal */ export interface TriggerAnnotation { concurrency?: number; @@ -91,7 +93,8 @@ export interface CloudEvent { export interface CloudFunction { (raw: CloudEvent): any | Promise; - __trigger: unknown; + __trigger?: unknown; + __endpoint: ManifestEndpoint; run(event: CloudEvent): any | Promise; } diff --git a/src/v2/options.ts b/src/v2/options.ts index 0b2238a9f..7db01167e 100644 --- a/src/v2/options.ts +++ b/src/v2/options.ts @@ -21,14 +21,16 @@ // SOFTWARE. import { + convertIfPresent, + copyIfPresent, durationFromSeconds, serviceAccountFromShorthand, } from '../common/encoding'; -import { convertIfPresent, copyIfPresent } from '../common/encoding'; import * as logger from '../logger'; import { TriggerAnnotation } from './core'; import { declaredParams } from './params'; import { ParamSpec } from './params/types'; +import { ManifestEndpoint } from '../common/manifest'; /** * List of all regions supported by Cloud Functions v2 @@ -284,6 +286,52 @@ export function optionsToTriggerAnnotations( return annotation; } +/** + * Apply GlobalOptions to endpoint manifest. + * @internal + */ +export function optionsToEndpoint( + opts: GlobalOptions | EventHandlerOptions +): ManifestEndpoint { + const endpoint: ManifestEndpoint = {}; + copyIfPresent( + endpoint, + opts, + 'concurrency', + 'minInstances', + 'maxInstances', + 'ingressSettings', + 'labels', + 'timeoutSeconds' + ); + convertIfPresent(endpoint, opts, 'serviceAccountEmail', 'serviceAccount'); + if (opts.vpcConnector) { + const vpc: ManifestEndpoint['vpc'] = { connector: opts.vpcConnector }; + convertIfPresent(vpc, opts, 'egressSettings', 'vpcConnectorEgressSettings'); + endpoint.vpc = vpc; + } + convertIfPresent(endpoint, opts, 'availableMemoryMb', 'memory', (mem) => { + const memoryLookup = { + '128MB': 128, + '256MB': 256, + '512MB': 512, + '1GB': 1024, + '2GB': 2048, + '4GB': 4096, + '8GB': 8192, + }; + return memoryLookup[mem]; + }); + convertIfPresent(endpoint, opts, 'region', 'region', (region) => { + if (typeof region === 'string') { + return [region]; + } + return region; + }); + + return endpoint; +} + /** * @hidden */ diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 43556a1ba..292c7cf0e 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -28,6 +28,7 @@ import { copyIfPresent, } from '../../common/encoding'; +import * as options from '../options'; import { CallableRequest, FunctionsErrorCode, @@ -39,7 +40,7 @@ import { TaskRequest, TaskRetryConfig, } from '../../common/providers/https'; -import * as options from '../options'; +import { ManifestEndpoint } from '../../common/manifest'; export { Request, @@ -74,7 +75,10 @@ export interface TaskQueueOptions extends options.GlobalOptions { export type HttpsFunction = (( req: Request, res: express.Response -) => void | Promise) & { __trigger: unknown }; +) => void | Promise) & { + __trigger?: unknown; + __endpoint: ManifestEndpoint; +}; export interface CallableFunction extends HttpsFunction { run(data: CallableRequest): Return; } @@ -126,6 +130,7 @@ export function onRequest( }); }; } + Object.defineProperty(handler, '__trigger', { get: () => { const baseOpts = options.optionsToTriggerAnnotations( @@ -161,6 +166,30 @@ export function onRequest( return trigger; }, }); + + const baseOpts = options.optionsToEndpoint(options.getGlobalOptions()); + // global options calls region a scalar and https allows it to be an array, + // but optionsToTriggerAnnotations handles both cases. + const specificOpts = options.optionsToEndpoint(opts as options.GlobalOptions); + const endpoint: Partial = { + platform: 'gcfv2', + ...baseOpts, + ...specificOpts, + labels: { + ...baseOpts?.labels, + ...specificOpts?.labels, + }, + httpsTrigger: {}, + }; + convertIfPresent( + endpoint.httpsTrigger, + opts, + 'invoker', + 'invoker', + convertInvoker + ); + (handler as HttpsFunction).__endpoint = endpoint; + return handler as HttpsFunction; } @@ -222,6 +251,21 @@ export function onCall>( }, }); + const baseOpts = options.optionsToEndpoint(options.getGlobalOptions()); + // global options calls region a scalar and https allows it to be an array, + // but optionsToManifestEndpoint handles both cases. + const specificOpts = options.optionsToEndpoint(opts as options.GlobalOptions); + func.__endpoint = { + platform: 'gcfv2', + ...baseOpts, + ...specificOpts, + labels: { + ...baseOpts?.labels, + ...specificOpts?.labels, + }, + callableTrigger: {}, + }; + func.run = handler; return func; } diff --git a/src/v2/providers/pubsub.ts b/src/v2/providers/pubsub.ts index cca25b294..a751f0cd2 100644 --- a/src/v2/providers/pubsub.ts +++ b/src/v2/providers/pubsub.ts @@ -1,5 +1,7 @@ -import { CloudEvent, CloudFunction } from '../core'; import * as options from '../options'; +import { CloudEvent, CloudFunction } from '../core'; +import { copyIfPresent } from '../../common/encoding'; +import { ManifestEndpoint } from '../../common/manifest'; /** * Interface representing a Google Cloud Pub/Sub message. @@ -133,11 +135,6 @@ export function onMessagePublished( func.run = handler; - // TypeScript doesn't recongize defineProperty as adding a property and complains - // that __trigger doesn't exist. We can either cast to any and lose all type safety - // or we can just assign a meaningless value before calling defineProperty. - func.__trigger = 'silence the transpiler'; - Object.defineProperty(func, '__trigger', { get: () => { const baseOpts = options.optionsToTriggerAnnotations( @@ -164,5 +161,25 @@ export function onMessagePublished( }, }); + const baseOpts = options.optionsToEndpoint(options.getGlobalOptions()); + const specificOpts = options.optionsToEndpoint(opts); + + const endpoint: ManifestEndpoint = { + platform: 'gcfv2', + ...baseOpts, + ...specificOpts, + labels: { + ...baseOpts?.labels, + ...specificOpts?.labels, + }, + eventTrigger: { + eventType: 'google.cloud.pubsub.topic.v1.messagePublished', + eventFilters: { topic }, + retry: false, + }, + }; + copyIfPresent(endpoint.eventTrigger, opts, 'retry', 'retry'); + func.__endpoint = endpoint; + return func; } diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts index e58405820..e03115df0 100644 --- a/src/v2/providers/storage.ts +++ b/src/v2/providers/storage.ts @@ -20,9 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +import * as options from '../options'; import { firebaseConfig } from '../../config'; import { CloudEvent, CloudFunction } from '../core'; -import * as options from '../options'; +import { copyIfPresent } from '../../common/encoding'; +import { ManifestEndpoint } from '../../common/manifest'; /** * An object within Google Cloud Storage. @@ -313,11 +315,6 @@ export function onOperation( func.run = handler; - // TypeScript doesn't recongize defineProperty as adding a property and complains - // that __trigger doesn't exist. We can either cast to any and lose all type safety - // or we can just assign a meaningless value before calling defineProperty. - func.__trigger = 'silence the transpiler'; - Object.defineProperty(func, '__trigger', { get: () => { const baseOpts = options.optionsToTriggerAnnotations( @@ -341,6 +338,39 @@ export function onOperation( }, }); + // TypeScript doesn't recognize defineProperty as adding a property and complains + // that __endpoint doesn't exist. We can either cast to any and lose all type safety + // or we can just assign a meaningless value before calling defineProperty. + func.__endpoint = {} as ManifestEndpoint; + + // SDK may attempt to read FIREBASE_CONFIG env var to fetch the default bucket name. + // To prevent runtime errors when FIREBASE_CONFIG env var is missing, we use getters. + Object.defineProperty(func, '__endpoint', { + get: () => { + const baseOpts = options.optionsToEndpoint(options.getGlobalOptions()); + const specificOpts = options.optionsToEndpoint(opts); + + const endpoint: ManifestEndpoint = { + platform: 'gcfv2', + ...baseOpts, + ...specificOpts, + labels: { + ...baseOpts?.labels, + ...specificOpts?.labels, + }, + eventTrigger: { + eventType: eventType, + eventFilters: { + bucket, + }, + retry: false, + }, + }; + copyIfPresent(endpoint.eventTrigger, opts, 'retry', 'retry'); + return endpoint; + }, + }); + return func; } From 7645a69ce120af0b6827a1ed89cdac606385ad33 Mon Sep 17 00:00:00 2001 From: Andrew Hopkins Date: Fri, 21 Jan 2022 01:36:01 +1000 Subject: [PATCH 023/370] Check for missing options / databaseURL on emulator start (#1016) When using firebase emulators Realtime Database throws an error due to app.options not existing. I've found adding an extra check to the database constructor stops this error, and results in the emulator starting up correctly. --- CHANGELOG.md | 1 + src/providers/database.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d6230214..2c8c9b14f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,3 @@ - Parallelizes network calls that occur when validating authorization for onCall handlers. - Adds new regions to V2 API +- Fixes bug where the emulator crashed when given app without an `options` property. diff --git a/src/providers/database.ts b/src/providers/database.ts index 76252d515..53eb9ba12 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -369,7 +369,7 @@ export class DataSnapshot { private app?: firebase.app.App, instance?: string ) { - if (app && app.options.databaseURL.startsWith('http:')) { + if (app?.options?.databaseURL?.startsWith('http:')) { // In this case we're dealing with an emulator this.instance = app.options.databaseURL; } else if (instance) { From 8e21f77a3d5022963f0d83adf6a97fb55fa3c95d Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Mon, 31 Jan 2022 16:28:11 -0500 Subject: [PATCH 024/370] Generic interface for provider enhancement (#1020) (#1024) * breaking out general interface * cleaning up exports * removing comments & references to specific interfaces * format * jsdoc comments * address pr comments * added param comment * addressing comments * Specific interface for provider enhancement (1/3) (#1021) * adding in app distro changes * removing comments & addings package refs * jsdoc comments * fix comments * adding periods to doc strings * fix wording * adding import/export statement * Specific interface for provider enhancement (2/3) (#1022) * adding in billing changes * removing comments & adding package refs * jsdoc comments * addressing pr comments * change handler doc string * changing to BillingEventHandler type * remove BillingEventHandler type * Specific interface for provider enhancement (3/3) (#1023) * adding in crashlytics changes * comments & adding package refs * address comments and make doc strings better * add opts.retry to event trigger --- CHANGELOG.md | 1 + package.json | 18 +- spec/v2/providers/alerts/alerts.spec.ts | 183 ++++++ .../providers/alerts/appDistribution.spec.ts | 127 ++++ spec/v2/providers/alerts/billing.spec.ts | 126 ++++ spec/v2/providers/alerts/crashlytics.spec.ts | 562 ++++++++++++++++++ src/v2/core.ts | 8 +- src/v2/index.ts | 3 +- src/v2/providers/alerts/alerts.ts | 127 ++++ src/v2/providers/alerts/appDistribution.ts | 107 ++++ src/v2/providers/alerts/billing.ts | 110 ++++ src/v2/providers/alerts/crashlytics.ts | 360 +++++++++++ src/v2/providers/alerts/index.ts | 6 + src/v2/providers/storage.ts | 2 +- v2/alerts/appDistribution.js | 26 + v2/alerts/billing.js | 26 + v2/alerts/crashlytics.js | 26 + v2/alerts/index.js | 26 + 18 files changed, 1839 insertions(+), 5 deletions(-) create mode 100644 spec/v2/providers/alerts/alerts.spec.ts create mode 100644 spec/v2/providers/alerts/appDistribution.spec.ts create mode 100644 spec/v2/providers/alerts/billing.spec.ts create mode 100644 spec/v2/providers/alerts/crashlytics.spec.ts create mode 100644 src/v2/providers/alerts/alerts.ts create mode 100644 src/v2/providers/alerts/appDistribution.ts create mode 100644 src/v2/providers/alerts/billing.ts create mode 100644 src/v2/providers/alerts/crashlytics.ts create mode 100644 src/v2/providers/alerts/index.ts create mode 100644 v2/alerts/appDistribution.js create mode 100644 v2/alerts/billing.js create mode 100644 v2/alerts/crashlytics.js create mode 100644 v2/alerts/index.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c8c9b14f..a909dbdbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ - Parallelizes network calls that occur when validating authorization for onCall handlers. - Adds new regions to V2 API - Fixes bug where the emulator crashed when given app without an `options` property. +- Adds new alerting providers diff --git a/package.json b/package.json index 50637bb74..0be8d9ef9 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,11 @@ "./v2/https": "./lib/v2/providers/https.js", "./v2/params": "./lib/v2/params/index.js", "./v2/pubsub": "./lib/v2/providers/pubsub.js", - "./v2/storage": "./lib/v2/providers/storage.js" + "./v2/storage": "./lib/v2/providers/storage.js", + "./v2/alerts": "./lib/v2/providers/alerts/index.js", + "./v2/alerts/appDistribution": "./lib/v2/providers/alerts/appDistribution.js", + "./v2/alerts/billing": "./lib/v2/providers/alerts/billing.js", + "./v2/alerts/crashlytics": "./lib/v2/providers/alerts/crashlytics.js" }, "typesVersions": { "*": { @@ -114,6 +118,18 @@ ], "v2/storage": [ "lib/v2/providers/storage" + ], + "v2/alerts": [ + "lib/v2/providers/alerts" + ], + "v2/alerts/appDistribution": [ + "lib/v2/providers/alerts/appDistribution" + ], + "v2/alerts/billing": [ + "lib/v2/providers/alerts/billing" + ], + "v2/alerts/crashlytics": [ + "lib/v2/providers/alerts/crashlytics" ] } }, diff --git a/spec/v2/providers/alerts/alerts.spec.ts b/spec/v2/providers/alerts/alerts.spec.ts new file mode 100644 index 000000000..b63f567aa --- /dev/null +++ b/spec/v2/providers/alerts/alerts.spec.ts @@ -0,0 +1,183 @@ +import { expect } from 'chai'; +import * as options from '../../../../src/v2/options'; +import * as alerts from '../../../../src/v2/providers/alerts'; +import { FULL_ENDPOINT, FULL_OPTIONS } from '../helpers'; + +const ALERT_TYPE = 'new-alert-type'; +const APPID = '123456789'; + +describe('alerts', () => { + describe('onAlertPublished', () => { + it('should create the function without opts', () => { + const result = alerts.onAlertPublished(ALERT_TYPE, () => 42); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: ALERT_TYPE, + }, + retry: false, + }, + }); + }); + + it('should create the function with opts', () => { + const result = alerts.onAlertPublished( + { + ...FULL_OPTIONS, + alertType: ALERT_TYPE, + appId: APPID, + }, + () => 42 + ); + + expect(result.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: ALERT_TYPE, + appId: APPID, + }, + retry: false, + }, + }); + }); + + it('should have a .run method', () => { + const func = alerts.onAlertPublished(ALERT_TYPE, (event) => event); + + const res = func.run('input' as any); + + expect(res).to.equal('input'); + }); + }); + + describe('getEndpointAnnotation', () => { + beforeEach(() => { + process.env.GCLOUD_PROJECT = 'aProject'; + }); + + afterEach(() => { + options.setGlobalOptions({}); + delete process.env.GCLOUD_PROJECT; + }); + + it('should define the endpoint without appId and opts', () => { + expect(alerts.getEndpointAnnotation({}, ALERT_TYPE)).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: ALERT_TYPE, + }, + retry: false, + }, + }); + }); + + it('should define a complex endpoint without appId', () => { + expect( + alerts.getEndpointAnnotation({ ...FULL_OPTIONS }, ALERT_TYPE) + ).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: ALERT_TYPE, + }, + retry: false, + }, + }); + }); + + it('should define a complex endpoint', () => { + expect( + alerts.getEndpointAnnotation({ ...FULL_OPTIONS }, ALERT_TYPE, APPID) + ).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: ALERT_TYPE, + appId: APPID, + }, + retry: false, + }, + }); + }); + + it('should merge global & specific opts', () => { + options.setGlobalOptions({ + concurrency: 20, + region: 'europe-west1', + minInstances: 1, + }); + const specificOpts = { + region: 'us-west1', + minInstances: 3, + }; + + expect( + alerts.getEndpointAnnotation(specificOpts, ALERT_TYPE, APPID) + ).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + concurrency: 20, + region: ['us-west1'], + minInstances: 3, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: ALERT_TYPE, + appId: APPID, + }, + retry: false, + }, + }); + }); + }); + + describe('getOptsAndAlertTypeAndApp', () => { + it('should parse a string', () => { + const [opts, alertType, appId] = alerts.getOptsAndAlertTypeAndApp( + ALERT_TYPE + ); + + expect(opts).to.deep.equal({}); + expect(alertType).to.equal(ALERT_TYPE); + expect(appId).to.be.undefined; + }); + + it('should parse an options object without appId', () => { + const myOpts: alerts.FirebaseAlertOptions = { + alertType: ALERT_TYPE, + region: 'us-west1', + }; + + const [opts, alertType, appId] = alerts.getOptsAndAlertTypeAndApp(myOpts); + + expect(opts).to.deep.equal({ region: 'us-west1' }); + expect(alertType).to.equal(myOpts.alertType); + expect(appId).to.be.undefined; + }); + + it('should parse an options object with appId', () => { + const myOpts: alerts.FirebaseAlertOptions = { + alertType: ALERT_TYPE, + appId: APPID, + region: 'us-west1', + }; + + const [opts, alertType, appId] = alerts.getOptsAndAlertTypeAndApp(myOpts); + + expect(opts).to.deep.equal({ region: 'us-west1' }); + expect(alertType).to.equal(myOpts.alertType); + expect(appId).to.be.equal(myOpts.appId); + }); + }); +}); diff --git a/spec/v2/providers/alerts/appDistribution.spec.ts b/spec/v2/providers/alerts/appDistribution.spec.ts new file mode 100644 index 000000000..52fdfd3b9 --- /dev/null +++ b/spec/v2/providers/alerts/appDistribution.spec.ts @@ -0,0 +1,127 @@ +import { expect } from 'chai'; +import * as alerts from '../../../../src/v2/providers/alerts'; +import * as appDistribution from '../../../../src/v2/providers/alerts/appDistribution'; +import { FULL_ENDPOINT, FULL_OPTIONS } from '../helpers'; + +const APPID = '123456789'; +const myHandler = () => 42; + +describe('appDistribution', () => { + describe('onNewTesterIosDevicePublished', () => { + it('should create a function with alertType & appId', () => { + const func = appDistribution.onNewTesterIosDevicePublished( + APPID, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: appDistribution.newTesterIosDeviceAlert, + appId: APPID, + }, + retry: false, + }, + }); + }); + + it('should create a function with opts', () => { + const func = appDistribution.onNewTesterIosDevicePublished( + { ...FULL_OPTIONS }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: appDistribution.newTesterIosDeviceAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with appid in opts', () => { + const func = appDistribution.onNewTesterIosDevicePublished( + { ...FULL_OPTIONS, appId: APPID }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: appDistribution.newTesterIosDeviceAlert, + appId: APPID, + }, + retry: false, + }, + }); + }); + + it('should create a function without opts or appId', () => { + const func = appDistribution.onNewTesterIosDevicePublished(myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: appDistribution.newTesterIosDeviceAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with a run method', () => { + const func = appDistribution.onNewTesterIosDevicePublished( + APPID, + (event) => event + ); + + const res = func.run('input' as any); + + expect(res).to.equal('input'); + }); + }); + + describe('getOptsAndApp', () => { + it('should parse a string', () => { + const [opts, appId] = appDistribution.getOptsAndApp(APPID); + + expect(opts).to.deep.equal({}); + expect(appId).to.equal(APPID); + }); + + it('should parse an options object without appId', () => { + const myOpts: appDistribution.AppDistributionOptions = { + region: 'us-west1', + }; + + const [opts, appId] = appDistribution.getOptsAndApp(myOpts); + + expect(opts).to.deep.equal({ region: 'us-west1' }); + expect(appId).to.be.undefined; + }); + + it('should parse an options object with appId', () => { + const myOpts: appDistribution.AppDistributionOptions = { + appId: APPID, + region: 'us-west1', + }; + + const [opts, appId] = appDistribution.getOptsAndApp(myOpts); + + expect(opts).to.deep.equal({ region: 'us-west1' }); + expect(appId).to.equal(APPID); + }); + }); +}); diff --git a/spec/v2/providers/alerts/billing.spec.ts b/spec/v2/providers/alerts/billing.spec.ts new file mode 100644 index 000000000..a9e4e173d --- /dev/null +++ b/spec/v2/providers/alerts/billing.spec.ts @@ -0,0 +1,126 @@ +import { expect } from 'chai'; +import * as alerts from '../../../../src/v2/providers/alerts'; +import * as billing from '../../../../src/v2/providers/alerts/billing'; +import { FULL_ENDPOINT, FULL_OPTIONS } from '../helpers'; + +const ALERT_TYPE = 'new-alert-type'; +const myHandler = () => 42; + +describe('billing', () => { + describe('onPlanUpdatePublished', () => { + it('should create a function with only handler', () => { + const func = billing.onPlanUpdatePublished(myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: billing.planUpdateAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with opts & handler', () => { + const func = billing.onPlanUpdatePublished( + { ...FULL_OPTIONS }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: billing.planUpdateAlert, + }, + retry: false, + }, + }); + }); + }); + + describe('onAutomatedPlanUpdatePublished', () => { + it('should create a function with only handler', () => { + const func = billing.onAutomatedPlanUpdatePublished(myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: billing.automatedPlanUpdateAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with opts & handler', () => { + const func = billing.onAutomatedPlanUpdatePublished( + { ...FULL_OPTIONS }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: billing.automatedPlanUpdateAlert, + }, + retry: false, + }, + }); + }); + }); + + describe('onOperation', () => { + it('should create a function with alertType only', () => { + const func = billing.onOperation(ALERT_TYPE, myHandler, undefined); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: ALERT_TYPE, + }, + retry: false, + }, + }); + }); + + it('should create a function with opts', () => { + const func = billing.onOperation( + ALERT_TYPE, + { ...FULL_OPTIONS }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: ALERT_TYPE, + }, + retry: false, + }, + }); + }); + + it('should create a function with a run method', () => { + const func = billing.onOperation(ALERT_TYPE, (event) => event, undefined); + + const res = func.run('input' as any); + + expect(res).to.equal('input'); + }); + }); +}); diff --git a/spec/v2/providers/alerts/crashlytics.spec.ts b/spec/v2/providers/alerts/crashlytics.spec.ts new file mode 100644 index 000000000..1d5c1a8b6 --- /dev/null +++ b/spec/v2/providers/alerts/crashlytics.spec.ts @@ -0,0 +1,562 @@ +import { expect } from 'chai'; +import * as alerts from '../../../../src/v2/providers/alerts'; +import * as crashlytics from '../../../../src/v2/providers/alerts/crashlytics'; +import { FULL_ENDPOINT, FULL_OPTIONS } from '../helpers'; + +const ALERT_TYPE = 'new-alert-type'; +const APPID = '123456789'; +const myHandler = () => 42; + +describe('crashlytics', () => { + describe('onNewFatalIssuePublished', () => { + it('should create a function only handler', () => { + const func = crashlytics.onNewFatalIssuePublished(myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.newFatalIssueAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with appId', () => { + const func = crashlytics.onNewFatalIssuePublished(APPID, myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.newFatalIssueAlert, + appId: APPID, + }, + retry: false, + }, + }); + }); + + it('should create a function with base opts', () => { + const func = crashlytics.onNewFatalIssuePublished( + { ...FULL_OPTIONS }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.newFatalIssueAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with opts', () => { + const func = crashlytics.onNewFatalIssuePublished( + { ...FULL_OPTIONS, appId: APPID }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.newFatalIssueAlert, + appId: APPID, + }, + retry: false, + }, + }); + }); + }); + + describe('onNewNonfatalIssuePublished', () => { + it('should create a function only handler', () => { + const func = crashlytics.onNewNonfatalIssuePublished(myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.newNonfatalIssueAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with appId', () => { + const func = crashlytics.onNewNonfatalIssuePublished(APPID, myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.newNonfatalIssueAlert, + appId: APPID, + }, + retry: false, + }, + }); + }); + + it('should create a function with base opts', () => { + const func = crashlytics.onNewNonfatalIssuePublished( + { ...FULL_OPTIONS }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.newNonfatalIssueAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with opts', () => { + const func = crashlytics.onNewNonfatalIssuePublished( + { ...FULL_OPTIONS, appId: APPID }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.newNonfatalIssueAlert, + appId: APPID, + }, + retry: false, + }, + }); + }); + }); + + describe('onRegressionAlertPublished', () => { + it('should create a function only handler', () => { + const func = crashlytics.onRegressionAlertPublished(myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.regressionAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with appId', () => { + const func = crashlytics.onRegressionAlertPublished(APPID, myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.regressionAlert, + appId: APPID, + }, + retry: false, + }, + }); + }); + + it('should create a function with base opts', () => { + const func = crashlytics.onRegressionAlertPublished( + { ...FULL_OPTIONS }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.regressionAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with opts', () => { + const func = crashlytics.onRegressionAlertPublished( + { ...FULL_OPTIONS, appId: APPID }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.regressionAlert, + appId: APPID, + }, + retry: false, + }, + }); + }); + }); + + describe('onStabilityDigestPublished', () => { + it('should create a function only handler', () => { + const func = crashlytics.onStabilityDigestPublished(myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.stabilityDigestAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with appId', () => { + const func = crashlytics.onStabilityDigestPublished(APPID, myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.stabilityDigestAlert, + appId: APPID, + }, + retry: false, + }, + }); + }); + + it('should create a function with base opts', () => { + const func = crashlytics.onStabilityDigestPublished( + { ...FULL_OPTIONS }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.stabilityDigestAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with opts', () => { + const func = crashlytics.onStabilityDigestPublished( + { ...FULL_OPTIONS, appId: APPID }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.stabilityDigestAlert, + appId: APPID, + }, + retry: false, + }, + }); + }); + }); + + describe('onVelocityAlertPublished', () => { + it('should create a function only handler', () => { + const func = crashlytics.onVelocityAlertPublished(myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.velocityAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with appId', () => { + const func = crashlytics.onVelocityAlertPublished(APPID, myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.velocityAlert, + appId: APPID, + }, + retry: false, + }, + }); + }); + + it('should create a function with base opts', () => { + const func = crashlytics.onVelocityAlertPublished( + { ...FULL_OPTIONS }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.velocityAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with opts', () => { + const func = crashlytics.onVelocityAlertPublished( + { ...FULL_OPTIONS, appId: APPID }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.velocityAlert, + appId: APPID, + }, + retry: false, + }, + }); + }); + }); + + describe('onNewAnrIssuePublished', () => { + it('should create a function only handler', () => { + const func = crashlytics.onNewAnrIssuePublished(myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.newAnrIssueAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with appId', () => { + const func = crashlytics.onNewAnrIssuePublished(APPID, myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.newAnrIssueAlert, + appId: APPID, + }, + retry: false, + }, + }); + }); + + it('should create a function with base opts', () => { + const func = crashlytics.onNewAnrIssuePublished( + { ...FULL_OPTIONS }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.newAnrIssueAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with opts', () => { + const func = crashlytics.onNewAnrIssuePublished( + { ...FULL_OPTIONS, appId: APPID }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: crashlytics.newAnrIssueAlert, + appId: APPID, + }, + retry: false, + }, + }); + }); + }); + + describe('onOperation', () => { + it('should create a function with alertType only', () => { + const func = crashlytics.onOperation(ALERT_TYPE, myHandler, undefined); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: ALERT_TYPE, + }, + retry: false, + }, + }); + }); + + it('should create a function with alertType & appId', () => { + const func = crashlytics.onOperation(ALERT_TYPE, APPID, myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: ALERT_TYPE, + appId: APPID, + }, + retry: false, + }, + }); + }); + + it('should create a function with base opts', () => { + const func = crashlytics.onOperation( + ALERT_TYPE, + { ...FULL_OPTIONS }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: ALERT_TYPE, + }, + retry: false, + }, + }); + }); + + it('should create a function with appid in opts', () => { + const func = crashlytics.onOperation( + ALERT_TYPE, + { ...FULL_OPTIONS, appId: APPID }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: ALERT_TYPE, + appId: APPID, + }, + retry: false, + }, + }); + }); + + it('should create a function with a run method', () => { + const func = crashlytics.onOperation( + ALERT_TYPE, + (event) => event, + undefined + ); + + const res = func.run('input' as any); + + expect(res).to.equal('input'); + }); + }); + + describe('getOptsAndApp', () => { + it('should parse a string', () => { + const APPID = '123456789'; + + const [opts, appId] = crashlytics.getOptsAndApp(APPID); + + expect(opts).to.deep.equal({}); + expect(appId).to.equal(APPID); + }); + + it('should parse an options object without appId', () => { + const myOpts: crashlytics.CrashlyticsOptions = { + region: 'us-west1', + }; + + const [opts, appId] = crashlytics.getOptsAndApp(myOpts); + + expect(opts).to.deep.equal({ region: 'us-west1' }); + expect(appId).to.be.undefined; + }); + + it('should parse an options object with appId', () => { + const myOpts: crashlytics.CrashlyticsOptions = { + appId: '123456789', + region: 'us-west1', + }; + + const [opts, appId] = crashlytics.getOptsAndApp(myOpts); + + expect(opts).to.deep.equal({ region: 'us-west1' }); + expect(appId).to.equal(myOpts.appId); + }); + }); +}); diff --git a/src/v2/core.ts b/src/v2/core.ts index c790be7a8..845cf2990 100644 --- a/src/v2/core.ts +++ b/src/v2/core.ts @@ -49,10 +49,10 @@ export interface TriggerAnnotation { } /** - * A CloudEvent is a cross-platform format for encoding a serverless event. + * A CloudEventBase is the base of a cross-platform format for encoding a serverless event. * More information can be found in https://github.com/cloudevents/spec */ -export interface CloudEvent { +interface CloudEventBase { /** Version of the CloudEvents spec for this event. */ readonly specversion: '1.0'; @@ -89,6 +89,10 @@ export interface CloudEvent { params?: Record; } +/** + * A CloudEvent with custom extension attributes + */ +export type CloudEvent = CloudEventBase & Ext; /** A handler for CloudEvents. */ export interface CloudFunction { (raw: CloudEvent): any | Promise; diff --git a/src/v2/index.ts b/src/v2/index.ts index 5c99a4678..3de9b749f 100644 --- a/src/v2/index.ts +++ b/src/v2/index.ts @@ -22,11 +22,12 @@ import * as logger from '../logger'; import * as params from './params'; +import * as alerts from './providers/alerts'; import * as https from './providers/https'; import * as pubsub from './providers/pubsub'; import * as storage from './providers/storage'; -export { https, pubsub, storage, logger, params }; +export { https, pubsub, storage, logger, params, alerts }; export { setGlobalOptions, GlobalOptions } from './options'; diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts new file mode 100644 index 000000000..4b7f0b952 --- /dev/null +++ b/src/v2/providers/alerts/alerts.ts @@ -0,0 +1,127 @@ +import { ManifestEndpoint } from '../../../common/manifest'; +import { CloudEvent, CloudFunction } from '../../core'; +import * as options from '../../options'; + +/** + * The CloudEvent data emitted by Firebase Alerts. + */ +export interface FirebaseAlertData { + createTime: string; + endTime: string; + payload: T; +} + +interface WithAlertTypeAndApp { + alertType: string; + appId?: string; +} +/** + * A custom CloudEvent for Firebase Alerts (with custom extension attributes). + */ +export type AlertEvent = CloudEvent< + FirebaseAlertData, + WithAlertTypeAndApp +>; + +/** @internal */ +export const eventType = 'firebase.firebasealerts.alerts.v1.published'; + +/** The underlying alert type of the Firebase Alerts provider. */ +export type AlertType = + | 'crashlytics.newFatalIssue' + | 'crashlytics.newNonfatalIssue' + | 'crashlytics.regression' + | 'crashlytics.stabilityDigest' + | 'crashlytics.velocity' + | 'crashlytics.newAnrIssue' + | 'billing.planUpdate' + | 'billing.automatedPlanUpdate' + | 'appDistribution.newTesterIosDevice' + | string; + +/** + * Configuration for Firebase Alert functions. + */ +export interface FirebaseAlertOptions extends options.EventHandlerOptions { + alertType: AlertType; + appId?: string; +} + +/** + * Declares a function that can handle Firebase Alerts from CloudEvents. + * @param alertTypeOrOpts the alert type or Firebase Alert function configuration. + * @param handler a function that can handle the Firebase Alert inside a CloudEvent. + */ +export function onAlertPublished( + alertTypeOrOpts: AlertType | FirebaseAlertOptions, + handler: (event: AlertEvent) => any | Promise +): CloudFunction> { + const [opts, alertType, appId] = getOptsAndAlertTypeAndApp(alertTypeOrOpts); + + const func = (raw: CloudEvent) => { + return handler( + raw as CloudEvent, WithAlertTypeAndApp> + ); + }; + + func.run = handler; + func.__endpoint = getEndpointAnnotation(opts, alertType, appId); + + return func; +} + +/** + * @internal + * Helper function for getting the endpoint annotation used in alert handling functions. + */ +export function getEndpointAnnotation( + opts: options.EventHandlerOptions, + alertType: string, + appId?: string +): ManifestEndpoint { + const baseOpts = options.optionsToEndpoint(options.getGlobalOptions()); + const specificOpts = options.optionsToEndpoint(opts); + const endpoint: ManifestEndpoint = { + platform: 'gcfv2', + ...baseOpts, + ...specificOpts, + labels: { + ...baseOpts?.labels, + ...specificOpts?.labels, + }, + eventTrigger: { + eventType, + eventFilters: { + alertType, + }, + retry: !!opts.retry, + }, + }; + if (appId) { + endpoint.eventTrigger.eventFilters.appId = appId; + } + return endpoint; +} + +/** + * @internal + * Helper function to parse the function opts, alert type, and appId. + */ +export function getOptsAndAlertTypeAndApp( + alertTypeOrOpts: AlertType | FirebaseAlertOptions +): [options.EventHandlerOptions, string, string | undefined] { + let opts: options.EventHandlerOptions; + let alertType: AlertType; + let appId: string | undefined; + if (typeof alertTypeOrOpts === 'string') { + alertType = alertTypeOrOpts; + opts = {}; + } else { + alertType = alertTypeOrOpts.alertType; + appId = alertTypeOrOpts.appId; + opts = { ...alertTypeOrOpts }; + delete (opts as any).alertType; + delete (opts as any).appId; + } + return [opts, alertType, appId]; +} diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts new file mode 100644 index 000000000..58b54e9b6 --- /dev/null +++ b/src/v2/providers/alerts/appDistribution.ts @@ -0,0 +1,107 @@ +import { getEndpointAnnotation, FirebaseAlertData } from './alerts'; +import { CloudEvent, CloudFunction } from '../../core'; +import * as options from '../../options'; + +/** + * The internal payload object for adding a new tester device to app distribution. + * Payload is wrapped inside a FirebaseAlertData object. + */ +export interface NewTesterDevicePayload { + ['@type']: 'com.google.firebase.firebasealerts.NewTesterDevicePayload'; + testerName: string; + testerEmail: string; + testerDeviceModelName: string; + testerDeviceIdentifier: string; +} + +interface WithAlertTypeAndApp { + alertType: string; + appId: string; +} +/** + * A custom CloudEvent for Firebase Alerts (with custom extension attributes). + */ +export type AppDistributionEvent = CloudEvent< + FirebaseAlertData, + WithAlertTypeAndApp +>; + +/** @internal */ +export const newTesterIosDeviceAlert = 'appDistribution.newTesterIosDevice'; + +/** + * Configuration for app distribution functions. + */ +export interface AppDistributionOptions extends options.EventHandlerOptions { + appId?: string; +} + +/** + * Declares a function that can handle adding a new tester iOS device. + */ +export function onNewTesterIosDevicePublished( + handler: ( + event: AppDistributionEvent + ) => any | Promise +): CloudFunction>; +export function onNewTesterIosDevicePublished( + appId: string, + handler: ( + event: AppDistributionEvent + ) => any | Promise +): CloudFunction>; +export function onNewTesterIosDevicePublished( + opts: AppDistributionOptions, + handler: ( + event: AppDistributionEvent + ) => any | Promise +): CloudFunction>; +export function onNewTesterIosDevicePublished( + appIdOrOptsOrHandler: + | string + | AppDistributionOptions + | (( + event: AppDistributionEvent + ) => any | Promise), + handler?: ( + event: AppDistributionEvent + ) => any | Promise +): CloudFunction> { + if (typeof appIdOrOptsOrHandler === 'function') { + handler = appIdOrOptsOrHandler as ( + event: AppDistributionEvent + ) => any | Promise; + appIdOrOptsOrHandler = {}; + } + + const [opts, appId] = getOptsAndApp(appIdOrOptsOrHandler); + + const func = (raw: CloudEvent) => { + return handler(raw as AppDistributionEvent); + }; + + func.run = handler; + func.__endpoint = getEndpointAnnotation(opts, newTesterIosDeviceAlert, appId); + + return func; +} + +/** + * @internal + * Helper function to parse the function opts and appId. + */ +export function getOptsAndApp( + appIdOrOpts: string | AppDistributionOptions +): [options.EventHandlerOptions, string | undefined] { + let opts: options.EventHandlerOptions; + let appId: string | undefined; + if (typeof appIdOrOpts === 'string') { + opts = {}; + appId = appIdOrOpts; + } else { + appId = appIdOrOpts.appId; + opts = { ...appIdOrOpts }; + delete (opts as any).appId; + } + return [opts, appId]; +} diff --git a/src/v2/providers/alerts/billing.ts b/src/v2/providers/alerts/billing.ts new file mode 100644 index 000000000..f2ecd4289 --- /dev/null +++ b/src/v2/providers/alerts/billing.ts @@ -0,0 +1,110 @@ +import { getEndpointAnnotation, FirebaseAlertData } from '.'; +import { CloudEvent, CloudFunction } from '../../core'; +import * as options from '../../options'; + +/** + * The internal payload object for billing plan updates. + * Payload is wrapped inside a FirebaseAlertData object. + */ +export interface PlanUpdatePayload { + ['@type']: 'com.google.firebase.firebasealerts.PlanUpdatePayload'; + billingPlan: string; + principalEmail: string; +} + +/** + * The internal payload object for billing plan automated updates. + * Payload is wrapped inside a FirebaseAlertData object. + */ +export interface PlanAutomatedUpdatePayload { + ['@type']: 'com.google.firebase.firebasealerts.PlanAutomatedUpdatePayload'; + billingPlan: string; +} + +interface WithAlertType { + alertType: string; +} +/** + * A custom CloudEvent for billing Firebase Alerts (with custom extension attributes). + */ +export type BillingEvent = CloudEvent, WithAlertType>; + +/** @internal */ +export const planUpdateAlert = 'billing.planUpdate'; +/** @internal */ +export const automatedPlanUpdateAlert = 'billing.automatedPlanUpdate'; + +/** + * Declares a function that can handle a billing plan update event. + */ +export function onPlanUpdatePublished( + handler: (event: BillingEvent) => any | Promise +): CloudFunction>; +export function onPlanUpdatePublished( + opts: options.EventHandlerOptions, + handler: (event: BillingEvent) => any | Promise +): CloudFunction>; +export function onPlanUpdatePublished( + optsOrHandler: + | options.EventHandlerOptions + | ((event: BillingEvent) => any | Promise), + handler?: (event: BillingEvent) => any | Promise +): CloudFunction> { + return onOperation( + planUpdateAlert, + optsOrHandler, + handler + ); +} + +/** + * Declares a function that can handle an automated billing plan update event. + */ +export function onAutomatedPlanUpdatePublished( + handler: ( + event: BillingEvent + ) => any | Promise +): CloudFunction>; +export function onAutomatedPlanUpdatePublished( + opts: options.EventHandlerOptions, + handler: ( + event: BillingEvent + ) => any | Promise +): CloudFunction>; +export function onAutomatedPlanUpdatePublished( + optsOrHandler: + | options.EventHandlerOptions + | ((event: BillingEvent) => any | Promise), + handler?: ( + event: BillingEvent + ) => any | Promise +): CloudFunction> { + return onOperation( + automatedPlanUpdateAlert, + optsOrHandler, + handler + ); +} + +/** @internal */ +export function onOperation( + alertType: string, + optsOrHandler: + | options.EventHandlerOptions + | ((event: BillingEvent) => any | Promise), + handler: (event: BillingEvent) => any | Promise +): CloudFunction> { + if (typeof optsOrHandler === 'function') { + handler = optsOrHandler as (event: BillingEvent) => any | Promise; + optsOrHandler = {}; + } + + const func = (raw: CloudEvent) => { + return handler(raw as BillingEvent); + }; + + func.run = handler; + func.__endpoint = getEndpointAnnotation(optsOrHandler, alertType); + + return func; +} diff --git a/src/v2/providers/alerts/crashlytics.ts b/src/v2/providers/alerts/crashlytics.ts new file mode 100644 index 000000000..d9cece913 --- /dev/null +++ b/src/v2/providers/alerts/crashlytics.ts @@ -0,0 +1,360 @@ +import { getEndpointAnnotation, FirebaseAlertData } from '.'; +import { CloudEvent, CloudFunction } from '../../core'; +import * as options from '../../options'; + +/** Generic crashlytics issue interface */ +interface Issue { + id: string; + title: string; + subtitle: string; + appVersion: string; +} + +/** + * The internal payload object for a new fatal issue. + * Payload is wrapped inside a FirebaseAlertData object. + */ +export interface NewFatalIssuePayload { + ['@type']: 'com.google.firebase.firebasealerts.CrashlyticsNewFatalIssuePayload'; + issue: Issue; +} + +/** + * The internal payload object for a new non-fatal issue. + * Payload is wrapped inside a FirebaseAlertData object. + */ +export interface NewNonfatalIssuePayload { + ['@type']: 'com.google.firebase.firebasealerts.CrashlyticsNewNonfatalIssuePayload'; + issue: Issue; +} + +/** + * The internal payload object for a regression alert. + * Payload is wrapped inside a FirebaseAlertData object. + */ +export interface RegressionAlertPayload { + ['@type']: 'com.google.firebase.firebasealerts.CrashlyticsRegressionAlertPayload'; + type: string; + issue: Issue; + resolveTime: string; +} + +/** Generic crashlytics trending issue interface */ +interface TrendingIssueDetails { + type: string; + issue: Issue; + eventCount: number; + userCount: number; +} + +/** + * The internal payload object for a stability digest. + * Payload is wrapped inside a FirebaseAlertData object. + */ +export interface StabilityDigestPayload { + ['@type']: 'com.google.firebase.firebasealerts.CrashlyticsStabilityDigestPayload'; + digestDate: string; + trendingIssues: TrendingIssueDetails[]; +} + +/** + * The internal payload object for a velocity alert. + * Payload is wrapped inside a FirebaseAlertData object. + */ +export interface VelocityAlertPayload { + ['@type']: 'com.google.firebase.firebasealerts.VelocityAlertPayload'; + issue: Issue; + createTime: string; + crashCount: number; + crashPercentage: number; + firstVersion: string; +} + +/** + * The internal payload object for a new Application Not Responding issue. + * Payload is wrapped inside a FirebaseAlertData object. + */ +export interface NewAnrIssuePayload { + ['@type']: 'com.google.firebase.firebasealerts.NewAnrIssuePayload'; + issue: Issue; +} + +interface WithAlertTypeAndApp { + alertType: string; + appId: string; +} +/** + * A custom CloudEvent for Firebase Alerts (with custom extension attributes). + */ +export type CrashlyticsEvent = CloudEvent< + FirebaseAlertData, + WithAlertTypeAndApp +>; + +/** @internal */ +export const newFatalIssueAlert = 'crashlytics.newFatalIssue'; +/** @internal */ +export const newNonfatalIssueAlert = 'crashlytics.newNonfatalIssue'; +/** @internal */ +export const regressionAlert = 'crashlytics.regression'; +/** @internal */ +export const stabilityDigestAlert = 'crashlytics.stabilityDigest'; +/** @internal */ +export const velocityAlert = 'crashlytics.velocity'; +/** @internal */ +export const newAnrIssueAlert = 'crashlytics.newAnrIssue'; + +/** + * Configuration for crashlytics functions. + */ +export interface CrashlyticsOptions extends options.EventHandlerOptions { + appId?: string; +} + +/** + * Declares a function that can handle a new fatal issue published to crashlytics. + */ +export function onNewFatalIssuePublished( + handler: (event: CrashlyticsEvent) => any | Promise +): CloudFunction>; +export function onNewFatalIssuePublished( + appId: string, + handler: (event: CrashlyticsEvent) => any | Promise +): CloudFunction>; +export function onNewFatalIssuePublished( + opts: CrashlyticsOptions, + handler: (event: CrashlyticsEvent) => any | Promise +): CloudFunction>; +export function onNewFatalIssuePublished( + appIdOrOptsOrHandler: + | string + | CrashlyticsOptions + | ((event: CrashlyticsEvent) => any | Promise), + handler?: ( + event: CrashlyticsEvent + ) => any | Promise +): CloudFunction> { + return onOperation( + newFatalIssueAlert, + appIdOrOptsOrHandler, + handler + ); +} + +/** + * Declares a function that can handle aa new non-fatal issue published to crashlytics. + */ +export function onNewNonfatalIssuePublished( + handler: ( + event: CrashlyticsEvent + ) => any | Promise +): CloudFunction>; +export function onNewNonfatalIssuePublished( + appId: string, + handler: ( + event: CrashlyticsEvent + ) => any | Promise +): CloudFunction>; +export function onNewNonfatalIssuePublished( + opts: CrashlyticsOptions, + handler: ( + event: CrashlyticsEvent + ) => any | Promise +): CloudFunction>; +export function onNewNonfatalIssuePublished( + appIdOrOptsOrHandler: + | string + | CrashlyticsOptions + | (( + event: CrashlyticsEvent + ) => any | Promise), + handler?: ( + event: CrashlyticsEvent + ) => any | Promise +): CloudFunction> { + return onOperation( + newNonfatalIssueAlert, + appIdOrOptsOrHandler, + handler + ); +} + +/** + * Declares a function that can handle a regression alert published to crashlytics. + */ +export function onRegressionAlertPublished( + handler: ( + event: CrashlyticsEvent + ) => any | Promise +): CloudFunction>; +export function onRegressionAlertPublished( + appId: string, + handler: ( + event: CrashlyticsEvent + ) => any | Promise +): CloudFunction>; +export function onRegressionAlertPublished( + opts: CrashlyticsOptions, + handler: ( + event: CrashlyticsEvent + ) => any | Promise +): CloudFunction>; +export function onRegressionAlertPublished( + appIdOrOptsOrHandler: + | string + | CrashlyticsOptions + | ((event: CrashlyticsEvent) => any | Promise), + handler?: ( + event: CrashlyticsEvent + ) => any | Promise +): CloudFunction> { + return onOperation( + regressionAlert, + appIdOrOptsOrHandler, + handler + ); +} + +/** + * Declares a function that can handle a stability digest published to crashlytics. + */ +export function onStabilityDigestPublished( + handler: ( + event: CrashlyticsEvent + ) => any | Promise +): CloudFunction>; +export function onStabilityDigestPublished( + appId: string, + handler: ( + event: CrashlyticsEvent + ) => any | Promise +): CloudFunction>; +export function onStabilityDigestPublished( + opts: CrashlyticsOptions, + handler: ( + event: CrashlyticsEvent + ) => any | Promise +): CloudFunction>; +export function onStabilityDigestPublished( + appIdOrOptsOrHandler: + | string + | CrashlyticsOptions + | ((event: CrashlyticsEvent) => any | Promise), + handler?: ( + event: CrashlyticsEvent + ) => any | Promise +): CloudFunction> { + return onOperation( + stabilityDigestAlert, + appIdOrOptsOrHandler, + handler + ); +} + +/** + * Declares a function that can handle a velocity alert published to crashlytics. + */ +export function onVelocityAlertPublished( + handler: (event: CrashlyticsEvent) => any | Promise +): CloudFunction>; +export function onVelocityAlertPublished( + appId: string, + handler: (event: CrashlyticsEvent) => any | Promise +): CloudFunction>; +export function onVelocityAlertPublished( + opts: CrashlyticsOptions, + handler: (event: CrashlyticsEvent) => any | Promise +): CloudFunction>; +export function onVelocityAlertPublished( + appIdOrOptsOrHandler: + | string + | CrashlyticsOptions + | ((event: CrashlyticsEvent) => any | Promise), + handler?: ( + event: CrashlyticsEvent + ) => any | Promise +): CloudFunction> { + return onOperation( + velocityAlert, + appIdOrOptsOrHandler, + handler + ); +} + +/** + * Declares a function that can handle a new Application Not Responding issue published to crashlytics. + */ +export function onNewAnrIssuePublished( + handler: (event: CrashlyticsEvent) => any | Promise +): CloudFunction>; +export function onNewAnrIssuePublished( + appId: string, + handler: (event: CrashlyticsEvent) => any | Promise +): CloudFunction>; +export function onNewAnrIssuePublished( + opts: CrashlyticsOptions, + handler: (event: CrashlyticsEvent) => any | Promise +): CloudFunction>; +export function onNewAnrIssuePublished( + appIdOrOptsOrHandler: + | string + | CrashlyticsOptions + | ((event: CrashlyticsEvent) => any | Promise), + handler?: (event: CrashlyticsEvent) => any | Promise +): CloudFunction> { + return onOperation( + newAnrIssueAlert, + appIdOrOptsOrHandler, + handler + ); +} + +/** @internal */ +export function onOperation( + alertType: string, + appIdOrOptsOrHandler: + | string + | CrashlyticsOptions + | ((event: CrashlyticsEvent) => any | Promise), + handler: (event: CrashlyticsEvent) => any | Promise +): CloudFunction> { + if (typeof appIdOrOptsOrHandler === 'function') { + handler = appIdOrOptsOrHandler as ( + event: CrashlyticsEvent + ) => any | Promise; + appIdOrOptsOrHandler = {}; + } + + const [opts, appId] = getOptsAndApp( + appIdOrOptsOrHandler as string | CrashlyticsOptions + ); + + const func = (raw: CloudEvent) => { + return handler(raw as CrashlyticsEvent); + }; + + func.run = handler; + func.__endpoint = getEndpointAnnotation(opts, alertType, appId); + + return func; +} + +/** + * @internal + * Helper function to parse the function opts and appId. + */ +export function getOptsAndApp( + appIdOrOpts: string | CrashlyticsOptions +): [options.EventHandlerOptions, string | undefined] { + let opts: options.EventHandlerOptions; + let appId: string | undefined; + if (typeof appIdOrOpts === 'string') { + opts = {}; + appId = appIdOrOpts; + } else { + appId = appIdOrOpts.appId; + opts = { ...appIdOrOpts }; + delete (opts as any).appId; + } + return [opts, appId]; +} diff --git a/src/v2/providers/alerts/index.ts b/src/v2/providers/alerts/index.ts new file mode 100644 index 000000000..ecf90a422 --- /dev/null +++ b/src/v2/providers/alerts/index.ts @@ -0,0 +1,6 @@ +import * as appDistribution from './appDistribution'; +import * as billing from './billing'; +import * as crashlytics from './crashlytics'; + +export { appDistribution, billing, crashlytics }; +export * from './alerts'; diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts index e03115df0..d07e1955a 100644 --- a/src/v2/providers/storage.ts +++ b/src/v2/providers/storage.ts @@ -359,7 +359,7 @@ export function onOperation( ...specificOpts?.labels, }, eventTrigger: { - eventType: eventType, + eventType, eventFilters: { bucket, }, diff --git a/v2/alerts/appDistribution.js b/v2/alerts/appDistribution.js new file mode 100644 index 000000000..7d725acc3 --- /dev/null +++ b/v2/alerts/appDistribution.js @@ -0,0 +1,26 @@ +// The MIT License (MIT) +// +// Copyright (c) 2021 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. + +// This file is not part of the firebase-functions SDK. It is used to silence the +// imports eslint plugin until it can understand import paths defined by node +// package exports. +// For more information, see github.com/import-js/eslint-plugin-import/issues/1810 diff --git a/v2/alerts/billing.js b/v2/alerts/billing.js new file mode 100644 index 000000000..7d725acc3 --- /dev/null +++ b/v2/alerts/billing.js @@ -0,0 +1,26 @@ +// The MIT License (MIT) +// +// Copyright (c) 2021 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. + +// This file is not part of the firebase-functions SDK. It is used to silence the +// imports eslint plugin until it can understand import paths defined by node +// package exports. +// For more information, see github.com/import-js/eslint-plugin-import/issues/1810 diff --git a/v2/alerts/crashlytics.js b/v2/alerts/crashlytics.js new file mode 100644 index 000000000..7d725acc3 --- /dev/null +++ b/v2/alerts/crashlytics.js @@ -0,0 +1,26 @@ +// The MIT License (MIT) +// +// Copyright (c) 2021 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. + +// This file is not part of the firebase-functions SDK. It is used to silence the +// imports eslint plugin until it can understand import paths defined by node +// package exports. +// For more information, see github.com/import-js/eslint-plugin-import/issues/1810 diff --git a/v2/alerts/index.js b/v2/alerts/index.js new file mode 100644 index 000000000..7d725acc3 --- /dev/null +++ b/v2/alerts/index.js @@ -0,0 +1,26 @@ +// The MIT License (MIT) +// +// Copyright (c) 2021 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. + +// This file is not part of the firebase-functions SDK. It is used to silence the +// imports eslint plugin until it can understand import paths defined by node +// package exports. +// For more information, see github.com/import-js/eslint-plugin-import/issues/1810 From f3ed26169030cf35331da870d7d204c770cf5875 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 2 Feb 2022 05:21:05 -0800 Subject: [PATCH 025/370] Firebase Functions ships with a binary (#1003) We introduce a bin associated with the firebase-functions SDK! The binary exposes a simple server on localhost that describes the Firebase Functions defined in the current directory: ``` $ STACK_CONTROL_API_PORT=8181 npx firebase-functions # Optionally can pass in a FUNCTIONS_DIR as first arg Serving at port 8181 $ curl localhost:8181/__/stack.yaml | jq { "endpoints": { "onreqv2": { "platform": "gcfv2", "labels": {}, "httpsTrigger": {}, "entryPoint": "onreqv2" }, "onRequest": { "platform": "gcfv1", "httpsTrigger": {}, "entryPoint": "onRequest" } }, "specVersion": "v1alpha1", "requiredAPIs": [] } ``` --- .github/workflows/test.yaml | 2 +- package-lock.json | 109 +++++- package.json | 4 + spec/fixtures/sources/commonjs-grouped/g1.js | 9 + .../sources/commonjs-grouped/index.js | 20 + .../sources/commonjs-grouped/package.json | 3 + .../sources/commonjs-main/functions.js | 18 + .../sources/commonjs-main/package.json | 4 + spec/fixtures/sources/commonjs/index.js | 18 + spec/fixtures/sources/commonjs/package.json | 3 + spec/fixtures/sources/esm-ext/index.mjs | 18 + spec/fixtures/sources/esm-ext/package.json | 4 + spec/fixtures/sources/esm-main/functions.js | 18 + spec/fixtures/sources/esm-main/package.json | 5 + spec/fixtures/sources/esm/index.js | 18 + spec/fixtures/sources/esm/package.json | 4 + spec/runtime/loader.spec.ts | 348 ++++++++++++++++++ spec/v1/providers/analytics.spec.ts | 2 +- src/bin/firebase-functions.ts | 56 +++ src/providers/https.ts | 2 +- src/runtime/loader.ts | 119 ++++++ src/runtime/manifest.ts | 85 +++++ src/v2/core.ts | 2 +- src/v2/options.ts | 2 +- src/v2/params/types.ts | 8 +- src/v2/providers/https.ts | 2 +- src/v2/providers/pubsub.ts | 2 +- src/v2/providers/storage.ts | 2 +- tsconfig.release.json | 3 +- 29 files changed, 872 insertions(+), 18 deletions(-) create mode 100644 spec/fixtures/sources/commonjs-grouped/g1.js create mode 100644 spec/fixtures/sources/commonjs-grouped/index.js create mode 100644 spec/fixtures/sources/commonjs-grouped/package.json create mode 100644 spec/fixtures/sources/commonjs-main/functions.js create mode 100644 spec/fixtures/sources/commonjs-main/package.json create mode 100644 spec/fixtures/sources/commonjs/index.js create mode 100644 spec/fixtures/sources/commonjs/package.json create mode 100644 spec/fixtures/sources/esm-ext/index.mjs create mode 100644 spec/fixtures/sources/esm-ext/package.json create mode 100644 spec/fixtures/sources/esm-main/functions.js create mode 100644 spec/fixtures/sources/esm-main/package.json create mode 100644 spec/fixtures/sources/esm/index.js create mode 100644 spec/fixtures/sources/esm/package.json create mode 100644 spec/runtime/loader.spec.ts create mode 100644 src/bin/firebase-functions.ts create mode 100644 src/runtime/loader.ts create mode 100644 src/runtime/manifest.ts diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index c56d65e12..4160a1eb3 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -31,4 +31,4 @@ jobs: - run: npm install - run: npm run lint - run: npm run format - - run: npm run test + - run: npm run build && npm run test diff --git a/package-lock.json b/package-lock.json index 45768bc71..05b89532d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,9 @@ "express": "^4.17.1", "lodash": "^4.17.14" }, + "bin": { + "firebase-functions": "lib/bin/firebase-functions.js" + }, "devDependencies": { "@types/chai": "^4.1.7", "@types/chai-as-promised": "^7.1.0", @@ -37,6 +40,7 @@ "mz": "^2.7.0", "nock": "^10.0.6", "prettier": "^1.18.2", + "semver": "^7.3.5", "sinon": "^7.3.2", "ts-node": "^8.3.0", "tslint": "^5.18.0", @@ -2994,6 +2998,15 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -3723,6 +3736,15 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/nock/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/node-environment-flags": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", @@ -4439,14 +4461,38 @@ } }, "node_modules/semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha1-fnQlb7qknHWqfHogXMInmcrIAAQ=", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { - "semver": "bin/semver" + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", @@ -4946,6 +4992,15 @@ "path-parse": "^1.0.6" } }, + "node_modules/tslint/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/tsutils": { "version": "2.29.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", @@ -8231,6 +8286,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true } } }, @@ -8872,6 +8933,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true } } }, @@ -9460,10 +9527,30 @@ } }, "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha1-fnQlb7qknHWqfHogXMInmcrIAAQ=", - "dev": true + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } }, "send": { "version": "0.17.1", @@ -9848,6 +9935,12 @@ "requires": { "path-parse": "^1.0.6" } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true } } }, diff --git a/package.json b/package.json index 0be8d9ef9..25f8d8d7f 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,9 @@ "lib" ], "main": "lib/index.js", + "bin": { + "firebase-functions": "./lib/bin/firebase-functions.js" + }, "types": "lib/index.d.ts", "exports": { ".": "./lib/index.js", @@ -178,6 +181,7 @@ "mz": "^2.7.0", "nock": "^10.0.6", "prettier": "^1.18.2", + "semver": "^7.3.5", "sinon": "^7.3.2", "ts-node": "^8.3.0", "tslint": "^5.18.0", diff --git a/spec/fixtures/sources/commonjs-grouped/g1.js b/spec/fixtures/sources/commonjs-grouped/g1.js new file mode 100644 index 000000000..7250c863d --- /dev/null +++ b/spec/fixtures/sources/commonjs-grouped/g1.js @@ -0,0 +1,9 @@ +const functions = require("../../../../src/index"); + +exports.groupedhttp = functions.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +exports.groupedcallable = functions.https.onCall(() => { + return "PASS"; +}); diff --git a/spec/fixtures/sources/commonjs-grouped/index.js b/spec/fixtures/sources/commonjs-grouped/index.js new file mode 100644 index 000000000..aafcf8145 --- /dev/null +++ b/spec/fixtures/sources/commonjs-grouped/index.js @@ -0,0 +1,20 @@ +const functions = require("../../../../src/index"); +const functionsv2 = require("../../../../src/v2/index"); + +exports.v1http = functions.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +exports.v1callable = functions.https.onCall(() => { + return "PASS"; +}); + +exports.v2http = functionsv2.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +exports.v2callable = functionsv2.https.onCall(() => { + return "PASS"; +}); + +exports.g1 = require("./g1"); diff --git a/spec/fixtures/sources/commonjs-grouped/package.json b/spec/fixtures/sources/commonjs-grouped/package.json new file mode 100644 index 000000000..1ec99f52f --- /dev/null +++ b/spec/fixtures/sources/commonjs-grouped/package.json @@ -0,0 +1,3 @@ +{ + "name": "commonjs-grouped" +} diff --git a/spec/fixtures/sources/commonjs-main/functions.js b/spec/fixtures/sources/commonjs-main/functions.js new file mode 100644 index 000000000..526599142 --- /dev/null +++ b/spec/fixtures/sources/commonjs-main/functions.js @@ -0,0 +1,18 @@ +const functions = require("../../../../src/index"); +const functionsv2 = require("../../../../src/v2/index"); + +exports.v1http = functions.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +exports.v1callable = functions.https.onCall(() => { + return "PASS"; +}); + +exports.v2http = functionsv2.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +exports.v2callable = functionsv2.https.onCall(() => { + return "PASS"; +}); diff --git a/spec/fixtures/sources/commonjs-main/package.json b/spec/fixtures/sources/commonjs-main/package.json new file mode 100644 index 000000000..a781259f8 --- /dev/null +++ b/spec/fixtures/sources/commonjs-main/package.json @@ -0,0 +1,4 @@ +{ + "name": "commonjs-main", + "main": "functions.js" +} diff --git a/spec/fixtures/sources/commonjs/index.js b/spec/fixtures/sources/commonjs/index.js new file mode 100644 index 000000000..526599142 --- /dev/null +++ b/spec/fixtures/sources/commonjs/index.js @@ -0,0 +1,18 @@ +const functions = require("../../../../src/index"); +const functionsv2 = require("../../../../src/v2/index"); + +exports.v1http = functions.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +exports.v1callable = functions.https.onCall(() => { + return "PASS"; +}); + +exports.v2http = functionsv2.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +exports.v2callable = functionsv2.https.onCall(() => { + return "PASS"; +}); diff --git a/spec/fixtures/sources/commonjs/package.json b/spec/fixtures/sources/commonjs/package.json new file mode 100644 index 000000000..30e1b1b27 --- /dev/null +++ b/spec/fixtures/sources/commonjs/package.json @@ -0,0 +1,3 @@ +{ + "name": "commonjs" +} diff --git a/spec/fixtures/sources/esm-ext/index.mjs b/spec/fixtures/sources/esm-ext/index.mjs new file mode 100644 index 000000000..28d538e3b --- /dev/null +++ b/spec/fixtures/sources/esm-ext/index.mjs @@ -0,0 +1,18 @@ +import * as functions from '../../../../lib/index.js'; +import * as functionsv2 from "../../../../lib/v2/index.js"; + +export const v1http = functions.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +export const v1callable = functions.https.onCall(() => { + return "PASS"; +}); + +export const v2http = functionsv2.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +export const v2callable = functionsv2.https.onCall(() => { + return "PASS"; +}); diff --git a/spec/fixtures/sources/esm-ext/package.json b/spec/fixtures/sources/esm-ext/package.json new file mode 100644 index 000000000..facb175c2 --- /dev/null +++ b/spec/fixtures/sources/esm-ext/package.json @@ -0,0 +1,4 @@ +{ + "name": "esm-ext", + "main": "index.mjs" +} diff --git a/spec/fixtures/sources/esm-main/functions.js b/spec/fixtures/sources/esm-main/functions.js new file mode 100644 index 000000000..d1f98b49a --- /dev/null +++ b/spec/fixtures/sources/esm-main/functions.js @@ -0,0 +1,18 @@ +import * as functions from "../../../../lib/index.js"; +import * as functionsv2 from "../../../../lib/v2/index.js"; + +export const v1http = functions.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +export const v1callable = functions.https.onCall(() => { + return "PASS"; +}); + +export const v2http = functionsv2.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +export const v2callable = functionsv2.https.onCall(() => { + return "PASS"; +}); diff --git a/spec/fixtures/sources/esm-main/package.json b/spec/fixtures/sources/esm-main/package.json new file mode 100644 index 000000000..6c0840b1d --- /dev/null +++ b/spec/fixtures/sources/esm-main/package.json @@ -0,0 +1,5 @@ +{ + "name": "esm-main", + "main": "functions.js", + "type": "module" +} diff --git a/spec/fixtures/sources/esm/index.js b/spec/fixtures/sources/esm/index.js new file mode 100644 index 000000000..d1f98b49a --- /dev/null +++ b/spec/fixtures/sources/esm/index.js @@ -0,0 +1,18 @@ +import * as functions from "../../../../lib/index.js"; +import * as functionsv2 from "../../../../lib/v2/index.js"; + +export const v1http = functions.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +export const v1callable = functions.https.onCall(() => { + return "PASS"; +}); + +export const v2http = functionsv2.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +export const v2callable = functionsv2.https.onCall(() => { + return "PASS"; +}); diff --git a/spec/fixtures/sources/esm/package.json b/spec/fixtures/sources/esm/package.json new file mode 100644 index 000000000..9cb65cb9f --- /dev/null +++ b/spec/fixtures/sources/esm/package.json @@ -0,0 +1,4 @@ +{ + "name": "esm", + "type": "module" +} diff --git a/spec/runtime/loader.spec.ts b/spec/runtime/loader.spec.ts new file mode 100644 index 000000000..1dab77c43 --- /dev/null +++ b/spec/runtime/loader.spec.ts @@ -0,0 +1,348 @@ +import * as path from 'path'; +import * as semver from 'semver'; +import { expect } from 'chai'; + +import * as loader from '../../src/runtime/loader'; +import * as functions from '../../src/index'; +import { + ManifestStack, + ManifestEndpoint, + ManifestRequiredAPI, +} from '../../src/runtime/manifest'; + +describe('extractStack', () => { + const httpFn = functions.https.onRequest(() => {}); + const httpEndpoint = { + platform: 'gcfv1', + httpsTrigger: {}, + }; + + const callableFn = functions.https.onCall(() => {}); + const callableEndpoint = { + platform: 'gcfv1', + labels: {}, // TODO: empty labels? + callableTrigger: {}, + }; + + it('extracts stack from a simple module', () => { + const module = { + http: httpFn, + callable: callableFn, + }; + + const endpoints: Record = {}; + const requiredAPIs: ManifestRequiredAPI[] = []; + + loader.extractStack(module, endpoints, requiredAPIs); + + expect(endpoints).to.be.deep.equal({ + http: { entryPoint: 'http', ...httpEndpoint }, + callable: { entryPoint: 'callable', ...callableEndpoint }, + }); + + expect(requiredAPIs).to.be.empty; + }); + + it('extracts stack with required APIs', () => { + const module = { + taskq: functions.https.taskQueue().onDispatch(() => {}), + }; + + const endpoints: Record = {}; + const requiredAPIs: ManifestRequiredAPI[] = []; + + loader.extractStack(module, endpoints, requiredAPIs); + + expect(endpoints).to.be.deep.equal({ + taskq: { + entryPoint: 'taskq', + platform: 'gcfv1', + taskQueueTrigger: {}, + }, + }); + + expect(requiredAPIs).to.be.deep.equal([ + { + api: 'cloudtasks.googleapis.com', + reason: 'Needed for task queue functions', + }, + ]); + }); + + it('extracts stack from a module with group functions', () => { + const module = { + fn1: httpFn, + g1: { + fn2: httpFn, + }, + }; + + const endpoints: Record = {}; + const requiredAPIs: ManifestRequiredAPI[] = []; + + loader.extractStack(module, endpoints, requiredAPIs); + + expect(endpoints).to.be.deep.equal({ + fn1: { + entryPoint: 'fn1', + ...httpEndpoint, + }, + 'g1-fn2': { + entryPoint: 'g1.fn2', + ...httpEndpoint, + }, + }); + }); + + describe('with GCLOUD_PROJECT env var', () => { + const project = 'my-project'; + let prev; + + beforeEach(() => { + prev = process.env.GCLOUD_PROJECT; + process.env.GCLOUD_PROJECT = project; + }); + + afterEach(() => { + process.env.GCLOUD_PROJECT = prev; + }); + + it('extracts stack from a simple module', () => { + const module = { + fn: functions.pubsub.topic('my-topic').onPublish(() => {}), + }; + + const endpoints: Record = {}; + const requiredAPIs: ManifestRequiredAPI[] = []; + + loader.extractStack(module, endpoints, requiredAPIs); + + expect(endpoints).to.be.deep.equal({ + fn: { + entryPoint: 'fn', + platform: 'gcfv1', + eventTrigger: { + eventType: 'google.pubsub.topic.publish', + eventFilters: { resource: 'projects/my-project/topics/my-topic' }, + retry: false, + }, + labels: {}, + }, + }); + }); + + it('extracts stack with required APIs', () => { + const module = { + scheduled: functions.pubsub.schedule('every 5 minutes').onRun(() => {}), + }; + + const endpoints: Record = {}; + const requiredAPIs: ManifestRequiredAPI[] = []; + + loader.extractStack(module, endpoints, requiredAPIs); + + expect(endpoints).to.be.deep.equal({ + scheduled: { + entryPoint: 'scheduled', + platform: 'gcfv1', + // TODO: This label should not exist? + labels: {}, + scheduleTrigger: { schedule: 'every 5 minutes' }, + }, + }); + + expect(requiredAPIs).to.be.deep.equal([ + { + api: 'cloudscheduler.googleapis.com', + reason: 'Needed for scheduled functions.', + }, + ]); + }); + }); +}); + +describe('mergedRequiredAPIs', () => { + it('leaves required APIs unchanged if nothing to merge', () => { + expect( + loader.mergeRequiredAPIs([ + { api: 'example1.com', reason: 'example1' }, + { api: 'example2.com', reason: 'example2' }, + ]) + ).to.be.deep.equal([ + { api: 'example1.com', reason: 'example1' }, + { api: 'example2.com', reason: 'example2' }, + ]); + }); + + it('merges reasons given overlapping required api', () => { + expect( + loader.mergeRequiredAPIs([ + { api: 'example1.com', reason: 'example1a' }, + { api: 'example1.com', reason: 'example1b' }, + { api: 'example2.com', reason: 'example2' }, + ]) + ).to.be.deep.equal([ + { api: 'example1.com', reason: 'example1a example1b' }, + { api: 'example2.com', reason: 'example2' }, + ]); + }); + + it('merges reasons given overlapping required api', () => { + expect( + loader.mergeRequiredAPIs([ + { api: 'example1.com', reason: 'example1a' }, + { api: 'example1.com', reason: 'example1b' }, + { api: 'example2.com', reason: 'example2' }, + ]) + ).to.be.deep.equal([ + { api: 'example1.com', reason: 'example1a example1b' }, + { api: 'example2.com', reason: 'example2' }, + ]); + }); + + it('does not repeat the same reason', () => { + expect( + loader.mergeRequiredAPIs([ + { api: 'example1.com', reason: 'example1a' }, + { api: 'example1.com', reason: 'example1a' }, + { api: 'example2.com', reason: 'example2' }, + ]) + ).to.be.deep.equal([ + { api: 'example1.com', reason: 'example1a' }, + { api: 'example2.com', reason: 'example2' }, + ]); + }); +}); + +describe('loadStack', () => { + const expected: ManifestStack = { + endpoints: { + v1http: { + platform: 'gcfv1', + entryPoint: 'v1http', + httpsTrigger: {}, + }, + v1callable: { + platform: 'gcfv1', + entryPoint: 'v1callable', + labels: {}, + callableTrigger: {}, + }, + v2http: { + platform: 'gcfv2', + entryPoint: 'v2http', + labels: {}, + httpsTrigger: {}, + }, + v2callable: { + platform: 'gcfv2', + entryPoint: 'v2callable', + labels: {}, + callableTrigger: {}, + }, + }, + requiredAPIs: [], + specVersion: 'v1alpha1', + }; + + type Testcase = { + name: string; + modulePath: string; + expected: ManifestStack; + }; + function runTests(tc: Testcase) { + it('loads backend given relative path', async () => { + await expect(loader.loadStack(tc.modulePath)).to.eventually.deep.equal( + tc.expected + ); + }); + + it('loads backend given absolute path', async () => { + await expect( + loader.loadStack(path.join(process.cwd(), tc.modulePath)) + ).to.eventually.deep.equal(tc.expected); + }); + } + + let prev; + + beforeEach(() => { + // TODO: When __trigger annotation is removed and GCLOUD_PROJECT is not required at runtime, remove this. + prev = process.env.GCLOUD_PROJECT; + process.env.GCLOUD_PROJECT = 'test-project'; + }); + + afterEach(() => { + process.env.GCLOUD_PROJECT = prev; + }); + + describe('commonjs', () => { + const testcases: Testcase[] = [ + { + name: 'basic', + modulePath: './spec/fixtures/sources/commonjs', + expected, + }, + { + name: 'has main', + modulePath: './spec/fixtures/sources/commonjs-main', + expected, + }, + { + name: 'grouped', + modulePath: './spec/fixtures/sources/commonjs-grouped', + expected: { + ...expected, + endpoints: { + ...expected.endpoints, + 'g1-groupedhttp': { + platform: 'gcfv1', + entryPoint: 'g1.groupedhttp', + httpsTrigger: {}, + }, + 'g1-groupedcallable': { + platform: 'gcfv1', + entryPoint: 'g1.groupedcallable', + labels: {}, + callableTrigger: {}, + }, + }, + }, + }, + ]; + + for (const tc of testcases) { + describe(tc.name, () => { + runTests(tc); + }); + } + }); + + if (semver.gt(process.versions.node, '13.2.0')) { + describe('esm', () => { + const testcases: Testcase[] = [ + { + name: 'basic', + modulePath: './spec/fixtures/sources/esm', + expected, + }, + { + name: 'with main', + modulePath: './spec/fixtures/sources/esm-main', + expected, + }, + { + name: 'with .m extension', + modulePath: './spec/fixtures/sources/esm-ext', + expected, + }, + ]; + + for (const tc of testcases) { + describe(tc.name, () => { + runTests(tc); + }); + } + }); + } +}); diff --git a/spec/v1/providers/analytics.spec.ts b/spec/v1/providers/analytics.spec.ts index 24eb0a008..ead39554f 100644 --- a/spec/v1/providers/analytics.spec.ts +++ b/spec/v1/providers/analytics.spec.ts @@ -327,7 +327,7 @@ describe('Analytics Functions', () => { () => null ); expect(cloudFunction.__trigger).to.deep.equal({}); - expect(cloudFunction.__endpoint).to.undefined; + expect(cloudFunction.__endpoint).to.be.undefined; }); it('should handle an event with the appropriate fields', () => { diff --git a/src/bin/firebase-functions.ts b/src/bin/firebase-functions.ts new file mode 100644 index 000000000..597819658 --- /dev/null +++ b/src/bin/firebase-functions.ts @@ -0,0 +1,56 @@ +#!/usr/bin/env node + +import * as express from 'express'; +import { loadStack } from '../runtime/loader'; + +function printUsageAndExit() { + console.error( + ` +Usage: firebase-functions [functionsDir] + +Arguments: + - functionsDir: Directory containing source code for Firebase Functions. +` + ); + process.exit(1); +} + +let functionsDir = '.'; + +const args = process.argv.slice(2); +if (args.length > 1) { + if (args[0] === '-h' || args[0] === '--help') { + printUsageAndExit(); + } + functionsDir = args[0]; +} + +let server; +const app = express(); + +async function handleQuitquitquit(req: express.Request, res: express.Response) { + res.send('ok'); + server.close(() => console.log('shutdown requested via /__/quitquitquit')); +} + +app.get('/__/quitquitquit', handleQuitquitquit); +app.post('/__/quitquitquit', handleQuitquitquit); +app.get('/__/stack.yaml', async (req, res) => { + try { + const stack = await loadStack(functionsDir); + res.setHeader('content-type', 'text/yaml'); + res.send(JSON.stringify(stack)); + } catch (e) { + res + .status(400) + .send(`Failed to generate manifest from function source: ${e}`); + } +}); + +let port = 8080; +if (process.env.STACK_CONTROL_API_PORT) { + port = Number.parseInt(process.env.STACK_CONTROL_API_PORT); +} + +console.log('Serving at port', port); +server = app.listen(port); diff --git a/src/providers/https.ts b/src/providers/https.ts index 8ab072e78..ae646fa02 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -157,7 +157,7 @@ export class TaskQueueBuilder { func.__requiredAPIs = [ { api: 'cloudtasks.googleapis.com', - reason: 'Needed for v1 task queue functions', + reason: 'Needed for task queue functions', }, ]; diff --git a/src/runtime/loader.ts b/src/runtime/loader.ts new file mode 100644 index 000000000..f9ba05652 --- /dev/null +++ b/src/runtime/loader.ts @@ -0,0 +1,119 @@ +// The MIT License (MIT) +// +// Copyright (c) 2021 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 * as url from 'url'; +import * as path from 'path'; + +import { + ManifestStack, + ManifestEndpoint, + ManifestRequiredAPI, +} from './manifest'; + +/** + * Dynamically load import function to prevent TypeScript from + * transpiling into a require. + * + * See https://github.com/microsoft/TypeScript/issues/43329. + */ +const dynamicImport = new Function( + 'modulePath', + 'return import(modulePath)' +) as (modulePath: string) => Promise; + +async function loadModule(functionsDir: string) { + const absolutePath = path.resolve(functionsDir); + try { + return require(path.resolve(absolutePath)); + } catch (e) { + if (e.code === 'ERR_REQUIRE_ESM') { + // This is an ESM package! + const modulePath = require.resolve(absolutePath); + // Resolve module path to file:// URL. Required for windows support. + // @ts-ignore pathToFileURL exists for Node.js v10 and up. Since ESM support exists for Node.js v13 and up, we + // can be sure that this function exists here. + const moduleURL = url.pathToFileURL(modulePath).href; + return await dynamicImport(moduleURL); + } + throw e; + } +} + +/* @internal */ +export function extractStack( + module, + endpoints: Record, + requiredAPIs: ManifestRequiredAPI[], + prefix = '' +) { + for (const [name, val] of Object.entries(module)) { + if ( + typeof val === 'function' && + val['__endpoint'] && + typeof val['__endpoint'] === 'object' + ) { + const funcName = prefix + name; + endpoints[funcName] = { + ...val['__endpoint'], + entryPoint: funcName.replace(/-/g, '.'), + }; + if (val['__requiredAPIs'] && Array.isArray(val['__requiredAPIs'])) { + requiredAPIs.push(...val['__requiredAPIs']); + } + } else if (typeof val === 'object' && val !== null) { + extractStack(val, endpoints, requiredAPIs, prefix + name + '-'); + } + } +} + +/* @internal */ +export function mergeRequiredAPIs( + requiredAPIs: ManifestRequiredAPI[] +): ManifestRequiredAPI[] { + const apiToReasons: Record> = {}; + for (const { api, reason } of requiredAPIs) { + const reasons = apiToReasons[api] || new Set(); + reasons.add(reason); + apiToReasons[api] = reasons; + } + + const merged: ManifestRequiredAPI[] = []; + for (const [api, reasons] of Object.entries(apiToReasons)) { + merged.push({ api, reason: Array.from(reasons).join(' ') }); + } + return merged; +} + +/* @internal */ +export async function loadStack(functionsDir: string): Promise { + const endpoints: Record = {}; + const requiredAPIs: ManifestRequiredAPI[] = []; + const mod = await loadModule(functionsDir); + + extractStack(mod, endpoints, requiredAPIs); + + const stack: ManifestStack = { + endpoints, + specVersion: 'v1alpha1', + requiredAPIs: mergeRequiredAPIs(requiredAPIs), + }; + return stack; +} diff --git a/src/runtime/manifest.ts b/src/runtime/manifest.ts new file mode 100644 index 000000000..379e61a4f --- /dev/null +++ b/src/runtime/manifest.ts @@ -0,0 +1,85 @@ +// The MIT License (MIT) +// +// Copyright (c) 2021 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. +/** + * @internal + * An definition of a function as appears in the Manifest. + */ +export interface ManifestEndpoint { + entryPoint?: string; + region?: string[]; + platform?: string; + availableMemoryMb?: number; + maxInstances?: number; + minInstances?: number; + concurrency?: number; + serviceAccountEmail?: string; + timeoutSeconds?: number; + vpc?: { + connector: string; + egressSettings?: string; + }; + labels?: Record; + ingressSettings?: string; + environmentVariables?: Record; + + httpsTrigger?: { + invoker?: string[]; + }; + + callableTrigger?: {}; + + eventTrigger?: { + eventFilters: Record; + eventType: string; + retry: boolean; + region?: string; + serviceAccountEmail?: string; + }; + + scheduleTrigger?: { + schedule?: string; + timezone?: string; + retryConfig?: { + retryCount?: number; + maxRetryDuration?: string; + minBackoffDuration?: string; + maxBackoffDuration?: string; + maxDoublings?: number; + }; + }; +} + +/* @internal */ +export interface ManifestRequiredAPI { + api: string; + reason: string; +} + +/** + * @internal + * An definition of a function deployment as appears in the Manifest. + **/ +export interface ManifestStack { + specVersion: 'v1alpha1'; + requiredAPIs: ManifestRequiredAPI[]; + endpoints: Record; +} diff --git a/src/v2/core.ts b/src/v2/core.ts index 845cf2990..1160d09ed 100644 --- a/src/v2/core.ts +++ b/src/v2/core.ts @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -import { ManifestEndpoint } from '../common/manifest'; +import { ManifestEndpoint } from '../runtime/manifest'; /** @internal */ export interface TriggerAnnotation { diff --git a/src/v2/options.ts b/src/v2/options.ts index 7db01167e..77919ab0b 100644 --- a/src/v2/options.ts +++ b/src/v2/options.ts @@ -30,7 +30,7 @@ import * as logger from '../logger'; import { TriggerAnnotation } from './core'; import { declaredParams } from './params'; import { ParamSpec } from './params/types'; -import { ManifestEndpoint } from '../common/manifest'; +import { ManifestEndpoint } from '../runtime/manifest'; /** * List of all regions supported by Cloud Functions v2 diff --git a/src/v2/params/types.ts b/src/v2/params/types.ts index 4bd80df4c..c345cf654 100644 --- a/src/v2/params/types.ts +++ b/src/v2/params/types.ts @@ -2,13 +2,17 @@ type ParamValueType = 'string' | 'list' | 'boolean' | 'int' | 'float' | 'json'; export interface ParamSpec { + name: string; default?: T; label?: string; description?: string; valueType?: ParamValueType; } -export type ParamOptions = Omit, 'valueType'>; +export type ParamOptions = Omit< + ParamSpec, + 'name' | 'valueType' +>; export class Param { static valueType: ParamValueType = 'string'; @@ -33,6 +37,7 @@ export class Param { toSpec(): ParamSpec { const out: ParamSpec = { + name: this.name, ...this.options, valueType: (this.constructor as typeof Param).valueType, }; @@ -120,6 +125,7 @@ export class ListParam extends Param { toSpec(): ParamSpec { const out: ParamSpec = { + name: this.name, valueType: 'list', ...this.options, }; diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 292c7cf0e..f7b51e508 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -40,7 +40,7 @@ import { TaskRequest, TaskRetryConfig, } from '../../common/providers/https'; -import { ManifestEndpoint } from '../../common/manifest'; +import { ManifestEndpoint } from '../../runtime/manifest'; export { Request, diff --git a/src/v2/providers/pubsub.ts b/src/v2/providers/pubsub.ts index a751f0cd2..2964af42d 100644 --- a/src/v2/providers/pubsub.ts +++ b/src/v2/providers/pubsub.ts @@ -1,7 +1,7 @@ import * as options from '../options'; import { CloudEvent, CloudFunction } from '../core'; import { copyIfPresent } from '../../common/encoding'; -import { ManifestEndpoint } from '../../common/manifest'; +import { ManifestEndpoint } from '../../runtime/manifest'; /** * Interface representing a Google Cloud Pub/Sub message. diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts index d07e1955a..d5490317d 100644 --- a/src/v2/providers/storage.ts +++ b/src/v2/providers/storage.ts @@ -24,7 +24,7 @@ import * as options from '../options'; import { firebaseConfig } from '../../config'; import { CloudEvent, CloudFunction } from '../core'; import { copyIfPresent } from '../../common/encoding'; -import { ManifestEndpoint } from '../../common/manifest'; +import { ManifestEndpoint } from '../../runtime/manifest'; /** * An object within Google Cloud Storage. diff --git a/tsconfig.release.json b/tsconfig.release.json index a53421f8f..5f19fe3eb 100644 --- a/tsconfig.release.json +++ b/tsconfig.release.json @@ -14,6 +14,7 @@ "./src/index.ts", "./src/logger/index.ts", "./src/logger/compat.ts", - "./src/v2/index.ts" + "./src/v2/index.ts", + "./src/bin/firebase-functions.ts" ] } From 297b15be245c0005bc8214414991002e241debc1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Feb 2022 07:23:24 -0800 Subject: [PATCH 026/370] Bump node-fetch from 2.6.1 to 2.6.7 (#1025) Bumps [node-fetch](https://github.com/node-fetch/node-fetch) from 2.6.1 to 2.6.7. - [Release notes](https://github.com/node-fetch/node-fetch/releases) - [Commits](https://github.com/node-fetch/node-fetch/compare/v2.6.1...v2.6.7) --- updated-dependencies: - dependency-name: node-fetch dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 140 ++++++++++++++++++++++++---------------------- 1 file changed, 73 insertions(+), 67 deletions(-) diff --git a/package-lock.json b/package-lock.json index 05b89532d..764655dc9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2926,16 +2926,6 @@ "node": ">=4" } }, - "node_modules/jsdom/node_modules/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, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/json-bigint": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", @@ -3403,12 +3393,6 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/mkdirp/node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, "node_modules/mocha": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.3.tgz", @@ -3500,12 +3484,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/mocha/node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, "node_modules/mocha/node_modules/mkdirp": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", @@ -3765,13 +3743,49 @@ } }, "node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dev": true, "optional": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, "engines": { "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true, + "optional": true + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true, + "optional": true + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "optional": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, "node_modules/node-forge": { @@ -4626,15 +4640,6 @@ "source-map": "^0.6.0" } }, - "node_modules/source-map-support/node_modules/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, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -8219,13 +8224,6 @@ "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 } } }, @@ -8638,14 +8636,6 @@ "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": { @@ -8716,12 +8706,6 @@ "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", @@ -8961,11 +8945,41 @@ } }, "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dev": true, - "optional": true + "optional": true, + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true, + "optional": true + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true, + "optional": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "optional": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } }, "node-forge": { "version": "0.10.0", @@ -9672,14 +9686,6 @@ "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": { From af2c51652f3161036a0ce6b977765896bf93c365 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Wed, 2 Feb 2022 18:59:07 -0500 Subject: [PATCH 027/370] Move common auth items into common path (#1026) * moving common auth items to identity.ts and fixing UserRecord definitions * fixing copyright year and exporting old artifacts --- spec/common/providers/identity.spec.ts | 88 ++++++++++++++++++++ spec/v1/providers/auth.spec.ts | 77 ++--------------- src/common/providers/identity.ts | 111 +++++++++++++++++++++++++ src/providers/auth.ts | 94 +++------------------ 4 files changed, 215 insertions(+), 155 deletions(-) create mode 100644 spec/common/providers/identity.spec.ts create mode 100644 src/common/providers/identity.ts diff --git a/spec/common/providers/identity.spec.ts b/spec/common/providers/identity.spec.ts new file mode 100644 index 000000000..fcb686102 --- /dev/null +++ b/spec/common/providers/identity.spec.ts @@ -0,0 +1,88 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as identity from '../../../src/common/providers/identity'; + +describe('identity', () => { + describe('userRecordConstructor', () => { + it('will provide falsey values for fields that are not in raw wire data', () => { + const record = identity.userRecordConstructor({ uid: '123' }); + expect(record.toJSON()).to.deep.equal({ + uid: '123', + email: null, + emailVerified: false, + displayName: null, + photoURL: null, + phoneNumber: null, + disabled: false, + providerData: [], + customClaims: {}, + passwordSalt: null, + passwordHash: null, + tokensValidAfterTime: null, + metadata: { + creationTime: null, + lastSignInTime: null, + }, + }); + }); + + it('will not interfere with fields that are in raw wire data', () => { + const raw: any = { + uid: '123', + email: 'email@gmail.com', + emailVerified: true, + displayName: 'User', + photoURL: 'url', + phoneNumber: '1233332222', + disabled: true, + providerData: [], + customClaims: {}, + passwordSalt: 'abc', + passwordHash: 'def', + tokensValidAfterTime: '2027-02-02T23:01:19.797Z', + metadata: { + creationTime: '2017-02-02T23:06:26.124Z', + lastSignInTime: '2017-02-02T23:01:19.797Z', + }, + }; + const record = identity.userRecordConstructor(raw); + expect(record.toJSON()).to.deep.equal(raw); + }); + + it('will convert raw wire fields createdAt and lastSignedInAt to creationTime and lastSignInTime', () => { + const raw: any = { + uid: '123', + metadata: { + createdAt: '2017-02-02T23:06:26.124Z', + lastSignedInAt: '2017-02-02T23:01:19.797Z', + }, + }; + const record = identity.userRecordConstructor(raw); + expect(record.metadata).to.deep.equal({ + creationTime: '2017-02-02T23:06:26.124Z', + lastSignInTime: '2017-02-02T23:01:19.797Z', + }); + }); + }); +}); diff --git a/spec/v1/providers/auth.spec.ts b/spec/v1/providers/auth.spec.ts index 6168cf767..8659a1967 100644 --- a/spec/v1/providers/auth.spec.ts +++ b/spec/v1/providers/auth.spec.ts @@ -21,13 +21,12 @@ // SOFTWARE. import { expect } from 'chai'; -import * as firebase from 'firebase-admin'; - import { CloudFunction, Event, EventContext, } from '../../../src/cloud-functions'; +import { UserRecord } from '../../../src/common/providers/identity'; import * as functions from '../../../src/index'; import * as auth from '../../../src/providers/auth'; @@ -75,7 +74,7 @@ describe('Auth Functions', () => { }; } - const handler = (user: firebase.auth.UserRecord) => { + const handler = (user: UserRecord) => { return Promise.resolve(); }; @@ -137,14 +136,12 @@ describe('Auth Functions', () => { }); describe('#_dataConstructor', () => { - let cloudFunctionDelete: CloudFunction; + let cloudFunctionDelete: CloudFunction; before(() => { cloudFunctionDelete = auth .user() - .onDelete( - (data: firebase.auth.UserRecord, context: EventContext) => data - ); + .onDelete((data: UserRecord, context: EventContext) => data); }); it('should handle wire format as of v5.0.0 of firebase-admin', () => { @@ -162,68 +159,6 @@ describe('Auth Functions', () => { }); }); - describe('userRecordConstructor', () => { - it('will provide falsey values for fields that are not in raw wire data', () => { - const record = auth.userRecordConstructor({ uid: '123' }); - expect(record.toJSON()).to.deep.equal({ - uid: '123', - email: null, - emailVerified: false, - displayName: null, - photoURL: null, - phoneNumber: null, - disabled: false, - providerData: [], - customClaims: {}, - passwordSalt: null, - passwordHash: null, - tokensValidAfterTime: null, - metadata: { - creationTime: null, - lastSignInTime: null, - }, - }); - }); - - it('will not interfere with fields that are in raw wire data', () => { - const raw: any = { - uid: '123', - email: 'email@gmail.com', - emailVerified: true, - displayName: 'User', - photoURL: 'url', - phoneNumber: '1233332222', - disabled: true, - providerData: [], - customClaims: {}, - passwordSalt: 'abc', - passwordHash: 'def', - tokensValidAfterTime: '2027-02-02T23:01:19.797Z', - metadata: { - creationTime: '2017-02-02T23:06:26.124Z', - lastSignInTime: '2017-02-02T23:01:19.797Z', - }, - }; - const record = auth.userRecordConstructor(raw); - expect(record.toJSON()).to.deep.equal(raw); - }); - - it('will convert raw wire fields createdAt and lastSignedInAt to creationTime and lastSignInTime', () => { - const raw: any = { - uid: '123', - metadata: { - createdAt: '2017-02-02T23:06:26.124Z', - lastSignedInAt: '2017-02-02T23:01:19.797Z', - }, - }; - const record = auth.userRecordConstructor(raw); - expect(record.metadata).to.deep.equal({ - creationTime: '2017-02-02T23:06:26.124Z', - lastSignInTime: '2017-02-02T23:01:19.797Z', - }); - }); - }); - describe('handler namespace', () => { describe('#onCreate', () => { it('should return an empty trigger', () => { @@ -238,8 +173,8 @@ describe('Auth Functions', () => { }); describe('#onDelete', () => { - const cloudFunctionDelete: CloudFunction = functions.handler.auth.user.onDelete( - (data: firebase.auth.UserRecord) => data + const cloudFunctionDelete: CloudFunction = functions.handler.auth.user.onDelete( + (data: UserRecord) => data ); it('should return an empty trigger', () => { diff --git a/src/common/providers/identity.ts b/src/common/providers/identity.ts new file mode 100644 index 000000000..24a4e2c34 --- /dev/null +++ b/src/common/providers/identity.ts @@ -0,0 +1,111 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as firebase from 'firebase-admin'; +import * as _ from 'lodash'; + +/** + * The UserRecord passed to Cloud Functions is the same UserRecord that is returned by the Firebase Admin + * SDK. + */ +export type UserRecord = firebase.auth.UserRecord; + +/** + * UserInfo that is part of the UserRecord + */ +export type UserInfo = firebase.auth.UserInfo; + +/** + * Helper class to create the user metadata in a UserRecord object + */ +export class UserRecordMetadata implements firebase.auth.UserMetadata { + constructor(public creationTime: string, public lastSignInTime: string) {} + + /** Returns a plain JavaScript object with the properties of UserRecordMetadata. */ + toJSON() { + return { + creationTime: this.creationTime, + lastSignInTime: this.lastSignInTime, + }; + } +} + +/** + * Helper function that creates a UserRecord Class from data sent over the wire. + * @param wireData data sent over the wire + * @returns an instance of UserRecord with correct toJSON functions + */ +export function userRecordConstructor(wireData: Object): UserRecord { + // Falsey values from the wire format proto get lost when converted to JSON, this adds them back. + const falseyValues: any = { + email: null, + emailVerified: false, + displayName: null, + photoURL: null, + phoneNumber: null, + disabled: false, + providerData: [], + customClaims: {}, + passwordSalt: null, + passwordHash: null, + tokensValidAfterTime: null, + }; + const record = _.assign({}, falseyValues, wireData); + + const meta = _.get(record, 'metadata'); + if (meta) { + _.set( + record, + 'metadata', + new UserRecordMetadata( + meta.createdAt || meta.creationTime, + meta.lastSignedInAt || meta.lastSignInTime + ) + ); + } else { + _.set(record, 'metadata', new UserRecordMetadata(null, null)); + } + _.forEach(record.providerData, (entry) => { + _.set(entry, 'toJSON', () => { + return entry; + }); + }); + _.set(record, 'toJSON', () => { + const json: any = _.pick(record, [ + 'uid', + 'email', + 'emailVerified', + 'displayName', + 'photoURL', + 'phoneNumber', + 'disabled', + 'passwordHash', + 'passwordSalt', + 'tokensValidAfterTime', + ]); + json.metadata = _.get(record, 'metadata').toJSON(); + json.customClaims = _.cloneDeep(record.customClaims); + json.providerData = _.map(record.providerData, (entry) => entry.toJSON()); + return json; + }); + return record as UserRecord; +} diff --git a/src/providers/auth.ts b/src/providers/auth.ts index 7274399b2..160de06c8 100644 --- a/src/providers/auth.ts +++ b/src/providers/auth.ts @@ -20,8 +20,12 @@ // 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 _ from 'lodash'; +import { + UserRecord, + UserInfo, + UserRecordMetadata, + userRecordConstructor, +} from '../common/providers/identity'; import { CloudFunction, Event, @@ -30,6 +34,9 @@ import { } from '../cloud-functions'; import { DeploymentOptions } from '../function-configuration'; +// TODO: yank in next breaking change release +export { UserRecord, UserInfo, UserRecordMetadata, userRecordConstructor }; + /** @hidden */ export const provider = 'google.firebase.auth'; /** @hidden */ @@ -52,21 +59,9 @@ export function _userWithOptions(options: DeploymentOptions) { }, options); } -export class UserRecordMetadata implements firebase.auth.UserMetadata { - constructor(public creationTime: string, public lastSignInTime: string) {} - - /** Returns a plain JavaScript object with the properties of UserRecordMetadata. */ - toJSON() { - return { - creationTime: this.creationTime, - lastSignInTime: this.lastSignInTime, - }; - } -} - /** Builder used to create Cloud Functions for Firebase Auth user lifecycle events. */ export class UserBuilder { - private static dataConstructor(raw: Event): firebase.auth.UserRecord { + private static dataConstructor(raw: Event): UserRecord { return userRecordConstructor(raw.data); } @@ -109,72 +104,3 @@ export class UserBuilder { }); } } - -/** - * The UserRecord passed to Cloud Functions is the same UserRecord that is returned by the Firebase Admin - * SDK. - */ -export type UserRecord = firebase.auth.UserRecord; - -/** - * UserInfo that is part of the UserRecord - */ -export type UserInfo = firebase.auth.UserInfo; - -export function userRecordConstructor( - wireData: Object -): firebase.auth.UserRecord { - // Falsey values from the wire format proto get lost when converted to JSON, this adds them back. - const falseyValues: any = { - email: null, - emailVerified: false, - displayName: null, - photoURL: null, - phoneNumber: null, - disabled: false, - providerData: [], - customClaims: {}, - passwordSalt: null, - passwordHash: null, - tokensValidAfterTime: null, - }; - const record = _.assign({}, falseyValues, wireData); - - const meta = _.get(record, 'metadata'); - if (meta) { - _.set( - record, - 'metadata', - new UserRecordMetadata( - meta.createdAt || meta.creationTime, - meta.lastSignedInAt || meta.lastSignInTime - ) - ); - } else { - _.set(record, 'metadata', new UserRecordMetadata(null, null)); - } - _.forEach(record.providerData, (entry) => { - _.set(entry, 'toJSON', () => { - return entry; - }); - }); - _.set(record, 'toJSON', () => { - const json: any = _.pick(record, [ - 'uid', - 'email', - 'emailVerified', - 'displayName', - 'photoURL', - 'phoneNumber', - 'disabled', - 'passwordHash', - 'passwordSalt', - 'tokensValidAfterTime', - ]); - json.metadata = _.get(record, 'metadata').toJSON(); - json.customClaims = _.cloneDeep(record.customClaims); - json.providerData = _.map(record.providerData, (entry) => entry.toJSON()); - return json; - }); - return record as firebase.auth.UserRecord; -} From 9e4ee8ad26b947d6698dcac1aa34cf1b8898008d Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Thu, 3 Feb 2022 17:16:17 -0500 Subject: [PATCH 028/370] fix event type (#1029) --- src/v2/providers/alerts/alerts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index 4b7f0b952..8c2699795 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -24,7 +24,7 @@ export type AlertEvent = CloudEvent< >; /** @internal */ -export const eventType = 'firebase.firebasealerts.alerts.v1.published'; +export const eventType = 'google.firebase.firebasealerts.alerts.v1.published'; /** The underlying alert type of the Firebase Alerts provider. */ export type AlertType = From f057ec2e761d55a5d112b86dbebcec5c66341a41 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 3 Feb 2022 14:22:10 -0800 Subject: [PATCH 029/370] Add integration test for firebase-functions binary (#1028) Add integration test for firebase-functions binary. The integration setup is more robust than what we had before - it builds and installs the Functions SDK package at the branch commit and try to run the produced binary that ships with the Functions SDK. We also modify the github CI/release script setting to run the integration test as part of its workflow. --- .github/workflows/test.yaml | 25 +- .mocharc.yaml | 4 +- package.json | 7 +- scripts/bin-test/mocha-setup.ts | 4 + scripts/bin-test/run.sh | 20 ++ .../bin-test/sources/commonjs-grouped/g1.js | 9 + .../sources/commonjs-grouped/index.js | 20 ++ .../sources/commonjs-grouped/package.json | 3 + .../sources/commonjs-main/functions.js | 18 ++ .../sources/commonjs-main/package.json | 4 + scripts/bin-test/sources/commonjs/index.js | 18 ++ .../bin-test/sources/commonjs/package.json | 3 + .../bin-test}/sources/esm-ext/index.mjs | 4 +- .../bin-test}/sources/esm-ext/package.json | 0 .../bin-test}/sources/esm-main/functions.js | 4 +- .../bin-test}/sources/esm-main/package.json | 0 .../bin-test}/sources/esm/index.js | 4 +- .../bin-test}/sources/esm/package.json | 0 scripts/bin-test/test.ts | 233 ++++++++++++++++++ scripts/publish.sh | 1 + spec/runtime/loader.spec.ts | 33 +-- 21 files changed, 371 insertions(+), 43 deletions(-) create mode 100644 scripts/bin-test/mocha-setup.ts create mode 100755 scripts/bin-test/run.sh create mode 100644 scripts/bin-test/sources/commonjs-grouped/g1.js create mode 100644 scripts/bin-test/sources/commonjs-grouped/index.js create mode 100644 scripts/bin-test/sources/commonjs-grouped/package.json create mode 100644 scripts/bin-test/sources/commonjs-main/functions.js create mode 100644 scripts/bin-test/sources/commonjs-main/package.json create mode 100644 scripts/bin-test/sources/commonjs/index.js create mode 100644 scripts/bin-test/sources/commonjs/package.json rename {spec/fixtures => scripts/bin-test}/sources/esm-ext/index.mjs (76%) rename {spec/fixtures => scripts/bin-test}/sources/esm-ext/package.json (100%) rename {spec/fixtures => scripts/bin-test}/sources/esm-main/functions.js (76%) rename {spec/fixtures => scripts/bin-test}/sources/esm-main/package.json (100%) rename {spec/fixtures => scripts/bin-test}/sources/esm/index.js (76%) rename {spec/fixtures => scripts/bin-test}/sources/esm/package.json (100%) create mode 100644 scripts/bin-test/test.ts diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 4160a1eb3..75cee99b0 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -16,6 +16,7 @@ jobs: - 10.x - 12.x - 14.x + - 16.x steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 @@ -31,4 +32,26 @@ jobs: - run: npm install - run: npm run lint - run: npm run format - - run: npm run build && npm run test + - run: npm run test + integration: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: + - 12.x + - 14.x + - 16.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 test:bin diff --git a/.mocharc.yaml b/.mocharc.yaml index 932144124..2bb78edfc 100644 --- a/.mocharc.yaml +++ b/.mocharc.yaml @@ -1,10 +1,8 @@ exit: true extension: - ts -file: - - mocha/setup.ts package: ./package.json reporter: spec require: - 'ts-node/register' -spec: spec/**/*.spec.ts + - 'source-map-support/register' diff --git a/package.json b/package.json index 25f8d8d7f..888085883 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,8 @@ "format:fix": "prettier --write '**/*.{json,md,ts,yml,yaml}'", "lint": "tslint --config tslint.json --project tsconfig.json ", "lint:fix": "tslint --config tslint.json --fix --project tsconfig.json", - "test": "mocha" + "test": "mocha --file ./mocha/setup.ts spec/**/*.spec.ts ", + "test:bin": "./scripts/bin-test/run.sh" }, "dependencies": { "@types/cors": "^2.8.5", @@ -180,10 +181,12 @@ "mock-require": "^3.0.3", "mz": "^2.7.0", "nock": "^10.0.6", + "node-fetch": "^2.6.7", + "portfinder": "^1.0.28", "prettier": "^1.18.2", "semver": "^7.3.5", "sinon": "^7.3.2", - "ts-node": "^8.3.0", + "ts-node": "^10.4.0", "tslint": "^5.18.0", "tslint-config-prettier": "^1.18.0", "tslint-no-unused-expression-chai": "^0.1.4", diff --git a/scripts/bin-test/mocha-setup.ts b/scripts/bin-test/mocha-setup.ts new file mode 100644 index 000000000..6dfdc7f9d --- /dev/null +++ b/scripts/bin-test/mocha-setup.ts @@ -0,0 +1,4 @@ +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; + +chai.use(chaiAsPromised); diff --git a/scripts/bin-test/run.sh b/scripts/bin-test/run.sh new file mode 100755 index 000000000..c3e3da673 --- /dev/null +++ b/scripts/bin-test/run.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -ex # Immediately exit on failure + +# Link the Functions SDK for the testing environment. +npm run build +npm link + +# Link local SDK to all test sources. +for f in scripts/bin-test/sources/*; do + if [ -d "$f" ]; then + (cd "$f" && npm link firebase-functions) + fi +done + +## DEBUG +ls -la scripts/bin-test/sources/commonjs/node_modules + +mocha \ + --file ./scripts/bin-test/mocha-setup.ts \ + ./scripts/bin-test/test.ts diff --git a/scripts/bin-test/sources/commonjs-grouped/g1.js b/scripts/bin-test/sources/commonjs-grouped/g1.js new file mode 100644 index 000000000..57766af02 --- /dev/null +++ b/scripts/bin-test/sources/commonjs-grouped/g1.js @@ -0,0 +1,9 @@ +const functions = require("firebase-functions"); + +exports.groupedhttp = functions.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +exports.groupedcallable = functions.https.onCall(() => { + return "PASS"; +}); diff --git a/scripts/bin-test/sources/commonjs-grouped/index.js b/scripts/bin-test/sources/commonjs-grouped/index.js new file mode 100644 index 000000000..b00828de0 --- /dev/null +++ b/scripts/bin-test/sources/commonjs-grouped/index.js @@ -0,0 +1,20 @@ +const functions = require("firebase-functions"); +const functionsv2 = require("firebase-functions/v2"); + +exports.v1http = functions.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +exports.v1callable = functions.https.onCall(() => { + return "PASS"; +}); + +exports.v2http = functionsv2.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +exports.v2callable = functionsv2.https.onCall(() => { + return "PASS"; +}); + +exports.g1 = require("./g1"); diff --git a/scripts/bin-test/sources/commonjs-grouped/package.json b/scripts/bin-test/sources/commonjs-grouped/package.json new file mode 100644 index 000000000..1ec99f52f --- /dev/null +++ b/scripts/bin-test/sources/commonjs-grouped/package.json @@ -0,0 +1,3 @@ +{ + "name": "commonjs-grouped" +} diff --git a/scripts/bin-test/sources/commonjs-main/functions.js b/scripts/bin-test/sources/commonjs-main/functions.js new file mode 100644 index 000000000..9122f9223 --- /dev/null +++ b/scripts/bin-test/sources/commonjs-main/functions.js @@ -0,0 +1,18 @@ +const functions = require("firebase-functions"); +const functionsv2 = require("firebase-functions/v2"); + +exports.v1http = functions.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +exports.v1callable = functions.https.onCall(() => { + return "PASS"; +}); + +exports.v2http = functionsv2.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +exports.v2callable = functionsv2.https.onCall(() => { + return "PASS"; +}); diff --git a/scripts/bin-test/sources/commonjs-main/package.json b/scripts/bin-test/sources/commonjs-main/package.json new file mode 100644 index 000000000..a781259f8 --- /dev/null +++ b/scripts/bin-test/sources/commonjs-main/package.json @@ -0,0 +1,4 @@ +{ + "name": "commonjs-main", + "main": "functions.js" +} diff --git a/scripts/bin-test/sources/commonjs/index.js b/scripts/bin-test/sources/commonjs/index.js new file mode 100644 index 000000000..9122f9223 --- /dev/null +++ b/scripts/bin-test/sources/commonjs/index.js @@ -0,0 +1,18 @@ +const functions = require("firebase-functions"); +const functionsv2 = require("firebase-functions/v2"); + +exports.v1http = functions.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +exports.v1callable = functions.https.onCall(() => { + return "PASS"; +}); + +exports.v2http = functionsv2.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +exports.v2callable = functionsv2.https.onCall(() => { + return "PASS"; +}); diff --git a/scripts/bin-test/sources/commonjs/package.json b/scripts/bin-test/sources/commonjs/package.json new file mode 100644 index 000000000..30e1b1b27 --- /dev/null +++ b/scripts/bin-test/sources/commonjs/package.json @@ -0,0 +1,3 @@ +{ + "name": "commonjs" +} diff --git a/spec/fixtures/sources/esm-ext/index.mjs b/scripts/bin-test/sources/esm-ext/index.mjs similarity index 76% rename from spec/fixtures/sources/esm-ext/index.mjs rename to scripts/bin-test/sources/esm-ext/index.mjs index 28d538e3b..91e974d93 100644 --- a/spec/fixtures/sources/esm-ext/index.mjs +++ b/scripts/bin-test/sources/esm-ext/index.mjs @@ -1,5 +1,5 @@ -import * as functions from '../../../../lib/index.js'; -import * as functionsv2 from "../../../../lib/v2/index.js"; +import * as functions from "firebase-functions"; +import * as functionsv2 from "firebase-functions/v2"; export const v1http = functions.https.onRequest((req, resp) => { resp.status(200).send("PASS"); diff --git a/spec/fixtures/sources/esm-ext/package.json b/scripts/bin-test/sources/esm-ext/package.json similarity index 100% rename from spec/fixtures/sources/esm-ext/package.json rename to scripts/bin-test/sources/esm-ext/package.json diff --git a/spec/fixtures/sources/esm-main/functions.js b/scripts/bin-test/sources/esm-main/functions.js similarity index 76% rename from spec/fixtures/sources/esm-main/functions.js rename to scripts/bin-test/sources/esm-main/functions.js index d1f98b49a..b09186731 100644 --- a/spec/fixtures/sources/esm-main/functions.js +++ b/scripts/bin-test/sources/esm-main/functions.js @@ -1,5 +1,5 @@ -import * as functions from "../../../../lib/index.js"; -import * as functionsv2 from "../../../../lib/v2/index.js"; +import * as functions from "firebase-functions"; +import * as functionsv2 from "firebase-functions/v2"; export const v1http = functions.https.onRequest((req, resp) => { resp.status(200).send("PASS"); diff --git a/spec/fixtures/sources/esm-main/package.json b/scripts/bin-test/sources/esm-main/package.json similarity index 100% rename from spec/fixtures/sources/esm-main/package.json rename to scripts/bin-test/sources/esm-main/package.json diff --git a/spec/fixtures/sources/esm/index.js b/scripts/bin-test/sources/esm/index.js similarity index 76% rename from spec/fixtures/sources/esm/index.js rename to scripts/bin-test/sources/esm/index.js index d1f98b49a..b09186731 100644 --- a/spec/fixtures/sources/esm/index.js +++ b/scripts/bin-test/sources/esm/index.js @@ -1,5 +1,5 @@ -import * as functions from "../../../../lib/index.js"; -import * as functionsv2 from "../../../../lib/v2/index.js"; +import * as functions from "firebase-functions"; +import * as functionsv2 from "firebase-functions/v2"; export const v1http = functions.https.onRequest((req, resp) => { resp.status(200).send("PASS"); diff --git a/spec/fixtures/sources/esm/package.json b/scripts/bin-test/sources/esm/package.json similarity index 100% rename from spec/fixtures/sources/esm/package.json rename to scripts/bin-test/sources/esm/package.json diff --git a/scripts/bin-test/test.ts b/scripts/bin-test/test.ts new file mode 100644 index 000000000..1c6f1a24e --- /dev/null +++ b/scripts/bin-test/test.ts @@ -0,0 +1,233 @@ +import * as path from 'path'; +import * as subprocess from 'child_process'; +import { promisify } from 'util'; + +import fetch from 'node-fetch'; +import * as portfinder from 'portfinder'; +import * as yaml from 'js-yaml'; +import * as semver from 'semver'; +import { expect } from 'chai'; + +const TIMEOUT_XL = 20_000; +const TIMEOUT_L = 10_000; +const TIMEOUT_M = 5_000; +const TIMEOUT_S = 1_000; + +const BASE_STACK = { + endpoints: { + v1http: { + platform: 'gcfv1', + entryPoint: 'v1http', + httpsTrigger: {}, + }, + v1callable: { + platform: 'gcfv1', + entryPoint: 'v1callable', + labels: {}, + callableTrigger: {}, + }, + v2http: { + platform: 'gcfv2', + entryPoint: 'v2http', + labels: {}, + httpsTrigger: {}, + }, + v2callable: { + platform: 'gcfv2', + entryPoint: 'v2callable', + labels: {}, + callableTrigger: {}, + }, + }, + requiredAPIs: [], + specVersion: 'v1alpha1', +}; + +type Testcase = { + name: string; + modulePath: string; + expected: Record; +}; + +async function retryUntil( + fn: () => Promise, + timeoutMs: number, + sleepMs: number = TIMEOUT_S +) { + const sleep = () => { + return new Promise((resolve) => { + setTimeout(() => resolve(), sleepMs); + }); + }; + const timedOut = new Promise((resolve, reject) => { + setTimeout(() => { + reject(new Error('retry timeout')); + }, timeoutMs); + }); + const retry = (async () => { + while (true) { + if (await fn()) break; + await sleep(); + } + })(); + await Promise.race([retry, timedOut]); +} + +async function startBin( + tc: Testcase, + debug?: boolean +): Promise<{ port: number; cleanup: () => Promise }> { + const getPort = promisify(portfinder.getPort) as () => Promise; + const port = await getPort(); + + const proc = subprocess.spawn('./node_modules/.bin/firebase-functions', [], { + cwd: path.resolve(tc.modulePath), + env: { + PATH: process.env.PATH, + GLCOUD_PROJECT: 'test-project', + STACK_CONTROL_API_PORT: port, + }, + }); + + if (!proc) { + throw new Error('Failed to start firebase functions'); + } + + await retryUntil(async () => { + try { + await fetch(`http://localhost:${port}/__/stack.yaml`); + } catch (e) { + if (e?.code === 'ECONNREFUSED') { + return false; + } + throw e; + } + return true; + }, TIMEOUT_M); + + if (debug) { + proc.stdout?.on('data', (data: unknown) => { + console.log(`[${tc.name} stdout] ` + data); + }); + + proc.stderr?.on('data', (data: unknown) => { + console.log(`[${tc.name} stderr] ` + data); + }); + } + + return { + port, + cleanup: async () => { + process.kill(proc.pid); + await retryUntil(async () => { + try { + process.kill(proc.pid, 0); + } catch { + // process.kill w/ signal 0 will throw an error if the pid no longer exists. + return true; + } + return false; + }, TIMEOUT_M); + }, + }; +} + +describe('stack.yaml', () => { + async function runTests(tc: Testcase) { + let port: number; + let cleanup: () => Promise; + + before(async () => { + const r = await startBin(tc); + port = r.port; + cleanup = r.cleanup; + }); + + after(async () => { + await cleanup?.(); + }); + + it('stack.yaml returns expected Manifest', async () => { + const res = await fetch(`http://localhost:${port}/__/stack.yaml`); + const text = await res.text(); + let parsed: any; + try { + parsed = yaml.load(text); + } catch (err) { + throw new Error('Failed to parse stack.yaml ' + err); + } + expect(parsed).to.be.deep.equal(tc.expected); + }); + } + + describe('commonjs', () => { + const testcases: Testcase[] = [ + { + name: 'basic', + modulePath: './scripts/bin-test/sources/commonjs', + expected: BASE_STACK, + }, + { + name: 'has main', + modulePath: './scripts/bin-test/sources/commonjs-main', + expected: BASE_STACK, + }, + { + name: 'grouped', + modulePath: './scripts/bin-test/sources/commonjs-grouped', + expected: { + ...BASE_STACK, + endpoints: { + ...BASE_STACK.endpoints, + 'g1-groupedhttp': { + platform: 'gcfv1', + entryPoint: 'g1.groupedhttp', + httpsTrigger: {}, + }, + 'g1-groupedcallable': { + platform: 'gcfv1', + entryPoint: 'g1.groupedcallable', + labels: {}, + callableTrigger: {}, + }, + }, + }, + }, + ]; + + for (const tc of testcases) { + describe(tc.name, async () => { + await runTests(tc); + }); + } + }).timeout(TIMEOUT_L); + + if (semver.gt(process.versions.node, '13.2.0')) { + describe('esm', () => { + const testcases: Testcase[] = [ + { + name: 'basic', + modulePath: './scripts/bin-test/sources/esm', + expected: BASE_STACK, + }, + { + name: 'with main', + + modulePath: './scripts/bin-test/sources/esm-main', + expected: BASE_STACK, + }, + { + name: 'with .m extension', + modulePath: './scripts/bin-test/sources/esm-ext', + expected: BASE_STACK, + }, + ]; + + for (const tc of testcases) { + describe(tc.name, async () => { + await runTests(tc); + }); + } + }).timeout(TIMEOUT_L); + } +}).timeout(TIMEOUT_XL); diff --git a/scripts/publish.sh b/scripts/publish.sh index ea5acde68..a7574a15a 100644 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -80,6 +80,7 @@ echo "Ran npm ci." echo "Running tests..." npm test +npm run test:bin echo "Ran tests." echo "Running publish build..." diff --git a/spec/runtime/loader.spec.ts b/spec/runtime/loader.spec.ts index 1dab77c43..436d3ea62 100644 --- a/spec/runtime/loader.spec.ts +++ b/spec/runtime/loader.spec.ts @@ -1,5 +1,4 @@ import * as path from 'path'; -import * as semver from 'semver'; import { expect } from 'chai'; import * as loader from '../../src/runtime/loader'; @@ -251,13 +250,13 @@ describe('loadStack', () => { expected: ManifestStack; }; function runTests(tc: Testcase) { - it('loads backend given relative path', async () => { + it('loads stack given relative path', async () => { await expect(loader.loadStack(tc.modulePath)).to.eventually.deep.equal( tc.expected ); }); - it('loads backend given absolute path', async () => { + it('loads stack given absolute path', async () => { await expect( loader.loadStack(path.join(process.cwd(), tc.modulePath)) ).to.eventually.deep.equal(tc.expected); @@ -317,32 +316,4 @@ describe('loadStack', () => { }); } }); - - if (semver.gt(process.versions.node, '13.2.0')) { - describe('esm', () => { - const testcases: Testcase[] = [ - { - name: 'basic', - modulePath: './spec/fixtures/sources/esm', - expected, - }, - { - name: 'with main', - modulePath: './spec/fixtures/sources/esm-main', - expected, - }, - { - name: 'with .m extension', - modulePath: './spec/fixtures/sources/esm-ext', - expected, - }, - ]; - - for (const tc of testcases) { - describe(tc.name, () => { - runTests(tc); - }); - } - }); - } }); From 03a898ac31822ddb91daeb7c29b27d43e05414d5 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 3 Feb 2022 22:30:13 +0000 Subject: [PATCH 030/370] 3.17.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 764655dc9..7fee69ccb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { "name": "firebase-functions", - "version": "3.16.0", + "version": "3.17.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "3.16.0", + "version": "3.17.0", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index 888085883..92ae3a5e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.16.0", + "version": "3.17.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 5da401538f42a626384415991647d12a0e6423a6 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 3 Feb 2022 22:30:18 +0000 Subject: [PATCH 031/370] [firebase-release] Removed change log and reset repo after 3.17.0 release --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a909dbdbd..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +0,0 @@ -- Parallelizes network calls that occur when validating authorization for onCall handlers. -- Adds new regions to V2 API -- Fixes bug where the emulator crashed when given app without an `options` property. -- Adds new alerting providers From 43fc63444b5453d6a322b691b1774102883e8321 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 4 Feb 2022 07:46:00 -0800 Subject: [PATCH 032/370] Unbreak TS users by removing internal annotations for non-internal type definitions. (#1032) Fixes https://github.com/firebase/firebase-functions/issues/1031 --- CHANGELOG.md | 1 + src/common/manifest.ts | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..e22defd51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Fix issue where TS users couldn't compile their code because of unexported types. (#1032) diff --git a/src/common/manifest.ts b/src/common/manifest.ts index 379e61a4f..e807cdc8f 100644 --- a/src/common/manifest.ts +++ b/src/common/manifest.ts @@ -20,7 +20,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. /** - * @internal * An definition of a function as appears in the Manifest. */ export interface ManifestEndpoint { @@ -68,7 +67,6 @@ export interface ManifestEndpoint { }; } -/* @internal */ export interface ManifestRequiredAPI { api: string; reason: string; From 313680bae2152defdb8c61912672418c8866f4ce Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 4 Feb 2022 15:50:26 +0000 Subject: [PATCH 033/370] 3.17.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7fee69ccb..b6eec3999 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { "name": "firebase-functions", - "version": "3.17.0", + "version": "3.17.1", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "3.17.0", + "version": "3.17.1", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index 92ae3a5e9..524bf88b1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.17.0", + "version": "3.17.1", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 4b3b9b83c0129d23ac3d6fb9b7c602760b16d3ea Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 4 Feb 2022 15:50:31 +0000 Subject: [PATCH 034/370] [firebase-release] Removed change log and reset repo after 3.17.1 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e22defd51..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Fix issue where TS users couldn't compile their code because of unexported types. (#1032) From 9da9a4eb10217b63774cfcc28b0190ef01c336c9 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Mon, 7 Feb 2022 10:23:24 -0800 Subject: [PATCH 035/370] Unbreak v2 TS users by safely removing duplicated manifest definition (#1033) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follows up https://github.com/firebase/firebase-functions/pull/1032 for V2 API to fix https://github.com/firebase/firebase-functions/issues/1031. While rebasing the branch that moved the location of manifest definition (https://github.com/firebase/firebase-functions/pull/1003), the original file was not removed and cleaned up. Having long-lasting PR is dangeroud 🤦🏼‍♂️ . --- CHANGELOG.md | 1 + src/cloud-functions.ts | 2 +- src/common/manifest.ts | 83 ------------------------------- src/providers/https.ts | 2 +- src/runtime/manifest.ts | 3 -- src/v2/providers/alerts/alerts.ts | 2 +- 6 files changed, 4 insertions(+), 89 deletions(-) delete mode 100644 src/common/manifest.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..693cef42b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Fix issue where v2 TS users couldn't compile their code because of unexported types. (#1033) diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index d2de0504c..d07676432 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -37,7 +37,7 @@ import { durationFromSeconds, serviceAccountFromShorthand, } from './common/encoding'; -import { ManifestEndpoint, ManifestRequiredAPI } from './common/manifest'; +import { ManifestEndpoint, ManifestRequiredAPI } from './runtime/manifest'; /** @hidden */ const WILDCARD_REGEX = new RegExp('{[^/{}]*}', 'g'); diff --git a/src/common/manifest.ts b/src/common/manifest.ts deleted file mode 100644 index e807cdc8f..000000000 --- a/src/common/manifest.ts +++ /dev/null @@ -1,83 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2021 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. -/** - * An definition of a function as appears in the Manifest. - */ -export interface ManifestEndpoint { - entryPoint?: string; - region?: string[]; - platform?: string; - availableMemoryMb?: number; - maxInstances?: number; - minInstances?: number; - concurrency?: number; - serviceAccountEmail?: string; - timeoutSeconds?: number; - vpc?: { - connector: string; - egressSettings?: string; - }; - labels?: Record; - ingressSettings?: string; - environmentVariables?: Record; - - httpsTrigger?: { - invoker?: string[]; - }; - - callableTrigger?: {}; - - eventTrigger?: { - eventFilters: Record; - eventType: string; - retry: boolean; - region?: string; - serviceAccountEmail?: string; - }; - - scheduleTrigger?: { - schedule?: string; - timezone?: string; - retryConfig?: { - retryCount?: number; - maxRetryDuration?: string; - minBackoffDuration?: string; - maxBackoffDuration?: string; - maxDoublings?: number; - }; - }; -} - -export interface ManifestRequiredAPI { - api: string; - reason: string; -} - -/** - * @internal - * An definition of a function deployment as appears in the Manifest. - **/ -export interface ManifestStack { - specVersion: 'v1alpha1'; - requiredAPIs: ManifestRequiredAPI[]; - endpoints: Record; -} diff --git a/src/providers/https.ts b/src/providers/https.ts index ae646fa02..dc62458a7 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -33,7 +33,7 @@ import { convertInvoker, copyIfPresent, } from '../common/encoding'; -import { ManifestEndpoint, ManifestRequiredAPI } from '../common/manifest'; +import { ManifestEndpoint, ManifestRequiredAPI } from '../runtime/manifest'; import { CallableContext, FunctionsErrorCode, diff --git a/src/runtime/manifest.ts b/src/runtime/manifest.ts index 379e61a4f..656a0c5d1 100644 --- a/src/runtime/manifest.ts +++ b/src/runtime/manifest.ts @@ -20,7 +20,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. /** - * @internal * An definition of a function as appears in the Manifest. */ export interface ManifestEndpoint { @@ -68,14 +67,12 @@ export interface ManifestEndpoint { }; } -/* @internal */ export interface ManifestRequiredAPI { api: string; reason: string; } /** - * @internal * An definition of a function deployment as appears in the Manifest. **/ export interface ManifestStack { diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index 8c2699795..b1501ed0e 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -1,4 +1,4 @@ -import { ManifestEndpoint } from '../../../common/manifest'; +import { ManifestEndpoint } from '../../../runtime/manifest'; import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; From c34553aa399d7cae679f3c39e0fd87618ec17862 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Mon, 7 Feb 2022 18:47:37 +0000 Subject: [PATCH 036/370] 3.17.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b6eec3999..e6b097d69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { "name": "firebase-functions", - "version": "3.17.1", + "version": "3.17.2", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "3.17.1", + "version": "3.17.2", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index 524bf88b1..ddf21183b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.17.1", + "version": "3.17.2", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 8a515c7634258172a76fd819581217eb247b6772 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Mon, 7 Feb 2022 18:47:42 +0000 Subject: [PATCH 037/370] [firebase-release] Removed change log and reset repo after 3.17.2 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 693cef42b..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Fix issue where v2 TS users couldn't compile their code because of unexported types. (#1033) From 2c897ea88d0c2397e5b9f1614cb3517f11988749 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 10 Feb 2022 15:18:16 -0800 Subject: [PATCH 038/370] Add runtime option to associate secrets to functions (#1018) Google Cloud Functions (GCF) now have support for loading secrets stored in Secret Manager to securely access application secrets (e.g. Third party API keys) on function executions. See the [documentations(https://cloud.google.com/functions/docs/configuring/secrets) for more details. We propose extending the function runtime option to associate secrets. Then, the secrets can be access as an environment once deployed to GCF. --- CHANGELOG.md | 1 + spec/v1/cloud-functions.spec.ts | 2 ++ spec/v1/function-builder.spec.ts | 36 +++++++++++++++++++++++++++++++- src/cloud-functions.ts | 11 +++++++++- src/common/manifest.ts | 0 src/function-builder.ts | 12 +++++++++++ src/function-configuration.ts | 5 +++++ src/runtime/manifest.ts | 1 + 8 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 src/common/manifest.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..ecd0a47c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Add new runtime option for setting secrets. diff --git a/spec/v1/cloud-functions.spec.ts b/spec/v1/cloud-functions.spec.ts index c894bfa23..5dd0a8941 100644 --- a/spec/v1/cloud-functions.spec.ts +++ b/spec/v1/cloud-functions.spec.ts @@ -107,6 +107,7 @@ describe('makeCloudFunction', () => { regions: ['us-central1'], memory: '128MB', serviceAccount: 'foo@google.com', + secrets: ['MY_SECRET'], }, }); @@ -123,6 +124,7 @@ describe('makeCloudFunction', () => { }, retry: false, }, + secretEnvironmentVariables: [{ secret: 'MY_SECRET', key: 'MY_SECRET' }], labels: {}, }); }); diff --git a/spec/v1/function-builder.spec.ts b/spec/v1/function-builder.spec.ts index 13d4daa06..8e01cd87a 100644 --- a/spec/v1/function-builder.spec.ts +++ b/spec/v1/function-builder.spec.ts @@ -472,11 +472,45 @@ describe('FunctionBuilder', () => { ).to.throw(); }); - it('', () => { + it('should throw an error if private identifier is in the invoker array', () => { expect(() => functions.runWith({ invoker: ['service-account1', 'private', 'service-account2'], }) ).to.throw(); }); + + it('should allow valid secret config expressed using short form', () => { + const secrets = ['API_KEY']; + const fn = functions + .runWith({ secrets }) + .auth.user() + .onCreate((user) => user); + + expect(fn.__trigger.secrets).to.deep.equal(secrets); + }); + + it('should throw error given secrets expressed with full resource name', () => { + expect(() => + functions.runWith({ + secrets: ['projects/my-project/secrets/API_KEY'], + }) + ).to.throw(); + }); + + it('should throw error given invalid secret config', () => { + expect(() => + functions.runWith({ + secrets: ['ABC/efg'], + }) + ).to.throw(); + }); + + it('should throw error given invalid secret with versions', () => { + expect(() => + functions.runWith({ + secrets: ['ABC@3'], + }) + ).to.throw(); + }); }); diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index d07676432..ffe94e031 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -281,6 +281,7 @@ export interface TriggerAnnotated { vpcConnectorEgressSettings?: string; serviceAccountEmail?: string; ingressSettings?: string; + secrets?: string[]; }; } @@ -552,7 +553,8 @@ export function optionsToTrigger(options: DeploymentOptions) { 'ingressSettings', 'vpcConnectorEgressSettings', 'vpcConnector', - 'labels' + 'labels', + 'secrets' ); convertIfPresent( trigger, @@ -620,6 +622,13 @@ export function optionsToEndpoint( 'serviceAccount', (sa) => sa ); + convertIfPresent( + endpoint, + options, + 'secretEnvironmentVariables', + 'secrets', + (secrets) => secrets.map((secret) => ({ secret, key: secret })) + ); if (options?.vpcConnector) { endpoint.vpc = { connector: options.vpcConnector }; convertIfPresent( diff --git a/src/common/manifest.ts b/src/common/manifest.ts new file mode 100644 index 000000000..e69de29bb diff --git a/src/function-builder.ts b/src/function-builder.ts index ef6f1ad16..cf52bfb56 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -229,6 +229,18 @@ function assertRuntimeOptionsValid(runtimeOptions: RuntimeOptions): boolean { } } + if (runtimeOptions.secrets !== undefined) { + const invalidSecrets = runtimeOptions.secrets.filter( + (s) => !/^[A-Za-z\d\-_]+$/.test(s) + ); + if (invalidSecrets.length > 0) { + throw new Error( + `Invalid secrets: ${invalidSecrets.join(',')}. ` + + 'Secret must be configured using the resource id (e.g. API_KEY)' + ); + } + } + return true; } diff --git a/src/function-configuration.ts b/src/function-configuration.ts index 5aa6177fd..73cc64eac 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -165,6 +165,11 @@ export interface RuntimeOptions { * Allow requests with invalid App Check tokens on callable functions. */ allowInvalidAppCheckToken?: boolean; + + /* + * Secrets to bind to a function instance. + */ + secrets?: string[]; } export interface DeploymentOptions extends RuntimeOptions { diff --git a/src/runtime/manifest.ts b/src/runtime/manifest.ts index 656a0c5d1..aaaf87761 100644 --- a/src/runtime/manifest.ts +++ b/src/runtime/manifest.ts @@ -39,6 +39,7 @@ export interface ManifestEndpoint { labels?: Record; ingressSettings?: string; environmentVariables?: Record; + secretEnvironmentVariables?: { key: string; secret?: string }[]; httpsTrigger?: { invoker?: string[]; From 388983a9e186a46a744598cbacc7a7f4eee9587c Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 11 Feb 2022 00:22:06 +0000 Subject: [PATCH 039/370] 3.18.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e6b097d69..0cd6dc48f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { "name": "firebase-functions", - "version": "3.17.2", + "version": "3.18.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "3.17.2", + "version": "3.18.0", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index ddf21183b..9a98a54f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.17.2", + "version": "3.18.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From f68e62922cb5a402cdd8290008e8eb08858c009c Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 11 Feb 2022 00:22:10 +0000 Subject: [PATCH 040/370] [firebase-release] Removed change log and reset repo after 3.18.0 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecd0a47c5..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Add new runtime option for setting secrets. From b3a549d3072a5ea8ec599076bd67177694ba8ab6 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 15 Feb 2022 11:40:39 -0800 Subject: [PATCH 041/370] Expose stack on functions.yaml instead of stack.yaml. (#1036) Some contractual changes to how we expose the container contract: * We will expose `__/functions.yaml` instead of `__/stack.yaml` in the control api * Environment variables for exposing the control api changes from `STACK_CONTROL_API_PORT` to `PORT` and `FUNCTIONS_CONTROL_API` (whose value should equal `true`). --- scripts/bin-test/test.ts | 13 +++++++------ src/bin/firebase-functions.ts | 29 ++++++++++++++++------------- src/runtime/loader.ts | 3 +-- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/scripts/bin-test/test.ts b/scripts/bin-test/test.ts index 1c6f1a24e..0ce2f6518 100644 --- a/scripts/bin-test/test.ts +++ b/scripts/bin-test/test.ts @@ -85,7 +85,8 @@ async function startBin( env: { PATH: process.env.PATH, GLCOUD_PROJECT: 'test-project', - STACK_CONTROL_API_PORT: port, + PORT: port, + FUNCTIONS_CONTROL_API: 'true', }, }); @@ -95,7 +96,7 @@ async function startBin( await retryUntil(async () => { try { - await fetch(`http://localhost:${port}/__/stack.yaml`); + await fetch(`http://localhost:${port}/__/functions.yaml`); } catch (e) { if (e?.code === 'ECONNREFUSED') { return false; @@ -132,7 +133,7 @@ async function startBin( }; } -describe('stack.yaml', () => { +describe('functions.yaml', () => { async function runTests(tc: Testcase) { let port: number; let cleanup: () => Promise; @@ -147,14 +148,14 @@ describe('stack.yaml', () => { await cleanup?.(); }); - it('stack.yaml returns expected Manifest', async () => { - const res = await fetch(`http://localhost:${port}/__/stack.yaml`); + it('functions.yaml returns expected Manifest', async () => { + const res = await fetch(`http://localhost:${port}/__/functions.yaml`); const text = await res.text(); let parsed: any; try { parsed = yaml.load(text); } catch (err) { - throw new Error('Failed to parse stack.yaml ' + err); + throw new Error('Failed to parse functions.yaml ' + err); } expect(parsed).to.be.deep.equal(tc.expected); }); diff --git a/src/bin/firebase-functions.ts b/src/bin/firebase-functions.ts index 597819658..4b4eba900 100644 --- a/src/bin/firebase-functions.ts +++ b/src/bin/firebase-functions.ts @@ -35,21 +35,24 @@ async function handleQuitquitquit(req: express.Request, res: express.Response) { app.get('/__/quitquitquit', handleQuitquitquit); app.post('/__/quitquitquit', handleQuitquitquit); -app.get('/__/stack.yaml', async (req, res) => { - try { - const stack = await loadStack(functionsDir); - res.setHeader('content-type', 'text/yaml'); - res.send(JSON.stringify(stack)); - } catch (e) { - res - .status(400) - .send(`Failed to generate manifest from function source: ${e}`); - } -}); + +if (process.env.FUNCTIONS_CONTROL_API === 'true') { + app.get('/__/functions.yaml', async (req, res) => { + try { + const stack = await loadStack(functionsDir); + res.setHeader('content-type', 'text/yaml'); + res.send(JSON.stringify(stack)); + } catch (e) { + res + .status(400) + .send(`Failed to generate manifest from function source: ${e}`); + } + }); +} let port = 8080; -if (process.env.STACK_CONTROL_API_PORT) { - port = Number.parseInt(process.env.STACK_CONTROL_API_PORT); +if (process.env.PORT) { + port = Number.parseInt(process.env.PORT); } console.log('Serving at port', port); diff --git a/src/runtime/loader.ts b/src/runtime/loader.ts index f9ba05652..8d08138b9 100644 --- a/src/runtime/loader.ts +++ b/src/runtime/loader.ts @@ -110,10 +110,9 @@ export async function loadStack(functionsDir: string): Promise { extractStack(mod, endpoints, requiredAPIs); - const stack: ManifestStack = { + return { endpoints, specVersion: 'v1alpha1', requiredAPIs: mergeRequiredAPIs(requiredAPIs), }; - return stack; } From 069695b99c5eb8f2eb65324b89c5fdae791725ba Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 17 Feb 2022 10:41:38 -0800 Subject: [PATCH 042/370] Add missing changelog entry for #1036. (#1041) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..17c1c5d19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Expose stack YAML via \_\_/functions.yaml endpoint instead (#1036). From 0345715531913ea93c6c0b8cc5ef6fd0a2945f09 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 18 Feb 2022 18:23:16 +0000 Subject: [PATCH 043/370] 3.18.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0cd6dc48f..50cc3bb62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { "name": "firebase-functions", - "version": "3.18.0", + "version": "3.18.1", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "3.18.0", + "version": "3.18.1", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index 9a98a54f8..86163fc08 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.18.0", + "version": "3.18.1", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 795a80500527f23723cdbca124bc7a7693cb14a7 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 18 Feb 2022 18:23:19 +0000 Subject: [PATCH 044/370] [firebase-release] Removed change log and reset repo after 3.18.1 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17c1c5d19..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Expose stack YAML via \_\_/functions.yaml endpoint instead (#1036). From 162979366c541b2c74d1a5d8d3d79bc4c2b6fe9e Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 23 Feb 2022 08:07:16 -0800 Subject: [PATCH 045/370] Update test command to run all tests, not a subset. (#1046) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 86163fc08..07ee2ccf5 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "format:fix": "prettier --write '**/*.{json,md,ts,yml,yaml}'", "lint": "tslint --config tslint.json --project tsconfig.json ", "lint:fix": "tslint --config tslint.json --fix --project tsconfig.json", - "test": "mocha --file ./mocha/setup.ts spec/**/*.spec.ts ", + "test": "mocha --file ./mocha/setup.ts \"spec/**/*.spec.ts\"", "test:bin": "./scripts/bin-test/run.sh" }, "dependencies": { From 2b83e1d2531cbf6a88f8350aabeb1a50b16cd62a Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 23 Feb 2022 13:02:41 -0800 Subject: [PATCH 046/370] Add support for more instance sizes and regions (#1037) --- src/v2/options.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/v2/options.ts b/src/v2/options.ts index 77919ab0b..73eca40ac 100644 --- a/src/v2/options.ts +++ b/src/v2/options.ts @@ -36,10 +36,13 @@ import { ManifestEndpoint } from '../runtime/manifest'; * List of all regions supported by Cloud Functions v2 */ export const SUPPORTED_REGIONS = [ - 'us-west1', - 'us-central1', - 'europe-west4', 'asia-northeast1', + 'europe-north1', + 'europe-west1', + 'europe-west4', + 'us-central1', + 'us-east1', + 'us-west1', ] as const; /** @@ -71,21 +74,27 @@ export const MAX_CONCURRENCY = 1_000; * List of available memory options supported by Cloud Functions. */ export const SUPPORTED_MEMORY_OPTIONS = [ + '128MB', '256MB', '512MB', '1GB', '2GB', '4GB', '8GB', + '16GB', + '32GB', ] as const; const MemoryOptionToMB: Record = { + '128MB': 128, '256MB': 256, '512MB': 512, '1GB': 1024, '2GB': 2048, '4GB': 4096, '8GB': 8192, + '16GB': 16384, + '32GB': 32768, }; /** From c09761aa4517e7b1d1e7a4f9ae3199fe8379348c Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Fri, 4 Mar 2022 10:29:44 -0500 Subject: [PATCH 047/370] Fix handler typo in billing alerts (#1045) * fixing typo * changing var name & fixing describe title * updating alerttype --- spec/v2/providers/alerts/billing.spec.ts | 10 +++++----- src/v2/providers/alerts/billing.ts | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spec/v2/providers/alerts/billing.spec.ts b/spec/v2/providers/alerts/billing.spec.ts index a9e4e173d..71e695fc7 100644 --- a/spec/v2/providers/alerts/billing.spec.ts +++ b/spec/v2/providers/alerts/billing.spec.ts @@ -43,9 +43,9 @@ describe('billing', () => { }); }); - describe('onAutomatedPlanUpdatePublished', () => { + describe('onPlanAutomatedUpdatePublished', () => { it('should create a function with only handler', () => { - const func = billing.onAutomatedPlanUpdatePublished(myHandler); + const func = billing.onPlanAutomatedUpdatePublished(myHandler); expect(func.__endpoint).to.deep.equal({ platform: 'gcfv2', @@ -53,7 +53,7 @@ describe('billing', () => { eventTrigger: { eventType: alerts.eventType, eventFilters: { - alertType: billing.automatedPlanUpdateAlert, + alertType: billing.planAutomatedUpdateAlert, }, retry: false, }, @@ -61,7 +61,7 @@ describe('billing', () => { }); it('should create a function with opts & handler', () => { - const func = billing.onAutomatedPlanUpdatePublished( + const func = billing.onPlanAutomatedUpdatePublished( { ...FULL_OPTIONS }, myHandler ); @@ -71,7 +71,7 @@ describe('billing', () => { eventTrigger: { eventType: alerts.eventType, eventFilters: { - alertType: billing.automatedPlanUpdateAlert, + alertType: billing.planAutomatedUpdateAlert, }, retry: false, }, diff --git a/src/v2/providers/alerts/billing.ts b/src/v2/providers/alerts/billing.ts index f2ecd4289..dd8ccb2c5 100644 --- a/src/v2/providers/alerts/billing.ts +++ b/src/v2/providers/alerts/billing.ts @@ -32,7 +32,7 @@ export type BillingEvent = CloudEvent, WithAlertType>; /** @internal */ export const planUpdateAlert = 'billing.planUpdate'; /** @internal */ -export const automatedPlanUpdateAlert = 'billing.automatedPlanUpdate'; +export const planAutomatedUpdateAlert = 'billing.planAutomatedUpdate'; /** * Declares a function that can handle a billing plan update event. @@ -60,18 +60,18 @@ export function onPlanUpdatePublished( /** * Declares a function that can handle an automated billing plan update event. */ -export function onAutomatedPlanUpdatePublished( +export function onPlanAutomatedUpdatePublished( handler: ( event: BillingEvent ) => any | Promise ): CloudFunction>; -export function onAutomatedPlanUpdatePublished( +export function onPlanAutomatedUpdatePublished( opts: options.EventHandlerOptions, handler: ( event: BillingEvent ) => any | Promise ): CloudFunction>; -export function onAutomatedPlanUpdatePublished( +export function onPlanAutomatedUpdatePublished( optsOrHandler: | options.EventHandlerOptions | ((event: BillingEvent) => any | Promise), @@ -80,7 +80,7 @@ export function onAutomatedPlanUpdatePublished( ) => any | Promise ): CloudFunction> { return onOperation( - automatedPlanUpdateAlert, + planAutomatedUpdateAlert, optsOrHandler, handler ); From f45e45f28294f578c22f2e26f65c16cf1563720f Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Mon, 7 Mar 2022 11:42:47 -0800 Subject: [PATCH 048/370] Migrate endpoint annotation eventTrigger.eventFilters to list of filters (#1052) Previously, `eventFilter` attribute was an object: ``` eventTrigger: { eventType: "an.event", eventFilter: { resource: "my-storage-bucket", appId: "12345", } } ``` We now prefer eventFilter as a list: ``` eventTrigger: { eventType: "an.event", eventFilter: [ { attribute: "resource", value: "my-storage-bucket", }. { attribute: "appId", value: "12345", }. ] } ``` Most of the change in this PR is in tests. Few other minor changes I've squeezed in here: 1. Added a changelog entry for https://github.com/firebase/firebase-functions/pull/1037 2. Deleted obsolete src/common/manifest.ts file 3. Small refactoring of alerts/crashlytics spec to make this migration easier. --- CHANGELOG.md | 1 + spec/runtime/loader.spec.ts | 7 +- spec/v1/cloud-functions.spec.ts | 36 +- spec/v1/providers/analytics.spec.ts | 9 +- spec/v1/providers/auth.spec.ts | 9 +- spec/v1/providers/database.spec.ts | 9 +- spec/v1/providers/firestore.spec.ts | 9 +- spec/v1/providers/pubsub.spec.ts | 9 +- spec/v1/providers/remoteConfig.spec.ts | 9 +- spec/v1/providers/storage.spec.ts | 9 +- spec/v1/providers/testLab.spec.ts | 9 +- spec/v2/providers/alerts/alerts.spec.ts | 69 ++- .../providers/alerts/appDistribution.spec.ts | 46 +- spec/v2/providers/alerts/billing.spec.ts | 54 +- spec/v2/providers/alerts/crashlytics.spec.ts | 568 +++++------------- spec/v2/providers/pubsub.spec.ts | 9 +- spec/v2/providers/storage.spec.ts | 162 +++-- src/cloud-functions.ts | 9 +- src/common/manifest.ts | 0 src/runtime/manifest.ts | 16 +- src/v2/providers/alerts/alerts.ts | 14 +- src/v2/providers/pubsub.ts | 2 +- src/v2/providers/storage.ts | 4 +- 23 files changed, 475 insertions(+), 594 deletions(-) delete mode 100644 src/common/manifest.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..c094d2168 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Add support for more regions and memory for v2 functions (#1037). diff --git a/spec/runtime/loader.spec.ts b/spec/runtime/loader.spec.ts index 436d3ea62..11acb583f 100644 --- a/spec/runtime/loader.spec.ts +++ b/spec/runtime/loader.spec.ts @@ -122,7 +122,12 @@ describe('extractStack', () => { platform: 'gcfv1', eventTrigger: { eventType: 'google.pubsub.topic.publish', - eventFilters: { resource: 'projects/my-project/topics/my-topic' }, + eventFilters: [ + { + attribute: 'resource', + value: 'projects/my-project/topics/my-topic', + }, + ], retry: false, }, labels: {}, diff --git a/spec/v1/cloud-functions.spec.ts b/spec/v1/cloud-functions.spec.ts index 5dd0a8941..3405af695 100644 --- a/spec/v1/cloud-functions.spec.ts +++ b/spec/v1/cloud-functions.spec.ts @@ -62,9 +62,12 @@ describe('makeCloudFunction', () => { platform: 'gcfv1', eventTrigger: { eventType: 'mock.provider.mock.event', - eventFilters: { - resource: 'resource', - }, + eventFilters: [ + { + attribute: 'resource', + value: 'resource', + }, + ], retry: false, }, labels: {}, @@ -86,9 +89,12 @@ describe('makeCloudFunction', () => { platform: 'gcfv1', eventTrigger: { eventType: 'providers/provider/eventTypes/event', - eventFilters: { - resource: 'resource', - }, + eventFilters: [ + { + attribute: 'resource', + value: 'resource', + }, + ], retry: false, }, labels: {}, @@ -119,9 +125,12 @@ describe('makeCloudFunction', () => { serviceAccountEmail: 'foo@google.com', eventTrigger: { eventType: 'mock.provider.mock.event', - eventFilters: { - resource: 'resource', - }, + eventFilters: [ + { + attribute: 'resource', + value: 'resource', + }, + ], retry: false, }, secretEnvironmentVariables: [{ secret: 'MY_SECRET', key: 'MY_SECRET' }], @@ -143,9 +152,12 @@ describe('makeCloudFunction', () => { platform: 'gcfv1', eventTrigger: { eventType: 'mock.provider.mock.event', - eventFilters: { - resource: 'resource', - }, + eventFilters: [ + { + attribute: 'resource', + value: 'resource', + }, + ], retry: true, }, labels: {}, diff --git a/spec/v1/providers/analytics.spec.ts b/spec/v1/providers/analytics.spec.ts index ead39554f..1603fc280 100644 --- a/spec/v1/providers/analytics.spec.ts +++ b/spec/v1/providers/analytics.spec.ts @@ -72,9 +72,12 @@ describe('Analytics Functions', () => { expect(cloudFunction.__endpoint).to.deep.equal({ platform: 'gcfv1', eventTrigger: { - eventFilters: { - resource: 'projects/project1/events/first_open', - }, + eventFilters: [ + { + attribute: 'resource', + value: 'projects/project1/events/first_open', + }, + ], eventType: 'providers/google.firebase.analytics/eventTypes/event.log', retry: false, diff --git a/spec/v1/providers/auth.spec.ts b/spec/v1/providers/auth.spec.ts index 8659a1967..3f5913d2b 100644 --- a/spec/v1/providers/auth.spec.ts +++ b/spec/v1/providers/auth.spec.ts @@ -64,9 +64,12 @@ describe('Auth Functions', () => { return { platform: 'gcfv1', eventTrigger: { - eventFilters: { - resource: `projects/${project}`, - }, + eventFilters: [ + { + attribute: 'resource', + value: `projects/${project}`, + }, + ], eventType: `providers/firebase.auth/eventTypes/${eventType}`, retry: false, }, diff --git a/spec/v1/providers/database.spec.ts b/spec/v1/providers/database.spec.ts index f932dd871..21706b2d5 100644 --- a/spec/v1/providers/database.spec.ts +++ b/spec/v1/providers/database.spec.ts @@ -45,9 +45,12 @@ describe('Database Functions', () => { return { platform: 'gcfv1', eventTrigger: { - eventFilters: { - resource, - }, + eventFilters: [ + { + attribute: 'resource', + value: resource, + }, + ], eventType: `providers/google.firebase.database/eventTypes/${eventType}`, retry: false, }, diff --git a/spec/v1/providers/firestore.spec.ts b/spec/v1/providers/firestore.spec.ts index 3f9f07fbe..0317e69f0 100644 --- a/spec/v1/providers/firestore.spec.ts +++ b/spec/v1/providers/firestore.spec.ts @@ -107,9 +107,12 @@ describe('Firestore Functions', () => { return { platform: 'gcfv1', eventTrigger: { - eventFilters: { - resource, - }, + eventFilters: [ + { + attribute: 'resource', + value: resource, + }, + ], eventType: `providers/cloud.firestore/eventTypes/${eventType}`, retry: false, }, diff --git a/spec/v1/providers/pubsub.spec.ts b/spec/v1/providers/pubsub.spec.ts index 5547515ab..eddf6533c 100644 --- a/spec/v1/providers/pubsub.spec.ts +++ b/spec/v1/providers/pubsub.spec.ts @@ -108,9 +108,12 @@ describe('Pubsub Functions', () => { platform: 'gcfv1', eventTrigger: { eventType: 'google.pubsub.topic.publish', - eventFilters: { - resource: 'projects/project1/topics/toppy', - }, + eventFilters: [ + { + attribute: 'resource', + value: 'projects/project1/topics/toppy', + }, + ], retry: false, }, labels: {}, diff --git a/spec/v1/providers/remoteConfig.spec.ts b/spec/v1/providers/remoteConfig.spec.ts index f3fde2043..8dc310a7b 100644 --- a/spec/v1/providers/remoteConfig.spec.ts +++ b/spec/v1/providers/remoteConfig.spec.ts @@ -68,9 +68,12 @@ describe('RemoteConfig Functions', () => { platform: 'gcfv1', eventTrigger: { eventType: 'google.firebase.remoteconfig.update', - eventFilters: { - resource: 'projects/project1', - }, + eventFilters: [ + { + attribute: 'resource', + value: 'projects/project1', + }, + ], retry: false, }, labels: {}, diff --git a/spec/v1/providers/storage.spec.ts b/spec/v1/providers/storage.spec.ts index d96b131b9..d99a14baa 100644 --- a/spec/v1/providers/storage.spec.ts +++ b/spec/v1/providers/storage.spec.ts @@ -42,9 +42,12 @@ describe('Storage Functions', () => { return { platform: 'gcfv1', eventTrigger: { - eventFilters: { - resource: `projects/_/buckets/${bucket}`, - }, + eventFilters: [ + { + attribute: 'resource', + value: `projects/_/buckets/${bucket}`, + }, + ], eventType: `google.storage.object.${eventType}`, retry: false, }, diff --git a/spec/v1/providers/testLab.spec.ts b/spec/v1/providers/testLab.spec.ts index b3ba22d7f..968ccb079 100644 --- a/spec/v1/providers/testLab.spec.ts +++ b/spec/v1/providers/testLab.spec.ts @@ -50,9 +50,12 @@ describe('Test Lab Functions', () => { platform: 'gcfv1', eventTrigger: { eventType: 'google.testing.testMatrix.complete', - eventFilters: { - resource: 'projects/project1/testMatrices/{matrix}', - }, + eventFilters: [ + { + attribute: 'resource', + value: 'projects/project1/testMatrices/{matrix}', + }, + ], retry: false, }, labels: {}, diff --git a/spec/v2/providers/alerts/alerts.spec.ts b/spec/v2/providers/alerts/alerts.spec.ts index b63f567aa..4f53c41d2 100644 --- a/spec/v2/providers/alerts/alerts.spec.ts +++ b/spec/v2/providers/alerts/alerts.spec.ts @@ -16,9 +16,12 @@ describe('alerts', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: ALERT_TYPE, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: ALERT_TYPE, + }, + ], retry: false, }, }); @@ -38,10 +41,16 @@ describe('alerts', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: ALERT_TYPE, - appId: APPID, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: ALERT_TYPE, + }, + { + attribute: 'appid', + value: APPID, + }, + ], retry: false, }, }); @@ -72,9 +81,12 @@ describe('alerts', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: ALERT_TYPE, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: ALERT_TYPE, + }, + ], retry: false, }, }); @@ -87,9 +99,12 @@ describe('alerts', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: ALERT_TYPE, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: ALERT_TYPE, + }, + ], retry: false, }, }); @@ -102,10 +117,16 @@ describe('alerts', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: ALERT_TYPE, - appId: APPID, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: ALERT_TYPE, + }, + { + attribute: 'appid', + value: APPID, + }, + ], retry: false, }, }); @@ -132,10 +153,16 @@ describe('alerts', () => { minInstances: 3, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: ALERT_TYPE, - appId: APPID, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: ALERT_TYPE, + }, + { + attribute: 'appid', + value: APPID, + }, + ], retry: false, }, }); diff --git a/spec/v2/providers/alerts/appDistribution.spec.ts b/spec/v2/providers/alerts/appDistribution.spec.ts index 52fdfd3b9..2f8ea5c5a 100644 --- a/spec/v2/providers/alerts/appDistribution.spec.ts +++ b/spec/v2/providers/alerts/appDistribution.spec.ts @@ -19,10 +19,16 @@ describe('appDistribution', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: appDistribution.newTesterIosDeviceAlert, - appId: APPID, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: appDistribution.newTesterIosDeviceAlert, + }, + { + attribute: 'appid', + value: APPID, + }, + ], retry: false, }, }); @@ -38,9 +44,12 @@ describe('appDistribution', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: appDistribution.newTesterIosDeviceAlert, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: appDistribution.newTesterIosDeviceAlert, + }, + ], retry: false, }, }); @@ -56,10 +65,16 @@ describe('appDistribution', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: appDistribution.newTesterIosDeviceAlert, - appId: APPID, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: appDistribution.newTesterIosDeviceAlert, + }, + { + attribute: 'appid', + value: APPID, + }, + ], retry: false, }, }); @@ -73,9 +88,12 @@ describe('appDistribution', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: appDistribution.newTesterIosDeviceAlert, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: appDistribution.newTesterIosDeviceAlert, + }, + ], retry: false, }, }); diff --git a/spec/v2/providers/alerts/billing.spec.ts b/spec/v2/providers/alerts/billing.spec.ts index 71e695fc7..bc3fa6a9a 100644 --- a/spec/v2/providers/alerts/billing.spec.ts +++ b/spec/v2/providers/alerts/billing.spec.ts @@ -16,9 +16,12 @@ describe('billing', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: billing.planUpdateAlert, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: billing.planUpdateAlert, + }, + ], retry: false, }, }); @@ -34,9 +37,12 @@ describe('billing', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: billing.planUpdateAlert, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: billing.planUpdateAlert, + }, + ], retry: false, }, }); @@ -52,9 +58,12 @@ describe('billing', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: billing.planAutomatedUpdateAlert, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: billing.planAutomatedUpdateAlert, + }, + ], retry: false, }, }); @@ -70,9 +79,12 @@ describe('billing', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: billing.planAutomatedUpdateAlert, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: billing.planAutomatedUpdateAlert, + }, + ], retry: false, }, }); @@ -88,9 +100,12 @@ describe('billing', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: ALERT_TYPE, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: ALERT_TYPE, + }, + ], retry: false, }, }); @@ -107,9 +122,12 @@ describe('billing', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: ALERT_TYPE, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: ALERT_TYPE, + }, + ], retry: false, }, }); diff --git a/spec/v2/providers/alerts/crashlytics.spec.ts b/spec/v2/providers/alerts/crashlytics.spec.ts index 1d5c1a8b6..0ac00c5e0 100644 --- a/spec/v2/providers/alerts/crashlytics.spec.ts +++ b/spec/v2/providers/alerts/crashlytics.spec.ts @@ -8,437 +8,121 @@ const APPID = '123456789'; const myHandler = () => 42; describe('crashlytics', () => { - describe('onNewFatalIssuePublished', () => { - it('should create a function only handler', () => { - const func = crashlytics.onNewFatalIssuePublished(myHandler); - - expect(func.__endpoint).to.deep.equal({ - platform: 'gcfv2', - labels: {}, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.newFatalIssueAlert, - }, - retry: false, - }, - }); - }); - - it('should create a function with appId', () => { - const func = crashlytics.onNewFatalIssuePublished(APPID, myHandler); - - expect(func.__endpoint).to.deep.equal({ - platform: 'gcfv2', - labels: {}, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.newFatalIssueAlert, - appId: APPID, - }, - retry: false, - }, - }); - }); - - it('should create a function with base opts', () => { - const func = crashlytics.onNewFatalIssuePublished( - { ...FULL_OPTIONS }, - myHandler - ); - - expect(func.__endpoint).to.deep.equal({ - ...FULL_ENDPOINT, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.newFatalIssueAlert, - }, - retry: false, - }, - }); - }); - - it('should create a function with opts', () => { - const func = crashlytics.onNewFatalIssuePublished( - { ...FULL_OPTIONS, appId: APPID }, - myHandler - ); - - expect(func.__endpoint).to.deep.equal({ - ...FULL_ENDPOINT, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.newFatalIssueAlert, - appId: APPID, - }, - retry: false, - }, - }); - }); - }); - - describe('onNewNonfatalIssuePublished', () => { - it('should create a function only handler', () => { - const func = crashlytics.onNewNonfatalIssuePublished(myHandler); - - expect(func.__endpoint).to.deep.equal({ - platform: 'gcfv2', - labels: {}, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.newNonfatalIssueAlert, - }, - retry: false, - }, - }); - }); - - it('should create a function with appId', () => { - const func = crashlytics.onNewNonfatalIssuePublished(APPID, myHandler); - - expect(func.__endpoint).to.deep.equal({ - platform: 'gcfv2', - labels: {}, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.newNonfatalIssueAlert, - appId: APPID, - }, - retry: false, - }, - }); - }); - - it('should create a function with base opts', () => { - const func = crashlytics.onNewNonfatalIssuePublished( - { ...FULL_OPTIONS }, - myHandler - ); - - expect(func.__endpoint).to.deep.equal({ - ...FULL_ENDPOINT, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.newNonfatalIssueAlert, + const testcases = [ + { + method: 'onNewFatalIssuePublished', + event: crashlytics.newFatalIssueAlert, + }, + { + method: 'onNewNonfatalIssuePublished', + event: crashlytics.newNonfatalIssueAlert, + }, + { + method: 'onRegressionAlertPublished', + event: crashlytics.regressionAlert, + }, + { + method: 'onStabilityDigestPublished', + event: crashlytics.stabilityDigestAlert, + }, + { + method: 'onVelocityAlertPublished', + event: crashlytics.velocityAlert, + }, + { + method: 'onNewAnrIssuePublished', + event: crashlytics.newAnrIssueAlert, + }, + ]; + + for (const { method, event } of testcases) { + describe(method, () => { + it('should create a function only handler', () => { + const func = crashlytics[method](myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: [ + { + attribute: 'alerttype', + value: event, + }, + ], + retry: false, }, - retry: false, - }, + }); }); - }); - - it('should create a function with opts', () => { - const func = crashlytics.onNewNonfatalIssuePublished( - { ...FULL_OPTIONS, appId: APPID }, - myHandler - ); - expect(func.__endpoint).to.deep.equal({ - ...FULL_ENDPOINT, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.newNonfatalIssueAlert, - appId: APPID, + it('should create a function with appId', () => { + const func = crashlytics[method](APPID, myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: [ + { + attribute: 'alerttype', + value: event, + }, + { + attribute: 'appid', + value: APPID, + }, + ], + retry: false, }, - retry: false, - }, + }); }); - }); - }); - describe('onRegressionAlertPublished', () => { - it('should create a function only handler', () => { - const func = crashlytics.onRegressionAlertPublished(myHandler); - - expect(func.__endpoint).to.deep.equal({ - platform: 'gcfv2', - labels: {}, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.regressionAlert, + it('should create a function with base opts', () => { + const func = crashlytics[method]({ ...FULL_OPTIONS }, myHandler); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: [ + { + attribute: 'alerttype', + value: event, + }, + ], + retry: false, }, - retry: false, - }, + }); }); - }); - - it('should create a function with appId', () => { - const func = crashlytics.onRegressionAlertPublished(APPID, myHandler); - expect(func.__endpoint).to.deep.equal({ - platform: 'gcfv2', - labels: {}, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.regressionAlert, - appId: APPID, + it('should create a function with opts', () => { + const func = crashlytics[method]( + { ...FULL_OPTIONS, appId: APPID }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: [ + { + attribute: 'alerttype', + value: event, + }, + { + attribute: 'appid', + value: APPID, + }, + ], + retry: false, }, - retry: false, - }, + }); }); }); - - it('should create a function with base opts', () => { - const func = crashlytics.onRegressionAlertPublished( - { ...FULL_OPTIONS }, - myHandler - ); - - expect(func.__endpoint).to.deep.equal({ - ...FULL_ENDPOINT, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.regressionAlert, - }, - retry: false, - }, - }); - }); - - it('should create a function with opts', () => { - const func = crashlytics.onRegressionAlertPublished( - { ...FULL_OPTIONS, appId: APPID }, - myHandler - ); - - expect(func.__endpoint).to.deep.equal({ - ...FULL_ENDPOINT, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.regressionAlert, - appId: APPID, - }, - retry: false, - }, - }); - }); - }); - - describe('onStabilityDigestPublished', () => { - it('should create a function only handler', () => { - const func = crashlytics.onStabilityDigestPublished(myHandler); - - expect(func.__endpoint).to.deep.equal({ - platform: 'gcfv2', - labels: {}, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.stabilityDigestAlert, - }, - retry: false, - }, - }); - }); - - it('should create a function with appId', () => { - const func = crashlytics.onStabilityDigestPublished(APPID, myHandler); - - expect(func.__endpoint).to.deep.equal({ - platform: 'gcfv2', - labels: {}, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.stabilityDigestAlert, - appId: APPID, - }, - retry: false, - }, - }); - }); - - it('should create a function with base opts', () => { - const func = crashlytics.onStabilityDigestPublished( - { ...FULL_OPTIONS }, - myHandler - ); - - expect(func.__endpoint).to.deep.equal({ - ...FULL_ENDPOINT, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.stabilityDigestAlert, - }, - retry: false, - }, - }); - }); - - it('should create a function with opts', () => { - const func = crashlytics.onStabilityDigestPublished( - { ...FULL_OPTIONS, appId: APPID }, - myHandler - ); - - expect(func.__endpoint).to.deep.equal({ - ...FULL_ENDPOINT, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.stabilityDigestAlert, - appId: APPID, - }, - retry: false, - }, - }); - }); - }); - - describe('onVelocityAlertPublished', () => { - it('should create a function only handler', () => { - const func = crashlytics.onVelocityAlertPublished(myHandler); - - expect(func.__endpoint).to.deep.equal({ - platform: 'gcfv2', - labels: {}, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.velocityAlert, - }, - retry: false, - }, - }); - }); - - it('should create a function with appId', () => { - const func = crashlytics.onVelocityAlertPublished(APPID, myHandler); - - expect(func.__endpoint).to.deep.equal({ - platform: 'gcfv2', - labels: {}, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.velocityAlert, - appId: APPID, - }, - retry: false, - }, - }); - }); - - it('should create a function with base opts', () => { - const func = crashlytics.onVelocityAlertPublished( - { ...FULL_OPTIONS }, - myHandler - ); - - expect(func.__endpoint).to.deep.equal({ - ...FULL_ENDPOINT, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.velocityAlert, - }, - retry: false, - }, - }); - }); - - it('should create a function with opts', () => { - const func = crashlytics.onVelocityAlertPublished( - { ...FULL_OPTIONS, appId: APPID }, - myHandler - ); - - expect(func.__endpoint).to.deep.equal({ - ...FULL_ENDPOINT, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.velocityAlert, - appId: APPID, - }, - retry: false, - }, - }); - }); - }); - - describe('onNewAnrIssuePublished', () => { - it('should create a function only handler', () => { - const func = crashlytics.onNewAnrIssuePublished(myHandler); - - expect(func.__endpoint).to.deep.equal({ - platform: 'gcfv2', - labels: {}, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.newAnrIssueAlert, - }, - retry: false, - }, - }); - }); - - it('should create a function with appId', () => { - const func = crashlytics.onNewAnrIssuePublished(APPID, myHandler); - - expect(func.__endpoint).to.deep.equal({ - platform: 'gcfv2', - labels: {}, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.newAnrIssueAlert, - appId: APPID, - }, - retry: false, - }, - }); - }); - - it('should create a function with base opts', () => { - const func = crashlytics.onNewAnrIssuePublished( - { ...FULL_OPTIONS }, - myHandler - ); - - expect(func.__endpoint).to.deep.equal({ - ...FULL_ENDPOINT, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.newAnrIssueAlert, - }, - retry: false, - }, - }); - }); - - it('should create a function with opts', () => { - const func = crashlytics.onNewAnrIssuePublished( - { ...FULL_OPTIONS, appId: APPID }, - myHandler - ); - - expect(func.__endpoint).to.deep.equal({ - ...FULL_ENDPOINT, - eventTrigger: { - eventType: alerts.eventType, - eventFilters: { - alertType: crashlytics.newAnrIssueAlert, - appId: APPID, - }, - retry: false, - }, - }); - }); - }); + } describe('onOperation', () => { it('should create a function with alertType only', () => { @@ -449,9 +133,12 @@ describe('crashlytics', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: ALERT_TYPE, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: ALERT_TYPE, + }, + ], retry: false, }, }); @@ -465,10 +152,16 @@ describe('crashlytics', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: ALERT_TYPE, - appId: APPID, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: ALERT_TYPE, + }, + { + attribute: 'appid', + value: APPID, + }, + ], retry: false, }, }); @@ -485,9 +178,12 @@ describe('crashlytics', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: ALERT_TYPE, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: ALERT_TYPE, + }, + ], retry: false, }, }); @@ -504,10 +200,16 @@ describe('crashlytics', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: { - alertType: ALERT_TYPE, - appId: APPID, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: ALERT_TYPE, + }, + { + attribute: 'appid', + value: APPID, + }, + ], retry: false, }, }); diff --git a/spec/v2/providers/pubsub.spec.ts b/spec/v2/providers/pubsub.spec.ts index f48e10f72..303ffa1d7 100644 --- a/spec/v2/providers/pubsub.spec.ts +++ b/spec/v2/providers/pubsub.spec.ts @@ -12,9 +12,12 @@ const EVENT_TRIGGER = { const ENDPOINT_EVENT_TRIGGER = { eventType: 'google.cloud.pubsub.topic.v1.messagePublished', - eventFilters: { - topic: 'topic', - }, + eventFilters: [ + { + attribute: 'topic', + value: 'topic', + }, + ], retry: false, }; diff --git a/spec/v2/providers/storage.spec.ts b/spec/v2/providers/storage.spec.ts index 8c2e25576..cb612e561 100644 --- a/spec/v2/providers/storage.spec.ts +++ b/spec/v2/providers/storage.spec.ts @@ -12,9 +12,12 @@ const EVENT_TRIGGER = { const ENDPOINT_EVENT_TRIGGER = { eventType: 'event-type', - eventFilters: { - bucket: 'some-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'some-bucket', + }, + ], retry: false, }; @@ -116,9 +119,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_EVENT_TRIGGER, - eventFilters: { - bucket: 'default-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'default-bucket', + }, + ], }, region: ['us-west1'], }); @@ -268,9 +274,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_ARCHIVED_TRIGGER, - eventFilters: { - bucket: 'default-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'default-bucket', + }, + ], }, }); }); @@ -292,9 +301,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_ARCHIVED_TRIGGER, - eventFilters: { - bucket: 'my-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'my-bucket', + }, + ], }, }); }); @@ -320,9 +332,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_ARCHIVED_TRIGGER, - eventFilters: { - bucket: 'my-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'my-bucket', + }, + ], }, region: ['us-west1'], }); @@ -348,9 +363,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_ARCHIVED_TRIGGER, - eventFilters: { - bucket: 'default-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'default-bucket', + }, + ], }, region: ['us-west1'], }); @@ -395,9 +413,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_FINALIZED_TRIGGER, - eventFilters: { - bucket: 'default-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'default-bucket', + }, + ], }, }); }); @@ -419,9 +440,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_FINALIZED_TRIGGER, - eventFilters: { - bucket: 'my-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'my-bucket', + }, + ], }, }); }); @@ -447,9 +471,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_FINALIZED_TRIGGER, - eventFilters: { - bucket: 'my-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'my-bucket', + }, + ], }, region: ['us-west1'], }); @@ -478,9 +505,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_FINALIZED_TRIGGER, - eventFilters: { - bucket: 'default-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'default-bucket', + }, + ], }, region: ['us-west1'], }); @@ -525,9 +555,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_DELETED_TRIGGER, - eventFilters: { - bucket: 'default-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'default-bucket', + }, + ], }, }); @@ -551,9 +584,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_DELETED_TRIGGER, - eventFilters: { - bucket: 'my-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'my-bucket', + }, + ], }, }); }); @@ -579,9 +615,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_DELETED_TRIGGER, - eventFilters: { - bucket: 'my-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'my-bucket', + }, + ], }, region: ['us-west1'], }); @@ -607,9 +646,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_DELETED_TRIGGER, - eventFilters: { - bucket: 'default-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'default-bucket', + }, + ], }, region: ['us-west1'], }); @@ -654,9 +696,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_METADATA_TRIGGER, - eventFilters: { - bucket: 'default-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'default-bucket', + }, + ], }, }); @@ -680,9 +725,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_METADATA_TRIGGER, - eventFilters: { - bucket: 'my-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'my-bucket', + }, + ], }, }); }); @@ -708,9 +756,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_METADATA_TRIGGER, - eventFilters: { - bucket: 'my-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'my-bucket', + }, + ], }, region: ['us-west1'], }); @@ -739,9 +790,12 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_METADATA_TRIGGER, - eventFilters: { - bucket: 'default-bucket', - }, + eventFilters: [ + { + attribute: 'bucket', + value: 'default-bucket', + }, + ], }, region: ['us-west1'], }); diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index ffe94e031..ab6fdb120 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -461,9 +461,12 @@ export function makeCloudFunction({ } else { endpoint.eventTrigger = { eventType: legacyEventType || provider + '.' + eventType, - eventFilters: { - resource: triggerResource(), - }, + eventFilters: [ + { + attribute: 'resource', + value: triggerResource(), + }, + ], retry: !!options.failurePolicy, }; } diff --git a/src/common/manifest.ts b/src/common/manifest.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/runtime/manifest.ts b/src/runtime/manifest.ts index aaaf87761..2b08385d5 100644 --- a/src/runtime/manifest.ts +++ b/src/runtime/manifest.ts @@ -19,6 +19,16 @@ // 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. +/** + * One or more event filters restrict the set of events delivered to an EventTrigger. + */ +interface EventFilter { + attribute: string; + value: string; + // if left unspecified, equality is used. + operator?: string; +} + /** * An definition of a function as appears in the Manifest. */ @@ -39,7 +49,7 @@ export interface ManifestEndpoint { labels?: Record; ingressSettings?: string; environmentVariables?: Record; - secretEnvironmentVariables?: { key: string; secret?: string }[]; + secretEnvironmentVariables?: Array<{ key: string; secret?: string }>; httpsTrigger?: { invoker?: string[]; @@ -48,7 +58,7 @@ export interface ManifestEndpoint { callableTrigger?: {}; eventTrigger?: { - eventFilters: Record; + eventFilters: EventFilter[]; eventType: string; retry: boolean; region?: string; @@ -75,7 +85,7 @@ export interface ManifestRequiredAPI { /** * An definition of a function deployment as appears in the Manifest. - **/ + */ export interface ManifestStack { specVersion: 'v1alpha1'; requiredAPIs: ManifestRequiredAPI[]; diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index b1501ed0e..902bd4232 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -91,14 +91,20 @@ export function getEndpointAnnotation( }, eventTrigger: { eventType, - eventFilters: { - alertType, - }, + eventFilters: [ + { + attribute: 'alerttype', + value: alertType, + }, + ], retry: !!opts.retry, }, }; if (appId) { - endpoint.eventTrigger.eventFilters.appId = appId; + endpoint.eventTrigger.eventFilters.push({ + attribute: 'appid', + value: appId, + }); } return endpoint; } diff --git a/src/v2/providers/pubsub.ts b/src/v2/providers/pubsub.ts index 2964af42d..01b731ccb 100644 --- a/src/v2/providers/pubsub.ts +++ b/src/v2/providers/pubsub.ts @@ -174,7 +174,7 @@ export function onMessagePublished( }, eventTrigger: { eventType: 'google.cloud.pubsub.topic.v1.messagePublished', - eventFilters: { topic }, + eventFilters: [{ attribute: 'topic', value: topic }], retry: false, }, }; diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts index d5490317d..8e63ea45e 100644 --- a/src/v2/providers/storage.ts +++ b/src/v2/providers/storage.ts @@ -360,9 +360,7 @@ export function onOperation( }, eventTrigger: { eventType, - eventFilters: { - bucket, - }, + eventFilters: [{ attribute: 'bucket', value: bucket }], retry: false, }, }; From 0a1c7ab54062a7f2413159e853130626380f5061 Mon Sep 17 00:00:00 2001 From: Rich Hodgkins Date: Fri, 11 Mar 2022 15:14:00 +0000 Subject: [PATCH 049/370] Fix bug where RTDB trigger for instances w/ "ref" in the db name were incorrectly parsed. Fixes #1055 --- CHANGELOG.md | 1 + spec/v1/providers/database.spec.ts | 16 ++++++++++++++++ src/providers/database.ts | 3 ++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c094d2168..939704fd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ - Add support for more regions and memory for v2 functions (#1037). +- Fixes bug where some RTDB instance names were incorrectly parsed (#1056). diff --git a/spec/v1/providers/database.spec.ts b/spec/v1/providers/database.spec.ts index 21706b2d5..2ea1022e3 100644 --- a/spec/v1/providers/database.spec.ts +++ b/spec/v1/providers/database.spec.ts @@ -529,6 +529,22 @@ describe('Database Functions', () => { expect(path).to.equal('/bar'); }); + it('should return the correct instance and path strings if root path is /refs', () => { + const [instance, path] = database.extractInstanceAndPath( + 'projects/_/instances/foo/refs/refs' + ); + expect(instance).to.equal('https://foo.firebaseio.com'); + expect(path).to.equal('/refs'); + }); + + it('should return the correct instance and path strings if a child path contain /refs', () => { + const [instance, path] = database.extractInstanceAndPath( + 'projects/_/instances/foo/refs/root/refs' + ); + expect(instance).to.equal('https://foo.firebaseio.com'); + expect(path).to.equal('/root/refs'); + }); + 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', diff --git a/src/providers/database.ts b/src/providers/database.ts index 53eb9ba12..e5f4d8ed3 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -307,6 +307,8 @@ export class RefBuilder { }; } +const resourceRegex = /^projects\/([^/]+)\/instances\/([a-zA-Z0-9-]+)\/refs(\/.+)?/; + /** * Utility function to extract database reference from resource string * @@ -320,7 +322,6 @@ 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) { throw new Error( From 307fe694cea998b4bfcd39a7bfcb19582814f29c Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 16 Mar 2022 18:16:07 +0000 Subject: [PATCH 050/370] 3.19.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 50cc3bb62..94bf393fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { "name": "firebase-functions", - "version": "3.18.1", + "version": "3.19.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "3.18.1", + "version": "3.19.0", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index 07ee2ccf5..a478a7a91 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.18.1", + "version": "3.19.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 270605dcc884933a7356406ef504ccd7a54cfaca Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 16 Mar 2022 18:16:12 +0000 Subject: [PATCH 051/370] [firebase-release] Removed change log and reset repo after 3.19.0 release --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 939704fd0..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +0,0 @@ -- Add support for more regions and memory for v2 functions (#1037). -- Fixes bug where some RTDB instance names were incorrectly parsed (#1056). From 6d146aa5604c899f21b19a749b8e6663bddef697 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Mon, 28 Mar 2022 16:28:00 -0700 Subject: [PATCH 052/370] Update package-lock.json and use npm ci for CI. (#1064) --- .github/workflows/test.yaml | 2 +- package-lock.json | 597 ++++++++++++++++++++++++++++-------- 2 files changed, 464 insertions(+), 135 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 75cee99b0..33242e9b8 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -29,7 +29,7 @@ jobs: path: ~/.npm key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }} - - run: npm install + - run: npm ci - run: npm run lint - run: npm run format - run: npm run test diff --git a/package-lock.json b/package-lock.json index 94bf393fb..0201ae801 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "firebase-functions", "version": "3.19.0", "license": "MIT", "dependencies": { @@ -39,10 +40,12 @@ "mock-require": "^3.0.3", "mz": "^2.7.0", "nock": "^10.0.6", + "node-fetch": "^2.6.7", + "portfinder": "^1.0.28", "prettier": "^1.18.2", "semver": "^7.3.5", "sinon": "^7.3.2", - "ts-node": "^8.3.0", + "ts-node": "^10.4.0", "tslint": "^5.18.0", "tslint-config-prettier": "^1.18.0", "tslint-no-unused-expression-chai": "^0.1.4", @@ -84,53 +87,33 @@ "js-tokens": "^4.0.0" } }, - "node_modules/@firebase/app": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.7.4.tgz", - "integrity": "sha512-XBrZb60m7N1XqmRhSJWADDD3J/0j9wM2VhxC5KUEtFA9SWfTn9Z3EWGtRGz7ahrMkgPJsmo0fXpvUh6cY8pQvQ==", + "node_modules/@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", "dev": true, - "peer": true, - "dependencies": { - "@firebase/component": "0.5.7", - "@firebase/logger": "0.3.0", - "@firebase/util": "1.4.0", - "tslib": "^2.1.0" + "engines": { + "node": ">= 12" } }, - "node_modules/@firebase/app-compat": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.5.tgz", - "integrity": "sha512-GJURp5Nn8dEm72/y13Z+XMvWmMomsYViNxw6VKYqVH9f9VKnJ46Q8zYtx2ePvOuj7pAqsfwNkvAdAFYcveTe9g==", + "node_modules/@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", "dev": true, - "peer": true, "dependencies": { - "@firebase/app": "0.7.4", - "@firebase/component": "0.5.7", - "@firebase/logger": "0.3.0", - "@firebase/util": "1.4.0", - "tslib": "^2.1.0" + "@cspotcode/source-map-consumer": "0.8.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/@firebase/app-compat/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true, - "peer": true - }, "node_modules/@firebase/app-types": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.7.0.tgz", "integrity": "sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg==", "dev": true }, - "node_modules/@firebase/app/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true, - "peer": true - }, "node_modules/@firebase/auth-interop-types": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.6.tgz", @@ -383,6 +366,9 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@grpc/grpc-js": { @@ -446,6 +432,9 @@ }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/@grpc/proto-loader/node_modules/cliui": { @@ -538,6 +527,9 @@ }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/@grpc/proto-loader/node_modules/yargs": { @@ -698,6 +690,30 @@ "node": ">= 6" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, "node_modules/@types/body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", @@ -950,6 +966,11 @@ }, "engines": { "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/agent-base/node_modules/ms": { @@ -969,6 +990,10 @@ "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/ansi-colors": { @@ -1082,6 +1107,15 @@ "node": "*" } }, + "node_modules/async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, "node_modules/async-retry": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz", @@ -1124,6 +1158,20 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "optional": true }, "node_modules/bcrypt-pbkdf": { @@ -1205,12 +1253,6 @@ "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=", "dev": true }, - "node_modules/buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=", - "dev": true - }, "node_modules/builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", @@ -1267,6 +1309,9 @@ "dev": true, "dependencies": { "check-error": "^1.0.2" + }, + "peerDependencies": { + "chai": ">= 2.1.2 < 5" } }, "node_modules/chalk": { @@ -1500,6 +1545,12 @@ "node": ">= 0.10" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", @@ -1816,6 +1867,9 @@ }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/es-to-primitive": { @@ -1830,6 +1884,9 @@ }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/escalade": { @@ -1867,6 +1924,9 @@ }, "engines": { "node": ">=4.0.0" + }, + "peerDependencies": { + "prettier": ">= 0.11.0" } }, "node_modules/estraverse": { @@ -2064,6 +2124,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "deprecated": "Fixed a prototype pollution security issue in 4.1.0, please upgrade to ^4.1.1 or ^5.0.1.", "dev": true, "dependencies": { "is-buffer": "~2.0.3" @@ -2218,6 +2279,9 @@ "optional": true, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/getpass": { @@ -2441,6 +2505,7 @@ "version": "5.1.5", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", "dev": true, "dependencies": { "ajv": "^6.12.3", @@ -2469,6 +2534,9 @@ "dev": true, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/hash-stream-validation": { @@ -2567,6 +2635,11 @@ }, "engines": { "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/http-proxy-agent/node_modules/ms": { @@ -2616,6 +2689,11 @@ }, "engines": { "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/https-proxy-agent/node_modules/ms": { @@ -2694,6 +2772,9 @@ "dev": true, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-date-object": { @@ -2703,6 +2784,9 @@ "dev": true, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-fullwidth-code-point": { @@ -2749,6 +2833,9 @@ }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-stream": { @@ -2778,6 +2865,9 @@ }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-typedarray": { @@ -2814,6 +2904,9 @@ }, "engines": { "node": ">=10.13.0 < 13 || >=13.7.0" + }, + "funding": { + "url": "https://github.com/sponsors/panva" } }, "node_modules/js-tokens": { @@ -2889,6 +2982,14 @@ }, "engines": { "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, "node_modules/jsdom/node_modules/escodegen": { @@ -3066,6 +3167,11 @@ }, "engines": { "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/jwks-rsa/node_modules/ms": { @@ -3260,6 +3366,9 @@ }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/make-dir/node_modules/semver": { @@ -3444,6 +3553,7 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", "dev": true, "dependencies": { "ms": "^2.1.1" @@ -3488,6 +3598,7 @@ "version": "0.5.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", "dev": true, "dependencies": { "minimist": "^1.2.5" @@ -3689,6 +3800,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", "dev": true, "dependencies": { "ms": "^2.1.1" @@ -3706,6 +3818,9 @@ "object-is": "^1.0.1", "object-keys": "^1.1.1", "regexp.prototype.flags": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/nock/node_modules/ms": { @@ -3747,7 +3862,6 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dev": true, - "optional": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -3767,22 +3881,19 @@ "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", - "dev": true, - "optional": true + "dev": true }, "node_modules/node-fetch/node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", - "dev": true, - "optional": true + "dev": true }, "node_modules/node-fetch/node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", "dev": true, - "optional": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -3855,7 +3966,10 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", - "dev": true + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/object-is": { "version": "1.1.2", @@ -3868,6 +3982,9 @@ }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object-keys": { @@ -3892,6 +4009,9 @@ }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object.assign/node_modules/es-abstract": { @@ -3915,6 +4035,9 @@ }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object.getownpropertydescriptors": { @@ -3928,6 +4051,9 @@ }, "engines": { "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/on-finished": { @@ -3961,6 +4087,9 @@ }, "engines": { "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/onigasm": { @@ -4014,6 +4143,9 @@ }, "engines": { "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { @@ -4095,6 +4227,35 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "node_modules/portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "dependencies": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/portfinder/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "node_modules/prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -4145,6 +4306,7 @@ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", "dev": true, + "hasInstallScript": true, "optional": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -4285,6 +4447,9 @@ }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/remove-trailing-separator": { @@ -4297,6 +4462,7 @@ "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", "dev": true, "dependencies": { "aws-sign2": "~0.7.0", @@ -4334,12 +4500,16 @@ }, "engines": { "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" } }, "node_modules/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==", + "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", "dev": true, "dependencies": { "request-promise-core": "1.1.4", @@ -4348,6 +4518,9 @@ }, "engines": { "node": ">=0.12.0" + }, + "peerDependencies": { + "request": "^2.34" } }, "node_modules/request-promise-native/node_modules/tough-cookie": { @@ -4389,6 +4562,7 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", "dev": true, "bin": { "uuid": "bin/uuid" @@ -4443,6 +4617,11 @@ }, "engines": { "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/retry-request/node_modules/ms": { @@ -4630,16 +4809,6 @@ "node": ">=0.10.0" } }, - "node_modules/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, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -4662,6 +4831,11 @@ "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, "engines": { "node": ">=0.10.0" } @@ -4724,6 +4898,20 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "optional": true }, "node_modules/string-width": { @@ -4747,6 +4935,9 @@ "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { @@ -4757,6 +4948,9 @@ "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/strip-ansi": { @@ -4866,25 +5060,67 @@ } }, "node_modules/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, - "dependencies": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", + "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", "arg": "^4.1.0", + "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "source-map-support": "^0.5.17", + "v8-compile-cache-lib": "^3.0.0", "yn": "3.1.1" }, "bin": { "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js", "ts-script": "dist/bin-script-deprecated.js" }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">=6.0.0" + "node": ">=0.4.0" + } + }, + "node_modules/ts-node/node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" } }, "node_modules/ts-node/node_modules/diff": { @@ -4927,6 +5163,9 @@ }, "engines": { "node": ">=4.8.0" + }, + "peerDependencies": { + "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev" } }, "node_modules/tslint-config-prettier": { @@ -4951,6 +5190,9 @@ }, "engines": { "node": ">=4" + }, + "peerDependencies": { + "tslint": ">=5.1.0" } }, "node_modules/tslint-no-unused-expression-chai/node_modules/tsutils": { @@ -4963,6 +5205,9 @@ }, "engines": { "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, "node_modules/tslint-plugin-prettier": { @@ -4977,6 +5222,10 @@ }, "engines": { "node": ">= 4" + }, + "peerDependencies": { + "prettier": "^1.9.0 || ^2.0.0", + "tslint": "^5.0.0 || ^6.0.0" } }, "node_modules/tslint/node_modules/diff": { @@ -4995,6 +5244,9 @@ "dev": true, "dependencies": { "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/tslint/node_modules/semver": { @@ -5013,6 +5265,9 @@ "dev": true, "dependencies": { "tslib": "^1.8.1" + }, + "peerDependencies": { + "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" } }, "node_modules/tunnel-agent": { @@ -5097,6 +5352,9 @@ }, "engines": { "node": ">= 12.20.0" + }, + "peerDependencies": { + "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x" } }, "node_modules/typedoc-default-themes": { @@ -5123,6 +5381,9 @@ }, "engines": { "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/typescript": { @@ -5206,6 +5467,12 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", + "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", + "dev": true + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -5424,6 +5691,18 @@ "dev": true, "engines": { "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, "node_modules/xdg-basedir": { @@ -5589,6 +5868,9 @@ }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/yargs/node_modules/cliui": { @@ -5757,6 +6039,9 @@ "optional": true, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } } }, @@ -5787,49 +6072,19 @@ "js-tokens": "^4.0.0" } }, - "@firebase/app": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.7.4.tgz", - "integrity": "sha512-XBrZb60m7N1XqmRhSJWADDD3J/0j9wM2VhxC5KUEtFA9SWfTn9Z3EWGtRGz7ahrMkgPJsmo0fXpvUh6cY8pQvQ==", - "dev": true, - "peer": true, - "requires": { - "@firebase/component": "0.5.7", - "@firebase/logger": "0.3.0", - "@firebase/util": "1.4.0", - "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true, - "peer": true - } - } + "@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true }, - "@firebase/app-compat": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.5.tgz", - "integrity": "sha512-GJURp5Nn8dEm72/y13Z+XMvWmMomsYViNxw6VKYqVH9f9VKnJ46Q8zYtx2ePvOuj7pAqsfwNkvAdAFYcveTe9g==", + "@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", "dev": true, - "peer": true, "requires": { - "@firebase/app": "0.7.4", - "@firebase/component": "0.5.7", - "@firebase/logger": "0.3.0", - "@firebase/util": "1.4.0", - "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true, - "peer": true - } + "@cspotcode/source-map-consumer": "0.8.0" } }, "@firebase/app-types": { @@ -6348,6 +6603,30 @@ "dev": true, "optional": true }, + "@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, "@types/body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", @@ -6695,6 +6974,15 @@ "integrity": "sha1-5gtrDo8wG9l+U3UhW9pAbIURjAs=", "dev": true }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, "async-retry": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz", @@ -6809,12 +7097,6 @@ "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", @@ -7053,6 +7335,12 @@ "vary": "^1" } }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "cross-spawn": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", @@ -8949,7 +9237,6 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dev": true, - "optional": true, "requires": { "whatwg-url": "^5.0.0" }, @@ -8958,22 +9245,19 @@ "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", - "dev": true, - "optional": true + "dev": true }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", - "dev": true, - "optional": true + "dev": true }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", "dev": true, - "optional": true, "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -9229,6 +9513,34 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -9678,16 +9990,6 @@ "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" - } - }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -9880,18 +10182,38 @@ } }, "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": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", + "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", "arg": "^4.1.0", + "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "source-map-support": "^0.5.17", + "v8-compile-cache-lib": "^3.0.0", "yn": "3.1.1" }, "dependencies": { + "acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -10140,6 +10462,12 @@ "dev": true, "optional": true }, + "v8-compile-cache-lib": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", + "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", + "dev": true + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -10321,7 +10649,8 @@ "version": "7.5.3", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", - "dev": true + "dev": true, + "requires": {} }, "xdg-basedir": { "version": "4.0.0", From e08a71dfe31a67685d2cb3787c53b6f3b42176a7 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Wed, 30 Mar 2022 15:39:18 -0700 Subject: [PATCH 053/370] Add identity common items for new functionality (#1027) * moving common auth items to identity.ts and fixing UserRecord definitions * fixing copyright year and exporting old artifacts * adding in common files only * breaking out parsing functions and adding a bunch of tests * clean up & formatter * adding jsdoc comments * cleaning up update mask and adding more tests * fixing projectId references * changing classes to interfaces, adding in a key cache, and more tests * formatter * decoupling decode & verify * added tests for setting the expiration time * cleaning up var names * adding in retry for key verification * format * address comments * add package-lock * change decoded JWT to decoded payload --- package-lock.json | 23 +- package.json | 4 +- spec/common/providers/identity.spec.ts | 1075 +++++++++++++++++++++++- src/common/providers/identity.ts | 1039 ++++++++++++++++++++++- 4 files changed, 2105 insertions(+), 36 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0201ae801..cc67d00f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,8 @@ "@types/express": "4.17.3", "cors": "^2.8.5", "express": "^4.17.1", - "lodash": "^4.17.14" + "lodash": "^4.17.14", + "node-fetch": "^2.6.7" }, "bin": { "firebase-functions": "lib/bin/firebase-functions.js" @@ -27,6 +28,7 @@ "@types/mock-require": "^2.0.0", "@types/nock": "^10.0.3", "@types/node": "^8.10.50", + "@types/node-fetch": "^3.0.3", "@types/sinon": "^7.0.13", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", @@ -855,6 +857,16 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.63.tgz", "integrity": "sha512-g+nSkeHFDd2WOQChfmy9SAXLywT47WZBrGS/NC5ym5PJ8c8RC6l4pbGaUW/X0+eZJnXw6/AVNEouXWhV4iz72Q==" }, + "node_modules/@types/node-fetch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-3.0.3.tgz", + "integrity": "sha512-HhggYPH5N+AQe/OmN6fmhKmRRt2XuNJow+R3pQwJxOOF9GuwM7O2mheyGeIrs5MOIeNjDEdgdoyHBOrFeJBR3g==", + "deprecated": "This is a stub types definition. node-fetch provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "node-fetch": "*" + } + }, "node_modules/@types/qs": { "version": "6.9.4", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz", @@ -6768,6 +6780,15 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.63.tgz", "integrity": "sha512-g+nSkeHFDd2WOQChfmy9SAXLywT47WZBrGS/NC5ym5PJ8c8RC6l4pbGaUW/X0+eZJnXw6/AVNEouXWhV4iz72Q==" }, + "@types/node-fetch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-3.0.3.tgz", + "integrity": "sha512-HhggYPH5N+AQe/OmN6fmhKmRRt2XuNJow+R3pQwJxOOF9GuwM7O2mheyGeIrs5MOIeNjDEdgdoyHBOrFeJBR3g==", + "dev": true, + "requires": { + "node-fetch": "*" + } + }, "@types/qs": { "version": "6.9.4", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz", diff --git a/package.json b/package.json index a478a7a91..099e5ec3f 100644 --- a/package.json +++ b/package.json @@ -157,7 +157,8 @@ "@types/express": "4.17.3", "cors": "^2.8.5", "express": "^4.17.1", - "lodash": "^4.17.14" + "lodash": "^4.17.14", + "node-fetch": "^2.6.7" }, "devDependencies": { "@types/chai": "^4.1.7", @@ -168,6 +169,7 @@ "@types/mock-require": "^2.0.0", "@types/nock": "^10.0.3", "@types/node": "^8.10.50", + "@types/node-fetch": "^3.0.3", "@types/sinon": "^7.0.13", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", diff --git a/spec/common/providers/identity.spec.ts b/spec/common/providers/identity.spec.ts index fcb686102..caa656e3e 100644 --- a/spec/common/providers/identity.spec.ts +++ b/spec/common/providers/identity.spec.ts @@ -20,10 +20,108 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -import { expect } from 'chai'; +import * as express from 'express'; +import * as jwt from 'jsonwebtoken'; +import * as sinon from 'sinon'; import * as identity from '../../../src/common/providers/identity'; +import { expect } from 'chai'; + +const PROJECT = 'my-project'; +const EVENT = 'EVENT_TYPE'; +const VALID_URL = `https://us-central1-${PROJECT}.cloudfunctions.net/function-1`; +const now = new Date(); describe('identity', () => { + describe('invalidPublicKeys', () => { + it('should return true if publicKeysExpireAt does not exist', () => { + expect( + identity.invalidPublicKeys({ + publicKeys: {}, + }) + ).to.be.true; + }); + + it('should return true if publicKeysExpireAt are less than equals Date.now() plus a buffer', () => { + const time = Date.now(); + expect( + identity.invalidPublicKeys( + { + publicKeys: {}, + publicKeysExpireAt: time, + }, + time + ) + ).to.be.true; + }); + + it('should return false if publicKeysExpireAt are greater than Date.now() plus a buffer', () => { + const time = Date.now(); + expect( + identity.invalidPublicKeys( + { + publicKeys: {}, + publicKeysExpireAt: time + identity.INVALID_TOKEN_BUFFER + 60000, + }, + time + ) + ).to.be.false; + }); + }); + + describe('setKeyExpirationTime', () => { + const time = Date.now(); + + it('should do nothing without cache-control', async () => { + const publicKeysCache = { + publicKeys: {}, + publicKeysExpireAt: undefined, + }; + const headers = new Map(); + const response = { + headers, + }; + + await identity.setKeyExpirationTime(response, publicKeysCache, time); + + expect(publicKeysCache.publicKeysExpireAt).to.be.undefined; + }); + + it('should do nothing with cache-control but without max-age', async () => { + const publicKeysCache = { + publicKeys: {}, + publicKeysExpireAt: undefined, + }; + const headers = new Map(); + headers.set('cache-control', 'item=val, item2=val2, item3=val3'); + const response = { + headers, + }; + + await identity.setKeyExpirationTime(response, publicKeysCache, time); + + expect(publicKeysCache.publicKeysExpireAt).to.be.undefined; + }); + + it('should set the correctly set the expiration time', async () => { + const publicKeysCache = { + publicKeys: {}, + publicKeysExpireAt: undefined, + }; + const headers = new Map(); + headers.set( + 'cache-control', + 'item=val, max-age=50, item2=val2, item3=val3' + ); + const response = { + headers, + }; + + await identity.setKeyExpirationTime(response, publicKeysCache, time); + + expect(publicKeysCache.publicKeysExpireAt).to.equal(time + 50 * 1000); + }); + }); + describe('userRecordConstructor', () => { it('will provide falsey values for fields that are not in raw wire data', () => { const record = identity.userRecordConstructor({ uid: '123' }); @@ -85,4 +183,979 @@ describe('identity', () => { }); }); }); + + describe('isValidRequest', () => { + it('should error on non-post', () => { + const req = ({ + method: 'GET', + header: { + 'Content-Type': 'application/json', + }, + body: { + data: { + jwt: '1.2.3', + }, + }, + } as unknown) as express.Request; + + expect(identity.isValidRequest(req)).to.be.false; + }); + + it('should error on bad Content-Type', () => { + const req = ({ + method: 'POST', + header(val: string) { + return 'text/css'; + }, + body: { + data: { + jwt: '1.2.3', + }, + }, + } as unknown) as express.Request; + + expect(identity.isValidRequest(req)).to.be.false; + }); + + it('should error without req body', () => { + const req = ({ + method: 'POST', + header(val: string) { + return 'application/json'; + }, + } as unknown) as express.Request; + + expect(identity.isValidRequest(req)).to.be.false; + }); + + it('should error without req body data', () => { + const req = ({ + method: 'POST', + header(val: string) { + return 'application/json'; + }, + body: {}, + } as unknown) as express.Request; + + expect(identity.isValidRequest(req)).to.be.false; + }); + + it('should error without req body', () => { + const req = ({ + method: 'POST', + header(val: string) { + return 'application/json'; + }, + body: { + data: {}, + }, + } as unknown) as express.Request; + + expect(identity.isValidRequest(req)).to.be.false; + }); + + it('should not error on valid request', () => { + const req = ({ + method: 'POST', + header(val: string) { + return 'application/json'; + }, + body: { + data: { + jwt: '1.2.3', + }, + }, + } as unknown) as express.Request; + + expect(identity.isValidRequest(req)).to.be.true; + }); + }); + + describe('getPublicKeyFromHeader', () => { + it('should throw if header.alg is not expected', () => { + expect(() => + identity.getPublicKeyFromHeader({ alg: 'RS128' }, {}) + ).to.throw( + `Provided JWT has incorrect algorithm. Expected ${identity.JWT_ALG} but got RS128.` + ); + }); + + it('should throw if header.kid is undefined', () => { + expect(() => + identity.getPublicKeyFromHeader({ alg: identity.JWT_ALG }, {}) + ).to.throw('JWT header missing "kid" claim.'); + }); + + it('should throw if the public keys do not have a property that matches header.kid', () => { + expect(() => + identity.getPublicKeyFromHeader( + { + alg: identity.JWT_ALG, + kid: '123456', + }, + {} + ) + ).to.throw( + 'Provided JWT has "kid" claim which does not correspond to a known public key. Most likely the JWT is expired.' + ); + }); + + it('should return the correct public key', () => { + expect( + identity.getPublicKeyFromHeader( + { + alg: identity.JWT_ALG, + kid: '123456', + }, + { + '123456': '7890', + '2468': '1357', + } + ) + ).to.eq('7890'); + }); + }); + + describe('isAuthorizedCloudFunctionURL', () => { + it('should return false on a bad gcf location', () => { + expect( + identity.isAuthorizedCloudFunctionURL( + `https://us-central1-europe-${PROJECT}.cloudfunctions.net/function-1`, + PROJECT + ) + ).to.be.false; + }); + + it('should return false on a bad project', () => { + expect( + identity.isAuthorizedCloudFunctionURL( + `https://us-central1-${PROJECT}-old.cloudfunctions.net/function-1`, + PROJECT + ) + ).to.be.false; + }); + + it('should return true on a good url', () => { + expect(identity.isAuthorizedCloudFunctionURL(VALID_URL, PROJECT)).to.be + .true; + }); + }); + + describe('checkDecodedToken', () => { + it('should throw on mismatching event types', () => { + expect(() => + identity.checkDecodedToken( + { + event_type: EVENT, + } as identity.DecodedPayload, + 'newEvent', + PROJECT + ) + ).to.throw(`Expected "newEvent" but received "${EVENT}".`); + }); + it('should throw on unauthorized function url', () => { + expect(() => + identity.checkDecodedToken( + { + aud: `fake-region-${PROJECT}.cloudfunctions.net/fn1`, + event_type: EVENT, + } as identity.DecodedPayload, + EVENT, + PROJECT + ) + ).to.throw('Provided JWT has incorrect "aud" (audience) claim.'); + }); + + it('should throw on a bad iss property', () => { + expect(() => + identity.checkDecodedToken( + { + aud: VALID_URL, + iss: `https://someissuer.com/a-project`, + event_type: EVENT, + } as identity.DecodedPayload, + EVENT, + PROJECT + ) + ).to.throw( + `Provided JWT has incorrect "iss" (issuer) claim. Expected "${identity.JWT_ISSUER}${PROJECT}" but got "https://someissuer.com/a-project".` + ); + }); + + it('should throw if sub is not a string', () => { + expect(() => + identity.checkDecodedToken( + ({ + aud: VALID_URL, + iss: `${identity.JWT_ISSUER}${PROJECT}`, + sub: { + key: 'val', + }, + event_type: EVENT, + } as unknown) as identity.DecodedPayload, + EVENT, + PROJECT + ) + ).to.throw('Provided JWT has no "sub" (subject) claim.'); + }); + + it('should throw if sub is empty', () => { + expect(() => + identity.checkDecodedToken( + { + aud: VALID_URL, + iss: `${identity.JWT_ISSUER}${PROJECT}`, + sub: '', + event_type: EVENT, + } as identity.DecodedPayload, + EVENT, + PROJECT + ) + ).to.throw('Provided JWT has no "sub" (subject) claim.'); + }); + + it('should throw if sub length is larger than 128 chars', () => { + const str = 'a'.repeat(129); + expect(() => + identity.checkDecodedToken( + { + aud: VALID_URL, + iss: `${identity.JWT_ISSUER}${PROJECT}`, + sub: str.toString(), + event_type: EVENT, + } as identity.DecodedPayload, + EVENT, + PROJECT + ) + ).to.throw( + 'Provided JWT has "sub" (subject) claim longer than 128 characters.' + ); + }); + + it('should not throw an error and set uid to sub', () => { + expect(() => { + const sub = '123456'; + const decoded = { + aud: VALID_URL, + iss: `${identity.JWT_ISSUER}${PROJECT}`, + sub: sub, + event_type: EVENT, + } as identity.DecodedPayload; + + identity.checkDecodedToken(decoded, EVENT, PROJECT); + + expect(decoded.uid).to.equal(sub); + }).to.not.throw(); + }); + }); + + describe('decodeJWT', () => { + let jwtDecodeStub: sinon.SinonStub; + + beforeEach(() => { + jwtDecodeStub = sinon + .stub(jwt, 'decode') + .throws('Unexpected call to jwt.decode'); + }); + + afterEach(() => { + sinon.verifyAndRestore(); + }); + + it('should throw HttpsError if jwt.decode errors', () => { + jwtDecodeStub.throws('An internal decode error occurred.'); + + expect(() => identity.decodeJWT('123456')).to.throw( + 'Failed to decode the JWT.' + ); + }); + + it('should error if jwt decoded returns undefined', () => { + jwtDecodeStub.returns(undefined); + + expect(() => identity.decodeJWT('123456')).to.throw( + 'The decoded JWT is not structured correctly.' + ); + }); + + it('should error if decoded jwt does not have a payload field', () => { + jwtDecodeStub.returns({ + header: { key: 'val' }, + }); + + expect(() => identity.decodeJWT('123456')).to.throw( + 'The decoded JWT is not structured correctly.' + ); + }); + + it('should return the raw decoded jwt', () => { + const decoded = { + header: { key: 'val' }, + payload: { + aud: VALID_URL, + iss: `${identity.JWT_ISSUER}${PROJECT}`, + event_type: EVENT, + }, + }; + jwtDecodeStub.returns(decoded); + + expect(identity.decodeJWT('123456')).to.deep.equal(decoded); + }); + }); + + describe('shouldVerifyJWT', () => { + /** Stub test that will fail when we eventually change the function */ + it('should return true', () => { + expect(identity.shouldVerifyJWT()).to.be.true; + }); + }); + + describe('verifyJWT', () => { + const time = Date.now(); + let jwtVerifyStub: sinon.SinonStub; + const keysCache = { + publicKeys: { + '123456': '7890', + '2468': '1357', + }, + publicKeysExpireAt: time + identity.INVALID_TOKEN_BUFFER + 10000, + }; + const rawDecodedJWT = { + header: { + alg: identity.JWT_ALG, + kid: '2468', + }, + payload: { + aud: VALID_URL, + iss: `${identity.JWT_ISSUER}${PROJECT}`, + event_type: EVENT, + }, + }; + + beforeEach(() => { + jwtVerifyStub = sinon + .stub(jwt, 'verify') + .throws('Unexpected call to jwt.verify'); + }); + + afterEach(() => { + sinon.verifyAndRestore(); + }); + + it('should error if header does not exist', () => { + const rawDecodedJwt = { payload: 'val' }; + + expect(() => + identity.verifyJWT('123456', rawDecodedJwt, keysCache, time) + ).to.throw( + 'Unable to verify JWT payload, the decoded JWT does not have a header property.' + ); + }); + + it('should return the decoded jwt', () => { + const decoded = { + aud: VALID_URL, + iss: `${identity.JWT_ISSUER}${PROJECT}`, + event_type: EVENT, + }; + jwtVerifyStub.returns(decoded); + + expect( + identity.verifyJWT('123456', rawDecodedJWT, keysCache, time) + ).to.deep.equal(decoded); + }); + }); + + describe('parseMetadata', () => { + const decodedMetadata = { + last_sign_in_time: 1476235905, + creation_time: 1476136676, + }; + const metadata = { + lastSignInTime: new Date(1476235905000).toUTCString(), + creationTime: new Date(1476136676000).toUTCString(), + }; + + it('should parse an undefined object', () => { + expect(identity.parseMetadata({})).to.deep.equal({ + creationTime: null, + lastSignInTime: null, + }); + }); + + it('should parse a decoded metadata object', () => { + const md = identity.parseMetadata(decodedMetadata); + + expect(md).to.deep.equal(metadata); + }); + }); + + describe('parseProviderData', () => { + const decodedUserInfo = { + provider_id: 'google.com', + display_name: 'John Doe', + photo_url: 'https://lh3.googleusercontent.com/1234567890/photo.jpg', + uid: '1234567890', + email: 'user@gmail.com', + }; + const userInfo = { + providerId: 'google.com', + displayName: 'John Doe', + photoURL: 'https://lh3.googleusercontent.com/1234567890/photo.jpg', + uid: '1234567890', + email: 'user@gmail.com', + phoneNumber: undefined, + }; + const decodedUserInfoPhone = { + provider_id: 'phone', + phone_number: '+11234567890', + uid: '+11234567890', + }; + const userInfoPhone = { + providerId: 'phone', + displayName: undefined, + photoURL: undefined, + uid: '+11234567890', + email: undefined, + phoneNumber: '+11234567890', + }; + + it('should parse the user info', () => { + expect(identity.parseProviderData([decodedUserInfo])).to.deep.equal([ + userInfo, + ]); + }); + + it('should parse the user info with phone', () => { + expect(identity.parseProviderData([decodedUserInfoPhone])).to.deep.equal([ + userInfoPhone, + ]); + }); + }); + + describe('parseDate', () => { + it('should return null if tokens undefined', () => { + expect(identity.parseDate()).to.be.null; + }); + + it('should parse the date', () => { + expect(identity.parseDate(1476136676)).to.equal( + new Date(1476136676000).toUTCString() + ); + }); + }); + + describe('parseMultiFactor', () => { + const decodedMultiFactors = { + enrolled_factors: [ + { + uid: 'enrollmentId1', + display_name: 'displayName1', + enrollment_time: now.toISOString(), + phone_number: '+16505551234', + }, + { + uid: 'enrollmentId2', + enrollment_time: now.toISOString(), + }, + ], + }; + const multiFactors = { + enrolledFactors: [ + { + uid: 'enrollmentId1', + displayName: 'displayName1', + enrollmentTime: now.toUTCString(), + phoneNumber: '+16505551234', + factorId: 'phone', + }, + { + uid: 'enrollmentId2', + displayName: undefined, + enrollmentTime: now.toUTCString(), + factorId: undefined, + phoneNumber: undefined, + }, + ], + }; + + it('should return null on undefined factor', () => { + expect(identity.parseMultiFactor()).to.be.null; + }); + + it('should return null without enrolled factors', () => { + expect(identity.parseMultiFactor({})).to.be.null; + }); + + it('should error on an invalid factor', () => { + const factors = { + enrolled_factors: [{} as identity.DecodedPayloadMfaInfo], + }; + + expect(() => identity.parseMultiFactor(factors)).to.throw( + 'INTERNAL ASSERT FAILED: Invalid multi-factor info response' + ); + }); + + it('should correctly parse factors', () => { + expect(identity.parseMultiFactor(decodedMultiFactors)).to.deep.equal( + multiFactors + ); + }); + }); + + describe('parseUserRecord', () => { + const decodedUserRecord = { + uid: 'abcdefghijklmnopqrstuvwxyz', + email: 'user@gmail.com', + email_verified: true, + display_name: 'John Doe', + phone_number: '+11234567890', + provider_data: [ + { + provider_id: 'google.com', + display_name: 'John Doe', + photo_url: 'https://lh3.googleusercontent.com/1234567890/photo.jpg', + email: 'user@gmail.com', + uid: '1234567890', + }, + { + provider_id: 'facebook.com', + display_name: 'John Smith', + photo_url: 'https://facebook.com/0987654321/photo.jpg', + email: 'user@facebook.com', + uid: '0987654321', + }, + { + provider_id: 'phone', + uid: '+11234567890', + phone_number: '+11234567890', + }, + { + provider_id: 'password', + email: 'user@gmail.com', + uid: 'user@gmail.com', + display_name: 'John Doe', + }, + ], + password_hash: 'passwordHash', + password_salt: 'passwordSalt', + photo_url: 'https://lh3.googleusercontent.com/1234567890/photo.jpg', + tokens_valid_after_time: 1476136676, + metadata: { + last_sign_in_time: 1476235905, + creation_time: 1476136676, + }, + custom_claims: { + admin: true, + group_id: 'group123', + }, + tenant_id: 'TENANT_ID', + multi_factor: { + enrolled_factors: [ + { + uid: 'enrollmentId1', + display_name: 'displayName1', + enrollment_time: now.toISOString(), + phone_number: '+16505551234', + factor_id: 'phone', + }, + { + uid: 'enrollmentId2', + enrollment_time: now.toISOString(), + phone_number: '+16505556789', + factor_id: 'phone', + }, + ], + }, + }; + + const userRecord = { + uid: 'abcdefghijklmnopqrstuvwxyz', + email: 'user@gmail.com', + phoneNumber: '+11234567890', + emailVerified: true, + disabled: false, + displayName: 'John Doe', + providerData: [ + { + providerId: 'google.com', + displayName: 'John Doe', + photoURL: 'https://lh3.googleusercontent.com/1234567890/photo.jpg', + email: 'user@gmail.com', + uid: '1234567890', + phoneNumber: undefined, + }, + { + providerId: 'facebook.com', + displayName: 'John Smith', + photoURL: 'https://facebook.com/0987654321/photo.jpg', + email: 'user@facebook.com', + uid: '0987654321', + phoneNumber: undefined, + }, + { + providerId: 'phone', + displayName: undefined, + photoURL: undefined, + email: undefined, + uid: '+11234567890', + phoneNumber: '+11234567890', + }, + { + providerId: 'password', + displayName: 'John Doe', + photoURL: undefined, + email: 'user@gmail.com', + uid: 'user@gmail.com', + phoneNumber: undefined, + }, + ], + passwordHash: 'passwordHash', + passwordSalt: 'passwordSalt', + photoURL: 'https://lh3.googleusercontent.com/1234567890/photo.jpg', + metadata: { + lastSignInTime: new Date(1476235905000).toUTCString(), + creationTime: new Date(1476136676000).toUTCString(), + }, + customClaims: { + admin: true, + group_id: 'group123', + }, + tokensValidAfterTime: new Date(1476136676000).toUTCString(), + tenantId: 'TENANT_ID', + multiFactor: { + enrolledFactors: [ + { + uid: 'enrollmentId1', + displayName: 'displayName1', + enrollmentTime: now.toUTCString(), + phoneNumber: '+16505551234', + factorId: 'phone', + }, + { + uid: 'enrollmentId2', + displayName: undefined, + enrollmentTime: now.toUTCString(), + phoneNumber: '+16505556789', + factorId: 'phone', + }, + ], + }, + }; + + it('should error if decoded does not have uid', () => { + expect(() => + identity.parseAuthUserRecord({} as identity.DecodedPayloadUserRecord) + ).to.throw('INTERNAL ASSERT FAILED: Invalid user response'); + }); + + it('should parse user record', () => { + const ur = identity.parseAuthUserRecord(decodedUserRecord); + + expect(ur).to.deep.equal(userRecord); + }); + }); + + describe('parseAuthEventContext', () => { + const rawUserInfo = { + name: 'John Doe', + granted_scopes: + 'openid https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile', + id: '123456789', + verified_email: true, + given_name: 'John', + locale: 'en', + family_name: 'Doe', + email: 'johndoe@gmail.com', + picture: 'https://lh3.googleusercontent.com/1233456789/mo/photo.jpg', + }; + + it('should parse an unknown event', () => { + const decodedJwt = { + aud: 'https://us-east1-project_id.cloudfunctions.net/function-1', + exp: 60 * 60 + 1, + iat: 1, + iss: 'https://securetoken.google.com/project_id', + sub: 'someUid', + uid: 'someUid', + event_id: 'EVENT_ID', + event_type: EVENT, + ip_address: '1.2.3.4', + user_agent: 'USER_AGENT', + locale: 'en', + raw_user_info: JSON.stringify(rawUserInfo), + }; + const context = { + locale: 'en', + ipAddress: '1.2.3.4', + userAgent: 'USER_AGENT', + eventId: 'EVENT_ID', + eventType: EVENT, + authType: 'UNAUTHENTICATED', + resource: { + service: 'identitytoolkit.googleapis.com', + name: 'projects/project-id', + }, + timestamp: new Date(1000).toUTCString(), + additionalUserInfo: { + providerId: undefined, + profile: rawUserInfo, + username: undefined, + isNewUser: false, + }, + credential: null, + params: {}, + }; + + expect( + identity.parseAuthEventContext(decodedJwt, 'project-id') + ).to.deep.equal(context); + }); + + it('should parse a beforeSignIn event', () => { + const time = now.getTime(); + const decodedJwt = { + aud: 'https://us-east1-project_id.cloudfunctions.net/function-1', + exp: 60 * 60 + 1, + iat: 1, + iss: 'https://securetoken.google.com/project_id', + sub: 'someUid', + uid: 'someUid', + event_id: 'EVENT_ID', + event_type: 'beforeSignIn', + ip_address: '1.2.3.4', + user_agent: 'USER_AGENT', + locale: 'en', + sign_in_method: 'password', + raw_user_info: JSON.stringify(rawUserInfo), + oauth_id_token: 'ID_TOKEN', + oauth_access_token: 'ACCESS_TOKEN', + oauth_refresh_token: 'REFRESH_TOKEN', + oauth_token_secret: 'OAUTH_TOKEN_SECRET', + oauth_expires_in: 3600, + }; + const context = { + locale: 'en', + ipAddress: '1.2.3.4', + userAgent: 'USER_AGENT', + eventId: 'EVENT_ID', + eventType: 'providers/cloud.auth/eventTypes/user.beforeSignIn:password', + authType: 'UNAUTHENTICATED', + resource: { + service: 'identitytoolkit.googleapis.com', + name: 'projects/project-id', + }, + timestamp: new Date(1000).toUTCString(), + additionalUserInfo: { + providerId: 'password', + profile: rawUserInfo, + username: undefined, + isNewUser: false, + }, + credential: { + claims: undefined, + idToken: 'ID_TOKEN', + accessToken: 'ACCESS_TOKEN', + refreshToken: 'REFRESH_TOKEN', + expirationTime: new Date(time + 3600 * 1000).toUTCString(), + secret: 'OAUTH_TOKEN_SECRET', + providerId: 'password', + signInMethod: 'password', + }, + params: {}, + }; + + expect( + identity.parseAuthEventContext(decodedJwt, 'project-id', time) + ).to.deep.equal(context); + }); + + it('should parse a beforeCreate event', () => { + const time = now.getTime(); + // beforeCreate + const decodedJwt = { + aud: 'https://us-east1-project_id.cloudfunctions.net/beforeCreate', + exp: 60 * 60 + 1, + iat: 1, + iss: 'https://securetoken.google.com/project_id', + sub: 'abcdefghijklmnopqrstuvwxyz', + uid: 'abcdefghijklmnopqrstuvwxyz', + event_id: 'EVENT_ID', + event_type: 'beforeCreate', + ip_address: '1.2.3.4', + user_agent: 'USER_AGENT', + locale: 'en', + sign_in_method: 'oidc.provider', + tenant_id: 'TENANT_ID', + user_record: { + uid: 'abcdefghijklmnopqrstuvwxyz', + email: 'user@gmail.com', + email_verified: true, + display_name: 'John Doe', + phone_number: '+11234567890', + provider_data: [ + { + provider_id: 'oidc.provider', + email: 'user@gmail.com', + uid: 'user@gmail.com', + display_name: 'John Doe', + }, + ], + photo_url: 'https://lh3.googleusercontent.com/1234567890/photo.jpg', + tokens_valid_after_time: 1476136676, + metadata: { + last_sign_in_time: 1476235905, + creation_time: 1476136676, + }, + custom_claims: { + admin: true, + group_id: 'group123', + }, + tenant_id: 'TENANT_ID', + }, + oauth_id_token: 'ID_TOKEN', + oauth_access_token: 'ACCESS_TOKEN', + oauth_refresh_token: 'REFRESH_TOKEN', + oauth_token_secret: 'OAUTH_TOKEN_SECRET', + oauth_expires_in: 3600, + raw_user_info: JSON.stringify(rawUserInfo), + }; + const context = { + locale: 'en', + ipAddress: '1.2.3.4', + userAgent: 'USER_AGENT', + eventId: 'EVENT_ID', + eventType: + 'providers/cloud.auth/eventTypes/user.beforeCreate:oidc.provider', + authType: 'USER', + resource: { + service: 'identitytoolkit.googleapis.com', + name: 'projects/project-id/tenants/TENANT_ID', + }, + timestamp: new Date(1000).toUTCString(), + additionalUserInfo: { + username: undefined, + providerId: 'oidc.provider', + profile: rawUserInfo, + isNewUser: true, + }, + credential: { + claims: undefined, + accessToken: 'ACCESS_TOKEN', + expirationTime: new Date(time + 3600 * 1000).toUTCString(), + idToken: 'ID_TOKEN', + providerId: 'oidc.provider', + refreshToken: 'REFRESH_TOKEN', + secret: 'OAUTH_TOKEN_SECRET', + signInMethod: 'oidc.provider', + }, + params: {}, + }; + + expect( + identity.parseAuthEventContext(decodedJwt, 'project-id', time) + ).to.deep.equal(context); + }); + }); + + describe('validateAuthResponse', () => { + it('should not throw on undefined request', () => { + expect(() => identity.validateAuthResponse('event', undefined)).to.not + .throw; + }); + + it('should throw an error if customClaims have a blocked claim', () => { + expect(() => + identity.validateAuthResponse('beforeCreate', { + customClaims: { acr: 'something' }, + }) + ).to.throw( + 'The customClaims claims "acr" are reserved and cannot be specified.' + ); + }); + + it('should throw an error if customClaims size is too big', () => { + const str = 'x'.repeat(1000); + + expect(() => + identity.validateAuthResponse('beforeCreate', { + customClaims: { idk: str }, + }) + ).to.throw('The customClaims payload should not exceed 1000 characters.'); + }); + + it('should throw an error if sessionClaims have a blocked claim', () => { + expect(() => + identity.validateAuthResponse('beforeSignIn', { + sessionClaims: { acr: 'something' }, + }) + ).to.throw( + 'The sessionClaims claims "acr" are reserved and cannot be specified.' + ); + }); + + it('should throw an error if sessionClaims size is too big', () => { + const str = 'x'.repeat(1000); + + expect(() => + identity.validateAuthResponse('beforeSignIn', { + sessionClaims: { idk: str }, + }) + ).to.throw( + 'The sessionClaims payload should not exceed 1000 characters.' + ); + }); + + it('should throw an error if the combined customClaims & sessionClaims size is too big', () => { + const str = 'x'.repeat(501); + + expect(() => + identity.validateAuthResponse('beforeSignIn', { + customClaims: { cc: str }, + sessionClaims: { sc: str }, + }) + ).to.throw( + 'The customClaims and sessionClaims payloads should not exceed 1000 characters combined.' + ); + }); + }); + + describe('getUpdateMask', () => { + it('should return empty string on undefined response', () => { + expect(identity.getUpdateMask()).to.eq(''); + }); + + it('should return empty on only customClaims and sessionClaims', () => { + const response = { + customClaims: { + claim1: 'abc', + }, + sessionClaims: { + claim2: 'def', + }, + }; + + expect(identity.getUpdateMask(response)).to.eq(''); + }); + + it('should return the right claims on a response', () => { + const response = { + displayName: 'john', + disabled: false, + emailVerified: true, + photoURL: 'google.com', + customClaims: { + claim1: 'abc', + }, + sessionClaims: { + claim2: 'def', + }, + }; + + expect(identity.getUpdateMask(response)).to.eq( + 'displayName,disabled,emailVerified,photoURL' + ); + }); + }); }); diff --git a/src/common/providers/identity.ts b/src/common/providers/identity.ts index 24a4e2c34..f3358f02e 100644 --- a/src/common/providers/identity.ts +++ b/src/common/providers/identity.ts @@ -20,8 +20,60 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +import * as express from 'express'; import * as firebase from 'firebase-admin'; -import * as _ from 'lodash'; +import * as jwt from 'jsonwebtoken'; +import fetch from 'node-fetch'; +import { HttpsError } from './https'; +import { EventContext } from '../../cloud-functions'; +import { SUPPORTED_REGIONS } from '../../function-configuration'; +import { logger } from '../..'; + +export { HttpsError }; + +/** @internal */ +export const INVALID_TOKEN_BUFFER = 60000; // set to 1 minute + +/** @internal */ +export const JWT_CLIENT_CERT_URL = 'https://www.googleapis.com'; +/** @internal */ +export const JWT_CLIENT_CERT_PATH = + 'robot/v1/metadata/x509/securetoken@system.gserviceaccount.com'; +/** @internal */ +export const JWT_ALG = 'RS256'; +/** @internal */ +export const JWT_ISSUER = 'https://securetoken.google.com/'; + +/** @internal */ +export interface PublicKeysCache { + publicKeys: Record; + publicKeysExpireAt?: number; +} + +const DISALLOWED_CUSTOM_CLAIMS = [ + 'acr', + 'amr', + 'at_hash', + 'aud', + 'auth_time', + 'azp', + 'cnf', + 'c_hash', + 'exp', + 'iat', + 'iss', + 'jti', + 'nbf', + 'nonce', + 'firebase', +]; + +const CLAIMS_MAX_PAYLOAD_SIZE = 1000; + +const EVENT_MAPPING: Record = { + beforeCreate: 'providers/cloud.auth/eventTypes/user.beforeCreate', + beforeSignIn: 'providers/cloud.auth/eventTypes/user.beforeSignIn', +}; /** * The UserRecord passed to Cloud Functions is the same UserRecord that is returned by the Firebase Admin @@ -41,7 +93,7 @@ export class UserRecordMetadata implements firebase.auth.UserMetadata { constructor(public creationTime: string, public lastSignInTime: string) {} /** Returns a plain JavaScript object with the properties of UserRecordMetadata. */ - toJSON() { + toJSON(): AuthUserMetadata { return { creationTime: this.creationTime, lastSignInTime: this.lastSignInTime, @@ -69,43 +121,964 @@ export function userRecordConstructor(wireData: Object): UserRecord { passwordHash: null, tokensValidAfterTime: null, }; - const record = _.assign({}, falseyValues, wireData); + const record = { ...falseyValues, ...wireData }; - const meta = _.get(record, 'metadata'); + const meta = record['metadata']; if (meta) { - _.set( - record, - 'metadata', - new UserRecordMetadata( - meta.createdAt || meta.creationTime, - meta.lastSignedInAt || meta.lastSignInTime - ) + record['metadata'] = new UserRecordMetadata( + meta.createdAt || meta.creationTime, + meta.lastSignedInAt || meta.lastSignInTime ); } else { - _.set(record, 'metadata', new UserRecordMetadata(null, null)); + record['metadata'] = new UserRecordMetadata(null, null); } - _.forEach(record.providerData, (entry) => { - _.set(entry, 'toJSON', () => { + for (const entry of Object.entries(record.providerData)) { + entry['toJSON'] = () => { return entry; - }); - }); - _.set(record, 'toJSON', () => { - const json: any = _.pick(record, [ - 'uid', - 'email', - 'emailVerified', - 'displayName', - 'photoURL', - 'phoneNumber', - 'disabled', - 'passwordHash', - 'passwordSalt', - 'tokensValidAfterTime', - ]); - json.metadata = _.get(record, 'metadata').toJSON(); - json.customClaims = _.cloneDeep(record.customClaims); - json.providerData = _.map(record.providerData, (entry) => entry.toJSON()); + }; + } + record['toJSON'] = () => { + const { + uid, + email, + emailVerified, + displayName, + photoURL, + phoneNumber, + disabled, + passwordHash, + passwordSalt, + tokensValidAfterTime, + } = record; + const json = { + uid, + email, + emailVerified, + displayName, + photoURL, + phoneNumber, + disabled, + passwordHash, + passwordSalt, + tokensValidAfterTime, + }; + json['metadata'] = record['metadata'].toJSON(); + json['customClaims'] = JSON.parse(JSON.stringify(record.customClaims)); + json['providerData'] = record.providerData.map((entry) => entry.toJSON()); return json; - }); + }; return record as UserRecord; } + +/** + * User info that is part of the AuthUserRecord + */ +export interface AuthUserInfo { + /** + * The user identifier for the linked provider. + */ + uid: string; + /** + * The display name for the linked provider. + */ + displayName: string; + /** + * The email for the linked provider. + */ + email: string; + /** + * The photo URL for the linked provider. + */ + photoURL: string; + /** + * The linked provider ID (for example, "google.com" for the Google provider). + */ + providerId: string; + /** + * The phone number for the linked provider. + */ + phoneNumber: string; +} + +/** + * Additional metadata about the user. + */ +export interface AuthUserMetadata { + /** + * The date the user was created, formatted as a UTC string. + */ + creationTime: string; + /** + * The date the user last signed in, formatted as a UTC string. + */ + lastSignInTime: string; +} + +/** + * Interface representing the common properties of a user-enrolled second factor. + */ +export interface AuthMultiFactorInfo { + /** + * The ID of the enrolled second factor. This ID is unique to the user. + */ + uid: string; + /** + * The optional display name of the enrolled second factor. + */ + displayName?: string; + /** + * The type identifier of the second factor. For SMS second factors, this is `phone`. + */ + factorId: string; + /** + * The optional date the second factor was enrolled, formatted as a UTC string. + */ + enrollmentTime?: string; + /** + * The phone number associated with a phone second factor. + */ + phoneNumber?: string; +} + +/** + * The multi-factor related properties for the current user, if available. + */ +export interface AuthMultiFactorSettings { + /** + * List of second factors enrolled with the current user. + */ + enrolledFactors: AuthMultiFactorInfo[]; +} + +/** + * The UserRecord passed to auth blocking Cloud Functions from the identity platform. + */ +export interface AuthUserRecord { + /** + * The user's `uid`. + */ + uid: string; + /** + * The user's primary email, if set. + */ + email?: string; + /** + * Whether or not the user's primary email is verified. + */ + emailVerified: boolean; + /** + * The user's display name. + */ + displayName?: string; + /** + * The user's photo URL. + */ + photoURL?: string; + /** + * The user's primary phone number, if set. + */ + phoneNumber?: string; + /** + * Whether or not the user is disabled: `true` for disabled; `false` for + * enabled. + */ + disabled: boolean; + /** + * Additional metadata about the user. + */ + metadata: AuthUserMetadata; + /** + * An array of providers (for example, Google, Facebook) linked to the user. + */ + providerData: AuthUserInfo[]; + /** + * The user's hashed password (base64-encoded). + */ + passwordHash?: string; + /** + * The user's password salt (base64-encoded). + */ + passwordSalt?: string; + /** + * The user's custom claims object if available, typically used to define + * user roles and propagated to an authenticated user's ID token. + */ + customClaims?: Record; + /** + * The ID of the tenant the user belongs to, if available. + */ + tenantId?: string | null; + /** + * The date the user's tokens are valid after, formatted as a UTC string. + */ + tokensValidAfterTime?: string; + /** + * The multi-factor related properties for the current user, if available. + */ + multiFactor?: AuthMultiFactorSettings; +} + +/** The additional user info component of the auth event context */ +export interface AdditionalUserInfo { + providerId: string; + profile?: any; + username?: string; + isNewUser: boolean; +} + +/** The credential component of the auth event context */ +export interface Credential { + claims?: { [key: string]: any }; + idToken?: string; + accessToken?: string; + refreshToken?: string; + expirationTime?: string; + secret?: string; + providerId: string; + signInMethod: string; +} + +/** Defines the auth event context for blocking events */ +export interface AuthEventContext extends EventContext { + locale?: string; + ipAddress: string; + userAgent: string; + additionalUserInfo?: AdditionalUserInfo; + credential?: Credential; +} + +/** The handler response type for beforeCreate blocking events */ +export interface BeforeCreateResponse { + displayName?: string; + disabled?: boolean; + emailVerified?: boolean; + photoURL?: string; + customClaims?: object; +} + +/** The handler response type for beforeSignIn blocking events */ +export interface BeforeSignInResponse extends BeforeCreateResponse { + sessionClaims?: object; +} + +interface DecodedPayloadUserRecordMetadata { + creation_time?: number; + last_sign_in_time?: number; +} + +interface DecodedPayloadUserRecordUserInfo { + uid: string; + display_name?: string; + email?: string; + photo_url?: string; + phone_number?: string; + provider_id: string; +} + +/** @internal */ +export interface DecodedPayloadMfaInfo { + uid: string; + display_name?: string; + phone_number?: string; + enrollment_time?: string; + factor_id?: string; +} + +interface DecodedPayloadUserRecordEnrolledFactors { + enrolled_factors?: DecodedPayloadMfaInfo[]; +} + +/** @internal */ +export interface DecodedPayloadUserRecord { + uid: string; + email?: string; + email_verified?: boolean; + phone_number?: string; + display_name?: string; + photo_url?: string; + disabled?: boolean; + metadata?: DecodedPayloadUserRecordMetadata; + password_hash?: string; + password_salt?: string; + provider_data?: DecodedPayloadUserRecordUserInfo[]; + multi_factor?: DecodedPayloadUserRecordEnrolledFactors; + custom_claims?: any; + tokens_valid_after_time?: number; + tenant_id?: string; + [key: string]: any; +} + +/** @internal */ +export interface DecodedPayload { + aud: string; + exp: number; + iat: number; + iss: string; + sub: string; + event_id: string; + event_type: string; + ip_address: string; + user_agent?: string; + locale?: string; + sign_in_method?: string; + user_record?: DecodedPayloadUserRecord; + tenant_id?: string; + raw_user_info?: string; + sign_in_attributes?: { + [key: string]: any; + }; + oauth_id_token?: string; + oauth_access_token?: string; + oauth_refresh_token?: string; + oauth_token_secret?: string; + oauth_expires_in?: number; + [key: string]: any; +} + +/** + * Helper to determine if we refresh the public keys + * @internal + */ +export function invalidPublicKeys( + keys: PublicKeysCache, + time: number = Date.now() +): boolean { + if (!keys.publicKeysExpireAt) { + return true; + } + return time + INVALID_TOKEN_BUFFER >= keys.publicKeysExpireAt; +} + +/** + * Helper to parse the response headers to obtain the expiration time. + * @internal + */ +export function setKeyExpirationTime( + response: any, + keysCache: PublicKeysCache, + time: number +): void { + if (response.headers.has('cache-control')) { + const ccHeader = response.headers.get('cache-control'); + const maxAgeEntry = ccHeader + .split(', ') + .find((item) => item.includes('max-age')); + if (maxAgeEntry) { + const maxAge = +maxAgeEntry.trim().split('=')[1]; + keysCache.publicKeysExpireAt = time + maxAge * 1000; + } + } +} + +/** + * Fetch the public keys for use in decoding and verifying the jwt sent from identity platform. + */ +async function refreshPublicKeys( + keysCache: PublicKeysCache, + time: number = Date.now() +): Promise { + const url = `${JWT_CLIENT_CERT_URL}/${JWT_CLIENT_CERT_PATH}`; + try { + const response = await fetch(url); + setKeyExpirationTime(response, keysCache, time); + const data = await response.json(); + keysCache.publicKeys = data as Record; + } catch (err) { + logger.error( + `Failed to obtain public keys for JWT verification: ${err.message}` + ); + throw new HttpsError( + 'internal', + 'Failed to obtain the public keys for JWT verification.' + ); + } +} + +/** + * Checks for a valid identity platform web request, otherwise throws an HttpsError + * @internal + */ +export function isValidRequest(req: express.Request): boolean { + if (req.method !== 'POST') { + logger.warn(`Request has invalid method "${req.method}".`); + return false; + } + + const contentType: string = (req.header('Content-Type') || '').toLowerCase(); + if (!contentType.includes('application/json')) { + logger.warn('Request has invalid header Content-Type.'); + return false; + } + + if (!req.body?.data?.jwt) { + logger.warn('Request has an invalid body.'); + return false; + } + return true; +} + +/** @internal */ +export function getPublicKeyFromHeader( + header: Record, + publicKeys: Record +): string { + if (header.alg !== JWT_ALG) { + throw new HttpsError( + 'invalid-argument', + `Provided JWT has incorrect algorithm. Expected ${JWT_ALG} but got ${header.alg}.` + ); + } + if (!header.kid) { + throw new HttpsError('invalid-argument', 'JWT header missing "kid" claim.'); + } + if (!publicKeys.hasOwnProperty(header.kid)) { + throw new HttpsError( + 'invalid-argument', + 'Provided JWT has "kid" claim which does not correspond to a known public key. Most likely the JWT is expired.' + ); + } + + return publicKeys[header.kid]; +} + +/** + * Checks for a well forms cloud functions url + * @internal + */ +export function isAuthorizedCloudFunctionURL( + cloudFunctionUrl: string, + projectId: string +): boolean { + const re = new RegExp( + `^https://(${SUPPORTED_REGIONS.join( + '|' + )})+-${projectId}\.cloudfunctions\.net/` + ); + const res = re.exec(cloudFunctionUrl) || []; + return res.length > 0; +} + +/** + * Checks for errors in a decoded jwt + * @internal + */ +export function checkDecodedToken( + decodedJWT: DecodedPayload, + eventType: string, + projectId: string +): void { + if (decodedJWT.event_type !== eventType) { + throw new HttpsError( + 'invalid-argument', + `Expected "${eventType}" but received "${decodedJWT.event_type}".` + ); + } + if (!isAuthorizedCloudFunctionURL(decodedJWT.aud, projectId)) { + throw new HttpsError( + 'invalid-argument', + 'Provided JWT has incorrect "aud" (audience) claim.' + ); + } + if (decodedJWT.iss !== `${JWT_ISSUER}${projectId}`) { + throw new HttpsError( + 'invalid-argument', + `Provided JWT has incorrect "iss" (issuer) claim. Expected ` + + `"${JWT_ISSUER}${projectId}" but got "${decodedJWT.iss}".` + ); + } + if (typeof decodedJWT.sub !== 'string' || decodedJWT.sub.length === 0) { + throw new HttpsError( + 'invalid-argument', + 'Provided JWT has no "sub" (subject) claim.' + ); + } + if (decodedJWT.sub.length > 128) { + throw new HttpsError( + 'invalid-argument', + 'Provided JWT has "sub" (subject) claim longer than 128 characters.' + ); + } + // set uid to sub + decodedJWT.uid = decodedJWT.sub; +} + +/** + * Helper function to decode the jwt, internally uses the 'jsonwebtoken' package. + * @internal + */ +export function decodeJWT(token: string): Record { + let decoded: Record; + try { + decoded = jwt.decode(token, { complete: true }) as Record; + } catch (err) { + logger.error('Decoding the JWT failed', err); + throw new HttpsError('internal', 'Failed to decode the JWT.'); + } + if (!decoded?.payload) { + throw new HttpsError( + 'internal', + 'The decoded JWT is not structured correctly.' + ); + } + return decoded; +} + +/** + * Helper function to determine if we need to do full verification of the jwt + * @internal + */ +export function shouldVerifyJWT(): boolean { + // TODO(colerogers): add emulator support to skip verification + return true; +} + +/** + * Verifies the jwt using the 'jwt' library and decodes the token with the public keys + * Throws an error if the event types do not match + * @internal + */ +export function verifyJWT( + token: string, + rawDecodedJWT: Record, + keysCache: PublicKeysCache, + time: number = Date.now() +): DecodedPayload { + if (!rawDecodedJWT.header) { + throw new HttpsError( + 'internal', + 'Unable to verify JWT payload, the decoded JWT does not have a header property.' + ); + } + const header = rawDecodedJWT.header; + let publicKey; + try { + if (invalidPublicKeys(keysCache, time)) { + refreshPublicKeys(keysCache); + } + publicKey = getPublicKeyFromHeader(header, keysCache.publicKeys); + return jwt.verify(token, publicKey, { + algorithms: [JWT_ALG], + }) as DecodedPayload; + } catch (err) { + logger.error('Verifying the JWT failed', err); + } + // force refresh keys and retry one more time + try { + refreshPublicKeys(keysCache); + publicKey = getPublicKeyFromHeader(header, keysCache.publicKeys); + return jwt.verify(token, publicKey, { + algorithms: [JWT_ALG], + }) as DecodedPayload; + } catch (err) { + logger.error('Verifying the JWT failed again', err); + throw new HttpsError('internal', 'Failed to verify the JWT.'); + } +} + +/** + * Helper function to parse the decoded metadata object into a UserMetaData object + * @internal + */ +export function parseMetadata( + metadata: DecodedPayloadUserRecordMetadata +): AuthUserMetadata { + const creationTime = metadata?.creation_time + ? new Date((metadata.creation_time as number) * 1000).toUTCString() + : null; + const lastSignInTime = metadata?.last_sign_in_time + ? new Date((metadata.last_sign_in_time as number) * 1000).toUTCString() + : null; + return { + creationTime, + lastSignInTime, + }; +} + +/** + * Helper function to parse the decoded user info array into an AuthUserInfo array + * @internal + */ +export function parseProviderData( + providerData: DecodedPayloadUserRecordUserInfo[] +): AuthUserInfo[] { + const providers: AuthUserInfo[] = []; + for (const provider of providerData) { + providers.push({ + uid: provider.uid, + displayName: provider.display_name, + email: provider.email, + photoURL: provider.photo_url, + providerId: provider.provider_id, + phoneNumber: provider.phone_number, + }); + } + return providers; +} + +/** + * Helper function to parse the date into a UTC string + * @internal + */ +export function parseDate(tokensValidAfterTime?: number): string | null { + if (!tokensValidAfterTime) { + return null; + } + tokensValidAfterTime = tokensValidAfterTime * 1000; + try { + const date = new Date(tokensValidAfterTime); + if (!isNaN(date.getTime())) { + return date.toUTCString(); + } + } catch {} + return null; +} + +/** + * Helper function to parse the decoded enrolled factors into a valid MultiFactorSettings + * @internal + */ +export function parseMultiFactor( + multiFactor?: DecodedPayloadUserRecordEnrolledFactors +): AuthMultiFactorSettings { + if (!multiFactor) { + return null; + } + const parsedEnrolledFactors: AuthMultiFactorInfo[] = []; + for (const factor of multiFactor.enrolled_factors || []) { + if (!factor.uid) { + throw new HttpsError( + 'internal', + 'INTERNAL ASSERT FAILED: Invalid multi-factor info response' + ); + } + const enrollmentTime = factor.enrollment_time + ? new Date(factor.enrollment_time).toUTCString() + : null; + parsedEnrolledFactors.push({ + uid: factor.uid, + factorId: factor.phone_number + ? factor.factor_id || 'phone' + : factor.factor_id, + displayName: factor.display_name, + enrollmentTime, + phoneNumber: factor.phone_number, + }); + } + + if (parsedEnrolledFactors.length > 0) { + return { + enrolledFactors: parsedEnrolledFactors, + }; + } + return null; +} + +/** + * Parses the decoded user record into a valid UserRecord for use in the handler + * @internal + */ +export function parseAuthUserRecord( + decodedJWTUserRecord: DecodedPayloadUserRecord +): AuthUserRecord { + if (!decodedJWTUserRecord.uid) { + throw new HttpsError( + 'internal', + 'INTERNAL ASSERT FAILED: Invalid user response' + ); + } + + const disabled = decodedJWTUserRecord.disabled || false; + const metadata = parseMetadata(decodedJWTUserRecord.metadata); + const providerData = parseProviderData(decodedJWTUserRecord.provider_data); + const tokensValidAfterTime = parseDate( + decodedJWTUserRecord.tokens_valid_after_time + ); + const multiFactor = parseMultiFactor(decodedJWTUserRecord.multi_factor); + + return { + uid: decodedJWTUserRecord.uid, + email: decodedJWTUserRecord.email, + emailVerified: decodedJWTUserRecord.email_verified, + displayName: decodedJWTUserRecord.display_name, + photoURL: decodedJWTUserRecord.photo_url, + phoneNumber: decodedJWTUserRecord.phone_number, + disabled, + metadata, + providerData, + passwordHash: decodedJWTUserRecord.password_hash, + passwordSalt: decodedJWTUserRecord.password_salt, + customClaims: decodedJWTUserRecord.custom_claims, + tenantId: decodedJWTUserRecord.tenant_id, + tokensValidAfterTime, + multiFactor, + }; +} + +/** Helper to get the AdditionalUserInfo from the decoded jwt */ +function parseAdditionalUserInfo( + decodedJWT: DecodedPayload +): AdditionalUserInfo { + let profile, username; + if (decodedJWT.raw_user_info) + try { + profile = JSON.parse(decodedJWT.raw_user_info); + } catch (err) { + logger.debug(`Parse Error: ${err.message}`); + } + if (profile) { + if (decodedJWT.sign_in_method === 'github.com') { + username = profile.login; + } + if (decodedJWT.sign_in_method === 'twitter.com') { + username = profile.screen_name; + } + } + + return { + providerId: + decodedJWT.sign_in_method === 'emailLink' + ? 'password' + : decodedJWT.sign_in_method, + profile, + username, + isNewUser: decodedJWT.event_type === 'beforeCreate' ? true : false, + }; +} + +/** Helper to get the Credential from the decoded jwt */ +function parseAuthCredential( + decodedJWT: DecodedPayload, + time: number +): Credential { + if ( + !decodedJWT.sign_in_attributes && + !decodedJWT.oauth_id_token && + !decodedJWT.oauth_access_token && + !decodedJWT.oauth_refresh_token + ) { + return null; + } + return { + claims: decodedJWT.sign_in_attributes, + idToken: decodedJWT.oauth_id_token, + accessToken: decodedJWT.oauth_access_token, + refreshToken: decodedJWT.oauth_refresh_token, + expirationTime: decodedJWT.oauth_expires_in + ? new Date(time + decodedJWT.oauth_expires_in * 1000).toUTCString() + : undefined, + secret: decodedJWT.oauth_token_secret, + providerId: + decodedJWT.sign_in_method === 'emailLink' + ? 'password' + : decodedJWT.sign_in_method, + signInMethod: decodedJWT.sign_in_method, + }; +} + +/** + * Parses the decoded jwt into a valid AuthEventContext for use in the handler + * @internal + */ +export function parseAuthEventContext( + decodedJWT: DecodedPayload, + projectId: string, + time: number = new Date().getTime() +): AuthEventContext { + const eventType = + (EVENT_MAPPING[decodedJWT.event_type] || decodedJWT.event_type) + + (decodedJWT.sign_in_method ? `:${decodedJWT.sign_in_method}` : ''); + + return { + locale: decodedJWT.locale, + ipAddress: decodedJWT.ip_address, + userAgent: decodedJWT.user_agent, + eventId: decodedJWT.event_id, + eventType, + authType: !!decodedJWT.user_record ? 'USER' : 'UNAUTHENTICATED', + resource: { + // TODO(colerogers): figure out the correct service + service: 'identitytoolkit.googleapis.com', + name: !!decodedJWT.tenant_id + ? `projects/${projectId}/tenants/${decodedJWT.tenant_id}` + : `projects/${projectId}`, + }, + timestamp: new Date(decodedJWT.iat * 1000).toUTCString(), + additionalUserInfo: parseAdditionalUserInfo(decodedJWT), + credential: parseAuthCredential(decodedJWT, time), + params: {}, + }; +} + +/** + * Checks the handler response for invalid customClaims & sessionClaims objects + * @internal + */ +export function validateAuthResponse( + eventType: string, + authRequest?: BeforeCreateResponse | BeforeSignInResponse +) { + if (!authRequest) { + authRequest = {}; + } + if (authRequest.customClaims) { + const invalidClaims = DISALLOWED_CUSTOM_CLAIMS.filter((claim) => + authRequest.customClaims.hasOwnProperty(claim) + ); + if (invalidClaims.length > 0) { + throw new HttpsError( + 'invalid-argument', + `The customClaims claims "${invalidClaims.join( + ',' + )}" are reserved and cannot be specified.` + ); + } + if ( + JSON.stringify(authRequest.customClaims).length > CLAIMS_MAX_PAYLOAD_SIZE + ) { + throw new HttpsError( + 'invalid-argument', + `The customClaims payload should not exceed ${CLAIMS_MAX_PAYLOAD_SIZE} characters.` + ); + } + } + if ( + eventType === 'beforeSignIn' && + (authRequest as BeforeSignInResponse).sessionClaims + ) { + const invalidClaims = DISALLOWED_CUSTOM_CLAIMS.filter((claim) => + (authRequest as BeforeSignInResponse).sessionClaims.hasOwnProperty(claim) + ); + if (invalidClaims.length > 0) { + throw new HttpsError( + 'invalid-argument', + `The sessionClaims claims "${invalidClaims.join( + ',' + )}" are reserved and cannot be specified.` + ); + } + if ( + JSON.stringify((authRequest as BeforeSignInResponse).sessionClaims) + .length > CLAIMS_MAX_PAYLOAD_SIZE + ) { + throw new HttpsError( + 'invalid-argument', + `The sessionClaims payload should not exceed ${CLAIMS_MAX_PAYLOAD_SIZE} characters.` + ); + } + const combinedClaims = { + ...authRequest.customClaims, + ...(authRequest as BeforeSignInResponse).sessionClaims, + }; + if (JSON.stringify(combinedClaims).length > CLAIMS_MAX_PAYLOAD_SIZE) { + throw new HttpsError( + 'invalid-argument', + `The customClaims and sessionClaims payloads should not exceed ${CLAIMS_MAX_PAYLOAD_SIZE} characters combined.` + ); + } + } +} + +/** + * Helper function to generate the update mask for the identity platform changed values + * @internal + */ +export function getUpdateMask( + authResponse?: BeforeCreateResponse | BeforeSignInResponse +): string { + if (!authResponse) { + return ''; + } + const updateMask: string[] = []; + for (const key in authResponse) { + if (key === 'customClaims' || key === 'sessionClaims') { + continue; + } + if ( + authResponse.hasOwnProperty(key) && + typeof authResponse[key] !== 'undefined' + ) { + updateMask.push(key); + } + } + return updateMask.join(','); +} + +/** @internal */ +export function createHandler( + handler: ( + user: AuthUserRecord, + context: AuthEventContext + ) => + | BeforeCreateResponse + | Promise + | BeforeSignInResponse + | Promise + | void + | Promise, + eventType: string, + keysCache: PublicKeysCache +): (req: express.Request, resp: express.Response) => Promise { + const wrappedHandler = wrapHandler(handler, eventType, keysCache); + return (req: express.Request, res: express.Response) => { + return new Promise((resolve) => { + res.on('finish', resolve); + resolve(wrappedHandler(req, res)); + }); + }; +} + +function wrapHandler( + handler: ( + user: AuthUserRecord, + context: AuthEventContext + ) => + | BeforeCreateResponse + | Promise + | BeforeSignInResponse + | Promise + | void + | Promise, + eventType: string, + keysCache: PublicKeysCache +) { + return async (req: express.Request, res: express.Response): Promise => { + try { + const projectId = process.env.GCLOUD_PROJECT; + if (!isValidRequest(req)) { + logger.error('Invalid request, unable to process'); + throw new HttpsError('invalid-argument', 'Bad Request'); + } + const rawDecodedJWT = decodeJWT(req.body.data.jwt); + const decodedPayload = shouldVerifyJWT() + ? verifyJWT(req.body.data.jwt, rawDecodedJWT, keysCache) + : (rawDecodedJWT.payload as DecodedPayload); + checkDecodedToken(decodedPayload, eventType, projectId); + const authUserRecord = parseAuthUserRecord(decodedPayload.user_record); + const authEventContext = parseAuthEventContext(decodedPayload, projectId); + const authResponse = + (await handler(authUserRecord, authEventContext)) || undefined; + validateAuthResponse(eventType, authResponse); + const updateMask = getUpdateMask(authResponse); + const result = { + userRecord: { + ...authResponse, + updateMask, + }, + }; + + res.status(200); + res.setHeader('Content-Type', 'application/json'); + res.send(JSON.stringify(result)); + } catch (err) { + if (!(err instanceof HttpsError)) { + // This doesn't count as an 'explicit' error. + logger.error('Unhandled error', err); + err = new HttpsError('internal', 'An unexpected error occurred.'); + } + + res.status(err.code); + res.setHeader('Content-Type', 'application/json'); + res.send({ error: err.toJson() }); + } + }; +} From b0140e419c9982ce1394dcd1f152eced914cff6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Mar 2022 16:40:33 -0700 Subject: [PATCH 054/370] Bump minimist from 1.2.5 to 1.2.6 (#1067) Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6. - [Release notes](https://github.com/substack/minimist/releases) - [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6) --- updated-dependencies: - dependency-name: minimist dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index cc67d00f6..b02a633c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3497,9 +3497,9 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "node_modules/mkdirp": { @@ -8933,9 +8933,9 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "mkdirp": { From 393efce8eae69c2b30541f1f583895db53fa4e91 Mon Sep 17 00:00:00 2001 From: CommanderRoot Date: Fri, 1 Apr 2022 01:43:18 +0200 Subject: [PATCH 055/370] refactor: replace deprecated String.prototype.substr() (#1066) .substr() is deprecated so we replace it with .slice() which works similarily but isn't deprecated Signed-off-by: Tobias Speicher Co-authored-by: Daniel Lee --- src/common/providers/https.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/providers/https.ts b/src/common/providers/https.ts index 5cbc016a1..5421ebb7b 100644 --- a/src/common/providers/https.ts +++ b/src/common/providers/https.ts @@ -437,7 +437,7 @@ function isValidRequest(req: Request): req is HttpRequest { // If it has a charset, just ignore it for now. const semiColon = contentType.indexOf(';'); if (semiColon >= 0) { - contentType = contentType.substr(0, semiColon).trim(); + contentType = contentType.slice(0, semiColon).trim(); } if (contentType !== 'application/json') { logger.warn('Request has incorrect Content-Type.', contentType); From 73325e7e93935d076569eacc3cdd4ada15265783 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Mar 2022 16:46:59 -0700 Subject: [PATCH 056/370] Bump ansi-regex from 3.0.0 to 3.0.1 (#1068) Bumps [ansi-regex](https://github.com/chalk/ansi-regex) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/chalk/ansi-regex/releases) - [Commits](https://github.com/chalk/ansi-regex/compare/v3.0.0...v3.0.1) --- updated-dependencies: - dependency-name: ansi-regex dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Daniel Lee --- package-lock.json | 84 +++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index b02a633c8..5998a51a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -414,9 +414,9 @@ } }, "node_modules/@grpc/proto-loader/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "optional": true, "engines": { @@ -1018,9 +1018,9 @@ } }, "node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true, "engines": { "node": ">=4" @@ -1393,9 +1393,9 @@ } }, "node_modules/cliui/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true, "engines": { "node": ">=6" @@ -3553,9 +3553,9 @@ } }, "node_modules/mocha/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true, "engines": { "node": ">=6" @@ -5643,9 +5643,9 @@ } }, "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true, "engines": { "node": ">=6" @@ -5802,9 +5802,9 @@ } }, "node_modules/yargs-unparser/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true, "engines": { "node": ">=6" @@ -5861,9 +5861,9 @@ } }, "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" @@ -6374,9 +6374,9 @@ }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "optional": true }, @@ -6909,9 +6909,9 @@ "dev": true }, "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true }, "ansi-styles": { @@ -7221,9 +7221,9 @@ }, "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==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true }, "string-width": { @@ -8979,9 +8979,9 @@ }, "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==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true }, "debug": { @@ -10620,9 +10620,9 @@ }, "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==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true }, "string-width": { @@ -10725,9 +10725,9 @@ }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { @@ -10883,9 +10883,9 @@ }, "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==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true }, "string-width": { From 47ac6b828ea7277a777b188d58ef8070002b403d Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Thu, 31 Mar 2022 16:53:45 -0700 Subject: [PATCH 057/370] Change Eventfilters to a Record structure (#1070) * changing to a map * adding channel & changelog Co-authored-by: Daniel Lee --- CHANGELOG.md | 1 + spec/runtime/loader.spec.ts | 9 +- spec/v1/cloud-functions.spec.ts | 36 ++--- spec/v1/providers/analytics.spec.ts | 9 +- spec/v1/providers/auth.spec.ts | 9 +- spec/v1/providers/database.spec.ts | 9 +- spec/v1/providers/firestore.spec.ts | 9 +- spec/v1/providers/pubsub.spec.ts | 9 +- spec/v1/providers/remoteConfig.spec.ts | 9 +- spec/v1/providers/storage.spec.ts | 9 +- spec/v1/providers/testLab.spec.ts | 9 +- spec/v2/providers/alerts/alerts.spec.ts | 63 ++------ .../providers/alerts/appDistribution.spec.ts | 50 +++---- spec/v2/providers/alerts/billing.spec.ts | 54 +++---- spec/v2/providers/alerts/crashlytics.spec.ts | 90 ++++-------- spec/v2/providers/pubsub.spec.ts | 9 +- spec/v2/providers/storage.spec.ts | 136 ++++-------------- src/cloud-functions.ts | 9 +- src/runtime/manifest.ts | 13 +- src/v2/providers/alerts/alerts.ts | 14 +- src/v2/providers/pubsub.ts | 2 +- src/v2/providers/storage.ts | 2 +- 22 files changed, 160 insertions(+), 400 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..4ccafa8c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Changes internal structure to be more flexible (#1070). diff --git a/spec/runtime/loader.spec.ts b/spec/runtime/loader.spec.ts index 11acb583f..6db4370fd 100644 --- a/spec/runtime/loader.spec.ts +++ b/spec/runtime/loader.spec.ts @@ -122,12 +122,9 @@ describe('extractStack', () => { platform: 'gcfv1', eventTrigger: { eventType: 'google.pubsub.topic.publish', - eventFilters: [ - { - attribute: 'resource', - value: 'projects/my-project/topics/my-topic', - }, - ], + eventFilters: { + resource: 'projects/my-project/topics/my-topic', + }, retry: false, }, labels: {}, diff --git a/spec/v1/cloud-functions.spec.ts b/spec/v1/cloud-functions.spec.ts index 3405af695..5dd0a8941 100644 --- a/spec/v1/cloud-functions.spec.ts +++ b/spec/v1/cloud-functions.spec.ts @@ -62,12 +62,9 @@ describe('makeCloudFunction', () => { platform: 'gcfv1', eventTrigger: { eventType: 'mock.provider.mock.event', - eventFilters: [ - { - attribute: 'resource', - value: 'resource', - }, - ], + eventFilters: { + resource: 'resource', + }, retry: false, }, labels: {}, @@ -89,12 +86,9 @@ describe('makeCloudFunction', () => { platform: 'gcfv1', eventTrigger: { eventType: 'providers/provider/eventTypes/event', - eventFilters: [ - { - attribute: 'resource', - value: 'resource', - }, - ], + eventFilters: { + resource: 'resource', + }, retry: false, }, labels: {}, @@ -125,12 +119,9 @@ describe('makeCloudFunction', () => { serviceAccountEmail: 'foo@google.com', eventTrigger: { eventType: 'mock.provider.mock.event', - eventFilters: [ - { - attribute: 'resource', - value: 'resource', - }, - ], + eventFilters: { + resource: 'resource', + }, retry: false, }, secretEnvironmentVariables: [{ secret: 'MY_SECRET', key: 'MY_SECRET' }], @@ -152,12 +143,9 @@ describe('makeCloudFunction', () => { platform: 'gcfv1', eventTrigger: { eventType: 'mock.provider.mock.event', - eventFilters: [ - { - attribute: 'resource', - value: 'resource', - }, - ], + eventFilters: { + resource: 'resource', + }, retry: true, }, labels: {}, diff --git a/spec/v1/providers/analytics.spec.ts b/spec/v1/providers/analytics.spec.ts index 1603fc280..ead39554f 100644 --- a/spec/v1/providers/analytics.spec.ts +++ b/spec/v1/providers/analytics.spec.ts @@ -72,12 +72,9 @@ describe('Analytics Functions', () => { expect(cloudFunction.__endpoint).to.deep.equal({ platform: 'gcfv1', eventTrigger: { - eventFilters: [ - { - attribute: 'resource', - value: 'projects/project1/events/first_open', - }, - ], + eventFilters: { + resource: 'projects/project1/events/first_open', + }, eventType: 'providers/google.firebase.analytics/eventTypes/event.log', retry: false, diff --git a/spec/v1/providers/auth.spec.ts b/spec/v1/providers/auth.spec.ts index 3f5913d2b..8659a1967 100644 --- a/spec/v1/providers/auth.spec.ts +++ b/spec/v1/providers/auth.spec.ts @@ -64,12 +64,9 @@ describe('Auth Functions', () => { return { platform: 'gcfv1', eventTrigger: { - eventFilters: [ - { - attribute: 'resource', - value: `projects/${project}`, - }, - ], + eventFilters: { + resource: `projects/${project}`, + }, eventType: `providers/firebase.auth/eventTypes/${eventType}`, retry: false, }, diff --git a/spec/v1/providers/database.spec.ts b/spec/v1/providers/database.spec.ts index 2ea1022e3..304c1d1fd 100644 --- a/spec/v1/providers/database.spec.ts +++ b/spec/v1/providers/database.spec.ts @@ -45,12 +45,9 @@ describe('Database Functions', () => { return { platform: 'gcfv1', eventTrigger: { - eventFilters: [ - { - attribute: 'resource', - value: resource, - }, - ], + eventFilters: { + resource, + }, eventType: `providers/google.firebase.database/eventTypes/${eventType}`, retry: false, }, diff --git a/spec/v1/providers/firestore.spec.ts b/spec/v1/providers/firestore.spec.ts index 0317e69f0..3f9f07fbe 100644 --- a/spec/v1/providers/firestore.spec.ts +++ b/spec/v1/providers/firestore.spec.ts @@ -107,12 +107,9 @@ describe('Firestore Functions', () => { return { platform: 'gcfv1', eventTrigger: { - eventFilters: [ - { - attribute: 'resource', - value: resource, - }, - ], + eventFilters: { + resource, + }, eventType: `providers/cloud.firestore/eventTypes/${eventType}`, retry: false, }, diff --git a/spec/v1/providers/pubsub.spec.ts b/spec/v1/providers/pubsub.spec.ts index eddf6533c..5547515ab 100644 --- a/spec/v1/providers/pubsub.spec.ts +++ b/spec/v1/providers/pubsub.spec.ts @@ -108,12 +108,9 @@ describe('Pubsub Functions', () => { platform: 'gcfv1', eventTrigger: { eventType: 'google.pubsub.topic.publish', - eventFilters: [ - { - attribute: 'resource', - value: 'projects/project1/topics/toppy', - }, - ], + eventFilters: { + resource: 'projects/project1/topics/toppy', + }, retry: false, }, labels: {}, diff --git a/spec/v1/providers/remoteConfig.spec.ts b/spec/v1/providers/remoteConfig.spec.ts index 8dc310a7b..f3fde2043 100644 --- a/spec/v1/providers/remoteConfig.spec.ts +++ b/spec/v1/providers/remoteConfig.spec.ts @@ -68,12 +68,9 @@ describe('RemoteConfig Functions', () => { platform: 'gcfv1', eventTrigger: { eventType: 'google.firebase.remoteconfig.update', - eventFilters: [ - { - attribute: 'resource', - value: 'projects/project1', - }, - ], + eventFilters: { + resource: 'projects/project1', + }, retry: false, }, labels: {}, diff --git a/spec/v1/providers/storage.spec.ts b/spec/v1/providers/storage.spec.ts index d99a14baa..d96b131b9 100644 --- a/spec/v1/providers/storage.spec.ts +++ b/spec/v1/providers/storage.spec.ts @@ -42,12 +42,9 @@ describe('Storage Functions', () => { return { platform: 'gcfv1', eventTrigger: { - eventFilters: [ - { - attribute: 'resource', - value: `projects/_/buckets/${bucket}`, - }, - ], + eventFilters: { + resource: `projects/_/buckets/${bucket}`, + }, eventType: `google.storage.object.${eventType}`, retry: false, }, diff --git a/spec/v1/providers/testLab.spec.ts b/spec/v1/providers/testLab.spec.ts index 968ccb079..b3ba22d7f 100644 --- a/spec/v1/providers/testLab.spec.ts +++ b/spec/v1/providers/testLab.spec.ts @@ -50,12 +50,9 @@ describe('Test Lab Functions', () => { platform: 'gcfv1', eventTrigger: { eventType: 'google.testing.testMatrix.complete', - eventFilters: [ - { - attribute: 'resource', - value: 'projects/project1/testMatrices/{matrix}', - }, - ], + eventFilters: { + resource: 'projects/project1/testMatrices/{matrix}', + }, retry: false, }, labels: {}, diff --git a/spec/v2/providers/alerts/alerts.spec.ts b/spec/v2/providers/alerts/alerts.spec.ts index 4f53c41d2..6ad78a811 100644 --- a/spec/v2/providers/alerts/alerts.spec.ts +++ b/spec/v2/providers/alerts/alerts.spec.ts @@ -6,6 +6,15 @@ import { FULL_ENDPOINT, FULL_OPTIONS } from '../helpers'; const ALERT_TYPE = 'new-alert-type'; const APPID = '123456789'; +const ALERT_EVENT_FILTER = { + alerttype: ALERT_TYPE, +}; + +const ALERT_APP_EVENT_FILTER = { + alerttype: ALERT_TYPE, + appid: APPID, +}; + describe('alerts', () => { describe('onAlertPublished', () => { it('should create the function without opts', () => { @@ -16,12 +25,7 @@ describe('alerts', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: ALERT_TYPE, - }, - ], + eventFilters: ALERT_EVENT_FILTER, retry: false, }, }); @@ -41,16 +45,7 @@ describe('alerts', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: ALERT_TYPE, - }, - { - attribute: 'appid', - value: APPID, - }, - ], + eventFilters: ALERT_APP_EVENT_FILTER, retry: false, }, }); @@ -81,12 +76,7 @@ describe('alerts', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: ALERT_TYPE, - }, - ], + eventFilters: ALERT_EVENT_FILTER, retry: false, }, }); @@ -99,12 +89,7 @@ describe('alerts', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: ALERT_TYPE, - }, - ], + eventFilters: ALERT_EVENT_FILTER, retry: false, }, }); @@ -117,16 +102,7 @@ describe('alerts', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: ALERT_TYPE, - }, - { - attribute: 'appid', - value: APPID, - }, - ], + eventFilters: ALERT_APP_EVENT_FILTER, retry: false, }, }); @@ -153,16 +129,7 @@ describe('alerts', () => { minInstances: 3, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: ALERT_TYPE, - }, - { - attribute: 'appid', - value: APPID, - }, - ], + eventFilters: ALERT_APP_EVENT_FILTER, retry: false, }, }); diff --git a/spec/v2/providers/alerts/appDistribution.spec.ts b/spec/v2/providers/alerts/appDistribution.spec.ts index 2f8ea5c5a..3242c7e07 100644 --- a/spec/v2/providers/alerts/appDistribution.spec.ts +++ b/spec/v2/providers/alerts/appDistribution.spec.ts @@ -6,6 +6,10 @@ import { FULL_ENDPOINT, FULL_OPTIONS } from '../helpers'; const APPID = '123456789'; const myHandler = () => 42; +const APP_EVENT_FILTER = { + appid: APPID, +}; + describe('appDistribution', () => { describe('onNewTesterIosDevicePublished', () => { it('should create a function with alertType & appId', () => { @@ -19,16 +23,10 @@ describe('appDistribution', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: appDistribution.newTesterIosDeviceAlert, - }, - { - attribute: 'appid', - value: APPID, - }, - ], + eventFilters: { + ...APP_EVENT_FILTER, + alerttype: appDistribution.newTesterIosDeviceAlert, + }, retry: false, }, }); @@ -44,12 +42,9 @@ describe('appDistribution', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: appDistribution.newTesterIosDeviceAlert, - }, - ], + eventFilters: { + alerttype: appDistribution.newTesterIosDeviceAlert, + }, retry: false, }, }); @@ -65,16 +60,10 @@ describe('appDistribution', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: appDistribution.newTesterIosDeviceAlert, - }, - { - attribute: 'appid', - value: APPID, - }, - ], + eventFilters: { + ...APP_EVENT_FILTER, + alerttype: appDistribution.newTesterIosDeviceAlert, + }, retry: false, }, }); @@ -88,12 +77,9 @@ describe('appDistribution', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: appDistribution.newTesterIosDeviceAlert, - }, - ], + eventFilters: { + alerttype: appDistribution.newTesterIosDeviceAlert, + }, retry: false, }, }); diff --git a/spec/v2/providers/alerts/billing.spec.ts b/spec/v2/providers/alerts/billing.spec.ts index bc3fa6a9a..f8e0eeebb 100644 --- a/spec/v2/providers/alerts/billing.spec.ts +++ b/spec/v2/providers/alerts/billing.spec.ts @@ -16,12 +16,9 @@ describe('billing', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: billing.planUpdateAlert, - }, - ], + eventFilters: { + alerttype: billing.planUpdateAlert, + }, retry: false, }, }); @@ -37,12 +34,9 @@ describe('billing', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: billing.planUpdateAlert, - }, - ], + eventFilters: { + alerttype: billing.planUpdateAlert, + }, retry: false, }, }); @@ -58,12 +52,9 @@ describe('billing', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: billing.planAutomatedUpdateAlert, - }, - ], + eventFilters: { + alerttype: billing.planAutomatedUpdateAlert, + }, retry: false, }, }); @@ -79,12 +70,9 @@ describe('billing', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: billing.planAutomatedUpdateAlert, - }, - ], + eventFilters: { + alerttype: billing.planAutomatedUpdateAlert, + }, retry: false, }, }); @@ -100,12 +88,9 @@ describe('billing', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: ALERT_TYPE, - }, - ], + eventFilters: { + alerttype: ALERT_TYPE, + }, retry: false, }, }); @@ -122,12 +107,9 @@ describe('billing', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: ALERT_TYPE, - }, - ], + eventFilters: { + alerttype: ALERT_TYPE, + }, retry: false, }, }); diff --git a/spec/v2/providers/alerts/crashlytics.spec.ts b/spec/v2/providers/alerts/crashlytics.spec.ts index 0ac00c5e0..1ea1c4f0e 100644 --- a/spec/v2/providers/alerts/crashlytics.spec.ts +++ b/spec/v2/providers/alerts/crashlytics.spec.ts @@ -36,6 +36,15 @@ describe('crashlytics', () => { ]; for (const { method, event } of testcases) { + const ALERT_EVENT_FILTER = { + alerttype: event, + }; + + const ALERT_APP_EVENT_FILTER = { + ...ALERT_EVENT_FILTER, + appid: APPID, + }; + describe(method, () => { it('should create a function only handler', () => { const func = crashlytics[method](myHandler); @@ -45,12 +54,7 @@ describe('crashlytics', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: event, - }, - ], + eventFilters: ALERT_EVENT_FILTER, retry: false, }, }); @@ -64,16 +68,7 @@ describe('crashlytics', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: event, - }, - { - attribute: 'appid', - value: APPID, - }, - ], + eventFilters: ALERT_APP_EVENT_FILTER, retry: false, }, }); @@ -86,12 +81,7 @@ describe('crashlytics', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: event, - }, - ], + eventFilters: ALERT_EVENT_FILTER, retry: false, }, }); @@ -107,16 +97,7 @@ describe('crashlytics', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: event, - }, - { - attribute: 'appid', - value: APPID, - }, - ], + eventFilters: ALERT_APP_EVENT_FILTER, retry: false, }, }); @@ -124,6 +105,15 @@ describe('crashlytics', () => { }); } + const ALERT_EVENT_FILTER = { + alerttype: ALERT_TYPE, + }; + + const ALERT_APP_EVENT_FILTER = { + ...ALERT_EVENT_FILTER, + appid: APPID, + }; + describe('onOperation', () => { it('should create a function with alertType only', () => { const func = crashlytics.onOperation(ALERT_TYPE, myHandler, undefined); @@ -133,12 +123,7 @@ describe('crashlytics', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: ALERT_TYPE, - }, - ], + eventFilters: ALERT_EVENT_FILTER, retry: false, }, }); @@ -152,16 +137,7 @@ describe('crashlytics', () => { labels: {}, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: ALERT_TYPE, - }, - { - attribute: 'appid', - value: APPID, - }, - ], + eventFilters: ALERT_APP_EVENT_FILTER, retry: false, }, }); @@ -178,12 +154,7 @@ describe('crashlytics', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: ALERT_TYPE, - }, - ], + eventFilters: ALERT_EVENT_FILTER, retry: false, }, }); @@ -200,16 +171,7 @@ describe('crashlytics', () => { ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: ALERT_TYPE, - }, - { - attribute: 'appid', - value: APPID, - }, - ], + eventFilters: ALERT_APP_EVENT_FILTER, retry: false, }, }); diff --git a/spec/v2/providers/pubsub.spec.ts b/spec/v2/providers/pubsub.spec.ts index 303ffa1d7..f48e10f72 100644 --- a/spec/v2/providers/pubsub.spec.ts +++ b/spec/v2/providers/pubsub.spec.ts @@ -12,12 +12,9 @@ const EVENT_TRIGGER = { const ENDPOINT_EVENT_TRIGGER = { eventType: 'google.cloud.pubsub.topic.v1.messagePublished', - eventFilters: [ - { - attribute: 'topic', - value: 'topic', - }, - ], + eventFilters: { + topic: 'topic', + }, retry: false, }; diff --git a/spec/v2/providers/storage.spec.ts b/spec/v2/providers/storage.spec.ts index cb612e561..0471b6931 100644 --- a/spec/v2/providers/storage.spec.ts +++ b/spec/v2/providers/storage.spec.ts @@ -12,15 +12,20 @@ const EVENT_TRIGGER = { const ENDPOINT_EVENT_TRIGGER = { eventType: 'event-type', - eventFilters: [ - { - attribute: 'bucket', - value: 'some-bucket', - }, - ], + eventFilters: { + bucket: 'some-bucket', + }, retry: false, }; +const DEFAULT_BUCKET_EVENT_FILTER = { + bucket: 'default-bucket', +}; + +const SPECIFIC_BUCKET_EVENT_FILTER = { + bucket: 'my-bucket', +}; + describe('v2/storage', () => { describe('getOptsAndBucket', () => { it('should return the default bucket with empty opts', () => { @@ -119,12 +124,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_EVENT_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'default-bucket', - }, - ], + eventFilters: DEFAULT_BUCKET_EVENT_FILTER, }, region: ['us-west1'], }); @@ -274,12 +274,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_ARCHIVED_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'default-bucket', - }, - ], + eventFilters: DEFAULT_BUCKET_EVENT_FILTER, }, }); }); @@ -301,12 +296,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_ARCHIVED_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'my-bucket', - }, - ], + eventFilters: SPECIFIC_BUCKET_EVENT_FILTER, }, }); }); @@ -332,12 +322,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_ARCHIVED_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'my-bucket', - }, - ], + eventFilters: SPECIFIC_BUCKET_EVENT_FILTER, }, region: ['us-west1'], }); @@ -363,12 +348,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_ARCHIVED_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'default-bucket', - }, - ], + eventFilters: DEFAULT_BUCKET_EVENT_FILTER, }, region: ['us-west1'], }); @@ -413,12 +393,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_FINALIZED_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'default-bucket', - }, - ], + eventFilters: DEFAULT_BUCKET_EVENT_FILTER, }, }); }); @@ -440,12 +415,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_FINALIZED_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'my-bucket', - }, - ], + eventFilters: SPECIFIC_BUCKET_EVENT_FILTER, }, }); }); @@ -471,12 +441,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_FINALIZED_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'my-bucket', - }, - ], + eventFilters: SPECIFIC_BUCKET_EVENT_FILTER, }, region: ['us-west1'], }); @@ -505,12 +470,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_FINALIZED_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'default-bucket', - }, - ], + eventFilters: DEFAULT_BUCKET_EVENT_FILTER, }, region: ['us-west1'], }); @@ -555,12 +515,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_DELETED_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'default-bucket', - }, - ], + eventFilters: DEFAULT_BUCKET_EVENT_FILTER, }, }); @@ -584,12 +539,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_DELETED_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'my-bucket', - }, - ], + eventFilters: SPECIFIC_BUCKET_EVENT_FILTER, }, }); }); @@ -615,12 +565,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_DELETED_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'my-bucket', - }, - ], + eventFilters: SPECIFIC_BUCKET_EVENT_FILTER, }, region: ['us-west1'], }); @@ -646,12 +591,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_DELETED_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'default-bucket', - }, - ], + eventFilters: DEFAULT_BUCKET_EVENT_FILTER, }, region: ['us-west1'], }); @@ -696,12 +636,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_METADATA_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'default-bucket', - }, - ], + eventFilters: DEFAULT_BUCKET_EVENT_FILTER, }, }); @@ -725,12 +660,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_METADATA_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'my-bucket', - }, - ], + eventFilters: SPECIFIC_BUCKET_EVENT_FILTER, }, }); }); @@ -756,12 +686,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_METADATA_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'my-bucket', - }, - ], + eventFilters: SPECIFIC_BUCKET_EVENT_FILTER, }, region: ['us-west1'], }); @@ -790,12 +715,7 @@ describe('v2/storage', () => { labels: {}, eventTrigger: { ...ENDPOINT_METADATA_TRIGGER, - eventFilters: [ - { - attribute: 'bucket', - value: 'default-bucket', - }, - ], + eventFilters: DEFAULT_BUCKET_EVENT_FILTER, }, region: ['us-west1'], }); diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index ab6fdb120..ffe94e031 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -461,12 +461,9 @@ export function makeCloudFunction({ } else { endpoint.eventTrigger = { eventType: legacyEventType || provider + '.' + eventType, - eventFilters: [ - { - attribute: 'resource', - value: triggerResource(), - }, - ], + eventFilters: { + resource: triggerResource(), + }, retry: !!options.failurePolicy, }; } diff --git a/src/runtime/manifest.ts b/src/runtime/manifest.ts index 2b08385d5..caece4ee5 100644 --- a/src/runtime/manifest.ts +++ b/src/runtime/manifest.ts @@ -19,15 +19,6 @@ // 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. -/** - * One or more event filters restrict the set of events delivered to an EventTrigger. - */ -interface EventFilter { - attribute: string; - value: string; - // if left unspecified, equality is used. - operator?: string; -} /** * An definition of a function as appears in the Manifest. @@ -58,7 +49,9 @@ export interface ManifestEndpoint { callableTrigger?: {}; eventTrigger?: { - eventFilters: EventFilter[]; + eventFilters: Record; + eventFilterPathPatterns?: Record; + channel?: string; eventType: string; retry: boolean; region?: string; diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index 902bd4232..173267422 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -91,20 +91,14 @@ export function getEndpointAnnotation( }, eventTrigger: { eventType, - eventFilters: [ - { - attribute: 'alerttype', - value: alertType, - }, - ], + eventFilters: { + alerttype: alertType, + }, retry: !!opts.retry, }, }; if (appId) { - endpoint.eventTrigger.eventFilters.push({ - attribute: 'appid', - value: appId, - }); + endpoint.eventTrigger.eventFilters.appid = appId; } return endpoint; } diff --git a/src/v2/providers/pubsub.ts b/src/v2/providers/pubsub.ts index 01b731ccb..2964af42d 100644 --- a/src/v2/providers/pubsub.ts +++ b/src/v2/providers/pubsub.ts @@ -174,7 +174,7 @@ export function onMessagePublished( }, eventTrigger: { eventType: 'google.cloud.pubsub.topic.v1.messagePublished', - eventFilters: [{ attribute: 'topic', value: topic }], + eventFilters: { topic }, retry: false, }, }; diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts index 8e63ea45e..bc9d26653 100644 --- a/src/v2/providers/storage.ts +++ b/src/v2/providers/storage.ts @@ -360,7 +360,7 @@ export function onOperation( }, eventTrigger: { eventType, - eventFilters: [{ attribute: 'bucket', value: bucket }], + eventFilters: { bucket }, retry: false, }, }; From b9de399c83b67e8fd0a4a2aca142c6bdf3a6689c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Mar 2022 23:36:20 -0700 Subject: [PATCH 058/370] Bump pathval from 1.1.0 to 1.1.1 (#1038) Bumps [pathval](https://github.com/chaijs/pathval) from 1.1.0 to 1.1.1. - [Release notes](https://github.com/chaijs/pathval/releases) - [Changelog](https://github.com/chaijs/pathval/blob/master/CHANGELOG.md) - [Commits](https://github.com/chaijs/pathval/compare/v1.1.0...v1.1.1) --- updated-dependencies: - dependency-name: pathval dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Daniel Lee --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5998a51a5..16140eff4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4225,9 +4225,9 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, "node_modules/pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true, "engines": { "node": "*" @@ -9523,9 +9523,9 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true }, "performance-now": { From c31cf60eaa5d525e71ddec1c5aed4391caf690a1 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Mon, 4 Apr 2022 11:27:27 -0700 Subject: [PATCH 059/370] Move some TQ triggers under new namespace. (#1069) Move all task queue related API under newly formed `tasks` package. Task queue function are now under `tasks` namespace: ``` export const factorize = functions.tasks.taskQueue({ rateLimits: { maxBurstSize: 2, maxConcurrentDispatches: 1, maxDispatchesPerSecond: 1, }, retryConfig: { maxAttempts: 100, maxRetrySeconds: 1000, maxBackoffSeconds: 20, maxDoublings: 10, minBackoffSeconds: 0.1, } }).onDispatch((data, context) => { deducePrivateKey(data.publicKey); }); ``` Same goes for handler namespace. Few other changes: 1) Add probably-forgotten `maxRetrySeconds` option for task queues. 2) Remove `maxBurstsize` option since it's outputonly. --- package.json | 30 +- spec/common/providers/https.spec.ts | 352 +----------------- spec/common/providers/tasks.spec.ts | 262 +++++++++++++ spec/helper.ts | 127 +++++++ spec/runtime/loader.spec.ts | 4 +- spec/v1/providers/https.spec.ts | 203 +--------- spec/v1/providers/tasks.spec.ts | 165 ++++++++ spec/v2/providers/alerts/alerts.spec.ts | 2 +- .../providers/alerts/appDistribution.spec.ts | 2 +- spec/v2/providers/alerts/billing.spec.ts | 2 +- spec/v2/providers/alerts/crashlytics.spec.ts | 2 +- spec/v2/providers/{helpers.ts => fixtures.ts} | 0 spec/v2/providers/https.spec.ts | 225 ++--------- spec/v2/providers/pubsub.spec.ts | 2 +- spec/v2/providers/storage.spec.ts | 2 +- spec/v2/providers/tasks.spec.ts | 201 ++++++++++ src/common/providers/https.ts | 118 +----- src/common/providers/tasks.ts | 147 ++++++++ src/function-builder.ts | 9 +- src/handler-builder.ts | 24 +- src/index.ts | 2 + src/providers/https.ts | 119 +----- src/providers/tasks.ts | 145 ++++++++ src/v2/index.ts | 3 +- src/v2/providers/https.ts | 104 +----- src/v2/providers/tasks.ts | 150 ++++++++ v1/tasks.js | 26 ++ v2/tasks.js | 26 ++ 28 files changed, 1353 insertions(+), 1101 deletions(-) create mode 100644 spec/common/providers/tasks.spec.ts create mode 100644 spec/helper.ts create mode 100644 spec/v1/providers/tasks.spec.ts rename spec/v2/providers/{helpers.ts => fixtures.ts} (100%) create mode 100644 spec/v2/providers/tasks.spec.ts create mode 100644 src/common/providers/tasks.ts create mode 100644 src/providers/tasks.ts create mode 100644 src/v2/providers/tasks.ts create mode 100644 v1/tasks.js create mode 100644 v2/tasks.js diff --git a/package.json b/package.json index 099e5ec3f..ad1c392c7 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "./lib/providers/pubsub": "./lib/providers/pubsub.js", "./lib/providers/remoteConfig": "./lib/providers/remoteConfig.js", "./lib/providers/storage": "./lib/providers/storage.js", + "./lib/providers/tasks": "./lib/providers/tasks.js", "./lib/providers/testLab": "./lib/providers/testLab.js", "./v1": "./lib/index.js", "./v1/analytics": "./lib/providers/analytics.js", @@ -58,6 +59,7 @@ "./v2/params": "./lib/v2/params/index.js", "./v2/pubsub": "./lib/v2/providers/pubsub.js", "./v2/storage": "./lib/v2/providers/storage.js", + "./v2/tasks": "./lib/v2/providers/tasks.js", "./v2/alerts": "./lib/v2/providers/alerts/index.js", "./v2/alerts/appDistribution": "./lib/v2/providers/alerts/appDistribution.js", "./v2/alerts/billing": "./lib/v2/providers/alerts/billing.js", @@ -98,12 +100,27 @@ "v1/storage": [ "lib/providers/storage" ], + "v1/tasks": [ + "lib/providers/tasks" + ], "v1/testLab": [ "lib/providers/testLab" ], "v2": [ "lib/v2" ], + "v2/alerts": [ + "lib/v2/providers/alerts" + ], + "v2/alerts/appDistribution": [ + "lib/v2/providers/alerts/appDistribution" + ], + "v2/alerts/billing": [ + "lib/v2/providers/alerts/billing" + ], + "v2/alerts/crashlytics": [ + "lib/v2/providers/alerts/crashlytics" + ], "v2/base": [ "lib/v2/base" ], @@ -122,17 +139,8 @@ "v2/storage": [ "lib/v2/providers/storage" ], - "v2/alerts": [ - "lib/v2/providers/alerts" - ], - "v2/alerts/appDistribution": [ - "lib/v2/providers/alerts/appDistribution" - ], - "v2/alerts/billing": [ - "lib/v2/providers/alerts/billing" - ], - "v2/alerts/crashlytics": [ - "lib/v2/providers/alerts/crashlytics" + "v2/tasks": [ + "lib/v2/providers/tasks" ] } }, diff --git a/spec/common/providers/https.spec.ts b/spec/common/providers/https.spec.ts index d9d8e63c4..d1f8703bd 100644 --- a/spec/common/providers/https.spec.ts +++ b/spec/common/providers/https.spec.ts @@ -1,20 +1,14 @@ import { expect } from 'chai'; -import * as express from 'express'; import * as firebase from 'firebase-admin'; import * as sinon from 'sinon'; import { apps as appsNamespace } from '../../../src/apps'; -import * as debug from '../../../src/common/debug'; -import * as https from '../../../src/common/providers/https'; import { - CallableContext, - CallableRequest, - TaskContext, - TaskRequest, - unsafeDecodeAppCheckToken, - unsafeDecodeIdToken, -} from '../../../src/common/providers/https'; -import * as mocks from '../../fixtures/credential/key.json'; + checkAppCheckContext, + checkAuthContext, + runHandler, + RunHandlerResult, +} from '../../helper'; import { expectedResponseHeaders, generateAppCheckToken, @@ -25,15 +19,9 @@ import { mockFetchPublicKeys, mockRequest, } from '../../fixtures/mockrequest'; - -/** - * RunHandlerResult contains the data from an express.Response. - */ -interface RunHandlerResult { - status: number; - headers: { [name: string]: string }; - body: any; -} +import * as debug from '../../../src/common/debug'; +import * as https from '../../../src/common/providers/https'; +import * as mocks from '../../fixtures/credential/key.json'; /** * A CallTest is a specification for a test of a callable function that @@ -58,64 +46,6 @@ interface CallTest { expectedHttpResponse: RunHandlerResult; } -/** - * Runs an express handler with a given request asynchronously and returns the - * data populated into the response. - */ -function runHandler( - handler: express.Handler, - request: https.Request -): Promise { - return new Promise((resolve, reject) => { - // MockResponse mocks an express.Response. - // This class lives here so it can reference resolve and reject. - class MockResponse { - private statusCode = 0; - private headers: { [name: string]: string } = {}; - private callback: Function; - - public status(code: number) { - this.statusCode = code; - return this; - } - - // Headers are only set by the cors handler. - public setHeader(name: string, value: string) { - this.headers[name] = value; - } - - public getHeader(name: string): string { - return this.headers[name]; - } - - public send(body: any) { - resolve({ - status: this.statusCode, - headers: this.headers, - body, - }); - if (this.callback) { - this.callback(); - } - } - - public end() { - this.send(undefined); - } - - public on(event: string, callback: Function) { - if (event !== 'finish') { - throw new Error('MockResponse only implements the finish event'); - } - this.callback = callback; - } - } - - const response = new MockResponse(); - handler(request, response as any, () => undefined); - }); -} - // Runs a CallTest test. async function runCallableTest(test: CallTest): Promise { const opts = { @@ -145,81 +75,6 @@ async function runCallableTest(test: CallTest): Promise { expect(responseV2.status).to.equal(test.expectedHttpResponse.status); } -/** Represents a test case for a Task Queue Function */ -interface TaskTest { - // An http request, mocking a subset of https.Request. - httpRequest: any; - - // The expected format of the request passed to the handler. - expectedData: any; - - taskFunction?: ( - data: any, - context: https.TaskContext - ) => void | Promise; - - taskFunction2?: (request: https.TaskRequest) => void | Promise; - - // The expected shape of the http response returned to the callable SDK. - expectedStatus: number; -} - -// Runs a TaskTest test. -async function runTaskTest(test: TaskTest): Promise { - const taskQueueFunctionV1 = https.onDispatchHandler((data, context) => { - expect(data).to.deep.equal(test.expectedData); - if (test.taskFunction) { - test.taskFunction(data, context); - } - }); - - const responseV1 = await runHandler(taskQueueFunctionV1, test.httpRequest); - expect(responseV1.status).to.equal(test.expectedStatus); - - const taskQueueFunctionV2 = https.onDispatchHandler((request) => { - expect(request.data).to.deep.equal(test.expectedData); - if (test.taskFunction2) { - test.taskFunction2(request); - } - }); - - const responseV2 = await runHandler(taskQueueFunctionV2, test.httpRequest); - expect(responseV2.status).to.equal(test.expectedStatus); -} - -function checkAuthContext( - context: CallableContext | CallableRequest | TaskContext | TaskRequest, - projectId: string, - userId: string -) { - expect(context.auth).to.not.be.undefined; - expect(context.auth).to.not.be.null; - expect(context.auth.uid).to.equal(userId); - expect(context.auth.token.uid).to.equal(userId); - expect(context.auth.token.sub).to.equal(userId); - expect(context.auth.token.aud).to.equal(projectId); - - // TaskContext & TaskRequest don't have instanceIdToken - if ({}.hasOwnProperty.call(context, 'instanceIdToken')) { - expect((context as CallableContext).instanceIdToken).to.be.undefined; - } -} - -function checkAppCheckContext( - context: CallableContext | CallableRequest, - projectId: string, - appId: string -) { - 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; -} - describe('onCallHandler', () => { let app: firebase.app.App; @@ -667,187 +522,6 @@ describe('onCallHandler', () => { }); }); -describe('onEnqueueHandler', () => { - let app: firebase.app.App; - - before(() => { - const credential = { - getAccessToken: () => { - return Promise.resolve({ - expires_in: 1000, - access_token: 'fake', - }); - }, - getCertificate: () => { - return { - projectId: 'aProjectId', - }; - }, - }; - app = firebase.initializeApp({ - projectId: 'aProjectId', - credential, - }); - Object.defineProperty(appsNamespace(), 'admin', { get: () => app }); - }); - - after(() => { - app.delete(); - delete appsNamespace.singleton; - }); - - it('should handle success', () => { - return runTaskTest({ - httpRequest: mockRequest({ foo: 'bar' }), - expectedData: { foo: 'bar' }, - expectedStatus: 204, - }); - }); - - it('should reject bad method', () => { - const req = mockRequest(null); - req.method = 'GET'; - return runTaskTest({ - httpRequest: req, - expectedData: null, - expectedStatus: 400, - }); - }); - - it('should ignore charset', () => { - return runTaskTest({ - httpRequest: mockRequest(null, 'application/json; charset=utf-8'), - expectedData: null, - expectedStatus: 204, - }); - }); - - it('should reject bad content type', () => { - return runTaskTest({ - httpRequest: mockRequest(null, 'text/plain'), - expectedData: null, - expectedStatus: 400, - }); - }); - - it('should reject extra body fields', () => { - const req = mockRequest(null); - req.body.extra = 'bad'; - return runTaskTest({ - httpRequest: req, - expectedData: null, - expectedStatus: 400, - }); - }); - - it('should handle unhandled error', () => { - return runTaskTest({ - httpRequest: mockRequest(null), - expectedData: null, - taskFunction: (data, context) => { - throw new Error(`ceci n'est pas une error`); - }, - taskFunction2: (request) => { - throw new Error(`cece n'est pas une error`); - }, - expectedStatus: 500, - }); - }); - - it('should handle unknown error status', () => { - return runTaskTest({ - httpRequest: mockRequest(null), - expectedData: null, - taskFunction: (data, context) => { - throw new https.HttpsError('THIS_IS_NOT_VALID' as any, 'nope'); - }, - taskFunction2: (request) => { - throw new https.HttpsError('THIS_IS_NOT_VALID' as any, 'nope'); - }, - expectedStatus: 500, - }); - }); - - it('should handle well-formed error', () => { - return runTaskTest({ - httpRequest: mockRequest(null), - expectedData: null, - taskFunction: (data, context) => { - throw new https.HttpsError('not-found', 'i am error'); - }, - taskFunction2: (request) => { - throw new https.HttpsError('not-found', 'i am error'); - }, - expectedStatus: 404, - }); - }); - - it('should handle auth', async () => { - const mock = mockFetchPublicKeys(); - const projectId = appsNamespace().admin.options.projectId; - const idToken = generateIdToken(projectId); - await runTaskTest({ - httpRequest: mockRequest(null, 'application/json', { - authorization: 'Bearer ' + idToken, - }), - expectedData: null, - taskFunction: (data, context) => { - checkAuthContext(context, projectId, mocks.user_id); - return null; - }, - taskFunction2: (request) => { - checkAuthContext(request, projectId, mocks.user_id); - return null; - }, - expectedStatus: 204, - }); - mock.done(); - }); - - it('should reject bad auth', async () => { - const projectId = appsNamespace().admin.options.projectId; - const idToken = generateUnsignedIdToken(projectId); - await runTaskTest({ - httpRequest: mockRequest(null, 'application/json', { - authorization: 'Bearer ' + idToken, - }), - expectedData: null, - expectedStatus: 401, - }); - }); - - describe('skip token verification debug mode support', () => { - before(() => { - sinon - .stub(debug, 'isDebugFeatureEnabled') - .withArgs('skipTokenVerification') - .returns(true); - }); - - after(() => { - sinon.verifyAndRestore(); - }); - - it('should skip auth token verification', async () => { - const projectId = appsNamespace().admin.options.projectId; - const idToken = generateUnsignedIdToken(projectId); - await runTaskTest({ - httpRequest: mockRequest(null, 'application/json', { - authorization: 'Bearer ' + idToken, - }), - expectedData: null, - taskFunction: (data, context) => { - checkAuthContext(context, projectId, mocks.user_id); - }, - taskFunction2: (request) => { - checkAuthContext(request, projectId, mocks.user_id); - }, - expectedStatus: 204, - }); - }); - }); -}); - describe('encoding/decoding', () => { it('encodes null', () => { expect(https.encode(null)).to.be.null; @@ -977,19 +651,21 @@ describe('decode tokens', () => { const appId = '123:web:abc'; it('decodes valid Auth ID Token', () => { - const idToken = unsafeDecodeIdToken(generateIdToken(projectId)); + const idToken = https.unsafeDecodeIdToken(generateIdToken(projectId)); expect(idToken.uid).to.equal(mocks.user_id); expect(idToken.sub).to.equal(mocks.user_id); }); it('decodes invalid Auth ID Token', () => { - const idToken = unsafeDecodeIdToken(generateUnsignedIdToken(projectId)); + const idToken = https.unsafeDecodeIdToken( + generateUnsignedIdToken(projectId) + ); expect(idToken.uid).to.equal(mocks.user_id); expect(idToken.sub).to.equal(mocks.user_id); }); it('decodes valid App Check Token', () => { - const idToken = unsafeDecodeAppCheckToken( + const idToken = https.unsafeDecodeAppCheckToken( generateAppCheckToken(projectId, appId) ); expect(idToken.app_id).to.equal(appId); @@ -997,7 +673,7 @@ describe('decode tokens', () => { }); it('decodes invalid App Check Token', () => { - const idToken = unsafeDecodeAppCheckToken( + const idToken = https.unsafeDecodeAppCheckToken( generateUnsignedAppCheckToken(projectId, appId) ); expect(idToken.app_id).to.equal(appId); diff --git a/spec/common/providers/tasks.spec.ts b/spec/common/providers/tasks.spec.ts new file mode 100644 index 000000000..a29731c5a --- /dev/null +++ b/spec/common/providers/tasks.spec.ts @@ -0,0 +1,262 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as sinon from 'sinon'; +import * as firebase from 'firebase-admin'; + +import { checkAuthContext, runHandler } from '../../helper'; +import { + generateIdToken, + generateUnsignedIdToken, + mockFetchPublicKeys, + mockRequest, +} from '../../fixtures/mockrequest'; +import { + onDispatchHandler, + TaskContext, + Request, +} from '../../../src/common/providers/tasks'; +import { apps as appsNamespace } from '../../../src/apps'; +import * as mocks from '../../fixtures/credential/key.json'; +import * as https from '../../../src/common/providers/https'; +import * as debug from '../../../src/common/debug'; + +/** Represents a test case for a Task Queue Function */ +interface TaskTest { + // An http request, mocking a subset of https.Request. + httpRequest: any; + + // The expected format of the request passed to the handler. + expectedData: any; + + taskFunction?: (data: any, context: TaskContext) => void | Promise; + + taskFunction2?: (request: Request) => void | Promise; + + // The expected shape of the http response returned to the callable SDK. + expectedStatus: number; +} + +// Runs a TaskTest test. +export async function runTaskTest(test: TaskTest): Promise { + const taskQueueFunctionV1 = onDispatchHandler((data, context) => { + expect(data).to.deep.equal(test.expectedData); + if (test.taskFunction) { + test.taskFunction(data, context); + } + }); + + const responseV1 = await runHandler(taskQueueFunctionV1, test.httpRequest); + expect(responseV1.status).to.equal(test.expectedStatus); + + const taskQueueFunctionV2 = onDispatchHandler((request) => { + expect(request.data).to.deep.equal(test.expectedData); + if (test.taskFunction2) { + test.taskFunction2(request); + } + }); + + const responseV2 = await runHandler(taskQueueFunctionV2, test.httpRequest); + expect(responseV2.status).to.equal(test.expectedStatus); +} + +describe('onEnqueueHandler', () => { + let app: firebase.app.App; + + before(() => { + const credential = { + getAccessToken: () => { + return Promise.resolve({ + expires_in: 1000, + access_token: 'fake', + }); + }, + getCertificate: () => { + return { + projectId: 'aProjectId', + }; + }, + }; + app = firebase.initializeApp({ + projectId: 'aProjectId', + credential, + }); + Object.defineProperty(appsNamespace(), 'admin', { get: () => app }); + }); + + after(() => { + app.delete(); + delete appsNamespace.singleton; + }); + + it('should handle success', () => { + return runTaskTest({ + httpRequest: mockRequest({ foo: 'bar' }), + expectedData: { foo: 'bar' }, + expectedStatus: 204, + }); + }); + + it('should reject bad method', () => { + const req = mockRequest(null); + req.method = 'GET'; + return runTaskTest({ + httpRequest: req, + expectedData: null, + expectedStatus: 400, + }); + }); + + it('should ignore charset', () => { + return runTaskTest({ + httpRequest: mockRequest(null, 'application/json; charset=utf-8'), + expectedData: null, + expectedStatus: 204, + }); + }); + + it('should reject bad content type', () => { + return runTaskTest({ + httpRequest: mockRequest(null, 'text/plain'), + expectedData: null, + expectedStatus: 400, + }); + }); + + it('should reject extra body fields', () => { + const req = mockRequest(null); + req.body.extra = 'bad'; + return runTaskTest({ + httpRequest: req, + expectedData: null, + expectedStatus: 400, + }); + }); + + it('should handle unhandled error', () => { + return runTaskTest({ + httpRequest: mockRequest(null), + expectedData: null, + taskFunction: (data, context) => { + throw new Error(`ceci n'est pas une error`); + }, + taskFunction2: (request) => { + throw new Error(`cece n'est pas une error`); + }, + expectedStatus: 500, + }); + }); + + it('should handle unknown error status', () => { + return runTaskTest({ + httpRequest: mockRequest(null), + expectedData: null, + taskFunction: (data, context) => { + throw new https.HttpsError('THIS_IS_NOT_VALID' as any, 'nope'); + }, + taskFunction2: (request) => { + throw new https.HttpsError('THIS_IS_NOT_VALID' as any, 'nope'); + }, + expectedStatus: 500, + }); + }); + + it('should handle well-formed error', () => { + return runTaskTest({ + httpRequest: mockRequest(null), + expectedData: null, + taskFunction: (data, context) => { + throw new https.HttpsError('not-found', 'i am error'); + }, + taskFunction2: (request) => { + throw new https.HttpsError('not-found', 'i am error'); + }, + expectedStatus: 404, + }); + }); + + it('should handle auth', async () => { + const mock = mockFetchPublicKeys(); + const projectId = appsNamespace().admin.options.projectId; + const idToken = generateIdToken(projectId); + await runTaskTest({ + httpRequest: mockRequest(null, 'application/json', { + authorization: 'Bearer ' + idToken, + }), + expectedData: null, + taskFunction: (data, context) => { + checkAuthContext(context, projectId, mocks.user_id); + return null; + }, + taskFunction2: (request) => { + checkAuthContext(request, projectId, mocks.user_id); + return null; + }, + expectedStatus: 204, + }); + mock.done(); + }); + + it('should reject bad auth', async () => { + const projectId = appsNamespace().admin.options.projectId; + const idToken = generateUnsignedIdToken(projectId); + await runTaskTest({ + httpRequest: mockRequest(null, 'application/json', { + authorization: 'Bearer ' + idToken, + }), + expectedData: null, + expectedStatus: 401, + }); + }); + + describe('skip token verification debug mode support', () => { + before(() => { + sinon + .stub(debug, 'isDebugFeatureEnabled') + .withArgs('skipTokenVerification') + .returns(true); + }); + + after(() => { + sinon.verifyAndRestore(); + }); + + it('should skip auth token verification', async () => { + const projectId = appsNamespace().admin.options.projectId; + const idToken = generateUnsignedIdToken(projectId); + await runTaskTest({ + httpRequest: mockRequest(null, 'application/json', { + authorization: 'Bearer ' + idToken, + }), + expectedData: null, + taskFunction: (data, context) => { + checkAuthContext(context, projectId, mocks.user_id); + }, + taskFunction2: (request) => { + checkAuthContext(request, projectId, mocks.user_id); + }, + expectedStatus: 204, + }); + }); + }); +}); diff --git a/spec/helper.ts b/spec/helper.ts new file mode 100644 index 000000000..f39b96e04 --- /dev/null +++ b/spec/helper.ts @@ -0,0 +1,127 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as express from 'express'; +import { expect } from 'chai'; + +import * as https from '../src/common/providers/https'; +import * as tasks from '../src/common/providers/tasks'; + +/** + * RunHandlerResult contains the data from an express.Response. + */ +export interface RunHandlerResult { + status: number; + headers: { [name: string]: string }; + body: any; +} + +/** + * Runs an express handler with a given request asynchronously and returns the + * data populated into the response. + */ +export function runHandler( + handler: express.Handler, + request: https.Request +): Promise { + return new Promise((resolve, reject) => { + // MockResponse mocks an express.Response. + // This class lives here so it can reference resolve and reject. + class MockResponse { + private statusCode = 0; + private headers: { [name: string]: string } = {}; + private callback: Function; + + public status(code: number) { + this.statusCode = code; + return this; + } + + // Headers are only set by the cors handler. + public setHeader(name: string, value: string) { + this.headers[name] = value; + } + + public getHeader(name: string): string { + return this.headers[name]; + } + + public send(body: any) { + resolve({ + status: this.statusCode, + headers: this.headers, + body, + }); + if (this.callback) { + this.callback(); + } + } + + public end() { + this.send(undefined); + } + + public on(event: string, callback: Function) { + if (event !== 'finish') { + throw new Error('MockResponse only implements the finish event'); + } + this.callback = callback; + } + } + + const response = new MockResponse(); + handler(request, response as any, () => undefined); + }); +} + +export function checkAuthContext( + context: https.CallableContext | https.CallableRequest | tasks.TaskContext, + projectId: string, + userId: string +) { + expect(context.auth).to.not.be.undefined; + expect(context.auth).to.not.be.null; + expect(context.auth.uid).to.equal(userId); + expect(context.auth.token.uid).to.equal(userId); + expect(context.auth.token.sub).to.equal(userId); + expect(context.auth.token.aud).to.equal(projectId); + + // TaskContext & TaskRequest don't have instanceIdToken + if ({}.hasOwnProperty.call(context, 'instanceIdToken')) { + expect((context as https.CallableContext).instanceIdToken).to.be.undefined; + } +} + +export function checkAppCheckContext( + context: https.CallableContext | https.CallableRequest, + projectId: string, + appId: string +) { + 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; +} diff --git a/spec/runtime/loader.spec.ts b/spec/runtime/loader.spec.ts index 6db4370fd..c75400064 100644 --- a/spec/runtime/loader.spec.ts +++ b/spec/runtime/loader.spec.ts @@ -4,9 +4,9 @@ import { expect } from 'chai'; import * as loader from '../../src/runtime/loader'; import * as functions from '../../src/index'; import { - ManifestStack, ManifestEndpoint, ManifestRequiredAPI, + ManifestStack, } from '../../src/runtime/manifest'; describe('extractStack', () => { @@ -44,7 +44,7 @@ describe('extractStack', () => { it('extracts stack with required APIs', () => { const module = { - taskq: functions.https.taskQueue().onDispatch(() => {}), + taskq: functions.tasks.taskQueue().onDispatch(() => {}), }; const endpoints: Record = {}; diff --git a/spec/v1/providers/https.spec.ts b/spec/v1/providers/https.spec.ts index 22e6d52fd..7046ad25e 100644 --- a/spec/v1/providers/https.spec.ts +++ b/spec/v1/providers/https.spec.ts @@ -21,7 +21,6 @@ // SOFTWARE. import { expect } from 'chai'; -import * as express from 'express'; import * as functions from '../../../src/index'; import * as https from '../../../src/providers/https'; @@ -29,68 +28,7 @@ import { expectedResponseHeaders, MockRequest, } from '../../fixtures/mockrequest'; - -/** - * RunHandlerResult contains the data from an express.Response. - */ -interface RunHandlerResult { - status: number; - headers: { [name: string]: string }; - body: any; -} - -function runHandler( - handler: express.Handler, - request: https.Request -): Promise { - return new Promise((resolve, reject) => { - // MockResponse mocks an express.Response. - // This class lives here so it can reference resolve and reject. - class MockResponse { - private statusCode = 0; - private headers: { [name: string]: string } = {}; - private callback: Function; - - public status(code: number) { - this.statusCode = code; - return this; - } - - // Headers are only set by the cors handler. - public setHeader(name: string, value: string) { - this.headers[name] = value; - } - - public getHeader(name: string): string { - return this.headers[name]; - } - - public send(body: any) { - resolve({ - status: this.statusCode, - headers: this.headers, - body, - }); - if (this.callback) { - this.callback(); - } - } - - public end() { - this.send(undefined); - } - - public on(event: string, callback: Function) { - if (event !== 'finish') { - throw new Error('MockResponse only implements the finish event'); - } - this.callback = callback; - } - } - const response = new MockResponse(); - handler(request, response as any, () => undefined); - }); -} +import { runHandler } from '../../helper'; describe('CloudHttpsBuilder', () => { describe('#onRequest', () => { @@ -146,14 +84,6 @@ describe('handler namespace', () => { expect(result.__endpoint).to.be.undefined; }); }); - - describe('#onEnqueue', () => { - it('should return an empty trigger', () => { - const result = functions.handler.https.taskQueue.onEnqueue(() => null); - expect(result.__trigger).to.deep.equal({}); - expect(result.__endpoint).to.be.undefined; - }); - }); }); describe('#onCall', () => { @@ -231,137 +161,6 @@ describe('#onCall', () => { }); }); -describe('#onEnqueue', () => { - it('should return a trigger/endpoint with appropriate values', () => { - const result = https - .taskQueue({ - rateLimits: { - maxBurstSize: 20, - maxConcurrentDispatches: 30, - maxDispatchesPerSecond: 40, - }, - retryConfig: { - maxAttempts: 5, - maxBackoffSeconds: 20, - maxDoublings: 3, - minBackoffSeconds: 5, - }, - invoker: 'private', - }) - .onDispatch(() => {}); - - expect(result.__trigger).to.deep.equal({ - taskQueueTrigger: { - rateLimits: { - maxBurstSize: 20, - maxConcurrentDispatches: 30, - maxDispatchesPerSecond: 40, - }, - retryConfig: { - maxAttempts: 5, - maxBackoffSeconds: 20, - maxDoublings: 3, - minBackoffSeconds: 5, - }, - invoker: ['private'], - }, - }); - - expect(result.__endpoint).to.deep.equal({ - platform: 'gcfv1', - taskQueueTrigger: { - rateLimits: { - maxBurstSize: 20, - maxConcurrentDispatches: 30, - maxDispatchesPerSecond: 40, - }, - retryConfig: { - maxAttempts: 5, - maxBackoffSeconds: 20, - maxDoublings: 3, - minBackoffSeconds: 5, - }, - invoker: ['private'], - }, - }); - }); - - it('should allow both region and runtime options to be set', () => { - const fn = functions - .region('us-east1') - .runWith({ - timeoutSeconds: 90, - memory: '256MB', - }) - .https.taskQueue({ retryConfig: { maxAttempts: 5 } }) - .onDispatch(() => null); - - expect(fn.__trigger).to.deep.equal({ - regions: ['us-east1'], - availableMemoryMb: 256, - timeout: '90s', - taskQueueTrigger: { - retryConfig: { - maxAttempts: 5, - }, - }, - }); - - expect(fn.__endpoint).to.deep.equal({ - platform: 'gcfv1', - region: ['us-east1'], - availableMemoryMb: 256, - timeoutSeconds: 90, - taskQueueTrigger: { - retryConfig: { - maxAttempts: 5, - }, - }, - }); - }); - - it('has a .run method', async () => { - const data = 'data'; - const context = { - auth: { - uid: 'abc', - token: 'token' as any, - }, - }; - let done = false; - const cf = https.taskQueue().onDispatch((d, c) => { - expect(d).to.equal(data); - expect(c).to.deep.equal(context); - done = true; - }); - - await cf.run(data, context); - expect(done).to.be.true; - }); - - // Regression test for firebase-functions#947 - it('should lock to the v1 API even with function.length == 1', async () => { - let gotData: Record; - const func = https.taskQueue().onDispatch((data) => { - gotData = data; - }); - - const req = new MockRequest( - { - data: { foo: 'bar' }, - }, - { - 'content-type': 'application/json', - } - ); - req.method = 'POST'; - - const response = await runHandler(func, req as any); - expect(response.status).to.equal(204); - expect(gotData).to.deep.equal({ foo: 'bar' }); - }); -}); - describe('callable CORS', () => { it('handles OPTIONS preflight', async () => { const func = https.onCall((data, context) => { diff --git a/spec/v1/providers/tasks.spec.ts b/spec/v1/providers/tasks.spec.ts new file mode 100644 index 000000000..640ceed68 --- /dev/null +++ b/spec/v1/providers/tasks.spec.ts @@ -0,0 +1,165 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as functions from '../../../src'; +import { taskQueue } from '../../../src/providers/tasks'; +import { MockRequest } from '../../fixtures/mockrequest'; +import { runHandler } from '../../helper'; + +describe('#onDispatch', () => { + it('should return a trigger/endpoint with appropriate values', () => { + const result = taskQueue({ + rateLimits: { + maxConcurrentDispatches: 30, + maxDispatchesPerSecond: 40, + }, + retryConfig: { + maxAttempts: 5, + maxRetrySeconds: 10, + maxBackoffSeconds: 20, + maxDoublings: 3, + minBackoffSeconds: 5, + }, + invoker: 'private', + }).onDispatch(() => {}); + + expect(result.__trigger).to.deep.equal({ + taskQueueTrigger: { + rateLimits: { + maxConcurrentDispatches: 30, + maxDispatchesPerSecond: 40, + }, + retryConfig: { + maxAttempts: 5, + maxRetrySeconds: 10, + maxBackoffSeconds: 20, + maxDoublings: 3, + minBackoffSeconds: 5, + }, + invoker: ['private'], + }, + }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv1', + taskQueueTrigger: { + rateLimits: { + maxConcurrentDispatches: 30, + maxDispatchesPerSecond: 40, + }, + retryConfig: { + maxAttempts: 5, + maxRetrySeconds: 10, + maxBackoffSeconds: 20, + maxDoublings: 3, + minBackoffSeconds: 5, + }, + invoker: ['private'], + }, + }); + }); + + it('should allow both region and runtime options to be set', () => { + const fn = functions + .region('us-east1') + .runWith({ + timeoutSeconds: 90, + memory: '256MB', + }) + .tasks.taskQueue({ retryConfig: { maxAttempts: 5 } }) + .onDispatch(() => null); + + expect(fn.__trigger).to.deep.equal({ + regions: ['us-east1'], + availableMemoryMb: 256, + timeout: '90s', + taskQueueTrigger: { + retryConfig: { + maxAttempts: 5, + }, + }, + }); + + expect(fn.__endpoint).to.deep.equal({ + platform: 'gcfv1', + region: ['us-east1'], + availableMemoryMb: 256, + timeoutSeconds: 90, + taskQueueTrigger: { + retryConfig: { + maxAttempts: 5, + }, + }, + }); + }); + + it('has a .run method', async () => { + const data = 'data'; + const context = { + auth: { + uid: 'abc', + token: 'token' as any, + }, + }; + let done = false; + const cf = taskQueue().onDispatch((d, c) => { + expect(d).to.equal(data); + expect(c).to.deep.equal(context); + done = true; + }); + + await cf.run(data, context); + expect(done).to.be.true; + }); + + // Regression test for firebase-functions#947 + it('should lock to the v1 API even with function.length == 1', async () => { + let gotData: Record; + const func = taskQueue().onDispatch((data) => { + gotData = data; + }); + + const req = new MockRequest( + { + data: { foo: 'bar' }, + }, + { + 'content-type': 'application/json', + } + ); + req.method = 'POST'; + + const response = await runHandler(func, req as any); + expect(response.status).to.equal(204); + expect(gotData).to.deep.equal({ foo: 'bar' }); + }); +}); + +describe('handler namespace', () => { + it('should return an empty trigger', () => { + const result = functions.handler.tasks.taskQueue.onDispatch(() => null); + expect(result.__trigger).to.deep.equal({}); + expect(result.__endpoint).to.be.undefined; + }); +}); diff --git a/spec/v2/providers/alerts/alerts.spec.ts b/spec/v2/providers/alerts/alerts.spec.ts index 6ad78a811..8c4741f43 100644 --- a/spec/v2/providers/alerts/alerts.spec.ts +++ b/spec/v2/providers/alerts/alerts.spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import * as options from '../../../../src/v2/options'; import * as alerts from '../../../../src/v2/providers/alerts'; -import { FULL_ENDPOINT, FULL_OPTIONS } from '../helpers'; +import { FULL_ENDPOINT, FULL_OPTIONS } from '../fixtures'; const ALERT_TYPE = 'new-alert-type'; const APPID = '123456789'; diff --git a/spec/v2/providers/alerts/appDistribution.spec.ts b/spec/v2/providers/alerts/appDistribution.spec.ts index 3242c7e07..5fb112daa 100644 --- a/spec/v2/providers/alerts/appDistribution.spec.ts +++ b/spec/v2/providers/alerts/appDistribution.spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import * as alerts from '../../../../src/v2/providers/alerts'; import * as appDistribution from '../../../../src/v2/providers/alerts/appDistribution'; -import { FULL_ENDPOINT, FULL_OPTIONS } from '../helpers'; +import { FULL_ENDPOINT, FULL_OPTIONS } from '../fixtures'; const APPID = '123456789'; const myHandler = () => 42; diff --git a/spec/v2/providers/alerts/billing.spec.ts b/spec/v2/providers/alerts/billing.spec.ts index f8e0eeebb..4bf5f3080 100644 --- a/spec/v2/providers/alerts/billing.spec.ts +++ b/spec/v2/providers/alerts/billing.spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import * as alerts from '../../../../src/v2/providers/alerts'; import * as billing from '../../../../src/v2/providers/alerts/billing'; -import { FULL_ENDPOINT, FULL_OPTIONS } from '../helpers'; +import { FULL_ENDPOINT, FULL_OPTIONS } from '../fixtures'; const ALERT_TYPE = 'new-alert-type'; const myHandler = () => 42; diff --git a/spec/v2/providers/alerts/crashlytics.spec.ts b/spec/v2/providers/alerts/crashlytics.spec.ts index 1ea1c4f0e..08956d8c7 100644 --- a/spec/v2/providers/alerts/crashlytics.spec.ts +++ b/spec/v2/providers/alerts/crashlytics.spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import * as alerts from '../../../../src/v2/providers/alerts'; import * as crashlytics from '../../../../src/v2/providers/alerts/crashlytics'; -import { FULL_ENDPOINT, FULL_OPTIONS } from '../helpers'; +import { FULL_ENDPOINT, FULL_OPTIONS } from '../fixtures'; const ALERT_TYPE = 'new-alert-type'; const APPID = '123456789'; diff --git a/spec/v2/providers/helpers.ts b/spec/v2/providers/fixtures.ts similarity index 100% rename from spec/v2/providers/helpers.ts rename to spec/v2/providers/fixtures.ts diff --git a/spec/v2/providers/https.spec.ts b/spec/v2/providers/https.spec.ts index 5f0f7eb74..587be89c6 100644 --- a/spec/v2/providers/https.spec.ts +++ b/spec/v2/providers/https.spec.ts @@ -1,5 +1,26 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as express from 'express'; import * as options from '../../../src/v2/options'; import * as https from '../../../src/v2/providers/https'; @@ -7,70 +28,8 @@ import { expectedResponseHeaders, MockRequest, } from '../../fixtures/mockrequest'; -import { FULL_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from './helpers'; - -/** - * RunHandlerResult contains the data from an express.Response. - */ -interface RunHandlerResult { - status: number; - headers: { [name: string]: string }; - body: any; -} - -function runHandler( - handler: express.Handler, - request: https.Request -): Promise { - return new Promise((resolve, reject) => { - // MockResponse mocks an express.Response. - // This class lives here so it can reference resolve and reject. - class MockResponse { - private statusCode = 0; - private headers: { [name: string]: string } = {}; - private callback: Function; - - public status(code: number) { - this.statusCode = code; - return this; - } - - // Headers are only set by the cors handler. - public setHeader(name: string, value: string) { - this.headers[name] = value; - } - - public getHeader(name: string): string { - return this.headers[name]; - } - - public send(body: any) { - resolve({ - status: this.statusCode, - headers: this.headers, - body, - }); - if (this.callback) { - this.callback(); - } - } - - public end() { - this.send(undefined); - } - - public on(event: string, callback: Function) { - if (event !== 'finish') { - throw new Error('MockResponse only implements the finish event'); - } - this.callback = callback; - } - } - - const response = new MockResponse(); - handler(request, response as any, () => undefined); - }); -} +import { FULL_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from './fixtures'; +import { runHandler } from '../../helper'; describe('onRequest', () => { beforeEach(() => { @@ -415,139 +374,3 @@ describe('onCall', () => { https.onCall((request: https.CallableRequest) => `Hello, ${request.data}`); }); }); - -describe('onTaskEnqueue', () => { - beforeEach(() => { - options.setGlobalOptions({}); - process.env.GCLOUD_PROJECT = 'aProject'; - }); - - afterEach(() => { - delete process.env.GCLOUD_PROJECT; - }); - - it('should return a minimal trigger with appropriate values', () => { - const result = https.onTaskDispatched(() => {}); - expect(result.__trigger).to.deep.equal({ - apiVersion: 2, - platform: 'gcfv2', - taskQueueTrigger: {}, - labels: {}, - }); - }); - - it('should create a complex trigger with appropriate values', () => { - const result = https.onTaskDispatched( - { - ...FULL_OPTIONS, - retryConfig: { - maxAttempts: 4, - maxDoublings: 3, - minBackoffSeconds: 1, - maxBackoffSeconds: 2, - }, - rateLimits: { - maxBurstSize: 10, - maxConcurrentDispatches: 5, - maxDispatchesPerSecond: 10, - }, - invoker: 'private', - }, - () => {} - ); - expect(result.__trigger).to.deep.equal({ - ...FULL_TRIGGER, - taskQueueTrigger: { - retryConfig: { - maxAttempts: 4, - maxDoublings: 3, - minBackoffSeconds: 1, - maxBackoffSeconds: 2, - }, - rateLimits: { - maxBurstSize: 10, - maxConcurrentDispatches: 5, - maxDispatchesPerSecond: 10, - }, - invoker: ['private'], - }, - }); - }); - - it('should merge options and globalOptions', () => { - options.setGlobalOptions({ - concurrency: 20, - region: 'europe-west1', - minInstances: 1, - }); - - const result = https.onTaskDispatched( - { - region: 'us-west1', - minInstances: 3, - }, - (request) => {} - ); - - expect(result.__trigger).to.deep.equal({ - apiVersion: 2, - platform: 'gcfv2', - taskQueueTrigger: {}, - concurrency: 20, - minInstances: 3, - regions: ['us-west1'], - labels: {}, - }); - }); - - it('has a .run method', async () => { - const request: any = { - data: 'data', - auth: { - uid: 'abc', - token: 'token', - }, - }; - const cf = https.onTaskDispatched((r) => { - expect(r.data).to.deep.equal(request.data); - expect(r.auth).to.deep.equal(request.auth); - }); - - await cf.run(request); - }); - - it('should be an express handler', async () => { - const func = https.onTaskDispatched((request) => {}); - - const req = new MockRequest( - { - data: {}, - }, - { - 'content-type': 'application/json', - origin: 'example.com', - } - ); - req.method = 'POST'; - - const resp = await runHandler(func, req as any); - expect(resp.status).to.equal(204); - }); - - // These tests pass if the code transpiles - it('allows desirable syntax', () => { - https.onTaskDispatched((request: https.TaskRequest) => { - // There should be no lint warnings that data is not a string. - console.log(`hello, ${request.data}`); - }); - https.onTaskDispatched((request: https.TaskRequest) => { - console.log(`hello, ${request.data}`); - }); - https.onTaskDispatched((request: https.TaskRequest) => { - console.log(`hello, ${request.data}`); - }); - https.onTaskDispatched((request: https.TaskRequest) => { - console.log(`Hello, ${request.data}`); - }); - }); -}); diff --git a/spec/v2/providers/pubsub.spec.ts b/spec/v2/providers/pubsub.spec.ts index f48e10f72..73e5b55ab 100644 --- a/spec/v2/providers/pubsub.spec.ts +++ b/spec/v2/providers/pubsub.spec.ts @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { CloudEvent } from '../../../src/v2/core'; import * as options from '../../../src/v2/options'; import * as pubsub from '../../../src/v2/providers/pubsub'; -import { FULL_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from './helpers'; +import { FULL_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from './fixtures'; const EVENT_TRIGGER = { eventType: 'google.cloud.pubsub.topic.v1.messagePublished', diff --git a/spec/v2/providers/storage.spec.ts b/spec/v2/providers/storage.spec.ts index 0471b6931..1c6d7c265 100644 --- a/spec/v2/providers/storage.spec.ts +++ b/spec/v2/providers/storage.spec.ts @@ -3,7 +3,7 @@ import * as sinon from 'sinon'; import * as config from '../../../src/config'; import * as options from '../../../src/v2/options'; import * as storage from '../../../src/v2/providers/storage'; -import { FULL_OPTIONS } from './helpers'; +import { FULL_OPTIONS } from './fixtures'; const EVENT_TRIGGER = { eventType: 'event-type', diff --git a/spec/v2/providers/tasks.spec.ts b/spec/v2/providers/tasks.spec.ts new file mode 100644 index 000000000..4ef4704db --- /dev/null +++ b/spec/v2/providers/tasks.spec.ts @@ -0,0 +1,201 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as options from '../../../src/v2/options'; +import { onTaskDispatched, Request } from '../../../src/v2/providers/tasks'; +import { FULL_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from './fixtures'; +import { MockRequest } from '../../fixtures/mockrequest'; +import { runHandler } from '../../helper'; + +describe('onTaskDispatched', () => { + beforeEach(() => { + options.setGlobalOptions({}); + process.env.GCLOUD_PROJECT = 'aProject'; + }); + + afterEach(() => { + delete process.env.GCLOUD_PROJECT; + }); + + it('should return a minimal trigger/endpoint with appropriate values', () => { + const result = onTaskDispatched(() => {}); + + expect(result.__trigger).to.deep.equal({ + apiVersion: 2, + platform: 'gcfv2', + taskQueueTrigger: {}, + labels: {}, + }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + taskQueueTrigger: {}, + labels: {}, + }); + }); + + it('should create a complex trigger/endpoint with appropriate values', () => { + const result = onTaskDispatched( + { + ...FULL_OPTIONS, + retryConfig: { + maxAttempts: 4, + maxRetrySeconds: 10, + maxDoublings: 3, + minBackoffSeconds: 1, + maxBackoffSeconds: 2, + }, + rateLimits: { + maxConcurrentDispatches: 5, + maxDispatchesPerSecond: 10, + }, + invoker: 'private', + }, + () => {} + ); + + expect(result.__trigger).to.deep.equal({ + ...FULL_TRIGGER, + taskQueueTrigger: { + retryConfig: { + maxAttempts: 4, + maxRetrySeconds: 10, + maxDoublings: 3, + minBackoffSeconds: 1, + maxBackoffSeconds: 2, + }, + rateLimits: { + maxConcurrentDispatches: 5, + maxDispatchesPerSecond: 10, + }, + invoker: ['private'], + }, + }); + + expect(result.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + platform: 'gcfv2', + taskQueueTrigger: { + retryConfig: { + maxAttempts: 4, + maxRetrySeconds: 10, + maxDoublings: 3, + minBackoffSeconds: 1, + maxBackoffSeconds: 2, + }, + rateLimits: { + maxConcurrentDispatches: 5, + maxDispatchesPerSecond: 10, + }, + invoker: ['private'], + }, + }); + }); + + it('should merge options and globalOptions', () => { + options.setGlobalOptions({ + concurrency: 20, + region: 'europe-west1', + minInstances: 1, + }); + + const result = onTaskDispatched( + { + region: 'us-west1', + minInstances: 3, + }, + (request) => {} + ); + + expect(result.__trigger).to.deep.equal({ + apiVersion: 2, + platform: 'gcfv2', + taskQueueTrigger: {}, + concurrency: 20, + minInstances: 3, + regions: ['us-west1'], + labels: {}, + }); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + taskQueueTrigger: {}, + concurrency: 20, + minInstances: 3, + region: ['us-west1'], + labels: {}, + }); + }); + + it('has a .run method', async () => { + const request: any = { + data: 'data', + auth: { + uid: 'abc', + token: 'token', + }, + }; + const cf = onTaskDispatched((r) => { + expect(r.data).to.deep.equal(request.data); + expect(r.auth).to.deep.equal(request.auth); + }); + + await cf.run(request); + }); + + it('should be an express handler', async () => { + const func = onTaskDispatched((request) => {}); + + const req = new MockRequest( + { + data: {}, + }, + { + 'content-type': 'application/json', + origin: 'example.com', + } + ); + req.method = 'POST'; + + const resp = await runHandler(func, req as any); + expect(resp.status).to.equal(204); + }); + + // These tests pass if the code transpiles + it('allows desirable syntax', () => { + onTaskDispatched((request: Request) => { + // There should be no lint warnings that data is not a string. + console.log(`hello, ${request.data}`); + }); + onTaskDispatched((request: Request) => { + console.log(`hello, ${request.data}`); + }); + onTaskDispatched((request: Request) => { + console.log(`hello, ${request.data}`); + }); + onTaskDispatched((request: Request) => { + console.log(`Hello, ${request.data}`); + }); + }); +}); diff --git a/src/common/providers/https.ts b/src/common/providers/https.ts index 5421ebb7b..76e02cd37 100644 --- a/src/common/providers/https.ts +++ b/src/common/providers/https.ts @@ -29,7 +29,8 @@ import * as logger from '../../logger'; // TODO(inlined): Decide whether we want to un-version apps or whether we want a // different strategy import { apps } from '../../apps'; -import { isDebugFeatureEnabled } from '../../common/debug'; +import { isDebugFeatureEnabled } from '../debug'; +import { TaskContext } from './tasks'; const JWT_REGEX = /^[a-zA-Z0-9\-_=]+?\.[a-zA-Z0-9\-_=]+?\.([a-zA-Z0-9\-_=]+)?$/; @@ -169,68 +170,6 @@ export interface CallableRequest { rawRequest: Request; } -/** How a task should be retried in the event of a non-2xx return. */ -export interface TaskRetryConfig { - /** - * Maximum number of times a request should be attempted. - * If left unspecified, will default to 3. - */ - maxAttempts?: number; - - /** - * The maximum amount of time to wait between attempts. - * If left unspecified will default to 1hr. - */ - maxBackoffSeconds?: number; - - /** - * The maximum number of times to double the backoff between - * retries. If left unspecified will default to 16. - */ - maxDoublings?: number; - - /** - * The minimum time to wait between attempts. If left unspecified - * will default to 100ms. - */ - minBackoffSeconds?: number; -} - -/** How congestion control should be applied to the function. */ -export interface TaskRateLimits { - // If left unspecified, will default to 100 - maxBurstSize?: number; - - // If left unspecified, wild default to 1000 - maxConcurrentDispatches?: number; - - // If left unspecified, will default to 500 - maxDispatchesPerSecond?: number; -} - -/** Metadata about a call to a Task Queue function. */ -export interface TaskContext { - /** - * The result of decoding and verifying an ODIC token. - */ - auth?: AuthData; -} - -/** - * The request used to call a Task Queue function. - */ -export interface TaskRequest { - /** - * The parameters used by a client when calling this function. - */ - data: T; - - /** - * The result of decoding and verifying an ODIC token. - */ - auth?: AuthData; -} - /** * The set of Firebase Functions status codes. The codes are the same at the * ones exposed by gRPC here: @@ -419,7 +358,7 @@ interface HttpResponseBody { /** @hidden */ // Returns true if req is a properly formatted callable request. -function isValidRequest(req: Request): req is HttpRequest { +export function isValidRequest(req: Request): req is HttpRequest { // The body must not be empty. if (!req.body) { logger.warn('Request is missing body.'); @@ -670,7 +609,7 @@ async function checkTokens( } /** @interanl */ -async function checkAuthToken( +export async function checkAuthToken( req: Request, ctx: CallableContext | TaskContext ): Promise { @@ -739,8 +678,6 @@ type v1CallableHandler = ( context: CallableContext ) => any | Promise; type v2CallableHandler = (request: CallableRequest) => Res; -type v1TaskHandler = (data: any, context: TaskContext) => void | Promise; -type v2TaskHandler = (request: TaskRequest) => void | Promise; /** @internal **/ export interface CallableOptions { @@ -828,50 +765,3 @@ function wrapOnCallHandler( } }; } - -/** @internal */ -export function onDispatchHandler( - handler: v1TaskHandler | v2TaskHandler -): (req: Request, res: express.Response) => Promise { - return async (req: Request, res: express.Response): Promise => { - try { - if (!isValidRequest(req)) { - logger.error('Invalid request, unable to process.'); - throw new HttpsError('invalid-argument', 'Bad Request'); - } - - const context: TaskContext = {}; - const status = await checkAuthToken(req, context); - // Note: this should never happen since task queue functions are guarded by IAM. - if (status === 'INVALID') { - throw new HttpsError('unauthenticated', 'Unauthenticated'); - } - - const data: Req = decode(req.body.data); - if (handler.length === 2) { - await handler(data, context); - } else { - const arg: TaskRequest = { - ...context, - data, - }; - // For some reason the type system isn't picking up that the handler - // is a one argument function. - await (handler as any)(arg); - } - - res.status(204).end(); - } catch (err) { - if (!(err instanceof HttpsError)) { - // This doesn't count as an 'explicit' error. - logger.error('Unhandled error', err); - err = new HttpsError('internal', 'INTERNAL'); - } - - const { status } = err.httpErrorCode; - const body = { error: err.toJSON() }; - - res.status(status).send(body); - } - }; -} diff --git a/src/common/providers/tasks.ts b/src/common/providers/tasks.ts new file mode 100644 index 000000000..57cecd215 --- /dev/null +++ b/src/common/providers/tasks.ts @@ -0,0 +1,147 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as express from 'express'; +import * as firebase from 'firebase-admin'; + +import * as logger from '../../logger'; +import * as https from './https'; + +/** How a task should be retried in the event of a non-2xx return. */ +export interface RetryConfig { + /** + * Maximum number of times a request should be attempted. + * If left unspecified, will default to 3. + */ + maxAttempts?: number; + + /** + * Maximum amount of time for retrying failed task. + * If left unspecified will retry indefinitely. + */ + maxRetrySeconds?: number; + + /** + * The maximum amount of time to wait between attempts. + * If left unspecified will default to 1hr. + */ + maxBackoffSeconds?: number; + + /** + * The maximum number of times to double the backoff between + * retries. If left unspecified will default to 16. + */ + maxDoublings?: number; + + /** + * The minimum time to wait between attempts. If left unspecified + * will default to 100ms. + */ + minBackoffSeconds?: number; +} + +/** How congestion control should be applied to the function. */ +export interface RateLimits { + // If left unspecified, wild default to 1000 + maxConcurrentDispatches?: number; + + // If left unspecified, will default to 500 + maxDispatchesPerSecond?: number; +} + +export interface AuthData { + uid: string; + token: firebase.auth.DecodedIdToken; +} + +/** Metadata about a call to a Task Queue function. */ +export interface TaskContext { + /** + * The result of decoding and verifying an ODIC token. + */ + auth?: AuthData; +} + +/** + * The request used to call a Task Queue function. + */ +export interface Request { + /** + * The parameters used by a client when calling this function. + */ + data: T; + + /** + * The result of decoding and verifying an ODIC token. + */ + auth?: AuthData; +} + +type v1TaskHandler = (data: any, context: TaskContext) => void | Promise; +type v2TaskHandler = (request: Request) => void | Promise; + +/** @internal */ +export function onDispatchHandler( + handler: v1TaskHandler | v2TaskHandler +): (req: https.Request, res: express.Response) => Promise { + return async (req: https.Request, res: express.Response): Promise => { + try { + if (!https.isValidRequest(req)) { + logger.error('Invalid request, unable to process.'); + throw new https.HttpsError('invalid-argument', 'Bad Request'); + } + + const context: TaskContext = {}; + const status = await https.checkAuthToken(req, context); + // Note: this should never happen since task queue functions are guarded by IAM. + if (status === 'INVALID') { + throw new https.HttpsError('unauthenticated', 'Unauthenticated'); + } + + const data: Req = https.decode(req.body.data); + if (handler.length === 2) { + await handler(data, context); + } else { + const arg: Request = { + ...context, + data, + }; + // For some reason the type system isn't picking up that the handler + // is a one argument function. + await (handler as any)(arg); + } + + res.status(204).end(); + } catch (err) { + if (!(err instanceof https.HttpsError)) { + // This doesn't count as an 'explicit' error. + logger.error('Unhandled error', err); + err = new https.HttpsError('internal', 'INTERNAL'); + } + + const { status } = err.httpErrorCode; + const body = { error: err.toJSON() }; + + res.status(status).send(body); + } + }; +} diff --git a/src/function-builder.ts b/src/function-builder.ts index cf52bfb56..88f58bb1c 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -42,6 +42,7 @@ 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 tasks from './providers/tasks'; import * as testLab from './providers/testLab'; /** @@ -367,14 +368,18 @@ export class FunctionBuilder { context: https.CallableContext ) => any | Promise ) => https._onCallWithOptions(handler, this.options), + }; + } + get tasks() { + return { /** * Declares a task queue function for clients to call using a Firebase Admin SDK. * @param options Configurations for the task queue function. */ /** @hidden */ - taskQueue: (options?: https.TaskQueueOptions) => { - return new https.TaskQueueBuilder(options, this.options); + taskQueue: (options?: tasks.TaskQueueOptions) => { + return new tasks.TaskQueueBuilder(options, this.options); }, }; } diff --git a/src/handler-builder.ts b/src/handler-builder.ts index c89f3c936..efa0d9f4c 100644 --- a/src/handler-builder.ts +++ b/src/handler-builder.ts @@ -32,6 +32,7 @@ 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 tasks from './providers/tasks'; import * as testLab from './providers/testLab'; /** @@ -86,16 +87,29 @@ export class HandlerBuilder { func.__requiredAPIs = undefined; return func; }, - /** @hidden */ + }; + } + + /** + * Create a handler for tasks functions. + * + * @example + * ```javascript + * exports.myFunction = functions.handler.tasks.onDispatch((data, context) => { ... }) + * ``` + */ + /** @hidden */ + get tasks() { + return { get taskQueue() { return { - onEnqueue( + onDispatch: ( handler: ( data: any, - context: https.TaskContext + context: tasks.TaskContext ) => void | Promise - ) { - const builder = new https.TaskQueueBuilder(); + ): HttpsFunction => { + const builder = new tasks.TaskQueueBuilder(); const func = builder.onDispatch(handler); func.__trigger = {}; func.__endpoint = undefined; diff --git a/src/index.ts b/src/index.ts index dbb42003c..a77ba3ff9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -29,6 +29,7 @@ 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 tasks from './providers/tasks'; import * as testLab from './providers/testLab'; import * as apps from './apps'; @@ -49,6 +50,7 @@ export { pubsub, remoteConfig, storage, + tasks, testLab, logger, }; diff --git a/src/providers/https.ts b/src/providers/https.ts index dc62458a7..842ebd84c 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -28,37 +28,17 @@ import { optionsToTrigger, Runnable, } from '../cloud-functions'; -import { - convertIfPresent, - convertInvoker, - copyIfPresent, -} from '../common/encoding'; -import { ManifestEndpoint, ManifestRequiredAPI } from '../runtime/manifest'; +import { convertIfPresent, convertInvoker } from '../common/encoding'; import { CallableContext, FunctionsErrorCode, HttpsError, onCallHandler, - onDispatchHandler, Request, - TaskContext, - TaskRateLimits, - TaskRetryConfig, } from '../common/providers/https'; import { DeploymentOptions } from '../function-configuration'; -export { - Request, - CallableContext, - FunctionsErrorCode, - HttpsError, - /** @hidden */ - TaskRetryConfig as TaskRetryPolicy, - /** @hidden */ - TaskRateLimits, - /** @hidden */ - TaskContext, -}; +export { Request, CallableContext, FunctionsErrorCode, HttpsError }; /** * Handle HTTP requests. @@ -81,101 +61,6 @@ export function onCall( return _onCallWithOptions(handler, {}); } -/** - * Configurations for Task Queue Functions. - * @hidden - */ -export interface TaskQueueOptions { - retryConfig?: TaskRetryConfig; - rateLimits?: TaskRateLimits; - - /** - * Who can enqueue tasks for this function. - * If left unspecified, only service accounts which have - * roles/cloudtasks.enqueuer and roles/cloudfunctions.invoker - * will have permissions. - */ - invoker?: 'private' | string | string[]; -} - -/** @hidden */ -export interface TaskQueueFunction { - (req: Request, res: express.Response): Promise; - __trigger: unknown; - __endpoint: ManifestEndpoint; - __requiredAPIs?: ManifestRequiredAPI[]; - run(data: any, context: TaskContext): void | Promise; -} - -/** @hidden */ -export class TaskQueueBuilder { - /** @internal */ - constructor( - private readonly tqOpts?: TaskQueueOptions, - private readonly depOpts?: DeploymentOptions - ) {} - - onDispatch( - handler: (data: any, context: TaskContext) => void | Promise - ): TaskQueueFunction { - // onEnqueueHandler sniffs the function length of the passed-in callback - // and the user could have only tried to listen to data. Wrap their handler - // in another handler to avoid accidentally triggering the v2 API - const fixedLen = (data: any, context: TaskContext) => - handler(data, context); - const func: any = onDispatchHandler(fixedLen); - - func.__trigger = { - ...optionsToTrigger(this.depOpts || {}), - taskQueueTrigger: {}, - }; - copyIfPresent(func.__trigger.taskQueueTrigger, this.tqOpts, 'retryConfig'); - copyIfPresent(func.__trigger.taskQueueTrigger, this.tqOpts, 'rateLimits'); - convertIfPresent( - func.__trigger.taskQueueTrigger, - this.tqOpts, - 'invoker', - 'invoker', - convertInvoker - ); - - func.__endpoint = { - platform: 'gcfv1', - ...optionsToEndpoint(this.depOpts), - taskQueueTrigger: {}, - }; - copyIfPresent(func.__endpoint.taskQueueTrigger, this.tqOpts, 'retryConfig'); - copyIfPresent(func.__endpoint.taskQueueTrigger, this.tqOpts, 'rateLimits'); - convertIfPresent( - func.__endpoint.taskQueueTrigger, - this.tqOpts, - 'invoker', - 'invoker', - convertInvoker - ); - - func.__requiredAPIs = [ - { - api: 'cloudtasks.googleapis.com', - reason: 'Needed for task queue functions', - }, - ]; - - func.run = handler; - - return func; - } -} - -/** - * Declares a function that can handle tasks enqueued using the Firebase Admin SDK. - * @param options Configuration for the Task Queue that feeds into this function. - * @hidden - */ -export function taskQueue(options?: TaskQueueOptions): TaskQueueBuilder { - return new TaskQueueBuilder(options); -} - /** @hidden */ export function _onRequestWithOptions( handler: (req: Request, resp: express.Response) => void | Promise, diff --git a/src/providers/tasks.ts b/src/providers/tasks.ts new file mode 100644 index 000000000..5d9520497 --- /dev/null +++ b/src/providers/tasks.ts @@ -0,0 +1,145 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as express from 'express'; + +import { Request } from '../common/providers/https'; +import { ManifestEndpoint, ManifestRequiredAPI } from '../runtime/manifest'; +import { DeploymentOptions } from '../function-configuration'; +import { optionsToEndpoint, optionsToTrigger } from '../cloud-functions'; +import { + convertIfPresent, + convertInvoker, + copyIfPresent, +} from '../common/encoding'; +import { + onDispatchHandler, + TaskContext, + RateLimits, + RetryConfig, +} from '../common/providers/tasks'; + +export { + /** @hidden */ + RetryConfig as RetryPolicy, + /** @hidden */ + RateLimits, + /** @hidden */ + TaskContext, +}; + +/** + * Configurations for Task Queue Functions. + * @hidden + */ +export interface TaskQueueOptions { + retryConfig?: RetryConfig; + rateLimits?: RateLimits; + + /** + * Who can enqueue tasks for this function. + * If left unspecified, only service accounts which have + * roles/cloudtasks.enqueuer and roles/cloudfunctions.invoker + * will have permissions. + */ + invoker?: 'private' | string | string[]; +} + +/** @hidden */ +export interface TaskQueueFunction { + (req: Request, res: express.Response): Promise; + + __trigger: unknown; + __endpoint: ManifestEndpoint; + __requiredAPIs?: ManifestRequiredAPI[]; + + run(data: any, context: TaskContext): void | Promise; +} + +/** @hidden */ +export class TaskQueueBuilder { + /** @internal */ + constructor( + private readonly tqOpts?: TaskQueueOptions, + private readonly depOpts?: DeploymentOptions + ) {} + + onDispatch( + handler: (data: any, context: TaskContext) => void | Promise + ): TaskQueueFunction { + // onEnqueueHandler sniffs the function length of the passed-in callback + // and the user could have only tried to listen to data. Wrap their handler + // in another handler to avoid accidentally triggering the v2 API + const fixedLen = (data: any, context: TaskContext) => + handler(data, context); + const func: any = onDispatchHandler(fixedLen); + + func.__trigger = { + ...optionsToTrigger(this.depOpts || {}), + taskQueueTrigger: {}, + }; + copyIfPresent(func.__trigger.taskQueueTrigger, this.tqOpts, 'retryConfig'); + copyIfPresent(func.__trigger.taskQueueTrigger, this.tqOpts, 'rateLimits'); + convertIfPresent( + func.__trigger.taskQueueTrigger, + this.tqOpts, + 'invoker', + 'invoker', + convertInvoker + ); + + func.__endpoint = { + platform: 'gcfv1', + ...optionsToEndpoint(this.depOpts), + taskQueueTrigger: {}, + }; + copyIfPresent(func.__endpoint.taskQueueTrigger, this.tqOpts, 'retryConfig'); + copyIfPresent(func.__endpoint.taskQueueTrigger, this.tqOpts, 'rateLimits'); + convertIfPresent( + func.__endpoint.taskQueueTrigger, + this.tqOpts, + 'invoker', + 'invoker', + convertInvoker + ); + + func.__requiredAPIs = [ + { + api: 'cloudtasks.googleapis.com', + reason: 'Needed for task queue functions', + }, + ]; + + func.run = handler; + + return func; + } +} + +/** + * Declares a function that can handle tasks enqueued using the Firebase Admin SDK. + * @param options Configuration for the Task Queue that feeds into this function. + * @hidden + */ +export function taskQueue(options?: TaskQueueOptions): TaskQueueBuilder { + return new TaskQueueBuilder(options); +} diff --git a/src/v2/index.ts b/src/v2/index.ts index 3de9b749f..3f6873810 100644 --- a/src/v2/index.ts +++ b/src/v2/index.ts @@ -26,8 +26,9 @@ import * as alerts from './providers/alerts'; import * as https from './providers/https'; import * as pubsub from './providers/pubsub'; import * as storage from './providers/storage'; +import * as tasks from './providers/tasks'; -export { https, pubsub, storage, logger, params, alerts }; +export { alerts, https, pubsub, storage, logger, params, tasks }; export { setGlobalOptions, GlobalOptions } from './options'; diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index f7b51e508..24367759c 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -22,11 +22,7 @@ import * as cors from 'cors'; import * as express from 'express'; -import { - convertIfPresent, - convertInvoker, - copyIfPresent, -} from '../../common/encoding'; +import { convertIfPresent, convertInvoker } from '../../common/encoding'; import * as options from '../options'; import { @@ -34,23 +30,11 @@ import { FunctionsErrorCode, HttpsError, onCallHandler, - onDispatchHandler, Request, - TaskRateLimits, - TaskRequest, - TaskRetryConfig, } from '../../common/providers/https'; import { ManifestEndpoint } from '../../runtime/manifest'; -export { - Request, - CallableRequest, - FunctionsErrorCode, - HttpsError, - TaskRateLimits, - TaskRequest, - TaskRetryConfig as TaskRetryPolicy, -}; +export { Request, CallableRequest, FunctionsErrorCode, HttpsError }; export interface HttpsOptions extends Omit { region?: @@ -60,18 +44,6 @@ export interface HttpsOptions extends Omit { cors?: string | boolean | RegExp | Array; } -export interface TaskQueueOptions extends options.GlobalOptions { - retryConfig?: TaskRetryConfig; - rateLimits?: TaskRateLimits; - /** - * Who can enqueue tasks for this function. - * If left unspecified, only service accounts which have - * roles/cloudtasks.enqueuer and roles/cloudfunctions.invoker - * will have permissions. - */ - invoker?: 'private' | string | string[]; -} - export type HttpsFunction = (( req: Request, res: express.Response @@ -82,9 +54,6 @@ export type HttpsFunction = (( export interface CallableFunction extends HttpsFunction { run(data: CallableRequest): Return; } -export interface TaskQueueFunction extends HttpsFunction { - run(data: TaskRequest): void | Promise; -} export function onRequest( opts: HttpsOptions, @@ -269,72 +238,3 @@ export function onCall>( func.run = handler; return func; } - -/** Handle a request sent to a Cloud Tasks queue. */ -export function onTaskDispatched( - handler: (request: TaskRequest) => void | Promise -): TaskQueueFunction; - -/** Handle a request sent to a Cloud Tasks queue. */ -export function onTaskDispatched( - options: TaskQueueOptions, - handler: (request: TaskRequest) => void | Promise -): TaskQueueFunction; - -export function onTaskDispatched( - optsOrHandler: - | TaskQueueOptions - | ((request: TaskRequest) => void | Promise), - handler?: (request: TaskRequest) => void | Promise -): TaskQueueFunction { - let opts: TaskQueueOptions; - if (arguments.length == 1) { - opts = {}; - handler = optsOrHandler as ( - request: TaskRequest - ) => void | Promise; - } else { - opts = optsOrHandler as TaskQueueOptions; - } - - // onEnqueueHandler sniffs the function length to determine which API to present. - // fix the length to prevent api versions from being mismatched. - const fixedLen = (req: TaskRequest) => handler(req); - const func: any = onDispatchHandler(fixedLen); - - Object.defineProperty(func, '__trigger', { - get: () => { - const baseOpts = options.optionsToTriggerAnnotations( - options.getGlobalOptions() - ); - // global options calls region a scalar and https allows it to be an array, - // but optionsToTriggerAnnotations handles both cases. - const specificOpts = options.optionsToTriggerAnnotations( - opts as options.GlobalOptions - ); - const taskQueueTrigger: Record = {}; - copyIfPresent(taskQueueTrigger, opts, 'retryConfig', 'rateLimits'); - convertIfPresent( - taskQueueTrigger, - opts, - 'invoker', - 'invoker', - convertInvoker - ); - return { - apiVersion: 2, - platform: 'gcfv2', - ...baseOpts, - ...specificOpts, - labels: { - ...baseOpts?.labels, - ...specificOpts?.labels, - }, - taskQueueTrigger, - }; - }, - }); - - func.run = handler; - return func; -} diff --git a/src/v2/providers/tasks.ts b/src/v2/providers/tasks.ts new file mode 100644 index 000000000..ab5892bb5 --- /dev/null +++ b/src/v2/providers/tasks.ts @@ -0,0 +1,150 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as options from '../options'; +import { + convertIfPresent, + convertInvoker, + copyIfPresent, +} from '../../common/encoding'; +import { HttpsFunction } from './https'; +import { + AuthData, + RateLimits, + Request, + RetryConfig, + onDispatchHandler, +} from '../../common/providers/tasks'; + +export { AuthData, RateLimits, Request, RetryConfig as RetryPolicy }; + +export interface TaskQueueOptions extends options.GlobalOptions { + retryConfig?: RetryConfig; + rateLimits?: RateLimits; + /** + * Who can enqueue tasks for this function. + * If left unspecified, only service accounts which have + * roles/cloudtasks.enqueuer and roles/cloudfunctions.invoker + * will have permissions. + */ + invoker?: 'private' | string | string[]; +} + +export interface TaskQueueFunction extends HttpsFunction { + run(data: Request): void | Promise; +} + +/** Handle a request sent to a Cloud Tasks queue. */ +export function onTaskDispatched( + handler: (request: Request) => void | Promise +): TaskQueueFunction; +/** Handle a request sent to a Cloud Tasks queue. */ +export function onTaskDispatched( + options: TaskQueueOptions, + handler: (request: Request) => void | Promise +): TaskQueueFunction; +export function onTaskDispatched( + optsOrHandler: + | TaskQueueOptions + | ((request: Request) => void | Promise), + handler?: (request: Request) => void | Promise +): TaskQueueFunction { + let opts: TaskQueueOptions; + if (arguments.length == 1) { + opts = {}; + handler = optsOrHandler as (request: Request) => void | Promise; + } else { + opts = optsOrHandler as TaskQueueOptions; + } + + // onDispatchHandler sniffs the function length to determine which API to present. + // fix the length to prevent api versions from being mismatched. + const fixedLen = (req: Request) => handler(req); + const func: any = onDispatchHandler(fixedLen); + + Object.defineProperty(func, '__trigger', { + get: () => { + const baseOpts = options.optionsToTriggerAnnotations( + options.getGlobalOptions() + ); + // global options calls region a scalar and https allows it to be an array, + // but optionsToTriggerAnnotations handles both cases. + const specificOpts = options.optionsToTriggerAnnotations( + opts as options.GlobalOptions + ); + const taskQueueTrigger: Record = {}; + copyIfPresent(taskQueueTrigger, opts, 'retryConfig', 'rateLimits'); + convertIfPresent( + taskQueueTrigger, + opts, + 'invoker', + 'invoker', + convertInvoker + ); + return { + apiVersion: 2, + platform: 'gcfv2', + ...baseOpts, + ...specificOpts, + labels: { + ...baseOpts?.labels, + ...specificOpts?.labels, + }, + taskQueueTrigger, + }; + }, + }); + + const baseOpts = options.optionsToEndpoint(options.getGlobalOptions()); + // global options calls region a scalar and https allows it to be an array, + // but optionsToManifestEndpoint handles both cases. + const specificOpts = options.optionsToEndpoint(opts as options.GlobalOptions); + func.__endpoint = { + platform: 'gcfv2', + ...baseOpts, + ...specificOpts, + labels: { + ...baseOpts?.labels, + ...specificOpts?.labels, + }, + taskQueueTrigger: {}, + }; + copyIfPresent(func.__endpoint.taskQueueTrigger, opts, 'retryConfig'); + copyIfPresent(func.__endpoint.taskQueueTrigger, opts, 'rateLimits'); + convertIfPresent( + func.__endpoint.taskQueueTrigger, + opts, + 'invoker', + 'invoker', + convertInvoker + ); + + func.__requiredAPIs = [ + { + api: 'cloudtasks.googleapis.com', + reason: 'Needed for task queue functions', + }, + ]; + + func.run = handler; + return func; +} diff --git a/v1/tasks.js b/v1/tasks.js new file mode 100644 index 000000000..ae33ba821 --- /dev/null +++ b/v1/tasks.js @@ -0,0 +1,26 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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. + +// This file is not part of the firebase-functions SDK. It is used to silence the +// imports eslint plugin until it can understand import paths defined by node +// package exports. +// For more information, see github.com/import-js/eslint-plugin-import/issues/1810 diff --git a/v2/tasks.js b/v2/tasks.js new file mode 100644 index 000000000..ae33ba821 --- /dev/null +++ b/v2/tasks.js @@ -0,0 +1,26 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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. + +// This file is not part of the firebase-functions SDK. It is used to silence the +// imports eslint plugin until it can understand import paths defined by node +// package exports. +// For more information, see github.com/import-js/eslint-plugin-import/issues/1810 From 6c1d0f8cab961a8fe72068dadd80f6ce5990d3fb Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Mon, 4 Apr 2022 20:57:43 +0000 Subject: [PATCH 060/370] 3.20.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 16140eff4..4c578b4db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-functions", - "version": "3.19.0", + "version": "3.20.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "firebase-functions", - "version": "3.19.0", + "version": "3.20.0", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index ad1c392c7..9a11f45f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.19.0", + "version": "3.20.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 6fac2f0d005d1eb5151de855801f68a536dd2f53 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Mon, 4 Apr 2022 20:57:46 +0000 Subject: [PATCH 061/370] [firebase-release] Removed change log and reset repo after 3.20.0 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ccafa8c9..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Changes internal structure to be more flexible (#1070). From c32db65981b6d8c71a5949d18329717f08693982 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Wed, 6 Apr 2022 14:45:50 -0400 Subject: [PATCH 062/370] Added eventarc onCustomEventPublished API (#1061) --- package.json | 6 +- spec/v2/providers/eventarc.spec.ts | 162 +++++++++++++++++++++++++++++ src/v2/index.ts | 3 +- src/v2/providers/eventarc.ts | 114 ++++++++++++++++++++ v2/eventarc.js | 26 +++++ 5 files changed, 309 insertions(+), 2 deletions(-) create mode 100644 spec/v2/providers/eventarc.spec.ts create mode 100644 src/v2/providers/eventarc.ts create mode 100644 v2/eventarc.js diff --git a/package.json b/package.json index 9a11f45f1..fefe74b70 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,8 @@ "./v2/alerts": "./lib/v2/providers/alerts/index.js", "./v2/alerts/appDistribution": "./lib/v2/providers/alerts/appDistribution.js", "./v2/alerts/billing": "./lib/v2/providers/alerts/billing.js", - "./v2/alerts/crashlytics": "./lib/v2/providers/alerts/crashlytics.js" + "./v2/alerts/crashlytics": "./lib/v2/providers/alerts/crashlytics.js", + "./v2/eventarc": "./lib/v2/providers/eventarc.js" }, "typesVersions": { "*": { @@ -124,6 +125,9 @@ "v2/base": [ "lib/v2/base" ], + "v2/eventarc": [ + "lib/v2/providers/eventarc" + ], "v2/options": [ "lib/v2/options" ], diff --git a/spec/v2/providers/eventarc.spec.ts b/spec/v2/providers/eventarc.spec.ts new file mode 100644 index 000000000..e7387bbd0 --- /dev/null +++ b/spec/v2/providers/eventarc.spec.ts @@ -0,0 +1,162 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as options from '../../../src/v2/options'; +import * as eventarc from '../../../src/v2/providers/eventarc'; +import { FULL_OPTIONS } from './fixtures'; + +const ENDPOINT_EVENT_TRIGGER = { + eventType: 'event-type', + retry: false, + eventFilters: {}, +}; + +describe('v2/eventarc', () => { + describe('onCustomEventPublished', () => { + beforeEach(() => { + process.env.GCLOUD_PROJECT = 'aProject'; + }); + + afterEach(() => { + options.setGlobalOptions({}); + delete process.env.GCLOUD_PROJECT; + }); + + it('should create a minimal trigger/endpoint with default channel', () => { + const result = eventarc.onCustomEventPublished('event-type', () => 42); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_EVENT_TRIGGER, + channel: 'locations/us-central1/channels/firebase', + }, + }); + }); + + it('should create a minimal trigger/endpoint with opts', () => { + const result = eventarc.onCustomEventPublished( + { eventType: 'event-type', region: 'us-west1' }, + () => 42 + ); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_EVENT_TRIGGER, + channel: 'locations/us-central1/channels/firebase', + }, + region: ['us-west1'], + }); + }); + + it('should create a minimal trigger with channel with opts', () => { + const result = eventarc.onCustomEventPublished( + { + eventType: 'event-type', + channel: 'locations/us-west1/channels/my-channel', + filters: { foo: 'bar' }, + }, + () => 42 + ); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + ...ENDPOINT_EVENT_TRIGGER, + channel: 'locations/us-west1/channels/my-channel', + eventFilters: { + foo: 'bar', + }, + }, + }); + }); + + it('should create a complex trigger/endpoint with appropriate values', () => { + const result = eventarc.onCustomEventPublished( + { + ...FULL_OPTIONS, + eventType: 'event-type', + channel: 'locations/us-west1/channels/my-channel', + }, + () => 42 + ); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + region: ['us-west1'], + availableMemoryMb: 512, + timeoutSeconds: 60, + minInstances: 1, + maxInstances: 3, + concurrency: 20, + vpc: { + connector: 'aConnector', + egressSettings: 'ALL_TRAFFIC', + }, + serviceAccountEmail: 'root@', + ingressSettings: 'ALLOW_ALL', + labels: { + hello: 'world', + }, + eventTrigger: { + ...ENDPOINT_EVENT_TRIGGER, + channel: 'locations/us-west1/channels/my-channel', + }, + }); + }); + + it('should merge options and globalOptions', () => { + options.setGlobalOptions({ + concurrency: 20, + region: 'europe-west1', + minInstances: 1, + }); + + const result = eventarc.onCustomEventPublished( + { + eventType: 'event-type', + channel: 'locations/us-west1/channels/my-channel', + region: 'us-west1', + minInstances: 3, + }, + () => 42 + ); + + expect(result.__endpoint).to.deep.equal({ + platform: 'gcfv2', + concurrency: 20, + minInstances: 3, + region: ['us-west1'], + labels: {}, + eventTrigger: { + ...ENDPOINT_EVENT_TRIGGER, + channel: 'locations/us-west1/channels/my-channel', + }, + }); + }); + }); +}); diff --git a/src/v2/index.ts b/src/v2/index.ts index 3f6873810..71189e76f 100644 --- a/src/v2/index.ts +++ b/src/v2/index.ts @@ -27,8 +27,9 @@ import * as https from './providers/https'; import * as pubsub from './providers/pubsub'; import * as storage from './providers/storage'; import * as tasks from './providers/tasks'; +import * as eventarc from './providers/eventarc'; -export { alerts, https, pubsub, storage, logger, params, tasks }; +export { alerts, https, pubsub, storage, logger, params, tasks, eventarc }; export { setGlobalOptions, GlobalOptions } from './options'; diff --git a/src/v2/providers/eventarc.ts b/src/v2/providers/eventarc.ts new file mode 100644 index 000000000..9c371cef5 --- /dev/null +++ b/src/v2/providers/eventarc.ts @@ -0,0 +1,114 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as options from '../options'; +import { CloudEvent, CloudFunction } from '../core'; +import { copyIfPresent, convertIfPresent } from '../../common/encoding'; +import { ManifestEndpoint } from '../../runtime/manifest'; + +/** Options that can be set on an Eventarc trigger. */ +export interface EventarcTriggerOptions extends options.EventHandlerOptions { + /** + * Type of the event. + */ + eventType: string; + + /** + * ID of the channel. Can be either: + * * fully qualified channel resource name: + * `projects/{project}/locations/{location}/channels/{channel-id}` + * * partial resource name with location and channel ID, in which case + * the runtime project ID of the function will be used: + * `locations/{location}/channels/{channel-id}` + * * partial channel ID, in which case the runtime project ID of the + * function and `us-central1` as location will be used: + * `{channel-id}` + * + * If not specified, the default Firebase channel will be used: + * `projects/{project}/locations/us-central1/channels/firebase` + */ + channel?: string; + + /** + * Eventarc event exact match filter. + */ + filters?: Record; +} + +export type CloudEventHandler = (event: CloudEvent) => any | Promise; + +/** Handle an Eventarc event published on the default channel. */ +export function onCustomEventPublished( + eventType: string, + handler: CloudEventHandler +): CloudFunction>; + +export function onCustomEventPublished( + opts: EventarcTriggerOptions, + handler: CloudEventHandler +): CloudFunction>; + +export function onCustomEventPublished( + eventTypeOrOpts: string | EventarcTriggerOptions, + handler: CloudEventHandler +): CloudFunction> { + let opts: EventarcTriggerOptions; + if (typeof eventTypeOrOpts === 'string') { + opts = { + eventType: eventTypeOrOpts as string, + }; + } else if (typeof eventTypeOrOpts === 'object') { + opts = eventTypeOrOpts as EventarcTriggerOptions; + } + const func = (raw: CloudEvent) => { + return handler(raw as CloudEvent); + }; + + func.run = handler; + + const channel = opts.channel ?? 'locations/us-central1/channels/firebase'; + + const baseOpts = options.optionsToEndpoint(options.getGlobalOptions()); + const specificOpts = options.optionsToEndpoint(opts); + + const endpoint: ManifestEndpoint = { + platform: 'gcfv2', + ...baseOpts, + ...specificOpts, + labels: { + ...baseOpts?.labels, + ...specificOpts?.labels, + }, + eventTrigger: { + eventType: opts.eventType, + eventFilters: {}, + retry: false, + channel, + }, + }; + convertIfPresent(endpoint.eventTrigger, opts, 'eventFilters', 'filters'); + copyIfPresent(endpoint.eventTrigger, opts, 'retry'); + + func.__endpoint = endpoint; + + return func; +} diff --git a/v2/eventarc.js b/v2/eventarc.js new file mode 100644 index 000000000..ae33ba821 --- /dev/null +++ b/v2/eventarc.js @@ -0,0 +1,26 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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. + +// This file is not part of the firebase-functions SDK. It is used to silence the +// imports eslint plugin until it can understand import paths defined by node +// package exports. +// For more information, see github.com/import-js/eslint-plugin-import/issues/1810 From 60a7a40afdaa2c1994b1a114b727fac8a5604c88 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 12 Apr 2022 14:11:23 -0700 Subject: [PATCH 063/370] Require auth token but skip verification for tasks queue functions. (#1073) Previously, we allowed TQ functions to either: 1) Not have any auth token 2) Have admin SDK verified auth tokens. Since TQ functions are guarded by IAM, we instead implement the following behavior: 1) MUST have auth token 2) We do not verify auth token - instead we just decode it and pass it to the handler --- CHANGELOG.md | 1 + spec/common/providers/tasks.spec.ts | 74 +++++++++++------------------ spec/v1/providers/tasks.spec.ts | 1 + spec/v2/providers/tasks.spec.ts | 10 +--- src/common/providers/tasks.ts | 14 ++++-- 5 files changed, 42 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..69f82438a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Improve authorization for tasks. (#1073) diff --git a/spec/common/providers/tasks.spec.ts b/spec/common/providers/tasks.spec.ts index a29731c5a..b511bcb59 100644 --- a/spec/common/providers/tasks.spec.ts +++ b/spec/common/providers/tasks.spec.ts @@ -21,14 +21,12 @@ // SOFTWARE. import { expect } from 'chai'; -import * as sinon from 'sinon'; import * as firebase from 'firebase-admin'; import { checkAuthContext, runHandler } from '../../helper'; import { generateIdToken, generateUnsignedIdToken, - mockFetchPublicKeys, mockRequest, } from '../../fixtures/mockrequest'; import { @@ -39,7 +37,6 @@ import { import { apps as appsNamespace } from '../../../src/apps'; import * as mocks from '../../fixtures/credential/key.json'; import * as https from '../../../src/common/providers/https'; -import * as debug from '../../../src/common/debug'; /** Represents a test case for a Task Queue Function */ interface TaskTest { @@ -83,6 +80,14 @@ export async function runTaskTest(test: TaskTest): Promise { describe('onEnqueueHandler', () => { let app: firebase.app.App; + function mockEnqueueRequest( + data: unknown, + contentType: string = 'application/json', + context: { authorization: string } = { authorization: 'Bearer abc' } + ): ReturnType { + return mockRequest(data, contentType, context); + } + before(() => { const credential = { getAccessToken: () => { @@ -111,7 +116,7 @@ describe('onEnqueueHandler', () => { it('should handle success', () => { return runTaskTest({ - httpRequest: mockRequest({ foo: 'bar' }), + httpRequest: mockEnqueueRequest({ foo: 'bar' }), expectedData: { foo: 'bar' }, expectedStatus: 204, }); @@ -129,7 +134,7 @@ describe('onEnqueueHandler', () => { it('should ignore charset', () => { return runTaskTest({ - httpRequest: mockRequest(null, 'application/json; charset=utf-8'), + httpRequest: mockEnqueueRequest(null, 'application/json; charset=utf-8'), expectedData: null, expectedStatus: 204, }); @@ -137,14 +142,14 @@ describe('onEnqueueHandler', () => { it('should reject bad content type', () => { return runTaskTest({ - httpRequest: mockRequest(null, 'text/plain'), + httpRequest: mockEnqueueRequest(null, 'text/plain'), expectedData: null, expectedStatus: 400, }); }); it('should reject extra body fields', () => { - const req = mockRequest(null); + const req = mockEnqueueRequest(null); req.body.extra = 'bad'; return runTaskTest({ httpRequest: req, @@ -155,7 +160,7 @@ describe('onEnqueueHandler', () => { it('should handle unhandled error', () => { return runTaskTest({ - httpRequest: mockRequest(null), + httpRequest: mockEnqueueRequest(null), expectedData: null, taskFunction: (data, context) => { throw new Error(`ceci n'est pas une error`); @@ -169,7 +174,7 @@ describe('onEnqueueHandler', () => { it('should handle unknown error status', () => { return runTaskTest({ - httpRequest: mockRequest(null), + httpRequest: mockEnqueueRequest(null), expectedData: null, taskFunction: (data, context) => { throw new https.HttpsError('THIS_IS_NOT_VALID' as any, 'nope'); @@ -183,7 +188,7 @@ describe('onEnqueueHandler', () => { it('should handle well-formed error', () => { return runTaskTest({ - httpRequest: mockRequest(null), + httpRequest: mockEnqueueRequest(null), expectedData: null, taskFunction: (data, context) => { throw new https.HttpsError('not-found', 'i am error'); @@ -196,11 +201,10 @@ describe('onEnqueueHandler', () => { }); it('should handle auth', async () => { - const mock = mockFetchPublicKeys(); const projectId = appsNamespace().admin.options.projectId; const idToken = generateIdToken(projectId); await runTaskTest({ - httpRequest: mockRequest(null, 'application/json', { + httpRequest: mockEnqueueRequest(null, 'application/json', { authorization: 'Bearer ' + idToken, }), expectedData: null, @@ -214,49 +218,25 @@ describe('onEnqueueHandler', () => { }, expectedStatus: 204, }); - mock.done(); }); - it('should reject bad auth', async () => { + it('should accept unsigned auth too', async () => { const projectId = appsNamespace().admin.options.projectId; const idToken = generateUnsignedIdToken(projectId); await runTaskTest({ - httpRequest: mockRequest(null, 'application/json', { + httpRequest: mockEnqueueRequest(null, 'application/json', { authorization: 'Bearer ' + idToken, }), expectedData: null, - expectedStatus: 401, - }); - }); - - describe('skip token verification debug mode support', () => { - before(() => { - sinon - .stub(debug, 'isDebugFeatureEnabled') - .withArgs('skipTokenVerification') - .returns(true); - }); - - after(() => { - sinon.verifyAndRestore(); - }); - - it('should skip auth token verification', async () => { - const projectId = appsNamespace().admin.options.projectId; - const idToken = generateUnsignedIdToken(projectId); - await runTaskTest({ - httpRequest: mockRequest(null, 'application/json', { - authorization: 'Bearer ' + idToken, - }), - expectedData: null, - taskFunction: (data, context) => { - checkAuthContext(context, projectId, mocks.user_id); - }, - taskFunction2: (request) => { - checkAuthContext(request, projectId, mocks.user_id); - }, - expectedStatus: 204, - }); + taskFunction: (data, context) => { + checkAuthContext(context, projectId, mocks.user_id); + return null; + }, + taskFunction2: (request) => { + checkAuthContext(request, projectId, mocks.user_id); + return null; + }, + expectedStatus: 204, }); }); }); diff --git a/spec/v1/providers/tasks.spec.ts b/spec/v1/providers/tasks.spec.ts index 640ceed68..20f8c8e00 100644 --- a/spec/v1/providers/tasks.spec.ts +++ b/spec/v1/providers/tasks.spec.ts @@ -146,6 +146,7 @@ describe('#onDispatch', () => { }, { 'content-type': 'application/json', + authorization: 'Bearer abc', } ); req.method = 'POST'; diff --git a/spec/v2/providers/tasks.spec.ts b/spec/v2/providers/tasks.spec.ts index 4ef4704db..d6be998a5 100644 --- a/spec/v2/providers/tasks.spec.ts +++ b/spec/v2/providers/tasks.spec.ts @@ -149,16 +149,9 @@ describe('onTaskDispatched', () => { }); it('has a .run method', async () => { - const request: any = { - data: 'data', - auth: { - uid: 'abc', - token: 'token', - }, - }; + const request: any = { data: 'data' }; const cf = onTaskDispatched((r) => { expect(r.data).to.deep.equal(request.data); - expect(r.auth).to.deep.equal(request.auth); }); await cf.run(request); @@ -173,6 +166,7 @@ describe('onTaskDispatched', () => { }, { 'content-type': 'application/json', + authorization: 'Bearer abc', origin: 'example.com', } ); diff --git a/src/common/providers/tasks.ts b/src/common/providers/tasks.ts index 57cecd215..9c6ffbc23 100644 --- a/src/common/providers/tasks.ts +++ b/src/common/providers/tasks.ts @@ -110,12 +110,20 @@ export function onDispatchHandler( throw new https.HttpsError('invalid-argument', 'Bad Request'); } - const context: TaskContext = {}; - const status = await https.checkAuthToken(req, context); + const authHeader = req.header('Authorization') || ''; + const token = authHeader.match(/^Bearer (.*)$/)?.[1]; // Note: this should never happen since task queue functions are guarded by IAM. - if (status === 'INVALID') { + if (!token) { throw new https.HttpsError('unauthenticated', 'Unauthenticated'); } + // We skip authenticating the token since tq functions are guarded by IAM. + const authToken = await https.unsafeDecodeIdToken(token); + const context: TaskContext = { + auth: { + uid: authToken.uid, + token: authToken, + }, + }; const data: Req = https.decode(req.body.data); if (handler.length === 2) { From 5f8770f2649cec30f3ea197fed7f3f35b8b790b3 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 13 Apr 2022 11:49:24 -0700 Subject: [PATCH 064/370] Modernize integration test (#1074) Small touchups to the existing integration test: 1) Use `node-fetch` to simplify HTTP calls 2) Group v1 triggers (in prep for running v2 trigger tests) 3) Replace use of `console.log` to `functions.logger.info` 4) Get rid of node10 as test target --- integration_test/functions/src/index.ts | 227 ++++++++---------- integration_test/functions/src/test-utils.ts | 38 --- integration_test/functions/src/testing.ts | 10 +- .../functions/src/{ => v1}/auth-tests.ts | 6 +- .../functions/src/{ => v1}/database-tests.ts | 12 +- .../functions/src/{ => v1}/firestore-tests.ts | 2 +- .../functions/src/{ => v1}/https-tests.ts | 2 +- integration_test/functions/src/v1/index.ts | 8 + .../functions/src/{ => v1}/pubsub-tests.ts | 2 +- .../src/{ => v1}/remoteConfig-tests.ts | 2 +- .../functions/src/{ => v1}/storage-tests.ts | 2 +- .../functions/src/{ => v1}/testLab-tests.ts | 4 +- .../functions/src/{ => v1}/testLab-utils.ts | 54 ++--- integration_test/package.json.template | 3 +- integration_test/run_tests.sh | 13 +- 15 files changed, 159 insertions(+), 226 deletions(-) delete mode 100644 integration_test/functions/src/test-utils.ts rename integration_test/functions/src/{ => v1}/auth-tests.ts (94%) rename integration_test/functions/src/{ => v1}/database-tests.ts (85%) rename integration_test/functions/src/{ => v1}/firestore-tests.ts (95%) rename integration_test/functions/src/{ => v1}/https-tests.ts (88%) create mode 100644 integration_test/functions/src/v1/index.ts rename integration_test/functions/src/{ => v1}/pubsub-tests.ts (97%) rename integration_test/functions/src/{ => v1}/remoteConfig-tests.ts (95%) rename integration_test/functions/src/{ => v1}/storage-tests.ts (94%) rename integration_test/functions/src/{ => v1}/testLab-tests.ts (86%) rename integration_test/functions/src/{ => v1}/testLab-utils.ts (72%) diff --git a/integration_test/functions/src/index.ts b/integration_test/functions/src/index.ts index 6865d7dd5..e51536295 100644 --- a/integration_test/functions/src/index.ts +++ b/integration_test/functions/src/index.ts @@ -1,41 +1,32 @@ import { PubSub } from '@google-cloud/pubsub'; import { Request, Response } from 'express'; +import fetch from 'node-fetch'; import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; import * as fs from 'fs'; -import * as https from 'https'; -export * from './pubsub-tests'; -export * from './database-tests'; -export * from './auth-tests'; -export * from './firestore-tests'; -export * from './https-tests'; -export * from './remoteConfig-tests'; -export * from './storage-tests'; -export * from './testLab-tests'; -const numTests = Object.keys(exports).length; // Assumption: every exported function is its own test. +import * as v1 from './v1/index'; +const numTests = Object.keys(v1).filter((k) => + ({}.hasOwnProperty.call(v1[k], '__endpoint')) +).length; +export { v1 }; -import * as utils from './test-utils'; -import * as testLab from './testLab-utils'; +import * as testLab from './v1/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; +admin.initializeApp(); -// TODO(klimt): Get rid of this once the JS client SDK supports callable triggers. -function callHttpsTrigger(name: string, data: any, baseUrl) { - return utils.makeRequest( +function callHttpsTrigger(name: string, data: any) { + return fetch( + `https://${REGION}-${firebaseConfig.projectId}.cloudfunctions.net/${name}`, { method: 'POST', - host: REGION + '-' + firebaseConfig.projectId + '.' + baseUrl, - path: '/' + name, headers: { 'Content-Type': 'application/json', }, - }, - JSON.stringify({ data }) + body: JSON.stringify({ data }), + } ); } @@ -43,41 +34,85 @@ 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: `/v1/projects/${firebaseConfig.projectId}/locations/us-central1/jobs/firebase-schedule-${functionName}-${region}:run`, - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${accessToken.access_token}`, - }, + const response = await fetch( + `https://cloudscheduler.googleapis.com/v1/projects/${firebaseConfig.projectId}/locations/us-central1/jobs/firebase-schedule-${functionName}-${region}:run`, + { + method: 'POST', + 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', () => { - console.log(`Successfully scheduled function ${functionName}`); - resolve(body); - }); - } - ); - request.on('error', (err) => { - console.error('Failed to schedule cloud scheduler job with error', err); - reject(err); - }); - request.write('{}'); - request.end(); - }); + } + ); + if (!response.ok) { + throw new Error(`Failed request with status ${response.status}!`); + } + const data = await response.text(); + functions.logger.log(`Successfully scheduled function ${functionName}`, data); + return; +} + +async function updateRemoteConfig( + testId: string, + accessToken: string +): Promise { + await fetch( + `https://firebaseremoteconfig.googleapis.com/v1/projects/${firebaseConfig.projectId}/remoteConfig`, + { + method: 'PUT', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json; UTF-8', + 'Accept-Encoding': 'gzip', + 'If-Match': '*', + }, + body: JSON.stringify({ version: { description: testId } }), + } + ); +} + +function v1Tests(testId: string, accessToken: string) { + return [ + // 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}`, + }) + .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('v1-callableTests', { foo: 'bar', testId }), + // A Remote Config update to trigger the Remote Config tests. + updateRemoteConfig(testId, accessToken), + // 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('v1-schedule', 'us-central1'), + ]; } export const integrationTests: any = functions @@ -86,12 +121,6 @@ export const integrationTests: any = functions timeoutSeconds: 540, }) .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 testId = admin .database() .ref() @@ -101,71 +130,15 @@ export const integrationTests: any = functions .ref(`testRuns/${testId}/timestamp`) .set(Date.now()); const testIdRef = admin.database().ref(`testRuns/${testId}`); - console.log('testId is: ', testId); + functions.logger.info('testId is: ', testId); fs.writeFile('/tmp/' + testId + '.txt', 'test', () => {}); 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}`, - }) - .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'), - ]); - + const accessToken = await admin.credential + .applicationDefault() + .getAccessToken(); + await Promise.all([...v1Tests(testId, accessToken.access_token)]); // 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...'); + functions.logger.info('Waiting for all tests to report they pass...'); await new Promise((resolve, reject) => { setTimeout(() => reject(new Error('Timeout')), 5 * 60 * 1000); let testsExecuted = 0; @@ -179,7 +152,7 @@ export const integrationTests: any = functions ); return; } - console.log( + functions.logger.info( `${snapshot.key} passed (${testsExecuted} of ${numTests})` ); if (testsExecuted < numTests) { @@ -190,10 +163,10 @@ export const integrationTests: any = functions resolve(); }); }); - console.log('All tests pass!'); + functions.logger.info('All tests pass!'); resp.status(200).send('PASS \n'); } catch (err) { - console.log(`Some tests failed: ${err}`); + functions.logger.info(`Some tests failed: ${err}`, err); resp .status(500) .send( diff --git a/integration_test/functions/src/test-utils.ts b/integration_test/functions/src/test-utils.ts deleted file mode 100644 index eda799a67..000000000 --- a/integration_test/functions/src/test-utils.ts +++ /dev/null @@ -1,38 +0,0 @@ -import * as https from 'https'; - -/** - * Makes an http request asynchronously and returns the response data. - * - * This function wraps the callback-based `http.request()` function with a - * Promise. The returned Promise will be rejected in case the request fails with an - * error, or the response code is not in the 200-299 range. - * - * @param options Request options for the request. - * @param body Optional body to send as part of the request. - * @returns Promise returning the response data as string. - */ -export function makeRequest( - options: https.RequestOptions, - body?: string -): Promise { - return new Promise((resolve, reject) => { - const request = https.request(options, (response) => { - let body = ''; - response.on('data', (chunk) => { - body += chunk; - }); - response.on('end', () => { - if (response.statusCode < 200 || response.statusCode > 299) { - reject(body); - return; - } - resolve(body); - }); - }); - if (body) { - request.write(body); - } - request.on('error', reject); - request.end(); - }); -} diff --git a/integration_test/functions/src/testing.ts b/integration_test/functions/src/testing.ts index 1cb9f7819..1054c7f01 100644 --- a/integration_test/functions/src/testing.ts +++ b/integration_test/functions/src/testing.ts @@ -1,7 +1,7 @@ import * as firebase from 'firebase-admin'; -import { EventContext } from 'firebase-functions'; +import * as functions from 'firebase-functions'; -export type TestCase = (data: T, context?: EventContext) => any; +export type TestCase = (data: T, context?: functions.EventContext) => any; export interface TestCaseMap { [key: string]: TestCase; } @@ -20,7 +20,7 @@ export class TestSuite { return this; } - run(testId: string, data: T, context?: EventContext): Promise { + run(testId: string, data: T, context?: functions.EventContext): Promise { const running: Array> = []; for (const testName in this.tests) { if (!this.tests.hasOwnProperty(testName)) { @@ -30,7 +30,7 @@ export class TestSuite { .then(() => this.tests[testName](data, context)) .then( (result) => { - console.log( + functions.logger.info( `${result ? 'Passed' : 'Failed with successful op'}: ${testName}` ); return { name: testName, passed: !!result }; @@ -47,7 +47,7 @@ export class TestSuite { results.forEach((val) => (sum = sum + val.passed)); const summary = `passed ${sum} of ${running.length}`; const passed = sum === running.length; - console.log(summary); + functions.logger.info(summary); const result = { passed, summary, tests: results }; return firebase .database() diff --git a/integration_test/functions/src/auth-tests.ts b/integration_test/functions/src/v1/auth-tests.ts similarity index 94% rename from integration_test/functions/src/auth-tests.ts rename to integration_test/functions/src/v1/auth-tests.ts index c97c907bc..c82166baf 100644 --- a/integration_test/functions/src/auth-tests.ts +++ b/integration_test/functions/src/v1/auth-tests.ts @@ -1,6 +1,6 @@ import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; -import { expectEq, TestSuite } from './testing'; +import { expectEq, TestSuite } from '../testing'; import UserMetadata = admin.auth.UserRecord; const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1'; @@ -10,7 +10,7 @@ export const createUserTests: any = functions .auth.user() .onCreate((u, c) => { const testId: string = u.displayName; - console.log(`testId is ${testId}`); + functions.logger.info(`testId is ${testId}`); return new TestSuite('auth user onCreate') .it('should have a project as resource', (user, context) => @@ -50,7 +50,7 @@ export const deleteUserTests: any = functions .auth.user() .onDelete((u, c) => { const testId: string = u.displayName; - console.log(`testId is ${testId}`); + functions.logger.info(`testId is ${testId}`); return new TestSuite('auth user onDelete') .it('should have a project as resource', (user, context) => diff --git a/integration_test/functions/src/database-tests.ts b/integration_test/functions/src/v1/database-tests.ts similarity index 85% rename from integration_test/functions/src/database-tests.ts rename to integration_test/functions/src/v1/database-tests.ts index 568d26bf4..040c6786c 100644 --- a/integration_test/functions/src/database-tests.ts +++ b/integration_test/functions/src/v1/database-tests.ts @@ -1,6 +1,6 @@ import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; -import { expectEq, expectMatches, TestSuite } from './testing'; +import { expectEq, expectMatches, TestSuite } from '../testing'; import DataSnapshot = admin.database.DataSnapshot; const testIdFieldName = 'testId'; @@ -11,7 +11,7 @@ export const databaseTests: any = functions .database.ref('dbTests/{testId}/start') .onWrite((ch, ctx) => { if (ch.after.val() === null) { - console.log( + functions.logger.info( 'Event for ' + ctx.params[testIdFieldName] + ' is null; presuming data cleanup, so skipping.' @@ -40,7 +40,7 @@ export const databaseTests: any = functions return expectMatches( url, new RegExp( - `^https://${process.env.GCLOUD_PROJECT}.firebaseio.com/dbTests` + `^https://${process.env.GCLOUD_PROJECT}(-default-rtdb)*.firebaseio.com/dbTests` ) ); }) @@ -50,9 +50,11 @@ export const databaseTests: any = functions }) .it('should have refs resources', (change, context) => - expectEq( + expectMatches( context.resource.name, - `projects/_/instances/${process.env.GCLOUD_PROJECT}/refs/dbTests/${context.params.testId}/start` + new RegExp( + `^projects/_/instances/${process.env.GCLOUD_PROJECT}(-default-rtdb)*/refs/dbTests/${context.params.testId}/start$` + ) ) ) diff --git a/integration_test/functions/src/firestore-tests.ts b/integration_test/functions/src/v1/firestore-tests.ts similarity index 95% rename from integration_test/functions/src/firestore-tests.ts rename to integration_test/functions/src/v1/firestore-tests.ts index 97daadea5..9916e2d3c 100644 --- a/integration_test/functions/src/firestore-tests.ts +++ b/integration_test/functions/src/v1/firestore-tests.ts @@ -1,6 +1,6 @@ import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; -import { expectDeepEq, expectEq, TestSuite } from './testing'; +import { expectDeepEq, expectEq, TestSuite } from '../testing'; import DocumentSnapshot = admin.firestore.DocumentSnapshot; const testIdFieldName = 'documentId'; diff --git a/integration_test/functions/src/https-tests.ts b/integration_test/functions/src/v1/https-tests.ts similarity index 88% rename from integration_test/functions/src/https-tests.ts rename to integration_test/functions/src/v1/https-tests.ts index 18972bd47..103228d7d 100644 --- a/integration_test/functions/src/https-tests.ts +++ b/integration_test/functions/src/v1/https-tests.ts @@ -1,5 +1,5 @@ import * as functions from 'firebase-functions'; -import { expectEq, TestSuite } from './testing'; +import { expectEq, TestSuite } from '../testing'; const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1'; diff --git a/integration_test/functions/src/v1/index.ts b/integration_test/functions/src/v1/index.ts new file mode 100644 index 000000000..6e6717b15 --- /dev/null +++ b/integration_test/functions/src/v1/index.ts @@ -0,0 +1,8 @@ +export * from './pubsub-tests'; +export * from './database-tests'; +export * from './auth-tests'; +export * from './firestore-tests'; +export * from './https-tests'; +export * from './remoteConfig-tests'; +export * from './storage-tests'; +export * from './testLab-tests'; diff --git a/integration_test/functions/src/pubsub-tests.ts b/integration_test/functions/src/v1/pubsub-tests.ts similarity index 97% rename from integration_test/functions/src/pubsub-tests.ts rename to integration_test/functions/src/v1/pubsub-tests.ts index a21c2011a..79f5889c6 100644 --- a/integration_test/functions/src/pubsub-tests.ts +++ b/integration_test/functions/src/v1/pubsub-tests.ts @@ -1,6 +1,6 @@ import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; -import { evaluate, expectEq, success, TestSuite } from './testing'; +import { evaluate, expectEq, success, TestSuite } from '../testing'; import PubsubMessage = functions.pubsub.Message; const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1'; diff --git a/integration_test/functions/src/remoteConfig-tests.ts b/integration_test/functions/src/v1/remoteConfig-tests.ts similarity index 95% rename from integration_test/functions/src/remoteConfig-tests.ts rename to integration_test/functions/src/v1/remoteConfig-tests.ts index 3f2cc8993..a9dbd10d7 100644 --- a/integration_test/functions/src/remoteConfig-tests.ts +++ b/integration_test/functions/src/v1/remoteConfig-tests.ts @@ -1,5 +1,5 @@ import * as functions from 'firebase-functions'; -import { expectEq, TestSuite } from './testing'; +import { expectEq, TestSuite } from '../testing'; import TemplateVersion = functions.remoteConfig.TemplateVersion; const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1'; diff --git a/integration_test/functions/src/storage-tests.ts b/integration_test/functions/src/v1/storage-tests.ts similarity index 94% rename from integration_test/functions/src/storage-tests.ts rename to integration_test/functions/src/v1/storage-tests.ts index df3032f78..4259c5a00 100644 --- a/integration_test/functions/src/storage-tests.ts +++ b/integration_test/functions/src/v1/storage-tests.ts @@ -1,5 +1,5 @@ import * as functions from 'firebase-functions'; -import { expectEq, TestSuite } from './testing'; +import { expectEq, TestSuite } from '../testing'; import ObjectMetadata = functions.storage.ObjectMetadata; const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1'; diff --git a/integration_test/functions/src/testLab-tests.ts b/integration_test/functions/src/v1/testLab-tests.ts similarity index 86% rename from integration_test/functions/src/testLab-tests.ts rename to integration_test/functions/src/v1/testLab-tests.ts index 8e064928a..805b988c8 100644 --- a/integration_test/functions/src/testLab-tests.ts +++ b/integration_test/functions/src/v1/testLab-tests.ts @@ -1,5 +1,5 @@ import * as functions from 'firebase-functions'; -import { expectEq, TestSuite } from './testing'; +import { expectEq, TestSuite } from '../testing'; import TestMatrix = functions.testLab.TestMatrix; const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1'; @@ -13,8 +13,6 @@ export const testLabTests: any = functions return new TestSuite('test matrix complete') .it('should have eventId', (snap, context) => context.eventId) - .it('should have timestamp', (snap, context) => context.timestamp) - .it('should have right eventType', (_, context) => expectEq(context.eventType, 'google.testing.testMatrix.complete') ) diff --git a/integration_test/functions/src/testLab-utils.ts b/integration_test/functions/src/v1/testLab-utils.ts similarity index 72% rename from integration_test/functions/src/testLab-utils.ts rename to integration_test/functions/src/v1/testLab-utils.ts index 3dbb7b763..8f0424d98 100644 --- a/integration_test/functions/src/testLab-utils.ts +++ b/integration_test/functions/src/v1/testLab-utils.ts @@ -1,7 +1,5 @@ +import fetch from 'node-fetch'; import * as admin from 'firebase-admin'; -import * as http from 'http'; -import * as https from 'https'; -import * as utils from './test-utils'; interface AndroidDevice { androidModelId: string; @@ -30,10 +28,16 @@ export async function startTestRun(projectId: string, testId: string) { async function fetchDefaultDevice( accessToken: admin.GoogleOAuthAccessToken ): Promise { - const response = await utils.makeRequest( - requestOptions(accessToken, 'GET', '/v1/testEnvironmentCatalog/ANDROID') + const response = await fetch( + `https://${TESTING_API_SERVICE_NAME}/v1/testEnvironmentCatalog/ANDROID`, + { + headers: { + Authorization: 'Bearer ' + accessToken.access_token, + 'Content-Type': 'application/json', + }, + } ); - const data = JSON.parse(response); + const data = await response.json(); const models = data?.androidDeviceCatalog?.models || []; const defaultModels = models.filter( (m) => @@ -58,17 +62,12 @@ async function fetchDefaultDevice( } as AndroidDevice; } -function createTestMatrix( +async function createTestMatrix( accessToken: admin.GoogleOAuthAccessToken, projectId: string, testId: string, device: AndroidDevice -): Promise { - const options = requestOptions( - accessToken, - 'POST', - '/v1/projects/' + projectId + '/testMatrices' - ); +): Promise { const body = { projectId, testSpecification: { @@ -96,21 +95,16 @@ function createTestMatrix( }, }, }; - return utils.makeRequest(options, JSON.stringify(body)); -} - -function requestOptions( - accessToken: admin.GoogleOAuthAccessToken, - method: string, - path: string -): https.RequestOptions { - return { - method, - hostname: TESTING_API_SERVICE_NAME, - path, - headers: { - Authorization: 'Bearer ' + accessToken.access_token, - 'Content-Type': 'application/json', - }, - }; + await fetch( + `https://${TESTING_API_SERVICE_NAME}/v1/projects/${projectId}/testMatrices`, + { + method: 'POST', + headers: { + Authorization: 'Bearer ' + accessToken.access_token, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + } + ); + return; } diff --git a/integration_test/package.json.template b/integration_test/package.json.template index a5259aeb6..07585cbe2 100644 --- a/integration_test/package.json.template +++ b/integration_test/package.json.template @@ -8,10 +8,11 @@ "@google-cloud/pubsub": "^2.10.0", "firebase-admin": "__FIREBASE_ADMIN__", "firebase-functions": "__SDK_TARBALL__", - "lodash": "~4.17.2" + "node-fetch": "^2.6.7" }, "main": "lib/index.js", "devDependencies": { + "@types/node-fetch": "^2.6.1", "typescript": "~4.2.2" }, "engines": { diff --git a/integration_test/run_tests.sh b/integration_test/run_tests.sh index 498a9b0a0..b78150cc2 100755 --- a/integration_test/run_tests.sh +++ b/integration_test/run_tests.sh @@ -85,9 +85,9 @@ function delete_all_functions { # Try to delete, if there are errors it is because the project is already empty, # in that case do nothing. if [[ "${TOKEN}" == "" ]]; then - firebase functions:delete callableTests createUserTests databaseTests deleteUserTests firestoreTests integrationTests pubsubTests remoteConfigTests testLabTests --force --project=$PROJECT_ID || : & + firebase functions:delete integrationTests v1 --force --project=$PROJECT_ID || : & else - firebase functions:delete callableTests createUserTests databaseTests deleteUserTests firestoreTests integrationTests pubsubTests remoteConfigTests testLabTests --force --project=$PROJECT_ID --token=$TOKEN || : & + firebase functions:delete integrationTests v1 --force --project=$PROJECT_ID --token=$TOKEN || : & fi wait announce "Project emptied." @@ -139,13 +139,8 @@ build_sdk delete_all_functions set_region -for version in 10 12 14 16; do - if [[ "$version" -eq 10 ]]; then - admin_sdk="^9.12.0" - else - admin_sdk="^10.0.0" - fi - create_package_json $TIMESTAMP $version $admin_sdk +for version in 12 14 16; do + create_package_json $TIMESTAMP $version "^10.0.0" install_deps announce "Re-deploying the same functions to Node $version runtime ..." deploy From b8f161450a85818962c6357cdd20e123904ed808 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 13 Apr 2022 13:31:45 -0700 Subject: [PATCH 065/370] Setup v2 triggers for integration tests (#1075) Also remove region setting option of the test since it's not possible to dynamically set region using function config when parsing triggers via container contract. Just adding a single v2 trigger def for now - with the structure introduced here, it should be easy to add more triggers in the near future. --- integration_test/functions/src/index.ts | 95 +++++++++++++++---- integration_test/functions/src/region.ts | 2 + .../functions/src/v1/auth-tests.ts | 3 +- .../functions/src/v1/database-tests.ts | 2 +- .../functions/src/v1/firestore-tests.ts | 2 +- .../functions/src/v1/https-tests.ts | 3 +- .../functions/src/v1/pubsub-tests.ts | 3 +- .../functions/src/v1/remoteConfig-tests.ts | 3 +- .../functions/src/v1/storage-tests.ts | 3 +- .../functions/src/v1/testLab-tests.ts | 2 +- .../functions/src/v1/testLab-utils.ts | 32 ++++--- .../functions/src/v2/https-tests.ts | 10 ++ integration_test/functions/src/v2/index.ts | 5 + integration_test/run_tests.sh | 29 +----- 14 files changed, 122 insertions(+), 72 deletions(-) create mode 100644 integration_test/functions/src/region.ts create mode 100644 integration_test/functions/src/v2/https-tests.ts create mode 100644 integration_test/functions/src/v2/index.ts diff --git a/integration_test/functions/src/index.ts b/integration_test/functions/src/index.ts index e51536295..184b5683d 100644 --- a/integration_test/functions/src/index.ts +++ b/integration_test/functions/src/index.ts @@ -5,20 +5,24 @@ import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; import * as fs from 'fs'; -import * as v1 from './v1/index'; -const numTests = Object.keys(v1).filter((k) => - ({}.hasOwnProperty.call(v1[k], '__endpoint')) -).length; -export { v1 }; +import * as v1 from './v1'; +import * as v2 from './v2'; +const getNumTests = (m: object): number => { + return Object.keys(m).filter((k) => + ({}.hasOwnProperty.call(m[k], '__endpoint')) + ).length; +}; +const numTests = getNumTests(v1) + getNumTests(v2); +export { v1, v2 }; import * as testLab from './v1/testLab-utils'; +import { REGION } from './region'; const firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG); -const REGION = functions.config().functions.test_region; admin.initializeApp(); -function callHttpsTrigger(name: string, data: any) { - return fetch( +async function callHttpsTrigger(name: string, data: any) { + const resp = await fetch( `https://${REGION}-${firebaseConfig.projectId}.cloudfunctions.net/${name}`, { method: 'POST', @@ -28,19 +32,56 @@ function callHttpsTrigger(name: string, data: any) { body: JSON.stringify({ data }), } ); + if (!resp.ok) { + throw Error(resp.statusText); + } +} + +async function callV2HttpsTrigger( + name: string, + data: any, + accessToken: string +) { + let resp = await fetch( + `https://cloudfunctions.googleapis.com/v2beta/projects/${firebaseConfig.projectId}/locations/${REGION}/functions/${name}`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + if (!resp.ok) { + throw new Error(resp.statusText); + } + const fn = await resp.json(); + const uri = fn.serviceConfig?.uri; + if (!uri) { + throw new Error(`Cannot call v2 https trigger ${name} - no uri found`); + } + resp = await fetch(uri, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ data }), + }); + if (!resp.ok) { + throw new Error(resp.statusText); + } } -async function callScheduleTrigger(functionName: string, region: string) { - const accessToken = await admin.credential - .applicationDefault() - .getAccessToken(); +async function callScheduleTrigger( + functionName: string, + region: string, + accessToken: string +) { const response = await fetch( `https://cloudscheduler.googleapis.com/v1/projects/${firebaseConfig.projectId}/locations/us-central1/jobs/firebase-schedule-${functionName}-${region}:run`, { method: 'POST', headers: { 'Content-Type': 'application/json', - Authorization: `Bearer ${accessToken.access_token}`, + Authorization: `Bearer ${accessToken}`, }, } ); @@ -56,7 +97,7 @@ async function updateRemoteConfig( testId: string, accessToken: string ): Promise { - await fetch( + const resp = await fetch( `https://firebaseremoteconfig.googleapis.com/v1/projects/${firebaseConfig.projectId}/remoteConfig`, { method: 'PUT', @@ -69,9 +110,12 @@ async function updateRemoteConfig( body: JSON.stringify({ version: { description: testId } }), } ); + if (!resp.ok) { + throw new Error(resp.statusText); + } } -function v1Tests(testId: string, accessToken: string) { +function v1Tests(testId: string, accessToken: string): Promise[] { return [ // A database write to trigger the Firebase Realtime Database tests. admin @@ -109,9 +153,16 @@ function v1Tests(testId: string, accessToken: string) { .storage() .bucket() .upload('/tmp/' + testId + '.txt'), - testLab.startTestRun(firebaseConfig.projectId, testId), + testLab.startTestRun(firebaseConfig.projectId, testId, accessToken), // Invoke the schedule for our scheduled function to fire - callScheduleTrigger('v1-schedule', 'us-central1'), + callScheduleTrigger('v1-schedule', 'us-central1', accessToken), + ]; +} + +function v2Tests(testId: string, accessToken: string): Promise[] { + return [ + // Invoke a callable HTTPS trigger. + callV2HttpsTrigger('v2-callabletests', { foo: 'bar', testId }, accessToken), ]; } @@ -136,15 +187,21 @@ export const integrationTests: any = functions const accessToken = await admin.credential .applicationDefault() .getAccessToken(); - await Promise.all([...v1Tests(testId, accessToken.access_token)]); + await Promise.all([ + ...v1Tests(testId, accessToken.access_token), + ...v2Tests(testId, accessToken.access_token), + ]); // On test completion, check that all tests pass and reply "PASS", or provide further details. functions.logger.info('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) => { + if (snapshot.key === 'timestamp') { + return; + } testsExecuted += 1; - if (snapshot.key != 'timestamp' && !snapshot.val().passed) { + if (!snapshot.val().passed) { reject( new Error( `test ${snapshot.key} failed; see database for details.` diff --git a/integration_test/functions/src/region.ts b/integration_test/functions/src/region.ts new file mode 100644 index 000000000..4252a4672 --- /dev/null +++ b/integration_test/functions/src/region.ts @@ -0,0 +1,2 @@ +// TODO: Add back support for selecting region for integration test once params is ready. +export const REGION = 'us-central1'; diff --git a/integration_test/functions/src/v1/auth-tests.ts b/integration_test/functions/src/v1/auth-tests.ts index c82166baf..2a8113c84 100644 --- a/integration_test/functions/src/v1/auth-tests.ts +++ b/integration_test/functions/src/v1/auth-tests.ts @@ -1,10 +1,9 @@ import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; import { expectEq, TestSuite } from '../testing'; +import { REGION } from '../region'; import UserMetadata = admin.auth.UserRecord; -const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1'; - export const createUserTests: any = functions .region(REGION) .auth.user() diff --git a/integration_test/functions/src/v1/database-tests.ts b/integration_test/functions/src/v1/database-tests.ts index 040c6786c..891c4ec70 100644 --- a/integration_test/functions/src/v1/database-tests.ts +++ b/integration_test/functions/src/v1/database-tests.ts @@ -1,10 +1,10 @@ import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; import { expectEq, expectMatches, TestSuite } from '../testing'; +import { REGION } from '../region'; import DataSnapshot = admin.database.DataSnapshot; const testIdFieldName = 'testId'; -const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1'; export const databaseTests: any = functions .region(REGION) diff --git a/integration_test/functions/src/v1/firestore-tests.ts b/integration_test/functions/src/v1/firestore-tests.ts index 9916e2d3c..ebd5ccaa8 100644 --- a/integration_test/functions/src/v1/firestore-tests.ts +++ b/integration_test/functions/src/v1/firestore-tests.ts @@ -1,10 +1,10 @@ import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; import { expectDeepEq, expectEq, TestSuite } from '../testing'; +import { REGION } from '../region'; import DocumentSnapshot = admin.firestore.DocumentSnapshot; const testIdFieldName = 'documentId'; -const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1'; export const firestoreTests: any = functions .runWith({ diff --git a/integration_test/functions/src/v1/https-tests.ts b/integration_test/functions/src/v1/https-tests.ts index 103228d7d..8acf8932f 100644 --- a/integration_test/functions/src/v1/https-tests.ts +++ b/integration_test/functions/src/v1/https-tests.ts @@ -1,8 +1,7 @@ import * as functions from 'firebase-functions'; +import { REGION } from '../region'; 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: any) => diff --git a/integration_test/functions/src/v1/pubsub-tests.ts b/integration_test/functions/src/v1/pubsub-tests.ts index 79f5889c6..515708e07 100644 --- a/integration_test/functions/src/v1/pubsub-tests.ts +++ b/integration_test/functions/src/v1/pubsub-tests.ts @@ -1,10 +1,9 @@ import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; +import { REGION } from '../region'; 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 diff --git a/integration_test/functions/src/v1/remoteConfig-tests.ts b/integration_test/functions/src/v1/remoteConfig-tests.ts index a9dbd10d7..47e70461b 100644 --- a/integration_test/functions/src/v1/remoteConfig-tests.ts +++ b/integration_test/functions/src/v1/remoteConfig-tests.ts @@ -1,9 +1,8 @@ import * as functions from 'firebase-functions'; +import { REGION } from '../region'; import { expectEq, TestSuite } from '../testing'; import TemplateVersion = functions.remoteConfig.TemplateVersion; -const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1'; - export const remoteConfigTests: any = functions .region(REGION) .remoteConfig.onUpdate((v, c) => { diff --git a/integration_test/functions/src/v1/storage-tests.ts b/integration_test/functions/src/v1/storage-tests.ts index 4259c5a00..51b947293 100644 --- a/integration_test/functions/src/v1/storage-tests.ts +++ b/integration_test/functions/src/v1/storage-tests.ts @@ -1,9 +1,8 @@ import * as functions from 'firebase-functions'; +import { REGION } from '../region'; 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, diff --git a/integration_test/functions/src/v1/testLab-tests.ts b/integration_test/functions/src/v1/testLab-tests.ts index 805b988c8..8e1986ce0 100644 --- a/integration_test/functions/src/v1/testLab-tests.ts +++ b/integration_test/functions/src/v1/testLab-tests.ts @@ -1,7 +1,7 @@ import * as functions from 'firebase-functions'; +import { REGION } from '../region'; import { expectEq, TestSuite } from '../testing'; import TestMatrix = functions.testLab.TestMatrix; -const REGION = process.env.FIREBASE_FUNCTIONS_TEST_REGION || 'us-central1'; export const testLabTests: any = functions .runWith({ diff --git a/integration_test/functions/src/v1/testLab-utils.ts b/integration_test/functions/src/v1/testLab-utils.ts index 8f0424d98..bd739a708 100644 --- a/integration_test/functions/src/v1/testLab-utils.ts +++ b/integration_test/functions/src/v1/testLab-utils.ts @@ -16,28 +16,31 @@ const TESTING_API_SERVICE_NAME = 'testing.googleapis.com'; * * @param projectId Project for which the test run will be created * @param testId Test id which will be encoded in client info details + * @param accessToken accessToken to attach to requested for authentication */ -export async function startTestRun(projectId: string, testId: string) { - const accessToken = await admin.credential - .applicationDefault() - .getAccessToken(); +export async function startTestRun( + projectId: string, + testId: string, + accessToken: string +) { const device = await fetchDefaultDevice(accessToken); return await createTestMatrix(accessToken, projectId, testId, device); } -async function fetchDefaultDevice( - accessToken: admin.GoogleOAuthAccessToken -): Promise { - const response = await fetch( +async function fetchDefaultDevice(accessToken: string): Promise { + const resp = await fetch( `https://${TESTING_API_SERVICE_NAME}/v1/testEnvironmentCatalog/ANDROID`, { headers: { - Authorization: 'Bearer ' + accessToken.access_token, + Authorization: 'Bearer ' + accessToken, 'Content-Type': 'application/json', }, } ); - const data = await response.json(); + if (!resp.ok) { + throw new Error(resp.statusText); + } + const data = await resp.json(); const models = data?.androidDeviceCatalog?.models || []; const defaultModels = models.filter( (m) => @@ -63,7 +66,7 @@ async function fetchDefaultDevice( } async function createTestMatrix( - accessToken: admin.GoogleOAuthAccessToken, + accessToken: string, projectId: string, testId: string, device: AndroidDevice @@ -95,16 +98,19 @@ async function createTestMatrix( }, }, }; - await fetch( + const resp = await fetch( `https://${TESTING_API_SERVICE_NAME}/v1/projects/${projectId}/testMatrices`, { method: 'POST', headers: { - Authorization: 'Bearer ' + accessToken.access_token, + Authorization: 'Bearer ' + accessToken, 'Content-Type': 'application/json', }, body: JSON.stringify(body), } ); + if (!resp.ok) { + throw new Error(resp.statusText); + } return; } diff --git a/integration_test/functions/src/v2/https-tests.ts b/integration_test/functions/src/v2/https-tests.ts new file mode 100644 index 000000000..4936c48ea --- /dev/null +++ b/integration_test/functions/src/v2/https-tests.ts @@ -0,0 +1,10 @@ +import { onCall } from 'firebase-functions/v2/https'; +import { expectEq, TestSuite } from '../testing'; + +export const callabletests = onCall((req) => { + return new TestSuite('v2 https onCall') + .it('should have the correct data', (data: any) => + expectEq(data?.foo, 'bar') + ) + .run(req.data.testId, req.data); +}); diff --git a/integration_test/functions/src/v2/index.ts b/integration_test/functions/src/v2/index.ts new file mode 100644 index 000000000..089064d9e --- /dev/null +++ b/integration_test/functions/src/v2/index.ts @@ -0,0 +1,5 @@ +import { REGION } from '../region'; +import { setGlobalOptions } from 'firebase-functions/v2'; +setGlobalOptions({ region: REGION }); + +export * from './https-tests'; diff --git a/integration_test/run_tests.sh b/integration_test/run_tests.sh index b78150cc2..6ae86f25f 100755 --- a/integration_test/run_tests.sh +++ b/integration_test/run_tests.sh @@ -38,26 +38,6 @@ 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 -} - # Creates a Package.json from package.json.template # @param timestmap of the current SDK build # @param Node version to test under @@ -85,9 +65,9 @@ function delete_all_functions { # Try to delete, if there are errors it is because the project is already empty, # in that case do nothing. if [[ "${TOKEN}" == "" ]]; then - firebase functions:delete integrationTests v1 --force --project=$PROJECT_ID || : & + firebase functions:delete integrationTests v1 v2 --force --project=$PROJECT_ID || : & else - firebase functions:delete integrationTests v1 --force --project=$PROJECT_ID --token=$TOKEN || : & + firebase functions:delete integrationTests v1 v2 --force --project=$PROJECT_ID --token=$TOKEN || : & fi wait announce "Project emptied." @@ -111,9 +91,6 @@ function run_tests { # causing this script to start failing, but currently we don't have a very # reliable way of determining the URL dynamically. TEST_DOMAIN="cloudfunctions.net" - if [[ "${FIREBASE_FUNCTIONS_URL}" == "https://preprod-cloudfunctions.sandbox.googleapis.com" ]]; then - TEST_DOMAIN="txcloud.net" - fi if [[ "${FIREBASE_FUNCTIONS_TEST_REGION}" == "" ]]; then FIREBASE_FUNCTIONS_TEST_REGION="us-central1" fi @@ -126,7 +103,6 @@ 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" @@ -137,7 +113,6 @@ function cleanup { # Setup build_sdk delete_all_functions -set_region for version in 12 14 16; do create_package_json $TIMESTAMP $version "^10.0.0" From 9a5b23f855f96cfd69e37480b63636cfbe3e39f9 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 14 Apr 2022 17:56:52 +0000 Subject: [PATCH 066/370] 3.20.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4c578b4db..f1d1eeff2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-functions", - "version": "3.20.0", + "version": "3.20.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "firebase-functions", - "version": "3.20.0", + "version": "3.20.1", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index fefe74b70..dc47fb24e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.20.0", + "version": "3.20.1", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 19d7d6e2044d0c8138cc338be834adb1932e408f Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 14 Apr 2022 17:56:54 +0000 Subject: [PATCH 067/370] [firebase-release] Removed change log and reset repo after 3.20.1 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69f82438a..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Improve authorization for tasks. (#1073) From 78dae73c955e7307c6de43b72d3dc834bfc833a0 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Fri, 15 Apr 2022 10:21:17 -0700 Subject: [PATCH 068/370] Add CPU option. Rip out deprecated apiVersion (#1077) * Add CPU option. Rip out deprecated apiVersion * Formatter * PR feedback --- integration_test/functions/src/index.ts | 8 ++--- .../functions/src/v1/auth-tests.ts | 2 +- .../functions/src/v1/database-tests.ts | 2 +- .../functions/src/v1/firestore-tests.ts | 2 +- .../functions/src/v1/testLab-utils.ts | 2 +- integration_test/functions/src/v2/index.ts | 2 +- scripts/bin-test/test.ts | 14 ++++---- spec/common/providers/https.spec.ts | 18 +++++----- spec/common/providers/identity.spec.ts | 12 +++---- spec/common/providers/tasks.spec.ts | 18 +++++----- spec/helper.ts | 2 +- spec/runtime/loader.spec.ts | 8 ++--- spec/v2/providers/eventarc.spec.ts | 19 ++--------- spec/v2/providers/fixtures.ts | 3 +- spec/v2/providers/https.spec.ts | 6 +--- spec/v2/providers/pubsub.spec.ts | 3 -- spec/v2/providers/storage.spec.ts | 34 ++----------------- spec/v2/providers/tasks.spec.ts | 4 +-- src/common/providers/identity.ts | 28 +++++++-------- src/providers/auth.ts | 12 +++---- src/providers/tasks.ts | 8 ++--- src/runtime/loader.ts | 18 +++++----- src/runtime/manifest.ts | 1 + src/v2/index.ts | 2 +- src/v2/options.ts | 19 +++++++++-- src/v2/providers/alerts/appDistribution.ts | 2 +- src/v2/providers/alerts/billing.ts | 2 +- src/v2/providers/alerts/crashlytics.ts | 2 +- src/v2/providers/eventarc.ts | 6 ++-- src/v2/providers/https.ts | 8 +---- src/v2/providers/pubsub.ts | 7 ++-- src/v2/providers/storage.ts | 6 ++-- src/v2/providers/tasks.ts | 7 ++-- 33 files changed, 120 insertions(+), 167 deletions(-) diff --git a/integration_test/functions/src/index.ts b/integration_test/functions/src/index.ts index 184b5683d..57654c8d1 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 fetch from 'node-fetch'; import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; import * as fs from 'fs'; +import fetch from 'node-fetch'; import * as v1 from './v1'; import * as v2 from './v2'; @@ -15,8 +15,8 @@ const getNumTests = (m: object): number => { const numTests = getNumTests(v1) + getNumTests(v2); export { v1, v2 }; -import * as testLab from './v1/testLab-utils'; import { REGION } from './region'; +import * as testLab from './v1/testLab-utils'; const firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG); admin.initializeApp(); @@ -115,7 +115,7 @@ async function updateRemoteConfig( } } -function v1Tests(testId: string, accessToken: string): Promise[] { +function v1Tests(testId: string, accessToken: string): Array> { return [ // A database write to trigger the Firebase Realtime Database tests. admin @@ -159,7 +159,7 @@ function v1Tests(testId: string, accessToken: string): Promise[] { ]; } -function v2Tests(testId: string, accessToken: string): Promise[] { +function v2Tests(testId: string, accessToken: string): Array> { return [ // Invoke a callable HTTPS trigger. callV2HttpsTrigger('v2-callabletests', { foo: 'bar', testId }, accessToken), diff --git a/integration_test/functions/src/v1/auth-tests.ts b/integration_test/functions/src/v1/auth-tests.ts index 2a8113c84..e6daf5162 100644 --- a/integration_test/functions/src/v1/auth-tests.ts +++ b/integration_test/functions/src/v1/auth-tests.ts @@ -1,7 +1,7 @@ import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; -import { expectEq, TestSuite } from '../testing'; import { REGION } from '../region'; +import { expectEq, TestSuite } from '../testing'; import UserMetadata = admin.auth.UserRecord; export const createUserTests: any = functions diff --git a/integration_test/functions/src/v1/database-tests.ts b/integration_test/functions/src/v1/database-tests.ts index 891c4ec70..fe3e29f17 100644 --- a/integration_test/functions/src/v1/database-tests.ts +++ b/integration_test/functions/src/v1/database-tests.ts @@ -1,7 +1,7 @@ import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; -import { expectEq, expectMatches, TestSuite } from '../testing'; import { REGION } from '../region'; +import { expectEq, expectMatches, TestSuite } from '../testing'; import DataSnapshot = admin.database.DataSnapshot; const testIdFieldName = 'testId'; diff --git a/integration_test/functions/src/v1/firestore-tests.ts b/integration_test/functions/src/v1/firestore-tests.ts index ebd5ccaa8..51a93d021 100644 --- a/integration_test/functions/src/v1/firestore-tests.ts +++ b/integration_test/functions/src/v1/firestore-tests.ts @@ -1,7 +1,7 @@ import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; -import { expectDeepEq, expectEq, TestSuite } from '../testing'; import { REGION } from '../region'; +import { expectDeepEq, expectEq, TestSuite } from '../testing'; import DocumentSnapshot = admin.firestore.DocumentSnapshot; const testIdFieldName = 'documentId'; diff --git a/integration_test/functions/src/v1/testLab-utils.ts b/integration_test/functions/src/v1/testLab-utils.ts index bd739a708..870c9b8ec 100644 --- a/integration_test/functions/src/v1/testLab-utils.ts +++ b/integration_test/functions/src/v1/testLab-utils.ts @@ -1,5 +1,5 @@ -import fetch from 'node-fetch'; import * as admin from 'firebase-admin'; +import fetch from 'node-fetch'; interface AndroidDevice { androidModelId: string; diff --git a/integration_test/functions/src/v2/index.ts b/integration_test/functions/src/v2/index.ts index 089064d9e..def01bfbb 100644 --- a/integration_test/functions/src/v2/index.ts +++ b/integration_test/functions/src/v2/index.ts @@ -1,5 +1,5 @@ -import { REGION } from '../region'; import { setGlobalOptions } from 'firebase-functions/v2'; +import { REGION } from '../region'; setGlobalOptions({ region: REGION }); export * from './https-tests'; diff --git a/scripts/bin-test/test.ts b/scripts/bin-test/test.ts index 0ce2f6518..154c2295c 100644 --- a/scripts/bin-test/test.ts +++ b/scripts/bin-test/test.ts @@ -1,12 +1,12 @@ -import * as path from 'path'; import * as subprocess from 'child_process'; +import * as path from 'path'; import { promisify } from 'util'; +import { expect } from 'chai'; +import * as yaml from 'js-yaml'; import fetch from 'node-fetch'; import * as portfinder from 'portfinder'; -import * as yaml from 'js-yaml'; import * as semver from 'semver'; -import { expect } from 'chai'; const TIMEOUT_XL = 20_000; const TIMEOUT_L = 10_000; @@ -43,11 +43,11 @@ const BASE_STACK = { specVersion: 'v1alpha1', }; -type Testcase = { +interface Testcase { name: string; modulePath: string; expected: Record; -}; +} async function retryUntil( fn: () => Promise, @@ -66,7 +66,9 @@ async function retryUntil( }); const retry = (async () => { while (true) { - if (await fn()) break; + if (await fn()) { + break; + } await sleep(); } })(); diff --git a/spec/common/providers/https.spec.ts b/spec/common/providers/https.spec.ts index d1f8703bd..8916a371a 100644 --- a/spec/common/providers/https.spec.ts +++ b/spec/common/providers/https.spec.ts @@ -3,12 +3,9 @@ import * as firebase from 'firebase-admin'; import * as sinon from 'sinon'; import { apps as appsNamespace } from '../../../src/apps'; -import { - checkAppCheckContext, - checkAuthContext, - runHandler, - RunHandlerResult, -} from '../../helper'; +import * as debug from '../../../src/common/debug'; +import * as https from '../../../src/common/providers/https'; +import * as mocks from '../../fixtures/credential/key.json'; import { expectedResponseHeaders, generateAppCheckToken, @@ -19,9 +16,12 @@ import { mockFetchPublicKeys, mockRequest, } from '../../fixtures/mockrequest'; -import * as debug from '../../../src/common/debug'; -import * as https from '../../../src/common/providers/https'; -import * as mocks from '../../fixtures/credential/key.json'; +import { + checkAppCheckContext, + checkAuthContext, + runHandler, + RunHandlerResult, +} from '../../helper'; /** * A CallTest is a specification for a test of a callable function that diff --git a/spec/common/providers/identity.spec.ts b/spec/common/providers/identity.spec.ts index caa656e3e..617f0f859 100644 --- a/spec/common/providers/identity.spec.ts +++ b/spec/common/providers/identity.spec.ts @@ -20,11 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +import { expect } from 'chai'; import * as express from 'express'; import * as jwt from 'jsonwebtoken'; import * as sinon from 'sinon'; import * as identity from '../../../src/common/providers/identity'; -import { expect } from 'chai'; const PROJECT = 'my-project'; const EVENT = 'EVENT_TYPE'; @@ -308,8 +308,8 @@ describe('identity', () => { kid: '123456', }, { - '123456': '7890', - '2468': '1357', + 123456: '7890', + 2468: '1357', } ) ).to.eq('7890'); @@ -438,7 +438,7 @@ describe('identity', () => { const decoded = { aud: VALID_URL, iss: `${identity.JWT_ISSUER}${PROJECT}`, - sub: sub, + sub, event_type: EVENT, } as identity.DecodedPayload; @@ -515,8 +515,8 @@ describe('identity', () => { let jwtVerifyStub: sinon.SinonStub; const keysCache = { publicKeys: { - '123456': '7890', - '2468': '1357', + 123456: '7890', + 2468: '1357', }, publicKeysExpireAt: time + identity.INVALID_TOKEN_BUFFER + 10000, }; diff --git a/spec/common/providers/tasks.spec.ts b/spec/common/providers/tasks.spec.ts index b511bcb59..d3711a18f 100644 --- a/spec/common/providers/tasks.spec.ts +++ b/spec/common/providers/tasks.spec.ts @@ -23,20 +23,20 @@ import { expect } from 'chai'; import * as firebase from 'firebase-admin'; -import { checkAuthContext, runHandler } from '../../helper'; -import { - generateIdToken, - generateUnsignedIdToken, - mockRequest, -} from '../../fixtures/mockrequest'; +import { apps as appsNamespace } from '../../../src/apps'; +import * as https from '../../../src/common/providers/https'; import { onDispatchHandler, - TaskContext, Request, + TaskContext, } from '../../../src/common/providers/tasks'; -import { apps as appsNamespace } from '../../../src/apps'; import * as mocks from '../../fixtures/credential/key.json'; -import * as https from '../../../src/common/providers/https'; +import { + generateIdToken, + generateUnsignedIdToken, + mockRequest, +} from '../../fixtures/mockrequest'; +import { checkAuthContext, runHandler } from '../../helper'; /** Represents a test case for a Task Queue Function */ interface TaskTest { diff --git a/spec/helper.ts b/spec/helper.ts index f39b96e04..a0cb57db3 100644 --- a/spec/helper.ts +++ b/spec/helper.ts @@ -20,8 +20,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -import * as express from 'express'; import { expect } from 'chai'; +import * as express from 'express'; import * as https from '../src/common/providers/https'; import * as tasks from '../src/common/providers/tasks'; diff --git a/spec/runtime/loader.spec.ts b/spec/runtime/loader.spec.ts index c75400064..9994557c4 100644 --- a/spec/runtime/loader.spec.ts +++ b/spec/runtime/loader.spec.ts @@ -1,8 +1,8 @@ -import * as path from 'path'; import { expect } from 'chai'; +import * as path from 'path'; -import * as loader from '../../src/runtime/loader'; import * as functions from '../../src/index'; +import * as loader from '../../src/runtime/loader'; import { ManifestEndpoint, ManifestRequiredAPI, @@ -246,11 +246,11 @@ describe('loadStack', () => { specVersion: 'v1alpha1', }; - type Testcase = { + interface Testcase { name: string; modulePath: string; expected: ManifestStack; - }; + } function runTests(tc: Testcase) { it('loads stack given relative path', async () => { await expect(loader.loadStack(tc.modulePath)).to.eventually.deep.equal( diff --git a/spec/v2/providers/eventarc.spec.ts b/spec/v2/providers/eventarc.spec.ts index e7387bbd0..96bd48940 100644 --- a/spec/v2/providers/eventarc.spec.ts +++ b/spec/v2/providers/eventarc.spec.ts @@ -23,7 +23,7 @@ import { expect } from 'chai'; import * as options from '../../../src/v2/options'; import * as eventarc from '../../../src/v2/providers/eventarc'; -import { FULL_OPTIONS } from './fixtures'; +import { FULL_ENDPOINT, FULL_OPTIONS } from './fixtures'; const ENDPOINT_EVENT_TRIGGER = { eventType: 'event-type', @@ -106,22 +106,7 @@ describe('v2/eventarc', () => { ); expect(result.__endpoint).to.deep.equal({ - platform: 'gcfv2', - region: ['us-west1'], - availableMemoryMb: 512, - timeoutSeconds: 60, - minInstances: 1, - maxInstances: 3, - concurrency: 20, - vpc: { - connector: 'aConnector', - egressSettings: 'ALL_TRAFFIC', - }, - serviceAccountEmail: 'root@', - ingressSettings: 'ALLOW_ALL', - labels: { - hello: 'world', - }, + ...FULL_ENDPOINT, eventTrigger: { ...ENDPOINT_EVENT_TRIGGER, channel: 'locations/us-west1/channels/my-channel', diff --git a/spec/v2/providers/fixtures.ts b/spec/v2/providers/fixtures.ts index a499be69a..53aa852b3 100644 --- a/spec/v2/providers/fixtures.ts +++ b/spec/v2/providers/fixtures.ts @@ -11,13 +11,13 @@ export const FULL_OPTIONS: options.GlobalOptions = { vpcConnectorEgressSettings: 'ALL_TRAFFIC', serviceAccount: 'root@', ingressSettings: 'ALLOW_ALL', + cpu: 'gcf_gen1', labels: { hello: 'world', }, }; export const FULL_TRIGGER = { - apiVersion: 2, platform: 'gcfv2', regions: ['us-west1'], availableMemoryMb: 512, @@ -48,6 +48,7 @@ export const FULL_ENDPOINT = { }, serviceAccountEmail: 'root@', ingressSettings: 'ALLOW_ALL', + cpu: 'gcf_gen1', labels: { hello: 'world', }, diff --git a/spec/v2/providers/https.spec.ts b/spec/v2/providers/https.spec.ts index 587be89c6..345bd8965 100644 --- a/spec/v2/providers/https.spec.ts +++ b/spec/v2/providers/https.spec.ts @@ -28,8 +28,8 @@ import { expectedResponseHeaders, MockRequest, } from '../../fixtures/mockrequest'; -import { FULL_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from './fixtures'; import { runHandler } from '../../helper'; +import { FULL_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from './fixtures'; describe('onRequest', () => { beforeEach(() => { @@ -47,7 +47,6 @@ describe('onRequest', () => { }); expect(result.__trigger).to.deep.equal({ - apiVersion: 2, platform: 'gcfv2', httpsTrigger: { allowInsecure: false, @@ -112,7 +111,6 @@ describe('onRequest', () => { ); expect(result.__trigger).to.deep.equal({ - apiVersion: 2, platform: 'gcfv2', httpsTrigger: { allowInsecure: false, @@ -199,7 +197,6 @@ describe('onCall', () => { const result = https.onCall((request) => 42); expect(result.__trigger).to.deep.equal({ - apiVersion: 2, platform: 'gcfv2', httpsTrigger: { allowInsecure: false, @@ -252,7 +249,6 @@ describe('onCall', () => { ); expect(result.__trigger).to.deep.equal({ - apiVersion: 2, platform: 'gcfv2', httpsTrigger: { allowInsecure: false, diff --git a/spec/v2/providers/pubsub.spec.ts b/spec/v2/providers/pubsub.spec.ts index 73e5b55ab..395dadace 100644 --- a/spec/v2/providers/pubsub.spec.ts +++ b/spec/v2/providers/pubsub.spec.ts @@ -32,7 +32,6 @@ describe('onMessagePublished', () => { const result = pubsub.onMessagePublished('topic', () => 42); expect(result.__trigger).to.deep.equal({ - apiVersion: 2, platform: 'gcfv2', eventTrigger: EVENT_TRIGGER, labels: {}, @@ -79,7 +78,6 @@ describe('onMessagePublished', () => { ); expect(result.__trigger).to.deep.equal({ - apiVersion: 2, platform: 'gcfv2', concurrency: 20, minInstances: 3, @@ -110,7 +108,6 @@ describe('onMessagePublished', () => { ); expect(result.__trigger).to.deep.equal({ - apiVersion: 2, platform: 'gcfv2', minInstances: 3, regions: ['us-west1'], diff --git a/spec/v2/providers/storage.spec.ts b/spec/v2/providers/storage.spec.ts index 1c6d7c265..4f5221b6a 100644 --- a/spec/v2/providers/storage.spec.ts +++ b/spec/v2/providers/storage.spec.ts @@ -3,7 +3,7 @@ import * as sinon from 'sinon'; import * as config from '../../../src/config'; import * as options from '../../../src/v2/options'; import * as storage from '../../../src/v2/providers/storage'; -import { FULL_OPTIONS } from './fixtures'; +import { FULL_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from './fixtures'; const EVENT_TRIGGER = { eventType: 'event-type', @@ -161,40 +161,12 @@ describe('v2/storage', () => { ); expect(result.__trigger).to.deep.equal({ - platform: 'gcfv2', - regions: ['us-west1'], - availableMemoryMb: 512, - timeout: '60s', - minInstances: 1, - maxInstances: 3, - concurrency: 20, - vpcConnector: 'aConnector', - vpcConnectorEgressSettings: 'ALL_TRAFFIC', - serviceAccountEmail: 'root@aProject.iam.gserviceaccount.com', - ingressSettings: 'ALLOW_ALL', - labels: { - hello: 'world', - }, + ...FULL_TRIGGER, eventTrigger: EVENT_TRIGGER, }); expect(result.__endpoint).to.deep.equal({ - platform: 'gcfv2', - region: ['us-west1'], - availableMemoryMb: 512, - timeoutSeconds: 60, - minInstances: 1, - maxInstances: 3, - concurrency: 20, - vpc: { - connector: 'aConnector', - egressSettings: 'ALL_TRAFFIC', - }, - serviceAccountEmail: 'root@', - ingressSettings: 'ALLOW_ALL', - labels: { - hello: 'world', - }, + ...FULL_ENDPOINT, eventTrigger: ENDPOINT_EVENT_TRIGGER, }); }); diff --git a/spec/v2/providers/tasks.spec.ts b/spec/v2/providers/tasks.spec.ts index d6be998a5..f0f8c9cc5 100644 --- a/spec/v2/providers/tasks.spec.ts +++ b/spec/v2/providers/tasks.spec.ts @@ -24,9 +24,9 @@ import { expect } from 'chai'; import * as options from '../../../src/v2/options'; import { onTaskDispatched, Request } from '../../../src/v2/providers/tasks'; -import { FULL_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from './fixtures'; import { MockRequest } from '../../fixtures/mockrequest'; import { runHandler } from '../../helper'; +import { FULL_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from './fixtures'; describe('onTaskDispatched', () => { beforeEach(() => { @@ -42,7 +42,6 @@ describe('onTaskDispatched', () => { const result = onTaskDispatched(() => {}); expect(result.__trigger).to.deep.equal({ - apiVersion: 2, platform: 'gcfv2', taskQueueTrigger: {}, labels: {}, @@ -129,7 +128,6 @@ describe('onTaskDispatched', () => { ); expect(result.__trigger).to.deep.equal({ - apiVersion: 2, platform: 'gcfv2', taskQueueTrigger: {}, concurrency: 20, diff --git a/src/common/providers/identity.ts b/src/common/providers/identity.ts index f3358f02e..8872057ad 100644 --- a/src/common/providers/identity.ts +++ b/src/common/providers/identity.ts @@ -24,10 +24,10 @@ import * as express from 'express'; import * as firebase from 'firebase-admin'; import * as jwt from 'jsonwebtoken'; import fetch from 'node-fetch'; -import { HttpsError } from './https'; +import { logger } from '../..'; import { EventContext } from '../../cloud-functions'; import { SUPPORTED_REGIONS } from '../../function-configuration'; -import { logger } from '../..'; +import { HttpsError } from './https'; export { HttpsError }; @@ -123,21 +123,16 @@ export function userRecordConstructor(wireData: Object): UserRecord { }; const record = { ...falseyValues, ...wireData }; - const meta = record['metadata']; + const meta = record.metadata; if (meta) { - record['metadata'] = new UserRecordMetadata( + record.metadata = new UserRecordMetadata( meta.createdAt || meta.creationTime, meta.lastSignedInAt || meta.lastSignInTime ); } else { - record['metadata'] = new UserRecordMetadata(null, null); + record.metadata = new UserRecordMetadata(null, null); } - for (const entry of Object.entries(record.providerData)) { - entry['toJSON'] = () => { - return entry; - }; - } - record['toJSON'] = () => { + record.toJSON = () => { const { uid, email, @@ -150,7 +145,7 @@ export function userRecordConstructor(wireData: Object): UserRecord { passwordSalt, tokensValidAfterTime, } = record; - const json = { + const json: Record = { uid, email, emailVerified, @@ -162,9 +157,9 @@ export function userRecordConstructor(wireData: Object): UserRecord { passwordSalt, tokensValidAfterTime, }; - json['metadata'] = record['metadata'].toJSON(); - json['customClaims'] = JSON.parse(JSON.stringify(record.customClaims)); - json['providerData'] = record.providerData.map((entry) => entry.toJSON()); + json.metadata = record.metadata.toJSON(); + json.customClaims = JSON.parse(JSON.stringify(record.customClaims)); + json.providerData = record.providerData.map((entry) => entry.toJSON()); return json; }; return record as UserRecord; @@ -819,12 +814,13 @@ function parseAdditionalUserInfo( decodedJWT: DecodedPayload ): AdditionalUserInfo { let profile, username; - if (decodedJWT.raw_user_info) + if (decodedJWT.raw_user_info) { try { profile = JSON.parse(decodedJWT.raw_user_info); } catch (err) { logger.debug(`Parse Error: ${err.message}`); } + } if (profile) { if (decodedJWT.sign_in_method === 'github.com') { username = profile.login; diff --git a/src/providers/auth.ts b/src/providers/auth.ts index 160de06c8..98568ce18 100644 --- a/src/providers/auth.ts +++ b/src/providers/auth.ts @@ -20,18 +20,18 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -import { - UserRecord, - UserInfo, - UserRecordMetadata, - userRecordConstructor, -} from '../common/providers/identity'; import { CloudFunction, Event, EventContext, makeCloudFunction, } from '../cloud-functions'; +import { + UserInfo, + UserRecord, + userRecordConstructor, + UserRecordMetadata, +} from '../common/providers/identity'; import { DeploymentOptions } from '../function-configuration'; // TODO: yank in next breaking change release diff --git a/src/providers/tasks.ts b/src/providers/tasks.ts index 5d9520497..64acc240f 100644 --- a/src/providers/tasks.ts +++ b/src/providers/tasks.ts @@ -22,21 +22,21 @@ import * as express from 'express'; -import { Request } from '../common/providers/https'; -import { ManifestEndpoint, ManifestRequiredAPI } from '../runtime/manifest'; -import { DeploymentOptions } from '../function-configuration'; import { optionsToEndpoint, optionsToTrigger } from '../cloud-functions'; import { convertIfPresent, convertInvoker, copyIfPresent, } from '../common/encoding'; +import { Request } from '../common/providers/https'; import { onDispatchHandler, - TaskContext, RateLimits, RetryConfig, + TaskContext, } from '../common/providers/tasks'; +import { DeploymentOptions } from '../function-configuration'; +import { ManifestEndpoint, ManifestRequiredAPI } from '../runtime/manifest'; export { /** @hidden */ diff --git a/src/runtime/loader.ts b/src/runtime/loader.ts index 8d08138b9..de31eefd3 100644 --- a/src/runtime/loader.ts +++ b/src/runtime/loader.ts @@ -19,13 +19,13 @@ // 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 * as url from 'url'; import * as path from 'path'; +import * as url from 'url'; import { - ManifestStack, ManifestEndpoint, ManifestRequiredAPI, + ManifestStack, } from './manifest'; /** @@ -64,19 +64,21 @@ export function extractStack( requiredAPIs: ManifestRequiredAPI[], prefix = '' ) { - for (const [name, val] of Object.entries(module)) { + for (const [name, valAsUnknown] of Object.entries(module)) { + // We're introspecting untrusted code here. Any is appropraite + const val: any = valAsUnknown; if ( typeof val === 'function' && - val['__endpoint'] && - typeof val['__endpoint'] === 'object' + val.__endpoint && + typeof val.__endpoint === 'object' ) { const funcName = prefix + name; endpoints[funcName] = { - ...val['__endpoint'], + ...val.__endpoint, entryPoint: funcName.replace(/-/g, '.'), }; - if (val['__requiredAPIs'] && Array.isArray(val['__requiredAPIs'])) { - requiredAPIs.push(...val['__requiredAPIs']); + if (val.__requiredAPIs && Array.isArray(val.__requiredAPIs)) { + requiredAPIs.push(...val.__requiredAPIs); } } else if (typeof val === 'object' && val !== null) { extractStack(val, endpoints, requiredAPIs, prefix + name + '-'); diff --git a/src/runtime/manifest.ts b/src/runtime/manifest.ts index caece4ee5..7939d9c09 100644 --- a/src/runtime/manifest.ts +++ b/src/runtime/manifest.ts @@ -33,6 +33,7 @@ export interface ManifestEndpoint { concurrency?: number; serviceAccountEmail?: string; timeoutSeconds?: number; + cpu?: number | 'gcf_gen1'; vpc?: { connector: string; egressSettings?: string; diff --git a/src/v2/index.ts b/src/v2/index.ts index 71189e76f..ae2c5df51 100644 --- a/src/v2/index.ts +++ b/src/v2/index.ts @@ -23,11 +23,11 @@ import * as logger from '../logger'; import * as params from './params'; import * as alerts from './providers/alerts'; +import * as eventarc from './providers/eventarc'; import * as https from './providers/https'; import * as pubsub from './providers/pubsub'; import * as storage from './providers/storage'; import * as tasks from './providers/tasks'; -import * as eventarc from './providers/eventarc'; export { alerts, https, pubsub, storage, logger, params, tasks, eventarc }; diff --git a/src/v2/options.ts b/src/v2/options.ts index 73eca40ac..bfbc3195d 100644 --- a/src/v2/options.ts +++ b/src/v2/options.ts @@ -27,10 +27,10 @@ import { serviceAccountFromShorthand, } from '../common/encoding'; import * as logger from '../logger'; +import { ManifestEndpoint } from '../runtime/manifest'; import { TriggerAnnotation } from './core'; import { declaredParams } from './params'; import { ParamSpec } from './params/types'; -import { ManifestEndpoint } from '../runtime/manifest'; /** * List of all regions supported by Cloud Functions v2 @@ -167,9 +167,21 @@ export interface GlobalOptions { /** * Number of requests a function can serve at once. * Can only be applied to functions running on Cloud Functions v2. - * A value of null restores the default concurrency. + * A value of null restores the default concurrency (80 when CPU >= 1, 1 otherwise). + * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. */ concurrency?: number | null; + + /** + * Fractional number of CPUs to allocate to a function. + * Defaults to 1 for functions with <= 2GB RAM and increases for larger memory sizes. + * This is different from the defaults when using the gcloud utility and is different from + * the fixed amount assigned in Google Cloud Functions generation 1. + * To revert to the CPU amounts used in gcloud or in Cloud Functions generation 1, set this + * to the value "gcf_gen1" + */ + cpu?: number | 'gcf_gen1'; + /** * Connect cloud function to specified VPC connector. * A value of null removes the VPC connector @@ -311,7 +323,8 @@ export function optionsToEndpoint( 'maxInstances', 'ingressSettings', 'labels', - 'timeoutSeconds' + 'timeoutSeconds', + 'cpu' ); convertIfPresent(endpoint, opts, 'serviceAccountEmail', 'serviceAccount'); if (opts.vpcConnector) { diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index 58b54e9b6..e70e1796a 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -1,6 +1,6 @@ -import { getEndpointAnnotation, FirebaseAlertData } from './alerts'; import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; +import { FirebaseAlertData, getEndpointAnnotation } from './alerts'; /** * The internal payload object for adding a new tester device to app distribution. diff --git a/src/v2/providers/alerts/billing.ts b/src/v2/providers/alerts/billing.ts index dd8ccb2c5..a4b7a151e 100644 --- a/src/v2/providers/alerts/billing.ts +++ b/src/v2/providers/alerts/billing.ts @@ -1,4 +1,4 @@ -import { getEndpointAnnotation, FirebaseAlertData } from '.'; +import { FirebaseAlertData, getEndpointAnnotation } from '.'; import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; diff --git a/src/v2/providers/alerts/crashlytics.ts b/src/v2/providers/alerts/crashlytics.ts index d9cece913..6a5f36f73 100644 --- a/src/v2/providers/alerts/crashlytics.ts +++ b/src/v2/providers/alerts/crashlytics.ts @@ -1,4 +1,4 @@ -import { getEndpointAnnotation, FirebaseAlertData } from '.'; +import { FirebaseAlertData, getEndpointAnnotation } from '.'; import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; diff --git a/src/v2/providers/eventarc.ts b/src/v2/providers/eventarc.ts index 9c371cef5..fd56e714f 100644 --- a/src/v2/providers/eventarc.ts +++ b/src/v2/providers/eventarc.ts @@ -20,10 +20,10 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -import * as options from '../options'; -import { CloudEvent, CloudFunction } from '../core'; -import { copyIfPresent, convertIfPresent } from '../../common/encoding'; +import { convertIfPresent, copyIfPresent } from '../../common/encoding'; import { ManifestEndpoint } from '../../runtime/manifest'; +import { CloudEvent, CloudFunction } from '../core'; +import * as options from '../options'; /** Options that can be set on an Eventarc trigger. */ export interface EventarcTriggerOptions extends options.EventHandlerOptions { diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 24367759c..f6101ba1b 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -24,7 +24,6 @@ import * as cors from 'cors'; import * as express from 'express'; import { convertIfPresent, convertInvoker } from '../../common/encoding'; -import * as options from '../options'; import { CallableRequest, FunctionsErrorCode, @@ -33,6 +32,7 @@ import { Request, } from '../../common/providers/https'; import { ManifestEndpoint } from '../../runtime/manifest'; +import * as options from '../options'; export { Request, CallableRequest, FunctionsErrorCode, HttpsError }; @@ -111,9 +111,6 @@ export function onRequest( opts as options.GlobalOptions ); const trigger: any = { - // TODO(inlined): Remove "apiVersion" once the latest version of the CLI - // has migrated to "platform". - apiVersion: 2, platform: 'gcfv2', ...baseOpts, ...specificOpts, @@ -202,9 +199,6 @@ export function onCall>( opts as options.GlobalOptions ); return { - // TODO(inlined): Remove "apiVersion" once the latest version of the CLI - // has migrated to "platform". - apiVersion: 2, platform: 'gcfv2', ...baseOpts, ...specificOpts, diff --git a/src/v2/providers/pubsub.ts b/src/v2/providers/pubsub.ts index 2964af42d..9f06fb083 100644 --- a/src/v2/providers/pubsub.ts +++ b/src/v2/providers/pubsub.ts @@ -1,7 +1,7 @@ -import * as options from '../options'; -import { CloudEvent, CloudFunction } from '../core'; import { copyIfPresent } from '../../common/encoding'; import { ManifestEndpoint } from '../../runtime/manifest'; +import { CloudEvent, CloudFunction } from '../core'; +import * as options from '../options'; /** * Interface representing a Google Cloud Pub/Sub message. @@ -143,9 +143,6 @@ export function onMessagePublished( const specificOpts = options.optionsToTriggerAnnotations(opts); return { - // TODO(inlined): Remove "apiVersion" once the CLI has migrated to - // "platform" - apiVersion: 2, platform: 'gcfv2', ...baseOpts, ...specificOpts, diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts index bc9d26653..f4ce5b004 100644 --- a/src/v2/providers/storage.ts +++ b/src/v2/providers/storage.ts @@ -20,11 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -import * as options from '../options'; -import { firebaseConfig } from '../../config'; -import { CloudEvent, CloudFunction } from '../core'; import { copyIfPresent } from '../../common/encoding'; +import { firebaseConfig } from '../../config'; import { ManifestEndpoint } from '../../runtime/manifest'; +import { CloudEvent, CloudFunction } from '../core'; +import * as options from '../options'; /** * An object within Google Cloud Storage. diff --git a/src/v2/providers/tasks.ts b/src/v2/providers/tasks.ts index ab5892bb5..b45de3361 100644 --- a/src/v2/providers/tasks.ts +++ b/src/v2/providers/tasks.ts @@ -20,20 +20,20 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -import * as options from '../options'; import { convertIfPresent, convertInvoker, copyIfPresent, } from '../../common/encoding'; -import { HttpsFunction } from './https'; import { AuthData, + onDispatchHandler, RateLimits, Request, RetryConfig, - onDispatchHandler, } from '../../common/providers/tasks'; +import * as options from '../options'; +import { HttpsFunction } from './https'; export { AuthData, RateLimits, Request, RetryConfig as RetryPolicy }; @@ -101,7 +101,6 @@ export function onTaskDispatched( convertInvoker ); return { - apiVersion: 2, platform: 'gcfv2', ...baseOpts, ...specificOpts, From 143eea8e06c75478c9d5404b631dd6140b096bd2 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Thu, 28 Apr 2022 17:46:17 -0400 Subject: [PATCH 069/370] Add auth blocking handlers (#1080) * adding inital components for v1 * getting the sdk to work with the cli * more tinkering * reverting package-lock * cleaning up things * adding back old package-lock * adding current package & check before using admin api * cleaning up & starting tests * removing old token verification peices & adding tests for v1/v2 handlers * remove node-fetch * bumping admin version & removing node-fetch types * add back in the @types * add RS256 alg to broken test * adding DOM to fix tsc errors * adding skip lib check * address comments * exporting HttpsError from v1 & v2 namespaces * wrap the handlers, remove skipLibCheck, and add back in node-fetch * Add polyfill to avoid depending on DOM or using skipLibCheck (#1085) * Add polyfill to avoid depending on DOM or using skipLibCheck * Formatter * Linter Co-authored-by: Thomas Bouldin --- package-lock.json | 9028 ++++-------------------- package.json | 8 +- spec/common/providers/identity.spec.ts | 389 - spec/fixtures/mockrequest.ts | 1 + spec/v1/providers/auth.spec.ts | 176 + spec/v2/providers/identity.spec.ts | 277 + src/cloud-functions.ts | 9 + src/common/providers/https.ts | 3 +- src/common/providers/identity.ts | 368 +- src/function-builder.ts | 3 +- src/handler-builder.ts | 2 +- src/providers/auth.ts | 140 +- src/runtime/manifest.ts | 5 + src/types/global.d.ts | 6 + src/v2/core.ts | 4 + src/v2/index.ts | 13 +- src/v2/providers/identity.ts | 215 + tsconfig.release.json | 1 + v2/identity.js | 21 + 19 files changed, 2318 insertions(+), 8351 deletions(-) create mode 100644 spec/v2/providers/identity.spec.ts create mode 100644 src/types/global.d.ts create mode 100644 src/v2/providers/identity.ts create mode 100644 v2/identity.js diff --git a/package-lock.json b/package-lock.json index f1d1eeff2..4e1d204b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6085 +1,31 @@ { "name": "firebase-functions", "version": "3.20.1", - "lockfileVersion": 2, + "lockfileVersion": 1, "requires": true, - "packages": { - "": { - "name": "firebase-functions", - "version": "3.20.1", - "license": "MIT", - "dependencies": { - "@types/cors": "^2.8.5", - "@types/express": "4.17.3", - "cors": "^2.8.5", - "express": "^4.17.1", - "lodash": "^4.17.14", - "node-fetch": "^2.6.7" - }, - "bin": { - "firebase-functions": "lib/bin/firebase-functions.js" - }, - "devDependencies": { - "@types/chai": "^4.1.7", - "@types/chai-as-promised": "^7.1.0", - "@types/jsonwebtoken": "^8.3.2", - "@types/lodash": "^4.14.135", - "@types/mocha": "^5.2.7", - "@types/mock-require": "^2.0.0", - "@types/nock": "^10.0.3", - "@types/node": "^8.10.50", - "@types/node-fetch": "^3.0.3", - "@types/sinon": "^7.0.13", - "chai": "^4.2.0", - "chai-as-promised": "^7.1.1", - "child-process-promise": "^2.2.1", - "firebase-admin": "10.0.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", - "nock": "^10.0.6", - "node-fetch": "^2.6.7", - "portfinder": "^1.0.28", - "prettier": "^1.18.2", - "semver": "^7.3.5", - "sinon": "^7.3.2", - "ts-node": "^10.4.0", - "tslint": "^5.18.0", - "tslint-config-prettier": "^1.18.0", - "tslint-no-unused-expression-chai": "^0.1.4", - "tslint-plugin-prettier": "^2.0.1", - "typedoc": "0.21.2", - "typescript": "^4.3.5", - "yargs": "^15.3.1" - }, - "engines": { - "node": "^8.13.0 || >=10.10.0" - }, - "peerDependencies": { - "firebase-admin": "^8.0.0 || ^9.0.0 || ^10.0.0" - } - }, - "node_modules/@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, - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/@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 - }, - "node_modules/@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, - "dependencies": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "node_modules/@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-consumer": "0.8.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@firebase/app-types": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.7.0.tgz", - "integrity": "sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg==", - "dev": true - }, - "node_modules/@firebase/auth-interop-types": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.6.tgz", - "integrity": "sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g==", - "dev": true, - "peerDependencies": { - "@firebase/app-types": "0.x", - "@firebase/util": "1.x" - } - }, - "node_modules/@firebase/component": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.7.tgz", - "integrity": "sha512-CiAHUPXh2hn/lpzMShNmfAxHNQhKQwmQUJSYMPCjf2bCCt4Z2vLGpS+UWEuNFm9Zf8LNmkS+Z+U/s4Obi5carg==", - "dev": true, - "dependencies": { - "@firebase/util": "1.4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@firebase/component/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, - "node_modules/@firebase/database": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.12.2.tgz", - "integrity": "sha512-Y1LZR1LIQM8YKMkeUPpAq3/e53hcfcXO+JEZ6vCzBeD6xRawqmpw6B5/DzePdCNNvjcqheXzSaR7T39eRZo/wA==", - "dev": true, - "dependencies": { - "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.7", - "@firebase/logger": "0.3.0", - "@firebase/util": "1.4.0", - "faye-websocket": "0.11.4", - "tslib": "^2.1.0" - } - }, - "node_modules/@firebase/database-compat": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.1.2.tgz", - "integrity": "sha512-sV32QIRSNIBj/6OYtpmPzA/SfQz1/NBZbhxg9dIhGaSt9e5HaMxXRuz2lImudX0Sd/v8DKdExrxa++K6rKrRtA==", - "dev": true, - "dependencies": { - "@firebase/component": "0.5.7", - "@firebase/database": "0.12.2", - "@firebase/database-types": "0.9.1", - "@firebase/logger": "0.3.0", - "@firebase/util": "1.4.0", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "@firebase/app-compat": "0.x" - } - }, - "node_modules/@firebase/database-compat/node_modules/@firebase/database-types": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.1.tgz", - "integrity": "sha512-RUixK/YrbpxbfdE+nYP0wMcEsz1xPTnafP0q3UlSS/+fW744OITKtR1J0cMRaXbvY7EH0wUVTNVkrtgxYY8IgQ==", - "dev": true, - "dependencies": { - "@firebase/app-types": "0.7.0", - "@firebase/util": "1.4.0" - } - }, - "node_modules/@firebase/database-compat/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, - "node_modules/@firebase/database-types": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.7.3.tgz", - "integrity": "sha512-dSOJmhKQ0nL8O4EQMRNGpSExWCXeHtH57gGg0BfNAdWcKhC8/4Y+qfKLfWXzyHvrSecpLmO0SmAi/iK2D5fp5A==", - "dev": true, - "dependencies": { - "@firebase/app-types": "0.6.3" - } - }, - "node_modules/@firebase/database-types/node_modules/@firebase/app-types": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.6.3.tgz", - "integrity": "sha512-/M13DPPati7FQHEQ9Minjk1HGLm/4K4gs9bR4rzLCWJg64yGtVC0zNg9gDpkw9yc2cvol/mNFxqTtd4geGrwdw==", - "dev": true - }, - "node_modules/@firebase/database/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, - "node_modules/@firebase/logger": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.3.0.tgz", - "integrity": "sha512-7oQ+TctqekfgZImWkKuda50JZfkmAKMgh5qY4aR4pwRyqZXuJXN1H/BKkHvN1y0S4XWtF0f/wiCLKHhyi1ppPA==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@firebase/logger/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, - "node_modules/@firebase/util": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.4.0.tgz", - "integrity": "sha512-Qn58d+DVi1nGn0bA9RV89zkz0zcbt6aUcRdyiuub/SuEvjKYstWmHcHwh1C0qmE1wPf9a3a+AuaRtduaGaRT7A==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@firebase/util/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, - "node_modules/@google-cloud/common": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.6.0.tgz", - "integrity": "sha512-aHIFTqJZmeTNO9md8XxV+ywuvXF3xBm5WNmgWeeCK+XN5X+kGW0WEX94wGwj+/MdOnrVf4dL2RvSIt9J5yJG6Q==", - "dev": true, - "optional": true, - "dependencies": { - "@google-cloud/projectify": "^2.0.0", - "@google-cloud/promisify": "^2.0.0", - "arrify": "^2.0.1", - "duplexify": "^4.1.1", - "ent": "^2.2.0", - "extend": "^3.0.2", - "google-auth-library": "^7.0.2", - "retry-request": "^4.1.1", - "teeny-request": "^7.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@google-cloud/firestore": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.11.0.tgz", - "integrity": "sha512-Do9WJzEkFBBB+zVFvFfrrrIFEz086lrdgKQic7XsdoTgtYtq0yMu2u3kGLyxMbdasl2c2yf49FE4YvO3AYjQMQ==", - "dev": true, - "optional": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "functional-red-black-tree": "^1.0.1", - "google-gax": "^2.9.2", - "protobufjs": "^6.8.6" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@google-cloud/paginator": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.5.tgz", - "integrity": "sha512-N4Uk4BT1YuskfRhKXBs0n9Lg2YTROZc6IMpkO/8DIHODtm5s3xY8K5vVBo23v/2XulY3azwITQlYWgT4GdLsUw==", - "dev": true, - "optional": true, - "dependencies": { - "arrify": "^2.0.0", - "extend": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@google-cloud/projectify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz", - "integrity": "sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ==", - "dev": true, - "optional": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@google-cloud/promisify": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz", - "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==", - "dev": true, - "optional": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@google-cloud/storage": { - "version": "5.8.5", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.8.5.tgz", - "integrity": "sha512-i0gB9CRwQeOBYP7xuvn14M40LhHCwMjceBjxE4CTvsqL519sVY5yVKxLiAedHWGwUZHJNRa7Q2CmNfkdRwVNPg==", - "dev": true, - "optional": true, - "dependencies": { - "@google-cloud/common": "^3.6.0", - "@google-cloud/paginator": "^3.0.0", - "@google-cloud/promisify": "^2.0.0", - "arrify": "^2.0.0", - "async-retry": "^1.3.1", - "compressible": "^2.0.12", - "date-and-time": "^1.0.0", - "duplexify": "^4.0.0", - "extend": "^3.0.2", - "gaxios": "^4.0.0", - "gcs-resumable-upload": "^3.1.4", - "get-stream": "^6.0.0", - "hash-stream-validation": "^0.2.2", - "mime": "^2.2.0", - "mime-types": "^2.0.8", - "onetime": "^5.1.0", - "p-limit": "^3.0.1", - "pumpify": "^2.0.0", - "snakeize": "^0.1.0", - "stream-events": "^1.0.1", - "xdg-basedir": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@google-cloud/storage/node_modules/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, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/@google-cloud/storage/node_modules/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, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@grpc/grpc-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.3.0.tgz", - "integrity": "sha512-fiL7ZaGg2HBiFtmv6m34d5jEgEtNXfctjzB3f7b3iuT7olBX4mHLMOqOBmGTTSOTfNRQJH5+vsyk6mEz3I0Q7Q==", - "dev": true, - "optional": true, - "dependencies": { - "@types/node": ">=12.12.47" - }, - "engines": { - "node": "^8.13.0 || >=10.10.0" - } - }, - "node_modules/@grpc/grpc-js/node_modules/@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 - }, - "node_modules/@grpc/proto-loader": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.1.tgz", - "integrity": "sha512-4DIvEOZhw5nGj3RQngIoiMXRsre3InEH136krZTcirs/G2em3WMXdtx4Lqlnb4E2ertbWGs5gPeVDKU5BHffXw==", - "dev": true, - "optional": true, - "dependencies": { - "@types/long": "^4.0.1", - "lodash.camelcase": "^4.3.0", - "long": "^4.0.0", - "protobufjs": "^6.10.0", - "yargs": "^16.1.1" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@grpc/proto-loader/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@grpc/proto-loader/node_modules/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, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@grpc/proto-loader/node_modules/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, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/@grpc/proto-loader/node_modules/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, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@grpc/proto-loader/node_modules/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 - }, - "node_modules/@grpc/proto-loader/node_modules/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 - }, - "node_modules/@grpc/proto-loader/node_modules/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/@grpc/proto-loader/node_modules/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, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@grpc/proto-loader/node_modules/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, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@grpc/proto-loader/node_modules/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, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@grpc/proto-loader/node_modules/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, - "dependencies": { - "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" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@grpc/proto-loader/node_modules/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, - "engines": { - "node": ">=10" - } - }, - "node_modules/@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, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@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 - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "dev": true, - "optional": true - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "dev": true, - "optional": true - }, - "node_modules/@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 - }, - "node_modules/@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, - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@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 - }, - "node_modules/@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 - }, - "node_modules/@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 - }, - "node_modules/@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 - }, - "node_modules/@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 - }, - "node_modules/@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, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@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, - "dependencies": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^3.1.0" - } - }, - "node_modules/@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, - "dependencies": { - "@sinonjs/commons": "^1.3.0", - "array-from": "^2.1.1", - "lodash": "^4.17.15" - } - }, - "node_modules/@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 - }, - "node_modules/@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, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", - "dev": true - }, - "node_modules/@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==", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@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 - }, - "node_modules/@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, - "dependencies": { - "@types/chai": "*" - } - }, - "node_modules/@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 - }, - "node_modules/@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==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@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==", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@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==", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@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, - "dependencies": { - "@types/express": "*", - "@types/express-unless": "*" - } - }, - "node_modules/@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==", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "node_modules/@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, - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@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, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@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 - }, - "node_modules/@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 - }, - "node_modules/@types/mime": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", - "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==" - }, - "node_modules/@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 - }, - "node_modules/@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, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@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, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@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==" - }, - "node_modules/@types/node-fetch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-3.0.3.tgz", - "integrity": "sha512-HhggYPH5N+AQe/OmN6fmhKmRRt2XuNJow+R3pQwJxOOF9GuwM7O2mheyGeIrs5MOIeNjDEdgdoyHBOrFeJBR3g==", - "deprecated": "This is a stub types definition. node-fetch provides its own type definitions, so you do not need this installed.", - "dev": true, - "dependencies": { - "node-fetch": "*" - } - }, - "node_modules/@types/qs": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz", - "integrity": "sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ==" - }, - "node_modules/@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==" - }, - "node_modules/@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==", - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/mime": "*" - } - }, - "node_modules/@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 - }, - "node_modules/abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", - "dev": true - }, - "node_modules/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, - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/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, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/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, - "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "node_modules/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, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "optional": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "optional": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/agent-base/node_modules/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 - }, - "node_modules/ajv": { - "version": "6.12.5", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", - "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/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, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", - "dev": true - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "node_modules/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 - }, - "node_modules/arrify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", - "dev": true, - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=", - "dev": true, - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/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, - "dependencies": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/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, - "engines": { - "node": "*" - } - }, - "node_modules/async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } - }, - "node_modules/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, - "dependencies": { - "retry": "0.12.0" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "node_modules/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, - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", - "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "optional": true - }, - "node_modules/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, - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/bignumber.js": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", - "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==", - "dev": true, - "optional": true, - "engines": { - "node": "*" - } - }, - "node_modules/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 - }, - "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "dependencies": { - "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" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/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, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "node_modules/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 - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha1-uqVZ7hTO1zRSIputcyZGfGH6vWA=", - "dev": true - }, - "node_modules/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 - }, - "node_modules/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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "node_modules/chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, - "dependencies": { - "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" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "dependencies": { - "check-error": "^1.0.2" - }, - "peerDependencies": { - "chai": ">= 2.1.2 < 5" - } - }, - "node_modules/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, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/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, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/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, - "dependencies": { - "cross-spawn": "^4.0.2", - "node-version": "^1.0.0", - "promise-polyfill": "^6.0.1" - } - }, - "node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui/node_modules/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, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui/node_modules/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, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/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, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha1-vXerfebelCBc6sxy8XFtKfIKd78=", - "dev": true - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "optional": true, - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compressible/node_modules/mime-db": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", - "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==", - "dev": true, - "optional": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/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 - }, - "node_modules/configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, - "optional": true, - "dependencies": { - "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" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "node_modules/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 - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha1-6sEdpRWS3Ya58G9uesKTs9+HXSk=", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", - "dev": true, - "dependencies": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, - "node_modules/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/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, - "dependencies": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/date-and-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-1.0.0.tgz", - "integrity": "sha512-477D7ypIiqlXBkxhU7YtG9wWZJEQ+RUpujt2quTfgf4+E8g5fNUkB0QIL0bVyP5/TKBg8y55Hfa1R/c4bt3dEw==", - "dev": true, - "optional": true - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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 - }, - "node_modules/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, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/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 - }, - "node_modules/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, - "dependencies": { - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/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, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "node_modules/dicer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", - "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", - "dev": true, - "dependencies": { - "streamsearch": "0.1.2" - }, - "engines": { - "node": ">=4.5.0" - } - }, - "node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "dependencies": { - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/duplexify": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", - "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", - "dev": true, - "optional": true, - "dependencies": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.0" - } - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/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, - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "node_modules/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, - "dependencies": { - "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" - } - }, - "node_modules/elliptic/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/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 - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/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, - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true, - "optional": true - }, - "node_modules/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, - "dependencies": { - "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" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "node_modules/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, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/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, - "dependencies": { - "fast-diff": "^1.1.1", - "jest-docblock": "^21.0.0" - }, - "engines": { - "node": ">=4.0.0" - }, - "peerDependencies": { - "prettier": ">= 0.11.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/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, - "engines": { - "node": ">=6" - } - }, - "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "dependencies": { - "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" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=", - "dev": true - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true, - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/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 - }, - "node_modules/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 - }, - "node_modules/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 - }, - "node_modules/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 - }, - "node_modules/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 - }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dev": true, - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dependencies": { - "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" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/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, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/firebase-admin": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-10.0.0.tgz", - "integrity": "sha512-EOAk5ZaqXhBBvx9ZyXd28kw8glMTt3xl0g3BepGRCy0RSSUPGOzfAqjGhc65guSKgFOpT5mAUycYcJbqullKUQ==", - "dev": true, - "dependencies": { - "@firebase/database-compat": "^0.1.1", - "@firebase/database-types": "^0.7.2", - "@types/node": ">=12.12.47", - "dicer": "^0.3.0", - "jsonwebtoken": "^8.5.1", - "jwks-rsa": "^2.0.2", - "node-forge": "^0.10.0" - }, - "engines": { - "node": ">=12.7.0" - }, - "optionalDependencies": { - "@google-cloud/firestore": "^4.5.0", - "@google-cloud/storage": "^5.3.0" - } - }, - "node_modules/firebase-admin/node_modules/@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 - }, - "node_modules/flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", - "deprecated": "Fixed a prototype pollution security issue in 4.1.0, please upgrade to ^4.1.1 or ^5.0.1.", - "dev": true, - "dependencies": { - "is-buffer": "~2.0.3" - }, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat/node_modules/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, - "engines": { - "node": ">=4" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/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 - }, - "node_modules/gaxios": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.2.1.tgz", - "integrity": "sha512-s+rTywpw6CmfB8r9TXYkpix7YFeuRjnR/AqhaJrQqsNhsAqej+IAiCc3hadzQH3gHyWth30tvYjxH8EVjQt/8Q==", - "dev": true, - "optional": true, - "dependencies": { - "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" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/gcp-metadata": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz", - "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==", - "dev": true, - "optional": true, - "dependencies": { - "gaxios": "^4.0.0", - "json-bigint": "^1.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/gcs-resumable-upload": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-3.1.4.tgz", - "integrity": "sha512-5dyDfHrrVcIskiw/cPssVD4HRiwoHjhk1Nd6h5W3pQ/qffDvhfy4oNCr1f3ZXFPwTnxkCbibsB+73oOM+NvmJQ==", - "dev": true, - "optional": true, - "dependencies": { - "abort-controller": "^3.0.0", - "configstore": "^5.0.0", - "extend": "^3.0.2", - "gaxios": "^4.0.0", - "google-auth-library": "^7.0.0", - "pumpify": "^2.0.0", - "stream-events": "^1.0.4" - }, - "bin": { - "gcs-upload": "build/src/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/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, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/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, - "engines": { - "node": "*" - } - }, - "node_modules/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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha1-OWCDLT8VdBCDQtr9OmezMsCWnfE=", - "dev": true, - "dependencies": { - "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" - }, - "engines": { - "node": "*" - } - }, - "node_modules/google-auth-library": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.0.4.tgz", - "integrity": "sha512-o8irYyeijEiecTXeoEe8UKNEzV1X+uhR4b2oNdapDMZixypp0J+eHimGOyx5Joa3UAeokGngdtDLXtq9vDqG2Q==", - "dev": true, - "optional": true, - "dependencies": { - "arrify": "^2.0.0", - "base64-js": "^1.3.0", - "ecdsa-sig-formatter": "^1.0.11", - "fast-text-encoding": "^1.0.0", - "gaxios": "^4.0.0", - "gcp-metadata": "^4.2.0", - "gtoken": "^5.0.4", - "jws": "^4.0.0", - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/google-auth-library/node_modules/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, - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/google-auth-library/node_modules/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, - "dependencies": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/google-auth-library/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/google-auth-library/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "optional": true - }, - "node_modules/google-gax": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.12.0.tgz", - "integrity": "sha512-UDx4ZZx85vXBe6GZ0sdRSzuegLrRQdRjCxlauX+U7i5YwOoSgcSaIM71BhcdHwGPhEkvO/SSHrEfc1wpL/J6JA==", - "dev": true, - "optional": true, - "dependencies": { - "@grpc/grpc-js": "~1.3.0", - "@grpc/proto-loader": "^0.6.1", - "@types/long": "^4.0.0", - "abort-controller": "^3.0.0", - "duplexify": "^4.0.0", - "fast-text-encoding": "^1.0.3", - "google-auth-library": "^7.0.2", - "is-stream-ended": "^0.1.4", - "node-fetch": "^2.6.1", - "object-hash": "^2.1.1", - "protobufjs": "^6.10.2", - "retry-request": "^4.0.0" - }, - "bin": { - "compileProtos": "build/tools/compileProtos.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/google-p12-pem": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz", - "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==", - "dev": true, - "optional": true, - "dependencies": { - "node-forge": "^0.10.0" - }, - "bin": { - "gp12-pem": "build/src/bin/gp12-pem.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/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, - "optional": true - }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha1-8nNdwig2dPpnR4sQGBBZNVw2nl4=", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, - "node_modules/gtoken": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.2.1.tgz", - "integrity": "sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw==", - "dev": true, - "optional": true, - "dependencies": { - "gaxios": "^4.0.0", - "google-p12-pem": "^3.0.3", - "jws": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/gtoken/node_modules/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, - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/gtoken/node_modules/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, - "dependencies": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dev": true, - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/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, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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 - }, - "node_modules/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, - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "node_modules/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, - "bin": { - "he": "bin/he" - } - }, - "node_modules/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, - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/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, - "dependencies": { - "whatwg-encoding": "^1.0.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/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==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", - "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==", - "dev": true - }, - "node_modules/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, - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "optional": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/http-proxy-agent/node_modules/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 - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/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, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "optional": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/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 - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "node_modules/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, - "engines": { - "node": ">=4" - } - }, - "node_modules/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==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/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, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/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 - }, - "node_modules/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, - "dependencies": { - "has-symbols": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-stream-ended": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", - "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==", - "dev": true, - "optional": true - }, - "node_modules/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, - "dependencies": { - "has-symbols": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/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 - }, - "node_modules/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 - }, - "node_modules/jose": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.5.tgz", - "integrity": "sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA==", - "dev": true, - "dependencies": { - "@panva/asn1.js": "^1.0.0" - }, - "engines": { - "node": ">=10.13.0 < 13 || >=13.7.0" - }, - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/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 - }, - "node_modules/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, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/js-yaml/node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "node_modules/jsdom": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", - "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", - "dev": true, - "dependencies": { - "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" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom/node_modules/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, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=4.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/jsdom/node_modules/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, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "dev": true, - "optional": true, - "dependencies": { - "bignumber.js": "^9.0.0" - } - }, - "node_modules/json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", - "dev": true - }, - "node_modules/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 - }, - "node_modules/jsonc-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", - "dev": true - }, - "node_modules/jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "dev": true, - "dependencies": { - "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" - }, - "engines": { - "node": ">=4", - "npm": ">=1.4.28" - } - }, - "node_modules/jsonwebtoken/node_modules/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_modules/jsonwebtoken/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "node_modules/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 - }, - "node_modules/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, - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/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, - "dependencies": { - "asn1.js": "^5.3.0", - "elliptic": "^6.5.4", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/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, - "dependencies": { - "@types/express-jwt": "0.0.42", - "debug": "^4.1.0", - "jose": "^2.0.5", - "limiter": "^1.1.5", - "lru-memoizer": "^2.1.2" - }, - "engines": { - "node": ">=10 < 13 || >=14" - } - }, - "node_modules/jwks-rsa/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/jwks-rsa/node_modules/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_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dev": true, - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/limiter": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", - "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==", - "dev": true - }, - "node_modules/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 - }, - "node_modules/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, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/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 - }, - "node_modules/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 - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=", - "dev": true - }, - "node_modules/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 - }, - "node_modules/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 - }, - "node_modules/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 - }, - "node_modules/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 - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", - "dev": true - }, - "node_modules/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 - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, - "node_modules/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, - "dependencies": { - "chalk": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/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 - }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "dev": true, - "optional": true - }, - "node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha1-i75Q6oW+1ZvJ4z3KuCNe6bz0Q80=", - "dev": true, - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/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, - "dependencies": { - "lodash.clonedeep": "^4.5.0", - "lru-cache": "~4.0.0" - } - }, - "node_modules/lru-memoizer/node_modules/lru-cache": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", - "integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=", - "dev": true, - "dependencies": { - "pseudomap": "^1.0.1", - "yallist": "^2.0.0" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, - "node_modules/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, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/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, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/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 - }, - "node_modules/marked": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz", - "integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==", - "dev": true, - "bin": { - "marked": "bin/marked" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/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==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "dependencies": { - "mime-db": "1.40.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/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, - "engines": { - "node": ">=6" - } - }, - "node_modules/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 - }, - "node_modules/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 - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "node_modules/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, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/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, - "dependencies": { - "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" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/mocha/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/mocha/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/mocha/node_modules/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, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mocha/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/mocha/node_modules/mkdirp": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", - "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", - "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mocha/node_modules/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 - }, - "node_modules/mocha/node_modules/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, - "dependencies": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mocha/node_modules/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, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/mocha/node_modules/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, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/mocha/node_modules/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, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/mocha/node_modules/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 - }, - "node_modules/mocha/node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "dependencies": { - "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" - } - }, - "node_modules/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, - "dependencies": { - "get-caller-file": "^1.0.2", - "normalize-path": "^2.1.1" - }, - "engines": { - "node": ">=4.3.0" - } - }, - "node_modules/mock-require/node_modules/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 - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/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 - }, - "node_modules/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, - "dependencies": { - "@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" - } - }, - "node_modules/nise/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "node_modules/nise/node_modules/lolex": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", - "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/nise/node_modules/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, - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/nock": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/nock/-/nock-10.0.6.tgz", - "integrity": "sha512-b47OWj1qf/LqSQYnmokNWM8D88KvUl2y7jT0567NB3ZBAZFz2bWp2PC81Xn7u8F2/vJxzkzNZybnemeFa7AZ2w==", - "dev": true, - "dependencies": { - "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" - }, - "engines": { - "node": ">= 6.0" - } - }, - "node_modules/nock/node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/nock/node_modules/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, - "dependencies": { - "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" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/nock/node_modules/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_modules/nock/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/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, - "dependencies": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - } - }, - "node_modules/node-environment-flags/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dev": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", - "dev": true - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", - "dev": true - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "dev": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", - "dev": true, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/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, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/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, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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 - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "engines": { - "node": ">= 6" - } - }, - "node_modules/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, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/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, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.0", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.assign/node_modules/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, - "dependencies": { - "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" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/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, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/onigasm": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", - "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", - "dev": true, - "dependencies": { - "lru-cache": "^5.1.1" - } - }, - "node_modules/onigasm/node_modules/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, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/onigasm/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "dependencies": { - "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" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/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, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "engines": { - "node": ">=6" - } - }, - "node_modules/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 - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/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, - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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 - }, - "node_modules/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=" - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "node_modules/portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "dev": true, - "dependencies": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" - }, - "engines": { - "node": ">= 0.12.0" - } - }, - "node_modules/portfinder/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/portfinder/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/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, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/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, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/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 - }, - "node_modules/propagate": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-1.0.0.tgz", - "integrity": "sha1-AMLa7t2iDofjeCs0Stuhzd1q1wk=", - "dev": true, - "engines": [ - "node >= 0.8.1" - ] - }, - "node_modules/protobufjs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", - "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "dependencies": { - "@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" - }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" - } - }, - "node_modules/protobufjs/node_modules/@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 - }, - "node_modules/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==", - "dependencies": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "node_modules/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 - }, - "node_modules/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, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pumpify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", - "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", - "dev": true, - "optional": true, - "dependencies": { - "duplexify": "^4.1.1", - "inherits": "^2.0.3", - "pump": "^3.0.0" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/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==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/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==", - "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/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, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/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, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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 - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dev": true, - "dependencies": { - "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" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/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, - "dependencies": { - "lodash": "^4.17.19" - }, - "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "request": "^2.34" - } - }, - "node_modules/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==", - "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", - "dev": true, - "dependencies": { - "request-promise-core": "1.1.4", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - }, - "engines": { - "node": ">=0.12.0" - }, - "peerDependencies": { - "request": "^2.34" - } - }, - "node_modules/request-promise-native/node_modules/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, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/request/node_modules/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, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/request/node_modules/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, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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 - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", - "dev": true, - "optional": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/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, - "dependencies": { - "debug": "^4.1.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/retry-request/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "optional": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/retry-request/node_modules/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 - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" - }, - "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "dependencies": { - "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" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/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==" - }, - "node_modules/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==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/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 - }, - "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "node_modules/shiki": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.12.tgz", - "integrity": "sha512-VXcROdldv0/Qu0w2XvzU4IrvTeBNs/Kj/FCmtcEXGz7Tic/veQzliJj6tEiAgoKianhQstpYmbPDStHU5Opqcw==", - "dev": true, - "dependencies": { - "jsonc-parser": "^3.0.0", - "onigasm": "^2.2.5", - "vscode-textmate": "5.2.0" - } - }, - "node_modules/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 - }, - "node_modules/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, - "dependencies": { - "@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" - } - }, - "node_modules/sinon/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/sinon/node_modules/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, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/snakeize": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/snakeize/-/snakeize-0.1.0.tgz", - "integrity": "sha1-EMCI2LWOsHazIpu1oE4jLOEmQi0=", - "dev": true, - "optional": true - }, - "node_modules/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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "dependencies": { - "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" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "dependencies": { - "stubs": "^3.0.0" - } - }, - "node_modules/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 - }, - "node_modules/streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "optional": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string_decoder/node_modules/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, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "optional": true - }, - "node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stubs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", - "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=", - "dev": true, - "optional": true - }, - "node_modules/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 - }, - "node_modules/teeny-request": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz", - "integrity": "sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==", - "dev": true, - "optional": true, - "dependencies": { - "http-proxy-agent": "^4.0.0", - "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.1", - "stream-events": "^1.0.5", - "uuid": "^8.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/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, - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/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, - "dependencies": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-node": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", - "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "0.7.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node/node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ts-node/node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/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 - }, - "node_modules/tslint": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", - "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", - "dev": true, - "dependencies": { - "@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" - }, - "bin": { - "tslint": "bin/tslint" - }, - "engines": { - "node": ">=4.8.0" - }, - "peerDependencies": { - "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev" - } - }, - "node_modules/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, - "bin": { - "tslint-config-prettier-check": "bin/check.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/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, - "dependencies": { - "tsutils": "^3.0.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "tslint": ">=5.1.0" - } - }, - "node_modules/tslint-no-unused-expression-chai/node_modules/tsutils": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", - "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/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, - "dependencies": { - "eslint-plugin-prettier": "^2.2.0", - "lines-and-columns": "^1.1.6", - "tslib": "^1.7.1" - }, - "engines": { - "node": ">= 4" - }, - "peerDependencies": { - "prettier": "^1.9.0 || ^2.0.0", - "tslint": "^5.0.0 || ^6.0.0" - } - }, - "node_modules/tslint/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/tslint/node_modules/resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "dependencies": { - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tslint/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "peerDependencies": { - "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" - } - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/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, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/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, - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typedoc": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.2.tgz", - "integrity": "sha512-SR1ByJB3USg+jxoxwzMRP07g/0f/cQUE5t7gOh1iTUyjTPyJohu9YSKRlK+MSXXqlhIq+m0jkEHEG5HoY7/Adg==", - "dev": true, - "dependencies": { - "glob": "^7.1.7", - "handlebars": "^4.7.7", - "lodash": "^4.17.21", - "lunr": "^2.3.9", - "marked": "^2.1.1", - "minimatch": "^3.0.0", - "progress": "^2.0.3", - "shiki": "^0.9.3", - "typedoc-default-themes": "^0.12.10" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 12.20.0" - }, - "peerDependencies": { - "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x" - } - }, - "node_modules/typedoc-default-themes": { - "version": "0.12.10", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", - "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/typedoc/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "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" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/uglify-js": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz", - "integrity": "sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==", - "dev": true, - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/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, - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/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, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/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 - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "optional": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", - "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", - "dev": true - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", - "dev": true - }, - "node_modules/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, - "dependencies": { - "browser-process-hrtime": "^1.0.0" - } - }, - "node_modules/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, - "dependencies": { - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/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, - "engines": { - "node": ">=10.4" - } - }, - "node_modules/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, - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/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, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/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, - "dependencies": { - "iconv-lite": "0.4.24" - } - }, - "node_modules/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 - }, - "node_modules/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, - "dependencies": { - "lodash.sortby": "^4.7.0", - "tr46": "^2.0.2", - "webidl-conversions": "^6.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/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 - }, - "node_modules/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, - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "node_modules/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, - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/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, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/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, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/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, - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/ws": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", - "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", - "dev": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/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 - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, - "node_modules/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, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "node_modules/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, - "dependencies": { - "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" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "node_modules/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, - "dependencies": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs-unparser/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs-unparser/node_modules/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, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs-unparser/node_modules/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, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs-unparser/node_modules/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 - }, - "node_modules/yargs-unparser/node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "dependencies": { - "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" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/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, - "dependencies": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/yargs/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/yargs/node_modules/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, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/yargs/node_modules/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 - }, - "node_modules/yargs/node_modules/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 - }, - "node_modules/yargs/node_modules/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, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/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, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/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, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/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, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/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, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/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, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/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 - }, - "node_modules/yargs/node_modules/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, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, "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==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", "dev": true, "requires": { - "@babel/highlight": "^7.10.4" + "@babel/highlight": "^7.16.7" } }, "@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==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", "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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", + "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.16.7", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } @@ -6109,167 +55,91 @@ "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, - "requires": {} + "dev": true }, "@firebase/component": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.7.tgz", - "integrity": "sha512-CiAHUPXh2hn/lpzMShNmfAxHNQhKQwmQUJSYMPCjf2bCCt4Z2vLGpS+UWEuNFm9Zf8LNmkS+Z+U/s4Obi5carg==", + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.13.tgz", + "integrity": "sha512-hxhJtpD8Ppf/VU2Rlos6KFCEV77TGIGD5bJlkPK1+B/WUe0mC6dTjW7KhZtXTc+qRBp9nFHWcsIORnT8liHP9w==", "dev": true, "requires": { - "@firebase/util": "1.4.0", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } } }, "@firebase/database": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.12.2.tgz", - "integrity": "sha512-Y1LZR1LIQM8YKMkeUPpAq3/e53hcfcXO+JEZ6vCzBeD6xRawqmpw6B5/DzePdCNNvjcqheXzSaR7T39eRZo/wA==", + "version": "0.12.8", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.12.8.tgz", + "integrity": "sha512-JBQVfFLzfhxlQbl4OU6ov9fdsddkytBQdtSSR49cz48homj38ccltAhK6seum+BI7f28cV2LFHF9672lcN+qxA==", "dev": true, "requires": { "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.7", - "@firebase/logger": "0.3.0", - "@firebase/util": "1.4.0", + "@firebase/component": "0.5.13", + "@firebase/logger": "0.3.2", + "@firebase/util": "1.5.2", "faye-websocket": "0.11.4", "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } } }, "@firebase/database-compat": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.1.2.tgz", - "integrity": "sha512-sV32QIRSNIBj/6OYtpmPzA/SfQz1/NBZbhxg9dIhGaSt9e5HaMxXRuz2lImudX0Sd/v8DKdExrxa++K6rKrRtA==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.1.8.tgz", + "integrity": "sha512-dhXr5CSieBuKNdU96HgeewMQCT9EgOIkfF1GNy+iRrdl7BWLxmlKuvLfK319rmIytSs/vnCzcD9uqyxTeU/A3A==", "dev": true, "requires": { - "@firebase/component": "0.5.7", - "@firebase/database": "0.12.2", - "@firebase/database-types": "0.9.1", - "@firebase/logger": "0.3.0", - "@firebase/util": "1.4.0", + "@firebase/component": "0.5.13", + "@firebase/database": "0.12.8", + "@firebase/database-types": "0.9.7", + "@firebase/logger": "0.3.2", + "@firebase/util": "1.5.2", "tslib": "^2.1.0" - }, - "dependencies": { - "@firebase/database-types": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.1.tgz", - "integrity": "sha512-RUixK/YrbpxbfdE+nYP0wMcEsz1xPTnafP0q3UlSS/+fW744OITKtR1J0cMRaXbvY7EH0wUVTNVkrtgxYY8IgQ==", - "dev": true, - "requires": { - "@firebase/app-types": "0.7.0", - "@firebase/util": "1.4.0" - } - }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } } }, "@firebase/database-types": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.7.3.tgz", - "integrity": "sha512-dSOJmhKQ0nL8O4EQMRNGpSExWCXeHtH57gGg0BfNAdWcKhC8/4Y+qfKLfWXzyHvrSecpLmO0SmAi/iK2D5fp5A==", + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.7.tgz", + "integrity": "sha512-EFhgL89Fz6DY3kkB8TzdHvdu8XaqqvzcF2DLVOXEnQ3Ms7L755p5EO42LfxXoJqb9jKFvgLpFmKicyJG25WFWw==", "dev": true, "requires": { - "@firebase/app-types": "0.6.3" - }, - "dependencies": { - "@firebase/app-types": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.6.3.tgz", - "integrity": "sha512-/M13DPPati7FQHEQ9Minjk1HGLm/4K4gs9bR4rzLCWJg64yGtVC0zNg9gDpkw9yc2cvol/mNFxqTtd4geGrwdw==", - "dev": true - } + "@firebase/app-types": "0.7.0", + "@firebase/util": "1.5.2" } }, "@firebase/logger": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.3.0.tgz", - "integrity": "sha512-7oQ+TctqekfgZImWkKuda50JZfkmAKMgh5qY4aR4pwRyqZXuJXN1H/BKkHvN1y0S4XWtF0f/wiCLKHhyi1ppPA==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.3.2.tgz", + "integrity": "sha512-lzLrcJp9QBWpo40OcOM9B8QEtBw2Fk1zOZQdvv+rWS6gKmhQBCEMc4SMABQfWdjsylBcDfniD1Q+fUX1dcBTXA==", "dev": true, "requires": { "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } } }, "@firebase/util": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.4.0.tgz", - "integrity": "sha512-Qn58d+DVi1nGn0bA9RV89zkz0zcbt6aUcRdyiuub/SuEvjKYstWmHcHwh1C0qmE1wPf9a3a+AuaRtduaGaRT7A==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.5.2.tgz", + "integrity": "sha512-YvBH2UxFcdWG2HdFnhxZptPl2eVFlpOyTH66iDo13JPEYraWzWToZ5AMTtkyRHVmu7sssUpQlU9igy1KET7TOw==", "dev": true, "requires": { "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } - } - }, - "@google-cloud/common": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.6.0.tgz", - "integrity": "sha512-aHIFTqJZmeTNO9md8XxV+ywuvXF3xBm5WNmgWeeCK+XN5X+kGW0WEX94wGwj+/MdOnrVf4dL2RvSIt9J5yJG6Q==", - "dev": true, - "optional": true, - "requires": { - "@google-cloud/projectify": "^2.0.0", - "@google-cloud/promisify": "^2.0.0", - "arrify": "^2.0.1", - "duplexify": "^4.1.1", - "ent": "^2.2.0", - "extend": "^3.0.2", - "google-auth-library": "^7.0.2", - "retry-request": "^4.1.1", - "teeny-request": "^7.0.0" } }, "@google-cloud/firestore": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.11.0.tgz", - "integrity": "sha512-Do9WJzEkFBBB+zVFvFfrrrIFEz086lrdgKQic7XsdoTgtYtq0yMu2u3kGLyxMbdasl2c2yf49FE4YvO3AYjQMQ==", + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.15.1.tgz", + "integrity": "sha512-2PWsCkEF1W02QbghSeRsNdYKN1qavrHBP3m72gPDMHQSYrGULOaTi7fSJquQmAtc4iPVB2/x6h80rdLHTATQtA==", "dev": true, "optional": true, "requires": { "fast-deep-equal": "^3.1.1", "functional-red-black-tree": "^1.0.1", - "google-gax": "^2.9.2", + "google-gax": "^2.24.1", "protobufjs": "^6.8.6" } }, "@google-cloud/paginator": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.5.tgz", - "integrity": "sha512-N4Uk4BT1YuskfRhKXBs0n9Lg2YTROZc6IMpkO/8DIHODtm5s3xY8K5vVBo23v/2XulY3azwITQlYWgT4GdLsUw==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.7.tgz", + "integrity": "sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ==", "dev": true, "optional": true, "requires": { @@ -6278,91 +148,86 @@ } }, "@google-cloud/projectify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz", - "integrity": "sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.1.1.tgz", + "integrity": "sha512-+rssMZHnlh0twl122gXY4/aCrk0G1acBqkHFfYddtsqpYXGxA29nj9V5V9SfC+GyOG00l650f6lG9KL+EpFEWQ==", "dev": true, "optional": true }, "@google-cloud/promisify": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz", - "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.4.tgz", + "integrity": "sha512-j8yRSSqswWi1QqUGKVEKOG03Q7qOoZP6/h2zN2YO+F5h2+DHU0bSrHCK9Y7lo2DI9fBd8qGAw795sf+3Jva4yA==", "dev": true, "optional": true }, "@google-cloud/storage": { - "version": "5.8.5", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.8.5.tgz", - "integrity": "sha512-i0gB9CRwQeOBYP7xuvn14M40LhHCwMjceBjxE4CTvsqL519sVY5yVKxLiAedHWGwUZHJNRa7Q2CmNfkdRwVNPg==", + "version": "5.19.4", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.19.4.tgz", + "integrity": "sha512-Jz7ugcPHhsEmMVvIxM9uoBsdEbKIYwDkh3u07tifsIymEWs47F4/D6+/Tv/W7kLhznqjyOjVJ/0frtBeIC0lJA==", "dev": true, "optional": true, "requires": { - "@google-cloud/common": "^3.6.0", - "@google-cloud/paginator": "^3.0.0", + "@google-cloud/paginator": "^3.0.7", + "@google-cloud/projectify": "^2.0.0", "@google-cloud/promisify": "^2.0.0", + "abort-controller": "^3.0.0", "arrify": "^2.0.0", - "async-retry": "^1.3.1", + "async-retry": "^1.3.3", "compressible": "^2.0.12", - "date-and-time": "^1.0.0", + "configstore": "^5.0.0", + "date-and-time": "^2.0.0", "duplexify": "^4.0.0", + "ent": "^2.2.0", "extend": "^3.0.2", "gaxios": "^4.0.0", - "gcs-resumable-upload": "^3.1.4", "get-stream": "^6.0.0", + "google-auth-library": "^7.14.1", "hash-stream-validation": "^0.2.2", - "mime": "^2.2.0", + "mime": "^3.0.0", "mime-types": "^2.0.8", - "onetime": "^5.1.0", "p-limit": "^3.0.1", "pumpify": "^2.0.0", + "retry-request": "^4.2.2", "snakeize": "^0.1.0", - "stream-events": "^1.0.1", + "stream-events": "^1.0.4", + "teeny-request": "^7.1.3", "xdg-basedir": "^4.0.0" }, "dependencies": { "mime": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", - "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", "dev": true, "optional": true - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "optional": true, - "requires": { - "yocto-queue": "^0.1.0" - } } } }, "@grpc/grpc-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.3.0.tgz", - "integrity": "sha512-fiL7ZaGg2HBiFtmv6m34d5jEgEtNXfctjzB3f7b3iuT7olBX4mHLMOqOBmGTTSOTfNRQJH5+vsyk6mEz3I0Q7Q==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.7.tgz", + "integrity": "sha512-eBM03pu9hd3VqDQG+kHahiG1x80RGkkqqRb1Pchcwqej/KkAH95gAvKs6laqaHCycYaPK+TKuNQnOz9UXYA8qw==", "dev": true, "optional": true, "requires": { + "@grpc/proto-loader": "^0.6.4", "@types/node": ">=12.12.47" }, "dependencies": { "@types/node": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz", - "integrity": "sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==", + "version": "17.0.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.29.tgz", + "integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==", "dev": true, "optional": true } } }, "@grpc/proto-loader": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.1.tgz", - "integrity": "sha512-4DIvEOZhw5nGj3RQngIoiMXRsre3InEH136krZTcirs/G2em3WMXdtx4Lqlnb4E2ertbWGs5gPeVDKU5BHffXw==", + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.9.tgz", + "integrity": "sha512-UlcCS8VbsU9d3XTXGiEVFonN7hXk+oMXZtoHHG2oSA1/GcDP1q6OUgs20PzHDGizzyi8ufGSUDlk3O2NyY7leg==", "dev": true, "optional": true, "requires": { @@ -6370,103 +235,9 @@ "lodash.camelcase": "^4.3.0", "long": "^4.0.0", "protobufjs": "^6.10.0", - "yargs": "^16.1.1" + "yargs": "^16.2.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "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" - } - }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -6482,13 +253,6 @@ "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 } } }, @@ -6573,9 +337,9 @@ "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==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", "dev": true, "requires": { "type-detect": "4.0.8" @@ -6609,9 +373,9 @@ "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==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true, "optional": true }, @@ -6640,50 +404,55 @@ "dev": 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==", + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", "requires": { "@types/connect": "*", "@types/node": "*" + }, + "dependencies": { + "@types/node": { + "version": "17.0.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.29.tgz", + "integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==" + } } }, "@types/chai": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.12.tgz", - "integrity": "sha512-aN5IAC8QNtSUdQzxu7lGBgYAOuU1tmRU4c9dIq5OKGf/SBVjXo+ffM2wEjudAWbgpOhy60nLoAGH1xm8fpCKFQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.1.tgz", + "integrity": "sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ==", "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==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz", + "integrity": "sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ==", "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==", + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", "requires": { "@types/node": "*" + }, + "dependencies": { + "@types/node": { + "version": "17.0.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.29.tgz", + "integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==" + } } }, "@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==", - "requires": { - "@types/express": "*" - } + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" }, "@types/express": { "version": "4.17.3", @@ -6706,50 +475,57 @@ } }, "@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==", + "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", "requires": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*" + }, + "dependencies": { + "@types/node": { + "version": "17.0.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.29.tgz", + "integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==" + } } }, "@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==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.3.tgz", + "integrity": "sha512-TyPLQaF6w8UlWdv4gj8i46B+INBVzURBNRahCozCSXfsK2VTlL1wNyTlMKw817VHygBtlcl5jfnPadlydr06Yw==", "dev": true, "requires": { "@types/express": "*" } }, "@types/jsonwebtoken": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.3.2.tgz", - "integrity": "sha512-Mkjljd9DTpkPlrmGfTJvcP4aBU7yO2QmW7wNVhV4/6AEUxYoacqU7FJU/N0yFEHTsIrE4da3rUrjrR5ejicFmA==", + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.8.tgz", + "integrity": "sha512-zm6xBQpFDIDM6o9r6HSgDeIcLy82TKWctCXEPbJJcXb5AKmi5BNNdLXneixK4lplX3PqIVcwLBCGE/kAGnlD4A==", "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==", + "version": "4.14.182", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz", + "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==", "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==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", "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==" + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "@types/mocha": { "version": "5.2.7", @@ -6758,9 +534,9 @@ "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==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/mock-require/-/mock-require-2.0.1.tgz", + "integrity": "sha512-O7U5DVGboY/Crueb5/huUCIRjKtRVRaLmRDbZJBlDQgJn966z3aiFDN+6AtYviu2ExwMkl34LjT/IiC0OPtKuQ==", "dev": true, "requires": { "@types/node": "*" @@ -6776,9 +552,10 @@ } }, "@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==" + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", + "dev": true }, "@types/node-fetch": { "version": "3.0.3", @@ -6790,22 +567,29 @@ } }, "@types/qs": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz", - "integrity": "sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ==" + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" }, "@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==" + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "@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==", + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", "requires": { - "@types/express-serve-static-core": "*", - "@types/mime": "*" + "@types/mime": "^1", + "@types/node": "*" + }, + "dependencies": { + "@types/node": { + "version": "17.0.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.29.tgz", + "integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==" + } } }, "@types/sinon": { @@ -6815,9 +599,9 @@ "dev": true }, "abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "dev": true }, "abort-controller": { @@ -6831,18 +615,18 @@ } }, "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" } }, "acorn": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", - "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", "dev": true }, "acorn-globals": { @@ -6853,6 +637,14 @@ "requires": { "acorn": "^7.1.1", "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } } }, "acorn-walk": { @@ -6866,17 +658,15 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, - "optional": true, "requires": { "debug": "4" }, "dependencies": { "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, - "optional": true, "requires": { "ms": "2.1.2" } @@ -6885,23 +675,10 @@ "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 + "dev": 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", @@ -6909,18 +686,18 @@ "dev": true }, "ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "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==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" } }, "any-promise": { @@ -6938,7 +715,7 @@ "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { "sprintf-js": "~1.0.2" @@ -6962,15 +739,6 @@ "dev": true, "optional": true }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, "asn1.js": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", @@ -6983,35 +751,29 @@ "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=", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, "requires": { "lodash": "^4.17.14" } }, "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==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "dev": true, "optional": true, "requires": { - "retry": "0.12.0" + "retry": "0.13.1" } }, "asynckit": { @@ -7020,22 +782,10 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", - "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==", - "dev": true - }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "base64-js": { @@ -7045,19 +795,10 @@ "dev": true, "optional": true }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, "bignumber.js": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", - "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", + "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==", "dev": true, "optional": true }, @@ -7068,26 +809,28 @@ "dev": true }, "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", "requires": { - "bytes": "3.1.0", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" } }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -7109,7 +852,7 @@ "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha1-uqVZ7hTO1zRSIputcyZGfGH6vWA=", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, "buffer-equal-constant-time": { @@ -7125,9 +868,18 @@ "dev": true }, "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } }, "camelcase": { "version": "5.3.1", @@ -7135,23 +887,18 @@ "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==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", "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", + "loupe": "^2.3.1", + "pathval": "^1.1.1", "type-detect": "^4.0.5" } }, @@ -7175,10 +922,28 @@ "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=", + "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" + } + }, + "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 }, "supports-color": { @@ -7210,57 +975,30 @@ } }, "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "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": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "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" - } - } + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.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==", + "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.3" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "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 }, "combined-stream": { @@ -7273,9 +1011,9 @@ } }, "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha1-vXerfebelCBc6sxy8XFtKfIKd78=", + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, "compressible": { @@ -7286,15 +1024,6 @@ "optional": true, "requires": { "mime-db": ">= 1.43.0 < 2" - }, - "dependencies": { - "mime-db": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", - "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==", - "dev": true, - "optional": true - } } }, "concat-map": { @@ -7319,38 +1048,32 @@ } }, "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" } }, "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=" + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, "cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha1-6sEdpRWS3Ya58G9uesKTs9+HXSk=", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "requires": { "object-assign": "^4", "vary": "^1" @@ -7402,15 +1125,6 @@ } } }, - "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", @@ -7420,19 +1134,47 @@ "abab": "^2.0.3", "whatwg-mimetype": "^2.3.0", "whatwg-url": "^8.0.0" + }, + "dependencies": { + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "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 + }, + "whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } + } } }, "date-and-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-1.0.0.tgz", - "integrity": "sha512-477D7ypIiqlXBkxhU7YtG9wWZJEQ+RUpujt2quTfgf4+E8g5fNUkB0QIL0bVyP5/TKBg8y55Hfa1R/c4bt3dEw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-2.3.1.tgz", + "integrity": "sha512-OaIRmSJXifwEN21rMVVDs0Kz8uhJ3wWPYd86atkRiqN54liaMQYEbbrgjZQea75YXOBWL4ZFb3rG/waenw1TEg==", "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=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" } @@ -7444,9 +1186,9 @@ "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==", + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", "dev": true }, "deep-eql": { @@ -7458,19 +1200,34 @@ "type-detect": "^4.0.0" } }, + "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" + } + }, "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=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "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=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", "dev": true, "requires": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" } }, "delayed-stream": { @@ -7480,28 +1237,28 @@ "dev": true }, "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, "dicer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", - "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.1.tgz", + "integrity": "sha512-ObioMtXnmjYs3aRtpIJt9rgQSPCIhKVkFPip+E9GUDyWl8N435znUxK/JfNwGZJ2wnn5JKQ7Ly3vOK5Q5dylGA==", "dev": true, "requires": { - "streamsearch": "0.1.2" + "streamsearch": "^1.1.0" } }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, "domexception": { @@ -7532,9 +1289,9 @@ } }, "duplexify": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", - "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", "dev": true, "optional": true, "requires": { @@ -7544,16 +1301,6 @@ "stream-shift": "^1.0.0" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -7581,20 +1328,12 @@ "inherits": "^2.0.4", "minimalistic-assert": "^1.0.1", "minimalistic-crypto-utils": "^1.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - } } }, "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "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 }, "encodeurl": { @@ -7620,22 +1359,45 @@ "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==", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", + "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", "dev": true, "requires": { + "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "dependencies": { + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } } }, "es-to-primitive": { @@ -7667,6 +1429,19 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, "eslint-plugin-prettier": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.7.0.tgz", @@ -7677,16 +1452,22 @@ "jest-docblock": "^21.0.0" } }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, "etag": { @@ -7702,37 +1483,38 @@ "optional": true }, "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.0.tgz", + "integrity": "sha512-EJEXxiTQJS3lIPrU1AE2vRuT7X7E+0KBbpm5GSoK524yl0K8X+er8zS2P14E64eqsVNoWbMCT7MpmQ+ErAhgRg==", "requires": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", "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", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -7741,20 +1523,16 @@ "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 + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "optional": 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 + "dev": true, + "optional": true }, "fast-diff": { "version": "1.2.0", @@ -7762,12 +1540,6 @@ "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", @@ -7791,16 +1563,16 @@ } }, "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" } }, @@ -7814,68 +1586,54 @@ } }, "firebase-admin": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-10.0.0.tgz", - "integrity": "sha512-EOAk5ZaqXhBBvx9ZyXd28kw8glMTt3xl0g3BepGRCy0RSSUPGOzfAqjGhc65guSKgFOpT5mAUycYcJbqullKUQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-10.1.0.tgz", + "integrity": "sha512-4i4wu+EFgNfY4+D4DxXkZcmbD832ozUMNvHMkOFQrf8upyp51n6jrDJS+wLok9sd62yeqcImbnsLOympGlISPA==", "dev": true, "requires": { "@firebase/database-compat": "^0.1.1", - "@firebase/database-types": "^0.7.2", - "@google-cloud/firestore": "^4.5.0", - "@google-cloud/storage": "^5.3.0", + "@firebase/database-types": "^0.9.3", + "@google-cloud/firestore": "^4.15.1", + "@google-cloud/storage": "^5.18.3", "@types/node": ">=12.12.47", "dicer": "^0.3.0", "jsonwebtoken": "^8.5.1", "jwks-rsa": "^2.0.2", - "node-forge": "^0.10.0" + "node-forge": "^1.3.1" }, "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==", + "version": "17.0.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.29.tgz", + "integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==", "dev": true } } }, "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", + "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", "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 - } + "is-buffer": "~2.0.3" } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", "dev": true, "requires": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "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=" + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, "fresh": { "version": "0.5.2", @@ -7891,8 +1649,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "functional-red-black-tree": { "version": "1.0.1", @@ -7901,10 +1658,16 @@ "dev": true, "optional": true }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, "gaxios": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.2.1.tgz", - "integrity": "sha512-s+rTywpw6CmfB8r9TXYkpix7YFeuRjnR/AqhaJrQqsNhsAqej+IAiCc3hadzQH3gHyWth30tvYjxH8EVjQt/8Q==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.3.3.tgz", + "integrity": "sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA==", "dev": true, "optional": true, "requires": { @@ -7912,13 +1675,13 @@ "extend": "^3.0.2", "https-proxy-agent": "^5.0.0", "is-stream": "^2.0.0", - "node-fetch": "^2.3.0" + "node-fetch": "^2.6.7" } }, "gcp-metadata": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz", - "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.1.tgz", + "integrity": "sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==", "dev": true, "optional": true, "requires": { @@ -7926,22 +1689,6 @@ "json-bigint": "^1.0.0" } }, - "gcs-resumable-upload": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-3.1.4.tgz", - "integrity": "sha512-5dyDfHrrVcIskiw/cPssVD4HRiwoHjhk1Nd6h5W3pQ/qffDvhfy4oNCr1f3ZXFPwTnxkCbibsB+73oOM+NvmJQ==", - "dev": true, - "optional": true, - "requires": { - "abort-controller": "^3.0.0", - "configstore": "^5.0.0", - "extend": "^3.0.2", - "gaxios": "^4.0.0", - "google-auth-library": "^7.0.0", - "pumpify": "^2.0.0", - "stream-events": "^1.0.4" - } - }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -7954,6 +1701,16 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -7961,19 +1718,20 @@ "dev": true, "optional": true }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "dev": true, "requires": { - "assert-plus": "^1.0.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" } }, "glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha1-OWCDLT8VdBCDQtr9OmezMsCWnfE=", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -7985,9 +1743,9 @@ } }, "google-auth-library": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.0.4.tgz", - "integrity": "sha512-o8irYyeijEiecTXeoEe8UKNEzV1X+uhR4b2oNdapDMZixypp0J+eHimGOyx5Joa3UAeokGngdtDLXtq9vDqG2Q==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.1.tgz", + "integrity": "sha512-5Rk7iLNDFhFeBYc3s8l1CqzbEBcdhwR193RlD4vSNFajIcINKI8W8P0JLmBpwymHqqWbX34pJDQu39cSy/6RsA==", "dev": true, "optional": true, "requires": { @@ -8002,29 +1760,6 @@ "lru-cache": "^6.0.0" }, "dependencies": { - "jwa": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", - "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", - "dev": true, - "optional": true, - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", - "dev": true, - "optional": true, - "requires": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" - } - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -8045,84 +1780,60 @@ } }, "google-gax": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.12.0.tgz", - "integrity": "sha512-UDx4ZZx85vXBe6GZ0sdRSzuegLrRQdRjCxlauX+U7i5YwOoSgcSaIM71BhcdHwGPhEkvO/SSHrEfc1wpL/J6JA==", + "version": "2.30.2", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.30.2.tgz", + "integrity": "sha512-BCNCT26kb0iC52zj2SosyOZMhI5sVfXuul1h0Aw5uT9nGAbmS5eOvQ49ft53ft6XotDj11sUSDV6XESEiQqCqg==", "dev": true, "optional": true, "requires": { - "@grpc/grpc-js": "~1.3.0", + "@grpc/grpc-js": "~1.6.0", "@grpc/proto-loader": "^0.6.1", "@types/long": "^4.0.0", "abort-controller": "^3.0.0", "duplexify": "^4.0.0", "fast-text-encoding": "^1.0.3", - "google-auth-library": "^7.0.2", + "google-auth-library": "^7.14.0", "is-stream-ended": "^0.1.4", "node-fetch": "^2.6.1", - "object-hash": "^2.1.1", - "protobufjs": "^6.10.2", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^0.1.8", + "protobufjs": "6.11.2", "retry-request": "^4.0.0" } }, "google-p12-pem": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz", - "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.1.4.tgz", + "integrity": "sha512-HHuHmkLgwjdmVRngf5+gSmpkyaRI6QmOg77J8tkNBHhNEI62sGHyw4/+UkgyZEI7h84NbWprXDJ+sa3xOYFvTg==", "dev": true, "optional": true, "requires": { - "node-forge": "^0.10.0" + "node-forge": "^1.3.1" } }, "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==", + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true, "optional": true }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha1-8nNdwig2dPpnR4sQGBBZNVw2nl4=", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, "gtoken": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.2.1.tgz", - "integrity": "sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.3.2.tgz", + "integrity": "sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ==", "dev": true, "optional": true, "requires": { "gaxios": "^4.0.0", - "google-p12-pem": "^3.0.3", + "google-p12-pem": "^3.1.3", "jws": "^4.0.0" - }, - "dependencies": { - "jwa": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", - "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", - "dev": true, - "optional": true, - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", - "dev": true, - "optional": true, - "requires": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" - } - } } }, "handlebars": { @@ -8138,37 +1849,49 @@ "wordwrap": "^1.0.0" } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "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 }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", "dev": true, "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" + "get-intrinsic": "^1.1.1" } }, - "has": { + "has-symbols": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "dev": true, "requires": { - "function-bind": "^1.1.1" + "has-symbols": "^1.0.2" } }, - "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", @@ -8213,39 +1936,39 @@ } }, "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==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" } }, "http-parser-js": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", - "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", + "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", "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==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, "optional": true, "requires": { - "@tootallnate/once": "1", + "@tootallnate/once": "2", "agent-base": "6", "debug": "4" }, "dependencies": { "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "optional": true, "requires": { @@ -8261,34 +1984,21 @@ } } }, - "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==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, - "optional": true, "requires": { "agent-base": "6", "debug": "4" }, "dependencies": { "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, - "optional": true, "requires": { "ms": "2.1.2" } @@ -8297,8 +2007,7 @@ "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 + "dev": true } } }, @@ -8328,51 +2037,106 @@ } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "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 + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } }, "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==" + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-bigint": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", "dev": true }, "is-callable": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.1.tgz", - "integrity": "sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", "dev": true }, + "is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "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 + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } }, "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "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 }, "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=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -8381,24 +2145,34 @@ "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=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "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==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" } }, "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==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "optional": true }, @@ -8409,19 +2183,44 @@ "dev": true, "optional": true }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "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==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.2" } }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true, + "optional": true + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", "dev": true }, "isexe": { @@ -8430,12 +2229,6 @@ "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", @@ -8458,81 +2251,107 @@ "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==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "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==", + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", "dev": true, "requires": { - "abab": "^2.0.3", - "acorn": "^7.1.1", + "abab": "^2.0.5", + "acorn": "^8.2.4", "acorn-globals": "^6.0.0", "cssom": "^0.4.4", - "cssstyle": "^2.2.0", + "cssstyle": "^2.3.0", "data-urls": "^2.0.0", - "decimal.js": "^10.2.0", + "decimal.js": "^10.2.1", "domexception": "^2.0.1", - "escodegen": "^1.14.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", "html-encoding-sniffer": "^2.0.1", - "is-potential-custom-element-name": "^1.0.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", "nwsapi": "^2.2.0", - "parse5": "5.1.1", - "request": "^2.88.2", - "request-promise-native": "^1.0.8", - "saxes": "^5.0.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", "symbol-tree": "^3.2.4", - "tough-cookie": "^3.0.1", + "tough-cookie": "^4.0.0", "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", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", "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==", + "@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 + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" + "ms": "2.1.2" } }, - "esprima": { + "http-proxy-agent": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "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 + }, + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "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 + }, + "whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } } } }, @@ -8546,18 +2365,6 @@ "bignumber.js": "^9.0.0" } }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", - "dev": true - }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -8588,10 +2395,31 @@ "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==", + "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" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "semver": { @@ -8602,29 +2430,18 @@ } } }, - "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==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", "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==", + "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", @@ -8643,22 +2460,22 @@ } }, "jwks-rsa": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.0.3.tgz", - "integrity": "sha512-/rkjXRWAp0cS00tunsHResw68P5iTQru8+jHufLNv3JHc4nObFEndfEUSuPugh09N+V9XYxKUqi7QrkmCHSSSg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.1.0.tgz", + "integrity": "sha512-GKOSDBWWBCiQTzawei6mEdRQvji5gecj8F9JwMt0ZOPnBPSmTjo5CKFvvbhE7jGPkU159Cpi0+OTLuABFcNOQQ==", "dev": true, "requires": { "@types/express-jwt": "0.0.42", - "debug": "^4.1.0", + "debug": "^4.3.4", "jose": "^2.0.5", "limiter": "^1.1.5", - "lru-memoizer": "^2.1.2" + "lru-memoizer": "^2.1.4" }, "dependencies": { "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -8673,12 +2490,13 @@ } }, "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "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": "^1.4.1", + "jwa": "^2.0.0", "safe-buffer": "^5.0.1" } }, @@ -8699,9 +2517,9 @@ "dev": true }, "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, "locate-path": { @@ -8774,12 +2592,6 @@ "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", @@ -8802,10 +2614,19 @@ "dev": true, "optional": true }, + "loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } + }, "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha1-i75Q6oW+1ZvJ4z3KuCNe6bz0Q80=", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, "requires": { "pseudomap": "^1.0.2", @@ -8892,25 +2713,18 @@ "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==" + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { - "mime-db": "1.40.0" + "mime-db": "1.52.0" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "optional": true - }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -8926,7 +2740,7 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -8939,9 +2753,9 @@ "dev": true }, "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "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" @@ -8984,6 +2798,41 @@ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "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" + } + }, + "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" + } + }, + "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 + }, "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", @@ -8993,16 +2842,16 @@ "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==", + "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 }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "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 }, "js-yaml": { @@ -9015,33 +2864,12 @@ "esprima": "^4.0.0" } }, - "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", @@ -9062,13 +2890,15 @@ "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==", + "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": { - "has-flag": "^3.0.0" + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" } }, "y18n": { @@ -9094,6 +2924,16 @@ "y18n": "^4.0.0", "yargs-parser": "^13.1.2" } + }, + "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" + } } } }, @@ -9132,9 +2972,9 @@ } }, "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, "neo-async": { "version": "2.6.2", @@ -9155,12 +2995,6 @@ "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", @@ -9199,26 +3033,12 @@ }, "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.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "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": "2.1.2" } }, "ms": { @@ -9257,39 +3077,14 @@ "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dev": true, "requires": { "whatwg-url": "^5.0.0" - }, - "dependencies": { - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", - "dev": true - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", - "dev": true - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "dev": true, - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - } } }, "node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", "dev": true }, "node-version": { @@ -9313,38 +3108,31 @@ "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", "dev": true }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=", - "dev": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-hash": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.1.1.tgz", - "integrity": "sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "dev": true, "optional": true }, "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", - "dev": true + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" }, "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==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, "object-keys": { @@ -9354,53 +3142,32 @@ "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==", + "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.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" - } - } + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" } }, "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==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz", + "integrity": "sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw==", "dev": true, "requires": { + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" + "es-abstract": "^1.19.1" } }, "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "requires": { "ee-first": "1.1.1" } @@ -9414,63 +3181,28 @@ "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" - } - }, - "onigasm": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", - "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", - "dev": true, - "requires": { - "lru-cache": "^5.1.1" - }, - "dependencies": { - "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, - "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 - } - } - }, "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", "dev": true, "requires": { "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", + "fast-levenshtein": "~2.0.6", "levn": "~0.3.0", "prelude-ls": "~1.1.2", "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "word-wrap": "~1.2.3" } }, "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "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": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" } }, "p-locate": { @@ -9480,6 +3212,17 @@ "dev": true, "requires": { "p-limit": "^2.0.0" + }, + "dependencies": { + "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-try": { @@ -9489,9 +3232,9 @@ "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==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, "parseurl": { @@ -9512,9 +3255,9 @@ "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==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-to-regexp": { @@ -9528,12 +3271,6 @@ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "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 - }, "portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -9554,6 +3291,15 @@ "ms": "^2.1.1" } }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -9592,6 +3338,16 @@ "integrity": "sha1-AMLa7t2iDofjeCs0Stuhzd1q1wk=", "dev": true }, + "proto3-json-serializer": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-0.1.8.tgz", + "integrity": "sha512-ACilkB6s1U1gWnl5jtICpnDai4VCxmI9GFxuEaYdxtDG2oVI3sVFIUsvUZcQbJgtPM6p+zqKbjTKQZp6Y4FpQw==", + "dev": true, + "optional": true, + "requires": { + "protobufjs": "^6.11.2" + } + }, "protobufjs": { "version": "6.11.2", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", @@ -9615,21 +3371,21 @@ }, "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==", + "version": "17.0.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.29.tgz", + "integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==", "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==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.0" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" } }, "pseudomap": { @@ -9670,13 +3426,16 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", + "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==" + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "requires": { + "side-channel": "^1.0.4" + } }, "range-parser": { "version": "1.2.1", @@ -9684,12 +3443,12 @@ "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==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } @@ -9707,13 +3466,14 @@ } }, "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==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dev": true, "requires": { + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" + "functions-have-names": "^1.2.2" } }, "remove-trailing-separator": { @@ -9722,90 +3482,6 @@ "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", @@ -9818,27 +3494,39 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, + "resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "requires": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true, "optional": true }, "retry-request": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.1.3.tgz", - "integrity": "sha512-QnRZUpuPNgX0+D1xVxul6DbJ9slvo4Rm6iV/dn63e048MvGbUZiKySVt6Tenp04JqmchxjiLltGerOJys7kJYQ==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.2.2.tgz", + "integrity": "sha512-xA93uxUD/rogV7BV59agW/JHPGXeREMWiZc9jhcwY4YdZ7QOtC7qbomYg0n4wyk2lJhggjvKvhNX8wln/Aldhg==", "dev": true, "optional": true, "requires": { - "debug": "^4.1.1" + "debug": "^4.1.1", + "extend": "^3.0.2" }, "dependencies": { "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "optional": true, "requires": { @@ -9855,14 +3543,14 @@ } }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "saxes": { "version": "5.0.1", @@ -9874,9 +3562,9 @@ } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -9900,41 +3588,41 @@ } }, "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "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==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, "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==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.18.0" } }, "set-blocking": { @@ -9944,25 +3632,35 @@ "dev": true }, "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "shiki": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.12.tgz", - "integrity": "sha512-VXcROdldv0/Qu0w2XvzU4IrvTeBNs/Kj/FCmtcEXGz7Tic/veQzliJj6tEiAgoKianhQstpYmbPDStHU5Opqcw==", + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.15.tgz", + "integrity": "sha512-/Y0z9IzhJ8nD9nbceORCqu6NgT9X6I8Fk8c3SICHI5NbZRLdZYFaB233gwct9sU0vvSypyaL/qaKvzyQGJBZSw==", "dev": true, "requires": { "jsonc-parser": "^3.0.0", - "onigasm": "^2.2.5", + "vscode-oniguruma": "^1.6.1", "vscode-textmate": "5.2.0" } }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, "optional": true }, @@ -9981,12 +3679,6 @@ "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", @@ -10017,33 +3709,10 @@ "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 + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, "stream-events": { "version": "1.0.5", @@ -10063,67 +3732,59 @@ "optional": true }, "streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", "dev": true }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "optional": true, "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "optional": true - } + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, - "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==", + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, - "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==", + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, + "optional": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "safe-buffer": "~5.2.0" } }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^5.0.1" } }, "strip-json-comments": { @@ -10139,6 +3800,21 @@ "dev": true, "optional": true }, + "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" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, "symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -10146,13 +3822,13 @@ "dev": true }, "teeny-request": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz", - "integrity": "sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.2.0.tgz", + "integrity": "sha512-SyY0pek1zWsi0LRVAALem+avzMLc33MKW/JLLakdP4s9+D7+jHcy5x6P+h94g2QNZsAqQNfX5lsbd3WSeJXrrw==", "dev": true, "optional": true, "requires": { - "http-proxy-agent": "^4.0.0", + "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", "node-fetch": "^2.6.1", "stream-events": "^1.0.5", @@ -10178,29 +3854,25 @@ } }, "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", "dev": true, "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" } }, "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" - } + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, "ts-node": { "version": "10.7.0", @@ -10223,12 +3895,6 @@ "yn": "3.1.1" }, "dependencies": { - "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true - }, "acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", @@ -10244,9 +3910,9 @@ } }, "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "tslint": { @@ -10276,20 +3942,17 @@ "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" - } - }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true } } }, @@ -10308,10 +3971,16 @@ "tsutils": "^3.0.0" }, "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, "tsutils": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", - "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, "requires": { "tslib": "^1.8.1" @@ -10328,6 +3997,14 @@ "eslint-plugin-prettier": "^2.2.0", "lines-and-columns": "^1.1.6", "tslib": "^1.7.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } } }, "tsutils": { @@ -10337,23 +4014,16 @@ "dev": true, "requires": { "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } } }, - "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", @@ -10428,18 +4098,30 @@ "dev": true }, "typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "dev": true }, "uglify-js": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz", - "integrity": "sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==", + "version": "3.15.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz", + "integrity": "sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA==", "dev": true, "optional": true }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, "unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -10450,20 +4132,17 @@ "crypto-random-string": "^2.0.0" } }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "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", @@ -10484,9 +4163,9 @@ "optional": true }, "v8-compile-cache-lib": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", - "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, "vary": { @@ -10494,16 +4173,11 @@ "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" - } + "vscode-oniguruma": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", + "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", + "dev": true }, "vscode-textmate": { "version": "5.2.0", @@ -10530,10 +4204,9 @@ } }, "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 + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" }, "websocket-driver": { "version": "0.7.4", @@ -10568,25 +4241,36 @@ "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, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^2.0.2", - "webidl-conversions": "^6.1.0" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", @@ -10600,53 +4284,65 @@ "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.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "strip-ansi": "^4.0.0" } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "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": "^4.1.0" + "ansi-regex": "^3.0.0" } } } }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "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" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -10667,11 +4363,10 @@ } }, "ws": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", - "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", - "dev": true, - "requires": {} + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", + "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "dev": true }, "xdg-basedir": { "version": "4.0.0", @@ -10724,22 +4419,6 @@ "yargs-parser": "^18.1.2" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "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", @@ -10751,27 +4430,6 @@ "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", @@ -10782,12 +4440,6 @@ "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", @@ -10797,6 +4449,15 @@ "p-locate": "^4.1.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": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", @@ -10812,26 +4473,6 @@ "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", @@ -10862,14 +4503,11 @@ } }, "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "optional": true }, "yargs-unparser": { "version": "1.6.0", @@ -10888,6 +4526,53 @@ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "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" + } + }, + "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" + } + }, + "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 + }, + "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 + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -10908,6 +4593,17 @@ "ansi-regex": "^4.1.0" } }, + "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" + } + }, "y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", @@ -10931,6 +4627,16 @@ "y18n": "^4.0.0", "yargs-parser": "^13.1.2" } + }, + "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" + } } } }, diff --git a/package.json b/package.json index dc47fb24e..f8cb58477 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,8 @@ "./v2/alerts/appDistribution": "./lib/v2/providers/alerts/appDistribution.js", "./v2/alerts/billing": "./lib/v2/providers/alerts/billing.js", "./v2/alerts/crashlytics": "./lib/v2/providers/alerts/crashlytics.js", - "./v2/eventarc": "./lib/v2/providers/eventarc.js" + "./v2/eventarc": "./lib/v2/providers/eventarc.js", + "./v2/identity": "./lib/v2/providers/identity.js" }, "typesVersions": { "*": { @@ -128,6 +129,9 @@ "v2/eventarc": [ "lib/v2/providers/eventarc" ], + "v2/identity": [ + "lib/v2/providers/identity" + ], "v2/options": [ "lib/v2/options" ], @@ -186,7 +190,7 @@ "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "child-process-promise": "^2.2.1", - "firebase-admin": "10.0.0", + "firebase-admin": "^10.1.0", "js-yaml": "^3.13.1", "jsdom": "^16.2.1", "jsonwebtoken": "^8.5.1", diff --git a/spec/common/providers/identity.spec.ts b/spec/common/providers/identity.spec.ts index 617f0f859..046ffa8b3 100644 --- a/spec/common/providers/identity.spec.ts +++ b/spec/common/providers/identity.spec.ts @@ -22,106 +22,12 @@ import { expect } from 'chai'; import * as express from 'express'; -import * as jwt from 'jsonwebtoken'; -import * as sinon from 'sinon'; import * as identity from '../../../src/common/providers/identity'; -const PROJECT = 'my-project'; const EVENT = 'EVENT_TYPE'; -const VALID_URL = `https://us-central1-${PROJECT}.cloudfunctions.net/function-1`; const now = new Date(); describe('identity', () => { - describe('invalidPublicKeys', () => { - it('should return true if publicKeysExpireAt does not exist', () => { - expect( - identity.invalidPublicKeys({ - publicKeys: {}, - }) - ).to.be.true; - }); - - it('should return true if publicKeysExpireAt are less than equals Date.now() plus a buffer', () => { - const time = Date.now(); - expect( - identity.invalidPublicKeys( - { - publicKeys: {}, - publicKeysExpireAt: time, - }, - time - ) - ).to.be.true; - }); - - it('should return false if publicKeysExpireAt are greater than Date.now() plus a buffer', () => { - const time = Date.now(); - expect( - identity.invalidPublicKeys( - { - publicKeys: {}, - publicKeysExpireAt: time + identity.INVALID_TOKEN_BUFFER + 60000, - }, - time - ) - ).to.be.false; - }); - }); - - describe('setKeyExpirationTime', () => { - const time = Date.now(); - - it('should do nothing without cache-control', async () => { - const publicKeysCache = { - publicKeys: {}, - publicKeysExpireAt: undefined, - }; - const headers = new Map(); - const response = { - headers, - }; - - await identity.setKeyExpirationTime(response, publicKeysCache, time); - - expect(publicKeysCache.publicKeysExpireAt).to.be.undefined; - }); - - it('should do nothing with cache-control but without max-age', async () => { - const publicKeysCache = { - publicKeys: {}, - publicKeysExpireAt: undefined, - }; - const headers = new Map(); - headers.set('cache-control', 'item=val, item2=val2, item3=val3'); - const response = { - headers, - }; - - await identity.setKeyExpirationTime(response, publicKeysCache, time); - - expect(publicKeysCache.publicKeysExpireAt).to.be.undefined; - }); - - it('should set the correctly set the expiration time', async () => { - const publicKeysCache = { - publicKeys: {}, - publicKeysExpireAt: undefined, - }; - const headers = new Map(); - headers.set( - 'cache-control', - 'item=val, max-age=50, item2=val2, item3=val3' - ); - const response = { - headers, - }; - - await identity.setKeyExpirationTime(response, publicKeysCache, time); - - expect(publicKeysCache.publicKeysExpireAt).to.equal(time + 50 * 1000); - }); - }); - describe('userRecordConstructor', () => { it('will provide falsey values for fields that are not in raw wire data', () => { const record = identity.userRecordConstructor({ uid: '123' }); @@ -271,301 +177,6 @@ describe('identity', () => { }); }); - describe('getPublicKeyFromHeader', () => { - it('should throw if header.alg is not expected', () => { - expect(() => - identity.getPublicKeyFromHeader({ alg: 'RS128' }, {}) - ).to.throw( - `Provided JWT has incorrect algorithm. Expected ${identity.JWT_ALG} but got RS128.` - ); - }); - - it('should throw if header.kid is undefined', () => { - expect(() => - identity.getPublicKeyFromHeader({ alg: identity.JWT_ALG }, {}) - ).to.throw('JWT header missing "kid" claim.'); - }); - - it('should throw if the public keys do not have a property that matches header.kid', () => { - expect(() => - identity.getPublicKeyFromHeader( - { - alg: identity.JWT_ALG, - kid: '123456', - }, - {} - ) - ).to.throw( - 'Provided JWT has "kid" claim which does not correspond to a known public key. Most likely the JWT is expired.' - ); - }); - - it('should return the correct public key', () => { - expect( - identity.getPublicKeyFromHeader( - { - alg: identity.JWT_ALG, - kid: '123456', - }, - { - 123456: '7890', - 2468: '1357', - } - ) - ).to.eq('7890'); - }); - }); - - describe('isAuthorizedCloudFunctionURL', () => { - it('should return false on a bad gcf location', () => { - expect( - identity.isAuthorizedCloudFunctionURL( - `https://us-central1-europe-${PROJECT}.cloudfunctions.net/function-1`, - PROJECT - ) - ).to.be.false; - }); - - it('should return false on a bad project', () => { - expect( - identity.isAuthorizedCloudFunctionURL( - `https://us-central1-${PROJECT}-old.cloudfunctions.net/function-1`, - PROJECT - ) - ).to.be.false; - }); - - it('should return true on a good url', () => { - expect(identity.isAuthorizedCloudFunctionURL(VALID_URL, PROJECT)).to.be - .true; - }); - }); - - describe('checkDecodedToken', () => { - it('should throw on mismatching event types', () => { - expect(() => - identity.checkDecodedToken( - { - event_type: EVENT, - } as identity.DecodedPayload, - 'newEvent', - PROJECT - ) - ).to.throw(`Expected "newEvent" but received "${EVENT}".`); - }); - it('should throw on unauthorized function url', () => { - expect(() => - identity.checkDecodedToken( - { - aud: `fake-region-${PROJECT}.cloudfunctions.net/fn1`, - event_type: EVENT, - } as identity.DecodedPayload, - EVENT, - PROJECT - ) - ).to.throw('Provided JWT has incorrect "aud" (audience) claim.'); - }); - - it('should throw on a bad iss property', () => { - expect(() => - identity.checkDecodedToken( - { - aud: VALID_URL, - iss: `https://someissuer.com/a-project`, - event_type: EVENT, - } as identity.DecodedPayload, - EVENT, - PROJECT - ) - ).to.throw( - `Provided JWT has incorrect "iss" (issuer) claim. Expected "${identity.JWT_ISSUER}${PROJECT}" but got "https://someissuer.com/a-project".` - ); - }); - - it('should throw if sub is not a string', () => { - expect(() => - identity.checkDecodedToken( - ({ - aud: VALID_URL, - iss: `${identity.JWT_ISSUER}${PROJECT}`, - sub: { - key: 'val', - }, - event_type: EVENT, - } as unknown) as identity.DecodedPayload, - EVENT, - PROJECT - ) - ).to.throw('Provided JWT has no "sub" (subject) claim.'); - }); - - it('should throw if sub is empty', () => { - expect(() => - identity.checkDecodedToken( - { - aud: VALID_URL, - iss: `${identity.JWT_ISSUER}${PROJECT}`, - sub: '', - event_type: EVENT, - } as identity.DecodedPayload, - EVENT, - PROJECT - ) - ).to.throw('Provided JWT has no "sub" (subject) claim.'); - }); - - it('should throw if sub length is larger than 128 chars', () => { - const str = 'a'.repeat(129); - expect(() => - identity.checkDecodedToken( - { - aud: VALID_URL, - iss: `${identity.JWT_ISSUER}${PROJECT}`, - sub: str.toString(), - event_type: EVENT, - } as identity.DecodedPayload, - EVENT, - PROJECT - ) - ).to.throw( - 'Provided JWT has "sub" (subject) claim longer than 128 characters.' - ); - }); - - it('should not throw an error and set uid to sub', () => { - expect(() => { - const sub = '123456'; - const decoded = { - aud: VALID_URL, - iss: `${identity.JWT_ISSUER}${PROJECT}`, - sub, - event_type: EVENT, - } as identity.DecodedPayload; - - identity.checkDecodedToken(decoded, EVENT, PROJECT); - - expect(decoded.uid).to.equal(sub); - }).to.not.throw(); - }); - }); - - describe('decodeJWT', () => { - let jwtDecodeStub: sinon.SinonStub; - - beforeEach(() => { - jwtDecodeStub = sinon - .stub(jwt, 'decode') - .throws('Unexpected call to jwt.decode'); - }); - - afterEach(() => { - sinon.verifyAndRestore(); - }); - - it('should throw HttpsError if jwt.decode errors', () => { - jwtDecodeStub.throws('An internal decode error occurred.'); - - expect(() => identity.decodeJWT('123456')).to.throw( - 'Failed to decode the JWT.' - ); - }); - - it('should error if jwt decoded returns undefined', () => { - jwtDecodeStub.returns(undefined); - - expect(() => identity.decodeJWT('123456')).to.throw( - 'The decoded JWT is not structured correctly.' - ); - }); - - it('should error if decoded jwt does not have a payload field', () => { - jwtDecodeStub.returns({ - header: { key: 'val' }, - }); - - expect(() => identity.decodeJWT('123456')).to.throw( - 'The decoded JWT is not structured correctly.' - ); - }); - - it('should return the raw decoded jwt', () => { - const decoded = { - header: { key: 'val' }, - payload: { - aud: VALID_URL, - iss: `${identity.JWT_ISSUER}${PROJECT}`, - event_type: EVENT, - }, - }; - jwtDecodeStub.returns(decoded); - - expect(identity.decodeJWT('123456')).to.deep.equal(decoded); - }); - }); - - describe('shouldVerifyJWT', () => { - /** Stub test that will fail when we eventually change the function */ - it('should return true', () => { - expect(identity.shouldVerifyJWT()).to.be.true; - }); - }); - - describe('verifyJWT', () => { - const time = Date.now(); - let jwtVerifyStub: sinon.SinonStub; - const keysCache = { - publicKeys: { - 123456: '7890', - 2468: '1357', - }, - publicKeysExpireAt: time + identity.INVALID_TOKEN_BUFFER + 10000, - }; - const rawDecodedJWT = { - header: { - alg: identity.JWT_ALG, - kid: '2468', - }, - payload: { - aud: VALID_URL, - iss: `${identity.JWT_ISSUER}${PROJECT}`, - event_type: EVENT, - }, - }; - - beforeEach(() => { - jwtVerifyStub = sinon - .stub(jwt, 'verify') - .throws('Unexpected call to jwt.verify'); - }); - - afterEach(() => { - sinon.verifyAndRestore(); - }); - - it('should error if header does not exist', () => { - const rawDecodedJwt = { payload: 'val' }; - - expect(() => - identity.verifyJWT('123456', rawDecodedJwt, keysCache, time) - ).to.throw( - 'Unable to verify JWT payload, the decoded JWT does not have a header property.' - ); - }); - - it('should return the decoded jwt', () => { - const decoded = { - aud: VALID_URL, - iss: `${identity.JWT_ISSUER}${PROJECT}`, - event_type: EVENT, - }; - jwtVerifyStub.returns(decoded); - - expect( - identity.verifyJWT('123456', rawDecodedJWT, keysCache, time) - ).to.deep.equal(decoded); - }); - }); - describe('parseMetadata', () => { const decodedMetadata = { last_sign_in_time: 1476235905, diff --git a/spec/fixtures/mockrequest.ts b/spec/fixtures/mockrequest.ts index 4a4a6df70..5accb339e 100644 --- a/spec/fixtures/mockrequest.ts +++ b/spec/fixtures/mockrequest.ts @@ -80,6 +80,7 @@ export function generateIdToken(projectId: string): string { algorithm: 'RS256', header: { kid: mockKey.key_id, + alg: 'RS256', }, }; return jwt.sign(claims, mockKey.private_key, options); diff --git a/spec/v1/providers/auth.spec.ts b/spec/v1/providers/auth.spec.ts index 8659a1967..91e394bc7 100644 --- a/spec/v1/providers/auth.spec.ts +++ b/spec/v1/providers/auth.spec.ts @@ -135,6 +135,182 @@ describe('Auth Functions', () => { }); }); + describe('beforeCreate', () => { + it('should create the function without options', () => { + const fn = auth.user().beforeCreate((u, c) => Promise.resolve()); + + expect(fn.__trigger).to.deep.equal({ + labels: {}, + blockingTrigger: { + eventType: 'providers/cloud.auth/eventTypes/user.beforeCreate', + options: { + accessToken: false, + idToken: false, + refreshToken: false, + }, + }, + }); + expect(fn.__endpoint).to.deep.equal({ + platform: 'gcfv1', + labels: {}, + blockingTrigger: { + eventType: 'providers/cloud.auth/eventTypes/user.beforeCreate', + options: { + accessToken: false, + idToken: false, + refreshToken: false, + }, + }, + }); + expect(fn.__requiredAPIs).to.deep.equal([ + { + api: 'identitytoolkit.googleapis.com', + reason: 'Needed for auth blocking functions', + }, + ]); + }); + + it('should create the function with options', () => { + const fn = functions + .region('us-east1') + .runWith({ + timeoutSeconds: 90, + memory: '256MB', + }) + .auth.user({ + blockingOptions: { + accessToken: true, + refreshToken: false, + }, + }) + .beforeCreate((u, c) => Promise.resolve()); + + expect(fn.__trigger).to.deep.equal({ + labels: {}, + regions: ['us-east1'], + availableMemoryMb: 256, + timeout: '90s', + blockingTrigger: { + eventType: 'providers/cloud.auth/eventTypes/user.beforeCreate', + options: { + accessToken: true, + idToken: false, + refreshToken: false, + }, + }, + }); + expect(fn.__endpoint).to.deep.equal({ + platform: 'gcfv1', + labels: {}, + region: ['us-east1'], + availableMemoryMb: 256, + timeoutSeconds: 90, + blockingTrigger: { + eventType: 'providers/cloud.auth/eventTypes/user.beforeCreate', + options: { + accessToken: true, + idToken: false, + refreshToken: false, + }, + }, + }); + expect(fn.__requiredAPIs).to.deep.equal([ + { + api: 'identitytoolkit.googleapis.com', + reason: 'Needed for auth blocking functions', + }, + ]); + }); + }); + + describe('beforeSignIn', () => { + it('should create the function without options', () => { + const fn = auth.user().beforeSignIn((u, c) => Promise.resolve()); + + expect(fn.__trigger).to.deep.equal({ + labels: {}, + blockingTrigger: { + eventType: 'providers/cloud.auth/eventTypes/user.beforeSignIn', + options: { + accessToken: false, + idToken: false, + refreshToken: false, + }, + }, + }); + expect(fn.__endpoint).to.deep.equal({ + platform: 'gcfv1', + labels: {}, + blockingTrigger: { + eventType: 'providers/cloud.auth/eventTypes/user.beforeSignIn', + options: { + accessToken: false, + idToken: false, + refreshToken: false, + }, + }, + }); + expect(fn.__requiredAPIs).to.deep.equal([ + { + api: 'identitytoolkit.googleapis.com', + reason: 'Needed for auth blocking functions', + }, + ]); + }); + + it('should create the function with options', () => { + const fn = functions + .region('us-east1') + .runWith({ + timeoutSeconds: 90, + memory: '256MB', + }) + .auth.user({ + blockingOptions: { + accessToken: true, + refreshToken: false, + }, + }) + .beforeSignIn((u, c) => Promise.resolve()); + + expect(fn.__trigger).to.deep.equal({ + labels: {}, + regions: ['us-east1'], + availableMemoryMb: 256, + timeout: '90s', + blockingTrigger: { + eventType: 'providers/cloud.auth/eventTypes/user.beforeSignIn', + options: { + accessToken: true, + idToken: false, + refreshToken: false, + }, + }, + }); + expect(fn.__endpoint).to.deep.equal({ + platform: 'gcfv1', + labels: {}, + region: ['us-east1'], + availableMemoryMb: 256, + timeoutSeconds: 90, + blockingTrigger: { + eventType: 'providers/cloud.auth/eventTypes/user.beforeSignIn', + options: { + accessToken: true, + idToken: false, + refreshToken: false, + }, + }, + }); + expect(fn.__requiredAPIs).to.deep.equal([ + { + api: 'identitytoolkit.googleapis.com', + reason: 'Needed for auth blocking functions', + }, + ]); + }); + }); + describe('#_dataConstructor', () => { let cloudFunctionDelete: CloudFunction; diff --git a/spec/v2/providers/identity.spec.ts b/spec/v2/providers/identity.spec.ts new file mode 100644 index 000000000..4c8b19d3d --- /dev/null +++ b/spec/v2/providers/identity.spec.ts @@ -0,0 +1,277 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as identity from '../../../src/v2/providers/identity'; + +const BEFORE_CREATE_TRIGGER = { + eventType: 'providers/cloud.auth/eventTypes/user.beforeCreate', + options: { + accessToken: false, + idToken: false, + refreshToken: false, + }, +}; + +const BEFORE_SIGN_IN_TRIGGER = { + eventType: 'providers/cloud.auth/eventTypes/user.beforeSignIn', + options: { + accessToken: false, + idToken: false, + refreshToken: false, + }, +}; + +const opts: identity.BlockingOptions = { + accessToken: true, + refreshToken: false, + minInstances: 1, + region: 'us-west1', +}; + +describe('identity', () => { + describe('beforeUserCreated', () => { + it('should accept a handler', () => { + const fn = identity.beforeUserCreated((event) => Promise.resolve()); + + expect(fn.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + blockingTrigger: BEFORE_CREATE_TRIGGER, + }); + expect(fn.__requiredAPIs).to.deep.equal([ + { + api: 'identitytoolkit.googleapis.com', + reason: 'Needed for auth blocking functions', + }, + ]); + }); + + it('should accept options and a handler', () => { + const fn = identity.beforeUserCreated(opts, (event) => Promise.resolve()); + + expect(fn.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + minInstances: 1, + region: ['us-west1'], + blockingTrigger: { + ...BEFORE_CREATE_TRIGGER, + options: { + ...BEFORE_CREATE_TRIGGER.options, + accessToken: true, + }, + }, + }); + expect(fn.__requiredAPIs).to.deep.equal([ + { + api: 'identitytoolkit.googleapis.com', + reason: 'Needed for auth blocking functions', + }, + ]); + }); + }); + + describe('beforeUserSignedIn', () => { + it('should accept a handler', () => { + const fn = identity.beforeUserSignedIn((event) => Promise.resolve()); + + expect(fn.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + blockingTrigger: BEFORE_SIGN_IN_TRIGGER, + }); + expect(fn.__requiredAPIs).to.deep.equal([ + { + api: 'identitytoolkit.googleapis.com', + reason: 'Needed for auth blocking functions', + }, + ]); + }); + + it('should accept options and a handler', () => { + const fn = identity.beforeUserSignedIn(opts, (event) => + Promise.resolve() + ); + + expect(fn.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + minInstances: 1, + region: ['us-west1'], + blockingTrigger: { + ...BEFORE_SIGN_IN_TRIGGER, + options: { + ...BEFORE_SIGN_IN_TRIGGER.options, + accessToken: true, + }, + }, + }); + expect(fn.__requiredAPIs).to.deep.equal([ + { + api: 'identitytoolkit.googleapis.com', + reason: 'Needed for auth blocking functions', + }, + ]); + }); + }); + + describe('beforeOperation', () => { + it('should handle eventType and handler for before create events', () => { + const fn = identity.beforeOperation( + 'beforeCreate', + (event) => Promise.resolve(), + undefined + ); + + expect(fn.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + blockingTrigger: BEFORE_CREATE_TRIGGER, + }); + expect(fn.__requiredAPIs).to.deep.equal([ + { + api: 'identitytoolkit.googleapis.com', + reason: 'Needed for auth blocking functions', + }, + ]); + }); + + it('should handle eventType and handler for before sign in events', () => { + const fn = identity.beforeOperation( + 'beforeSignIn', + (event) => Promise.resolve(), + undefined + ); + + expect(fn.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + blockingTrigger: BEFORE_SIGN_IN_TRIGGER, + }); + expect(fn.__requiredAPIs).to.deep.equal([ + { + api: 'identitytoolkit.googleapis.com', + reason: 'Needed for auth blocking functions', + }, + ]); + }); + + it('should handle eventType, options, and handler for before create events', () => { + const fn = identity.beforeOperation('beforeCreate', opts, (event) => + Promise.resolve() + ); + + expect(fn.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + minInstances: 1, + region: ['us-west1'], + blockingTrigger: { + ...BEFORE_CREATE_TRIGGER, + options: { + ...BEFORE_CREATE_TRIGGER.options, + accessToken: true, + }, + }, + }); + expect(fn.__requiredAPIs).to.deep.equal([ + { + api: 'identitytoolkit.googleapis.com', + reason: 'Needed for auth blocking functions', + }, + ]); + }); + + it('should handle eventType, options, and handler for before sign in events', () => { + const fn = identity.beforeOperation('beforeSignIn', opts, (event) => + Promise.resolve() + ); + + expect(fn.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + minInstances: 1, + region: ['us-west1'], + blockingTrigger: { + ...BEFORE_SIGN_IN_TRIGGER, + options: { + ...BEFORE_SIGN_IN_TRIGGER.options, + accessToken: true, + }, + }, + }); + expect(fn.__requiredAPIs).to.deep.equal([ + { + api: 'identitytoolkit.googleapis.com', + reason: 'Needed for auth blocking functions', + }, + ]); + }); + }); + + describe('getOpts', () => { + it('should parse an empty object', () => { + const internalOpts = identity.getOpts({}); + + expect(internalOpts).to.deep.equal({ + opts: {}, + accessToken: false, + idToken: false, + refreshToken: false, + }); + }); + + it('should parse global options', () => { + const internalOpts = identity.getOpts({ region: 'us-central1', cpu: 2 }); + + expect(internalOpts).to.deep.equal({ + opts: { + region: 'us-central1', + cpu: 2, + }, + accessToken: false, + idToken: false, + refreshToken: false, + }); + }); + + it('should a full options', () => { + const internalOpts = identity.getOpts({ + region: 'us-central1', + cpu: 2, + accessToken: true, + idToken: false, + refreshToken: true, + }); + + expect(internalOpts).to.deep.equal({ + opts: { + region: 'us-central1', + cpu: 2, + }, + accessToken: true, + idToken: false, + refreshToken: true, + }); + }); + }); +}); diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index ffe94e031..5941b1db3 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -264,6 +264,10 @@ export interface Resource { export interface TriggerAnnotated { __trigger: { availableMemoryMb?: number; + blockingTrigger?: { + eventType: string; + options?: Record; + }; eventTrigger?: { eventType: string; resource: string; @@ -315,6 +319,11 @@ export type HttpsFunction = TriggerAnnotated & EndpointAnnotated & ((req: Request, resp: Response) => void | Promise); +/** + * The Cloud Function type for Blocking triggers. + */ +export type BlockingFunction = HttpsFunction; + /** * The Cloud Function type for all non-HTTPS triggers. This should be exported * from your JavaScript file to define a Cloud Function. diff --git a/src/common/providers/https.ts b/src/common/providers/https.ts index 76e02cd37..29f516bc7 100644 --- a/src/common/providers/https.ts +++ b/src/common/providers/https.ts @@ -505,7 +505,8 @@ interface CallableTokenStatus { auth: TokenStatus; } -function unsafeDecodeToken(token: string): unknown { +/** @internal */ +export function unsafeDecodeToken(token: string): unknown { if (!JWT_REGEX.test(token)) { return {}; } diff --git a/src/common/providers/identity.ts b/src/common/providers/identity.ts index 8872057ad..6912b9d1b 100644 --- a/src/common/providers/identity.ts +++ b/src/common/providers/identity.ts @@ -22,34 +22,14 @@ import * as express from 'express'; import * as firebase from 'firebase-admin'; -import * as jwt from 'jsonwebtoken'; -import fetch from 'node-fetch'; import { logger } from '../..'; +import { apps } from '../../apps'; import { EventContext } from '../../cloud-functions'; -import { SUPPORTED_REGIONS } from '../../function-configuration'; -import { HttpsError } from './https'; +import { isDebugFeatureEnabled } from '../debug'; +import { HttpsError, unsafeDecodeToken } from './https'; export { HttpsError }; -/** @internal */ -export const INVALID_TOKEN_BUFFER = 60000; // set to 1 minute - -/** @internal */ -export const JWT_CLIENT_CERT_URL = 'https://www.googleapis.com'; -/** @internal */ -export const JWT_CLIENT_CERT_PATH = - 'robot/v1/metadata/x509/securetoken@system.gserviceaccount.com'; -/** @internal */ -export const JWT_ALG = 'RS256'; -/** @internal */ -export const JWT_ISSUER = 'https://securetoken.google.com/'; - -/** @internal */ -export interface PublicKeysCache { - publicKeys: Record; - publicKeysExpireAt?: number; -} - const DISALLOWED_CUSTOM_CLAIMS = [ 'acr', 'amr', @@ -70,6 +50,9 @@ const DISALLOWED_CUSTOM_CLAIMS = [ const CLAIMS_MAX_PAYLOAD_SIZE = 1000; +/** Shorthand auth blocking events from GCIP. */ +export type AuthBlockingEventType = 'beforeCreate' | 'beforeSignIn'; + const EVENT_MAPPING: Record = { beforeCreate: 'providers/cloud.auth/eventTypes/user.beforeCreate', beforeSignIn: 'providers/cloud.auth/eventTypes/user.beforeSignIn', @@ -342,6 +325,11 @@ export interface AuthEventContext extends EventContext { credential?: Credential; } +/** Defines the auth event for v2 blocking events */ +export interface AuthBlockingEvent extends AuthEventContext { + data: AuthUserRecord; +} + /** The handler response type for beforeCreate blocking events */ export interface BeforeCreateResponse { displayName?: string; @@ -430,64 +418,26 @@ export interface DecodedPayload { [key: string]: any; } -/** - * Helper to determine if we refresh the public keys - * @internal - */ -export function invalidPublicKeys( - keys: PublicKeysCache, - time: number = Date.now() -): boolean { - if (!keys.publicKeysExpireAt) { - return true; - } - return time + INVALID_TOKEN_BUFFER >= keys.publicKeysExpireAt; -} - -/** - * Helper to parse the response headers to obtain the expiration time. - * @internal - */ -export function setKeyExpirationTime( - response: any, - keysCache: PublicKeysCache, - time: number -): void { - if (response.headers.has('cache-control')) { - const ccHeader = response.headers.get('cache-control'); - const maxAgeEntry = ccHeader - .split(', ') - .find((item) => item.includes('max-age')); - if (maxAgeEntry) { - const maxAge = +maxAgeEntry.trim().split('=')[1]; - keysCache.publicKeysExpireAt = time + maxAge * 1000; - } - } -} - -/** - * Fetch the public keys for use in decoding and verifying the jwt sent from identity platform. - */ -async function refreshPublicKeys( - keysCache: PublicKeysCache, - time: number = Date.now() -): Promise { - const url = `${JWT_CLIENT_CERT_URL}/${JWT_CLIENT_CERT_PATH}`; - try { - const response = await fetch(url); - setKeyExpirationTime(response, keysCache, time); - const data = await response.json(); - keysCache.publicKeys = data as Record; - } catch (err) { - logger.error( - `Failed to obtain public keys for JWT verification: ${err.message}` - ); - throw new HttpsError( - 'internal', - 'Failed to obtain the public keys for JWT verification.' - ); - } -} +type HandlerV1 = ( + user: AuthUserRecord, + context: AuthEventContext +) => + | BeforeCreateResponse + | BeforeSignInResponse + | void + | Promise + | Promise + | Promise; + +type HandlerV2 = ( + event: AuthBlockingEvent +) => + | BeforeCreateResponse + | BeforeSignInResponse + | void + | Promise + | Promise + | Promise; /** * Checks for a valid identity platform web request, otherwise throws an HttpsError @@ -512,164 +462,19 @@ export function isValidRequest(req: express.Request): boolean { return true; } -/** @internal */ -export function getPublicKeyFromHeader( - header: Record, - publicKeys: Record -): string { - if (header.alg !== JWT_ALG) { - throw new HttpsError( - 'invalid-argument', - `Provided JWT has incorrect algorithm. Expected ${JWT_ALG} but got ${header.alg}.` - ); - } - if (!header.kid) { - throw new HttpsError('invalid-argument', 'JWT header missing "kid" claim.'); - } - if (!publicKeys.hasOwnProperty(header.kid)) { - throw new HttpsError( - 'invalid-argument', - 'Provided JWT has "kid" claim which does not correspond to a known public key. Most likely the JWT is expired.' - ); - } - - return publicKeys[header.kid]; -} - -/** - * Checks for a well forms cloud functions url - * @internal - */ -export function isAuthorizedCloudFunctionURL( - cloudFunctionUrl: string, - projectId: string -): boolean { - const re = new RegExp( - `^https://(${SUPPORTED_REGIONS.join( - '|' - )})+-${projectId}\.cloudfunctions\.net/` - ); - const res = re.exec(cloudFunctionUrl) || []; - return res.length > 0; -} - -/** - * Checks for errors in a decoded jwt - * @internal - */ -export function checkDecodedToken( - decodedJWT: DecodedPayload, - eventType: string, - projectId: string -): void { - if (decodedJWT.event_type !== eventType) { - throw new HttpsError( - 'invalid-argument', - `Expected "${eventType}" but received "${decodedJWT.event_type}".` - ); - } - if (!isAuthorizedCloudFunctionURL(decodedJWT.aud, projectId)) { - throw new HttpsError( - 'invalid-argument', - 'Provided JWT has incorrect "aud" (audience) claim.' - ); - } - if (decodedJWT.iss !== `${JWT_ISSUER}${projectId}`) { - throw new HttpsError( - 'invalid-argument', - `Provided JWT has incorrect "iss" (issuer) claim. Expected ` + - `"${JWT_ISSUER}${projectId}" but got "${decodedJWT.iss}".` - ); - } - if (typeof decodedJWT.sub !== 'string' || decodedJWT.sub.length === 0) { - throw new HttpsError( - 'invalid-argument', - 'Provided JWT has no "sub" (subject) claim.' - ); - } - if (decodedJWT.sub.length > 128) { - throw new HttpsError( - 'invalid-argument', - 'Provided JWT has "sub" (subject) claim longer than 128 characters.' - ); - } - // set uid to sub - decodedJWT.uid = decodedJWT.sub; -} - /** - * Helper function to decode the jwt, internally uses the 'jsonwebtoken' package. - * @internal + * Decode, but not verify, an Auth Blocking token. + * + * Do not use in production. Token should always be verified using the Admin SDK. + * + * This is exposed only for testing. */ -export function decodeJWT(token: string): Record { - let decoded: Record; - try { - decoded = jwt.decode(token, { complete: true }) as Record; - } catch (err) { - logger.error('Decoding the JWT failed', err); - throw new HttpsError('internal', 'Failed to decode the JWT.'); - } - if (!decoded?.payload) { - throw new HttpsError( - 'internal', - 'The decoded JWT is not structured correctly.' - ); - } +function unsafeDecodeAuthBlockingToken(token: string): DecodedPayload { + const decoded = unsafeDecodeToken(token) as DecodedPayload; + decoded.uid = decoded.sub; return decoded; } -/** - * Helper function to determine if we need to do full verification of the jwt - * @internal - */ -export function shouldVerifyJWT(): boolean { - // TODO(colerogers): add emulator support to skip verification - return true; -} - -/** - * Verifies the jwt using the 'jwt' library and decodes the token with the public keys - * Throws an error if the event types do not match - * @internal - */ -export function verifyJWT( - token: string, - rawDecodedJWT: Record, - keysCache: PublicKeysCache, - time: number = Date.now() -): DecodedPayload { - if (!rawDecodedJWT.header) { - throw new HttpsError( - 'internal', - 'Unable to verify JWT payload, the decoded JWT does not have a header property.' - ); - } - const header = rawDecodedJWT.header; - let publicKey; - try { - if (invalidPublicKeys(keysCache, time)) { - refreshPublicKeys(keysCache); - } - publicKey = getPublicKeyFromHeader(header, keysCache.publicKeys); - return jwt.verify(token, publicKey, { - algorithms: [JWT_ALG], - }) as DecodedPayload; - } catch (err) { - logger.error('Verifying the JWT failed', err); - } - // force refresh keys and retry one more time - try { - refreshPublicKeys(keysCache); - publicKey = getPublicKeyFromHeader(header, keysCache.publicKeys); - return jwt.verify(token, publicKey, { - algorithms: [JWT_ALG], - }) as DecodedPayload; - } catch (err) { - logger.error('Verifying the JWT failed again', err); - throw new HttpsError('internal', 'Failed to verify the JWT.'); - } -} - /** * Helper function to parse the decoded metadata object into a UserMetaData object * @internal @@ -1000,42 +805,9 @@ export function getUpdateMask( } /** @internal */ -export function createHandler( - handler: ( - user: AuthUserRecord, - context: AuthEventContext - ) => - | BeforeCreateResponse - | Promise - | BeforeSignInResponse - | Promise - | void - | Promise, - eventType: string, - keysCache: PublicKeysCache -): (req: express.Request, resp: express.Response) => Promise { - const wrappedHandler = wrapHandler(handler, eventType, keysCache); - return (req: express.Request, res: express.Response) => { - return new Promise((resolve) => { - res.on('finish', resolve); - resolve(wrappedHandler(req, res)); - }); - }; -} - -function wrapHandler( - handler: ( - user: AuthUserRecord, - context: AuthEventContext - ) => - | BeforeCreateResponse - | Promise - | BeforeSignInResponse - | Promise - | void - | Promise, - eventType: string, - keysCache: PublicKeysCache +export function wrapHandler( + eventType: AuthBlockingEventType, + handler: HandlerV1 | HandlerV2 ) { return async (req: express.Request, res: express.Response): Promise => { try { @@ -1044,23 +816,48 @@ function wrapHandler( logger.error('Invalid request, unable to process'); throw new HttpsError('invalid-argument', 'Bad Request'); } - const rawDecodedJWT = decodeJWT(req.body.data.jwt); - const decodedPayload = shouldVerifyJWT() - ? verifyJWT(req.body.data.jwt, rawDecodedJWT, keysCache) - : (rawDecodedJWT.payload as DecodedPayload); - checkDecodedToken(decodedPayload, eventType, projectId); + + if (!apps().admin.auth()._verifyAuthBlockingToken) { + throw new Error( + 'Cannot validate Auth Blocking token. Please update Firebase Admin SDK to >= v10.1.0' + ); + } + + const decodedPayload: DecodedPayload = isDebugFeatureEnabled( + 'skipTokenVerification' + ) + ? unsafeDecodeAuthBlockingToken(req.body.data.jwt) + : await apps() + .admin.auth() + ._verifyAuthBlockingToken(req.body.data.jwt); + const authUserRecord = parseAuthUserRecord(decodedPayload.user_record); const authEventContext = parseAuthEventContext(decodedPayload, projectId); - const authResponse = - (await handler(authUserRecord, authEventContext)) || undefined; + + let authResponse; + if (handler.length === 2) { + authResponse = + (await (handler as HandlerV1)(authUserRecord, authEventContext)) || + undefined; + } else { + authResponse = + (await (handler as HandlerV2)({ + ...authEventContext, + data: authUserRecord, + } as AuthBlockingEvent)) || undefined; + } + validateAuthResponse(eventType, authResponse); const updateMask = getUpdateMask(authResponse); - const result = { - userRecord: { - ...authResponse, - updateMask, - }, - }; + const result = + updateMask.length === 0 + ? {} + : { + userRecord: { + ...authResponse, + updateMask, + }, + }; res.status(200); res.setHeader('Content-Type', 'application/json'); @@ -1072,9 +869,10 @@ function wrapHandler( err = new HttpsError('internal', 'An unexpected error occurred.'); } - res.status(err.code); + const { status } = err.httpErrorCode; + const body = { error: err.toJSON() }; res.setHeader('Content-Type', 'application/json'); - res.send({ error: err.toJson() }); + res.status(status).send(body); } }; } diff --git a/src/function-builder.ts b/src/function-builder.ts index 88f58bb1c..f0158a3e0 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -513,7 +513,8 @@ export class FunctionBuilder { /** * Handle events related to Firebase authentication users. */ - user: () => auth._userWithOptions(this.options), + user: (userOptions?: auth.UserOptions) => + auth._userWithOptions(this.options, userOptions), }; } diff --git a/src/handler-builder.ts b/src/handler-builder.ts index efa0d9f4c..c976bf8e6 100644 --- a/src/handler-builder.ts +++ b/src/handler-builder.ts @@ -352,7 +352,7 @@ export class HandlerBuilder { get auth() { return { get user() { - return new auth.UserBuilder(() => null, {}); + return new auth.UserBuilder(() => null, {}, {}); }, }; } diff --git a/src/providers/auth.ts b/src/providers/auth.ts index 98568ce18..283310472 100644 --- a/src/providers/auth.ts +++ b/src/providers/auth.ts @@ -21,42 +21,70 @@ // SOFTWARE. import { + BlockingFunction, CloudFunction, Event, EventContext, makeCloudFunction, + optionsToEndpoint, + optionsToTrigger, } from '../cloud-functions'; import { + AuthBlockingEventType, + AuthEventContext, + AuthUserRecord, + BeforeCreateResponse, + BeforeSignInResponse, + HttpsError, UserInfo, UserRecord, userRecordConstructor, UserRecordMetadata, + wrapHandler, } from '../common/providers/identity'; import { DeploymentOptions } from '../function-configuration'; // TODO: yank in next breaking change release export { UserRecord, UserInfo, UserRecordMetadata, userRecordConstructor }; +export { HttpsError }; + /** @hidden */ export const provider = 'google.firebase.auth'; /** @hidden */ export const service = 'firebaseauth.googleapis.com'; +/** Resource level options */ +export interface UserOptions { + blockingOptions?: { + idToken?: boolean; + accessToken?: boolean; + refreshToken?: boolean; + }; +} + /** * Handle events related to Firebase authentication users. */ -export function user() { - return _userWithOptions({}); +export function user(userOptions?: UserOptions) { + return _userWithOptions({}, userOptions || {}); } /** @hidden */ -export function _userWithOptions(options: DeploymentOptions) { - return new UserBuilder(() => { - if (!process.env.GCLOUD_PROJECT) { - throw new Error('process.env.GCLOUD_PROJECT is not set.'); - } - return 'projects/' + process.env.GCLOUD_PROJECT; - }, options); +export function _userWithOptions( + options: DeploymentOptions, + userOptions: UserOptions +) { + return new UserBuilder( + () => { + if (!process.env.GCLOUD_PROJECT) { + throw new Error('process.env.GCLOUD_PROJECT is not set.'); + } + return 'projects/' + process.env.GCLOUD_PROJECT; + }, + options, + userOptions + ); } /** Builder used to create Cloud Functions for Firebase Auth user lifecycle events. */ @@ -68,7 +96,8 @@ export class UserBuilder { /** @hidden */ constructor( private triggerResource: () => string, - private options?: DeploymentOptions + private options: DeploymentOptions, + private userOptions?: UserOptions ) {} /** Respond to the creation of a Firebase Auth user. */ @@ -85,6 +114,32 @@ export class UserBuilder { return this.onOperation(handler, 'user.delete'); } + beforeCreate( + handler: ( + user: AuthUserRecord, + context: AuthEventContext + ) => + | BeforeCreateResponse + | void + | Promise + | Promise + ): BlockingFunction { + return this.beforeOperation(handler, 'beforeCreate'); + } + + beforeSignIn( + handler: ( + user: AuthUserRecord, + context: AuthEventContext + ) => + | BeforeSignInResponse + | void + | Promise + | Promise + ): BlockingFunction { + return this.beforeOperation(handler, 'beforeSignIn'); + } + private onOperation( handler: ( user: UserRecord, @@ -103,4 +158,69 @@ export class UserBuilder { options: this.options, }); } + + private beforeOperation( + handler: ( + user: AuthUserRecord, + context: AuthEventContext + ) => + | BeforeCreateResponse + | BeforeSignInResponse + | void + | Promise + | Promise + | Promise, + eventType: AuthBlockingEventType + ): BlockingFunction { + const accessToken = this.userOptions?.blockingOptions?.accessToken || false; + const idToken = this.userOptions?.blockingOptions?.idToken || false; + const refreshToken = + this.userOptions?.blockingOptions?.refreshToken || false; + + // Create our own function that just calls the provided function so we know for sure that + // handler takes two arguments. This is something common/providers/identity depends on. + const wrappedHandler = (user: AuthUserRecord, context: AuthEventContext) => + handler(user, context); + const func: any = wrapHandler(eventType, wrappedHandler); + + const legacyEventType = `providers/cloud.auth/eventTypes/user.${eventType}`; + + func.__trigger = { + labels: {}, + ...optionsToTrigger(this.options), + blockingTrigger: { + eventType: legacyEventType, + options: { + accessToken, + idToken, + refreshToken, + }, + }, + }; + + func.__endpoint = { + platform: 'gcfv1', + labels: {}, + ...optionsToEndpoint(this.options), + blockingTrigger: { + eventType: legacyEventType, + options: { + accessToken, + idToken, + refreshToken, + }, + }, + }; + + func.__requiredAPIs = [ + { + api: 'identitytoolkit.googleapis.com', + reason: 'Needed for auth blocking functions', + }, + ]; + + func.run = handler; + + return func; + } } diff --git a/src/runtime/manifest.ts b/src/runtime/manifest.ts index 7939d9c09..562d1a52f 100644 --- a/src/runtime/manifest.ts +++ b/src/runtime/manifest.ts @@ -70,6 +70,11 @@ export interface ManifestEndpoint { maxDoublings?: number; }; }; + + blockingTrigger?: { + eventType: string; + options?: Record; + }; } export interface ManifestRequiredAPI { diff --git a/src/types/global.d.ts b/src/types/global.d.ts new file mode 100644 index 000000000..164cd4d74 --- /dev/null +++ b/src/types/global.d.ts @@ -0,0 +1,6 @@ +export {}; + +declare global { + interface AbortSignal {} + interface AbortController {} +} diff --git a/src/v2/core.ts b/src/v2/core.ts index 1160d09ed..af02c1d0d 100644 --- a/src/v2/core.ts +++ b/src/v2/core.ts @@ -44,6 +44,10 @@ export interface TriggerAnnotation { vpcConnectorEgressSettings?: string; serviceAccountEmail?: string; ingressSettings?: string; + blockingTrigger?: { + eventType: string; + options?: Record; + }; // TODO: schedule } diff --git a/src/v2/index.ts b/src/v2/index.ts index ae2c5df51..31070b4dc 100644 --- a/src/v2/index.ts +++ b/src/v2/index.ts @@ -25,11 +25,22 @@ import * as params from './params'; import * as alerts from './providers/alerts'; import * as eventarc from './providers/eventarc'; import * as https from './providers/https'; +import * as identity from './providers/identity'; import * as pubsub from './providers/pubsub'; import * as storage from './providers/storage'; import * as tasks from './providers/tasks'; -export { alerts, https, pubsub, storage, logger, params, tasks, eventarc }; +export { + alerts, + https, + identity, + pubsub, + storage, + logger, + params, + tasks, + eventarc, +}; export { setGlobalOptions, GlobalOptions } from './options'; diff --git a/src/v2/providers/identity.ts b/src/v2/providers/identity.ts new file mode 100644 index 000000000..798a39212 --- /dev/null +++ b/src/v2/providers/identity.ts @@ -0,0 +1,215 @@ +import { BlockingFunction } from '../../cloud-functions'; +import { + AuthBlockingEvent, + AuthBlockingEventType, + BeforeCreateResponse, + BeforeSignInResponse, + HttpsError, + wrapHandler, +} from '../../common/providers/identity'; +import * as options from '../options'; + +export { HttpsError }; + +/** Internally used when parsing the options. */ +interface InternalOptions { + opts: options.GlobalOptions; + idToken: boolean; + accessToken: boolean; + refreshToken: boolean; +} + +/** + * All function options plus idToken, accessToken, and refreshToken. + */ +export interface BlockingOptions extends options.GlobalOptions { + idToken?: boolean; + accessToken?: boolean; + refreshToken?: boolean; +} + +/** + * Handle an event that is triggered before a user is created. + */ +export function beforeUserCreated( + handler: ( + event: AuthBlockingEvent + ) => + | BeforeCreateResponse + | Promise + | void + | Promise +): BlockingFunction; +export function beforeUserCreated( + opts: BlockingOptions, + handler: ( + event: AuthBlockingEvent + ) => + | BeforeCreateResponse + | Promise + | void + | Promise +): BlockingFunction; +export function beforeUserCreated( + optsOrHandler: + | BlockingOptions + | (( + event: AuthBlockingEvent + ) => + | BeforeCreateResponse + | Promise + | void + | Promise), + handler?: ( + event: AuthBlockingEvent + ) => + | BeforeCreateResponse + | Promise + | void + | Promise +): BlockingFunction { + return beforeOperation('beforeCreate', optsOrHandler, handler); +} + +/** + * Handle an event that is triggered before a user is signed in. + */ +export function beforeUserSignedIn( + handler: ( + event: AuthBlockingEvent + ) => + | BeforeSignInResponse + | Promise + | void + | Promise +): BlockingFunction; +export function beforeUserSignedIn( + opts: BlockingOptions, + handler: ( + event: AuthBlockingEvent + ) => + | BeforeSignInResponse + | Promise + | void + | Promise +): BlockingFunction; +export function beforeUserSignedIn( + optsOrHandler: + | BlockingOptions + | (( + event: AuthBlockingEvent + ) => + | BeforeSignInResponse + | Promise + | void + | Promise), + handler?: ( + event: AuthBlockingEvent + ) => + | BeforeSignInResponse + | Promise + | void + | Promise +): BlockingFunction { + return beforeOperation('beforeSignIn', optsOrHandler, handler); +} + +/** @internal */ +export function beforeOperation( + eventType: AuthBlockingEventType, + optsOrHandler: + | BlockingOptions + | (( + event: AuthBlockingEvent + ) => + | BeforeCreateResponse + | BeforeSignInResponse + | void + | Promise + | Promise + | Promise), + handler: ( + event: AuthBlockingEvent + ) => + | BeforeCreateResponse + | BeforeSignInResponse + | void + | Promise + | Promise + | Promise +): BlockingFunction { + if (!handler || typeof optsOrHandler === 'function') { + handler = optsOrHandler as ( + event: AuthBlockingEvent + ) => + | BeforeCreateResponse + | BeforeSignInResponse + | void + | Promise + | Promise + | Promise; + optsOrHandler = {}; + } + + const { opts, accessToken, idToken, refreshToken } = getOpts( + optsOrHandler as BlockingOptions + ); + + // Create our own function that just calls the provided function so we know for sure that + // handler takes one argument. This is something common/providers/identity depends on. + const wrappedHandler = (event: AuthBlockingEvent) => handler(event); + const func: any = wrapHandler(eventType, wrappedHandler); + + const legacyEventType = `providers/cloud.auth/eventTypes/user.${eventType}`; + + /** Endpoint */ + const baseOptsEndpoint = options.optionsToEndpoint( + options.getGlobalOptions() + ); + const specificOptsEndpoint = options.optionsToEndpoint(opts); + func.__endpoint = { + platform: 'gcfv2', + ...baseOptsEndpoint, + ...specificOptsEndpoint, + labels: { + ...baseOptsEndpoint?.labels, + ...specificOptsEndpoint?.labels, + }, + blockingTrigger: { + eventType: legacyEventType, + options: { + accessToken, + idToken, + refreshToken, + }, + }, + }; + + func.__requiredAPIs = [ + { + api: 'identitytoolkit.googleapis.com', + reason: 'Needed for auth blocking functions', + }, + ]; + + func.run = handler; + + return func; +} + +/** @internal */ +export function getOpts(blockingOptions: BlockingOptions): InternalOptions { + const accessToken = blockingOptions.accessToken || false; + const idToken = blockingOptions.idToken || false; + const refreshToken = blockingOptions.refreshToken || false; + const opts = { ...blockingOptions }; + delete (opts as any).accessToken; + delete (opts as any).idToken; + delete (opts as any).refreshToken; + return { + opts, + accessToken, + idToken, + refreshToken, + }; +} diff --git a/tsconfig.release.json b/tsconfig.release.json index 5f19fe3eb..b98955ee5 100644 --- a/tsconfig.release.json +++ b/tsconfig.release.json @@ -11,6 +11,7 @@ "typeRoots": ["./node_modules/@types"] }, "files": [ + "./src/types/global.d.ts", "./src/index.ts", "./src/logger/index.ts", "./src/logger/compat.ts", diff --git a/v2/identity.js b/v2/identity.js new file mode 100644 index 000000000..ac9a09d74 --- /dev/null +++ b/v2/identity.js @@ -0,0 +1,21 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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. From 0406897773ac445e252e98020d56fef6b5ac9a69 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Fri, 29 Apr 2022 13:47:31 -0400 Subject: [PATCH 070/370] add changelog (#1086) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..6008e08a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +Adds auth blocking triggers to the auth and identity namespaces (1080). From c91fd9b001518a40fcb5bfffaee0e30d4735ac1a Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 29 Apr 2022 13:39:20 -0700 Subject: [PATCH 071/370] Add support for secrets in v2 (#1079) In addition to adding secrets to v2 `__endpoint` annotation, I'm also making following (small) changes: * Enforce that `platform` be part of trigger annotation - Firebase CLI already assumes that it's included. * Endpoint secret environment variable annotation (for v1 & v2) --- CHANGELOG.md | 3 ++- spec/v1/cloud-functions.spec.ts | 4 ++-- spec/v2/providers/fixtures.ts | 9 +++++++-- src/cloud-functions.ts | 2 +- src/v2/core.ts | 3 ++- src/v2/options.ts | 20 +++++++++++++++++--- src/v2/providers/https.ts | 21 ++++++++++----------- 7 files changed, 41 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6008e08a6..ddc0e009c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ -Adds auth blocking triggers to the auth and identity namespaces (1080). +- Adds auth blocking triggers to the auth and identity namespaces (1080). +- Add support for secrets for v2 triggers (#1079). diff --git a/spec/v1/cloud-functions.spec.ts b/spec/v1/cloud-functions.spec.ts index 5dd0a8941..fec1bd580 100644 --- a/spec/v1/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'; describe('makeCloudFunction', () => { const cloudFunctionArgs: MakeCloudFunctionArgs = { @@ -124,7 +124,7 @@ describe('makeCloudFunction', () => { }, retry: false, }, - secretEnvironmentVariables: [{ secret: 'MY_SECRET', key: 'MY_SECRET' }], + secretEnvironmentVariables: [{ key: 'MY_SECRET' }], labels: {}, }); }); diff --git a/spec/v2/providers/fixtures.ts b/spec/v2/providers/fixtures.ts index 53aa852b3..7a054572e 100644 --- a/spec/v2/providers/fixtures.ts +++ b/spec/v2/providers/fixtures.ts @@ -1,3 +1,5 @@ +import { ManifestEndpoint } from '../../../src/runtime/manifest'; +import { TriggerAnnotation } from '../../../src/v2/core'; import * as options from '../../../src/v2/options'; export const FULL_OPTIONS: options.GlobalOptions = { @@ -15,9 +17,10 @@ export const FULL_OPTIONS: options.GlobalOptions = { labels: { hello: 'world', }, + secrets: ['MY_SECRET'], }; -export const FULL_TRIGGER = { +export const FULL_TRIGGER: TriggerAnnotation = { platform: 'gcfv2', regions: ['us-west1'], availableMemoryMb: 512, @@ -32,9 +35,10 @@ export const FULL_TRIGGER = { labels: { hello: 'world', }, + secrets: ['MY_SECRET'], }; -export const FULL_ENDPOINT = { +export const FULL_ENDPOINT: ManifestEndpoint = { platform: 'gcfv2', region: ['us-west1'], availableMemoryMb: 512, @@ -52,4 +56,5 @@ export const FULL_ENDPOINT = { labels: { hello: 'world', }, + secretEnvironmentVariables: [{ key: 'MY_SECRET' }], }; diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 5941b1db3..1f59f50d8 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -636,7 +636,7 @@ export function optionsToEndpoint( options, 'secretEnvironmentVariables', 'secrets', - (secrets) => secrets.map((secret) => ({ secret, key: secret })) + (secrets) => secrets.map((secret) => ({ key: secret })) ); if (options?.vpcConnector) { endpoint.vpc = { connector: options.vpcConnector }; diff --git a/src/v2/core.ts b/src/v2/core.ts index af02c1d0d..f5561d66f 100644 --- a/src/v2/core.ts +++ b/src/v2/core.ts @@ -24,6 +24,7 @@ import { ManifestEndpoint } from '../runtime/manifest'; /** @internal */ export interface TriggerAnnotation { + platform?: string; concurrency?: number; minInstances?: number; maxInstances?: number; @@ -44,11 +45,11 @@ export interface TriggerAnnotation { vpcConnectorEgressSettings?: string; serviceAccountEmail?: string; ingressSettings?: string; + secrets?: string[]; blockingTrigger?: { eventType: string; options?: Record; }; - // TODO: schedule } diff --git a/src/v2/options.ts b/src/v2/options.ts index bfbc3195d..42404bfa0 100644 --- a/src/v2/options.ts +++ b/src/v2/options.ts @@ -31,6 +31,7 @@ import { ManifestEndpoint } from '../runtime/manifest'; import { TriggerAnnotation } from './core'; import { declaredParams } from './params'; import { ParamSpec } from './params/types'; +import { HttpsOptions } from './providers/https'; /** * List of all regions supported by Cloud Functions v2 @@ -215,6 +216,11 @@ export interface GlobalOptions { * Invoker to set access control on https functions. */ invoker?: 'public' | 'private' | string | string[]; + + /* + * Secrets to bind to a functions. + */ + secrets?: string[]; } let globalOptions: GlobalOptions | undefined; @@ -251,7 +257,7 @@ export interface EventHandlerOptions extends GlobalOptions { * @internal */ export function optionsToTriggerAnnotations( - opts: GlobalOptions | EventHandlerOptions + opts: GlobalOptions | EventHandlerOptions | HttpsOptions ): TriggerAnnotation { const annotation: TriggerAnnotation = {}; copyIfPresent( @@ -263,7 +269,8 @@ export function optionsToTriggerAnnotations( 'ingressSettings', 'labels', 'vpcConnector', - 'vpcConnectorEgressSettings' + 'vpcConnectorEgressSettings', + 'secrets' ); convertIfPresent( annotation, @@ -312,7 +319,7 @@ export function optionsToTriggerAnnotations( * @internal */ export function optionsToEndpoint( - opts: GlobalOptions | EventHandlerOptions + opts: GlobalOptions | EventHandlerOptions | HttpsOptions ): ManifestEndpoint { const endpoint: ManifestEndpoint = {}; copyIfPresent( @@ -350,6 +357,13 @@ export function optionsToEndpoint( } return region; }); + convertIfPresent( + endpoint, + opts, + 'secretEnvironmentVariables', + 'secrets', + (secrets) => secrets.map((secret) => ({ key: secret })) + ); return endpoint; } diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index f6101ba1b..3cd095e94 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -33,14 +33,16 @@ import { } from '../../common/providers/https'; import { ManifestEndpoint } from '../../runtime/manifest'; import * as options from '../options'; +import { GlobalOptions, SupportedRegion } from '../options'; export { Request, CallableRequest, FunctionsErrorCode, HttpsError }; -export interface HttpsOptions extends Omit { - region?: - | options.SupportedRegion - | string - | Array; +/** + * Options that can be set on an individual HTTPS Cloud Function. + */ +export interface HttpsOptions extends Omit { + /* HTTP functions can override and specify more than one regions. */ + region?: SupportedRegion | string | Array; cors?: string | boolean | RegExp | Array; } @@ -54,7 +56,6 @@ export type HttpsFunction = (( export interface CallableFunction extends HttpsFunction { run(data: CallableRequest): Return; } - export function onRequest( opts: HttpsOptions, handler: ( @@ -195,9 +196,7 @@ export function onCall>( ); // global options calls region a scalar and https allows it to be an array, // but optionsToTriggerAnnotations handles both cases. - const specificOpts = options.optionsToTriggerAnnotations( - opts as options.GlobalOptions - ); + const specificOpts = options.optionsToTriggerAnnotations(opts); return { platform: 'gcfv2', ...baseOpts, @@ -216,8 +215,8 @@ export function onCall>( const baseOpts = options.optionsToEndpoint(options.getGlobalOptions()); // global options calls region a scalar and https allows it to be an array, - // but optionsToManifestEndpoint handles both cases. - const specificOpts = options.optionsToEndpoint(opts as options.GlobalOptions); + // but optionsToEndpoint handles both cases. + const specificOpts = options.optionsToEndpoint(opts); func.__endpoint = { platform: 'gcfv2', ...baseOpts, From c4f63c719f200dca723771922727b477b2e08eb2 Mon Sep 17 00:00:00 2001 From: Tyler Stark Date: Fri, 29 Apr 2022 16:23:47 -0500 Subject: [PATCH 072/370] Update types for AlertPayloads (#1087) * Update types for AlertPayloads This commit updates the typing for the AlertPayloads. There was a discrepency between the type definitions and what prod was returning. * From: `com.google.firebase.firebasealerts.*` * To: `type.googleapis.com/google.events.firebase.firebasealerts.v1.*` Co-authored-by: Daniel Lee --- src/v2/providers/alerts/billing.ts | 4 ++-- src/v2/providers/alerts/crashlytics.ts | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/v2/providers/alerts/billing.ts b/src/v2/providers/alerts/billing.ts index a4b7a151e..8ba9b06c9 100644 --- a/src/v2/providers/alerts/billing.ts +++ b/src/v2/providers/alerts/billing.ts @@ -7,7 +7,7 @@ import * as options from '../../options'; * Payload is wrapped inside a FirebaseAlertData object. */ export interface PlanUpdatePayload { - ['@type']: 'com.google.firebase.firebasealerts.PlanUpdatePayload'; + ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.BillingPlanUpdatePayload'; billingPlan: string; principalEmail: string; } @@ -17,7 +17,7 @@ export interface PlanUpdatePayload { * Payload is wrapped inside a FirebaseAlertData object. */ export interface PlanAutomatedUpdatePayload { - ['@type']: 'com.google.firebase.firebasealerts.PlanAutomatedUpdatePayload'; + ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.BillingPlanAutomatedUpdatePayload'; billingPlan: string; } diff --git a/src/v2/providers/alerts/crashlytics.ts b/src/v2/providers/alerts/crashlytics.ts index 6a5f36f73..4e2b4b121 100644 --- a/src/v2/providers/alerts/crashlytics.ts +++ b/src/v2/providers/alerts/crashlytics.ts @@ -15,7 +15,7 @@ interface Issue { * Payload is wrapped inside a FirebaseAlertData object. */ export interface NewFatalIssuePayload { - ['@type']: 'com.google.firebase.firebasealerts.CrashlyticsNewFatalIssuePayload'; + ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsNewFatalIssuePayload'; issue: Issue; } @@ -24,7 +24,7 @@ export interface NewFatalIssuePayload { * Payload is wrapped inside a FirebaseAlertData object. */ export interface NewNonfatalIssuePayload { - ['@type']: 'com.google.firebase.firebasealerts.CrashlyticsNewNonfatalIssuePayload'; + ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsNewNonfatalIssuePayload'; issue: Issue; } @@ -33,7 +33,7 @@ export interface NewNonfatalIssuePayload { * Payload is wrapped inside a FirebaseAlertData object. */ export interface RegressionAlertPayload { - ['@type']: 'com.google.firebase.firebasealerts.CrashlyticsRegressionAlertPayload'; + ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsRegressionAlertPayload'; type: string; issue: Issue; resolveTime: string; @@ -52,7 +52,7 @@ interface TrendingIssueDetails { * Payload is wrapped inside a FirebaseAlertData object. */ export interface StabilityDigestPayload { - ['@type']: 'com.google.firebase.firebasealerts.CrashlyticsStabilityDigestPayload'; + ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsStabilityDigestPayload'; digestDate: string; trendingIssues: TrendingIssueDetails[]; } @@ -62,7 +62,7 @@ export interface StabilityDigestPayload { * Payload is wrapped inside a FirebaseAlertData object. */ export interface VelocityAlertPayload { - ['@type']: 'com.google.firebase.firebasealerts.VelocityAlertPayload'; + ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsVelocityAlertPayload'; issue: Issue; createTime: string; crashCount: number; @@ -75,7 +75,7 @@ export interface VelocityAlertPayload { * Payload is wrapped inside a FirebaseAlertData object. */ export interface NewAnrIssuePayload { - ['@type']: 'com.google.firebase.firebasealerts.NewAnrIssuePayload'; + ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsNewAnrIssuePayload'; issue: Issue; } From 0bac53d7e1edbcb9c23ffc9f129245524433cc36 Mon Sep 17 00:00:00 2001 From: Tyler Stark Date: Fri, 29 Apr 2022 20:06:56 -0500 Subject: [PATCH 073/370] Update AppDistribution [@type] (#1088) This is a short followup to update the App Distribution @type. [More info here](https://github.com/firebase/firebase-functions/commit/c4f63c719f200dca723771922727b477b2e08eb2) ``` type.googleapis.com/google.events.firebase.firebasealerts.v1.AppDistroNewTesterIosDevicePayload ``` --- src/v2/providers/alerts/appDistribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index e70e1796a..ce5512dd4 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -7,7 +7,7 @@ import { FirebaseAlertData, getEndpointAnnotation } from './alerts'; * Payload is wrapped inside a FirebaseAlertData object. */ export interface NewTesterDevicePayload { - ['@type']: 'com.google.firebase.firebasealerts.NewTesterDevicePayload'; + ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.AppDistroNewTesterIosDevicePayload'; testerName: string; testerEmail: string; testerDeviceModelName: string; From 1aa1865dc64d3aeab849fe7aed916f429d1231d1 Mon Sep 17 00:00:00 2001 From: Tyler Stark Date: Mon, 2 May 2022 11:53:58 -0500 Subject: [PATCH 074/370] Update CloudEvent types (#1089) * Updates to exported CloudEvent Types. This commit makes several updates to the exported CloudEvent types: * Billing types include a notificationType * [Link](https://github.com/googleapis/google-cloudevents/blob/main/proto/google/events/firebase/firebasealerts/v1/cloud_event_payload.proto#L132-L133) * Crashlytics have more documentation on fields * Storage has an exported `StorageEvent` which includes the bucket. * Storage function api references `StorageEvent` to better match to expected output. * Fixed NPE issue for Storage without Config * Updated more signatures to optionally expect a bucket. --- src/v2/providers/alerts/alerts.ts | 8 +++ src/v2/providers/alerts/appDistribution.ts | 2 + src/v2/providers/alerts/billing.ts | 8 +++ src/v2/providers/alerts/crashlytics.ts | 34 +++++++++++++ src/v2/providers/storage.ts | 57 ++++++++++++---------- 5 files changed, 84 insertions(+), 25 deletions(-) diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index 173267422..44edd4e55 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -6,13 +6,21 @@ import * as options from '../../options'; * The CloudEvent data emitted by Firebase Alerts. */ export interface FirebaseAlertData { + /** Time that the event has created. */ createTime: string; + /** Time that the event has ended. Optional, only present for ongoing alerts. */ endTime: string; + /** Payload of the event, which includes the details of the specific alert. */ payload: T; } interface WithAlertTypeAndApp { + /** The type of the alerts that got triggered. */ alertType: string; + /** + * The Firebase App ID that’s associated with the alert. This is optional, + * and only present when the alert is targeting at a specific Firebase App. + */ appId?: string; } /** diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index ce5512dd4..94bd81ab8 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -15,7 +15,9 @@ export interface NewTesterDevicePayload { } interface WithAlertTypeAndApp { + /** The type of the alerts that got triggered. */ alertType: string; + /** The Firebase App ID that’s associated with the alert. */ appId: string; } /** diff --git a/src/v2/providers/alerts/billing.ts b/src/v2/providers/alerts/billing.ts index 8ba9b06c9..b9aaffc09 100644 --- a/src/v2/providers/alerts/billing.ts +++ b/src/v2/providers/alerts/billing.ts @@ -8,8 +8,12 @@ import * as options from '../../options'; */ export interface PlanUpdatePayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.BillingPlanUpdatePayload'; + /** A Firebase billing plan. */ billingPlan: string; + /** The email address of the person that triggered billing plan change */ principalEmail: string; + /** The type of the notification, e.g. upgrade, downgrade */ + notificationType: string; } /** @@ -18,10 +22,14 @@ export interface PlanUpdatePayload { */ export interface PlanAutomatedUpdatePayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.BillingPlanAutomatedUpdatePayload'; + /** A Firebase billing plan. */ billingPlan: string; + /** The type of the notification, e.g. upgrade, downgrade */ + notificationType: string; } interface WithAlertType { + /** The type of the alerts that got triggered. */ alertType: string; } /** diff --git a/src/v2/providers/alerts/crashlytics.ts b/src/v2/providers/alerts/crashlytics.ts index 4e2b4b121..14e8a87e8 100644 --- a/src/v2/providers/alerts/crashlytics.ts +++ b/src/v2/providers/alerts/crashlytics.ts @@ -16,6 +16,7 @@ interface Issue { */ export interface NewFatalIssuePayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsNewFatalIssuePayload'; + /** Basic information of the Crashlytics issue */ issue: Issue; } @@ -25,6 +26,7 @@ export interface NewFatalIssuePayload { */ export interface NewNonfatalIssuePayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsNewNonfatalIssuePayload'; + /** Basic information of the Crashlytics issue */ issue: Issue; } @@ -34,16 +36,26 @@ export interface NewNonfatalIssuePayload { */ export interface RegressionAlertPayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsRegressionAlertPayload'; + /** The type of the Crashlytics issue, e.g. new fatal, new nonfatal, ANR */ type: string; + /** Basic information of the Crashlytics issue */ issue: Issue; + /** + * The time that the Crashlytics issues was most recently resolved before it + * began to reoccur. + */ resolveTime: string; } /** Generic crashlytics trending issue interface */ interface TrendingIssueDetails { + /** The type of the Crashlytics issue, e.g. new fatal, new nonfatal, ANR */ type: string; + /** Basic information of the Crashlytics issue */ issue: Issue; + /** The number of crashes that occurred with the issue */ eventCount: number; + /** The number of distinct users that were affected by the issue */ userCount: number; } @@ -53,7 +65,12 @@ interface TrendingIssueDetails { */ export interface StabilityDigestPayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsStabilityDigestPayload'; + /** + * The date that the digest gets created. Issues in the digest should have the + * same date as the digest date + */ digestDate: string; + /** A stability digest containing several trending Crashlytics issues */ trendingIssues: TrendingIssueDetails[]; } @@ -63,10 +80,24 @@ export interface StabilityDigestPayload { */ export interface VelocityAlertPayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsVelocityAlertPayload'; + /** Basic information of the Crashlytics issue */ issue: Issue; + /** The time that the Crashlytics issue gets created */ createTime: string; + /** + * The number of user sessions for the given app version that had this + * specific crash issue in the time period used to trigger the velocity alert. + */ crashCount: number; + /** + * The percentage of user sessions for the given app version that had this + * specific crash issue in the time period used to trigger the velocity alert. + */ crashPercentage: number; + /** + * The first app version where this issue was seen, and not necessarily the + * version that has triggered the alert. + */ firstVersion: string; } @@ -76,11 +107,14 @@ export interface VelocityAlertPayload { */ export interface NewAnrIssuePayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsNewAnrIssuePayload'; + /** Basic information of the Crashlytics issue */ issue: Issue; } interface WithAlertTypeAndApp { + /** The type of the alerts that got triggered. */ alertType: string; + /** The Firebase App ID that’s associated with the alert. */ appId: string; } /** diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts index f4ce5b004..6c815615e 100644 --- a/src/v2/providers/storage.ts +++ b/src/v2/providers/storage.ts @@ -174,6 +174,13 @@ export interface CustomerEncryption { keySha256?: string; } +interface WithBucket { + /** The name of the bucket containing this object. */ + bucket: string; +} + +export type StorageEvent = CloudEvent; + /** @internal */ export const archivedEvent = 'google.cloud.storage.object.v1.archived'; /** @internal */ @@ -191,100 +198,100 @@ export interface StorageOptions extends options.EventHandlerOptions { /** Handle a storage object archived */ export function onObjectArchived( - handler: (event: CloudEvent) => any | Promise + handler: (event: StorageEvent) => any | Promise ): CloudFunction; export function onObjectArchived( bucket: string, - handler: (event: CloudEvent) => any | Promise + handler: (event: StorageEvent) => any | Promise ): CloudFunction; export function onObjectArchived( opts: StorageOptions, - handler: (event: CloudEvent) => any | Promise + handler: (event: StorageEvent) => any | Promise ): CloudFunction; export function onObjectArchived( buketOrOptsOrHandler: | string | StorageOptions - | ((event: CloudEvent) => any | Promise), - handler?: (event: CloudEvent) => any | Promise + | ((event: StorageEvent) => any | Promise), + handler?: (event: StorageEvent) => any | Promise ): CloudFunction { return onOperation(archivedEvent, buketOrOptsOrHandler, handler); } /** Handle a storage object finalized */ export function onObjectFinalized( - handler: (event: CloudEvent) => any | Promise + handler: (event: StorageEvent) => any | Promise ): CloudFunction; export function onObjectFinalized( bucket: string, - handler: (event: CloudEvent) => any | Promise + handler: (event: StorageEvent) => any | Promise ): CloudFunction; export function onObjectFinalized( opts: StorageOptions, - handler: (event: CloudEvent) => any | Promise + handler: (event: StorageEvent) => any | Promise ): CloudFunction; export function onObjectFinalized( buketOrOptsOrHandler: | string | StorageOptions - | ((event: CloudEvent) => any | Promise), - handler?: (event: CloudEvent) => any | Promise + | ((event: StorageEvent) => any | Promise), + handler?: (event: StorageEvent) => any | Promise ): CloudFunction { return onOperation(finalizedEvent, buketOrOptsOrHandler, handler); } /** Handle a storage object deleted */ export function onObjectDeleted( - handler: (event: CloudEvent) => any | Promise + handler: (event: StorageEvent) => any | Promise ): CloudFunction; export function onObjectDeleted( bucket: string, - handler: (event: CloudEvent) => any | Promise + handler: (event: StorageEvent) => any | Promise ): CloudFunction; export function onObjectDeleted( opts: StorageOptions, - handler: (event: CloudEvent) => any | Promise + handler: (event: StorageEvent) => any | Promise ): CloudFunction; export function onObjectDeleted( buketOrOptsOrHandler: | string | StorageOptions - | ((event: CloudEvent) => any | Promise), - handler?: (event: CloudEvent) => any | Promise + | ((event: StorageEvent) => any | Promise), + handler?: (event: StorageEvent) => any | Promise ): CloudFunction { return onOperation(deletedEvent, buketOrOptsOrHandler, handler); } /** Handle a storage object metadata updated */ export function onObjectMetadataUpdated( - handler: (event: CloudEvent) => any | Promise + handler: (event: StorageEvent) => any | Promise ): CloudFunction; export function onObjectMetadataUpdated( bucket: string, - handler: (event: CloudEvent) => any | Promise + handler: (event: StorageEvent) => any | Promise ): CloudFunction; export function onObjectMetadataUpdated( opts: StorageOptions, - handler: (event: CloudEvent) => any | Promise + handler: (event: StorageEvent) => any | Promise ): CloudFunction; export function onObjectMetadataUpdated( buketOrOptsOrHandler: | string | StorageOptions - | ((event: CloudEvent) => any | Promise), - handler?: (event: CloudEvent) => any | Promise + | ((event: StorageEvent) => any | Promise), + handler?: (event: StorageEvent) => any | Promise ): CloudFunction { return onOperation(metadataUpdatedEvent, buketOrOptsOrHandler, handler); } @@ -295,12 +302,12 @@ export function onOperation( bucketOrOptsOrHandler: | string | StorageOptions - | ((event: CloudEvent) => any | Promise), - handler: (event: CloudEvent) => any | Promise + | ((event: StorageEvent) => any | Promise), + handler: (event: StorageEvent) => any | Promise ): CloudFunction { if (typeof bucketOrOptsOrHandler === 'function') { handler = bucketOrOptsOrHandler as ( - event: CloudEvent + event: StorageEvent ) => any | Promise; bucketOrOptsOrHandler = {}; } @@ -310,7 +317,7 @@ export function onOperation( ); const func = (raw: CloudEvent) => { - return handler(raw as CloudEvent); + return handler(raw as StorageEvent); }; func.run = handler; @@ -382,7 +389,7 @@ export function getOptsAndBucket( bucket = bucketOrOpts; opts = {}; } else { - bucket = bucketOrOpts.bucket || firebaseConfig().storageBucket; + bucket = bucketOrOpts.bucket || firebaseConfig()?.storageBucket; opts = { ...bucketOrOpts }; delete (opts as any).bucket; } From af511569ff494892a0f7e4d4781e5c5eb9d54381 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Mon, 2 May 2022 23:26:36 -0700 Subject: [PATCH 075/370] Generate documentation with api-extractor (#1071) This picks up where https://github.com/firebase/firebase-functions/pull/984 had left off. This gets us started with generating docs using api-extractor. By no means it's complete - I wanted us to generate the doc and see the output as we incrementally improve the reference docs. Separately, I'm going to be pushing out PRs to suppress many warnings generated by api-extractor (e.g. mostly on missing documentation/annotation). To generate reference doc for v2, run the following command: ``` $ npm i $ npm run build $ npm run docgen:v2 ``` Documents should be generated in `docgen/v2/markdown` folder. --- .gitignore | 4 + docgen/api-extractor.base.json | 364 ++++++++++++++++++++++ docgen/api-extractor.v1.json | 14 + docgen/api-extractor.v2.json | 14 + docgen/tsconfig.json | 3 - package-lock.json | 540 ++++++++++++++++++++++++++++++++- package.json | 9 + 7 files changed, 941 insertions(+), 7 deletions(-) create mode 100644 docgen/api-extractor.base.json create mode 100644 docgen/api-extractor.v1.json create mode 100644 docgen/api-extractor.v2.json delete mode 100644 docgen/tsconfig.json diff --git a/.gitignore b/.gitignore index b5a890408..ee22dbd83 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,10 @@ .vscode/ coverage docgen/html +docgen/*/temp +docgen/*/markdown +docgen/*/*.json +docgen/*/*.md firebase-functions-*.tgz integration_test/.firebaserc integration_test/*.log diff --git a/docgen/api-extractor.base.json b/docgen/api-extractor.base.json new file mode 100644 index 000000000..2938a396f --- /dev/null +++ b/docgen/api-extractor.base.json @@ -0,0 +1,364 @@ +/** + * Config file for API Extractor. For more info, please visit: https://api-extractor.com + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + /** + * Optionally specifies another JSON config file that this file extends from. This provides a way for + * standard settings to be shared across multiple projects. + * + * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains + * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be + * resolved using NodeJS require(). + * + * SUPPORTED TOKENS: none + * DEFAULT VALUE: "" + */ + // "extends": "./shared/api-extractor-base.json" + // "extends": "my-package/include/api-extractor-base.json" + + /** + * Determines the "" token that can be used with other config file settings. The project folder + * typically contains the tsconfig.json and package.json config files, but the path is user-defined. + * + * The path is resolved relative to the folder of the config file that contains the setting. + * + * The default value for "projectFolder" is the token "", which means the folder is determined by traversing + * parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder + * that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error + * will be reported. + * + * SUPPORTED TOKENS: + * DEFAULT VALUE: "" + */ + "projectFolder": "..", + + /** + * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor + * analyzes the symbols exported by this module. + * + * The file extension must be ".d.ts" and not ".ts". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + */ + "mainEntryPointFilePath": "/lib/index.d.ts", + + /** + * A list of NPM package names whose exports should be treated as part of this package. + * + * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1", + * and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part + * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly + * imports library2. To avoid this, we can specify: + * + * "bundledPackages": [ "library2" ], + * + * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been + * local files for library1. + */ + "bundledPackages": [], + + /** + * Determines how the TypeScript compiler engine will be invoked by API Extractor. + */ + "compiler": { + /** + * Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * Note: This setting will be ignored if "overrideTsconfig" is used. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/tsconfig.json" + */ + "tsconfigFilePath": "/tsconfig.release.json" + /** + * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk. + * The object must conform to the TypeScript tsconfig schema: + * + * http://json.schemastore.org/tsconfig + * + * If omitted, then the tsconfig.json file will be read from the "projectFolder". + * + * DEFAULT VALUE: no overrideTsconfig section + */ + // "overrideTsconfig": { + // . . . + // } + /** + * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended + * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when + * dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses + * for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck. + * + * DEFAULT VALUE: false + */ + // "skipLibCheck": true, + }, + + /** + * Configures how the API report file (*.api.md) will be generated. + */ + "apiReport": { + /** + * (REQUIRED) Whether to generate an API report. + */ + "enabled": true, + + /** + * The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce + * a full file path. + * + * The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/". + * + * SUPPORTED TOKENS: , + * DEFAULT VALUE: ".api.md" + */ + // "reportFileName": ".api.md", + + /** + * Specifies the folder where the API report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy, + * e.g. for an API review. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/etc/" + */ + "reportFolder": "/docgen/etc/", + + /** + * Specifies the folder where the temporary report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * After the temporary file is written to disk, it is compared with the file in the "reportFolder". + * If they are different, a production build will fail. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/" + */ + "reportTempFolder": "/docgen/temp/" + }, + + /** + * Configures how the doc model file (*.api.json) will be generated. + */ + "docModel": { + /** + * (REQUIRED) Whether to generate a doc model file. + */ + "enabled": true, + + /** + * The output path for the doc model file. The file extension should be ".api.json". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/.api.json" + */ + "apiJsonFilePath": "/docgen/.api.json" + }, + + /** + * Configures how the .d.ts rollup file will be generated. + */ + "dtsRollup": { + /** + * (REQUIRED) Whether to generate the .d.ts rollup file. + */ + "enabled": true + + /** + * Specifies the output path for a .d.ts rollup file to be generated without any trimming. + * This file will include all declarations that are exported by the main entry point. + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/dist/.d.ts" + */ + // "untrimmedFilePath": "/dist/.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release. + * This file will include only declarations that are marked as "@public" or "@beta". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "betaTrimmedFilePath": "/dist/-beta.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release. + * This file will include only declarations that are marked as "@public". + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "publicTrimmedFilePath": "/dist/-public.d.ts", + + /** + * When a declaration is trimmed, by default it will be replaced by a code comment such as + * "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the + * declaration completely. + * + * DEFAULT VALUE: false + */ + // "omitTrimmingComments": true + }, + + /** + * Configures how the tsdoc-metadata.json file will be generated. + */ + "tsdocMetadata": { + /** + * Whether to generate the tsdoc-metadata.json file. + * + * DEFAULT VALUE: true + */ + // "enabled": true, + /** + * Specifies where the TSDoc metadata file should be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * The default value is "", which causes the path to be automatically inferred from the "tsdocMetadata", + * "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup + * falls back to "tsdoc-metadata.json" in the package folder. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json" + }, + + /** + * Specifies what type of newlines API Extractor should use when writing output files. By default, the output files + * will be written with Windows-style newlines. To use POSIX-style newlines, specify "lf" instead. + * To use the OS's default newline kind, specify "os". + * + * DEFAULT VALUE: "crlf" + */ + // "newlineKind": "crlf", + + /** + * Configures how API Extractor reports error and warning messages produced during analysis. + * + * There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages. + */ + "messages": { + /** + * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing + * the input .d.ts files. + * + * TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "compilerMessageReporting": { + /** + * Configures the default routing for messages that don't match an explicit rule in this table. + */ + "default": { + /** + * Specifies whether the message should be written to the the tool's output log. Note that + * the "addToApiReportFile" property may supersede this option. + * + * Possible values: "error", "warning", "none" + * + * Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail + * and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes + * the "--local" option), the warning is displayed but the build will not fail. + * + * DEFAULT VALUE: "warning" + */ + "logLevel": "warning" + + /** + * When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md), + * then the message will be written inside that file; otherwise, the message is instead logged according to + * the "logLevel" option. + * + * DEFAULT VALUE: false + */ + // "addToApiReportFile": false + } + + // "TS2551": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + /** + * Configures handling of messages reported by API Extractor during its analysis. + * + * API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag" + * + * DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings + */ + "extractorMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + } + + // "ae-extra-release-tag": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + /** + * Configures handling of messages reported by the TSDoc parser when analyzing code comments. + * + * TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "tsdocMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + } + + // "tsdoc-link-tag-unescaped-text": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + } + } +} diff --git a/docgen/api-extractor.v1.json b/docgen/api-extractor.v1.json new file mode 100644 index 000000000..f03058f13 --- /dev/null +++ b/docgen/api-extractor.v1.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "./api-extractor.base.json", + "mainEntryPointFilePath": "/lib/index.d.ts", + "docModel": { + "enabled": true, + "apiJsonFilePath": "/docgen/v1/firebase-functions.api.json" + }, + "apiReport": { + "enabled": true, + "reportTempFolder": "/docgen/v1/temp", + "reportFolder": "/docgen/v1" + } +} diff --git a/docgen/api-extractor.v2.json b/docgen/api-extractor.v2.json new file mode 100644 index 000000000..2cf0596b7 --- /dev/null +++ b/docgen/api-extractor.v2.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "./api-extractor.base.json", + "mainEntryPointFilePath": "/lib/v2/index.d.ts", + "docModel": { + "enabled": true, + "apiJsonFilePath": "/docgen/v2/firebase-functions.api.json" + }, + "apiReport": { + "enabled": true, + "reportTempFolder": "/docgen/v2/temp", + "reportFolder": "/docgen/v2" + } +} diff --git a/docgen/tsconfig.json b/docgen/tsconfig.json deleted file mode 100644 index 3c43903cf..000000000 --- a/docgen/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../tsconfig.json" -} diff --git a/package-lock.json b/package-lock.json index 4e1d204b0..5b5f7fc85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,6 +45,48 @@ "@cspotcode/source-map-consumer": "0.8.0" } }, + "@firebase/api-documenter": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@firebase/api-documenter/-/api-documenter-0.1.2.tgz", + "integrity": "sha512-aDofRZebqbMzrbo5WAi9f21qUTzhIub7yOszirik3AwujqOzcUr1F7lIFrI41686JD1Zw56lLL/B5EWZTwvVjA==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.12.24", + "@rushstack/node-core-library": "3.36.0", + "@rushstack/ts-command-line": "4.7.8", + "api-extractor-model-me": "0.1.1", + "colors": "~1.2.1", + "js-yaml": "4.0.0", + "resolve": "~1.17.0", + "tslib": "^2.1.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "js-yaml": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "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" + } + } + } + }, "@firebase/app-types": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.7.0.tgz", @@ -256,6 +298,296 @@ } } }, + "@microsoft/api-documenter": { + "version": "7.17.11", + "resolved": "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.17.11.tgz", + "integrity": "sha512-VdCu4eG5RISGEiG/hFO3jaN3gEgMZVXZRFpch/PQzrLO3Vh2QhZTz2LsZ2SYyb7UwBZCK9Z76ULv5cGoflylNA==", + "dev": true, + "requires": { + "@microsoft/api-extractor-model": "7.17.2", + "@microsoft/tsdoc": "0.14.1", + "@rushstack/node-core-library": "3.45.4", + "@rushstack/ts-command-line": "4.10.10", + "colors": "~1.2.1", + "js-yaml": "~3.13.1", + "resolve": "~1.17.0" + }, + "dependencies": { + "@microsoft/tsdoc": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz", + "integrity": "sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw==", + "dev": true + }, + "@rushstack/node-core-library": { + "version": "3.45.4", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.4.tgz", + "integrity": "sha512-FMoEQWjK7nWAO2uFgV1eVpVhY9ZDGOdIIomi9zTej64cKJ+8/Nvu+ny0xKaUDEjw/ALftN2D2ml7L0RDpW/Z9g==", + "dev": true, + "requires": { + "@types/node": "12.20.24", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "timsort": "~0.3.0", + "z-schema": "~5.0.2" + } + }, + "@rushstack/ts-command-line": { + "version": "4.10.10", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.10.tgz", + "integrity": "sha512-F+MH7InPDXqX40qvvcEsnvPpmg566SBpfFqj2fcCh8RjM6AyOoWlXc8zx7giBD3ZN85NVAEjZAgrcLU0z+R2yg==", + "dev": true, + "requires": { + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "colors": "~1.2.1", + "string-argv": "~0.3.1" + } + }, + "@types/node": { + "version": "12.20.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.24.tgz", + "integrity": "sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ==", + "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" + } + }, + "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" + } + }, + "validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "dev": true + }, + "z-schema": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.3.tgz", + "integrity": "sha512-sGvEcBOTNum68x9jCpCVGPFJ6mWnkD0YxOcddDlJHRx3tKdB2q8pCHExMVZo/AV/6geuVJXG7hljDaWG8+5GDw==", + "dev": true, + "requires": { + "commander": "^2.20.3", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + } + } + } + }, + "@microsoft/api-extractor": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.23.0.tgz", + "integrity": "sha512-fbdX05RVE1EMA7nvyRHuS9nx1pryhjgURDx6pQlE/9yOXQ5PO7MpYdfWGaRsQwyYuU3+tPxgro819c0R3AK6KA==", + "dev": true, + "requires": { + "@microsoft/api-extractor-model": "7.17.2", + "@microsoft/tsdoc": "0.14.1", + "@microsoft/tsdoc-config": "~0.16.1", + "@rushstack/node-core-library": "3.45.4", + "@rushstack/rig-package": "0.3.11", + "@rushstack/ts-command-line": "4.10.10", + "colors": "~1.2.1", + "lodash": "~4.17.15", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "source-map": "~0.6.1", + "typescript": "~4.6.3" + }, + "dependencies": { + "@microsoft/tsdoc": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz", + "integrity": "sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw==", + "dev": true + }, + "@rushstack/node-core-library": { + "version": "3.45.4", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.4.tgz", + "integrity": "sha512-FMoEQWjK7nWAO2uFgV1eVpVhY9ZDGOdIIomi9zTej64cKJ+8/Nvu+ny0xKaUDEjw/ALftN2D2ml7L0RDpW/Z9g==", + "dev": true, + "requires": { + "@types/node": "12.20.24", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "timsort": "~0.3.0", + "z-schema": "~5.0.2" + } + }, + "@rushstack/ts-command-line": { + "version": "4.10.10", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.10.tgz", + "integrity": "sha512-F+MH7InPDXqX40qvvcEsnvPpmg566SBpfFqj2fcCh8RjM6AyOoWlXc8zx7giBD3ZN85NVAEjZAgrcLU0z+R2yg==", + "dev": true, + "requires": { + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "colors": "~1.2.1", + "string-argv": "~0.3.1" + } + }, + "@types/node": { + "version": "12.20.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.24.tgz", + "integrity": "sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ==", + "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" + } + }, + "validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "dev": true + }, + "z-schema": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.3.tgz", + "integrity": "sha512-sGvEcBOTNum68x9jCpCVGPFJ6mWnkD0YxOcddDlJHRx3tKdB2q8pCHExMVZo/AV/6geuVJXG7hljDaWG8+5GDw==", + "dev": true, + "requires": { + "commander": "^2.20.3", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + } + } + } + }, + "@microsoft/api-extractor-model": { + "version": "7.17.2", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.17.2.tgz", + "integrity": "sha512-fYfCeBeLm7jnZligC64qHiH4/vzswFLDfyPpX+uKO36OI2kIeMHrYG0zaezmuinKvE4vg1dAz38zZeDbPvBKGg==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.14.1", + "@microsoft/tsdoc-config": "~0.16.1", + "@rushstack/node-core-library": "3.45.4" + }, + "dependencies": { + "@microsoft/tsdoc": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz", + "integrity": "sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw==", + "dev": true + }, + "@rushstack/node-core-library": { + "version": "3.45.4", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.4.tgz", + "integrity": "sha512-FMoEQWjK7nWAO2uFgV1eVpVhY9ZDGOdIIomi9zTej64cKJ+8/Nvu+ny0xKaUDEjw/ALftN2D2ml7L0RDpW/Z9g==", + "dev": true, + "requires": { + "@types/node": "12.20.24", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "timsort": "~0.3.0", + "z-schema": "~5.0.2" + } + }, + "@types/node": { + "version": "12.20.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.24.tgz", + "integrity": "sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ==", + "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" + } + }, + "validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "dev": true + }, + "z-schema": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.3.tgz", + "integrity": "sha512-sGvEcBOTNum68x9jCpCVGPFJ6mWnkD0YxOcddDlJHRx3tKdB2q8pCHExMVZo/AV/6geuVJXG7hljDaWG8+5GDw==", + "dev": true, + "requires": { + "commander": "^2.20.3", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + } + } + } + }, + "@microsoft/tsdoc": { + "version": "0.12.24", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.24.tgz", + "integrity": "sha512-Mfmij13RUTmHEMi9vRUhMXD7rnGR2VvxeNYtaGtaJ4redwwjT4UXYJ+nzmVJF7hhd4pn/Fx5sncDKxMVFJSWPg==", + "dev": true + }, + "@microsoft/tsdoc-config": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.1.tgz", + "integrity": "sha512-2RqkwiD4uN6MLnHFljqBlZIXlt/SaUT6cuogU1w2ARw4nKuuppSmR0+s+NC+7kXBQykd9zzu0P4HtBpZT5zBpQ==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.14.1", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + }, + "dependencies": { + "@microsoft/tsdoc": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz", + "integrity": "sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw==", + "dev": true + }, + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + } + } + }, "@panva/asn1.js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", @@ -336,6 +668,79 @@ "dev": true, "optional": true }, + "@rushstack/node-core-library": { + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.36.0.tgz", + "integrity": "sha512-bID2vzXpg8zweXdXgQkKToEdZwVrVCN9vE9viTRk58gqzYaTlz4fMId6V3ZfpXN6H0d319uGi2KDlm+lUEeqCg==", + "dev": true, + "requires": { + "@types/node": "10.17.13", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "timsort": "~0.3.0", + "z-schema": "~3.18.3" + }, + "dependencies": { + "@types/node": { + "version": "10.17.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz", + "integrity": "sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==", + "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" + } + } + } + }, + "@rushstack/rig-package": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.11.tgz", + "integrity": "sha512-uI1/g5oQPtyrT9nStoyX/xgZSLa2b+srRFaDk3r1eqC7zA5th4/bvTGl2QfV3C9NcP+coSqmk5mFJkUfH6i3Lw==", + "dev": true, + "requires": { + "resolve": "~1.17.0", + "strip-json-comments": "~3.1.1" + }, + "dependencies": { + "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" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + } + } + }, + "@rushstack/ts-command-line": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.7.8.tgz", + "integrity": "sha512-8ghIWhkph7NnLCMDJtthpsb7TMOsVGXVDvmxjE/CeklTqjbbUFBjGXizJfpbEkRQTELuZQ2+vGn7sGwIWKN2uA==", + "dev": true, + "requires": { + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "colors": "~1.2.1", + "string-argv": "~0.3.1" + } + }, "@sinonjs/commons": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", @@ -403,6 +808,12 @@ "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "dev": true }, + "@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "dev": true + }, "@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -679,6 +1090,18 @@ } } }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "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", @@ -706,6 +1129,16 @@ "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", "dev": true }, + "api-extractor-model-me": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/api-extractor-model-me/-/api-extractor-model-me-0.1.1.tgz", + "integrity": "sha512-Ez801ZMADfkseOWNRFquvyQYDm3D9McpxfkKMWL6JFCGcpub0miJ+TFNphIR1nSZbrsxz3kIeOovNMY4VlL6Bw==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.12.24", + "@rushstack/node-core-library": "3.36.0" + } + }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -1001,6 +1434,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "colors": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", + "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", + "dev": true + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1531,8 +1970,7 @@ "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, - "optional": true + "dev": true }, "fast-diff": { "version": "1.2.0", @@ -1540,6 +1978,12 @@ "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", @@ -1640,6 +2084,17 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1815,8 +2270,7 @@ "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true, - "optional": true + "dev": true }, "growl": { "version": "1.10.5", @@ -2019,6 +2473,12 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -2235,6 +2695,12 @@ "integrity": "sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw==", "dev": true }, + "jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=", + "dev": true + }, "jose": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.5.tgz", @@ -2365,6 +2831,12 @@ "bignumber.js": "^9.0.0" } }, + "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", @@ -2377,6 +2849,15 @@ "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", "dev": true }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, "jsonwebtoken": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", @@ -2550,6 +3031,12 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -2562,6 +3049,12 @@ "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=", "dev": true }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", + "dev": true + }, "lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -3737,6 +4230,12 @@ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", "dev": true }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -3853,6 +4352,12 @@ "thenify": ">= 3.1.0 < 4" } }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", + "dev": true + }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -4143,6 +4648,15 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "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", @@ -4168,6 +4682,12 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, + "validator": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-8.2.0.tgz", + "integrity": "sha512-Yw5wW34fSv5spzTXNkokD6S6/Oq92d8q/t14TqsS3fAiA1RYnxSFSIZ+CY3n6PGGRCq5HhJTSepQvFUS2QUDxA==", + "dev": true + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -4652,6 +5172,18 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "optional": true + }, + "z-schema": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.18.4.tgz", + "integrity": "sha512-DUOKC/IhbkdLKKiV89gw9DUauTV8U/8yJl1sjf6MtDmzevLKOF2duNJ495S3MFVjqZarr+qNGCPbkg4mu4PpLw==", + "dev": true, + "requires": { + "commander": "^2.7.1", + "lodash.get": "^4.0.0", + "lodash.isequal": "^4.0.0", + "validator": "^8.0.0" + } } } } diff --git a/package.json b/package.json index f8cb58477..20f8c6ce0 100644 --- a/package.json +++ b/package.json @@ -157,6 +157,12 @@ }, "scripts": { "apidocs": "node docgen/generate-docs.js", + "docgen:v1:extract": "api-extractor run -c docgen/api-extractor.v1.json --local", + "docgen:v1:gen": "api-documenter-fire markdown -i docgen/v1 -o docgen/v1/markdown && api-documenter-fire toc -i docgen/v1 -o docgen/v1/markdown/toc -p /docs/reference/functions", + "docgen:v1": "npm run docgen:v1:extract && npm run docgen:v1:gen", + "docgen:v2:extract": "api-extractor run -c docgen/api-extractor.v2.json --local", + "docgen:v2:gen": "api-documenter-fire markdown -i docgen/v2 -o docgen/v2/markdown && api-documenter-fire toc -i docgen/v2 -o docgen/v2/markdown/toc -p /docs/reference/functions/v2", + "docgen:v2": "npm run docgen:v2:extract && npm run docgen:v2:gen", "build:pack": "rm -rf lib && npm install && tsc -p tsconfig.release.json && npm pack", "build:release": "npm ci --production && npm install --no-save typescript firebase-admin && tsc -p tsconfig.release.json", "build": "tsc -p tsconfig.release.json", @@ -177,6 +183,9 @@ "node-fetch": "^2.6.7" }, "devDependencies": { + "@firebase/api-documenter": "^0.1.2", + "@microsoft/api-documenter": "^7.13.45", + "@microsoft/api-extractor": "^7.18.7", "@types/chai": "^4.1.7", "@types/chai-as-promised": "^7.1.0", "@types/jsonwebtoken": "^8.3.2", From ea634a8cee8b55b0a873e9a1aa2eda776556524f Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Mon, 2 May 2022 23:33:13 -0700 Subject: [PATCH 076/370] Change type info to be inheritance friendly. (#1091) Unwind the cleverness of CloudEvent. Types now match the approved format of the Python API. CloudFunction now templatizes the CloudEvent rather than the EventType so that we can keep typing to include subtypes of events both in code complete and in firebase-functions-test. --- src/v2/core.ts | 24 ++------- src/v2/providers/alerts/alerts.ts | 21 ++++---- src/v2/providers/alerts/appDistribution.ts | 21 ++++---- src/v2/providers/alerts/billing.ts | 23 ++++---- src/v2/providers/alerts/crashlytics.ts | 62 ++++++++++------------ src/v2/providers/pubsub.ts | 6 +-- src/v2/providers/storage.ts | 38 +++++++------ 7 files changed, 82 insertions(+), 113 deletions(-) diff --git a/src/v2/core.ts b/src/v2/core.ts index f5561d66f..54d61ff1c 100644 --- a/src/v2/core.ts +++ b/src/v2/core.ts @@ -57,7 +57,7 @@ export interface TriggerAnnotation { * A CloudEventBase is the base of a cross-platform format for encoding a serverless event. * More information can be found in https://github.com/cloudevents/spec */ -interface CloudEventBase { +export interface CloudEvent { /** Version of the CloudEvents spec for this event. */ readonly specversion: '1.0'; @@ -78,32 +78,14 @@ interface CloudEventBase { /** Information about this specific event. */ data: T; - - /** - * A map of template parameter name to value for subject strings. - * - * This map is only available on some event types that allow templates - * in the subject string, such as Firestore. When listening to a document - * template "/users/{uid}", an event with subject "/documents/users/1234" - * would have a params of {"uid": "1234"}. - * - * Params are generated inside the firebase-functions SDK and are not - * part of the CloudEvents spec nor the payload that a Cloud Function - * actually receives. - */ - params?: Record; } -/** - * A CloudEvent with custom extension attributes - */ -export type CloudEvent = CloudEventBase & Ext; /** A handler for CloudEvents. */ -export interface CloudFunction { +export interface CloudFunction> { (raw: CloudEvent): any | Promise; __trigger?: unknown; __endpoint: ManifestEndpoint; - run(event: CloudEvent): any | Promise; + run(event: EventType): any | Promise; } diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index 44edd4e55..df800fadb 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -14,7 +14,10 @@ export interface FirebaseAlertData { payload: T; } -interface WithAlertTypeAndApp { +/** + * A custom CloudEvent for Firebase Alerts (with custom extension attributes). + */ +export interface AlertEvent extends CloudEvent> { /** The type of the alerts that got triggered. */ alertType: string; /** @@ -22,14 +25,10 @@ interface WithAlertTypeAndApp { * and only present when the alert is targeting at a specific Firebase App. */ appId?: string; + + /** Data for an AlertEvent is a FirebaseAlertData with a given payload. */ + data: FirebaseAlertData; } -/** - * A custom CloudEvent for Firebase Alerts (with custom extension attributes). - */ -export type AlertEvent = CloudEvent< - FirebaseAlertData, - WithAlertTypeAndApp ->; /** @internal */ export const eventType = 'google.firebase.firebasealerts.alerts.v1.published'; @@ -63,13 +62,11 @@ export interface FirebaseAlertOptions extends options.EventHandlerOptions { export function onAlertPublished( alertTypeOrOpts: AlertType | FirebaseAlertOptions, handler: (event: AlertEvent) => any | Promise -): CloudFunction> { +): CloudFunction> { const [opts, alertType, appId] = getOptsAndAlertTypeAndApp(alertTypeOrOpts); const func = (raw: CloudEvent) => { - return handler( - raw as CloudEvent, WithAlertTypeAndApp> - ); + return handler(raw as AlertEvent); }; func.run = handler; diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index 94bd81ab8..014b1d937 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -14,19 +14,16 @@ export interface NewTesterDevicePayload { testerDeviceIdentifier: string; } -interface WithAlertTypeAndApp { +/** + * A custom CloudEvent for Firebase Alerts (with custom extension attributes). + */ +export interface AppDistributionEvent + extends CloudEvent> { /** The type of the alerts that got triggered. */ alertType: string; /** The Firebase App ID that’s associated with the alert. */ appId: string; } -/** - * A custom CloudEvent for Firebase Alerts (with custom extension attributes). - */ -export type AppDistributionEvent = CloudEvent< - FirebaseAlertData, - WithAlertTypeAndApp ->; /** @internal */ export const newTesterIosDeviceAlert = 'appDistribution.newTesterIosDevice'; @@ -45,19 +42,19 @@ export function onNewTesterIosDevicePublished( handler: ( event: AppDistributionEvent ) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onNewTesterIosDevicePublished( appId: string, handler: ( event: AppDistributionEvent ) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onNewTesterIosDevicePublished( opts: AppDistributionOptions, handler: ( event: AppDistributionEvent ) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onNewTesterIosDevicePublished( appIdOrOptsOrHandler: | string @@ -68,7 +65,7 @@ export function onNewTesterIosDevicePublished( handler?: ( event: AppDistributionEvent ) => any | Promise -): CloudFunction> { +): CloudFunction> { if (typeof appIdOrOptsOrHandler === 'function') { handler = appIdOrOptsOrHandler as ( event: AppDistributionEvent diff --git a/src/v2/providers/alerts/billing.ts b/src/v2/providers/alerts/billing.ts index b9aaffc09..4439546a5 100644 --- a/src/v2/providers/alerts/billing.ts +++ b/src/v2/providers/alerts/billing.ts @@ -28,14 +28,13 @@ export interface PlanAutomatedUpdatePayload { notificationType: string; } -interface WithAlertType { - /** The type of the alerts that got triggered. */ - alertType: string; -} /** * A custom CloudEvent for billing Firebase Alerts (with custom extension attributes). */ -export type BillingEvent = CloudEvent, WithAlertType>; +export interface BillingEvent extends CloudEvent> { + /** The type of the alerts that got triggered. */ + alertType: string; +} /** @internal */ export const planUpdateAlert = 'billing.planUpdate'; @@ -47,17 +46,17 @@ export const planAutomatedUpdateAlert = 'billing.planAutomatedUpdate'; */ export function onPlanUpdatePublished( handler: (event: BillingEvent) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onPlanUpdatePublished( opts: options.EventHandlerOptions, handler: (event: BillingEvent) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onPlanUpdatePublished( optsOrHandler: | options.EventHandlerOptions | ((event: BillingEvent) => any | Promise), handler?: (event: BillingEvent) => any | Promise -): CloudFunction> { +): CloudFunction> { return onOperation( planUpdateAlert, optsOrHandler, @@ -72,13 +71,13 @@ export function onPlanAutomatedUpdatePublished( handler: ( event: BillingEvent ) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onPlanAutomatedUpdatePublished( opts: options.EventHandlerOptions, handler: ( event: BillingEvent ) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onPlanAutomatedUpdatePublished( optsOrHandler: | options.EventHandlerOptions @@ -86,7 +85,7 @@ export function onPlanAutomatedUpdatePublished( handler?: ( event: BillingEvent ) => any | Promise -): CloudFunction> { +): CloudFunction> { return onOperation( planAutomatedUpdateAlert, optsOrHandler, @@ -101,7 +100,7 @@ export function onOperation( | options.EventHandlerOptions | ((event: BillingEvent) => any | Promise), handler: (event: BillingEvent) => any | Promise -): CloudFunction> { +): CloudFunction> { if (typeof optsOrHandler === 'function') { handler = optsOrHandler as (event: BillingEvent) => any | Promise; optsOrHandler = {}; diff --git a/src/v2/providers/alerts/crashlytics.ts b/src/v2/providers/alerts/crashlytics.ts index 14e8a87e8..c9c208e57 100644 --- a/src/v2/providers/alerts/crashlytics.ts +++ b/src/v2/providers/alerts/crashlytics.ts @@ -111,19 +111,15 @@ export interface NewAnrIssuePayload { issue: Issue; } -interface WithAlertTypeAndApp { +/** + * A custom CloudEvent for Firebase Alerts (with custom extension attributes). + */ +export interface CrashlyticsEvent extends CloudEvent> { /** The type of the alerts that got triggered. */ alertType: string; /** The Firebase App ID that’s associated with the alert. */ appId: string; } -/** - * A custom CloudEvent for Firebase Alerts (with custom extension attributes). - */ -export type CrashlyticsEvent = CloudEvent< - FirebaseAlertData, - WithAlertTypeAndApp ->; /** @internal */ export const newFatalIssueAlert = 'crashlytics.newFatalIssue'; @@ -150,15 +146,15 @@ export interface CrashlyticsOptions extends options.EventHandlerOptions { */ export function onNewFatalIssuePublished( handler: (event: CrashlyticsEvent) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onNewFatalIssuePublished( appId: string, handler: (event: CrashlyticsEvent) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onNewFatalIssuePublished( opts: CrashlyticsOptions, handler: (event: CrashlyticsEvent) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onNewFatalIssuePublished( appIdOrOptsOrHandler: | string @@ -167,7 +163,7 @@ export function onNewFatalIssuePublished( handler?: ( event: CrashlyticsEvent ) => any | Promise -): CloudFunction> { +): CloudFunction> { return onOperation( newFatalIssueAlert, appIdOrOptsOrHandler, @@ -182,19 +178,19 @@ export function onNewNonfatalIssuePublished( handler: ( event: CrashlyticsEvent ) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onNewNonfatalIssuePublished( appId: string, handler: ( event: CrashlyticsEvent ) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onNewNonfatalIssuePublished( opts: CrashlyticsOptions, handler: ( event: CrashlyticsEvent ) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onNewNonfatalIssuePublished( appIdOrOptsOrHandler: | string @@ -205,7 +201,7 @@ export function onNewNonfatalIssuePublished( handler?: ( event: CrashlyticsEvent ) => any | Promise -): CloudFunction> { +): CloudFunction> { return onOperation( newNonfatalIssueAlert, appIdOrOptsOrHandler, @@ -220,19 +216,19 @@ export function onRegressionAlertPublished( handler: ( event: CrashlyticsEvent ) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onRegressionAlertPublished( appId: string, handler: ( event: CrashlyticsEvent ) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onRegressionAlertPublished( opts: CrashlyticsOptions, handler: ( event: CrashlyticsEvent ) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onRegressionAlertPublished( appIdOrOptsOrHandler: | string @@ -241,7 +237,7 @@ export function onRegressionAlertPublished( handler?: ( event: CrashlyticsEvent ) => any | Promise -): CloudFunction> { +): CloudFunction> { return onOperation( regressionAlert, appIdOrOptsOrHandler, @@ -256,19 +252,19 @@ export function onStabilityDigestPublished( handler: ( event: CrashlyticsEvent ) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onStabilityDigestPublished( appId: string, handler: ( event: CrashlyticsEvent ) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onStabilityDigestPublished( opts: CrashlyticsOptions, handler: ( event: CrashlyticsEvent ) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onStabilityDigestPublished( appIdOrOptsOrHandler: | string @@ -277,7 +273,7 @@ export function onStabilityDigestPublished( handler?: ( event: CrashlyticsEvent ) => any | Promise -): CloudFunction> { +): CloudFunction> { return onOperation( stabilityDigestAlert, appIdOrOptsOrHandler, @@ -290,15 +286,15 @@ export function onStabilityDigestPublished( */ export function onVelocityAlertPublished( handler: (event: CrashlyticsEvent) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onVelocityAlertPublished( appId: string, handler: (event: CrashlyticsEvent) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onVelocityAlertPublished( opts: CrashlyticsOptions, handler: (event: CrashlyticsEvent) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onVelocityAlertPublished( appIdOrOptsOrHandler: | string @@ -307,7 +303,7 @@ export function onVelocityAlertPublished( handler?: ( event: CrashlyticsEvent ) => any | Promise -): CloudFunction> { +): CloudFunction> { return onOperation( velocityAlert, appIdOrOptsOrHandler, @@ -320,22 +316,22 @@ export function onVelocityAlertPublished( */ export function onNewAnrIssuePublished( handler: (event: CrashlyticsEvent) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onNewAnrIssuePublished( appId: string, handler: (event: CrashlyticsEvent) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onNewAnrIssuePublished( opts: CrashlyticsOptions, handler: (event: CrashlyticsEvent) => any | Promise -): CloudFunction>; +): CloudFunction>; export function onNewAnrIssuePublished( appIdOrOptsOrHandler: | string | CrashlyticsOptions | ((event: CrashlyticsEvent) => any | Promise), handler?: (event: CrashlyticsEvent) => any | Promise -): CloudFunction> { +): CloudFunction> { return onOperation( newAnrIssueAlert, appIdOrOptsOrHandler, @@ -351,7 +347,7 @@ export function onOperation( | CrashlyticsOptions | ((event: CrashlyticsEvent) => any | Promise), handler: (event: CrashlyticsEvent) => any | Promise -): CloudFunction> { +): CloudFunction> { if (typeof appIdOrOptsOrHandler === 'function') { handler = appIdOrOptsOrHandler as ( event: CrashlyticsEvent diff --git a/src/v2/providers/pubsub.ts b/src/v2/providers/pubsub.ts index 9f06fb083..62a5ba4a1 100644 --- a/src/v2/providers/pubsub.ts +++ b/src/v2/providers/pubsub.ts @@ -101,18 +101,18 @@ export interface PubSubOptions extends options.EventHandlerOptions { export function onMessagePublished( topic: string, handler: (event: CloudEvent>) => any | Promise -): CloudFunction>; +): CloudFunction>>; /** Handle a message being published to a Pub/Sub topic. */ export function onMessagePublished( options: PubSubOptions, handler: (event: CloudEvent>) => any | Promise -): CloudFunction>; +): CloudFunction>>; export function onMessagePublished( topicOrOptions: string | PubSubOptions, handler: (event: CloudEvent>) => any | Promise -): CloudFunction> { +): CloudFunction>> { let topic: string; let opts: options.EventHandlerOptions; if (typeof topicOrOptions === 'string') { diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts index 6c815615e..7c5fe01b1 100644 --- a/src/v2/providers/storage.ts +++ b/src/v2/providers/storage.ts @@ -174,13 +174,11 @@ export interface CustomerEncryption { keySha256?: string; } -interface WithBucket { +export interface StorageEvent extends CloudEvent { /** The name of the bucket containing this object. */ bucket: string; } -export type StorageEvent = CloudEvent; - /** @internal */ export const archivedEvent = 'google.cloud.storage.object.v1.archived'; /** @internal */ @@ -199,17 +197,17 @@ export interface StorageOptions extends options.EventHandlerOptions { /** Handle a storage object archived */ export function onObjectArchived( handler: (event: StorageEvent) => any | Promise -): CloudFunction; +): CloudFunction; export function onObjectArchived( bucket: string, handler: (event: StorageEvent) => any | Promise -): CloudFunction; +): CloudFunction; export function onObjectArchived( opts: StorageOptions, handler: (event: StorageEvent) => any | Promise -): CloudFunction; +): CloudFunction; export function onObjectArchived( buketOrOptsOrHandler: @@ -217,24 +215,24 @@ export function onObjectArchived( | StorageOptions | ((event: StorageEvent) => any | Promise), handler?: (event: StorageEvent) => any | Promise -): CloudFunction { +): CloudFunction { return onOperation(archivedEvent, buketOrOptsOrHandler, handler); } /** Handle a storage object finalized */ export function onObjectFinalized( handler: (event: StorageEvent) => any | Promise -): CloudFunction; +): CloudFunction; export function onObjectFinalized( bucket: string, handler: (event: StorageEvent) => any | Promise -): CloudFunction; +): CloudFunction; export function onObjectFinalized( opts: StorageOptions, handler: (event: StorageEvent) => any | Promise -): CloudFunction; +): CloudFunction; export function onObjectFinalized( buketOrOptsOrHandler: @@ -242,24 +240,24 @@ export function onObjectFinalized( | StorageOptions | ((event: StorageEvent) => any | Promise), handler?: (event: StorageEvent) => any | Promise -): CloudFunction { +): CloudFunction { return onOperation(finalizedEvent, buketOrOptsOrHandler, handler); } /** Handle a storage object deleted */ export function onObjectDeleted( handler: (event: StorageEvent) => any | Promise -): CloudFunction; +): CloudFunction; export function onObjectDeleted( bucket: string, handler: (event: StorageEvent) => any | Promise -): CloudFunction; +): CloudFunction; export function onObjectDeleted( opts: StorageOptions, handler: (event: StorageEvent) => any | Promise -): CloudFunction; +): CloudFunction; export function onObjectDeleted( buketOrOptsOrHandler: @@ -267,24 +265,24 @@ export function onObjectDeleted( | StorageOptions | ((event: StorageEvent) => any | Promise), handler?: (event: StorageEvent) => any | Promise -): CloudFunction { +): CloudFunction { return onOperation(deletedEvent, buketOrOptsOrHandler, handler); } /** Handle a storage object metadata updated */ export function onObjectMetadataUpdated( handler: (event: StorageEvent) => any | Promise -): CloudFunction; +): CloudFunction; export function onObjectMetadataUpdated( bucket: string, handler: (event: StorageEvent) => any | Promise -): CloudFunction; +): CloudFunction; export function onObjectMetadataUpdated( opts: StorageOptions, handler: (event: StorageEvent) => any | Promise -): CloudFunction; +): CloudFunction; export function onObjectMetadataUpdated( buketOrOptsOrHandler: @@ -292,7 +290,7 @@ export function onObjectMetadataUpdated( | StorageOptions | ((event: StorageEvent) => any | Promise), handler?: (event: StorageEvent) => any | Promise -): CloudFunction { +): CloudFunction { return onOperation(metadataUpdatedEvent, buketOrOptsOrHandler, handler); } @@ -304,7 +302,7 @@ export function onOperation( | StorageOptions | ((event: StorageEvent) => any | Promise), handler: (event: StorageEvent) => any | Promise -): CloudFunction { +): CloudFunction { if (typeof bucketOrOptsOrHandler === 'function') { handler = bucketOrOptsOrHandler as ( event: StorageEvent From 27bc77faa7efaca01777ac3e7ee571e4886bb37b Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Tue, 3 May 2022 13:32:26 -0700 Subject: [PATCH 077/370] MB -> MiB; GB -> GiB (#1090) Change memory options to use the new V2 standard suffix that makes it clear we use base 2 units instead of base 10. --- spec/v2/providers/fixtures.ts | 2 +- src/v2/options.ts | 47 ++++++++++++++--------------------- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/spec/v2/providers/fixtures.ts b/spec/v2/providers/fixtures.ts index 7a054572e..f330efed1 100644 --- a/spec/v2/providers/fixtures.ts +++ b/spec/v2/providers/fixtures.ts @@ -4,7 +4,7 @@ import * as options from '../../../src/v2/options'; export const FULL_OPTIONS: options.GlobalOptions = { region: 'us-west1', - memory: '512MB', + memory: '512MiB', timeoutSeconds: 60, minInstances: 1, maxInstances: 3, diff --git a/src/v2/options.ts b/src/v2/options.ts index 42404bfa0..c4e3cd7de 100644 --- a/src/v2/options.ts +++ b/src/v2/options.ts @@ -75,27 +75,27 @@ export const MAX_CONCURRENCY = 1_000; * List of available memory options supported by Cloud Functions. */ export const SUPPORTED_MEMORY_OPTIONS = [ - '128MB', - '256MB', - '512MB', - '1GB', - '2GB', - '4GB', - '8GB', - '16GB', - '32GB', + '128MiB', + '256MiB', + '512MiB', + '1GiB', + '2GiB', + '4GiB', + '8GiB', + '16GiB', + '32GiB', ] as const; const MemoryOptionToMB: Record = { - '128MB': 128, - '256MB': 256, - '512MB': 512, - '1GB': 1024, - '2GB': 2048, - '4GB': 4096, - '8GB': 8192, - '16GB': 16384, - '32GB': 32768, + '128MiB': 128, + '256MiB': 256, + '512MiB': 512, + '1GiB': 1024, + '2GiB': 2048, + '4GiB': 4096, + '8GiB': 8192, + '16GiB': 16384, + '32GiB': 32768, }; /** @@ -340,16 +340,7 @@ export function optionsToEndpoint( endpoint.vpc = vpc; } convertIfPresent(endpoint, opts, 'availableMemoryMb', 'memory', (mem) => { - const memoryLookup = { - '128MB': 128, - '256MB': 256, - '512MB': 512, - '1GB': 1024, - '2GB': 2048, - '4GB': 4096, - '8GB': 8192, - }; - return memoryLookup[mem]; + return MemoryOptionToMB[mem]; }); convertIfPresent(endpoint, opts, 'region', 'region', (region) => { if (typeof region === 'string') { From e463f85c677d69acb083b9d0295b9e1b3c84e060 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 4 May 2022 11:40:34 -0700 Subject: [PATCH 078/370] Change typing info to fix compiler error (#1093) --- integration_test/functions/src/index.ts | 2 +- integration_test/run_tests.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integration_test/functions/src/index.ts b/integration_test/functions/src/index.ts index 57654c8d1..e8c6918a5 100644 --- a/integration_test/functions/src/index.ts +++ b/integration_test/functions/src/index.ts @@ -115,7 +115,7 @@ async function updateRemoteConfig( } } -function v1Tests(testId: string, accessToken: string): Array> { +function v1Tests(testId: string, accessToken: string): Array> { return [ // A database write to trigger the Firebase Realtime Database tests. admin diff --git a/integration_test/run_tests.sh b/integration_test/run_tests.sh index 6ae86f25f..814ddc492 100755 --- a/integration_test/run_tests.sh +++ b/integration_test/run_tests.sh @@ -114,7 +114,7 @@ function cleanup { build_sdk delete_all_functions -for version in 12 14 16; do +for version in 14 16; do create_package_json $TIMESTAMP $version "^10.0.0" install_deps announce "Re-deploying the same functions to Node $version runtime ..." From e147a02653f8643d2171f16ad102df642c5cd44b Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 5 May 2022 17:21:24 +0900 Subject: [PATCH 079/370] Remove all errors when generating reference doc using api-extractor (#1095) As is, generating reference doc for v2 sdk produces tons of errors. Most common errors are: 1) Exported type definition depending on unexported type definition 2) Using `@hidden` tag which is unsupported in api-extractor I solve both of these issues here. I selected to use `@alpha` tag for params related types - that would prevent it from showing up in reference docs. --- docgen/api-extractor.v2.json | 20 ++ package-lock.json | 394 +++++++++++++++++++----------- package.json | 3 +- src/cloud-functions.ts | 31 ++- src/common/providers/https.ts | 32 +-- src/logger/index.ts | 14 +- src/providers/pubsub.ts | 24 +- src/v2/options.ts | 13 +- src/v2/params/index.ts | 40 +-- src/v2/params/types.ts | 10 +- src/v2/providers/alerts/alerts.ts | 4 +- src/v2/providers/pubsub.ts | 6 +- 12 files changed, 370 insertions(+), 221 deletions(-) diff --git a/docgen/api-extractor.v2.json b/docgen/api-extractor.v2.json index 2cf0596b7..492c8bae6 100644 --- a/docgen/api-extractor.v2.json +++ b/docgen/api-extractor.v2.json @@ -10,5 +10,25 @@ "enabled": true, "reportTempFolder": "/docgen/v2/temp", "reportFolder": "/docgen/v2" + }, + "messages": { + "compilerMessageReporting": { + "default": { + "logLevel": "warning" + } + }, + "extractorMessageReporting": { + "default": { + "logLevel": "warning" + }, + "ae-missing-release-tag": { + "logLevel": "none" + } + }, + "tsdocMessageReporting": { + "default": { + "logLevel": "error" + } + } } } diff --git a/package-lock.json b/package-lock.json index 5b5f7fc85..2bd402d37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,12 +61,122 @@ "tslib": "^2.1.0" }, "dependencies": { + "@microsoft/tsdoc": { + "version": "0.12.24", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.24.tgz", + "integrity": "sha512-Mfmij13RUTmHEMi9vRUhMXD7rnGR2VvxeNYtaGtaJ4redwwjT4UXYJ+nzmVJF7hhd4pn/Fx5sncDKxMVFJSWPg==", + "dev": true + }, + "@rushstack/node-core-library": { + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.36.0.tgz", + "integrity": "sha512-bID2vzXpg8zweXdXgQkKToEdZwVrVCN9vE9viTRk58gqzYaTlz4fMId6V3ZfpXN6H0d319uGi2KDlm+lUEeqCg==", + "dev": true, + "requires": { + "@types/node": "10.17.13", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "timsort": "~0.3.0", + "z-schema": "~3.18.3" + } + }, + "@rushstack/ts-command-line": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.7.8.tgz", + "integrity": "sha512-8ghIWhkph7NnLCMDJtthpsb7TMOsVGXVDvmxjE/CeklTqjbbUFBjGXizJfpbEkRQTELuZQ2+vGn7sGwIWKN2uA==", + "dev": true, + "requires": { + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "colors": "~1.2.1", + "string-argv": "~0.3.1" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + } + } + }, + "@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "dev": true + }, + "@types/node": { + "version": "10.17.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz", + "integrity": "sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==", + "dev": true + }, + "api-extractor-model-me": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/api-extractor-model-me/-/api-extractor-model-me-0.1.1.tgz", + "integrity": "sha512-Ez801ZMADfkseOWNRFquvyQYDm3D9McpxfkKMWL6JFCGcpub0miJ+TFNphIR1nSZbrsxz3kIeOovNMY4VlL6Bw==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.12.24", + "@rushstack/node-core-library": "3.36.0" + } + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "colors": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", + "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "optional": true + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true + }, + "jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=", + "dev": true + }, "js-yaml": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", @@ -76,6 +186,42 @@ "argparse": "^2.0.1" } }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, "resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", @@ -84,6 +230,69 @@ "requires": { "path-parse": "^1.0.6" } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", + "dev": true + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "validator": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-8.2.0.tgz", + "integrity": "sha512-Yw5wW34fSv5spzTXNkokD6S6/Oq92d8q/t14TqsS3fAiA1RYnxSFSIZ+CY3n6PGGRCq5HhJTSepQvFUS2QUDxA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "z-schema": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.18.4.tgz", + "integrity": "sha512-DUOKC/IhbkdLKKiV89gw9DUauTV8U/8yJl1sjf6MtDmzevLKOF2duNJ495S3MFVjqZarr+qNGCPbkg4mu4PpLw==", + "dev": true, + "requires": { + "commander": "^2.7.1", + "lodash.get": "^4.0.0", + "lodash.isequal": "^4.0.0", + "validator": "^8.0.0" + } } } }, @@ -552,12 +761,6 @@ } } }, - "@microsoft/tsdoc": { - "version": "0.12.24", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.24.tgz", - "integrity": "sha512-Mfmij13RUTmHEMi9vRUhMXD7rnGR2VvxeNYtaGtaJ4redwwjT4UXYJ+nzmVJF7hhd4pn/Fx5sncDKxMVFJSWPg==", - "dev": true - }, "@microsoft/tsdoc-config": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.1.tgz", @@ -668,40 +871,6 @@ "dev": true, "optional": true }, - "@rushstack/node-core-library": { - "version": "3.36.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.36.0.tgz", - "integrity": "sha512-bID2vzXpg8zweXdXgQkKToEdZwVrVCN9vE9viTRk58gqzYaTlz4fMId6V3ZfpXN6H0d319uGi2KDlm+lUEeqCg==", - "dev": true, - "requires": { - "@types/node": "10.17.13", - "colors": "~1.2.1", - "fs-extra": "~7.0.1", - "import-lazy": "~4.0.0", - "jju": "~1.4.0", - "resolve": "~1.17.0", - "semver": "~7.3.0", - "timsort": "~0.3.0", - "z-schema": "~3.18.3" - }, - "dependencies": { - "@types/node": { - "version": "10.17.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz", - "integrity": "sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==", - "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" - } - } - } - }, "@rushstack/rig-package": { "version": "0.3.11", "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.11.tgz", @@ -729,18 +898,6 @@ } } }, - "@rushstack/ts-command-line": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.7.8.tgz", - "integrity": "sha512-8ghIWhkph7NnLCMDJtthpsb7TMOsVGXVDvmxjE/CeklTqjbbUFBjGXizJfpbEkRQTELuZQ2+vGn7sGwIWKN2uA==", - "dev": true, - "requires": { - "@types/argparse": "1.0.38", - "argparse": "~1.0.9", - "colors": "~1.2.1", - "string-argv": "~0.3.1" - } - }, "@sinonjs/commons": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", @@ -1129,16 +1286,6 @@ "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", "dev": true }, - "api-extractor-model-me": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/api-extractor-model-me/-/api-extractor-model-me-0.1.1.tgz", - "integrity": "sha512-Ez801ZMADfkseOWNRFquvyQYDm3D9McpxfkKMWL6JFCGcpub0miJ+TFNphIR1nSZbrsxz3kIeOovNMY4VlL6Bw==", - "dev": true, - "requires": { - "@microsoft/tsdoc": "0.12.24", - "@rushstack/node-core-library": "3.36.0" - } - }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -2290,19 +2437,6 @@ "jws": "^4.0.0" } }, - "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "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" - } - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -3180,9 +3314,9 @@ "dev": true }, "marked": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz", - "integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==", + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.15.tgz", + "integrity": "sha512-esX5lPdTfG4p8LDkv+obbRCyOKzB+820ZZyMOXJZygZBHrH9b3xXR64X4kT3sPe9Nx8qQXbmcz6kFSMt4Nfk6Q==", "dev": true }, "media-typer": { @@ -3469,12 +3603,6 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, - "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", @@ -3813,12 +3941,6 @@ "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", "dev": true }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, "promise-polyfill": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz", @@ -4130,9 +4252,9 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "shiki": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.15.tgz", - "integrity": "sha512-/Y0z9IzhJ8nD9nbceORCqu6NgT9X6I8Fk8c3SICHI5NbZRLdZYFaB233gwct9sU0vvSypyaL/qaKvzyQGJBZSw==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", + "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", "dev": true, "requires": { "jsonc-parser": "^3.0.0", @@ -4564,20 +4686,16 @@ } }, "typedoc": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.2.tgz", - "integrity": "sha512-SR1ByJB3USg+jxoxwzMRP07g/0f/cQUE5t7gOh1iTUyjTPyJohu9YSKRlK+MSXXqlhIq+m0jkEHEG5HoY7/Adg==", + "version": "0.22.15", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.15.tgz", + "integrity": "sha512-CMd1lrqQbFvbx6S9G6fL4HKp3GoIuhujJReWqlIvSb2T26vGai+8Os3Mde7Pn832pXYemd9BMuuYWhFpL5st0Q==", "dev": true, "requires": { - "glob": "^7.1.7", - "handlebars": "^4.7.7", - "lodash": "^4.17.21", + "glob": "^7.2.0", "lunr": "^2.3.9", - "marked": "^2.1.1", - "minimatch": "^3.0.0", - "progress": "^2.0.3", - "shiki": "^0.9.3", - "typedoc-default-themes": "^0.12.10" + "marked": "^4.0.12", + "minimatch": "^5.0.1", + "shiki": "^0.10.1" }, "dependencies": { "glob": { @@ -4592,29 +4710,47 @@ "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + } } } } }, - "typedoc-default-themes": { - "version": "0.12.10", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", - "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", - "dev": true - }, "typescript": { "version": "4.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "dev": true }, - "uglify-js": { - "version": "3.15.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz", - "integrity": "sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA==", - "dev": true, - "optional": true - }, "unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -4682,12 +4818,6 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, - "validator": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-8.2.0.tgz", - "integrity": "sha512-Yw5wW34fSv5spzTXNkokD6S6/Oq92d8q/t14TqsS3fAiA1RYnxSFSIZ+CY3n6PGGRCq5HhJTSepQvFUS2QUDxA==", - "dev": true - }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -4845,12 +4975,6 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -5172,18 +5296,6 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "optional": true - }, - "z-schema": { - "version": "3.18.4", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.18.4.tgz", - "integrity": "sha512-DUOKC/IhbkdLKKiV89gw9DUauTV8U/8yJl1sjf6MtDmzevLKOF2duNJ495S3MFVjqZarr+qNGCPbkg4mu4PpLw==", - "dev": true, - "requires": { - "commander": "^2.7.1", - "lodash.get": "^4.0.0", - "lodash.isequal": "^4.0.0", - "validator": "^8.0.0" - } } } } diff --git a/package.json b/package.json index 20f8c6ce0..86e128cad 100644 --- a/package.json +++ b/package.json @@ -208,7 +208,6 @@ "mock-require": "^3.0.3", "mz": "^2.7.0", "nock": "^10.0.6", - "node-fetch": "^2.6.7", "portfinder": "^1.0.28", "prettier": "^1.18.2", "semver": "^7.3.5", @@ -218,7 +217,7 @@ "tslint-config-prettier": "^1.18.0", "tslint-no-unused-expression-chai": "^0.1.4", "tslint-plugin-prettier": "^2.0.1", - "typedoc": "0.21.2", + "typedoc": "^0.22.15", "typescript": "^4.3.5", "yargs": "^15.3.1" }, diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 1f59f50d8..6018e4caa 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -39,13 +39,13 @@ import { } from './common/encoding'; import { ManifestEndpoint, ManifestRequiredAPI } from './runtime/manifest'; -/** @hidden */ +/* @internal */ const WILDCARD_REGEX = new RegExp('{[^/{}]*}', 'g'); /** - * @hidden - * * Wire format for an event. + * + * @internal */ export interface Event { context: { @@ -181,20 +181,21 @@ export interface ChangeJson { */ before?: any; /** - * @hidden * Comma-separated string that represents names of fields that changed. + * + * @internal */ fieldMask?: string; } export namespace Change { - /** @hidden */ + /** @internal */ function reinterpretCast(x: any) { return x as T; } /** - * @hidden + * @internal * Factory method for creating a Change from a `before` object and an `after` * object. */ @@ -203,7 +204,7 @@ export namespace Change { } /** - * @hidden + * @internal * Factory method for creating a Change from a JSON and an optional customizer * function to be applied to both the `before` and the `after` fields. */ @@ -222,7 +223,7 @@ export namespace Change { ); } - /** @hidden */ + /** @internal */ export function applyFieldMask( sparseBefore: any, after: any, @@ -257,7 +258,6 @@ export interface Resource { } /** - * @hidden * TriggerAnnotated is used internally by the firebase CLI to understand what * type of Cloud Function to deploy. */ @@ -290,7 +290,6 @@ export interface TriggerAnnotated { } /** - * @hidden * EndpointAnnotated is used to generate the manifest that conforms to the container contract. */ export interface EndpointAnnotated { @@ -336,7 +335,7 @@ export type CloudFunction = Runnable & EndpointAnnotated & ((input: any, context?: any) => PromiseLike | any); -/** @hidden */ +/** @internal */ export interface MakeCloudFunctionArgs { after?: (raw: Event) => void; before?: (raw: Event) => void; @@ -356,7 +355,7 @@ export interface MakeCloudFunctionArgs { triggerResource: () => string; } -/** @hidden */ +/** @internal */ export function makeCloudFunction({ after = () => {}, before = () => {}, @@ -499,7 +498,7 @@ export function makeCloudFunction({ return cloudFunction; } -/** @hidden */ +/** @internal */ function _makeParams( context: EventContext, triggerResourceGetter: () => string @@ -527,7 +526,7 @@ function _makeParams( return params; } -/** @hidden */ +/** @internal */ function _makeAuth(event: Event, authType: string) { if (authType === 'UNAUTHENTICATED') { return null; @@ -538,7 +537,7 @@ function _makeAuth(event: Event, authType: string) { }; } -/** @hidden */ +/** @internal */ function _detectAuthType(event: Event) { if (_.get(event, 'context.auth.admin')) { return 'ADMIN'; @@ -549,7 +548,7 @@ function _detectAuthType(event: Event) { return 'UNAUTHENTICATED'; } -/** @hidden */ +/** @internal */ export function optionsToTrigger(options: DeploymentOptions) { const trigger: any = {}; copyIfPresent( diff --git a/src/common/providers/https.ts b/src/common/providers/https.ts index 29f516bc7..be50df1d4 100644 --- a/src/common/providers/https.ts +++ b/src/common/providers/https.ts @@ -34,7 +34,9 @@ import { TaskContext } from './tasks'; const JWT_REGEX = /^[a-zA-Z0-9\-_=]+?\.[a-zA-Z0-9\-_=]+?\.([a-zA-Z0-9\-_=]+)?$/; -/** @hidden */ +/** + * Represents incoming request object. + */ export interface Request extends express.Request { rawBody: Buffer; } @@ -228,7 +230,9 @@ export type FunctionsErrorCode = | 'data-loss' | 'unauthenticated'; -/** @hidden */ +/** + * Canonical name of HTTP status. + */ export type CanonicalErrorCodeName = | 'OK' | 'CANCELLED' @@ -248,7 +252,6 @@ export type CanonicalErrorCodeName = | 'UNAVAILABLE' | 'DATA_LOSS'; -/** @hidden */ interface HttpErrorCode { canonicalName: CanonicalErrorCodeName; status: number; @@ -283,7 +286,6 @@ const errorCodeMap: { [name in FunctionsErrorCode]: HttpErrorCode } = { 'data-loss': { canonicalName: 'DATA_LOSS', status: 500 }, }; -/** @hidden */ interface HttpErrorWireFormat { details?: unknown; message: string; @@ -309,7 +311,7 @@ export class HttpsError extends Error { /** * A wire format representation of a provided error code. * - * @hidden + * @internal */ public readonly httpErrorCode: HttpErrorCode; @@ -341,7 +343,6 @@ export class HttpsError extends Error { } } -/** @hidden */ // The allowed interface for an HTTP request to a Callable function. interface HttpRequest extends Request { body: { @@ -349,15 +350,17 @@ interface HttpRequest extends Request { }; } -/** @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. +/** + * Returns true if req is a properly formatted callable request. + * + * @internal + */ export function isValidRequest(req: Request): req is HttpRequest { // The body must not be empty. if (!req.body) { @@ -400,16 +403,15 @@ export 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'; /** * Encodes arbitrary data in our special format for JSON. * This is exposed only for testing. + * + * @internal */ -/** @hidden */ export function encode(data: any): any { if (data === null || typeof data === 'undefined') { return null; @@ -448,8 +450,9 @@ export function encode(data: any): any { /** * Decodes our special format for JSON into native types. * This is exposed only for testing. + * + * @internal */ -/** @hidden */ export function decode(data: any): any { if (data === null) { return data; @@ -494,12 +497,9 @@ export function decode(data: any): any { * * 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; diff --git a/src/logger/index.ts b/src/logger/index.ts index deb7c1762..5ea3e9e36 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -30,7 +30,6 @@ export interface LogEntry { [key: string]: any; } -/** @internal */ function removeCircular(obj: any, refs: any[] = []): any { if (typeof obj !== 'object' || !obj) { return obj; @@ -62,7 +61,7 @@ function removeCircular(obj: any, refs: any[] = []): any { /** * 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) { @@ -94,7 +93,7 @@ export function write(entry: LogEntry) { /** * Writes a `DEBUG` severity log. If the last argument provided is a plain object, * it is added to the `jsonPayload` in the Cloud Logging entry. - * @param args Arguments, concatenated into the log message with space separators. + * @param args - Arguments, concatenated into the log message with space separators. */ export function debug(...args: any[]) { write(entryFromArgs('DEBUG', args)); @@ -103,7 +102,7 @@ export function debug(...args: any[]) { /** * Writes an `INFO` severity log. If the last argument provided is a plain object, * it is added to the `jsonPayload` in the Cloud Logging entry. - * @param args Arguments, concatenated into the log message with space separators. + * @param args - Arguments, concatenated into the log message with space separators. */ export function log(...args: any[]) { write(entryFromArgs('INFO', args)); @@ -112,7 +111,7 @@ export function log(...args: any[]) { /** * Writes an `INFO` severity log. If the last argument provided is a plain object, * it is added to the `jsonPayload` in the Cloud Logging entry. - * @param args Arguments, concatenated into the log message with space separators. + * @param args - Arguments, concatenated into the log message with space separators. */ export function info(...args: any[]) { write(entryFromArgs('INFO', args)); @@ -121,7 +120,7 @@ export function info(...args: any[]) { /** * Writes a `WARNING` severity log. If the last argument provided is a plain object, * it is added to the `jsonPayload` in the Cloud Logging entry. - * @param args Arguments, concatenated into the log message with space separators. + * @param args - Arguments, concatenated into the log message with space separators. */ export function warn(...args: any[]) { write(entryFromArgs('WARNING', args)); @@ -130,13 +129,12 @@ export function warn(...args: any[]) { /** * Writes an `ERROR` severity log. If the last argument provided is a plain object, * it is added to the `jsonPayload` in the Cloud Logging entry. - * @param args Arguments, concatenated into the log message with space separators. + * @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/providers/pubsub.ts b/src/providers/pubsub.ts index 9cd88bd86..c7e205642 100644 --- a/src/providers/pubsub.ts +++ b/src/providers/pubsub.ts @@ -30,23 +30,23 @@ import { ScheduleRetryConfig, } from '../function-configuration'; -/** @hidden */ +/** @internal */ export const provider = 'google.pubsub'; -/** @hidden */ +/** @internal */ export const service = 'pubsub.googleapis.com'; /** * Registers a Cloud Function triggered when a Google Cloud Pub/Sub message * is sent to a specified topic. * - * @param topic The Pub/Sub topic to watch for message events. + * @param topic - The Pub/Sub topic to watch for message events. * @return Pub/Sub topic builder interface. */ export function topic(topic: string) { return _topicWithOptions(topic, {}); } -/** @hidden */ +/** @internal */ export function _topicWithOptions( topic: string, options: DeploymentOptions @@ -69,7 +69,7 @@ export function _topicWithOptions( * Access via [`functions.pubsub.topic()`](providers_pubsub_.html#topic). */ export class TopicBuilder { - /** @hidden */ + /** @internal */ constructor( private triggerResource: () => string, private options: DeploymentOptions @@ -79,7 +79,7 @@ export class TopicBuilder { * Event handler that fires every time a Cloud Pub/Sub message is * published. * - * @param handler Event handler that runs every time a Cloud Pub/Sub message + * @param handler - Event handler that runs every time a Cloud Pub/Sub message * is published. * @return A Cloud Function that you can export and deploy. */ @@ -101,14 +101,14 @@ export class TopicBuilder { /** * Registers a Cloud Function to run at specified times. * - * @param schedule The schedule, in Unix Crontab or AppEngine syntax. + * @param schedule - The schedule, in Unix Crontab or AppEngine syntax. * @return ScheduleBuilder interface. */ export function schedule(schedule: string): ScheduleBuilder { return _scheduleWithOptions(schedule, {}); } -/** @hidden */ +/** @internal */ export function _scheduleWithOptions( schedule: string, options: DeploymentOptions @@ -136,7 +136,7 @@ export function _scheduleWithOptions( * Access via [`functions.pubsub.schedule()`](providers_pubsub_.html#schedule). */ export class ScheduleBuilder { - /** @hidden */ + /** @internal */ constructor( private triggerResource: () => string, private options: DeploymentOptions @@ -156,7 +156,7 @@ export class ScheduleBuilder { * Event handler for scheduled functions. Triggered whenever the associated * scheduler job sends a Pub/Sub message. * - * @param handler Handler that fires whenever the associated + * @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. */ @@ -177,7 +177,7 @@ export class ScheduleBuilder { /** * Interface representing a Google Cloud Pub/Sub message. * - * @param data Payload of a Pub/Sub message. + * @param data - Payload of a Pub/Sub message. */ export class Message { /** @@ -190,7 +190,7 @@ export class Message { */ readonly attributes: { [key: string]: string }; - /** @hidden */ + /** @internal */ private _json: any; constructor(data: any) { diff --git a/src/v2/options.ts b/src/v2/options.ts index c4e3cd7de..88d1be69e 100644 --- a/src/v2/options.ts +++ b/src/v2/options.ts @@ -168,14 +168,14 @@ export interface GlobalOptions { /** * Number of requests a function can serve at once. * Can only be applied to functions running on Cloud Functions v2. - * A value of null restores the default concurrency (80 when CPU >= 1, 1 otherwise). + * A value of null restores the default concurrency (80 when CPU \>= 1, 1 otherwise). * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. */ concurrency?: number | null; /** * Fractional number of CPUs to allocate to a function. - * Defaults to 1 for functions with <= 2GB RAM and increases for larger memory sizes. + * Defaults to 1 for functions with \<= 2GB RAM and increases for larger memory sizes. * This is different from the defaults when using the gcloud utility and is different from * the fixed amount assigned in Google Cloud Functions generation 1. * To revert to the CPU amounts used in gcloud or in Cloud Functions generation 1, set this @@ -227,7 +227,7 @@ let globalOptions: GlobalOptions | undefined; /** * Sets default options for all functions written using the v2 SDK. - * @param options Options to set as default + * @param options - Options to set as default */ export function setGlobalOptions(options: GlobalOptions) { if (globalOptions) { @@ -239,6 +239,7 @@ export function setGlobalOptions(options: GlobalOptions) { /** * Get the currently set default options. * Used only for trigger generation. + * * @internal */ export function getGlobalOptions(): GlobalOptions { @@ -254,6 +255,7 @@ export interface EventHandlerOptions extends GlobalOptions { /** * Apply GlobalOptions to trigger definitions. + * * @internal */ export function optionsToTriggerAnnotations( @@ -316,6 +318,7 @@ export function optionsToTriggerAnnotations( /** * Apply GlobalOptions to endpoint manifest. + * * @internal */ export function optionsToEndpoint( @@ -359,9 +362,7 @@ export function optionsToEndpoint( return endpoint; } -/** - * @hidden - */ +/* @internal */ export function __getSpec(): { globalOptions: GlobalOptions; params: ParamSpec[]; diff --git a/src/v2/params/index.ts b/src/v2/params/index.ts index f4ea32b53..29acab37a 100644 --- a/src/v2/params/index.ts +++ b/src/v2/params/index.ts @@ -1,4 +1,3 @@ -/** @hidden */ import { BooleanParam, FloatParam, @@ -10,14 +9,15 @@ import { StringParam, } from './types'; +/* @alpha */ export { ParamOptions }; +/* @alpha */ export const declaredParams: Param[] = []; /** * Use a helper to manage the list such that params are uniquely * registered once only but order is preserved. - * @internal */ function registerParam(param: Param) { for (let i = 0; i < declaredParams.length; i++) { @@ -31,9 +31,11 @@ function registerParam(param: Param) { /** * Declare a string param. * - * @param name The name of the environment variable to use to load the param. - * @param options Configuration options for the param. + * @param name - The name of the environment variable to use to load the param. + * @param options - Configuration options for the param. * @returns A Param with a `string` return type for `.value`. + * + * @alpha */ export function defineString( name: string, @@ -47,9 +49,11 @@ export function defineString( /** * Declare a boolean param. * - * @param name The name of the environment variable to use to load the param. - * @param options Configuration options for the param. + * @param name - The name of the environment variable to use to load the param. + * @param options - Configuration options for the param. * @returns A Param with a `boolean` return type for `.value`. + * + * @alpha */ export function defineBoolean( name: string, @@ -63,9 +67,11 @@ export function defineBoolean( /** * Declare an integer param. * - * @param name The name of the environment variable to use to load the param. - * @param options Configuration options for the param. + * @param name - The name of the environment variable to use to load the param. + * @param options - Configuration options for the param. * @returns A Param with a `number` return type for `.value`. + * + * @alpha */ export function defineInt( name: string, @@ -79,9 +85,11 @@ export function defineInt( /** * Declare a float param. * - * @param name The name of the environment variable to use to load the param. - * @param options Configuration options for the param. + * @param name - The name of the environment variable to use to load the param. + * @param options - Configuration options for the param. * @returns A Param with a `number` return type for `.value`. + * + * @alpha */ export function defineFloat( name: string, @@ -95,9 +103,11 @@ export function defineFloat( /** * Declare a list param (array of strings). * - * @param name The name of the environment variable to use to load the param. - * @param options Configuration options for the param. + * @param name - The name of the environment variable to use to load the param. + * @param options - Configuration options for the param. * @returns A Param with a `string[]` return type for `.value`. + * + * @alpha */ export function defineList( name: string, @@ -112,9 +122,11 @@ export function defineList( * Declare a JSON param. The associated environment variable will be treated * as a JSON string when loading its value. * - * @param name The name of the environment variable to use to load the param. - * @param options Configuration options for the param. + * @param name - The name of the environment variable to use to load the param. + * @param options - Configuration options for the param. * @returns A Param with a specifiable return type for `.value`. + * + * @alpha */ export function defineJSON( name: string, diff --git a/src/v2/params/types.ts b/src/v2/params/types.ts index c345cf654..e0cc3c701 100644 --- a/src/v2/params/types.ts +++ b/src/v2/params/types.ts @@ -1,6 +1,6 @@ -/** @hidden */ type ParamValueType = 'string' | 'list' | 'boolean' | 'int' | 'float' | 'json'; +/* @alpha */ export interface ParamSpec { name: string; default?: T; @@ -9,11 +9,13 @@ export interface ParamSpec { valueType?: ParamValueType; } +/* @alpha */ export type ParamOptions = Omit< ParamSpec, 'name' | 'valueType' >; +/* @alpha */ export class Param { static valueType: ParamValueType = 'string'; @@ -51,10 +53,12 @@ export class Param { } } +/* @alpha */ export class StringParam extends Param { // identical to the abstract class, just explicitly a string } +/* @alpha */ export class IntParam extends Param { static valueType: ParamValueType = 'int'; @@ -74,6 +78,7 @@ export class IntParam extends Param { } } +/* @alpha */ export class FloatParam extends Param { static valueType: ParamValueType = 'float'; @@ -92,6 +97,7 @@ export class FloatParam extends Param { } } +/* @alpha */ export class BooleanParam extends Param { static valueType: ParamValueType = 'boolean'; @@ -114,6 +120,7 @@ export class BooleanParam extends Param { } } +/* @alpha */ export class ListParam extends Param { static valueType: ParamValueType = 'list'; @@ -137,6 +144,7 @@ export class ListParam extends Param { } } +/* @alpha */ export class JSONParam extends Param { static valueType: ParamValueType = 'json'; diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index df800fadb..ee83a734e 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -56,8 +56,8 @@ export interface FirebaseAlertOptions extends options.EventHandlerOptions { /** * Declares a function that can handle Firebase Alerts from CloudEvents. - * @param alertTypeOrOpts the alert type or Firebase Alert function configuration. - * @param handler a function that can handle the Firebase Alert inside a CloudEvent. + * @param alertTypeOrOpts - the alert type or Firebase Alert function configuration. + * @param handler - a function that can handle the Firebase Alert inside a CloudEvent. */ export function onAlertPublished( alertTypeOrOpts: AlertType | FirebaseAlertOptions, diff --git a/src/v2/providers/pubsub.ts b/src/v2/providers/pubsub.ts index 62a5ba4a1..abda5f6d8 100644 --- a/src/v2/providers/pubsub.ts +++ b/src/v2/providers/pubsub.ts @@ -6,7 +6,7 @@ import * as options from '../options'; /** * Interface representing a Google Cloud Pub/Sub message. * - * @param data Payload of a Pub/Sub message. + * @param data - Payload of a Pub/Sub message. */ export class Message { /** @@ -34,7 +34,7 @@ export class Message { */ readonly orderingKey: string; - /** @hidden */ + /** @internal */ private _json: T; constructor(data: any) { @@ -68,7 +68,7 @@ export class Message { /** * Returns a JSON-serializable representation of this object. * - * @return A JSON-serializable representation of this object. + * @returns A JSON-serializable representation of this object. */ toJSON(): any { const json: Record = { From 7cbc9c7303f6beae31c00d945b4ca1aff5fd2c4f Mon Sep 17 00:00:00 2001 From: Tyler Stark Date: Thu, 5 May 2022 09:52:20 -0500 Subject: [PATCH 080/370] Update changelog for v3.21.0 (#1092) * Update changelog for v3.20.1 -> v3.21.0 --- CHANGELOG.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ddc0e009c..ccd110162 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,9 @@ -- Adds auth blocking triggers to the auth and identity namespaces (1080). -- Add support for secrets for v2 triggers (#1079). +- Adds CPU option and enhances internal data structures (#1077) +- Add auth blocking handlers (#1080) +- Add support for secrets in v2 (#1079) +- Update types for AlertPayloads (#1087) +- Update AppDistribution `[@type]` (#1088) +- Update CloudEvent types (#1089) +- Generate documentation with api-extractor (#1071) +- Change type info to be inheritance friendly. (#1091) +- Changes the memory options from MB to MiB and GB to GiB for greater clarity (#1090) From 6d0a912df02534d0698802aa0210f726618de292 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 6 May 2022 01:46:10 +0900 Subject: [PATCH 081/370] Revert "Remove all errors when generating reference doc using api-extractor (#1095)" (#1096) Reverts firebase/firebase-functions#1095 It turns out that @hidden -> @internal is not a safe transformation for exported values since our `tsconfig.json` specifies `stripInternal: true`. Temporarily rolling back change - I'll send out a smaller patch that contains subset of changes in #1095 --- docgen/api-extractor.v2.json | 20 -- package-lock.json | 394 +++++++++++------------------- package.json | 3 +- src/cloud-functions.ts | 31 +-- src/common/providers/https.ts | 32 +-- src/logger/index.ts | 14 +- src/providers/pubsub.ts | 24 +- src/v2/options.ts | 13 +- src/v2/params/index.ts | 40 ++- src/v2/params/types.ts | 10 +- src/v2/providers/alerts/alerts.ts | 4 +- src/v2/providers/pubsub.ts | 6 +- 12 files changed, 221 insertions(+), 370 deletions(-) diff --git a/docgen/api-extractor.v2.json b/docgen/api-extractor.v2.json index 492c8bae6..2cf0596b7 100644 --- a/docgen/api-extractor.v2.json +++ b/docgen/api-extractor.v2.json @@ -10,25 +10,5 @@ "enabled": true, "reportTempFolder": "/docgen/v2/temp", "reportFolder": "/docgen/v2" - }, - "messages": { - "compilerMessageReporting": { - "default": { - "logLevel": "warning" - } - }, - "extractorMessageReporting": { - "default": { - "logLevel": "warning" - }, - "ae-missing-release-tag": { - "logLevel": "none" - } - }, - "tsdocMessageReporting": { - "default": { - "logLevel": "error" - } - } } } diff --git a/package-lock.json b/package-lock.json index 2bd402d37..5b5f7fc85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,122 +61,12 @@ "tslib": "^2.1.0" }, "dependencies": { - "@microsoft/tsdoc": { - "version": "0.12.24", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.24.tgz", - "integrity": "sha512-Mfmij13RUTmHEMi9vRUhMXD7rnGR2VvxeNYtaGtaJ4redwwjT4UXYJ+nzmVJF7hhd4pn/Fx5sncDKxMVFJSWPg==", - "dev": true - }, - "@rushstack/node-core-library": { - "version": "3.36.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.36.0.tgz", - "integrity": "sha512-bID2vzXpg8zweXdXgQkKToEdZwVrVCN9vE9viTRk58gqzYaTlz4fMId6V3ZfpXN6H0d319uGi2KDlm+lUEeqCg==", - "dev": true, - "requires": { - "@types/node": "10.17.13", - "colors": "~1.2.1", - "fs-extra": "~7.0.1", - "import-lazy": "~4.0.0", - "jju": "~1.4.0", - "resolve": "~1.17.0", - "semver": "~7.3.0", - "timsort": "~0.3.0", - "z-schema": "~3.18.3" - } - }, - "@rushstack/ts-command-line": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.7.8.tgz", - "integrity": "sha512-8ghIWhkph7NnLCMDJtthpsb7TMOsVGXVDvmxjE/CeklTqjbbUFBjGXizJfpbEkRQTELuZQ2+vGn7sGwIWKN2uA==", - "dev": true, - "requires": { - "@types/argparse": "1.0.38", - "argparse": "~1.0.9", - "colors": "~1.2.1", - "string-argv": "~0.3.1" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - } - } - }, - "@types/argparse": { - "version": "1.0.38", - "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", - "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", - "dev": true - }, - "@types/node": { - "version": "10.17.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz", - "integrity": "sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==", - "dev": true - }, - "api-extractor-model-me": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/api-extractor-model-me/-/api-extractor-model-me-0.1.1.tgz", - "integrity": "sha512-Ez801ZMADfkseOWNRFquvyQYDm3D9McpxfkKMWL6JFCGcpub0miJ+TFNphIR1nSZbrsxz3kIeOovNMY4VlL6Bw==", - "dev": true, - "requires": { - "@microsoft/tsdoc": "0.12.24", - "@rushstack/node-core-library": "3.36.0" - } - }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "colors": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", - "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", - "dev": true - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "optional": true - }, - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "import-lazy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", - "dev": true - }, - "jju": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", - "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=", - "dev": true - }, "js-yaml": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", @@ -186,42 +76,6 @@ "argparse": "^2.0.1" } }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, "resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", @@ -230,69 +84,6 @@ "requires": { "path-parse": "^1.0.6" } - }, - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true - }, - "timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", - "dev": true - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "validator": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-8.2.0.tgz", - "integrity": "sha512-Yw5wW34fSv5spzTXNkokD6S6/Oq92d8q/t14TqsS3fAiA1RYnxSFSIZ+CY3n6PGGRCq5HhJTSepQvFUS2QUDxA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "z-schema": { - "version": "3.18.4", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.18.4.tgz", - "integrity": "sha512-DUOKC/IhbkdLKKiV89gw9DUauTV8U/8yJl1sjf6MtDmzevLKOF2duNJ495S3MFVjqZarr+qNGCPbkg4mu4PpLw==", - "dev": true, - "requires": { - "commander": "^2.7.1", - "lodash.get": "^4.0.0", - "lodash.isequal": "^4.0.0", - "validator": "^8.0.0" - } } } }, @@ -761,6 +552,12 @@ } } }, + "@microsoft/tsdoc": { + "version": "0.12.24", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.24.tgz", + "integrity": "sha512-Mfmij13RUTmHEMi9vRUhMXD7rnGR2VvxeNYtaGtaJ4redwwjT4UXYJ+nzmVJF7hhd4pn/Fx5sncDKxMVFJSWPg==", + "dev": true + }, "@microsoft/tsdoc-config": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.1.tgz", @@ -871,6 +668,40 @@ "dev": true, "optional": true }, + "@rushstack/node-core-library": { + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.36.0.tgz", + "integrity": "sha512-bID2vzXpg8zweXdXgQkKToEdZwVrVCN9vE9viTRk58gqzYaTlz4fMId6V3ZfpXN6H0d319uGi2KDlm+lUEeqCg==", + "dev": true, + "requires": { + "@types/node": "10.17.13", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "timsort": "~0.3.0", + "z-schema": "~3.18.3" + }, + "dependencies": { + "@types/node": { + "version": "10.17.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz", + "integrity": "sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==", + "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" + } + } + } + }, "@rushstack/rig-package": { "version": "0.3.11", "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.11.tgz", @@ -898,6 +729,18 @@ } } }, + "@rushstack/ts-command-line": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.7.8.tgz", + "integrity": "sha512-8ghIWhkph7NnLCMDJtthpsb7TMOsVGXVDvmxjE/CeklTqjbbUFBjGXizJfpbEkRQTELuZQ2+vGn7sGwIWKN2uA==", + "dev": true, + "requires": { + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "colors": "~1.2.1", + "string-argv": "~0.3.1" + } + }, "@sinonjs/commons": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", @@ -1286,6 +1129,16 @@ "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", "dev": true }, + "api-extractor-model-me": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/api-extractor-model-me/-/api-extractor-model-me-0.1.1.tgz", + "integrity": "sha512-Ez801ZMADfkseOWNRFquvyQYDm3D9McpxfkKMWL6JFCGcpub0miJ+TFNphIR1nSZbrsxz3kIeOovNMY4VlL6Bw==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.12.24", + "@rushstack/node-core-library": "3.36.0" + } + }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -2437,6 +2290,19 @@ "jws": "^4.0.0" } }, + "handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "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" + } + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -3314,9 +3180,9 @@ "dev": true }, "marked": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.15.tgz", - "integrity": "sha512-esX5lPdTfG4p8LDkv+obbRCyOKzB+820ZZyMOXJZygZBHrH9b3xXR64X4kT3sPe9Nx8qQXbmcz6kFSMt4Nfk6Q==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz", + "integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==", "dev": true }, "media-typer": { @@ -3603,6 +3469,12 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, + "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", @@ -3941,6 +3813,12 @@ "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", "dev": true }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "promise-polyfill": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz", @@ -4252,9 +4130,9 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "shiki": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", - "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.15.tgz", + "integrity": "sha512-/Y0z9IzhJ8nD9nbceORCqu6NgT9X6I8Fk8c3SICHI5NbZRLdZYFaB233gwct9sU0vvSypyaL/qaKvzyQGJBZSw==", "dev": true, "requires": { "jsonc-parser": "^3.0.0", @@ -4686,16 +4564,20 @@ } }, "typedoc": { - "version": "0.22.15", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.15.tgz", - "integrity": "sha512-CMd1lrqQbFvbx6S9G6fL4HKp3GoIuhujJReWqlIvSb2T26vGai+8Os3Mde7Pn832pXYemd9BMuuYWhFpL5st0Q==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.2.tgz", + "integrity": "sha512-SR1ByJB3USg+jxoxwzMRP07g/0f/cQUE5t7gOh1iTUyjTPyJohu9YSKRlK+MSXXqlhIq+m0jkEHEG5HoY7/Adg==", "dev": true, "requires": { - "glob": "^7.2.0", + "glob": "^7.1.7", + "handlebars": "^4.7.7", + "lodash": "^4.17.21", "lunr": "^2.3.9", - "marked": "^4.0.12", - "minimatch": "^5.0.1", - "shiki": "^0.10.1" + "marked": "^2.1.1", + "minimatch": "^3.0.0", + "progress": "^2.0.3", + "shiki": "^0.9.3", + "typedoc-default-themes": "^0.12.10" }, "dependencies": { "glob": { @@ -4710,47 +4592,29 @@ "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" - }, - "dependencies": { - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - } } } } }, + "typedoc-default-themes": { + "version": "0.12.10", + "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", + "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", + "dev": true + }, "typescript": { "version": "4.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "dev": true }, + "uglify-js": { + "version": "3.15.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz", + "integrity": "sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA==", + "dev": true, + "optional": true + }, "unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -4818,6 +4682,12 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, + "validator": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-8.2.0.tgz", + "integrity": "sha512-Yw5wW34fSv5spzTXNkokD6S6/Oq92d8q/t14TqsS3fAiA1RYnxSFSIZ+CY3n6PGGRCq5HhJTSepQvFUS2QUDxA==", + "dev": true + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -4975,6 +4845,12 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -5296,6 +5172,18 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "optional": true + }, + "z-schema": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.18.4.tgz", + "integrity": "sha512-DUOKC/IhbkdLKKiV89gw9DUauTV8U/8yJl1sjf6MtDmzevLKOF2duNJ495S3MFVjqZarr+qNGCPbkg4mu4PpLw==", + "dev": true, + "requires": { + "commander": "^2.7.1", + "lodash.get": "^4.0.0", + "lodash.isequal": "^4.0.0", + "validator": "^8.0.0" + } } } } diff --git a/package.json b/package.json index 86e128cad..20f8c6ce0 100644 --- a/package.json +++ b/package.json @@ -208,6 +208,7 @@ "mock-require": "^3.0.3", "mz": "^2.7.0", "nock": "^10.0.6", + "node-fetch": "^2.6.7", "portfinder": "^1.0.28", "prettier": "^1.18.2", "semver": "^7.3.5", @@ -217,7 +218,7 @@ "tslint-config-prettier": "^1.18.0", "tslint-no-unused-expression-chai": "^0.1.4", "tslint-plugin-prettier": "^2.0.1", - "typedoc": "^0.22.15", + "typedoc": "0.21.2", "typescript": "^4.3.5", "yargs": "^15.3.1" }, diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 6018e4caa..1f59f50d8 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -39,13 +39,13 @@ import { } from './common/encoding'; import { ManifestEndpoint, ManifestRequiredAPI } from './runtime/manifest'; -/* @internal */ +/** @hidden */ const WILDCARD_REGEX = new RegExp('{[^/{}]*}', 'g'); /** - * Wire format for an event. + * @hidden * - * @internal + * Wire format for an event. */ export interface Event { context: { @@ -181,21 +181,20 @@ export interface ChangeJson { */ before?: any; /** + * @hidden * Comma-separated string that represents names of fields that changed. - * - * @internal */ fieldMask?: string; } export namespace Change { - /** @internal */ + /** @hidden */ function reinterpretCast(x: any) { return x as T; } /** - * @internal + * @hidden * Factory method for creating a Change from a `before` object and an `after` * object. */ @@ -204,7 +203,7 @@ export namespace Change { } /** - * @internal + * @hidden * Factory method for creating a Change from a JSON and an optional customizer * function to be applied to both the `before` and the `after` fields. */ @@ -223,7 +222,7 @@ export namespace Change { ); } - /** @internal */ + /** @hidden */ export function applyFieldMask( sparseBefore: any, after: any, @@ -258,6 +257,7 @@ export interface Resource { } /** + * @hidden * TriggerAnnotated is used internally by the firebase CLI to understand what * type of Cloud Function to deploy. */ @@ -290,6 +290,7 @@ export interface TriggerAnnotated { } /** + * @hidden * EndpointAnnotated is used to generate the manifest that conforms to the container contract. */ export interface EndpointAnnotated { @@ -335,7 +336,7 @@ export type CloudFunction = Runnable & EndpointAnnotated & ((input: any, context?: any) => PromiseLike | any); -/** @internal */ +/** @hidden */ export interface MakeCloudFunctionArgs { after?: (raw: Event) => void; before?: (raw: Event) => void; @@ -355,7 +356,7 @@ export interface MakeCloudFunctionArgs { triggerResource: () => string; } -/** @internal */ +/** @hidden */ export function makeCloudFunction({ after = () => {}, before = () => {}, @@ -498,7 +499,7 @@ export function makeCloudFunction({ return cloudFunction; } -/** @internal */ +/** @hidden */ function _makeParams( context: EventContext, triggerResourceGetter: () => string @@ -526,7 +527,7 @@ function _makeParams( return params; } -/** @internal */ +/** @hidden */ function _makeAuth(event: Event, authType: string) { if (authType === 'UNAUTHENTICATED') { return null; @@ -537,7 +538,7 @@ function _makeAuth(event: Event, authType: string) { }; } -/** @internal */ +/** @hidden */ function _detectAuthType(event: Event) { if (_.get(event, 'context.auth.admin')) { return 'ADMIN'; @@ -548,7 +549,7 @@ function _detectAuthType(event: Event) { return 'UNAUTHENTICATED'; } -/** @internal */ +/** @hidden */ export function optionsToTrigger(options: DeploymentOptions) { const trigger: any = {}; copyIfPresent( diff --git a/src/common/providers/https.ts b/src/common/providers/https.ts index be50df1d4..29f516bc7 100644 --- a/src/common/providers/https.ts +++ b/src/common/providers/https.ts @@ -34,9 +34,7 @@ import { TaskContext } from './tasks'; const JWT_REGEX = /^[a-zA-Z0-9\-_=]+?\.[a-zA-Z0-9\-_=]+?\.([a-zA-Z0-9\-_=]+)?$/; -/** - * Represents incoming request object. - */ +/** @hidden */ export interface Request extends express.Request { rawBody: Buffer; } @@ -230,9 +228,7 @@ export type FunctionsErrorCode = | 'data-loss' | 'unauthenticated'; -/** - * Canonical name of HTTP status. - */ +/** @hidden */ export type CanonicalErrorCodeName = | 'OK' | 'CANCELLED' @@ -252,6 +248,7 @@ export type CanonicalErrorCodeName = | 'UNAVAILABLE' | 'DATA_LOSS'; +/** @hidden */ interface HttpErrorCode { canonicalName: CanonicalErrorCodeName; status: number; @@ -286,6 +283,7 @@ const errorCodeMap: { [name in FunctionsErrorCode]: HttpErrorCode } = { 'data-loss': { canonicalName: 'DATA_LOSS', status: 500 }, }; +/** @hidden */ interface HttpErrorWireFormat { details?: unknown; message: string; @@ -311,7 +309,7 @@ export class HttpsError extends Error { /** * A wire format representation of a provided error code. * - * @internal + * @hidden */ public readonly httpErrorCode: HttpErrorCode; @@ -343,6 +341,7 @@ export class HttpsError extends Error { } } +/** @hidden */ // The allowed interface for an HTTP request to a Callable function. interface HttpRequest extends Request { body: { @@ -350,17 +349,15 @@ interface HttpRequest extends Request { }; } +/** @hidden */ // The format for an HTTP body response from a Callable function. interface HttpResponseBody { result?: any; error?: HttpsError; } -/** - * Returns true if req is a properly formatted callable request. - * - * @internal - */ +/** @hidden */ +// Returns true if req is a properly formatted callable request. export function isValidRequest(req: Request): req is HttpRequest { // The body must not be empty. if (!req.body) { @@ -403,15 +400,16 @@ export 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'; /** * Encodes arbitrary data in our special format for JSON. * This is exposed only for testing. - * - * @internal */ +/** @hidden */ export function encode(data: any): any { if (data === null || typeof data === 'undefined') { return null; @@ -450,9 +448,8 @@ export function encode(data: any): any { /** * Decodes our special format for JSON into native types. * This is exposed only for testing. - * - * @internal */ +/** @hidden */ export function decode(data: any): any { if (data === null) { return data; @@ -497,9 +494,12 @@ export function decode(data: any): any { * * 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; diff --git a/src/logger/index.ts b/src/logger/index.ts index 5ea3e9e36..deb7c1762 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -30,6 +30,7 @@ export interface LogEntry { [key: string]: any; } +/** @internal */ function removeCircular(obj: any, refs: any[] = []): any { if (typeof obj !== 'object' || !obj) { return obj; @@ -61,7 +62,7 @@ function removeCircular(obj: any, refs: any[] = []): any { /** * 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) { @@ -93,7 +94,7 @@ export function write(entry: LogEntry) { /** * Writes a `DEBUG` severity log. If the last argument provided is a plain object, * it is added to the `jsonPayload` in the Cloud Logging entry. - * @param args - Arguments, concatenated into the log message with space separators. + * @param args Arguments, concatenated into the log message with space separators. */ export function debug(...args: any[]) { write(entryFromArgs('DEBUG', args)); @@ -102,7 +103,7 @@ export function debug(...args: any[]) { /** * Writes an `INFO` severity log. If the last argument provided is a plain object, * it is added to the `jsonPayload` in the Cloud Logging entry. - * @param args - Arguments, concatenated into the log message with space separators. + * @param args Arguments, concatenated into the log message with space separators. */ export function log(...args: any[]) { write(entryFromArgs('INFO', args)); @@ -111,7 +112,7 @@ export function log(...args: any[]) { /** * Writes an `INFO` severity log. If the last argument provided is a plain object, * it is added to the `jsonPayload` in the Cloud Logging entry. - * @param args - Arguments, concatenated into the log message with space separators. + * @param args Arguments, concatenated into the log message with space separators. */ export function info(...args: any[]) { write(entryFromArgs('INFO', args)); @@ -120,7 +121,7 @@ export function info(...args: any[]) { /** * Writes a `WARNING` severity log. If the last argument provided is a plain object, * it is added to the `jsonPayload` in the Cloud Logging entry. - * @param args - Arguments, concatenated into the log message with space separators. + * @param args Arguments, concatenated into the log message with space separators. */ export function warn(...args: any[]) { write(entryFromArgs('WARNING', args)); @@ -129,12 +130,13 @@ export function warn(...args: any[]) { /** * Writes an `ERROR` severity log. If the last argument provided is a plain object, * it is added to the `jsonPayload` in the Cloud Logging entry. - * @param args - Arguments, concatenated into the log message with space separators. + * @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/providers/pubsub.ts b/src/providers/pubsub.ts index c7e205642..9cd88bd86 100644 --- a/src/providers/pubsub.ts +++ b/src/providers/pubsub.ts @@ -30,23 +30,23 @@ import { ScheduleRetryConfig, } from '../function-configuration'; -/** @internal */ +/** @hidden */ export const provider = 'google.pubsub'; -/** @internal */ +/** @hidden */ export const service = 'pubsub.googleapis.com'; /** * Registers a Cloud Function triggered when a Google Cloud Pub/Sub message * is sent to a specified topic. * - * @param topic - The Pub/Sub topic to watch for message events. + * @param topic The Pub/Sub topic to watch for message events. * @return Pub/Sub topic builder interface. */ export function topic(topic: string) { return _topicWithOptions(topic, {}); } -/** @internal */ +/** @hidden */ export function _topicWithOptions( topic: string, options: DeploymentOptions @@ -69,7 +69,7 @@ export function _topicWithOptions( * Access via [`functions.pubsub.topic()`](providers_pubsub_.html#topic). */ export class TopicBuilder { - /** @internal */ + /** @hidden */ constructor( private triggerResource: () => string, private options: DeploymentOptions @@ -79,7 +79,7 @@ export class TopicBuilder { * Event handler that fires every time a Cloud Pub/Sub message is * published. * - * @param handler - Event handler that runs every time a Cloud Pub/Sub message + * @param handler Event handler that runs every time a Cloud Pub/Sub message * is published. * @return A Cloud Function that you can export and deploy. */ @@ -101,14 +101,14 @@ export class TopicBuilder { /** * Registers a Cloud Function to run at specified times. * - * @param schedule - The schedule, in Unix Crontab or AppEngine syntax. + * @param schedule The schedule, in Unix Crontab or AppEngine syntax. * @return ScheduleBuilder interface. */ export function schedule(schedule: string): ScheduleBuilder { return _scheduleWithOptions(schedule, {}); } -/** @internal */ +/** @hidden */ export function _scheduleWithOptions( schedule: string, options: DeploymentOptions @@ -136,7 +136,7 @@ export function _scheduleWithOptions( * Access via [`functions.pubsub.schedule()`](providers_pubsub_.html#schedule). */ export class ScheduleBuilder { - /** @internal */ + /** @hidden */ constructor( private triggerResource: () => string, private options: DeploymentOptions @@ -156,7 +156,7 @@ export class ScheduleBuilder { * Event handler for scheduled functions. Triggered whenever the associated * scheduler job sends a Pub/Sub message. * - * @param handler - Handler that fires whenever the associated + * @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. */ @@ -177,7 +177,7 @@ export class ScheduleBuilder { /** * Interface representing a Google Cloud Pub/Sub message. * - * @param data - Payload of a Pub/Sub message. + * @param data Payload of a Pub/Sub message. */ export class Message { /** @@ -190,7 +190,7 @@ export class Message { */ readonly attributes: { [key: string]: string }; - /** @internal */ + /** @hidden */ private _json: any; constructor(data: any) { diff --git a/src/v2/options.ts b/src/v2/options.ts index 88d1be69e..c4e3cd7de 100644 --- a/src/v2/options.ts +++ b/src/v2/options.ts @@ -168,14 +168,14 @@ export interface GlobalOptions { /** * Number of requests a function can serve at once. * Can only be applied to functions running on Cloud Functions v2. - * A value of null restores the default concurrency (80 when CPU \>= 1, 1 otherwise). + * A value of null restores the default concurrency (80 when CPU >= 1, 1 otherwise). * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. */ concurrency?: number | null; /** * Fractional number of CPUs to allocate to a function. - * Defaults to 1 for functions with \<= 2GB RAM and increases for larger memory sizes. + * Defaults to 1 for functions with <= 2GB RAM and increases for larger memory sizes. * This is different from the defaults when using the gcloud utility and is different from * the fixed amount assigned in Google Cloud Functions generation 1. * To revert to the CPU amounts used in gcloud or in Cloud Functions generation 1, set this @@ -227,7 +227,7 @@ let globalOptions: GlobalOptions | undefined; /** * Sets default options for all functions written using the v2 SDK. - * @param options - Options to set as default + * @param options Options to set as default */ export function setGlobalOptions(options: GlobalOptions) { if (globalOptions) { @@ -239,7 +239,6 @@ export function setGlobalOptions(options: GlobalOptions) { /** * Get the currently set default options. * Used only for trigger generation. - * * @internal */ export function getGlobalOptions(): GlobalOptions { @@ -255,7 +254,6 @@ export interface EventHandlerOptions extends GlobalOptions { /** * Apply GlobalOptions to trigger definitions. - * * @internal */ export function optionsToTriggerAnnotations( @@ -318,7 +316,6 @@ export function optionsToTriggerAnnotations( /** * Apply GlobalOptions to endpoint manifest. - * * @internal */ export function optionsToEndpoint( @@ -362,7 +359,9 @@ export function optionsToEndpoint( return endpoint; } -/* @internal */ +/** + * @hidden + */ export function __getSpec(): { globalOptions: GlobalOptions; params: ParamSpec[]; diff --git a/src/v2/params/index.ts b/src/v2/params/index.ts index 29acab37a..f4ea32b53 100644 --- a/src/v2/params/index.ts +++ b/src/v2/params/index.ts @@ -1,3 +1,4 @@ +/** @hidden */ import { BooleanParam, FloatParam, @@ -9,15 +10,14 @@ import { StringParam, } from './types'; -/* @alpha */ export { ParamOptions }; -/* @alpha */ export const declaredParams: Param[] = []; /** * Use a helper to manage the list such that params are uniquely * registered once only but order is preserved. + * @internal */ function registerParam(param: Param) { for (let i = 0; i < declaredParams.length; i++) { @@ -31,11 +31,9 @@ function registerParam(param: Param) { /** * Declare a string param. * - * @param name - The name of the environment variable to use to load the param. - * @param options - Configuration options for the param. + * @param name The name of the environment variable to use to load the param. + * @param options Configuration options for the param. * @returns A Param with a `string` return type for `.value`. - * - * @alpha */ export function defineString( name: string, @@ -49,11 +47,9 @@ export function defineString( /** * Declare a boolean param. * - * @param name - The name of the environment variable to use to load the param. - * @param options - Configuration options for the param. + * @param name The name of the environment variable to use to load the param. + * @param options Configuration options for the param. * @returns A Param with a `boolean` return type for `.value`. - * - * @alpha */ export function defineBoolean( name: string, @@ -67,11 +63,9 @@ export function defineBoolean( /** * Declare an integer param. * - * @param name - The name of the environment variable to use to load the param. - * @param options - Configuration options for the param. + * @param name The name of the environment variable to use to load the param. + * @param options Configuration options for the param. * @returns A Param with a `number` return type for `.value`. - * - * @alpha */ export function defineInt( name: string, @@ -85,11 +79,9 @@ export function defineInt( /** * Declare a float param. * - * @param name - The name of the environment variable to use to load the param. - * @param options - Configuration options for the param. + * @param name The name of the environment variable to use to load the param. + * @param options Configuration options for the param. * @returns A Param with a `number` return type for `.value`. - * - * @alpha */ export function defineFloat( name: string, @@ -103,11 +95,9 @@ export function defineFloat( /** * Declare a list param (array of strings). * - * @param name - The name of the environment variable to use to load the param. - * @param options - Configuration options for the param. + * @param name The name of the environment variable to use to load the param. + * @param options Configuration options for the param. * @returns A Param with a `string[]` return type for `.value`. - * - * @alpha */ export function defineList( name: string, @@ -122,11 +112,9 @@ export function defineList( * Declare a JSON param. The associated environment variable will be treated * as a JSON string when loading its value. * - * @param name - The name of the environment variable to use to load the param. - * @param options - Configuration options for the param. + * @param name The name of the environment variable to use to load the param. + * @param options Configuration options for the param. * @returns A Param with a specifiable return type for `.value`. - * - * @alpha */ export function defineJSON( name: string, diff --git a/src/v2/params/types.ts b/src/v2/params/types.ts index e0cc3c701..c345cf654 100644 --- a/src/v2/params/types.ts +++ b/src/v2/params/types.ts @@ -1,6 +1,6 @@ +/** @hidden */ type ParamValueType = 'string' | 'list' | 'boolean' | 'int' | 'float' | 'json'; -/* @alpha */ export interface ParamSpec { name: string; default?: T; @@ -9,13 +9,11 @@ export interface ParamSpec { valueType?: ParamValueType; } -/* @alpha */ export type ParamOptions = Omit< ParamSpec, 'name' | 'valueType' >; -/* @alpha */ export class Param { static valueType: ParamValueType = 'string'; @@ -53,12 +51,10 @@ export class Param { } } -/* @alpha */ export class StringParam extends Param { // identical to the abstract class, just explicitly a string } -/* @alpha */ export class IntParam extends Param { static valueType: ParamValueType = 'int'; @@ -78,7 +74,6 @@ export class IntParam extends Param { } } -/* @alpha */ export class FloatParam extends Param { static valueType: ParamValueType = 'float'; @@ -97,7 +92,6 @@ export class FloatParam extends Param { } } -/* @alpha */ export class BooleanParam extends Param { static valueType: ParamValueType = 'boolean'; @@ -120,7 +114,6 @@ export class BooleanParam extends Param { } } -/* @alpha */ export class ListParam extends Param { static valueType: ParamValueType = 'list'; @@ -144,7 +137,6 @@ export class ListParam extends Param { } } -/* @alpha */ export class JSONParam extends Param { static valueType: ParamValueType = 'json'; diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index ee83a734e..df800fadb 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -56,8 +56,8 @@ export interface FirebaseAlertOptions extends options.EventHandlerOptions { /** * Declares a function that can handle Firebase Alerts from CloudEvents. - * @param alertTypeOrOpts - the alert type or Firebase Alert function configuration. - * @param handler - a function that can handle the Firebase Alert inside a CloudEvent. + * @param alertTypeOrOpts the alert type or Firebase Alert function configuration. + * @param handler a function that can handle the Firebase Alert inside a CloudEvent. */ export function onAlertPublished( alertTypeOrOpts: AlertType | FirebaseAlertOptions, diff --git a/src/v2/providers/pubsub.ts b/src/v2/providers/pubsub.ts index abda5f6d8..62a5ba4a1 100644 --- a/src/v2/providers/pubsub.ts +++ b/src/v2/providers/pubsub.ts @@ -6,7 +6,7 @@ import * as options from '../options'; /** * Interface representing a Google Cloud Pub/Sub message. * - * @param data - Payload of a Pub/Sub message. + * @param data Payload of a Pub/Sub message. */ export class Message { /** @@ -34,7 +34,7 @@ export class Message { */ readonly orderingKey: string; - /** @internal */ + /** @hidden */ private _json: T; constructor(data: any) { @@ -68,7 +68,7 @@ export class Message { /** * Returns a JSON-serializable representation of this object. * - * @returns A JSON-serializable representation of this object. + * @return A JSON-serializable representation of this object. */ toJSON(): any { const json: Record = { From e25ddfb2016503936717ad9b06ff4506fcb8fdec Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 5 May 2022 20:01:36 +0000 Subject: [PATCH 082/370] 3.21.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 5b5f7fc85..e2500d647 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.20.1", + "version": "3.21.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 20f8c6ce0..557cd8fa4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.20.1", + "version": "3.21.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 74323273b52ec4cf71e844f54f4c69b06c75262a Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 5 May 2022 20:01:39 +0000 Subject: [PATCH 083/370] [firebase-release] Removed change log and reset repo after 3.21.0 release --- CHANGELOG.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccd110162..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +0,0 @@ -- Adds CPU option and enhances internal data structures (#1077) -- Add auth blocking handlers (#1080) -- Add support for secrets in v2 (#1079) -- Update types for AlertPayloads (#1087) -- Update AppDistribution `[@type]` (#1088) -- Update CloudEvent types (#1089) -- Generate documentation with api-extractor (#1071) -- Change type info to be inheritance friendly. (#1091) -- Changes the memory options from MB to MiB and GB to GiB for greater clarity (#1090) From 5a141758e4be0c08ba3adb997cc914c7d3cfccee Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Fri, 6 May 2022 16:21:35 -0400 Subject: [PATCH 084/370] exporting interfaces and adding tsdoc comments (#1100) --- src/v2/providers/alerts/appDistribution.ts | 4 ++++ src/v2/providers/alerts/crashlytics.ts | 8 ++++++-- src/v2/providers/storage.ts | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index 014b1d937..e22f847c0 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -8,9 +8,13 @@ import { FirebaseAlertData, getEndpointAnnotation } from './alerts'; */ export interface NewTesterDevicePayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.AppDistroNewTesterIosDevicePayload'; + /** Name of the tester */ testerName: string; + /** Email of the tester */ testerEmail: string; + /** The device model name */ testerDeviceModelName: string; + /** The device ID */ testerDeviceIdentifier: string; } diff --git a/src/v2/providers/alerts/crashlytics.ts b/src/v2/providers/alerts/crashlytics.ts index c9c208e57..1cbe13760 100644 --- a/src/v2/providers/alerts/crashlytics.ts +++ b/src/v2/providers/alerts/crashlytics.ts @@ -3,10 +3,14 @@ import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; /** Generic crashlytics issue interface */ -interface Issue { +export interface Issue { + /** The ID of the crashlytics issue */ id: string; + /** The title of the crashlytics issue */ title: string; + /** The subtitle of the crashlytics issue */ subtitle: string; + /** The application version of the crashlytics issue */ appVersion: string; } @@ -48,7 +52,7 @@ export interface RegressionAlertPayload { } /** Generic crashlytics trending issue interface */ -interface TrendingIssueDetails { +export interface TrendingIssueDetails { /** The type of the Crashlytics issue, e.g. new fatal, new nonfatal, ANR */ type: string; /** Basic information of the Crashlytics issue */ diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts index 7c5fe01b1..cc6f0a5ec 100644 --- a/src/v2/providers/storage.ts +++ b/src/v2/providers/storage.ts @@ -174,6 +174,7 @@ export interface CustomerEncryption { keySha256?: string; } +/** A CloudEvent that contains StorageObjectData */ export interface StorageEvent extends CloudEvent { /** The name of the bucket containing this object. */ bucket: string; From 129591cc10c7f0960c3ad2f65671688dda8a359f Mon Sep 17 00:00:00 2001 From: Tyler Stark Date: Mon, 9 May 2022 12:29:38 -0500 Subject: [PATCH 085/370] Combine `npm run build` with `docgen` scripts (#1105) * Combine `npm run build` with `docgen` scripts * Remove duplicate instances of `npm run build` from docgen process * Also ignore the dist/ directory --- .gitignore | 1 + package.json | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index ee22dbd83..017bc9f40 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ .tmp .vscode/ coverage +dist/ docgen/html docgen/*/temp docgen/*/markdown diff --git a/package.json b/package.json index 557cd8fa4..0b2911c92 100644 --- a/package.json +++ b/package.json @@ -159,10 +159,10 @@ "apidocs": "node docgen/generate-docs.js", "docgen:v1:extract": "api-extractor run -c docgen/api-extractor.v1.json --local", "docgen:v1:gen": "api-documenter-fire markdown -i docgen/v1 -o docgen/v1/markdown && api-documenter-fire toc -i docgen/v1 -o docgen/v1/markdown/toc -p /docs/reference/functions", - "docgen:v1": "npm run docgen:v1:extract && npm run docgen:v1:gen", + "docgen:v1": "npm run build && npm run docgen:v1:extract && npm run docgen:v1:gen", "docgen:v2:extract": "api-extractor run -c docgen/api-extractor.v2.json --local", "docgen:v2:gen": "api-documenter-fire markdown -i docgen/v2 -o docgen/v2/markdown && api-documenter-fire toc -i docgen/v2 -o docgen/v2/markdown/toc -p /docs/reference/functions/v2", - "docgen:v2": "npm run docgen:v2:extract && npm run docgen:v2:gen", + "docgen:v2": "npm run build && npm run docgen:v2:extract && npm run docgen:v2:gen", "build:pack": "rm -rf lib && npm install && tsc -p tsconfig.release.json && npm pack", "build:release": "npm ci --production && npm install --no-save typescript firebase-admin && tsc -p tsconfig.release.json", "build": "tsc -p tsconfig.release.json", From 18e7f1636a6b504ffbe85e4a9419f61339406ada Mon Sep 17 00:00:00 2001 From: Tyler Stark Date: Mon, 9 May 2022 15:15:35 -0500 Subject: [PATCH 086/370] Update docs for auth (#1108) * Update docs for auth Update docs for auth --- src/providers/auth.ts | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/providers/auth.ts b/src/providers/auth.ts index 283310472..fcc612e76 100644 --- a/src/providers/auth.ts +++ b/src/providers/auth.ts @@ -54,7 +54,10 @@ export const provider = 'google.firebase.auth'; /** @hidden */ export const service = 'firebaseauth.googleapis.com'; -/** Resource level options */ +/** + * Resource level options + * @public + */ export interface UserOptions { blockingOptions?: { idToken?: boolean; @@ -64,9 +67,12 @@ export interface UserOptions { } /** - * Handle events related to Firebase authentication users. + * Handles events related to Firebase authentication users. + * @param userOptions - Resource level options + * @returns UserBuilder - Builder used to create Cloud Functions for Firebase Auth user lifecycle events + * @public */ -export function user(userOptions?: UserOptions) { +export function user(userOptions?: UserOptions): UserBuilder { return _userWithOptions({}, userOptions || {}); } @@ -87,7 +93,10 @@ export function _userWithOptions( ); } -/** Builder used to create Cloud Functions for Firebase Auth user lifecycle events. */ +/** + * Builder used to create Cloud Functions for Firebase Auth user lifecycle events. + * @public + */ export class UserBuilder { private static dataConstructor(raw: Event): UserRecord { return userRecordConstructor(raw.data); @@ -100,14 +109,20 @@ export class UserBuilder { private userOptions?: UserOptions ) {} - /** Respond to the creation of a Firebase Auth user. */ + /** + * Responds to the creation of a Firebase Auth user. + * @public + */ onCreate( handler: (user: UserRecord, context: EventContext) => PromiseLike | any ): CloudFunction { return this.onOperation(handler, 'user.create'); } - /** Respond to the deletion of a Firebase Auth user. */ + /** + * Responds to the deletion of a Firebase Auth user. + * @public + */ onDelete( handler: (user: UserRecord, context: EventContext) => PromiseLike | any ): CloudFunction { From fd0e372aebaf02850fccf608324092a949f8f697 Mon Sep 17 00:00:00 2001 From: Tyler Stark Date: Mon, 9 May 2022 16:14:42 -0500 Subject: [PATCH 087/370] Update docs for logger (#1103) Update docs for logger --- src/logger/index.ts | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/logger/index.ts b/src/logger/index.ts index deb7c1762..9bb43725f 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -8,6 +8,7 @@ import { /** * `LogSeverity` indicates the detailed severity of the log entry. See [LogSeverity](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity). + * @public */ export type LogSeverity = | 'DEBUG' @@ -23,6 +24,7 @@ export type LogSeverity = * `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. + * @public */ export interface LogEntry { severity: LogSeverity; @@ -62,7 +64,8 @@ function removeCircular(obj: any, refs: any[] = []): any { /** * 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. + * @public */ export function write(entry: LogEntry) { if (SUPPORTS_STRUCTURED_LOGS) { @@ -94,7 +97,8 @@ export function write(entry: LogEntry) { /** * Writes a `DEBUG` severity log. If the last argument provided is a plain object, * it is added to the `jsonPayload` in the Cloud Logging entry. - * @param args Arguments, concatenated into the log message with space separators. + * @param args - Arguments, concatenated into the log message with space separators. + * @public */ export function debug(...args: any[]) { write(entryFromArgs('DEBUG', args)); @@ -103,7 +107,8 @@ export function debug(...args: any[]) { /** * Writes an `INFO` severity log. If the last argument provided is a plain object, * it is added to the `jsonPayload` in the Cloud Logging entry. - * @param args Arguments, concatenated into the log message with space separators. + * @param args - Arguments, concatenated into the log message with space separators. + * @public */ export function log(...args: any[]) { write(entryFromArgs('INFO', args)); @@ -112,7 +117,8 @@ export function log(...args: any[]) { /** * Writes an `INFO` severity log. If the last argument provided is a plain object, * it is added to the `jsonPayload` in the Cloud Logging entry. - * @param args Arguments, concatenated into the log message with space separators. + * @param args - Arguments, concatenated into the log message with space separators. + * @public */ export function info(...args: any[]) { write(entryFromArgs('INFO', args)); @@ -121,7 +127,8 @@ export function info(...args: any[]) { /** * Writes a `WARNING` severity log. If the last argument provided is a plain object, * it is added to the `jsonPayload` in the Cloud Logging entry. - * @param args Arguments, concatenated into the log message with space separators. + * @param args - Arguments, concatenated into the log message with space separators. + * @public */ export function warn(...args: any[]) { write(entryFromArgs('WARNING', args)); @@ -130,7 +137,8 @@ export function warn(...args: any[]) { /** * Writes an `ERROR` severity log. If the last argument provided is a plain object, * it is added to the `jsonPayload` in the Cloud Logging entry. - * @param args Arguments, concatenated into the log message with space separators. + * @param args - Arguments, concatenated into the log message with space separators. + * @public */ export function error(...args: any[]) { write(entryFromArgs('ERROR', args)); From 09b28897bc9477a3fbb507320d65001e56a53b14 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 10 May 2022 07:12:48 +0900 Subject: [PATCH 088/370] Fix toc.yaml for V2 reference docs (#1097) toc.ts is a short script to generate `toc.yaml` based on the generated markdown file. --- docgen/toc.ts | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 4 +- 2 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 docgen/toc.ts diff --git a/docgen/toc.ts b/docgen/toc.ts new file mode 100644 index 000000000..d552a9cba --- /dev/null +++ b/docgen/toc.ts @@ -0,0 +1,124 @@ +/** + * @license + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Forked of https://github.com/firebase/firebase-js-sdk/blob/5ce06766303b92fea969c58172a7c1ab8695e21e/repo-scripts/api-documenter/src/toc.ts. + */ +import { writeFileSync } from 'fs'; +import { resolve } from 'path'; + +import yargs from 'yargs'; +import * as yaml from 'js-yaml'; +import { FileSystem } from '@rushstack/node-core-library'; + +export interface TocGenerationOptions { + inputFolder: string; + outputFolder: string; + g3Path: string; +} + +interface TocItem { + title: string; + path: string; + section?: TocItem[]; + status?: 'experimental'; +} + +function fileExt(f: string) { + const parts = f.split('.'); + if (parts.length < 2) { + return ''; + } + return parts.pop(); +} + +export function generateToc({ + inputFolder, + g3Path, + outputFolder, +}: TocGenerationOptions) { + const asObj = FileSystem.readFolder(inputFolder) + .filter((f) => fileExt(f) === 'md') + .reduce((acc, f) => { + const parts = f.split('.'); + parts.pop(); // Get rid of file extenion (.md) + + let cursor = acc; + for (const p of parts) { + cursor[p] = cursor[p] || {}; + cursor = cursor[p]; + } + return acc; + }, {} as any); + + function toToc(obj, prefix = ''): TocItem[] { + const toc: TocItem[] = []; + for (const key of Object.keys(obj)) { + const path = prefix?.length ? `${prefix}.${key}` : key; + const section = toToc(obj[key], path); + const tic: TocItem = { + title: key, + path: `${g3Path}/${path}.md`, + }; + if (section.length > 0) { + tic.section = section; + } + toc.push(tic); + } + return toc; + } + + const toc: TocItem[] = [ + { + title: 'firebase-functions', + status: 'experimental', + path: `${g3Path}/firebase-functions.md`, + }, + ...toToc(asObj['firebase-functions'], 'firebase-functions'), + ]; + + writeFileSync( + resolve(outputFolder, 'toc.yaml'), + yaml.dump( + { toc }, + { + quotingType: '"', + } + ) + ); +} + +const { input, output, path } = yargs(process.argv.slice(2)) + .option('input', { + alias: 'i', + describe: 'input folder containing the *.api.json files to be processed.', + default: './input', + }) + .option('output', { + alias: 'o', + describe: 'destination for the generated toc content.', + default: './toc', + }) + .option('path', { + alias: 'p', + describe: 'specifies the path where the reference docs resides (e.g. g3)', + default: '/', + }) + .help().argv; + +FileSystem.ensureFolder(output); +generateToc({ inputFolder: input, g3Path: path, outputFolder: output }); diff --git a/package.json b/package.json index 0b2911c92..aca2baeba 100644 --- a/package.json +++ b/package.json @@ -161,7 +161,8 @@ "docgen:v1:gen": "api-documenter-fire markdown -i docgen/v1 -o docgen/v1/markdown && api-documenter-fire toc -i docgen/v1 -o docgen/v1/markdown/toc -p /docs/reference/functions", "docgen:v1": "npm run build && npm run docgen:v1:extract && npm run docgen:v1:gen", "docgen:v2:extract": "api-extractor run -c docgen/api-extractor.v2.json --local", - "docgen:v2:gen": "api-documenter-fire markdown -i docgen/v2 -o docgen/v2/markdown && api-documenter-fire toc -i docgen/v2 -o docgen/v2/markdown/toc -p /docs/reference/functions/v2", + "docgen:v2:toc": "ts-node docgen/toc.ts --input docgen/v2/markdown --output docgen/v2/markdown/toc --path /docs/functions/beta/reference", + "docgen:v2:gen": "api-documenter-fire markdown -i docgen/v2 -o docgen/v2/markdown && npm run docgen:v2:toc", "docgen:v2": "npm run build && npm run docgen:v2:extract && npm run docgen:v2:gen", "build:pack": "rm -rf lib && npm install && tsc -p tsconfig.release.json && npm pack", "build:release": "npm ci --production && npm install --no-save typescript firebase-admin && tsc -p tsconfig.release.json", @@ -196,6 +197,7 @@ "@types/node": "^8.10.50", "@types/node-fetch": "^3.0.3", "@types/sinon": "^7.0.13", + "api-extractor-model-me": "^0.1.1", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "child-process-promise": "^2.2.1", From 27b4c102f8d0c51ccdc9d3e6505411167a8c4aca Mon Sep 17 00:00:00 2001 From: Tyler Stark Date: Tue, 10 May 2022 10:32:17 -0500 Subject: [PATCH 089/370] Update docs for v2 storage (#1104) * Update docs for v2 storage --- src/v2/providers/storage.ts | 174 +++++++++++++++++++++++++++++++++--- 1 file changed, 162 insertions(+), 12 deletions(-) diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts index cc6f0a5ec..27e6d01dc 100644 --- a/src/v2/providers/storage.ts +++ b/src/v2/providers/storage.ts @@ -192,107 +192,257 @@ export const metadataUpdatedEvent = /** StorageOptions extend EventHandlerOptions with a bucket name */ export interface StorageOptions extends options.EventHandlerOptions { + /** The name of the bucket containing this object. */ bucket?: string; } -/** Handle a storage object archived */ +/** + * Event handler sent only when a bucket has enabled object versioning. + * This event indicates that the live version of an object has become an + * archived version, either because it was archived or because it was + * overwritten by the upload of an object of the same name. + * + * @param handler - Event handler which is run every time a Google Cloud Storage archival occurs. + */ export function onObjectArchived( handler: (event: StorageEvent) => any | Promise ): CloudFunction; +/** + * Event handler sent only when a bucket has enabled object versioning. + * This event indicates that the live version of an object has become an + * archived version, either because it was archived or because it was + * overwritten by the upload of an object of the same name. + * + * @param bucket - The name of the bucket containing this object. + * @param handler - Event handler which is run every time a Google Cloud Storage archival occurs. + */ export function onObjectArchived( bucket: string, handler: (event: StorageEvent) => any | Promise ): CloudFunction; +/** + * Event handler sent only when a bucket has enabled object versioning. + * This event indicates that the live version of an object has become an + * archived version, either because it was archived or because it was + * overwritten by the upload of an object of the same name. + * + * @param opts - Options that can be set on an individual event-handling function. + * @param handler - Event handler which is run every time a Google Cloud Storage archival occurs. + */ export function onObjectArchived( opts: StorageOptions, handler: (event: StorageEvent) => any | Promise ): CloudFunction; +/** + * Event handler sent only when a bucket has enabled object versioning. + * This event indicates that the live version of an object has become an + * archived version, either because it was archived or because it was + * overwritten by the upload of an object of the same name. + * + * @param bucketOrOptsOrHandler - Options or string that may (or may not) define the bucket to be used. + * @param handler - Event handler which is run every time a Google Cloud Storage archival occurs. + */ export function onObjectArchived( - buketOrOptsOrHandler: + bucketOrOptsOrHandler: | string | StorageOptions | ((event: StorageEvent) => any | Promise), handler?: (event: StorageEvent) => any | Promise ): CloudFunction { - return onOperation(archivedEvent, buketOrOptsOrHandler, handler); + return onOperation(archivedEvent, bucketOrOptsOrHandler, handler); } -/** Handle a storage object finalized */ +/** + * Event handler which fires every time a Google Cloud Storage object + * creation occurs. + * + * Sent when a new object (or a new generation of an existing object) + * is successfully created in the bucket. This includes copying or rewriting + * an existing object. A failed upload does not trigger this event. + * + * @param handler - Event handler which is run every time a Google Cloud Storage object creation occurs. + */ export function onObjectFinalized( handler: (event: StorageEvent) => any | Promise ): CloudFunction; +/** + * Event handler which fires every time a Google Cloud Storage object + * creation occurs. + * + * Sent when a new object (or a new generation of an existing object) + * is successfully created in the bucket. This includes copying or rewriting + * an existing object. A failed upload does not trigger this event. + * + * @param bucket - The name of the bucket containing this object. + * @param handler - Event handler which is run every time a Google Cloud Storage object creation occurs. + */ export function onObjectFinalized( bucket: string, handler: (event: StorageEvent) => any | Promise ): CloudFunction; +/** + * Event handler which fires every time a Google Cloud Storage object + * creation occurs. + * + * Sent when a new object (or a new generation of an existing object) + * is successfully created in the bucket. This includes copying or rewriting + * an existing object. A failed upload does not trigger this event. + * + * @param opts - Options that can be set on an individual event-handling function. + * @param handler - Event handler which is run every time a Google Cloud Storage object creation occurs. + */ export function onObjectFinalized( opts: StorageOptions, handler: (event: StorageEvent) => any | Promise ): CloudFunction; +/** + * Event handler which fires every time a Google Cloud Storage object + * creation occurs. + * + * Sent when a new object (or a new generation of an existing object) + * is successfully created in the bucket. This includes copying or rewriting + * an existing object. A failed upload does not trigger this event. + * + * @param bucketOrOptsOrHandler - Options or string that may (or may not) define the bucket to be used. + * @param handler - Event handler which is run every time a Google Cloud Storage object creation occurs. + */ export function onObjectFinalized( - buketOrOptsOrHandler: + bucketOrOptsOrHandler: | string | StorageOptions | ((event: StorageEvent) => any | Promise), handler?: (event: StorageEvent) => any | Promise ): CloudFunction { - return onOperation(finalizedEvent, buketOrOptsOrHandler, handler); + return onOperation(finalizedEvent, bucketOrOptsOrHandler, handler); } -/** Handle a storage object deleted */ +/** + * Event handler which fires every time a Google Cloud Storage deletion occurs. + * + * Sent when an object has been permanently deleted. This includes objects + * that are overwritten or are deleted as part of the bucket's lifecycle + * configuration. For buckets with object versioning enabled, this is not + * sent when an object is archived, even if archival occurs + * via the `storage.objects.delete` method. + * + * @param handler - Event handler which is run every time a Google Cloud Storage object deletion occurs. + */ export function onObjectDeleted( handler: (event: StorageEvent) => any | Promise ): CloudFunction; +/** + * Event handler which fires every time a Google Cloud Storage deletion occurs. + * + * Sent when an object has been permanently deleted. This includes objects + * that are overwritten or are deleted as part of the bucket's lifecycle + * configuration. For buckets with object versioning enabled, this is not + * sent when an object is archived, even if archival occurs + * via the `storage.objects.delete` method. + * + * @param bucket - The name of the bucket containing this object. + * @param handler - Event handler which is run every time a Google Cloud Storage object deletion occurs. + */ export function onObjectDeleted( bucket: string, handler: (event: StorageEvent) => any | Promise ): CloudFunction; +/** + * Event handler which fires every time a Google Cloud Storage deletion occurs. + * + * Sent when an object has been permanently deleted. This includes objects + * that are overwritten or are deleted as part of the bucket's lifecycle + * configuration. For buckets with object versioning enabled, this is not + * sent when an object is archived, even if archival occurs + * via the `storage.objects.delete` method. + * + * @param opts - Options that can be set on an individual event-handling function. + * @param handler - Event handler which is run every time a Google Cloud Storage object deletion occurs. + */ export function onObjectDeleted( opts: StorageOptions, handler: (event: StorageEvent) => any | Promise ): CloudFunction; +/** + * Event handler which fires every time a Google Cloud Storage deletion occurs. + * + * Sent when an object has been permanently deleted. This includes objects + * that are overwritten or are deleted as part of the bucket's lifecycle + * configuration. For buckets with object versioning enabled, this is not + * sent when an object is archived, even if archival occurs + * via the `storage.objects.delete` method. + * + * @param bucketOrOptsOrHandler - Options or string that may (or may not) define the bucket to be used. + * @param handler - Event handler which is run every time a Google Cloud Storage object deletion occurs. + */ export function onObjectDeleted( - buketOrOptsOrHandler: + bucketOrOptsOrHandler: | string | StorageOptions | ((event: StorageEvent) => any | Promise), handler?: (event: StorageEvent) => any | Promise ): CloudFunction { - return onOperation(deletedEvent, buketOrOptsOrHandler, handler); + return onOperation(deletedEvent, bucketOrOptsOrHandler, handler); } -/** Handle a storage object metadata updated */ +/** + * Event handler which fires every time the metadata of an existing object + * changes. + * + * @param bucketOrOptsOrHandler - Options or string that may (or may not) define the bucket to be used. + * @param handler - Event handler which is run every time a Google Cloud Storage object metadata update occurs. + */ export function onObjectMetadataUpdated( handler: (event: StorageEvent) => any | Promise ): CloudFunction; +/** + * Event handler which fires every time the metadata of an existing object + * changes. + * + * @param bucket - The name of the bucket containing this object. + * @param handler - Event handler which is run every time a Google Cloud Storage object metadata update occurs. + */ export function onObjectMetadataUpdated( bucket: string, handler: (event: StorageEvent) => any | Promise ): CloudFunction; +/** + * Event handler which fires every time the metadata of an existing object + * changes. + * + * @param opts - Options that can be set on an individual event-handling function. + * @param handler - Event handler which is run every time a Google Cloud Storage object metadata update occurs. + */ export function onObjectMetadataUpdated( opts: StorageOptions, handler: (event: StorageEvent) => any | Promise ): CloudFunction; +/** + * Event handler which fires every time the metadata of an existing object + * changes. + * + * @param bucketOrOptsOrHandler - Options or string that may (or may not) define the bucket to be used. + * @param handler - Event handler which is run every time a Google Cloud Storage object metadata update occurs. + */ export function onObjectMetadataUpdated( - buketOrOptsOrHandler: + bucketOrOptsOrHandler: | string | StorageOptions | ((event: StorageEvent) => any | Promise), handler?: (event: StorageEvent) => any | Promise ): CloudFunction { - return onOperation(metadataUpdatedEvent, buketOrOptsOrHandler, handler); + return onOperation(metadataUpdatedEvent, bucketOrOptsOrHandler, handler); } /** @internal */ From 2164615a296463b128897446e6d2ab64e2212a57 Mon Sep 17 00:00:00 2001 From: Tyler Stark Date: Tue, 10 May 2022 10:39:51 -0500 Subject: [PATCH 090/370] Update pubsub docs (#1106) * Update pubsub docs --- src/providers/pubsub.ts | 12 ++++---- src/v2/providers/pubsub.ts | 57 ++++++++++++++++++++++++++++++++------ 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/src/providers/pubsub.ts b/src/providers/pubsub.ts index 9cd88bd86..18517bee0 100644 --- a/src/providers/pubsub.ts +++ b/src/providers/pubsub.ts @@ -39,8 +39,8 @@ export const service = 'pubsub.googleapis.com'; * Registers a Cloud Function triggered when a Google Cloud Pub/Sub message * is sent to a specified topic. * - * @param topic The Pub/Sub topic to watch for message events. - * @return Pub/Sub topic builder interface. + * @param topic - The Pub/Sub topic to watch for message events. + * @returns Pub/Sub topic builder interface. */ export function topic(topic: string) { return _topicWithOptions(topic, {}); @@ -79,7 +79,7 @@ export class TopicBuilder { * Event handler that fires every time a Cloud Pub/Sub message is * published. * - * @param handler Event handler that runs every time a Cloud Pub/Sub message + * @param handler - Event handler that runs every time a Cloud Pub/Sub message * is published. * @return A Cloud Function that you can export and deploy. */ @@ -101,7 +101,7 @@ export class TopicBuilder { /** * Registers a Cloud Function to run at specified times. * - * @param schedule The schedule, in Unix Crontab or AppEngine syntax. + * @param schedule - The schedule, in Unix Crontab or AppEngine syntax. * @return ScheduleBuilder interface. */ export function schedule(schedule: string): ScheduleBuilder { @@ -156,7 +156,7 @@ export class ScheduleBuilder { * Event handler for scheduled functions. Triggered whenever the associated * scheduler job sends a Pub/Sub message. * - * @param handler Handler that fires whenever the associated + * @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. */ @@ -177,7 +177,7 @@ export class ScheduleBuilder { /** * Interface representing a Google Cloud Pub/Sub message. * - * @param data Payload of a Pub/Sub message. + * @param data - Payload of a Pub/Sub message. */ export class Message { /** diff --git a/src/v2/providers/pubsub.ts b/src/v2/providers/pubsub.ts index 62a5ba4a1..137d0fd73 100644 --- a/src/v2/providers/pubsub.ts +++ b/src/v2/providers/pubsub.ts @@ -3,10 +3,27 @@ import { ManifestEndpoint } from '../../runtime/manifest'; import { CloudEvent, CloudFunction } from '../core'; import * as options from '../options'; +/** + * A PubSub Topic is: + *
    + *
  • A resource that you can publish messages to and then consume those messages via subscriptions. + *
  • An isolated data stream for Pub/Sub messages. + *
  • Messages are published to a topic. + *
  • Messages are listened to via a subscription. + *
  • Each subscription listens to the messages published to exactly one topic. + */ +export type PubSubTopic = string; + +/** + * Resource that listens to the messages published by exactly one topic. + */ +export type PubSubSubscription = string; + /** * Interface representing a Google Cloud Pub/Sub message. * - * @param data Payload of a Pub/Sub message. + * @param data - Payload of a Pub/Sub message. + * @typeParam T - Type representing `Message.data`'s JSON format */ export class Message { /** @@ -68,7 +85,7 @@ export class Message { /** * Returns a JSON-serializable representation of this object. * - * @return A JSON-serializable representation of this object. + * @returns A JSON-serializable representation of this object. */ toJSON(): any { const json: Record = { @@ -86,29 +103,51 @@ export class Message { } } -/** The interface published in a Pub/Sub publish subscription. */ +/** + * The interface published in a Pub/Sub publish subscription. + * @typeParam T - Type representing `Message.data`'s JSON format + */ export interface MessagePublishedData { + /** Google Cloud Pub/Sub message. */ readonly message: Message; - readonly subscription: string; + /** A subscription resource. */ + readonly subscription: PubSubSubscription; } /** PubSubOptions extend EventHandlerOptions but must include a topic. */ export interface PubSubOptions extends options.EventHandlerOptions { - topic: string; + /** The Pub/Sub topic to watch for message events */ + topic: PubSubTopic; } -/** Handle a message being published to a Pub/Sub topic. */ +/** + * Handle a message being published to a Pub/Sub topic. + * @param topic - The Pub/Sub topic to watch for message events. + * @param handler - runs every time a Cloud Pub/Sub message is published + * @typeParam T - Type representing `Message.data`'s JSON format + */ export function onMessagePublished( - topic: string, + topic: PubSubTopic, handler: (event: CloudEvent>) => any | Promise ): CloudFunction>>; -/** Handle a message being published to a Pub/Sub topic. */ +/** + * Handle a message being published to a Pub/Sub topic. + * @param options - Option containing information (topic) for event + * @param handler - runs every time a Cloud Pub/Sub message is published + * @typeParam T - Type representing `Message.data`'s JSON format + */ export function onMessagePublished( options: PubSubOptions, handler: (event: CloudEvent>) => any | Promise ): CloudFunction>>; +/** + * Handle a message being published to a Pub/Sub topic. + * @param topicOrOptions - A string representing the PubSub topic or an option (which contains the topic) + * @param handler - runs every time a Cloud Pub/Sub message is published + * @typeParam T - Type representing `Message.data`'s JSON format + */ export function onMessagePublished( topicOrOptions: string | PubSubOptions, handler: (event: CloudEvent>) => any | Promise @@ -127,7 +166,7 @@ export function onMessagePublished( const func = (raw: CloudEvent) => { const messagePublishedData = raw.data as { message: unknown; - subscription: string; + subscription: PubSubSubscription; }; messagePublishedData.message = new Message(messagePublishedData.message); return handler(raw as CloudEvent>); From bba43ab576948bdccd074a8000ccc1dfbfb539e1 Mon Sep 17 00:00:00 2001 From: Tyler Stark Date: Tue, 10 May 2022 10:45:13 -0500 Subject: [PATCH 091/370] Update docs for identity (#1107) * Update docs for identity --- src/v2/providers/identity.ts | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/v2/providers/identity.ts b/src/v2/providers/identity.ts index 798a39212..e59db6590 100644 --- a/src/v2/providers/identity.ts +++ b/src/v2/providers/identity.ts @@ -11,7 +11,7 @@ import * as options from '../options'; export { HttpsError }; -/** Internally used when parsing the options. */ +/** @hidden Internally used when parsing the options. */ interface InternalOptions { opts: options.GlobalOptions; idToken: boolean; @@ -29,7 +29,8 @@ export interface BlockingOptions extends options.GlobalOptions { } /** - * Handle an event that is triggered before a user is created. + * Handles an event that is triggered before a user is created. + * @param handler - Event handler which is run every time before a user is created */ export function beforeUserCreated( handler: ( @@ -40,6 +41,12 @@ export function beforeUserCreated( | void | Promise ): BlockingFunction; + +/** + * Handles an event that is triggered before a user is created. + * @param opts - Object containing function options + * @param handler - Event handler which is run every time before a user is created + */ export function beforeUserCreated( opts: BlockingOptions, handler: ( @@ -50,6 +57,12 @@ export function beforeUserCreated( | void | Promise ): BlockingFunction; + +/** + * Handles an event that is triggered before a user is created + * @param optsOrHandler - Either an object containing function options, or an event handler (run before user creation) + * @param handler? - If defined, an event handler which is run every time before a user is created + */ export function beforeUserCreated( optsOrHandler: | BlockingOptions @@ -72,7 +85,8 @@ export function beforeUserCreated( } /** - * Handle an event that is triggered before a user is signed in. + * Handles an event that is triggered before a user is signed in. + * @param handler - Event handler which is run every time before a user is signed in */ export function beforeUserSignedIn( handler: ( @@ -83,6 +97,12 @@ export function beforeUserSignedIn( | void | Promise ): BlockingFunction; + +/** + * Handles an event that is triggered before a user is signed in. + * @param opts - Object containing function options + * @param handler - Event handler which is run every time before a user is signed in + */ export function beforeUserSignedIn( opts: BlockingOptions, handler: ( @@ -93,6 +113,12 @@ export function beforeUserSignedIn( | void | Promise ): BlockingFunction; + +/** + * Handles an event that is triggered before a user is signed in. + * @param optsOrHandler - Either an object containing function options, or an event handler (run before user signin) + * @param handler - Event handler which is run every time before a user is signed in + */ export function beforeUserSignedIn( optsOrHandler: | BlockingOptions @@ -114,7 +140,7 @@ export function beforeUserSignedIn( return beforeOperation('beforeSignIn', optsOrHandler, handler); } -/** @internal */ +/** @hidden */ export function beforeOperation( eventType: AuthBlockingEventType, optsOrHandler: @@ -197,7 +223,7 @@ export function beforeOperation( return func; } -/** @internal */ +/** @hidden */ export function getOpts(blockingOptions: BlockingOptions): InternalOptions { const accessToken = blockingOptions.accessToken || false; const idToken = blockingOptions.idToken || false; From 4825fb4fa0abdc33120a94b8c691a79f80234908 Mon Sep 17 00:00:00 2001 From: joehan Date: Tue, 10 May 2022 09:01:34 -0700 Subject: [PATCH 092/370] Docs review for v2.https (#1109) * Docs review for v2.https * PR fixes * pr feedback * pr feedback --- src/common/providers/https.ts | 54 ++++++++++++++++++++++++----------- src/v2/providers/https.ts | 46 +++++++++++++++++++++++++++-- 2 files changed, 81 insertions(+), 19 deletions(-) diff --git a/src/common/providers/https.ts b/src/common/providers/https.ts index 29f516bc7..b3bed0e6d 100644 --- a/src/common/providers/https.ts +++ b/src/common/providers/https.ts @@ -36,6 +36,7 @@ const JWT_REGEX = /^[a-zA-Z0-9\-_=]+?\.[a-zA-Z0-9\-_=]+?\.([a-zA-Z0-9\-_=]+)?$/; /** @hidden */ export interface Request extends express.Request { + /** The wire format representation of the request body. */ rawBody: Buffer; } @@ -176,37 +177,53 @@ export interface CallableRequest { * https://github.com/grpc/grpc/blob/master/doc/statuscodes.md * * Possible values: - * - 'cancelled': The operation was cancelled (typically by the caller). - * - 'unknown': Unknown error or an error from a different error domain. - * - 'invalid-argument': Client specified an invalid argument. Note that this - * differs from 'failed-precondition'. 'invalid-argument' indicates + * + * - `cancelled`: The operation was cancelled (typically by the caller). + * + * - `unknown`: Unknown error or an error from a different error domain. + * + * - `invalid-argument`: Client specified an invalid argument. Note that this + * differs from `failed-precondition`. `invalid-argument` indicates * arguments that are problematic regardless of the state of the system * (e.g. an invalid field name). - * - 'deadline-exceeded': Deadline expired before operation could complete. + * + * - `deadline-exceeded`: Deadline expired before operation could complete. * For operations that change the state of the system, this error may be * returned even if the operation has completed successfully. For example, * a successful response from a server could have been delayed long enough * for the deadline to expire. - * - 'not-found': Some requested document was not found. - * - 'already-exists': Some document that we attempted to create already + * + * - `not-found`: Some requested document was not found. + * + * - `already-exists`: Some document that we attempted to create already * exists. - * - 'permission-denied': The caller does not have permission to execute the + * + * - `permission-denied`: The caller does not have permission to execute the * specified operation. - * - 'resource-exhausted': Some resource has been exhausted, perhaps a + * + * - `resource-exhausted`: Some resource has been exhausted, perhaps a * per-user quota, or perhaps the entire file system is out of space. - * - 'failed-precondition': Operation was rejected because the system is not + * + * - `failed-precondition`: Operation was rejected because the system is not * in a state required for the operation's execution. - * - 'aborted': The operation was aborted, typically due to a concurrency + * + * - `aborted`: The operation was aborted, typically due to a concurrency * issue like transaction aborts, etc. - * - 'out-of-range': Operation was attempted past the valid range. - * - 'unimplemented': Operation is not implemented or not supported/enabled. - * - 'internal': Internal errors. Means some invariants expected by + * + * - `out-of-range`: Operation was attempted past the valid range. + * + * - `unimplemented`: Operation is not implemented or not supported/enabled. + * + * - `internal`: Internal errors. Means some invariants expected by * underlying system has been broken. If you see one of these errors, * something is very broken. - * - 'unavailable': The service is currently unavailable. This is most likely + * + * - `unavailable`: The service is currently unavailable. This is most likely * a transient condition and may be corrected by retrying with a backoff. - * - 'data-loss': Unrecoverable data loss or corruption. - * - 'unauthenticated': The request does not have valid authentication + * + * - `data-loss`: Unrecoverable data loss or corruption. + * + * - `unauthenticated`: The request does not have valid authentication * credentials for the operation. */ export type FunctionsErrorCode = @@ -326,6 +343,9 @@ export class HttpsError extends Error { this.httpErrorCode = errorCodeMap[code]; } + /** + * Returns a JSON-serializable representation of this object. + */ public toJSON(): HttpErrorWireFormat { const { details, diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 3cd095e94..12e4063a0 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -38,24 +38,50 @@ import { GlobalOptions, SupportedRegion } from '../options'; export { Request, CallableRequest, FunctionsErrorCode, HttpsError }; /** - * Options that can be set on an individual HTTPS Cloud Function. + * Options that can be set on an individual HTTPS function. */ export interface HttpsOptions extends Omit { - /* HTTP functions can override and specify more than one regions. */ + /** HTTP functions can override global options and can specify multiple regions to deploy to. */ region?: SupportedRegion | string | Array; + /** If true, allows CORS on requests to this function. + * If this is a `string` or `RegExp`, allows requests from domains that match the provided value. + * If this is an `Array`, allows requests from domains matching at least one entry of the array. + * Defaults to true for {@link CallableFunction}s and false otherwise. + */ cors?: string | boolean | RegExp | Array; } +/** + * Handles HTTPS requests. + */ export type HttpsFunction = (( + /** An Express request object representing the HTTPS call to the function. */ req: Request, + /** An Express response object, for this function to respond to callers. */ res: express.Response ) => void | Promise) & { + /** @alpha */ __trigger?: unknown; + /** @alpha */ __endpoint: ManifestEndpoint; }; + +/** + * Creates a callable method for clients to call using a Firebase SDK. + */ export interface CallableFunction extends HttpsFunction { + /** Executes the handler function with the provided data as input. Used for unit testing. + * @param data - An input for the handler function. + * @returns The output of the handler function. + */ run(data: CallableRequest): Return; } +/** + * Handles HTTPS requests. + * @param opts - Options to set on this function + * @param handler - A function that takes a {@link Request} and response object, same signature as an Express app. + * @returns A function that you can export and deploy. + */ export function onRequest( opts: HttpsOptions, handler: ( @@ -63,6 +89,11 @@ export function onRequest( response: express.Response ) => void | Promise ): HttpsFunction; +/** + * Handles HTTPS requests. + * @param handler - A function that takes a {@link Request} and response object, same signature as an Express app. + * @returns A function that you can export and deploy. + */ export function onRequest( handler: ( request: Request, @@ -160,10 +191,21 @@ export function onRequest( return handler as HttpsFunction; } +/** + * Declares a callable method for clients to call using a Firebase SDK. + * @param opts - Options to set on this function. + * @param handler - A function that takes a {@link CallableRequest}. + * @returns A function that you can export and deploy. + */ export function onCall>( opts: HttpsOptions, handler: (request: CallableRequest) => Return ): CallableFunction; +/** + * Declares a callable method for clients to call using a Firebase SDK. + * @param handler - A function that takes a {@link CallableRequest}. + * @returns A function that you can export and deploy. + */ export function onCall>( handler: (request: CallableRequest) => Return ): CallableFunction; From 8d45c40ce66a8aefbf28d43e9154b1955f3b61a2 Mon Sep 17 00:00:00 2001 From: joehan Date: Tue, 10 May 2022 09:03:20 -0700 Subject: [PATCH 093/370] Docs pass for v2.eventarc (#1110) * Docs pass for v2.eventarc * formats * Style pass * PR feedback --- src/v2/providers/eventarc.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/v2/providers/eventarc.ts b/src/v2/providers/eventarc.ts index fd56e714f..17750d34a 100644 --- a/src/v2/providers/eventarc.ts +++ b/src/v2/providers/eventarc.ts @@ -28,7 +28,7 @@ import * as options from '../options'; /** Options that can be set on an Eventarc trigger. */ export interface EventarcTriggerOptions extends options.EventHandlerOptions { /** - * Type of the event. + * Type of the event to trigger on. */ eventType: string; @@ -54,14 +54,26 @@ export interface EventarcTriggerOptions extends options.EventHandlerOptions { filters?: Record; } +/** + * A function that takes a {@link CloudEvent}. + */ export type CloudEventHandler = (event: CloudEvent) => any | Promise; -/** Handle an Eventarc event published on the default channel. */ +/** Handles an Eventarc event published on the default channel. + * @param eventType - Type of the event to trigger on. + * @param handler - A function to execute when triggered. + * @returns A function that you can export and deploy. + */ export function onCustomEventPublished( eventType: string, handler: CloudEventHandler ): CloudFunction>; +/** Handles an Eventarc event. + * @param opts - Options to set on this function + * @param handler - A function to execute when triggered. + * @returns A function that you can export and deploy. + */ export function onCustomEventPublished( opts: EventarcTriggerOptions, handler: CloudEventHandler From ff4fc3f88e8d35115e8f47fcf81224385d73ff0c Mon Sep 17 00:00:00 2001 From: Tyler Stark Date: Tue, 10 May 2022 12:16:58 -0500 Subject: [PATCH 094/370] Update docs for pubsub to show `string` for topic + subscription (#1114) The generated docs show the typing as `PubSubTopic` and `PubSubSubscription`. But to the end user they just need to know its a string. --- src/v2/providers/pubsub.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/v2/providers/pubsub.ts b/src/v2/providers/pubsub.ts index 137d0fd73..bfcd8c699 100644 --- a/src/v2/providers/pubsub.ts +++ b/src/v2/providers/pubsub.ts @@ -12,12 +12,12 @@ import * as options from '../options'; *
  • Messages are listened to via a subscription. *
  • Each subscription listens to the messages published to exactly one topic. */ -export type PubSubTopic = string; +export type Topic = string; /** * Resource that listens to the messages published by exactly one topic. */ -export type PubSubSubscription = string; +export type Subscription = string; /** * Interface representing a Google Cloud Pub/Sub message. @@ -111,13 +111,13 @@ export interface MessagePublishedData { /** Google Cloud Pub/Sub message. */ readonly message: Message; /** A subscription resource. */ - readonly subscription: PubSubSubscription; + readonly subscription: string; } /** PubSubOptions extend EventHandlerOptions but must include a topic. */ export interface PubSubOptions extends options.EventHandlerOptions { /** The Pub/Sub topic to watch for message events */ - topic: PubSubTopic; + topic: string; } /** @@ -127,7 +127,7 @@ export interface PubSubOptions extends options.EventHandlerOptions { * @typeParam T - Type representing `Message.data`'s JSON format */ export function onMessagePublished( - topic: PubSubTopic, + topic: string, handler: (event: CloudEvent>) => any | Promise ): CloudFunction>>; @@ -166,7 +166,7 @@ export function onMessagePublished( const func = (raw: CloudEvent) => { const messagePublishedData = raw.data as { message: unknown; - subscription: PubSubSubscription; + subscription: string; }; messagePublishedData.message = new Message(messagePublishedData.message); return handler(raw as CloudEvent>); From c5a0fb5007ccfa5bff9e7ba7b8017cf28dec24a7 Mon Sep 17 00:00:00 2001 From: joehan Date: Tue, 10 May 2022 10:30:43 -0700 Subject: [PATCH 095/370] Fixing broken @link tags for https docs (#1115) --- src/v2/providers/https.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 12e4063a0..d4d3d07c7 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -46,7 +46,7 @@ export interface HttpsOptions extends Omit { /** If true, allows CORS on requests to this function. * If this is a `string` or `RegExp`, allows requests from domains that match the provided value. * If this is an `Array`, allows requests from domains matching at least one entry of the array. - * Defaults to true for {@link CallableFunction}s and false otherwise. + * Defaults to true for {@link https.CallableFunction} and false otherwise. */ cors?: string | boolean | RegExp | Array; } @@ -79,7 +79,7 @@ export interface CallableFunction extends HttpsFunction { /** * Handles HTTPS requests. * @param opts - Options to set on this function - * @param handler - A function that takes a {@link Request} and response object, same signature as an Express app. + * @param handler - A function that takes a {@link https.Request} and response object, same signature as an Express app. * @returns A function that you can export and deploy. */ export function onRequest( @@ -91,7 +91,7 @@ export function onRequest( ): HttpsFunction; /** * Handles HTTPS requests. - * @param handler - A function that takes a {@link Request} and response object, same signature as an Express app. + * @param handler - A function that takes a {@link https.Request} and response object, same signature as an Express app. * @returns A function that you can export and deploy. */ export function onRequest( @@ -194,7 +194,7 @@ export function onRequest( /** * Declares a callable method for clients to call using a Firebase SDK. * @param opts - Options to set on this function. - * @param handler - A function that takes a {@link CallableRequest}. + * @param handler - A function that takes a {@link https.CallableRequest}. * @returns A function that you can export and deploy. */ export function onCall>( @@ -203,7 +203,7 @@ export function onCall>( ): CallableFunction; /** * Declares a callable method for clients to call using a Firebase SDK. - * @param handler - A function that takes a {@link CallableRequest}. + * @param handler - A function that takes a {@link https.CallableRequest}. * @returns A function that you can export and deploy. */ export function onCall>( From 67094673da5b5add0c3eb224736fddc15a786050 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Tue, 10 May 2022 11:20:08 -0700 Subject: [PATCH 096/370] Update docs for root namespace (#1101) * Attempted fix; not sure why some things aren't showing up * Update docs; pull params namespace * Don't try to use bulleted list in field comments; they don't render * Yank @beta tags; run formatter * PR feedback --- src/v2/core.ts | 20 +++++++++- src/v2/index.ts | 23 +++++------ src/v2/options.ts | 98 +++++++++++++++-------------------------------- 3 files changed, 60 insertions(+), 81 deletions(-) diff --git a/src/v2/core.ts b/src/v2/core.ts index 54d61ff1c..533349b55 100644 --- a/src/v2/core.ts +++ b/src/v2/core.ts @@ -56,6 +56,8 @@ export interface TriggerAnnotation { /** * A CloudEventBase is the base of a cross-platform format for encoding a serverless event. * More information can be found in https://github.com/cloudevents/spec + * @typeParam T - The type of the event data. + * @beta */ export interface CloudEvent { /** Version of the CloudEvents spec for this event. */ @@ -80,12 +82,28 @@ export interface CloudEvent { data: T; } -/** A handler for CloudEvents. */ +/** + * A handler for CloudEvents. + * @typeParam EventType - The kind of event this function handles. + * Always a subclass of CloudEvent<> + * @beta + */ export interface CloudFunction> { (raw: CloudEvent): any | Promise; + /** @alpha */ __trigger?: unknown; + /** @alpha */ __endpoint: ManifestEndpoint; + /** + * The callback passed to the CloudFunction constructor. + * Use run to test a CloudFunction + * @param event - The parsed event to handle. + * @returns Any return value. Google Cloud Functions awaits any promise + * before shutting down your function. Resolved return values + * are only used for unit testing purposes. + * @beta + */ run(event: EventType): any | Promise; } diff --git a/src/v2/index.ts b/src/v2/index.ts index 31070b4dc..fd89e52fa 100644 --- a/src/v2/index.ts +++ b/src/v2/index.ts @@ -21,7 +21,6 @@ // SOFTWARE. import * as logger from '../logger'; -import * as params from './params'; import * as alerts from './providers/alerts'; import * as eventarc from './providers/eventarc'; import * as https from './providers/https'; @@ -30,18 +29,16 @@ import * as pubsub from './providers/pubsub'; import * as storage from './providers/storage'; import * as tasks from './providers/tasks'; -export { - alerts, - https, - identity, - pubsub, - storage, - logger, - params, - tasks, - eventarc, -}; +export { alerts, https, identity, pubsub, storage, logger, tasks, eventarc }; -export { setGlobalOptions, GlobalOptions } from './options'; +export { + setGlobalOptions, + GlobalOptions, + SupportedRegion, + MemoryOption, + VpcEgressSetting, + IngressSetting, + EventHandlerOptions, +} from './options'; export { CloudFunction, CloudEvent } from './core'; diff --git a/src/v2/options.ts b/src/v2/options.ts index c4e3cd7de..d3155f3c3 100644 --- a/src/v2/options.ts +++ b/src/v2/options.ts @@ -36,55 +36,28 @@ import { HttpsOptions } from './providers/https'; /** * List of all regions supported by Cloud Functions v2 */ -export const SUPPORTED_REGIONS = [ - 'asia-northeast1', - 'europe-north1', - 'europe-west1', - 'europe-west4', - 'us-central1', - 'us-east1', - 'us-west1', -] as const; - -/** - * A region known to be supported by CloudFunctions v2 - */ -export type SupportedRegion = typeof SUPPORTED_REGIONS[number]; - -/** - * Cloud Functions v2 min timeout value. - */ -export const MIN_TIMEOUT_SECONDS = 1; - -/** - * Cloud Functions v2 max timeout value for event handlers. - */ -export const MAX_EVENT_TIMEOUT_SECONDS = 540; - -/** - * Cloud Functions v2 max timeout for HTTPS functions. - */ -export const MAX_HTTPS_TIMEOUT_SECONDS = 36_000; - -/** - * Maximum number of requests to serve on a single instance. - */ -export const MAX_CONCURRENCY = 1_000; +export type SupportedRegion = + | 'asia-northeast1' + | 'europe-north1' + | 'europe-west1' + | 'europe-west4' + | 'us-central1' + | 'us-east1' + | 'us-west1'; /** * List of available memory options supported by Cloud Functions. */ -export const SUPPORTED_MEMORY_OPTIONS = [ - '128MiB', - '256MiB', - '512MiB', - '1GiB', - '2GiB', - '4GiB', - '8GiB', - '16GiB', - '32GiB', -] as const; +export type MemoryOption = + | '128MiB' + | '256MiB' + | '512MiB' + | '1GiB' + | '2GiB' + | '4GiB' + | '8GiB' + | '16GiB' + | '32GiB'; const MemoryOptionToMB: Record = { '128MiB': 128, @@ -98,34 +71,18 @@ const MemoryOptionToMB: Record = { '32GiB': 32768, }; -/** - * A supported memory option. - */ -export type MemoryOption = typeof SUPPORTED_MEMORY_OPTIONS[number]; - /** * List of available options for VpcConnectorEgressSettings. */ -export const SUPPORTED_VPC_EGRESS_SETTINGS = [ - 'PRIVATE_RANGES_ONLY', - 'ALL_TRAFFIC', -] as const; - -/** - * A valid VPC Egress setting. - */ -export type VpcEgressSetting = typeof SUPPORTED_VPC_EGRESS_SETTINGS[number]; +export type VpcEgressSetting = 'PRIVATE_RANGES_ONLY' | 'ALL_TRAFFIC'; /** * List of available options for IngressSettings. */ -export const SUPPORTED_INGRESS_SETTINGS = [ - 'ALLOW_ALL', - 'ALLOW_INTERNAL_ONLY', - 'ALLOW_INTERNAL_AND_GCLB', -] as const; - -export type IngressSetting = typeof SUPPORTED_INGRESS_SETTINGS[number]; +export type IngressSetting = + | 'ALLOW_ALL' + | 'ALLOW_INTERNAL_ONLY' + | 'ALLOW_INTERNAL_AND_GCLB'; /** * GlobalOptions are options that can be set across an entire project. @@ -148,6 +105,11 @@ export interface GlobalOptions { * Timeout for the function in sections, possible values are 0 to 540. * HTTPS functions can specify a higher timeout. * A value of null restores the default of 60s + * The minimum timeout for a gen 2 function is 1s. The maximum timeout for a + * function depends on the type of function: Event handling functions have a + * maximum timeout of 540s (9 minutes). HTTPS and callable functions have a + * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum + * timeout of 1,800s (30 minutes) */ timeoutSeconds?: number | null; @@ -170,6 +132,7 @@ export interface GlobalOptions { * Can only be applied to functions running on Cloud Functions v2. * A value of null restores the default concurrency (80 when CPU >= 1, 1 otherwise). * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. + * The maximum value for concurrency is 1,000. */ concurrency?: number | null; @@ -246,7 +209,7 @@ export function getGlobalOptions(): GlobalOptions { } /** - * Options that can be set on an individual event-handling Cloud Function. + * Additional fields that can be set on any event-handling Cloud Function. */ export interface EventHandlerOptions extends GlobalOptions { retry?: boolean; @@ -361,6 +324,7 @@ export function optionsToEndpoint( /** * @hidden + * @alpha */ export function __getSpec(): { globalOptions: GlobalOptions; From 92aa571f1fde27d8ad2778568c698fe30c517399 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Tue, 10 May 2022 11:32:10 -0700 Subject: [PATCH 097/370] Move some types to interfaces; inline interfaces to make fields private (#1113) * Move some types to interfaces; inline interfaces to make fields private * Run formatter --- docgen/toc.ts | 4 +- src/cloud-functions.ts | 104 ++++++++++++++++++------------- src/common/providers/identity.ts | 5 +- 3 files changed, 66 insertions(+), 47 deletions(-) diff --git a/docgen/toc.ts b/docgen/toc.ts index d552a9cba..24cc58092 100644 --- a/docgen/toc.ts +++ b/docgen/toc.ts @@ -21,9 +21,9 @@ import { writeFileSync } from 'fs'; import { resolve } from 'path'; -import yargs from 'yargs'; -import * as yaml from 'js-yaml'; import { FileSystem } from '@rushstack/node-core-library'; +import * as yaml from 'js-yaml'; +import yargs from 'yargs'; export interface TocGenerationOptions { inputFolder: string; diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 1f59f50d8..a362230fb 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -257,45 +257,33 @@ export interface Resource { } /** - * @hidden - * TriggerAnnotated is used internally by the firebase CLI to understand what + * TriggerAnnotion is used internally by the firebase CLI to understand what * type of Cloud Function to deploy. */ -export interface TriggerAnnotated { - __trigger: { - availableMemoryMb?: number; - blockingTrigger?: { - eventType: string; - options?: Record; - }; - eventTrigger?: { - eventType: string; - resource: string; - service: string; - }; - failurePolicy?: FailurePolicy; - httpsTrigger?: { - invoker?: string[]; - }; - labels?: { [key: string]: string }; - regions?: string[]; - schedule?: Schedule; - timeout?: Duration; - vpcConnector?: string; - vpcConnectorEgressSettings?: string; - serviceAccountEmail?: string; - ingressSettings?: string; - secrets?: string[]; +interface TriggerAnnotation { + availableMemoryMb?: number; + blockingTrigger?: { + eventType: string; + options?: Record; }; -} - -/** - * @hidden - * EndpointAnnotated is used to generate the manifest that conforms to the container contract. - */ -export interface EndpointAnnotated { - __endpoint: ManifestEndpoint; - __requiredAPIs?: ManifestRequiredAPI[]; + eventTrigger?: { + eventType: string; + resource: string; + service: string; + }; + failurePolicy?: FailurePolicy; + httpsTrigger?: { + invoker?: string[]; + }; + labels?: { [key: string]: string }; + regions?: string[]; + schedule?: Schedule; + timeout?: Duration; + vpcConnector?: string; + vpcConnectorEgressSettings?: string; + serviceAccountEmail?: string; + ingressSettings?: string; + secrets?: string[]; } /** @@ -315,14 +303,34 @@ export interface Runnable { * [`Response`](https://expressjs.com/en/api.html#res) objects as its only * arguments. */ -export type HttpsFunction = TriggerAnnotated & - EndpointAnnotated & - ((req: Request, resp: Response) => void | Promise); +export interface HttpsFunction { + (req: Request, resp: Response): void | Promise; + + /** @alpha */ + __trigger: TriggerAnnotation; + + /** @alpha */ + __endpoint: ManifestEndpoint; + + /** @alpha */ + __requiredAPIs?: ManifestRequiredAPI[]; +} /** * The Cloud Function type for Blocking triggers. */ -export type BlockingFunction = HttpsFunction; +export interface BlockingFunction { + (req: Request, resp: Response): void | Promise; + + /** @alpha */ + __trigger: TriggerAnnotation; + + /** @alpha */ + __endpoint: ManifestEndpoint; + + /** @alpha */ + __requiredAPIs?: ManifestRequiredAPI[]; +} /** * The Cloud Function type for all non-HTTPS triggers. This should be exported @@ -331,10 +339,18 @@ export type BlockingFunction = HttpsFunction; * This type is a special JavaScript function which takes a templated * `Event` object as its only argument. */ -export type CloudFunction = Runnable & - TriggerAnnotated & - EndpointAnnotated & - ((input: any, context?: any) => PromiseLike | any); +export interface CloudFunction extends Runnable { + (input: any, context?: any): PromiseLike | any; + + /** @alpha */ + __trigger: TriggerAnnotation; + + /** @alpha */ + __endpoint: ManifestEndpoint; + + /** @alpha */ + __requiredAPIs?: ManifestRequiredAPI[]; +} /** @hidden */ export interface MakeCloudFunctionArgs { diff --git a/src/common/providers/identity.ts b/src/common/providers/identity.ts index 6912b9d1b..031e36f97 100644 --- a/src/common/providers/identity.ts +++ b/src/common/providers/identity.ts @@ -50,7 +50,10 @@ const DISALLOWED_CUSTOM_CLAIMS = [ const CLAIMS_MAX_PAYLOAD_SIZE = 1000; -/** Shorthand auth blocking events from GCIP. */ +/** + * Shorthand auth blocking events from GCIP. + * @internal + */ export type AuthBlockingEventType = 'beforeCreate' | 'beforeSignIn'; const EVENT_MAPPING: Record = { From 2e976034da1d2aede54fb7edb4bcb8ee37bb73a5 Mon Sep 17 00:00:00 2001 From: Tyler Stark Date: Tue, 10 May 2022 14:11:32 -0500 Subject: [PATCH 098/370] Updated PubSub docs, removing unused types + top-level comments (#1117) * Updated PubSub docs, removing unused types and included top-level comments --- src/v2/providers/pubsub.ts | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/v2/providers/pubsub.ts b/src/v2/providers/pubsub.ts index bfcd8c699..e5321ef70 100644 --- a/src/v2/providers/pubsub.ts +++ b/src/v2/providers/pubsub.ts @@ -4,20 +4,25 @@ import { CloudEvent, CloudFunction } from '../core'; import * as options from '../options'; /** - * A PubSub Topic is: + * Google Cloud Pub/Sub is a globally distributed message bus that automatically scales as you need it. + * You can create a function ({@link onMessagePublished}) that handles pub/sub events by using functions.pubsub. + * + * This function triggers whenever a new pub/sub message is sent to a specific topic. + * You must specify the Pub/Sub topic name that you want to trigger your function, and set the event within the + * onPublish() event handler. + * + * PubSub Topic: *
      *
    • A resource that you can publish messages to and then consume those messages via subscriptions. - *
    • An isolated data stream for Pub/Sub messages. + *
    • An isolated data stream for pub/sub messages. *
    • Messages are published to a topic. *
    • Messages are listened to via a subscription. *
    • Each subscription listens to the messages published to exactly one topic. + * + * Subscriptions - Resource that listens to the messages published by exactly one topic. + * + * [More info here](https://firebase.google.com/docs/functions/pubsub-events) */ -export type Topic = string; - -/** - * Resource that listens to the messages published by exactly one topic. - */ -export type Subscription = string; /** * Interface representing a Google Cloud Pub/Sub message. From d9536a991573c5382613dc77337a7f05405ec64d Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Tue, 10 May 2022 15:20:26 -0400 Subject: [PATCH 099/370] Update alerts namespace docs (#1111) * updating docs * fixing comments --- src/v2/providers/alerts/alerts.ts | 12 +- src/v2/providers/alerts/appDistribution.ts | 29 +++- src/v2/providers/alerts/billing.ts | 37 +++- src/v2/providers/alerts/crashlytics.ts | 186 ++++++++++++++++++--- 4 files changed, 237 insertions(+), 27 deletions(-) diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index df800fadb..4b260a851 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -4,6 +4,7 @@ import * as options from '../../options'; /** * The CloudEvent data emitted by Firebase Alerts. + * @typeParam T - the payload type that is expected for this alert. */ export interface FirebaseAlertData { /** Time that the event has created. */ @@ -16,6 +17,7 @@ export interface FirebaseAlertData { /** * A custom CloudEvent for Firebase Alerts (with custom extension attributes). + * @typeParam T - the data type for this alert that is wrapped in a `FirebaseAlertData` object. */ export interface AlertEvent extends CloudEvent> { /** The type of the alerts that got triggered. */ @@ -26,7 +28,7 @@ export interface AlertEvent extends CloudEvent> { */ appId?: string; - /** Data for an AlertEvent is a FirebaseAlertData with a given payload. */ + /** Data for an `AlertEvent` is a `FirebaseAlertData` object with a given payload. */ data: FirebaseAlertData; } @@ -50,14 +52,18 @@ export type AlertType = * Configuration for Firebase Alert functions. */ export interface FirebaseAlertOptions extends options.EventHandlerOptions { + /** Scope the handler to trigger on an alert type. */ alertType: AlertType; + /** Scope the function to trigger on a specific application. */ appId?: string; } /** * Declares a function that can handle Firebase Alerts from CloudEvents. + * @typeParam T - The data type of the `FirebaseAlertData` object the function receives. * @param alertTypeOrOpts the alert type or Firebase Alert function configuration. * @param handler a function that can handle the Firebase Alert inside a CloudEvent. + * @returns A function that you can export and deploy. */ export function onAlertPublished( alertTypeOrOpts: AlertType | FirebaseAlertOptions, @@ -76,8 +82,8 @@ export function onAlertPublished( } /** - * @internal * Helper function for getting the endpoint annotation used in alert handling functions. + * @internal */ export function getEndpointAnnotation( opts: options.EventHandlerOptions, @@ -109,8 +115,8 @@ export function getEndpointAnnotation( } /** - * @internal * Helper function to parse the function opts, alert type, and appId. + * @internal */ export function getOptsAndAlertTypeAndApp( alertTypeOrOpts: AlertType | FirebaseAlertOptions diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index e22f847c0..fc8ec18d2 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -4,7 +4,7 @@ import { FirebaseAlertData, getEndpointAnnotation } from './alerts'; /** * The internal payload object for adding a new tester device to app distribution. - * Payload is wrapped inside a FirebaseAlertData object. + * Payload is wrapped inside a `FirebaseAlertData` object. */ export interface NewTesterDevicePayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.AppDistroNewTesterIosDevicePayload'; @@ -20,6 +20,7 @@ export interface NewTesterDevicePayload { /** * A custom CloudEvent for Firebase Alerts (with custom extension attributes). + * @typeParam T - the data type for app distribution alerts that is wrapped in a `FirebaseAlertData` object. */ export interface AppDistributionEvent extends CloudEvent> { @@ -36,29 +37,53 @@ export const newTesterIosDeviceAlert = 'appDistribution.newTesterIosDevice'; * Configuration for app distribution functions. */ export interface AppDistributionOptions extends options.EventHandlerOptions { + /** Scope the function to trigger on a specific application. */ appId?: string; } /** * Declares a function that can handle adding a new tester iOS device. + * @param handler - Event handler which is run every time a new tester iOS device is added. + * @returns A function that you can export and deploy. */ export function onNewTesterIosDevicePublished( handler: ( event: AppDistributionEvent ) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle adding a new tester iOS device. + * @param appId - A specific application the handler will trigger on. + * @param handler - Event handler which is run every time a new tester iOS device is added. + * @returns A function that you can export and deploy. + */ export function onNewTesterIosDevicePublished( appId: string, handler: ( event: AppDistributionEvent ) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle adding a new tester iOS device. + * @param opts - Options that can be set on the function. + * @param handler - Event handler which is run every time a new tester iOS device is added. + * @returns A function that you can export and deploy. + */ export function onNewTesterIosDevicePublished( opts: AppDistributionOptions, handler: ( event: AppDistributionEvent ) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle adding a new tester iOS device. + * @param appIdOrOptsOrHandler - A specific application, options, or an event-handling function. + * @param handler - Event handler which is run every time a new tester iOS device is added. + * @returns A function that you can export and deploy. + */ export function onNewTesterIosDevicePublished( appIdOrOptsOrHandler: | string @@ -90,8 +115,8 @@ export function onNewTesterIosDevicePublished( } /** - * @internal * Helper function to parse the function opts and appId. + * @internal */ export function getOptsAndApp( appIdOrOpts: string | AppDistributionOptions diff --git a/src/v2/providers/alerts/billing.ts b/src/v2/providers/alerts/billing.ts index 4439546a5..8990481cc 100644 --- a/src/v2/providers/alerts/billing.ts +++ b/src/v2/providers/alerts/billing.ts @@ -4,7 +4,7 @@ import * as options from '../../options'; /** * The internal payload object for billing plan updates. - * Payload is wrapped inside a FirebaseAlertData object. + * Payload is wrapped inside a `FirebaseAlertData` object. */ export interface PlanUpdatePayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.BillingPlanUpdatePayload'; @@ -18,7 +18,7 @@ export interface PlanUpdatePayload { /** * The internal payload object for billing plan automated updates. - * Payload is wrapped inside a FirebaseAlertData object. + * Payload is wrapped inside a `FirebaseAlertData` object. */ export interface PlanAutomatedUpdatePayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.BillingPlanAutomatedUpdatePayload'; @@ -30,6 +30,7 @@ export interface PlanAutomatedUpdatePayload { /** * A custom CloudEvent for billing Firebase Alerts (with custom extension attributes). + * @typeParam T - the data type for billing alerts that is wrapped in a `FirebaseAlertData` object. */ export interface BillingEvent extends CloudEvent> { /** The type of the alerts that got triggered. */ @@ -43,14 +44,30 @@ export const planAutomatedUpdateAlert = 'billing.planAutomatedUpdate'; /** * Declares a function that can handle a billing plan update event. + * @param handler - Event handler which is run every time a billing plan is updated. + * @returns A function that you can export and deploy. */ export function onPlanUpdatePublished( handler: (event: BillingEvent) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a billing plan update event. + * @param opts - Options that can be set on the function. + * @param handler - Event handler which is run every time a billing plan is updated. + * @returns A function that you can export and deploy. + */ export function onPlanUpdatePublished( opts: options.EventHandlerOptions, handler: (event: BillingEvent) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a billing plan update event. + * @param optsOrHandler - Options or an event-handling function. + * @param handler - Event handler which is run every time a billing plan is updated. + * @returns A function that you can export and deploy. + */ export function onPlanUpdatePublished( optsOrHandler: | options.EventHandlerOptions @@ -66,18 +83,34 @@ export function onPlanUpdatePublished( /** * Declares a function that can handle an automated billing plan update event. + * @param handler - Event handler which is run every time an automated billing plan update occurs. + * @returns A function that you can export and deploy. */ export function onPlanAutomatedUpdatePublished( handler: ( event: BillingEvent ) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle an automated billing plan update event. + * @param opts - Options that can be set on the function. + * @param handler - Event handler which is run every time an automated billing plan update occurs. + * @returns A function that you can export and deploy. + */ export function onPlanAutomatedUpdatePublished( opts: options.EventHandlerOptions, handler: ( event: BillingEvent ) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle an automated billing plan update event. + * @param optsOrHandler - Options or an event-handling function. + * @param handler - Event handler which is run every time an automated billing plan update occurs. + * @returns A function that you can export and deploy. + */ export function onPlanAutomatedUpdatePublished( optsOrHandler: | options.EventHandlerOptions diff --git a/src/v2/providers/alerts/crashlytics.ts b/src/v2/providers/alerts/crashlytics.ts index 1cbe13760..1efff4f28 100644 --- a/src/v2/providers/alerts/crashlytics.ts +++ b/src/v2/providers/alerts/crashlytics.ts @@ -2,21 +2,21 @@ import { FirebaseAlertData, getEndpointAnnotation } from '.'; import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; -/** Generic crashlytics issue interface */ +/** Generic Crashlytics issue interface */ export interface Issue { - /** The ID of the crashlytics issue */ + /** The ID of the Crashlytics issue */ id: string; - /** The title of the crashlytics issue */ + /** The title of the Crashlytics issue */ title: string; - /** The subtitle of the crashlytics issue */ + /** The subtitle of the Crashlytics issue */ subtitle: string; - /** The application version of the crashlytics issue */ + /** The application version of the Crashlytics issue */ appVersion: string; } /** * The internal payload object for a new fatal issue. - * Payload is wrapped inside a FirebaseAlertData object. + * Payload is wrapped inside a `FirebaseAlertData` object. */ export interface NewFatalIssuePayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsNewFatalIssuePayload'; @@ -26,7 +26,7 @@ export interface NewFatalIssuePayload { /** * The internal payload object for a new non-fatal issue. - * Payload is wrapped inside a FirebaseAlertData object. + * Payload is wrapped inside a `FirebaseAlertData` object. */ export interface NewNonfatalIssuePayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsNewNonfatalIssuePayload'; @@ -36,7 +36,7 @@ export interface NewNonfatalIssuePayload { /** * The internal payload object for a regression alert. - * Payload is wrapped inside a FirebaseAlertData object. + * Payload is wrapped inside a `FirebaseAlertData` object. */ export interface RegressionAlertPayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsRegressionAlertPayload'; @@ -51,7 +51,7 @@ export interface RegressionAlertPayload { resolveTime: string; } -/** Generic crashlytics trending issue interface */ +/** Generic Crashlytics trending issue interface */ export interface TrendingIssueDetails { /** The type of the Crashlytics issue, e.g. new fatal, new nonfatal, ANR */ type: string; @@ -65,7 +65,7 @@ export interface TrendingIssueDetails { /** * The internal payload object for a stability digest. - * Payload is wrapped inside a FirebaseAlertData object. + * Payload is wrapped inside a `FirebaseAlertData` object. */ export interface StabilityDigestPayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsStabilityDigestPayload'; @@ -80,7 +80,7 @@ export interface StabilityDigestPayload { /** * The internal payload object for a velocity alert. - * Payload is wrapped inside a FirebaseAlertData object. + * Payload is wrapped inside a `FirebaseAlertData` object. */ export interface VelocityAlertPayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsVelocityAlertPayload'; @@ -107,7 +107,7 @@ export interface VelocityAlertPayload { /** * The internal payload object for a new Application Not Responding issue. - * Payload is wrapped inside a FirebaseAlertData object. + * Payload is wrapped inside a `FirebaseAlertData` object. */ export interface NewAnrIssuePayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.CrashlyticsNewAnrIssuePayload'; @@ -117,6 +117,7 @@ export interface NewAnrIssuePayload { /** * A custom CloudEvent for Firebase Alerts (with custom extension attributes). + * @typeParam T - the data type for Crashlytics alerts that is wrapped in a `FirebaseAlertData` object. */ export interface CrashlyticsEvent extends CloudEvent> { /** The type of the alerts that got triggered. */ @@ -139,26 +140,50 @@ export const velocityAlert = 'crashlytics.velocity'; export const newAnrIssueAlert = 'crashlytics.newAnrIssue'; /** - * Configuration for crashlytics functions. + * Configuration for Crashlytics functions. */ export interface CrashlyticsOptions extends options.EventHandlerOptions { + /** Scope the function to trigger on a specific application. */ appId?: string; } /** - * Declares a function that can handle a new fatal issue published to crashlytics. + * Declares a function that can handle a new fatal issue published to Crashlytics. + * @param handler - Event handler that is triggered when a new fatal issue is published to Crashlytics. + * @returns A function that you can export and deploy. */ export function onNewFatalIssuePublished( handler: (event: CrashlyticsEvent) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a new fatal issue published to Crashlytics. + * @param appId - A specific application the handler will trigger on. + * @param handler - Event handler that is triggered when a new fatal issue is published to Crashlytics. + * @returns A function that you can export and deploy. + */ export function onNewFatalIssuePublished( appId: string, handler: (event: CrashlyticsEvent) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a new fatal issue published to Crashlytics. + * @param opts - Options that can be set on the function. + * @param handler - Event handler that is triggered when a new fatal issue is published to Crashlytics. + * @returns A function that you can export and deploy. + */ export function onNewFatalIssuePublished( opts: CrashlyticsOptions, handler: (event: CrashlyticsEvent) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a new fatal issue published to Crashlytics. + * @param appIdOrOptsOrHandler - A specific application, options, or an event-handling function. + * @param handler - Event handler that is triggered when a new fatal issue is published to Crashlytics. + * @returns A function that you can export and deploy. + */ export function onNewFatalIssuePublished( appIdOrOptsOrHandler: | string @@ -176,25 +201,48 @@ export function onNewFatalIssuePublished( } /** - * Declares a function that can handle aa new non-fatal issue published to crashlytics. + * Declares a function that can handle a new non-fatal issue published to Crashlytics. + * @param handler - Event handler that is triggered when a new fatal issue is published to Crashlytics. + * @returns A function that you can export and deploy. */ export function onNewNonfatalIssuePublished( handler: ( event: CrashlyticsEvent ) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a new non-fatal issue published to Crashlytics. + * @param appId - A specific application the handler will trigger on. + * @param handler - Event handler that is triggered when a new non-fatal issue is published to Crashlytics. + * @returns A function that you can export and deploy. + */ export function onNewNonfatalIssuePublished( appId: string, handler: ( event: CrashlyticsEvent ) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a new non-fatal issue published to Crashlytics. + * @param opts - Options that can be set on the function. + * @param handler - Event handler that is triggered when a new non-fatal issue is published to Crashlytics. + * @returns A function that you can export and deploy. + */ export function onNewNonfatalIssuePublished( opts: CrashlyticsOptions, handler: ( event: CrashlyticsEvent ) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a new non-fatal issue published to Crashlytics. + * @param appIdOrOptsOrHandler - A specific application, options, or an event-handling function. + * @param handler - Event handler that is triggered when a new non-fatal issue is published to Crashlytics. + * @returns A function that you can export and deploy. + */ export function onNewNonfatalIssuePublished( appIdOrOptsOrHandler: | string @@ -214,25 +262,50 @@ export function onNewNonfatalIssuePublished( } /** - * Declares a function that can handle a regression alert published to crashlytics. + * Declares a function that can handle a regression alert published to Crashlytics. + * @param handler - Event handler that is triggered when a regression alert is published to Crashlytics. + * @returns A function that you can export and deploy. */ export function onRegressionAlertPublished( handler: ( event: CrashlyticsEvent ) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a regression alert published to Crashlytics. + * @param appId - A specific application the handler will trigger on. + * @param handler - Event handler that is triggered when a regression alert is published to Crashlytics. + * @returns A function that you can export and deploy. + + */ export function onRegressionAlertPublished( appId: string, handler: ( event: CrashlyticsEvent ) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a regression alert published to Crashlytics. + * @param opts - Options that can be set on the function. + * @param handler - Event handler that is triggered when a regression alert is published to Crashlytics. + * @returns A function that you can export and deploy. + + */ export function onRegressionAlertPublished( opts: CrashlyticsOptions, handler: ( event: CrashlyticsEvent ) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a regression alert published to Crashlytics. + * @param appIdOrOptsOrHandler - A specific application, options, or an event-handling function. + * @param handler - Event handler that is triggered when a regression alert is published to Crashlytics. + * @returns A function that you can export and deploy. + */ export function onRegressionAlertPublished( appIdOrOptsOrHandler: | string @@ -250,25 +323,50 @@ export function onRegressionAlertPublished( } /** - * Declares a function that can handle a stability digest published to crashlytics. + * Declares a function that can handle a stability digest published to Crashlytics. + * @param handler - Event handler that is triggered when a stability digest is published to Crashlytics. + * @returns A function that you can export and deploy. */ export function onStabilityDigestPublished( handler: ( event: CrashlyticsEvent ) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a stability digest published to Crashlytics. + * @param appId - A specific application the handler will trigger on. + * @param handler - Event handler that is triggered when a stability digest is published to Crashlytics. + * @returns A function that you can export and deploy. + + */ export function onStabilityDigestPublished( appId: string, handler: ( event: CrashlyticsEvent ) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a stability digest published to Crashlytics. + * @param opts - Options that can be set on the function. + * @param handler - Event handler that is triggered when a stability digest is published to Crashlytics. + * @returns A function that you can export and deploy. + + */ export function onStabilityDigestPublished( opts: CrashlyticsOptions, handler: ( event: CrashlyticsEvent ) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a stability digest published to Crashlytics. + * @param appIdOrOptsOrHandler - A specific application, options, or an event-handling function. + * @param handler - Event handler that is triggered when a stability digest is published to Crashlytics. + * @returns A function that you can export and deploy. + */ export function onStabilityDigestPublished( appIdOrOptsOrHandler: | string @@ -286,19 +384,42 @@ export function onStabilityDigestPublished( } /** - * Declares a function that can handle a velocity alert published to crashlytics. + * Declares a function that can handle a velocity alert published to Crashlytics. + * @param handler - Event handler that is triggered when a velocity alert is published to Crashlytics. + * @returns A function that you can export and deploy. */ export function onVelocityAlertPublished( handler: (event: CrashlyticsEvent) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a velocity alert published to Crashlytics. + * @param appId - A specific application the handler will trigger on. + * @param handler - Event handler that is triggered when a velocity alert is published to Crashlytics. + * @returns A function that you can export and deploy. + */ export function onVelocityAlertPublished( appId: string, handler: (event: CrashlyticsEvent) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a velocity alert published to Crashlytics. + * @param opts - Options that can be set on the function. + * @param handler - Event handler that is triggered when a velocity alert is published to Crashlytics. + * @returns A function that you can export and deploy. + */ export function onVelocityAlertPublished( opts: CrashlyticsOptions, handler: (event: CrashlyticsEvent) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a velocity alert published to Crashlytics. + * @param appIdOrOptsOrHandler - A specific application, options, or an event-handling function. + * @param handler - Event handler that is triggered when a velocity alert is published to Crashlytics. + * @returns A function that you can export and deploy. + */ export function onVelocityAlertPublished( appIdOrOptsOrHandler: | string @@ -316,19 +437,44 @@ export function onVelocityAlertPublished( } /** - * Declares a function that can handle a new Application Not Responding issue published to crashlytics. + * Declares a function that can handle a new Application Not Responding issue published to Crashlytics. + * @param handler - Event handler that is triggered when a new Application Not Responding issue is published to Crashlytics. + * @returns A function that you can export and deploy. */ export function onNewAnrIssuePublished( handler: (event: CrashlyticsEvent) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a new Application Not Responding issue published to Crashlytics. + * @param appId - A specific application the handler will trigger on. + * @param handler - Event handler that is triggered when a new Application Not Responding issue is published to Crashlytics. + * @returns A function that you can export and deploy. + + */ export function onNewAnrIssuePublished( appId: string, handler: (event: CrashlyticsEvent) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a new Application Not Responding issue published to Crashlytics. + * @param opts - Options that can be set on the function. + * @param handler - Event handler that is triggered when a new Application Not Responding issue is published to Crashlytics. + * @returns A function that you can export and deploy. + + */ export function onNewAnrIssuePublished( opts: CrashlyticsOptions, handler: (event: CrashlyticsEvent) => any | Promise ): CloudFunction>; + +/** + * Declares a function that can handle a new Application Not Responding issue published to Crashlytics. + * @param appIdOrOptsOrHandler - A specific application, options, or an event-handling function. + * @param handler - Event handler that is triggered when a new Application Not Responding issue is published to Crashlytics. + * @returns A function that you can export and deploy. + */ export function onNewAnrIssuePublished( appIdOrOptsOrHandler: | string @@ -374,8 +520,8 @@ export function onOperation( } /** - * @internal * Helper function to parse the function opts and appId. + * @internal */ export function getOptsAndApp( appIdOrOpts: string | CrashlyticsOptions From be7e52907bb91379772bcd46c63f4c40e932a3e5 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Tue, 10 May 2022 14:31:16 -0700 Subject: [PATCH 100/370] Redefine fields in options so they get better docs (#1119) * Redefine fields in options so they get better docs * PR feedback --- src/common/providers/identity.ts | 3 +- src/v2/options.ts | 3 +- src/v2/providers/alerts/alerts.ts | 114 ++++++++++++++++++++- src/v2/providers/alerts/appDistribution.ts | 93 +++++++++++++++++ src/v2/providers/alerts/crashlytics.ts | 93 +++++++++++++++++ src/v2/providers/eventarc.ts | 93 +++++++++++++++++ src/v2/providers/https.ts | 93 +++++++++++++++++ src/v2/providers/identity.ts | 92 ++++++++++++++++- src/v2/providers/pubsub.ts | 93 +++++++++++++++++ src/v2/providers/storage.ts | 93 +++++++++++++++++ src/v2/providers/tasks.ts | 99 +++++++++++++++++- 11 files changed, 863 insertions(+), 6 deletions(-) diff --git a/src/common/providers/identity.ts b/src/common/providers/identity.ts index 031e36f97..08658c48d 100644 --- a/src/common/providers/identity.ts +++ b/src/common/providers/identity.ts @@ -52,7 +52,8 @@ const CLAIMS_MAX_PAYLOAD_SIZE = 1000; /** * Shorthand auth blocking events from GCIP. - * @internal + * @hidden + * @alpha */ export type AuthBlockingEventType = 'beforeCreate' | 'beforeSignIn'; diff --git a/src/v2/options.ts b/src/v2/options.ts index d3155f3c3..d8beb1341 100644 --- a/src/v2/options.ts +++ b/src/v2/options.ts @@ -181,7 +181,7 @@ export interface GlobalOptions { invoker?: 'public' | 'private' | string | string[]; /* - * Secrets to bind to a functions. + * Secrets to bind to a function. */ secrets?: string[]; } @@ -212,6 +212,7 @@ export function getGlobalOptions(): GlobalOptions { * Additional fields that can be set on any event-handling Cloud Function. */ export interface EventHandlerOptions extends GlobalOptions { + /** Whether failed executions should be delivered again. */ retry?: boolean; } diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index 4b260a851..6d4e09d54 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -54,17 +54,127 @@ export type AlertType = export interface FirebaseAlertOptions extends options.EventHandlerOptions { /** Scope the handler to trigger on an alert type. */ alertType: AlertType; + /** Scope the function to trigger on a specific application. */ appId?: string; + + /** + * Region where functions should be deployed. + */ + region?: options.SupportedRegion | string; + + /** + * Amount of memory to allocate to a function. + * A value of null restores the defaults of 256MB. + */ + memory?: options.MemoryOption | null; + + /** + * Timeout for the function in sections, possible values are 0 to 540. + * HTTPS functions can specify a higher timeout. + * A value of null restores the default of 60s + * The minimum timeout for a gen 2 function is 1s. The maximum timeout for a + * function depends on the type of function: Event handling functions have a + * maximum timeout of 540s (9 minutes). HTTPS and callable functions have a + * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum + * timeout of 1,800s (30 minutes) + */ + timeoutSeconds?: number | null; + + /** + * Min number of actual instances to be running at a given time. + * Instances will be billed for memory allocation and 10% of CPU allocation + * while idle. + * A value of null restores the default min instances. + */ + minInstances?: number | null; + + /** + * Max number of instances to be running in parallel. + * A value of null restores the default max instances. + */ + maxInstances?: number | null; + + /** + * Number of requests a function can serve at once. + * Can only be applied to functions running on Cloud Functions v2. + * A value of null restores the default concurrency (80 when CPU >= 1, 1 otherwise). + * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. + * The maximum value for concurrency is 1,000. + */ + concurrency?: number | null; + + /** + * Fractional number of CPUs to allocate to a function. + * Defaults to 1 for functions with <= 2GB RAM and increases for larger memory sizes. + * This is different from the defaults when using the gcloud utility and is different from + * the fixed amount assigned in Google Cloud Functions generation 1. + * To revert to the CPU amounts used in gcloud or in Cloud Functions generation 1, set this + * to the value "gcf_gen1" + */ + cpu?: number | 'gcf_gen1'; + + /** + * Connect cloud function to specified VPC connector. + * A value of null removes the VPC connector + */ + vpcConnector?: string | null; + + /** + * Egress settings for VPC connector. + * A value of null turns off VPC connector egress settings + */ + vpcConnectorEgressSettings?: options.VpcEgressSetting | null; + + /** + * Specific service account for the function to run as. + * A value of null restores the default service account. + */ + serviceAccount?: string | null; + + /** + * Ingress settings which control where this function can be called from. + * A value of null turns off ingress settings. + */ + ingressSettings?: options.IngressSetting | null; + + /** + * User labels to set on the function. + */ + labels?: Record; + + /* + * Secrets to bind to a function. + */ + secrets?: string[]; + + /** Whether failed executions should be delivered again. */ + retry?: boolean; } /** * Declares a function that can handle Firebase Alerts from CloudEvents. - * @typeParam T - The data type of the `FirebaseAlertData` object the function receives. - * @param alertTypeOrOpts the alert type or Firebase Alert function configuration. + * @typeParam T - the type of event.data.payload. + * @param alertType - the alert type or Firebase Alert function configuration. * @param handler a function that can handle the Firebase Alert inside a CloudEvent. * @returns A function that you can export and deploy. */ +export function onAlertPublished( + alertType: AlertType, + handler: (event: AlertEvent) => any | Promise +): CloudFunction>; + +/** + * Declares a function that can handle Firebase Alerts from CloudEvents. + * @typeParam T - the type of event.data.payload. + * @param options - the alert type and other options for this cloud function. + * @param handler a function that can handle the Firebase Alert inside a CloudEvent. + */ +export function onAlertPublished( + options: FirebaseAlertOptions, + handler: (event: AlertEvent) => any | Promise +): CloudFunction>; + export function onAlertPublished( alertTypeOrOpts: AlertType | FirebaseAlertOptions, handler: (event: AlertEvent) => any | Promise diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index fc8ec18d2..d26f84044 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -39,6 +39,99 @@ export const newTesterIosDeviceAlert = 'appDistribution.newTesterIosDevice'; export interface AppDistributionOptions extends options.EventHandlerOptions { /** Scope the function to trigger on a specific application. */ appId?: string; + + /** + * Region where functions should be deployed. + */ + region?: options.SupportedRegion | string; + + /** + * Amount of memory to allocate to a function. + * A value of null restores the defaults of 256MB. + */ + memory?: options.MemoryOption | null; + + /** + * Timeout for the function in sections, possible values are 0 to 540. + * HTTPS functions can specify a higher timeout. + * A value of null restores the default of 60s + * The minimum timeout for a gen 2 function is 1s. The maximum timeout for a + * function depends on the type of function: Event handling functions have a + * maximum timeout of 540s (9 minutes). HTTPS and callable functions have a + * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum + * timeout of 1,800s (30 minutes) + */ + timeoutSeconds?: number | null; + + /** + * Min number of actual instances to be running at a given time. + * Instances will be billed for memory allocation and 10% of CPU allocation + * while idle. + * A value of null restores the default min instances. + */ + minInstances?: number | null; + + /** + * Max number of instances to be running in parallel. + * A value of null restores the default max instances. + */ + maxInstances?: number | null; + + /** + * Number of requests a function can serve at once. + * Can only be applied to functions running on Cloud Functions v2. + * A value of null restores the default concurrency (80 when CPU >= 1, 1 otherwise). + * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. + * The maximum value for concurrency is 1,000. + */ + concurrency?: number | null; + + /** + * Fractional number of CPUs to allocate to a function. + * Defaults to 1 for functions with <= 2GB RAM and increases for larger memory sizes. + * This is different from the defaults when using the gcloud utility and is different from + * the fixed amount assigned in Google Cloud Functions generation 1. + * To revert to the CPU amounts used in gcloud or in Cloud Functions generation 1, set this + * to the value "gcf_gen1" + */ + cpu?: number | 'gcf_gen1'; + + /** + * Connect cloud function to specified VPC connector. + * A value of null removes the VPC connector + */ + vpcConnector?: string | null; + + /** + * Egress settings for VPC connector. + * A value of null turns off VPC connector egress settings + */ + vpcConnectorEgressSettings?: options.VpcEgressSetting | null; + + /** + * Specific service account for the function to run as. + * A value of null restores the default service account. + */ + serviceAccount?: string | null; + + /** + * Ingress settings which control where this function can be called from. + * A value of null turns off ingress settings. + */ + ingressSettings?: options.IngressSetting | null; + + /** + * User labels to set on the function. + */ + labels?: Record; + + /* + * Secrets to bind to a function. + */ + secrets?: string[]; + + /** Whether failed executions should be delivered again. */ + retry?: boolean; } /** diff --git a/src/v2/providers/alerts/crashlytics.ts b/src/v2/providers/alerts/crashlytics.ts index 1efff4f28..307dda260 100644 --- a/src/v2/providers/alerts/crashlytics.ts +++ b/src/v2/providers/alerts/crashlytics.ts @@ -145,6 +145,99 @@ export const newAnrIssueAlert = 'crashlytics.newAnrIssue'; export interface CrashlyticsOptions extends options.EventHandlerOptions { /** Scope the function to trigger on a specific application. */ appId?: string; + + /** + * Region where functions should be deployed. + */ + region?: options.SupportedRegion | string; + + /** + * Amount of memory to allocate to a function. + * A value of null restores the defaults of 256MB. + */ + memory?: options.MemoryOption | null; + + /** + * Timeout for the function in sections, possible values are 0 to 540. + * HTTPS functions can specify a higher timeout. + * A value of null restores the default of 60s + * The minimum timeout for a gen 2 function is 1s. The maximum timeout for a + * function depends on the type of function: Event handling functions have a + * maximum timeout of 540s (9 minutes). HTTPS and callable functions have a + * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum + * timeout of 1,800s (30 minutes) + */ + timeoutSeconds?: number | null; + + /** + * Min number of actual instances to be running at a given time. + * Instances will be billed for memory allocation and 10% of CPU allocation + * while idle. + * A value of null restores the default min instances. + */ + minInstances?: number | null; + + /** + * Max number of instances to be running in parallel. + * A value of null restores the default max instances. + */ + maxInstances?: number | null; + + /** + * Number of requests a function can serve at once. + * Can only be applied to functions running on Cloud Functions v2. + * A value of null restores the default concurrency (80 when CPU >= 1, 1 otherwise). + * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. + * The maximum value for concurrency is 1,000. + */ + concurrency?: number | null; + + /** + * Fractional number of CPUs to allocate to a function. + * Defaults to 1 for functions with <= 2GB RAM and increases for larger memory sizes. + * This is different from the defaults when using the gcloud utility and is different from + * the fixed amount assigned in Google Cloud Functions generation 1. + * To revert to the CPU amounts used in gcloud or in Cloud Functions generation 1, set this + * to the value "gcf_gen1" + */ + cpu?: number | 'gcf_gen1'; + + /** + * Connect cloud function to specified VPC connector. + * A value of null removes the VPC connector + */ + vpcConnector?: string | null; + + /** + * Egress settings for VPC connector. + * A value of null turns off VPC connector egress settings + */ + vpcConnectorEgressSettings?: options.VpcEgressSetting | null; + + /** + * Specific service account for the function to run as. + * A value of null restores the default service account. + */ + serviceAccount?: string | null; + + /** + * Ingress settings which control where this function can be called from. + * A value of null turns off ingress settings. + */ + ingressSettings?: options.IngressSetting | null; + + /** + * User labels to set on the function. + */ + labels?: Record; + + /* + * Secrets to bind to a function. + */ + secrets?: string[]; + + /** Whether failed executions should be delivered again. */ + retry?: boolean; } /** diff --git a/src/v2/providers/eventarc.ts b/src/v2/providers/eventarc.ts index 17750d34a..289a1a490 100644 --- a/src/v2/providers/eventarc.ts +++ b/src/v2/providers/eventarc.ts @@ -52,6 +52,99 @@ export interface EventarcTriggerOptions extends options.EventHandlerOptions { * Eventarc event exact match filter. */ filters?: Record; + + /** + * Region where functions should be deployed. + */ + region?: options.SupportedRegion | string; + + /** + * Amount of memory to allocate to a function. + * A value of null restores the defaults of 256MB. + */ + memory?: options.MemoryOption | null; + + /** + * Timeout for the function in sections, possible values are 0 to 540. + * HTTPS functions can specify a higher timeout. + * A value of null restores the default of 60s + * The minimum timeout for a gen 2 function is 1s. The maximum timeout for a + * function depends on the type of function: Event handling functions have a + * maximum timeout of 540s (9 minutes). HTTPS and callable functions have a + * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum + * timeout of 1,800s (30 minutes) + */ + timeoutSeconds?: number | null; + + /** + * Min number of actual instances to be running at a given time. + * Instances will be billed for memory allocation and 10% of CPU allocation + * while idle. + * A value of null restores the default min instances. + */ + minInstances?: number | null; + + /** + * Max number of instances to be running in parallel. + * A value of null restores the default max instances. + */ + maxInstances?: number | null; + + /** + * Number of requests a function can serve at once. + * Can only be applied to functions running on Cloud Functions v2. + * A value of null restores the default concurrency (80 when CPU >= 1, 1 otherwise). + * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. + * The maximum value for concurrency is 1,000. + */ + concurrency?: number | null; + + /** + * Fractional number of CPUs to allocate to a function. + * Defaults to 1 for functions with <= 2GB RAM and increases for larger memory sizes. + * This is different from the defaults when using the gcloud utility and is different from + * the fixed amount assigned in Google Cloud Functions generation 1. + * To revert to the CPU amounts used in gcloud or in Cloud Functions generation 1, set this + * to the value "gcf_gen1" + */ + cpu?: number | 'gcf_gen1'; + + /** + * Connect cloud function to specified VPC connector. + * A value of null removes the VPC connector + */ + vpcConnector?: string | null; + + /** + * Egress settings for VPC connector. + * A value of null turns off VPC connector egress settings + */ + vpcConnectorEgressSettings?: options.VpcEgressSetting | null; + + /** + * Specific service account for the function to run as. + * A value of null restores the default service account. + */ + serviceAccount?: string | null; + + /** + * Ingress settings which control where this function can be called from. + * A value of null turns off ingress settings. + */ + ingressSettings?: options.IngressSetting | null; + + /** + * User labels to set on the function. + */ + labels?: Record; + + /* + * Secrets to bind to a function. + */ + secrets?: string[]; + + /** Whether failed executions should be delivered again. */ + retry?: boolean; } /** diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index d4d3d07c7..847d65631 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -49,6 +49,99 @@ export interface HttpsOptions extends Omit { * Defaults to true for {@link https.CallableFunction} and false otherwise. */ cors?: string | boolean | RegExp | Array; + + /** + * Amount of memory to allocate to a function. + * A value of null restores the defaults of 256MB. + */ + memory?: options.MemoryOption | null; + + /** + * Timeout for the function in sections, possible values are 0 to 540. + * HTTPS functions can specify a higher timeout. + * A value of null restores the default of 60s + * The minimum timeout for a gen 2 function is 1s. The maximum timeout for a + * function depends on the type of function: Event handling functions have a + * maximum timeout of 540s (9 minutes). HTTPS and callable functions have a + * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum + * timeout of 1,800s (30 minutes) + */ + timeoutSeconds?: number | null; + + /** + * Min number of actual instances to be running at a given time. + * Instances will be billed for memory allocation and 10% of CPU allocation + * while idle. + * A value of null restores the default min instances. + */ + minInstances?: number | null; + + /** + * Max number of instances to be running in parallel. + * A value of null restores the default max instances. + */ + maxInstances?: number | null; + + /** + * Number of requests a function can serve at once. + * Can only be applied to functions running on Cloud Functions v2. + * A value of null restores the default concurrency (80 when CPU >= 1, 1 otherwise). + * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. + * The maximum value for concurrency is 1,000. + */ + concurrency?: number | null; + + /** + * Fractional number of CPUs to allocate to a function. + * Defaults to 1 for functions with <= 2GB RAM and increases for larger memory sizes. + * This is different from the defaults when using the gcloud utility and is different from + * the fixed amount assigned in Google Cloud Functions generation 1. + * To revert to the CPU amounts used in gcloud or in Cloud Functions generation 1, set this + * to the value "gcf_gen1" + */ + cpu?: number | 'gcf_gen1'; + + /** + * Connect cloud function to specified VPC connector. + * A value of null removes the VPC connector + */ + vpcConnector?: string | null; + + /** + * Egress settings for VPC connector. + * A value of null turns off VPC connector egress settings + */ + vpcConnectorEgressSettings?: options.VpcEgressSetting | null; + + /** + * Specific service account for the function to run as. + * A value of null restores the default service account. + */ + serviceAccount?: string | null; + + /** + * Ingress settings which control where this function can be called from. + * A value of null turns off ingress settings. + */ + ingressSettings?: options.IngressSetting | null; + + /** + * User labels to set on the function. + */ + labels?: Record; + + /** + * Invoker to set access control on https functions. + */ + invoker?: 'public' | 'private' | string | string[]; + + /* + * Secrets to bind to a function. + */ + secrets?: string[]; + + /** Whether failed executions should be delivered again. */ + retry?: boolean; } /** diff --git a/src/v2/providers/identity.ts b/src/v2/providers/identity.ts index e59db6590..b101a8194 100644 --- a/src/v2/providers/identity.ts +++ b/src/v2/providers/identity.ts @@ -22,10 +22,100 @@ interface InternalOptions { /** * All function options plus idToken, accessToken, and refreshToken. */ -export interface BlockingOptions extends options.GlobalOptions { +export interface BlockingOptions { idToken?: boolean; accessToken?: boolean; refreshToken?: boolean; + + /** + * Region where functions should be deployed. + */ + region?: options.SupportedRegion | string; + + /** + * Amount of memory to allocate to a function. + * A value of null restores the defaults of 256MB. + */ + memory?: options.MemoryOption | null; + + /** + * Timeout for the function in sections, possible values are 0 to 540. + * HTTPS functions can specify a higher timeout. + * A value of null restores the default of 60s + * The minimum timeout for a gen 2 function is 1s. The maximum timeout for a + * function depends on the type of function: Event handling functions have a + * maximum timeout of 540s (9 minutes). HTTPS and callable functions have a + * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum + * timeout of 1,800s (30 minutes) + */ + timeoutSeconds?: number | null; + + /** + * Min number of actual instances to be running at a given time. + * Instances will be billed for memory allocation and 10% of CPU allocation + * while idle. + * A value of null restores the default min instances. + */ + minInstances?: number | null; + + /** + * Max number of instances to be running in parallel. + * A value of null restores the default max instances. + */ + maxInstances?: number | null; + + /** + * Number of requests a function can serve at once. + * Can only be applied to functions running on Cloud Functions v2. + * A value of null restores the default concurrency (80 when CPU >= 1, 1 otherwise). + * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. + * The maximum value for concurrency is 1,000. + */ + concurrency?: number | null; + + /** + * Fractional number of CPUs to allocate to a function. + * Defaults to 1 for functions with <= 2GB RAM and increases for larger memory sizes. + * This is different from the defaults when using the gcloud utility and is different from + * the fixed amount assigned in Google Cloud Functions generation 1. + * To revert to the CPU amounts used in gcloud or in Cloud Functions generation 1, set this + * to the value "gcf_gen1" + */ + cpu?: number | 'gcf_gen1'; + + /** + * Connect cloud function to specified VPC connector. + * A value of null removes the VPC connector + */ + vpcConnector?: string | null; + + /** + * Egress settings for VPC connector. + * A value of null turns off VPC connector egress settings + */ + vpcConnectorEgressSettings?: options.VpcEgressSetting | null; + + /** + * Specific service account for the function to run as. + * A value of null restores the default service account. + */ + serviceAccount?: string | null; + + /** + * Ingress settings which control where this function can be called from. + * A value of null turns off ingress settings. + */ + ingressSettings?: options.IngressSetting | null; + + /** + * User labels to set on the function. + */ + labels?: Record; + + /* + * Secrets to bind to a function. + */ + secrets?: string[]; } /** diff --git a/src/v2/providers/pubsub.ts b/src/v2/providers/pubsub.ts index e5321ef70..09c56d59c 100644 --- a/src/v2/providers/pubsub.ts +++ b/src/v2/providers/pubsub.ts @@ -123,6 +123,99 @@ export interface MessagePublishedData { export interface PubSubOptions extends options.EventHandlerOptions { /** The Pub/Sub topic to watch for message events */ topic: string; + + /** + * Region where functions should be deployed. + */ + region?: options.SupportedRegion | string; + + /** + * Amount of memory to allocate to a function. + * A value of null restores the defaults of 256MB. + */ + memory?: options.MemoryOption | null; + + /** + * Timeout for the function in sections, possible values are 0 to 540. + * HTTPS functions can specify a higher timeout. + * A value of null restores the default of 60s + * The minimum timeout for a gen 2 function is 1s. The maximum timeout for a + * function depends on the type of function: Event handling functions have a + * maximum timeout of 540s (9 minutes). HTTPS and callable functions have a + * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum + * timeout of 1,800s (30 minutes) + */ + timeoutSeconds?: number | null; + + /** + * Min number of actual instances to be running at a given time. + * Instances will be billed for memory allocation and 10% of CPU allocation + * while idle. + * A value of null restores the default min instances. + */ + minInstances?: number | null; + + /** + * Max number of instances to be running in parallel. + * A value of null restores the default max instances. + */ + maxInstances?: number | null; + + /** + * Number of requests a function can serve at once. + * Can only be applied to functions running on Cloud Functions v2. + * A value of null restores the default concurrency (80 when CPU >= 1, 1 otherwise). + * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. + * The maximum value for concurrency is 1,000. + */ + concurrency?: number | null; + + /** + * Fractional number of CPUs to allocate to a function. + * Defaults to 1 for functions with <= 2GB RAM and increases for larger memory sizes. + * This is different from the defaults when using the gcloud utility and is different from + * the fixed amount assigned in Google Cloud Functions generation 1. + * To revert to the CPU amounts used in gcloud or in Cloud Functions generation 1, set this + * to the value "gcf_gen1" + */ + cpu?: number | 'gcf_gen1'; + + /** + * Connect cloud function to specified VPC connector. + * A value of null removes the VPC connector + */ + vpcConnector?: string | null; + + /** + * Egress settings for VPC connector. + * A value of null turns off VPC connector egress settings + */ + vpcConnectorEgressSettings?: options.VpcEgressSetting | null; + + /** + * Specific service account for the function to run as. + * A value of null restores the default service account. + */ + serviceAccount?: string | null; + + /** + * Ingress settings which control where this function can be called from. + * A value of null turns off ingress settings. + */ + ingressSettings?: options.IngressSetting | null; + + /** + * User labels to set on the function. + */ + labels?: Record; + + /* + * Secrets to bind to a function. + */ + secrets?: string[]; + + /** Whether failed executions should be delivered again. */ + retry?: boolean; } /** diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts index 27e6d01dc..ec8068119 100644 --- a/src/v2/providers/storage.ts +++ b/src/v2/providers/storage.ts @@ -194,6 +194,99 @@ export const metadataUpdatedEvent = export interface StorageOptions extends options.EventHandlerOptions { /** The name of the bucket containing this object. */ bucket?: string; + + /** + * Region where functions should be deployed. + */ + region?: options.SupportedRegion | string; + + /** + * Amount of memory to allocate to a function. + * A value of null restores the defaults of 256MB. + */ + memory?: options.MemoryOption | null; + + /** + * Timeout for the function in sections, possible values are 0 to 540. + * HTTPS functions can specify a higher timeout. + * A value of null restores the default of 60s + * The minimum timeout for a gen 2 function is 1s. The maximum timeout for a + * function depends on the type of function: Event handling functions have a + * maximum timeout of 540s (9 minutes). HTTPS and callable functions have a + * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum + * timeout of 1,800s (30 minutes) + */ + timeoutSeconds?: number | null; + + /** + * Min number of actual instances to be running at a given time. + * Instances will be billed for memory allocation and 10% of CPU allocation + * while idle. + * A value of null restores the default min instances. + */ + minInstances?: number | null; + + /** + * Max number of instances to be running in parallel. + * A value of null restores the default max instances. + */ + maxInstances?: number | null; + + /** + * Number of requests a function can serve at once. + * Can only be applied to functions running on Cloud Functions v2. + * A value of null restores the default concurrency (80 when CPU >= 1, 1 otherwise). + * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. + * The maximum value for concurrency is 1,000. + */ + concurrency?: number | null; + + /** + * Fractional number of CPUs to allocate to a function. + * Defaults to 1 for functions with <= 2GB RAM and increases for larger memory sizes. + * This is different from the defaults when using the gcloud utility and is different from + * the fixed amount assigned in Google Cloud Functions generation 1. + * To revert to the CPU amounts used in gcloud or in Cloud Functions generation 1, set this + * to the value "gcf_gen1" + */ + cpu?: number | 'gcf_gen1'; + + /** + * Connect cloud function to specified VPC connector. + * A value of null removes the VPC connector + */ + vpcConnector?: string | null; + + /** + * Egress settings for VPC connector. + * A value of null turns off VPC connector egress settings + */ + vpcConnectorEgressSettings?: options.VpcEgressSetting | null; + + /** + * Specific service account for the function to run as. + * A value of null restores the default service account. + */ + serviceAccount?: string | null; + + /** + * Ingress settings which control where this function can be called from. + * A value of null turns off ingress settings. + */ + ingressSettings?: options.IngressSetting | null; + + /** + * User labels to set on the function. + */ + labels?: Record; + + /* + * Secrets to bind to a function. + */ + secrets?: string[]; + + /** Whether failed executions should be delivered again. */ + retry?: boolean; } /** diff --git a/src/v2/providers/tasks.ts b/src/v2/providers/tasks.ts index b45de3361..1c175ead8 100644 --- a/src/v2/providers/tasks.ts +++ b/src/v2/providers/tasks.ts @@ -37,9 +37,13 @@ import { HttpsFunction } from './https'; export { AuthData, RateLimits, Request, RetryConfig as RetryPolicy }; -export interface TaskQueueOptions extends options.GlobalOptions { +export interface TaskQueueOptions extends options.EventHandlerOptions { + /** How a task should be retried in the event of a non-2xx return. */ retryConfig?: RetryConfig; + + /** How congestion control should be applied to the function. */ rateLimits?: RateLimits; + /** * Who can enqueue tasks for this function. * If left unspecified, only service accounts which have @@ -47,6 +51,99 @@ export interface TaskQueueOptions extends options.GlobalOptions { * will have permissions. */ invoker?: 'private' | string | string[]; + + /** + * Region where functions should be deployed. + */ + region?: options.SupportedRegion | string; + + /** + * Amount of memory to allocate to a function. + * A value of null restores the defaults of 256MB. + */ + memory?: options.MemoryOption | null; + + /** + * Timeout for the function in sections, possible values are 0 to 540. + * HTTPS functions can specify a higher timeout. + * A value of null restores the default of 60s + * The minimum timeout for a gen 2 function is 1s. The maximum timeout for a + * function depends on the type of function: Event handling functions have a + * maximum timeout of 540s (9 minutes). HTTPS and callable functions have a + * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum + * timeout of 1,800s (30 minutes) + */ + timeoutSeconds?: number | null; + + /** + * Min number of actual instances to be running at a given time. + * Instances will be billed for memory allocation and 10% of CPU allocation + * while idle. + * A value of null restores the default min instances. + */ + minInstances?: number | null; + + /** + * Max number of instances to be running in parallel. + * A value of null restores the default max instances. + */ + maxInstances?: number | null; + + /** + * Number of requests a function can serve at once. + * Can only be applied to functions running on Cloud Functions v2. + * A value of null restores the default concurrency (80 when CPU >= 1, 1 otherwise). + * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. + * The maximum value for concurrency is 1,000. + */ + concurrency?: number | null; + + /** + * Fractional number of CPUs to allocate to a function. + * Defaults to 1 for functions with <= 2GB RAM and increases for larger memory sizes. + * This is different from the defaults when using the gcloud utility and is different from + * the fixed amount assigned in Google Cloud Functions generation 1. + * To revert to the CPU amounts used in gcloud or in Cloud Functions generation 1, set this + * to the value "gcf_gen1" + */ + cpu?: number | 'gcf_gen1'; + + /** + * Connect cloud function to specified VPC connector. + * A value of null removes the VPC connector + */ + vpcConnector?: string | null; + + /** + * Egress settings for VPC connector. + * A value of null turns off VPC connector egress settings + */ + vpcConnectorEgressSettings?: options.VpcEgressSetting | null; + + /** + * Specific service account for the function to run as. + * A value of null restores the default service account. + */ + serviceAccount?: string | null; + + /** + * Ingress settings which control where this function can be called from. + * A value of null turns off ingress settings. + */ + ingressSettings?: options.IngressSetting | null; + + /** + * User labels to set on the function. + */ + labels?: Record; + + /* + * Secrets to bind to a function. + */ + secrets?: string[]; + + /** Whether failed executions should be delivered again. */ + retry?: boolean; } export interface TaskQueueFunction extends HttpsFunction { From 1bf0a86b5a7eacb768592e56dae99c68b51b4257 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Tue, 10 May 2022 14:33:18 -0700 Subject: [PATCH 101/370] Inline eventarc callback type (#1118) --- src/v2/providers/eventarc.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/v2/providers/eventarc.ts b/src/v2/providers/eventarc.ts index 289a1a490..fed753864 100644 --- a/src/v2/providers/eventarc.ts +++ b/src/v2/providers/eventarc.ts @@ -20,6 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +/** + * Cloud functions to integrate directly with Eventarc. + * @packageDocumentation + */ + import { convertIfPresent, copyIfPresent } from '../../common/encoding'; import { ManifestEndpoint } from '../../runtime/manifest'; import { CloudEvent, CloudFunction } from '../core'; @@ -147,11 +152,6 @@ export interface EventarcTriggerOptions extends options.EventHandlerOptions { retry?: boolean; } -/** - * A function that takes a {@link CloudEvent}. - */ -export type CloudEventHandler = (event: CloudEvent) => any | Promise; - /** Handles an Eventarc event published on the default channel. * @param eventType - Type of the event to trigger on. * @param handler - A function to execute when triggered. @@ -159,7 +159,7 @@ export type CloudEventHandler = (event: CloudEvent) => any | Promise; */ export function onCustomEventPublished( eventType: string, - handler: CloudEventHandler + handler: (event: CloudEvent) => any | Promise ): CloudFunction>; /** Handles an Eventarc event. @@ -169,12 +169,12 @@ export function onCustomEventPublished( */ export function onCustomEventPublished( opts: EventarcTriggerOptions, - handler: CloudEventHandler + handler: (event: CloudEvent) => any | Promise ): CloudFunction>; export function onCustomEventPublished( eventTypeOrOpts: string | EventarcTriggerOptions, - handler: CloudEventHandler + handler: (event: CloudEvent) => any | Promise ): CloudFunction> { let opts: EventarcTriggerOptions; if (typeof eventTypeOrOpts === 'string') { From 4ab383b62c5f88c2825ccc1238e638bca1a8a5a6 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Tue, 10 May 2022 17:57:14 -0400 Subject: [PATCH 102/370] Add tsdocs for blocking function options (#1121) * updating docs * fixing comments * add blocking options tsdoc * wording --- src/providers/auth.ts | 6 ++++++ src/v2/providers/identity.ts | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/src/providers/auth.ts b/src/providers/auth.ts index fcc612e76..fec895e89 100644 --- a/src/providers/auth.ts +++ b/src/providers/auth.ts @@ -59,9 +59,15 @@ export const service = 'firebaseauth.googleapis.com'; * @public */ export interface UserOptions { + /** Options to set configuration at the resource level for blocking functions. */ blockingOptions?: { + /** Pass the ID Token credential to the function. */ idToken?: boolean; + + /** Pass the Access Token credential to the function. */ accessToken?: boolean; + + /** Pass the Refresh Token credential to the function. */ refreshToken?: boolean; }; } diff --git a/src/v2/providers/identity.ts b/src/v2/providers/identity.ts index b101a8194..e924b0cd7 100644 --- a/src/v2/providers/identity.ts +++ b/src/v2/providers/identity.ts @@ -23,8 +23,13 @@ interface InternalOptions { * All function options plus idToken, accessToken, and refreshToken. */ export interface BlockingOptions { + /** Pass the ID Token credential to the function. */ idToken?: boolean; + + /** Pass the Access Token credential to the function. */ accessToken?: boolean; + + /** Pass the Refresh Token credential to the function. */ refreshToken?: boolean; /** From 1c35efafc2e85a1da76773f891d9554e92df3659 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Tue, 10 May 2022 15:20:05 -0700 Subject: [PATCH 103/370] Improve documentation for task queue functions (#1112) * Improve documentation for task queue functions * PR feedback --- src/common/providers/tasks.ts | 11 +++++++-- src/providers/tasks.ts | 46 +++++++++++++++++++++++------------ src/v2/providers/tasks.ts | 33 +++++++++++++++++++++---- 3 files changed, 68 insertions(+), 22 deletions(-) diff --git a/src/common/providers/tasks.ts b/src/common/providers/tasks.ts index 9c6ffbc23..d84f7a585 100644 --- a/src/common/providers/tasks.ts +++ b/src/common/providers/tasks.ts @@ -61,13 +61,20 @@ export interface RetryConfig { /** How congestion control should be applied to the function. */ export interface RateLimits { - // If left unspecified, wild default to 1000 + /** + * The maximum number of requests that can be outstanding at a time. + * If left unspecified, will default to 1000. + */ maxConcurrentDispatches?: number; - // If left unspecified, will default to 500 + /** + * The maximum number of requests that can be invoked per second. + * If left unspecified, will default to 500. + */ maxDispatchesPerSecond?: number; } +/** Metadata about the authorization used to invoke a function. */ export interface AuthData { uid: string; token: firebase.auth.DecodedIdToken; diff --git a/src/providers/tasks.ts b/src/providers/tasks.ts index 64acc240f..f06e7b92e 100644 --- a/src/providers/tasks.ts +++ b/src/providers/tasks.ts @@ -38,44 +38,55 @@ import { import { DeploymentOptions } from '../function-configuration'; import { ManifestEndpoint, ManifestRequiredAPI } from '../runtime/manifest'; -export { - /** @hidden */ - RetryConfig as RetryPolicy, - /** @hidden */ - RateLimits, - /** @hidden */ - TaskContext, -}; +export { RetryConfig, RateLimits, TaskContext }; /** - * Configurations for Task Queue Functions. - * @hidden + * Options for configuring the task queue to listen to. */ export interface TaskQueueOptions { + /** How a task should be retried in the event of a non-2xx return. */ retryConfig?: RetryConfig; + /** How congestion control should be applied to the function. */ rateLimits?: RateLimits; /** * Who can enqueue tasks for this function. * If left unspecified, only service accounts which have - * roles/cloudtasks.enqueuer and roles/cloudfunctions.invoker + * `roles/cloudtasks.enqueuer` and `roles/cloudfunctions.invoker` * will have permissions. */ invoker?: 'private' | string | string[]; } -/** @hidden */ +/** + * A handler for tasks. + */ export interface TaskQueueFunction { (req: Request, res: express.Response): Promise; + /** @alpha */ __trigger: unknown; + + /** @alpha */ __endpoint: ManifestEndpoint; + + /** @alpha */ __requiredAPIs?: ManifestRequiredAPI[]; + /** + * The callback passed to the `TaskQueueFunction` constructor. + * @param data - The body enqueued into a task queue. + * @param context - The request context of the enqueued task + * @returns Any return value. Google Cloud Functions will await any promise + * before shutting down your function. Resolved return values + * are only used for unit testing purposes. + */ run(data: any, context: TaskContext): void | Promise; } -/** @hidden */ +/** + * Builder for creating a `TaskQueueFunction`. + */ export class TaskQueueBuilder { /** @internal */ constructor( @@ -83,6 +94,11 @@ export class TaskQueueBuilder { private readonly depOpts?: DeploymentOptions ) {} + /** + * Creates a handler for tasks sent to a Google Cloud Tasks queue. + * @param handler - A callback to handle task requests. + * @returns A Cloud Function you can export and deploy. + */ onDispatch( handler: (data: any, context: TaskContext) => void | Promise ): TaskQueueFunction { @@ -137,8 +153,8 @@ export class TaskQueueBuilder { /** * Declares a function that can handle tasks enqueued using the Firebase Admin SDK. - * @param options Configuration for the Task Queue that feeds into this function. - * @hidden + * @param options - Configuration for the Task Queue that feeds into this function. + * Omitting options will configure a Task Queue with default settings. */ export function taskQueue(options?: TaskQueueOptions): TaskQueueBuilder { return new TaskQueueBuilder(options); diff --git a/src/v2/providers/tasks.ts b/src/v2/providers/tasks.ts index 1c175ead8..753c09010 100644 --- a/src/v2/providers/tasks.ts +++ b/src/v2/providers/tasks.ts @@ -35,7 +35,7 @@ import { import * as options from '../options'; import { HttpsFunction } from './https'; -export { AuthData, RateLimits, Request, RetryConfig as RetryPolicy }; +export { AuthData, RateLimits, Request, RetryConfig }; export interface TaskQueueOptions extends options.EventHandlerOptions { /** How a task should be retried in the event of a non-2xx return. */ @@ -47,7 +47,7 @@ export interface TaskQueueOptions extends options.EventHandlerOptions { /** * Who can enqueue tasks for this function. * If left unspecified, only service accounts which have - * roles/cloudtasks.enqueuer and roles/cloudfunctions.invoker + * `roles/cloudtasks.enqueuer` and `roles/cloudfunctions.invoker` * will have permissions. */ invoker?: 'private' | string | string[]; @@ -146,15 +146,38 @@ export interface TaskQueueOptions extends options.EventHandlerOptions { retry?: boolean; } +/** + * A handler for tasks. + * @typeParam T - The task data interface. Task data is unmarshaled from JSON. + */ export interface TaskQueueFunction extends HttpsFunction { - run(data: Request): void | Promise; + /** + * The callback passed to the `TaskQueueFunction` constructor. + * @param request - A TaskRequest containing data and auth information. + * @returns Any return value. Google Cloud Functions will await any promise + * before shutting down your function. Resolved return values + * are only used for unit testing purposes. + */ + run(request: Request): void | Promise; } -/** Handle a request sent to a Cloud Tasks queue. */ +/** + * Creates a handler for tasks sent to a Google Cloud Tasks queue. + * @param handler - A callback to handle task requests. + * @typeParam Args - The interface for the request's `data` field. + * @returns A Cloud Function you can export and deploy. + */ export function onTaskDispatched( handler: (request: Request) => void | Promise ): TaskQueueFunction; -/** Handle a request sent to a Cloud Tasks queue. */ + +/** + * Creates a handler for tasks sent to a Google Cloud Tasks queue. + * @param options - Configuration for the task queue or Cloud Function. + * @param handler - A callback to handle task requests. + * @typeParam Args - The interface for the request's `data` field. + * @returns A Cloud Function you can export and deploy. + */ export function onTaskDispatched( options: TaskQueueOptions, handler: (request: Request) => void | Promise From e720145d04fd5a97e97b72f5d00f1a96a11d8496 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Tue, 10 May 2022 15:59:33 -0700 Subject: [PATCH 104/370] Add package docs and copyright headers (#1116) * Add package docs and copyright headers * Run formatter * Fix copyright date --- src/bin/firebase-functions.ts | 22 +++++++++++++++ src/common/encoding.ts | 22 +++++++++++++++ src/logger/common.ts | 22 +++++++++++++++ src/logger/compat.ts | 22 +++++++++++++++ src/logger/index.ts | 22 +++++++++++++++ src/v2/core.ts | 5 ++++ src/v2/index.ts | 10 ++++++- src/v2/options.ts | 5 ++++ src/v2/params/index.ts | 27 ++++++++++++++++++- src/v2/params/types.ts | 22 +++++++++++++++ src/v2/providers/alerts/alerts.ts | 22 +++++++++++++++ src/v2/providers/alerts/appDistribution.ts | 27 +++++++++++++++++++ src/v2/providers/alerts/billing.ts | 27 +++++++++++++++++++ src/v2/providers/alerts/crashlytics.ts | 27 +++++++++++++++++++ src/v2/providers/alerts/index.ts | 29 ++++++++++++++++++++ src/v2/providers/https.ts | 5 ++++ src/v2/providers/identity.ts | 27 +++++++++++++++++++ src/v2/providers/pubsub.ts | 31 ++++++++++++++++++++++ src/v2/providers/storage.ts | 7 ++++- src/v2/providers/tasks.ts | 5 ++++ 20 files changed, 383 insertions(+), 3 deletions(-) diff --git a/src/bin/firebase-functions.ts b/src/bin/firebase-functions.ts index 4b4eba900..646433fec 100644 --- a/src/bin/firebase-functions.ts +++ b/src/bin/firebase-functions.ts @@ -1,5 +1,27 @@ #!/usr/bin/env node +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as express from 'express'; import { loadStack } from '../runtime/loader'; diff --git a/src/common/encoding.ts b/src/common/encoding.ts index 8960b5ddc..554c5fcb1 100644 --- a/src/common/encoding.ts +++ b/src/common/encoding.ts @@ -1,3 +1,25 @@ +// The MIT License (MIT) +// +// Copyright (c) 2021 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. + // Copied from firebase-tools/src/gcp/proto /** diff --git a/src/logger/common.ts b/src/logger/common.ts index f7ff0de78..020883bbb 100644 --- a/src/logger/common.ts +++ b/src/logger/common.ts @@ -1,3 +1,25 @@ +// 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. + // Determine if structured logs are supported (node >= 10). If something goes wrong, // assume no since unstructured is safer. /** @hidden */ diff --git a/src/logger/compat.ts b/src/logger/compat.ts index 4239221f0..7300a197b 100644 --- a/src/logger/compat.ts +++ b/src/logger/compat.ts @@ -1,3 +1,25 @@ +// 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 { format } from 'util'; import { CONSOLE_SEVERITY, diff --git a/src/logger/index.ts b/src/logger/index.ts index 9bb43725f..02750f845 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -1,3 +1,25 @@ +// 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 { format } from 'util'; import { diff --git a/src/v2/core.ts b/src/v2/core.ts index 533349b55..5e71d2458 100644 --- a/src/v2/core.ts +++ b/src/v2/core.ts @@ -20,6 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +/** + * Core functionality of the Firebase Functions v2 SDK. + * @packageDocumentation + */ + import { ManifestEndpoint } from '../runtime/manifest'; /** @internal */ diff --git a/src/v2/index.ts b/src/v2/index.ts index fd89e52fa..d1cf4836a 100644 --- a/src/v2/index.ts +++ b/src/v2/index.ts @@ -20,6 +20,14 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +/** + * The V2 API for Cloud Functions for Firebase. + * This SDK also supports deep imports. For example, the namespace + * 'pubsub' is available at 'firebase-functions/v2' or is directly importable + * from 'firebase-functions/v2/pubsub'. + * @packageDocumentation + */ + import * as logger from '../logger'; import * as alerts from './providers/alerts'; import * as eventarc from './providers/eventarc'; @@ -29,7 +37,7 @@ import * as pubsub from './providers/pubsub'; import * as storage from './providers/storage'; import * as tasks from './providers/tasks'; -export { alerts, https, identity, pubsub, storage, logger, tasks, eventarc }; +export { alerts, storage, https, identity, pubsub, logger, tasks, eventarc }; export { setGlobalOptions, diff --git a/src/v2/options.ts b/src/v2/options.ts index d8beb1341..c2ebf5248 100644 --- a/src/v2/options.ts +++ b/src/v2/options.ts @@ -20,6 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +/** + * Options to configure cloud functions. + * @packageDocumentation + */ + import { convertIfPresent, copyIfPresent, diff --git a/src/v2/params/index.ts b/src/v2/params/index.ts index f4ea32b53..bf45e8c45 100644 --- a/src/v2/params/index.ts +++ b/src/v2/params/index.ts @@ -1,4 +1,29 @@ -/** @hidden */ +// The MIT License (MIT) +// +// Copyright (c) 2021 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. + +/** + * @hidden + * @alpha + */ import { BooleanParam, FloatParam, diff --git a/src/v2/params/types.ts b/src/v2/params/types.ts index c345cf654..9612dc8dc 100644 --- a/src/v2/params/types.ts +++ b/src/v2/params/types.ts @@ -1,3 +1,25 @@ +// The MIT License (MIT) +// +// Copyright (c) 2021 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. + /** @hidden */ type ParamValueType = 'string' | 'list' | 'boolean' | 'int' | 'float' | 'json'; diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index 6d4e09d54..4cb0ac2de 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -1,3 +1,25 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 { ManifestEndpoint } from '../../../runtime/manifest'; import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index d26f84044..c3f058cff 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -1,3 +1,30 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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. + +/** + * Cloud functions to handle Firebase App Distribution events from Firebase Alerts. + * @packageDocumentation + */ + import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; import { FirebaseAlertData, getEndpointAnnotation } from './alerts'; diff --git a/src/v2/providers/alerts/billing.ts b/src/v2/providers/alerts/billing.ts index 8990481cc..4d42af79b 100644 --- a/src/v2/providers/alerts/billing.ts +++ b/src/v2/providers/alerts/billing.ts @@ -1,3 +1,30 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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. + +/** + * Cloud functions to handle billing events from Firebase Alerts. + * @packageDocumentation + */ + import { FirebaseAlertData, getEndpointAnnotation } from '.'; import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; diff --git a/src/v2/providers/alerts/crashlytics.ts b/src/v2/providers/alerts/crashlytics.ts index 307dda260..6f814320f 100644 --- a/src/v2/providers/alerts/crashlytics.ts +++ b/src/v2/providers/alerts/crashlytics.ts @@ -1,3 +1,30 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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. + +/** + * Cloud functions to handle Crashlytics events from Firebase Alerts. + * @packageDocumentation + */ + import { FirebaseAlertData, getEndpointAnnotation } from '.'; import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; diff --git a/src/v2/providers/alerts/index.ts b/src/v2/providers/alerts/index.ts index ecf90a422..d16ef00fe 100644 --- a/src/v2/providers/alerts/index.ts +++ b/src/v2/providers/alerts/index.ts @@ -1,3 +1,32 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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. + +/** + * Cloud functions to handle events from Firebase Alerts. + * Subpackages give stronger typing to specific services which + * notify users via Firebase Alerts. + * @packageDocumentation + */ + import * as appDistribution from './appDistribution'; import * as billing from './billing'; import * as crashlytics from './crashlytics'; diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 847d65631..2f22f8d75 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -20,6 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +/** + * Cloud functions to handle HTTPS request or callable RPCs. + * @packageDocumentation + */ + import * as cors from 'cors'; import * as express from 'express'; import { convertIfPresent, convertInvoker } from '../../common/encoding'; diff --git a/src/v2/providers/identity.ts b/src/v2/providers/identity.ts index e924b0cd7..b68136d8e 100644 --- a/src/v2/providers/identity.ts +++ b/src/v2/providers/identity.ts @@ -1,3 +1,30 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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. + +/** + * Cloud functions to handle events from Google Cloud Identity Platform. + * @packageDocumentation + */ + import { BlockingFunction } from '../../cloud-functions'; import { AuthBlockingEvent, diff --git a/src/v2/providers/pubsub.ts b/src/v2/providers/pubsub.ts index 09c56d59c..380ed6946 100644 --- a/src/v2/providers/pubsub.ts +++ b/src/v2/providers/pubsub.ts @@ -1,3 +1,30 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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. + +/** + * Cloud functions to handle events from Google Cloud Pub/Sub. + * @packageDocumentation + */ + import { copyIfPresent } from '../../common/encoding'; import { ManifestEndpoint } from '../../runtime/manifest'; import { CloudEvent, CloudFunction } from '../core'; @@ -59,6 +86,10 @@ export class Message { /** @hidden */ private _json: T; + /** + * @hidden + * @alpha + */ constructor(data: any) { this.messageId = data.messageId; this.data = data.data; diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts index ec8068119..33281abd1 100644 --- a/src/v2/providers/storage.ts +++ b/src/v2/providers/storage.ts @@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) 2017 Firebase +// Copyright (c) 2022 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 @@ -20,6 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +/** + * Cloud functions to handle events from Google Cloud Storage. + * @packageDocumentation + */ + import { copyIfPresent } from '../../common/encoding'; import { firebaseConfig } from '../../config'; import { ManifestEndpoint } from '../../runtime/manifest'; diff --git a/src/v2/providers/tasks.ts b/src/v2/providers/tasks.ts index 753c09010..df3b9b8c2 100644 --- a/src/v2/providers/tasks.ts +++ b/src/v2/providers/tasks.ts @@ -20,6 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +/** + * Cloud functions to handle Tasks enqueued with Google Cloud Tasks. + * @packageDocumentation + */ + import { convertIfPresent, convertInvoker, From 5fda331792e60722813e0387a81386e0253aa8a5 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 11 May 2022 08:20:57 +0900 Subject: [PATCH 105/370] Add debug feature to always enable cors for emulated v2 https functions (#1099) * Add debug feature to always enable cors. * Add test cases. * Fix tests. * Add changelog. * Format. * Fix typo. Co-authored-by: Thomas Bouldin --- CHANGELOG.md | 1 + spec/v2/providers/https.spec.ts | 76 ++++++++++++++++++++++++++++++++- src/common/debug.ts | 1 + src/v2/providers/https.ts | 12 ++++-- 4 files changed, 85 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..ef9fc2ff8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Add debug feature to enable cors option for v2 onRequest and onCall handlers. (#1099) diff --git a/spec/v2/providers/https.spec.ts b/spec/v2/providers/https.spec.ts index 345bd8965..6e2be1a23 100644 --- a/spec/v2/providers/https.spec.ts +++ b/spec/v2/providers/https.spec.ts @@ -20,8 +20,10 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +import * as sinon from 'sinon'; import { expect } from 'chai'; +import * as debug from '../../../src/common/debug'; import * as options from '../../../src/v2/options'; import * as https from '../../../src/v2/providers/https'; import { @@ -166,7 +168,7 @@ describe('onRequest', () => { { 'Access-Control-Request-Method': 'POST', 'Access-Control-Request-Headers': 'origin', - Origin: 'example.com', + origin: 'example.com', } ); req.method = 'OPTIONS'; @@ -181,6 +183,41 @@ describe('onRequest', () => { Vary: 'Origin, Access-Control-Request-Headers', }); }); + + it('should add CORS headers if debug feature is enabled', async () => { + sinon + .stub(debug, 'isDebugFeatureEnabled') + .withArgs('enableCors') + .returns(true); + + const func = https.onRequest((req, res) => { + throw new Error('Should not reach here for OPTIONS preflight'); + }); + + const req = new MockRequest( + { + data: {}, + }, + { + 'Access-Control-Request-Method': 'POST', + 'Access-Control-Request-Headers': 'origin', + origin: 'localhost', + } + ); + req.method = 'OPTIONS'; + + const resp = await runHandler(func, req as any); + expect(resp.status).to.equal(204); + expect(resp.body).to.be.undefined; + expect(resp.headers).to.deep.equal({ + 'Access-Control-Allow-Methods': 'GET,HEAD,PUT,PATCH,POST,DELETE', + 'Access-Control-Allow-Origin': 'localhost', + 'Content-Length': '0', + Vary: 'Origin, Access-Control-Request-Headers', + }); + + sinon.restore(); + }); }); describe('onCall', () => { @@ -317,7 +354,7 @@ describe('onCall', () => { { 'Access-Control-Request-Method': 'POST', 'Access-Control-Request-Headers': 'origin', - Origin: 'example.com', + origin: 'example.com', } ); req.method = 'OPTIONS'; @@ -333,6 +370,41 @@ describe('onCall', () => { }); }); + it('overrides CORS headers if debug feature is enabled', async () => { + sinon + .stub(debug, 'isDebugFeatureEnabled') + .withArgs('enableCors') + .returns(true); + + const func = https.onCall({ cors: 'example.com' }, (request) => { + throw new Error('Should not reach here for OPTIONS preflight'); + }); + const req = new MockRequest( + { + data: {}, + }, + { + 'Access-Control-Request-Method': 'POST', + 'Access-Control-Request-Headers': 'origin', + origin: 'localhost', + } + ); + req.method = 'OPTIONS'; + + const response = await runHandler(func, req as any); + + expect(response.status).to.equal(204); + expect(response.body).to.be.undefined; + expect(response.headers).to.deep.equal({ + 'Access-Control-Allow-Methods': 'POST', + 'Access-Control-Allow-Origin': 'localhost', + 'Content-Length': '0', + Vary: 'Origin, Access-Control-Request-Headers', + }); + + sinon.restore(); + }); + it('adds CORS headers', async () => { const func = https.onCall((request) => 42); const req = new MockRequest( diff --git a/src/common/debug.ts b/src/common/debug.ts index 4e2fd2a8c..55221c9e8 100644 --- a/src/common/debug.ts +++ b/src/common/debug.ts @@ -25,6 +25,7 @@ const debugMode = process.env.FIREBASE_DEBUG_MODE === 'true'; interface DebugFeatures { skipTokenVerification?: boolean; + enableCors?: boolean; } function loadDebugFeatures(): DebugFeatures { diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 2f22f8d75..980edded1 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -39,6 +39,7 @@ import { import { ManifestEndpoint } from '../../runtime/manifest'; import * as options from '../options'; import { GlobalOptions, SupportedRegion } from '../options'; +import { isDebugFeatureEnabled } from '../../common/debug'; export { Request, CallableRequest, FunctionsErrorCode, HttpsError }; @@ -218,12 +219,13 @@ export function onRequest( opts = optsOrHandler as HttpsOptions; } - if ('cors' in opts) { + if (isDebugFeatureEnabled('enableCors') || 'cors' in opts) { + const origin = isDebugFeatureEnabled('enableCors') ? true : opts.cors; const userProvidedHandler = handler; handler = (req: Request, res: express.Response): void | Promise => { return new Promise((resolve) => { res.on('finish', resolve); - cors({ origin: opts.cors })(req, res, () => { + cors({ origin })(req, res, () => { resolve(userProvidedHandler(req, res)); }); }); @@ -319,7 +321,11 @@ export function onCall>( opts = optsOrHandler as HttpsOptions; } - const origin = 'cors' in opts ? opts.cors : true; + const origin = isDebugFeatureEnabled('enableCors') + ? true + : 'cors' in opts + ? opts.cors + : true; // onCallHandler sniffs the function length to determine which API to present. // fix the length to prevent api versions from being mismatched. From 341ff0d2a488de1c684b1e660871062fd2bbee4a Mon Sep 17 00:00:00 2001 From: egilmorez Date: Tue, 10 May 2022 17:11:40 -0700 Subject: [PATCH 106/370] Adding new TOC entries. (#1122) * half-hearted attempt at rushstack - much work to be done * Update deps, remove generated files. * Pretty * Adding tasks entries to TOC source. * Fixing per feedback and removing package-lock file (I hope). * Resolving package lock file. * Restore package-lock.json * Run formatter * Adding entries, possibly all of them now. * Adding fixes from linter. * Fixing typo. * Run formatter Co-authored-by: Michael Bleigh Co-authored-by: Daniel Young Lee Co-authored-by: Thomas Bouldin --- docgen/content-sources/v1/toc.yaml | 27 +++++++++++++++++++++++++++ spec/v2/providers/https.spec.ts | 2 +- src/v2/providers/https.ts | 2 +- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/docgen/content-sources/v1/toc.yaml b/docgen/content-sources/v1/toc.yaml index c4091f15d..f6bb5ce0a 100644 --- a/docgen/content-sources/v1/toc.yaml +++ b/docgen/content-sources/v1/toc.yaml @@ -14,6 +14,8 @@ toc: path: /docs/reference/functions/cloud_functions.change.html - title: 'ChangeJson' path: /docs/reference/functions/cloud_functions.changejson.html + - title: 'BlockingFunction' + path: /docs/reference/functions/cloud_functions.blockingfunction.html - title: 'functions.config' path: /docs/reference/functions/config.html @@ -124,6 +126,8 @@ toc: path: /docs/reference/functions/providers_remoteconfig.remoteconfiguser.html - title: 'TemplateVersion' path: /docs/reference/functions/providers_remoteconfig.templateversion.html + - title: 'UpdateBuilder' + path: /docs/reference/functions/providers_remoteconfig.updatebuilder.html - title: 'functions.storage' path: /docs/reference/functions/providers_storage.html @@ -135,6 +139,24 @@ toc: - title: 'ObjectMetadata' path: /docs/reference/functions/providers_storage.objectmetadata.html + - title: 'functions.tasks' + path: /docs/reference/functions/providers_tasks.html + section: + - title: AuthData + path: /docs/reference/functions/common_providers_tasks.authdata.html + - title: RateLimits + path: /docs/reference/functions/common_providers_tasks.ratelimits.html + - title: RetryConfig + path: /docs/reference/functions/common_providers_tasks.retryconfig.html + - title: TaskContext + path: /docs/reference/functions/common_providers_tasks.taskcontext.html + - title: TaskQueueBuilder + path: /docs/reference/functions/providers_tasks.taskqueuebuilder.html + - title: TaskQueueFunction + path: /docs/reference/functions/providers_tasks.taskqueuefunction.html + - title: TaskQueueOptions + path: /docs/reference/functions/providers_tasks.taskqueueoptions.html + - title: 'functions.testLab' path: /docs/reference/functions/providers_testlab.html section: @@ -152,3 +174,8 @@ toc: section: - title: 'HandlerBuilder' path: /docs/reference/functions/handler_builder.handlerbuilder.html + + - heading: Test SDK for Cloud Functions for Firebase + - title: firebase-functions-test + section: + - include: /docs/reference/functions/test/_toc.yaml diff --git a/spec/v2/providers/https.spec.ts b/spec/v2/providers/https.spec.ts index 6e2be1a23..65e573849 100644 --- a/spec/v2/providers/https.spec.ts +++ b/spec/v2/providers/https.spec.ts @@ -20,8 +20,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -import * as sinon from 'sinon'; import { expect } from 'chai'; +import * as sinon from 'sinon'; import * as debug from '../../../src/common/debug'; import * as options from '../../../src/v2/options'; diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 980edded1..dad7355cd 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -29,6 +29,7 @@ import * as cors from 'cors'; import * as express from 'express'; import { convertIfPresent, convertInvoker } from '../../common/encoding'; +import { isDebugFeatureEnabled } from '../../common/debug'; import { CallableRequest, FunctionsErrorCode, @@ -39,7 +40,6 @@ import { import { ManifestEndpoint } from '../../runtime/manifest'; import * as options from '../options'; import { GlobalOptions, SupportedRegion } from '../options'; -import { isDebugFeatureEnabled } from '../../common/debug'; export { Request, CallableRequest, FunctionsErrorCode, HttpsError }; From 39ee2de401b665e9850024f031054c64db765fdb Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Tue, 10 May 2022 17:27:23 -0700 Subject: [PATCH 107/370] Make private APIs hidden (#1123) --- src/providers/auth.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/providers/auth.ts b/src/providers/auth.ts index fec895e89..f845424ea 100644 --- a/src/providers/auth.ts +++ b/src/providers/auth.ts @@ -161,6 +161,7 @@ export class UserBuilder { return this.beforeOperation(handler, 'beforeSignIn'); } + /** @hidden */ private onOperation( handler: ( user: UserRecord, @@ -180,6 +181,7 @@ export class UserBuilder { }); } + /** @hidden */ private beforeOperation( handler: ( user: AuthUserRecord, From 4efd0b06ea98aa13bca80f323066620eabf67bac Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 11 May 2022 07:29:59 -0700 Subject: [PATCH 108/370] Temporarily remove docs for fireabse-functions-test (#1124) --- docgen/content-sources/v1/toc.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docgen/content-sources/v1/toc.yaml b/docgen/content-sources/v1/toc.yaml index f6bb5ce0a..59e380458 100644 --- a/docgen/content-sources/v1/toc.yaml +++ b/docgen/content-sources/v1/toc.yaml @@ -174,8 +174,3 @@ toc: section: - title: 'HandlerBuilder' path: /docs/reference/functions/handler_builder.handlerbuilder.html - - - heading: Test SDK for Cloud Functions for Firebase - - title: firebase-functions-test - section: - - include: /docs/reference/functions/test/_toc.yaml From 715bdfe87ffbf3e93ca7d682eadc88c352a0453e Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 11 May 2022 21:39:20 +0000 Subject: [PATCH 109/370] 3.21.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 e2500d647..832969104 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.21.0", + "version": "3.21.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index aca2baeba..236bc4a22 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.21.0", + "version": "3.21.1", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 27a49837ea7b74d9d2926e74c387f45704d57889 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 11 May 2022 21:39:24 +0000 Subject: [PATCH 110/370] [firebase-release] Removed change log and reset repo after 3.21.1 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef9fc2ff8..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Add debug feature to enable cors option for v2 onRequest and onCall handlers. (#1099) From cb90fab75b312b19a10f19a56feb5e42fef5e795 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Mon, 16 May 2022 11:52:42 -0400 Subject: [PATCH 111/370] Fix common identity package internal toJSON function (#1125) * fixing the toJSON function * fix nit * adding changelog --- CHANGELOG.md | 1 + spec/common/providers/identity.spec.ts | 28 ++++++++++++++++++++++++++ src/common/providers/identity.ts | 6 +++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..50a3a0093 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Fixes bug where `toJSON` was not defined in `UserRecord` (#1125). diff --git a/spec/common/providers/identity.spec.ts b/spec/common/providers/identity.spec.ts index 046ffa8b3..592d14767 100644 --- a/spec/common/providers/identity.spec.ts +++ b/spec/common/providers/identity.spec.ts @@ -88,6 +88,34 @@ describe('identity', () => { lastSignInTime: '2017-02-02T23:01:19.797Z', }); }); + + it('should stringify the record', () => { + const raw: any = { + uid: '123', + email: 'email@gmail.com', + emailVerified: true, + displayName: 'User', + photoURL: 'url', + phoneNumber: '1233332222', + disabled: true, + providerData: ['something'], + customClaims: { + claim: 'value', + another: { + inner: 'value', + }, + }, + passwordSalt: 'abc', + passwordHash: 'def', + tokensValidAfterTime: '2027-02-02T23:01:19.797Z', + metadata: { + creationTime: '2017-02-02T23:06:26.124Z', + lastSignInTime: '2017-02-02T23:01:19.797Z', + }, + }; + const record = identity.userRecordConstructor(raw); + expect(() => JSON.stringify(record)).to.not.throw; + }); }); describe('isValidRequest', () => { diff --git a/src/common/providers/identity.ts b/src/common/providers/identity.ts index 08658c48d..948c73ce9 100644 --- a/src/common/providers/identity.ts +++ b/src/common/providers/identity.ts @@ -146,7 +146,11 @@ export function userRecordConstructor(wireData: Object): UserRecord { }; json.metadata = record.metadata.toJSON(); json.customClaims = JSON.parse(JSON.stringify(record.customClaims)); - json.providerData = record.providerData.map((entry) => entry.toJSON()); + json.providerData = record.providerData.map((entry) => { + const newEntry = { ...entry }; + newEntry.toJSON = () => entry; + return newEntry; + }); return json; }; return record as UserRecord; From 301720f2c5f47ec2340369a8735fb5bf5bee3b67 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Tue, 17 May 2022 18:40:03 +0000 Subject: [PATCH 112/370] 3.21.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 832969104..c11eebe69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.21.1", + "version": "3.21.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 236bc4a22..026c79db3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.21.1", + "version": "3.21.2", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 40f0b4331517755fcd0a906d9e5361ad7305aa05 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Tue, 17 May 2022 18:40:06 +0000 Subject: [PATCH 113/370] [firebase-release] Removed change log and reset repo after 3.21.2 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50a3a0093..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Fixes bug where `toJSON` was not defined in `UserRecord` (#1125). From e42fe3aecf091d4e31efae1ab3a3a9b28d05ecd5 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Thu, 9 Jun 2022 13:53:41 -0400 Subject: [PATCH 114/370] Adds RTDB triggers for v2 functions (#1127) * moving DataSnapshot to common, removing lodash, and adding in initial v2 interface and logic * fixing tests * normalize path & remove commented structure * changing default rtdb url * defualt to * instances and export everything correctly * linter * fixed parsing for multi segments and adding in tests * update tsdoc comments and add changelog * fixing export path * cleaning up params code * ref is always a path pattern * addressing comments and creating path pattern class * linter * adding describe block * adding in changes from @rhodgkins to match the admin SDK * linter --- CHANGELOG.md | 1 + package.json | 6 +- spec/utilities/path-pattern.spec.ts | 145 +++++++ spec/v1/providers/database.spec.ts | 143 ++++++- spec/v2/providers/database.spec.ts | 569 ++++++++++++++++++++++++++++ src/common/providers/database.ts | 338 +++++++++++++++++ src/providers/database.ts | 296 +-------------- src/utilities/path-pattern.ts | 173 +++++++++ src/v2/index.ts | 13 +- src/v2/providers/database.ts | 409 ++++++++++++++++++++ v2/database.js | 26 ++ 11 files changed, 1816 insertions(+), 303 deletions(-) create mode 100644 spec/utilities/path-pattern.spec.ts create mode 100644 spec/v2/providers/database.spec.ts create mode 100644 src/common/providers/database.ts create mode 100644 src/utilities/path-pattern.ts create mode 100644 src/v2/providers/database.ts create mode 100644 v2/database.js diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..b174a17fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Adds RTDB Triggers for v2 functions (#1127) diff --git a/package.json b/package.json index 026c79db3..03f56c851 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,8 @@ "./v2/alerts/billing": "./lib/v2/providers/alerts/billing.js", "./v2/alerts/crashlytics": "./lib/v2/providers/alerts/crashlytics.js", "./v2/eventarc": "./lib/v2/providers/eventarc.js", - "./v2/identity": "./lib/v2/providers/identity.js" + "./v2/identity": "./lib/v2/providers/identity.js", + "./v2/database": "./lib/v2/providers/database.js" }, "typesVersions": { "*": { @@ -126,6 +127,9 @@ "v2/base": [ "lib/v2/base" ], + "v2/database": [ + "lib/v2/providers/database" + ], "v2/eventarc": [ "lib/v2/providers/eventarc" ], diff --git a/spec/utilities/path-pattern.spec.ts b/spec/utilities/path-pattern.spec.ts new file mode 100644 index 000000000..8fe513284 --- /dev/null +++ b/spec/utilities/path-pattern.spec.ts @@ -0,0 +1,145 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as pathPattern from '../../src/utilities/path-pattern'; + +describe('path-pattern', () => { + describe('trimParam', () => { + it('should trim a capture param without equals', () => { + expect(pathPattern.trimParam('{something}')).to.equal('something'); + }); + + it('should trim a capture param with equals', () => { + expect(pathPattern.trimParam('{something=*}')).to.equal('something'); + }); + }); + + describe('extractMatches', () => { + it('should parse without multi segment', () => { + const pp = new pathPattern.PathPattern('{a}/something/else/{b}/end/{c}'); + + expect( + pp.extractMatches('match_a/something/else/match_b/end/match_c') + ).to.deep.equal({ + a: 'match_a', + b: 'match_b', + c: 'match_c', + }); + }); + + it('should parse multi segment with params after', () => { + const pp = new pathPattern.PathPattern( + 'something/**/else/{a}/hello/{b}/world' + ); + + expect( + pp.extractMatches('something/is/a/thing/else/nothing/hello/user/world') + ).to.deep.equal({ + a: 'nothing', + b: 'user', + }); + }); + + it('should parse multi segment param with params after', () => { + const pp = new pathPattern.PathPattern( + 'something/{path=**}/else/{a}/hello/{b}/world' + ); + + expect( + pp.extractMatches('something/is/a/thing/else/nothing/hello/user/world') + ).to.deep.equal({ + path: 'is/a/thing', + a: 'nothing', + b: 'user', + }); + }); + + it('should parse multi segment with params before', () => { + const pp = new pathPattern.PathPattern('{a}/something/{b}/**/end'); + + expect( + pp.extractMatches( + 'match_a/something/match_b/thing/else/nothing/hello/user/end' + ) + ).to.deep.equal({ + a: 'match_a', + b: 'match_b', + }); + }); + + it('should parse multi segment param with params before', () => { + const pp = new pathPattern.PathPattern('{a}/something/{b}/{path=**}/end'); + + expect( + pp.extractMatches( + 'match_a/something/match_b/thing/else/nothing/hello/user/end' + ) + ).to.deep.equal({ + a: 'match_a', + b: 'match_b', + path: 'thing/else/nothing/hello/user', + }); + }); + + it('should parse multi segment with params before and after', () => { + const pp = new pathPattern.PathPattern('{a}/something/**/{b}/end'); + + expect( + pp.extractMatches( + 'match_a/something/thing/else/nothing/hello/user/match_b/end' + ) + ).to.deep.equal({ + a: 'match_a', + b: 'match_b', + }); + }); + + it('should parse multi segment param with params before', () => { + const pp = new pathPattern.PathPattern('{a}/something/{path=**}/{b}/end'); + + expect( + pp.extractMatches( + 'match_a/something/thing/else/nothing/hello/user/match_b/end' + ) + ).to.deep.equal({ + a: 'match_a', + b: 'match_b', + path: 'thing/else/nothing/hello/user', + }); + }); + + // handle an instance param + it('should parse an instance', () => { + const pp = new pathPattern.PathPattern('{a}-something-{b}-else-{c}'); + + expect( + pp.extractMatches('match_a-something-match_b-else-match_c') + ).to.deep.equal({}); + + const anotherPP = new pathPattern.PathPattern('{a}'); + + expect(anotherPP.extractMatches('match_a')).to.deep.equal({ + a: 'match_a', + }); + }); + }); +}); diff --git a/spec/v1/providers/database.spec.ts b/spec/v1/providers/database.spec.ts index 304c1d1fd..30d24ea3c 100644 --- a/spec/v1/providers/database.spec.ts +++ b/spec/v1/providers/database.spec.ts @@ -639,10 +639,6 @@ describe('Database Functions', () => { expect(subject.val()).to.equal(0); populate({ myKey: 0 }); expect(subject.val()).to.deep.equal({ myKey: 0 }); - - // Null values are still reported as null. - populate({ myKey: null }); - expect(subject.val()).to.deep.equal({ myKey: null }); }); // Regression test: .val() was returning array of nulls when there's a property called length (BUG#37683995) @@ -650,6 +646,45 @@ describe('Database Functions', () => { populate({ length: 3, foo: 'bar' }); expect(subject.val()).to.deep.equal({ length: 3, foo: 'bar' }); }); + + it('should deal with null-values appropriately', () => { + populate(null); + expect(subject.val()).to.be.null; + + populate({ myKey: null }); + expect(subject.val()).to.be.null; + }); + + it('should deal with empty object values appropriately', () => { + populate({}); + expect(subject.val()).to.be.null; + + populate({ myKey: {} }); + expect(subject.val()).to.be.null; + + populate({ myKey: { child: null } }); + expect(subject.val()).to.be.null; + }); + + it('should deal with empty array values appropriately', () => { + populate([]); + expect(subject.val()).to.be.null; + + populate({ myKey: [] }); + expect(subject.val()).to.be.null; + + populate({ myKey: [null] }); + expect(subject.val()).to.be.null; + + populate({ myKey: [{}] }); + expect(subject.val()).to.be.null; + + populate({ myKey: [{ myKey: null }] }); + expect(subject.val()).to.be.null; + + populate({ myKey: [{ myKey: {} }] }); + expect(subject.val()).to.be.null; + }); }); describe('#child(): DataSnapshot', () => { @@ -676,14 +711,37 @@ describe('Database Functions', () => { }); it('should be false for a non-existent value', () => { - populate({ a: { b: 'c' } }); + populate({ a: { b: 'c', nullChild: null } }); expect(subject.child('d').exists()).to.be.false; + expect(subject.child('nullChild').exists()).to.be.false; }); it('should be false for a value pathed beyond a leaf', () => { populate({ a: { b: 'c' } }); expect(subject.child('a/b/c').exists()).to.be.false; }); + + it('should be false for an empty object value', () => { + populate({ a: {} }); + expect(subject.child('a').exists()).to.be.false; + + populate({ a: { child: null } }); + expect(subject.child('a').exists()).to.be.false; + + populate({ a: { child: {} } }); + expect(subject.child('a').exists()).to.be.false; + }); + + it('should be false for an empty array value', () => { + populate({ a: [] }); + expect(subject.child('a').exists()).to.be.false; + + populate({ a: [null] }); + expect(subject.child('a').exists()).to.be.false; + + populate({ a: [{}] }); + expect(subject.child('a').exists()).to.be.false; + }); }); describe('#forEach(action: (a: DataSnapshot) => boolean): boolean', () => { @@ -712,6 +770,17 @@ describe('Database Functions', () => { expect(subject.forEach(counter)).to.equal(false); expect(count).to.eq(0); + + populate({ + a: 'foo', + nullChild: null, + emptyObjectChild: {}, + emptyArrayChild: [], + }); + count = 0; + + expect(subject.forEach(counter)).to.equal(false); + expect(count).to.eq(1); }); it('should cancel further enumeration if callback returns true', () => { @@ -751,13 +820,51 @@ describe('Database Functions', () => { describe('#numChildren()', () => { it('should be key count for objects', () => { - populate({ a: 'b', c: 'd' }); + populate({ + a: 'b', + c: 'd', + nullChild: null, + emptyObjectChild: {}, + emptyArrayChild: [], + }); expect(subject.numChildren()).to.eq(2); }); it('should be 0 for non-objects', () => { populate(23); expect(subject.numChildren()).to.eq(0); + + populate({ + nullChild: null, + emptyObjectChild: {}, + emptyArrayChild: [], + }); + expect(subject.numChildren()).to.eq(0); + }); + }); + + describe('#hasChildren()', () => { + it('should true for objects', () => { + populate({ + a: 'b', + c: 'd', + nullChild: null, + emptyObjectChild: {}, + emptyArrayChild: [], + }); + expect(subject.hasChildren()).to.be.true; + }); + + it('should be false for non-objects', () => { + populate(23); + expect(subject.hasChildren()).to.be.false; + + populate({ + nullChild: null, + emptyObjectChild: {}, + emptyArrayChild: [], + }); + expect(subject.hasChildren()).to.be.false; }); }); @@ -769,9 +876,17 @@ describe('Database Functions', () => { }); it('should return false if a child is missing', () => { - populate({ a: 'b' }); + populate({ + a: 'b', + nullChild: null, + emptyObjectChild: {}, + emptyArrayChild: [], + }); expect(subject.hasChild('c')).to.be.false; expect(subject.hasChild('a/b')).to.be.false; + expect(subject.hasChild('nullChild')).to.be.false; + expect(subject.hasChild('emptyObjectChild')).to.be.false; + expect(subject.hasChild('emptyArrayChild')).to.be.false; }); }); @@ -801,11 +916,21 @@ describe('Database Functions', () => { describe('#toJSON(): Object', () => { it('should return the current value', () => { - populate({ a: 'b' }); + populate({ + a: 'b', + nullChild: null, + emptyObjectChild: {}, + emptyArrayChild: [], + }); expect(subject.toJSON()).to.deep.equal(subject.val()); }); it('should be stringifyable', () => { - populate({ a: 'b' }); + populate({ + a: 'b', + nullChild: null, + emptyObjectChild: {}, + emptyArrayChild: [], + }); expect(JSON.stringify(subject)).to.deep.equal('{"a":"b"}'); }); }); diff --git a/spec/v2/providers/database.spec.ts b/spec/v2/providers/database.spec.ts new file mode 100644 index 000000000..58d4285ab --- /dev/null +++ b/spec/v2/providers/database.spec.ts @@ -0,0 +1,569 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 { PathPattern } from '../../../src/utilities/path-pattern'; +import * as database from '../../../src/v2/providers/database'; + +const RAW_RTDB_EVENT: database.RawRTDBCloudEvent = { + data: { + ['@type']: + 'type.googleapis.com/google.events.firebase.database.v1.ReferenceEventData', + data: {}, + delta: {}, + }, + firebasedatabasehost: 'firebaseio.com', + instance: 'my-instance', + ref: 'foo/bar', + location: 'us-central1', + id: 'id', + source: 'source', + specversion: '1.0', + time: 'time', + type: 'type', +}; + +describe('database', () => { + describe('makeParams', () => { + it('should make params with basic path', () => { + const event: database.RawRTDBCloudEvent = { + ...RAW_RTDB_EVENT, + ref: 'match_a/something/else/nothing/end/match_b', + }; + + expect( + database.makeParams( + event, + new PathPattern('{a}/something/else/*/end/{b}'), + new PathPattern('*') + ) + ).to.deep.equal({ + a: 'match_a', + b: 'match_b', + }); + }); + + it('should make params with multi segment path', () => { + const event: database.RawRTDBCloudEvent = { + ...RAW_RTDB_EVENT, + ref: 'something/is/a/thing/else/match_a/hello/match_b/world', + }; + + expect( + database.makeParams( + event, + new PathPattern('something/**/else/{a}/hello/{b}/world'), + new PathPattern('*') + ) + ).to.deep.equal({ + a: 'match_a', + b: 'match_b', + }); + }); + + it('should make params with multi segment path capture', () => { + const event: database.RawRTDBCloudEvent = { + ...RAW_RTDB_EVENT, + ref: 'something/is/a/thing/else/match_a/hello/match_b/world', + }; + + expect( + database.makeParams( + event, + new PathPattern('something/{path=**}/else/{a}/hello/{b}/world'), + new PathPattern('*') + ) + ).to.deep.equal({ + path: 'is/a/thing', + a: 'match_a', + b: 'match_b', + }); + }); + + it('should make params for a full path and instance', () => { + const event: database.RawRTDBCloudEvent = { + ...RAW_RTDB_EVENT, + ref: 'something/is/a/thing/else/match_a/hello/match_b/world', + }; + + expect( + database.makeParams( + event, + new PathPattern('something/{path=**}/else/{a}/hello/{b}/world'), + new PathPattern('{inst}') + ) + ).to.deep.equal({ + path: 'is/a/thing', + a: 'match_a', + b: 'match_b', + inst: 'my-instance', + }); + }); + }); + + describe('getOpts', () => { + it('should return opts when passed in a path', () => { + expect(database.getOpts('/foo/{bar}/')).to.deep.equal({ + path: 'foo/{bar}', + instance: '*', + opts: {}, + }); + }); + + it('should return opts when passed in an options object', () => { + expect( + database.getOpts({ + ref: '/foo/{bar}/', + instance: '{inst}', + region: 'us-central1', + }) + ).to.deep.equal({ + path: 'foo/{bar}', + instance: '{inst}', + opts: { + region: 'us-central1', + }, + }); + }); + }); + + describe('makeEndpoint', () => { + it('should create an endpoint with an instance wildcard', () => { + const ep = database.makeEndpoint( + database.writtenEventType, + { + region: 'us-central1', + labels: { 1: '2' }, + }, + new PathPattern('foo/bar'), + new PathPattern('{inst}') + ); + + expect(ep).to.deep.equal({ + platform: 'gcfv2', + labels: { + 1: '2', + }, + region: ['us-central1'], + eventTrigger: { + eventType: database.writtenEventType, + eventFilters: {}, + eventFilterPathPatterns: { + ref: 'foo/bar', + instance: '{inst}', + }, + retry: false, + }, + }); + }); + + it('should create an endpoint without an instance wildcard', () => { + const ep = database.makeEndpoint( + database.writtenEventType, + { + region: 'us-central1', + labels: { 1: '2' }, + }, + new PathPattern('foo/bar'), + new PathPattern('my-instance') + ); + + expect(ep).to.deep.equal({ + platform: 'gcfv2', + labels: { + 1: '2', + }, + region: ['us-central1'], + eventTrigger: { + eventType: database.writtenEventType, + eventFilters: { + instance: 'my-instance', + }, + eventFilterPathPatterns: { + ref: 'foo/bar', + }, + retry: false, + }, + }); + }); + }); + + describe('onChangedOperation', () => { + it('should create a function for a written event', () => { + const func = database.onChangedOperation( + database.writtenEventType, + '/foo/{bar}/', + (event) => 2 + ); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: database.writtenEventType, + eventFilters: {}, + eventFilterPathPatterns: { + ref: 'foo/{bar}', + instance: '*', + }, + retry: false, + }, + }); + }); + + it('should create a function for a updated event', () => { + const func = database.onChangedOperation( + database.updatedEventType, + '/foo/{bar}/', + (event) => 2 + ); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: database.updatedEventType, + eventFilters: {}, + eventFilterPathPatterns: { + ref: 'foo/{bar}', + instance: '*', + }, + retry: false, + }, + }); + }); + + it('should create a complex function', () => { + const func = database.onChangedOperation( + database.writtenEventType, + { + ref: '/foo/{path=**}/{bar}/', + instance: 'my-instance', + region: 'us-central1', + cpu: 'gcf_gen1', + minInstances: 2, + }, + (event) => 2 + ); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + cpu: 'gcf_gen1', + minInstances: 2, + region: ['us-central1'], + labels: {}, + eventTrigger: { + eventType: database.writtenEventType, + eventFilters: { + instance: 'my-instance', + }, + eventFilterPathPatterns: { + ref: 'foo/{path=**}/{bar}', + }, + retry: false, + }, + }); + }); + }); + + describe('onOperation', () => { + it('should create a function for a created event', () => { + const func = database.onOperation( + database.createdEventType, + '/foo/{bar}/', + (event) => 2 + ); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: database.createdEventType, + eventFilters: {}, + eventFilterPathPatterns: { + ref: 'foo/{bar}', + instance: '*', + }, + retry: false, + }, + }); + }); + + it('should create a function for a deleted event', () => { + const func = database.onOperation( + database.deletedEventType, + '/foo/{bar}/', + (event) => 2 + ); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: database.deletedEventType, + eventFilters: {}, + eventFilterPathPatterns: { + ref: 'foo/{bar}', + instance: '*', + }, + retry: false, + }, + }); + }); + + it('should create a complex function', () => { + const func = database.onOperation( + database.createdEventType, + { + ref: '/foo/{path=**}/{bar}/', + instance: 'my-instance', + region: 'us-central1', + cpu: 'gcf_gen1', + minInstances: 2, + }, + (event) => 2 + ); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + cpu: 'gcf_gen1', + minInstances: 2, + region: ['us-central1'], + labels: {}, + eventTrigger: { + eventType: database.createdEventType, + eventFilters: { + instance: 'my-instance', + }, + eventFilterPathPatterns: { + ref: 'foo/{path=**}/{bar}', + }, + retry: false, + }, + }); + }); + }); + + describe('onRefWritten', () => { + it('should create a function with a reference', () => { + const func = database.onRefWritten('/foo/{bar}/', (event) => 2); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: database.writtenEventType, + eventFilters: {}, + eventFilterPathPatterns: { + ref: 'foo/{bar}', + instance: '*', + }, + retry: false, + }, + }); + }); + + it('should create a function with opts', () => { + const func = database.onRefWritten( + { + ref: '/foo/{path=**}/{bar}/', + instance: 'my-instance', + region: 'us-central1', + cpu: 'gcf_gen1', + minInstances: 2, + }, + (event) => 2 + ); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + cpu: 'gcf_gen1', + minInstances: 2, + region: ['us-central1'], + labels: {}, + eventTrigger: { + eventType: database.writtenEventType, + eventFilters: { + instance: 'my-instance', + }, + eventFilterPathPatterns: { + ref: 'foo/{path=**}/{bar}', + }, + retry: false, + }, + }); + }); + }); + + describe('onRefCreated', () => { + it('should create a function with a reference', () => { + const func = database.onRefCreated('/foo/{bar}/', (event) => 2); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: database.createdEventType, + eventFilters: {}, + eventFilterPathPatterns: { + ref: 'foo/{bar}', + instance: '*', + }, + retry: false, + }, + }); + }); + + it('should create a function with opts', () => { + const func = database.onRefCreated( + { + ref: '/foo/{path=**}/{bar}/', + instance: 'my-instance', + region: 'us-central1', + cpu: 'gcf_gen1', + minInstances: 2, + }, + (event) => 2 + ); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + cpu: 'gcf_gen1', + minInstances: 2, + region: ['us-central1'], + labels: {}, + eventTrigger: { + eventType: database.createdEventType, + eventFilters: { + instance: 'my-instance', + }, + eventFilterPathPatterns: { + ref: 'foo/{path=**}/{bar}', + }, + retry: false, + }, + }); + }); + }); + + describe('onRefUpdated', () => { + it('should create a function with a reference', () => { + const func = database.onRefUpdated('/foo/{bar}/', (event) => 2); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: database.updatedEventType, + eventFilters: {}, + eventFilterPathPatterns: { + ref: 'foo/{bar}', + instance: '*', + }, + retry: false, + }, + }); + }); + + it('should create a function with opts', () => { + const func = database.onRefUpdated( + { + ref: '/foo/{path=**}/{bar}/', + instance: 'my-instance', + region: 'us-central1', + cpu: 'gcf_gen1', + minInstances: 2, + }, + (event) => 2 + ); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + cpu: 'gcf_gen1', + minInstances: 2, + region: ['us-central1'], + labels: {}, + eventTrigger: { + eventType: database.updatedEventType, + eventFilters: { + instance: 'my-instance', + }, + eventFilterPathPatterns: { + ref: 'foo/{path=**}/{bar}', + }, + retry: false, + }, + }); + }); + }); + + describe('onRefDeleted', () => { + it('should create a function with a reference', () => { + const func = database.onRefDeleted('/foo/{bar}/', (event) => 2); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: database.deletedEventType, + eventFilters: {}, + eventFilterPathPatterns: { + ref: 'foo/{bar}', + instance: '*', + }, + retry: false, + }, + }); + }); + + it('should create a function with opts', () => { + const func = database.onRefDeleted( + { + ref: '/foo/{path=**}/{bar}/', + instance: 'my-instance', + region: 'us-central1', + cpu: 'gcf_gen1', + minInstances: 2, + }, + (event) => 2 + ); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + cpu: 'gcf_gen1', + minInstances: 2, + region: ['us-central1'], + labels: {}, + eventTrigger: { + eventType: database.deletedEventType, + eventFilters: { + instance: 'my-instance', + }, + eventFilterPathPatterns: { + ref: 'foo/{path=**}/{bar}', + }, + retry: false, + }, + }); + }); + }); +}); diff --git a/src/common/providers/database.ts b/src/common/providers/database.ts new file mode 100644 index 000000000..f14ce8b1d --- /dev/null +++ b/src/common/providers/database.ts @@ -0,0 +1,338 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as firebase from 'firebase-admin'; +import { firebaseConfig } from '../../config'; +import { joinPath, pathParts } from '../../utilities/path'; + +/** + * Interface representing a Firebase Realtime database data snapshot. + */ +export class DataSnapshot implements firebase.database.DataSnapshot { + public instance: string; + + /** @hidden */ + private _ref: firebase.database.Reference; + + /** @hidden */ + private _path: string; + + /** @hidden */ + private _data: any; + + /** @hidden */ + private _childPath: string; + + constructor( + data: any, + path?: string, // path is undefined for the database root + private app?: firebase.app.App, + instance?: string + ) { + const config = firebaseConfig(); + if (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) { + this.instance = app.options.databaseURL; + } else if (config.databaseURL) { + this.instance = config.databaseURL; + } else if (process.env.GCLOUD_PROJECT) { + this.instance = + 'https://' + + process.env.GCLOUD_PROJECT + + '-default-rtdb.firebaseio.com'; + } + + this._path = path; + this._data = data; + } + + /** + * Returns a [`Reference`](/docs/reference/admin/node/admin.database.Reference) + * to the database location where the triggering write occurred. Has + * full read and write access. + */ + get ref(): firebase.database.Reference { + if (!this.app) { + // may be unpopulated in user's unit tests + throw new Error( + 'Please supply a Firebase app in the constructor for DataSnapshot' + + ' in order to use the .ref method.' + ); + } + if (!this._ref) { + this._ref = this.app.database(this.instance).ref(this._fullPath()); + } + return this._ref; + } + + /** + * The key (last part of the path) of the location of this `DataSnapshot`. + * + * The last token in a database location is considered its key. For example, + * "ada" is the key for the `/users/ada/` node. Accessing the key on any + * `DataSnapshot` returns the key for the location that generated it. + * However, accessing the key on the root URL of a database returns `null`. + */ + get key(): string | null { + const segments = pathParts(this._fullPath()); + const last = segments[segments.length - 1]; + return !last || last === '' ? null : last; + } + + /** + * Extracts a JavaScript value from a `DataSnapshot`. + * + * Depending on the data in a `DataSnapshot`, the `val()` method may return a + * scalar type (string, number, or boolean), an array, or an object. It may also + * return `null`, indicating that the `DataSnapshot` is empty (contains no + * data). + * + * @return The snapshot's contents as a JavaScript value (Object, + * Array, string, number, boolean, or `null`). + */ + val(): any { + const parts = pathParts(this._childPath); + let source = this._data; + if (parts.length) { + for (const part of parts) { + source = source[part]; + } + } + const node = source ?? null; + + return this._checkAndConvertToArray(node); + } + + /** + * Exports the entire contents of the `DataSnapshot` as a JavaScript object. + * + * @return The contents of the `DataSnapshot` as a JavaScript value + * (Object, Array, string, number, boolean, or `null`). + */ + exportVal(): any { + return this.val(); + } + + /** + * Gets the priority value of the data in this `DataSnapshot`. + * + * As an alternative to using priority, applications can order collections by + * ordinary properties. See [Sorting and filtering + * data](/docs/database/web/lists-of-data#sorting_and_filtering_data). + * + * @return The priority value of the data. + */ + getPriority(): string | number | null { + return 0; + } + + /** + * Returns `true` if this `DataSnapshot` contains any data. It is slightly more + * efficient than using `snapshot.val() !== null`. + * + * @return `true` if this `DataSnapshot` contains any data; otherwise, `false`. + */ + exists(): boolean { + const val = this.val(); + if (!val || val === null) { + return false; + } + if (typeof val === 'object' && Object.keys(val).length === 0) { + return false; + } + return true; + } + + /** + * Gets a `DataSnapshot` for the location at the specified relative path. + * + * The relative path can either be a simple child name (for example, "ada") or + * a deeper slash-separated path (for example, "ada/name/first"). + * + * @param path A relative path from this location to the desired child + * location. + * @return The specified child location. + */ + child(childPath: string): DataSnapshot { + if (!childPath) { + return this; + } + return this._dup(childPath); + } + + /** + * Enumerates the `DataSnapshot`s of the children items. + * + * Because of the way JavaScript objects work, the ordering of data in the + * JavaScript object returned by `val()` is not guaranteed to match the ordering + * on the server nor the ordering of `child_added` events. That is where + * `forEach()` comes in handy. It guarantees the children of a `DataSnapshot` + * can be iterated in their query order. + * + * If no explicit `orderBy*()` method is used, results are returned + * ordered by key (unless priorities are used, in which case, results are + * returned by priority). + * + * @param action A function that is called for each child `DataSnapshot`. + * The callback can return `true` to cancel further enumeration. + * + * @return `true` if enumeration was canceled due to your callback + * returning `true`. + */ + forEach(action: (a: DataSnapshot) => boolean | void): boolean { + const val = this.val() || {}; + if (typeof val === 'object') { + return Object.keys(val).some((key) => action(this.child(key)) === true); + } + return false; + } + + /** + * Returns `true` if the specified child path has (non-`null`) data. + * + * @param path A relative path to the location of a potential child. + * @return `true` if data exists at the specified child path; otherwise, + * `false`. + */ + hasChild(childPath: string): boolean { + return this.child(childPath).exists(); + } + + /** + * Returns whether or not the `DataSnapshot` has any non-`null` child + * properties. + * + * You can use `hasChildren()` to determine if a `DataSnapshot` has any + * children. If it does, you can enumerate them using `forEach()`. If it + * doesn't, then either this snapshot contains a primitive value (which can be + * retrieved with `val()`) or it is empty (in which case, `val()` returns + * `null`). + * + * @return `true` if this snapshot has any children; else `false`. + */ + hasChildren(): boolean { + const val = this.val(); + return ( + val !== null && typeof val === 'object' && Object.keys(val).length > 0 + ); + } + + /** + * Returns the number of child properties of this `DataSnapshot`. + * + * @return Number of child properties of this `DataSnapshot`. + */ + numChildren(): number { + const val = this.val(); + return val !== null && typeof val === 'object' + ? Object.keys(val).length + : 0; + } + + /** + * Returns a JSON-serializable representation of this object. + * + * @return A JSON-serializable representation of this object. + */ + toJSON(): Object { + return this.val(); + } + + /** Recursive function to check if keys are numeric & convert node object to array if they are + * + * @hidden + */ + private _checkAndConvertToArray(node: any): any { + if (node === null || typeof node === 'undefined') { + return null; + } + if (typeof node !== 'object') { + return node; + } + const obj: any = {}; + let numKeys = 0; + let maxKey = 0; + let allIntegerKeys = true; + for (const key in node) { + if (!node.hasOwnProperty(key)) { + continue; + } + const childNode = node[key]; + const v = this._checkAndConvertToArray(childNode); + if (v === null) { + // Empty child node + continue; + } + obj[key] = v; + numKeys++; + const integerRegExp = /^(0|[1-9]\d*)$/; + if (allIntegerKeys && integerRegExp.test(key)) { + maxKey = Math.max(maxKey, Number(key)); + } else { + allIntegerKeys = false; + } + } + + if (numKeys === 0) { + // Empty node + return null; + } + + if (allIntegerKeys && maxKey < 2 * numKeys) { + // convert to array. + const array: any = []; + for (const key of Object.keys(obj)) { + array[key] = obj[key]; + } + + return array; + } + return obj; + } + + /** @hidden */ + private _dup(childPath?: string): DataSnapshot { + const dup = new DataSnapshot( + this._data, + undefined, + this.app, + this.instance + ); + [dup._path, dup._childPath] = [this._path, this._childPath]; + + if (childPath) { + dup._childPath = joinPath(dup._childPath, childPath); + } + + return dup; + } + + /** @hidden */ + private _fullPath(): string { + return (this._path || '') + '/' + (this._childPath || ''); + } +} diff --git a/src/providers/database.ts b/src/providers/database.ts index e5f4d8ed3..5b7dc59f5 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -20,8 +20,6 @@ // 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 _ from 'lodash'; import { apps } from '../apps'; import { Change, @@ -30,11 +28,14 @@ import { EventContext, makeCloudFunction, } from '../cloud-functions'; +import { DataSnapshot } from '../common/providers/database'; import { firebaseConfig } from '../config'; import { DeploymentOptions } from '../function-configuration'; -import { joinPath, normalizePath, pathParts } from '../utilities/path'; +import { normalizePath } from '../utilities/path'; import { applyChange } from '../utils'; +export { DataSnapshot }; + /** @hidden */ export const provider = 'google.firebase.database'; /** @hidden */ @@ -345,292 +346,3 @@ export function extractInstanceAndPath( return [dbInstance, path]; } } - -/** - * Interface representing a Firebase Realtime Database data snapshot. - */ -export class DataSnapshot { - public instance: string; - - /** @hidden */ - private _ref: firebase.database.Reference; - - /** @hidden */ - private _path: string; - - /** @hidden */ - private _data: any; - - /** @hidden */ - private _childPath: string; - - constructor( - data: any, - path?: string, // path will be undefined for the database root - private app?: firebase.app.App, - instance?: string - ) { - if (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) { - this.instance = app.options.databaseURL; - } else if (process.env.GCLOUD_PROJECT) { - this.instance = - 'https://' + process.env.GCLOUD_PROJECT + '.firebaseio.com'; - } - - this._path = path; - this._data = data; - } - - /** - * Returns a [`Reference`](/docs/reference/admin/node/admin.database.Reference) - * to the Database location where the triggering write occurred. Has - * full read and write access. - */ - get ref(): firebase.database.Reference { - if (!this.app) { - // may be unpopulated in user's unit tests - throw new Error( - 'Please supply a Firebase app in the constructor for DataSnapshot' + - ' in order to use the .ref method.' - ); - } - if (!this._ref) { - this._ref = this.app.database(this.instance).ref(this._fullPath()); - } - return this._ref; - } - - /** - * The key (last part of the path) of the location of this `DataSnapshot`. - * - * The last token in a Database location is considered its key. For example, - * "ada" is the key for the `/users/ada/` node. Accessing the key on any - * `DataSnapshot` will return the key for the location that generated it. - * However, accessing the key on the root URL of a Database will return `null`. - */ - get key(): string { - const last = _.last(pathParts(this._fullPath())); - return !last || last === '' ? null : last; - } - - /** - * Extracts a JavaScript value from a `DataSnapshot`. - * - * Depending on the data in a `DataSnapshot`, the `val()` method may return a - * scalar type (string, number, or boolean), an array, or an object. It may also - * return `null`, indicating that the `DataSnapshot` is empty (contains no - * data). - * - * @return The DataSnapshot's contents as a JavaScript value (Object, - * Array, string, number, boolean, or `null`). - */ - val(): any { - const parts = pathParts(this._childPath); - const source = this._data; - const node = _.cloneDeep( - parts.length ? _.get(source, parts, null) : source - ); - return this._checkAndConvertToArray(node); - } - - /** - * Exports the entire contents of the `DataSnapshot` as a JavaScript object. - * - * The `exportVal()` method is similar to `val()`, except priority information - * is included (if available), making it suitable for backing up your data. - * - * @return The contents of the `DataSnapshot` as a JavaScript value - * (Object, Array, string, number, boolean, or `null`). - */ - exportVal(): any { - return this.val(); - } - - /** - * Gets the priority value of the data in this `DataSnapshot`. - * - * As an alternative to using priority, applications can order collections by - * ordinary properties. See [Sorting and filtering - * data](/docs/database/web/lists-of-data#sorting_and_filtering_data). - * - * @return The priority value of the data. - */ - getPriority(): string | number | null { - return 0; - } - - /** - * Returns `true` if this `DataSnapshot` contains any data. It is slightly more - * efficient than using `snapshot.val() !== null`. - * - * @return `true` if this `DataSnapshot` contains any data; otherwise, `false`. - */ - exists(): boolean { - return !_.isNull(this.val()); - } - - /** - * Gets a `DataSnapshot` for the location at the specified relative path. - * - * The relative path can either be a simple child name (for example, "ada") or - * a deeper slash-separated path (for example, "ada/name/first"). - * - * @param path A relative path from this location to the desired child - * location. - * @return The specified child location. - */ - child(childPath: string): DataSnapshot { - if (!childPath) { - return this; - } - return this._dup(childPath); - } - - /** - * Enumerates the `DataSnapshot`s of the children items. - * - * Because of the way JavaScript objects work, the ordering of data in the - * JavaScript object returned by `val()` is not guaranteed to match the ordering - * on the server nor the ordering of `child_added` events. That is where - * `forEach()` comes in handy. It guarantees the children of a `DataSnapshot` - * will be iterated in their query order. - * - * If no explicit `orderBy*()` method is used, results are returned - * ordered by key (unless priorities are used, in which case, results are - * returned by priority). - * - * @param action A function that will be called for each child `DataSnapshot`. - * The callback can return `true` to cancel further enumeration. - * - * @return `true` if enumeration was canceled due to your callback - * returning `true`. - */ - forEach(action: (a: DataSnapshot) => boolean | void): boolean { - const val = this.val(); - if (_.isPlainObject(val)) { - return _.some( - val, - (value, key: string) => action(this.child(key)) === true - ); - } - return false; - } - - /** - * Returns `true` if the specified child path has (non-`null`) data. - * - * @param path A relative path to the location of a potential child. - * @return `true` if data exists at the specified child path; otherwise, - * `false`. - */ - hasChild(childPath: string): boolean { - return this.child(childPath).exists(); - } - - /** - * Returns whether or not the `DataSnapshot` has any non-`null` child - * properties. - * - * You can use `hasChildren()` to determine if a `DataSnapshot` has any - * children. If it does, you can enumerate them using `forEach()`. If it - * doesn't, then either this snapshot contains a primitive value (which can be - * retrieved with `val()`) or it is empty (in which case, `val()` will return - * `null`). - * - * @return `true` if this snapshot has any children; else `false`. - */ - hasChildren(): boolean { - const val = this.val(); - return _.isPlainObject(val) && _.keys(val).length > 0; - } - - /** - * Returns the number of child properties of this `DataSnapshot`. - * - * @return Number of child properties of this `DataSnapshot`. - */ - numChildren(): number { - const val = this.val(); - return _.isPlainObject(val) ? Object.keys(val).length : 0; - } - - /** - * Returns a JSON-serializable representation of this object. - * - * @return A JSON-serializable representation of this object. - */ - toJSON(): Object { - return this.val(); - } - - /** Recursive function to check if keys are numeric & convert node object to array if they are - * - * @hidden - */ - private _checkAndConvertToArray(node: any): any { - if (node === null || typeof node === 'undefined') { - return null; - } - if (typeof node !== 'object') { - return node; - } - const obj: any = {}; - let numKeys = 0; - let maxKey = 0; - let allIntegerKeys = true; - for (const key in node) { - if (!node.hasOwnProperty(key)) { - continue; - } - const childNode = node[key]; - obj[key] = this._checkAndConvertToArray(childNode); - numKeys++; - const integerRegExp = /^(0|[1-9]\d*)$/; - if (allIntegerKeys && integerRegExp.test(key)) { - maxKey = Math.max(maxKey, Number(key)); - } else { - allIntegerKeys = false; - } - } - - if (allIntegerKeys && maxKey < 2 * numKeys) { - // convert to array. - const array: any = []; - _.forOwn(obj, (val, key) => { - array[key] = val; - }); - - return array; - } - return obj; - } - - /** @hidden */ - private _dup(childPath?: string): DataSnapshot { - const dup = new DataSnapshot( - this._data, - undefined, - this.app, - this.instance - ); - [dup._path, dup._childPath] = [this._path, this._childPath]; - - if (childPath) { - dup._childPath = joinPath(dup._childPath, childPath); - } - - return dup; - } - - /** @hidden */ - private _fullPath(): string { - const out = (this._path || '') + '/' + (this._childPath || ''); - return out; - } -} diff --git a/src/utilities/path-pattern.ts b/src/utilities/path-pattern.ts new file mode 100644 index 000000000..dadc96bb1 --- /dev/null +++ b/src/utilities/path-pattern.ts @@ -0,0 +1,173 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 { pathParts } from './path'; + +/** https://cloud.google.com/eventarc/docs/path-patterns */ + +/** @hidden */ +const WILDCARD_CAPTURE_REGEX = new RegExp('{[^/{}]+}', 'g'); + +/** @internal */ +export function trimParam(param: string) { + const paramNoBraces = param.slice(1, -1); + if (paramNoBraces.includes('=')) { + return paramNoBraces.slice(0, paramNoBraces.indexOf('=')); + } + return paramNoBraces; +} + +/** @hidden */ +type SegmentName = 'segment' | 'single-capture' | 'multi-capture'; + +/** @hidden */ +interface PathSegment { + readonly name: SegmentName; + readonly value: string; + readonly trimmed: string; + isSingleSegmentWildcard(): boolean; + isMultiSegmentWildcard(): boolean; +} + +/** @hidden */ +class Segment implements PathSegment { + readonly name = 'segment'; + readonly trimmed: string; + constructor(readonly value: string) { + this.trimmed = value; + } + isSingleSegmentWildcard(): boolean { + return this.value.includes('*') && !this.isMultiSegmentWildcard(); + } + isMultiSegmentWildcard(): boolean { + return this.value.includes('**'); + } +} + +/** @hidden */ +class SingleCaptureSegment implements PathSegment { + readonly name = 'single-capture'; + readonly trimmed: string; + constructor(readonly value: string) { + this.trimmed = trimParam(value); + } + isSingleSegmentWildcard(): boolean { + return true; + } + isMultiSegmentWildcard(): boolean { + return false; + } +} + +/** @hidden */ +class MultiCaptureSegment implements PathSegment { + readonly name = 'multi-capture'; + readonly trimmed: string; + constructor(readonly value: string) { + this.trimmed = trimParam(value); + } + isSingleSegmentWildcard(): boolean { + return false; + } + isMultiSegmentWildcard(): boolean { + return true; + } +} + +/** + * Implements Eventarc's path pattern from the spec https://cloud.google.com/eventarc/docs/path-patterns + * @internal + */ +export class PathPattern { + /** @throws on validation error */ + static compile(rawPath: string) {} + private segments: PathSegment[]; + + constructor(private raw: string) { + this.segments = []; + this.initPathSegments(raw); + } + + getValue(): string { + return this.raw; + } + + // If false, we don't need to use pathPattern as our eventarc match type. + hasWildcards(): boolean { + return this.segments.some( + (segment) => + segment.isSingleSegmentWildcard() || segment.isMultiSegmentWildcard() + ); + } + + hasCaptures(): boolean { + return this.segments.some( + (segment) => + segment.name == 'single-capture' || segment.name === 'multi-capture' + ); + } + + extractMatches(path: string): Record { + const matches: Record = {}; + if (!this.hasCaptures()) { + return matches; + } + const pathSegments = pathParts(path); + let pathNdx = 0; + + for ( + let segmentNdx = 0; + segmentNdx < this.segments.length && pathNdx < pathSegments.length; + segmentNdx++ + ) { + const segment = this.segments[segmentNdx]; + const remainingSegments = this.segments.length - 1 - segmentNdx; + const nextPathNdx = pathSegments.length - remainingSegments; + if (segment.name === 'single-capture') { + matches[segment.trimmed] = pathSegments[pathNdx]; + } else if (segment.name === 'multi-capture') { + matches[segment.trimmed] = pathSegments + .slice(pathNdx, nextPathNdx) + .join('/'); + } + pathNdx = segment.isMultiSegmentWildcard() ? nextPathNdx : pathNdx + 1; + } + + return matches; + } + + private initPathSegments(raw: string) { + const parts = pathParts(raw); + for (const part of parts) { + let segment: PathSegment; + const capture = part.match(WILDCARD_CAPTURE_REGEX); + if (capture && capture.length === 1) { + segment = part.includes('**') + ? new MultiCaptureSegment(part) + : new SingleCaptureSegment(part); + } else { + segment = new Segment(part); + } + this.segments.push(segment); + } + } +} diff --git a/src/v2/index.ts b/src/v2/index.ts index d1cf4836a..d2161ab71 100644 --- a/src/v2/index.ts +++ b/src/v2/index.ts @@ -30,6 +30,7 @@ import * as logger from '../logger'; import * as alerts from './providers/alerts'; +import * as database from './providers/database'; import * as eventarc from './providers/eventarc'; import * as https from './providers/https'; import * as identity from './providers/identity'; @@ -37,7 +38,17 @@ import * as pubsub from './providers/pubsub'; import * as storage from './providers/storage'; import * as tasks from './providers/tasks'; -export { alerts, storage, https, identity, pubsub, logger, tasks, eventarc }; +export { + alerts, + database, + storage, + https, + identity, + pubsub, + logger, + tasks, + eventarc, +}; export { setGlobalOptions, diff --git a/src/v2/providers/database.ts b/src/v2/providers/database.ts new file mode 100644 index 000000000..b0a38c313 --- /dev/null +++ b/src/v2/providers/database.ts @@ -0,0 +1,409 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 { apps } from '../../apps'; +import { Change } from '../../cloud-functions'; +import { DataSnapshot } from '../../common/providers/database'; +import { ManifestEndpoint } from '../../runtime/manifest'; +import { normalizePath } from '../../utilities/path'; +import { PathPattern } from '../../utilities/path-pattern'; +import { applyChange } from '../../utils'; +import { CloudEvent, CloudFunction } from '../core'; +import * as options from '../options'; + +export { DataSnapshot }; + +/** @internal */ +export const writtenEventType = 'google.firebase.database.ref.v1.written'; + +/** @internal */ +export const createdEventType = 'google.firebase.database.ref.v1.created'; + +/** @internal */ +export const updatedEventType = 'google.firebase.database.ref.v1.updated'; + +/** @internal */ +export const deletedEventType = 'google.firebase.database.ref.v1.deleted'; + +/** @internal */ +export interface RawRTDBCloudEventData { + ['@type']: 'type.googleapis.com/google.events.firebase.database.v1.ReferenceEventData'; + data: any; + delta: any; +} + +/** @internal */ +export interface RawRTDBCloudEvent extends CloudEvent { + firebasedatabasehost: string; + instance: string; + ref: string; + location: string; +} + +/** A CloudEvent that contains a DataSnapshot or a Change */ +export interface DatabaseEvent extends CloudEvent { + /** The domain of the database instance */ + firebaseDatabaseHost: string; + /** The instance ID portion of the fully qualified resource name */ + instance: string; + /** The database reference path */ + ref: string; + /** The location of the database */ + location: string; + /** + * An object containing the values of the path patterns. + * Only named capture groups will be populated - {key}, {key=*}, {key=**} + */ + params: Record; +} + +/** ReferenceOptions extend EventHandlerOptions with provided ref and optional instance */ +export interface ReferenceOptions extends options.EventHandlerOptions { + /** + * Specify the handler to trigger on a database reference(s). + * This value can either be a single reference or a pattern. + * Examples: '/foo/bar', '/foo/{bar}' + */ + ref: string; + /** + * Specify the handler to trigger on a database instance(s). + * If present, this value can either be a single instance or a pattern. + * Examples: 'my-instance-1', '{instance}' + */ + instance?: string; +} + +/** + * Event handler which triggers when data is created, updated, or deleted in Realtime Database. + * + * @param reference - The database reference path to trigger on. + * @param handler - Event handler which is run every time a Realtime Database create, update, or delete occurs. + */ +export function onRefWritten( + reference: string, + handler: (event: DatabaseEvent>) => any | Promise +): CloudFunction>>; + +/** + * Event handler which triggers when data is created, updated, or deleted in Realtime Database. + * + * @param opts - Options that can be set on an individual event-handling function. + * @param handler - Event handler which is run every time a Realtime Database create, update, or delete occurs. + */ +export function onRefWritten( + opts: ReferenceOptions, + handler: (event: DatabaseEvent>) => any | Promise +): CloudFunction>>; + +/** + * Event handler which triggers when data is created, updated, or deleted in Realtime Database. + * + * @param referenceOrOpts - Options or a string reference. + * @param handler - Event handler which is run every time a Realtime Database create, update, or delete occurs. + */ +export function onRefWritten( + referenceOrOpts: string | ReferenceOptions, + handler: (event: DatabaseEvent>) => any | Promise +): CloudFunction>> { + return onChangedOperation(writtenEventType, referenceOrOpts, handler); +} + +/** + * Event handler which triggers when data is created in Realtime Database. + * + * @param reference - The database reference path to trigger on. + * @param handler - Event handler which is run every time a Realtime Database create occurs. + */ +export function onRefCreated( + reference: string, + handler: (event: DatabaseEvent) => any | Promise +): CloudFunction>; + +/** + * Event handler which triggers when data is created in Realtime Database. + * + * @param opts - Options that can be set on an individual event-handling function. + * @param handler - Event handler which is run every time a Realtime Database create occurs. + */ +export function onRefCreated( + opts: ReferenceOptions, + handler: (event: DatabaseEvent) => any | Promise +): CloudFunction>; + +/** + * Event handler which triggers when data is created in Realtime Database. + * + * @param referenceOrOpts - Options or a string reference. + * @param handler - Event handler which is run every time a Realtime Database create occurs. + */ +export function onRefCreated( + referenceOrOpts: string | ReferenceOptions, + handler: (event: DatabaseEvent) => any | Promise +): CloudFunction> { + return onOperation(createdEventType, referenceOrOpts, handler); +} + +/** + * Event handler which triggers when data is updated in Realtime Database. + * + * @param reference - The database reference path to trigger on. + * @param handler - Event handler which is run every time a Realtime Database update occurs. + */ +export function onRefUpdated( + reference: string, + handler: (event: DatabaseEvent>) => any | Promise +): CloudFunction>>; + +/** + * Event handler which triggers when data is updated in Realtime Database. + * + * @param opts - Options that can be set on an individual event-handling function. + * @param handler - Event handler which is run every time a Realtime Database update occurs. + */ +export function onRefUpdated( + opts: ReferenceOptions, + handler: (event: DatabaseEvent>) => any | Promise +): CloudFunction>>; + +/** + * Event handler which triggers when data is updated in Realtime Database. + * + * @param referenceOrOpts - Options or a string reference. + * @param handler - Event handler which is run every time a Realtime Database update occurs. + */ +export function onRefUpdated( + referenceOrOpts: string | ReferenceOptions, + handler: (event: DatabaseEvent>) => any | Promise +): CloudFunction>> { + return onChangedOperation(updatedEventType, referenceOrOpts, handler); +} + +/** + * Event handler which triggers when data is deleted in Realtime Database. + * + * @param reference - The database reference path to trigger on. + * @param handler - Event handler which is run every time a Realtime Database deletion occurs. + */ +export function onRefDeleted( + reference: string, + handler: (event: DatabaseEvent) => any | Promise +): CloudFunction>; + +/** + * Event handler which triggers when data is deleted in Realtime Database. + * + * @param opts - Options that can be set on an individual event-handling function. + * @param handler - Event handler which is run every time a Realtime Database deletion occurs. + */ +export function onRefDeleted( + opts: ReferenceOptions, + handler: (event: DatabaseEvent) => any | Promise +): CloudFunction>; + +/** + * Event handler which triggers when data is deleted in Realtime Database. + * + * @param referenceOrOpts - Options or a string reference. + * @param handler - Event handler which is run every time a Realtime Database deletion occurs. + */ +export function onRefDeleted( + referenceOrOpts: string | ReferenceOptions, + handler: (event: DatabaseEvent) => any | Promise +): CloudFunction> { + // TODO - need to use event.data.delta + return onOperation(deletedEventType, referenceOrOpts, handler); +} + +/** @internal */ +export function getOpts(referenceOrOpts: string | ReferenceOptions) { + let path: string, instance: string, opts: options.EventHandlerOptions; + if (typeof referenceOrOpts === 'string') { + path = normalizePath(referenceOrOpts); + instance = '*'; + opts = {}; + } else { + path = normalizePath(referenceOrOpts.ref); + instance = referenceOrOpts.instance || '*'; + opts = { ...referenceOrOpts }; + delete (opts as any).ref; + delete (opts as any).instance; + } + + return { + path, + instance, + opts, + }; +} + +/** @internal */ +export function makeParams( + event: RawRTDBCloudEvent, + path: PathPattern, + instance: PathPattern +) { + return { + ...path.extractMatches(event.ref), + ...instance.extractMatches(event.instance), + }; +} + +/** @hidden */ +function makeDatabaseEvent( + event: RawRTDBCloudEvent, + data: any, + instance: string, + params: Record +): DatabaseEvent { + const snapshot = new DataSnapshot(data, event.ref, apps().admin, instance); + const databaseEvent: DatabaseEvent = { + ...event, + firebaseDatabaseHost: event.firebasedatabasehost, + data: snapshot, + params, + }; + delete (databaseEvent as any).firebasedatabasehost; + return databaseEvent; +} + +/** @hidden */ +function makeChangedDatabaseEvent( + event: RawRTDBCloudEvent, + instance: string, + params: Record +): DatabaseEvent> { + const before = new DataSnapshot( + event.data.data, + event.ref, + apps().admin, + instance + ); + const after = new DataSnapshot( + applyChange(event.data.data, event.data.delta), + event.ref, + apps().admin, + instance + ); + const databaseEvent: DatabaseEvent> = { + ...event, + firebaseDatabaseHost: event.firebasedatabasehost, + data: { + before, + after, + }, + params, + }; + delete (databaseEvent as any).firebasedatabasehost; + return databaseEvent; +} + +/** @internal */ +export function makeEndpoint( + eventType: string, + opts: options.EventHandlerOptions, + path: PathPattern, + instance: PathPattern +): ManifestEndpoint { + const baseOpts = options.optionsToEndpoint(options.getGlobalOptions()); + const specificOpts = options.optionsToEndpoint(opts); + + const eventFilters: Record = {}; + const eventFilterPathPatterns: Record = { + // Note: Eventarc always treats ref as a path pattern + ref: path.getValue(), + }; + instance.hasWildcards() + ? (eventFilterPathPatterns.instance = instance.getValue()) + : (eventFilters.instance = instance.getValue()); + + return { + platform: 'gcfv2', + ...baseOpts, + ...specificOpts, + labels: { + ...baseOpts?.labels, + ...specificOpts?.labels, + }, + eventTrigger: { + eventType, + eventFilters, + eventFilterPathPatterns, + retry: false, + }, + }; +} + +/** @internal */ +export function onChangedOperation( + eventType: string, + referenceOrOpts: string | ReferenceOptions, + handler: (event: DatabaseEvent>) => any | Promise +): CloudFunction>> { + const { path, instance, opts } = getOpts(referenceOrOpts); + + const pathPattern = new PathPattern(path); + const instancePattern = new PathPattern(instance); + + // wrap the handler + const func = (raw: CloudEvent) => { + const event = raw as RawRTDBCloudEvent; + const instanceUrl = `https://${event.instance}.${event.firebasedatabasehost}`; + const params = makeParams(event, pathPattern, instancePattern); + const databaseEvent = makeChangedDatabaseEvent(event, instanceUrl, params); + return handler(databaseEvent); + }; + + func.run = handler; + + func.__endpoint = makeEndpoint(eventType, opts, pathPattern, instancePattern); + + return func; +} + +/** @internal */ +export function onOperation( + eventType: string, + referenceOrOpts: string | ReferenceOptions, + handler: (event: DatabaseEvent) => any | Promise +): CloudFunction> { + const { path, instance, opts } = getOpts(referenceOrOpts); + + const pathPattern = new PathPattern(path); + const instancePattern = new PathPattern(instance); + + // wrap the handler + const func = (raw: CloudEvent) => { + const event = raw as RawRTDBCloudEvent; + const instanceUrl = `https://${event.instance}.${event.firebasedatabasehost}`; + const params = makeParams(event, pathPattern, instancePattern); + const data = + eventType === deletedEventType ? event.data.data : event.data.delta; + const databaseEvent = makeDatabaseEvent(event, data, instanceUrl, params); + return handler(databaseEvent); + }; + + func.run = handler; + + func.__endpoint = makeEndpoint(eventType, opts, pathPattern, instancePattern); + + return func; +} diff --git a/v2/database.js b/v2/database.js new file mode 100644 index 000000000..c822b56f1 --- /dev/null +++ b/v2/database.js @@ -0,0 +1,26 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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. + +// This file is not part of the firebase-functions SDK. It is used to silence the +// imports eslint plugin until it can understand import paths defined by node +// package exports. +// For more information, see github.com/import-js/eslint-plugin-import/issues/1810 \ No newline at end of file From a2b9e73f011212847c7a867ede4f13a11504e601 Mon Sep 17 00:00:00 2001 From: Yusei-Yagi <45314822+Yamahitsuji@users.noreply.github.com> Date: Wed, 15 Jun 2022 02:19:35 +0900 Subject: [PATCH 115/370] add 8GB to memory option doc (#1129) Co-authored-by: Yamahitsuji --- src/function-builder.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/function-builder.ts b/src/function-builder.ts index f0158a3e0..1fd4efb05 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -277,7 +277,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', '2GB', and '4GB'. + * are: '128MB', '256MB', '512MB', '1GB', '2GB', '4GB', and '8GB'. * 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 @@ -321,7 +321,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', '2GB', and '4GB'. + * are: '128MB', '256MB', '512MB', '1GB', '2GB', '4GB', and '8GB'. * 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 76e0afd442e141ad0016d01545e5883e27712c3f Mon Sep 17 00:00:00 2001 From: Tyler Stark Date: Wed, 15 Jun 2022 12:51:03 -0500 Subject: [PATCH 116/370] Update v2 rtdb to expose RawRTDBEvent and RawRTDBCloudEventData (#1137) * Update v2 rtdb to expose RawRTDBEvent and RawRTDBCloudEventData This commit updates the v2 database provider to also expose the RawRTDB Events. This will be used in test-sdk when mocking returned DatabaseEvents. * Adding @hidden instead of @internal --- src/v2/providers/database.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/v2/providers/database.ts b/src/v2/providers/database.ts index b0a38c313..e0b410422 100644 --- a/src/v2/providers/database.ts +++ b/src/v2/providers/database.ts @@ -44,14 +44,14 @@ export const updatedEventType = 'google.firebase.database.ref.v1.updated'; /** @internal */ export const deletedEventType = 'google.firebase.database.ref.v1.deleted'; -/** @internal */ +/** @hidden */ export interface RawRTDBCloudEventData { ['@type']: 'type.googleapis.com/google.events.firebase.database.v1.ReferenceEventData'; data: any; delta: any; } -/** @internal */ +/** @hidden */ export interface RawRTDBCloudEvent extends CloudEvent { firebasedatabasehost: string; instance: string; From 3737898b5954082943bea1248d89661688723514 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 22 Jun 2022 09:46:06 -0700 Subject: [PATCH 117/370] Skip auth check when emulating task queue function. (#1154) Fixes https://github.com/firebase/firebase-functions/issues/1146 --- CHANGELOG.md | 1 + spec/common/providers/tasks.spec.ts | 22 +++++++++++++++++++++- src/common/providers/tasks.ts | 25 +++++++++++++------------ 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b174a17fe..5d516b215 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ - Adds RTDB Triggers for v2 functions (#1127) +- Fixes bug where emulated task queue function required auth header (#1154) diff --git a/spec/common/providers/tasks.spec.ts b/spec/common/providers/tasks.spec.ts index d3711a18f..6811f0b2a 100644 --- a/spec/common/providers/tasks.spec.ts +++ b/spec/common/providers/tasks.spec.ts @@ -83,7 +83,7 @@ describe('onEnqueueHandler', () => { function mockEnqueueRequest( data: unknown, contentType: string = 'application/json', - context: { authorization: string } = { authorization: 'Bearer abc' } + context: { authorization?: string } = { authorization: 'Bearer abc' } ): ReturnType { return mockRequest(data, contentType, context); } @@ -239,4 +239,24 @@ describe('onEnqueueHandler', () => { expectedStatus: 204, }); }); + + it('should skip auth in emulated environment', async () => { + const restore = process.env.FUNCTIONS_EMULATOR; + process.env.FUNCTIONS_EMULATOR = 'true'; + + await runTaskTest({ + httpRequest: mockEnqueueRequest(null, 'application/json', {}), + expectedData: null, + taskFunction: (data, context) => { + expect(context.auth).to.be.undefined; + return null; + }, + taskFunction2: (request) => { + expect(request.auth).to.be.undefined; + }, + expectedStatus: 204, + }); + + process.env.FUNCTIONS_EMULATOR = restore; + }); }); diff --git a/src/common/providers/tasks.ts b/src/common/providers/tasks.ts index d84f7a585..52360af48 100644 --- a/src/common/providers/tasks.ts +++ b/src/common/providers/tasks.ts @@ -117,20 +117,21 @@ export function onDispatchHandler( throw new https.HttpsError('invalid-argument', 'Bad Request'); } - const authHeader = req.header('Authorization') || ''; - const token = authHeader.match(/^Bearer (.*)$/)?.[1]; - // Note: this should never happen since task queue functions are guarded by IAM. - if (!token) { - throw new https.HttpsError('unauthenticated', 'Unauthenticated'); - } - // We skip authenticating the token since tq functions are guarded by IAM. - const authToken = await https.unsafeDecodeIdToken(token); - const context: TaskContext = { - auth: { + const context: TaskContext = {}; + if (!process.env.FUNCTIONS_EMULATOR) { + const authHeader = req.header('Authorization') || ''; + const token = authHeader.match(/^Bearer (.*)$/)?.[1]; + // Note: this should never happen since task queue functions are guarded by IAM. + if (!token) { + throw new https.HttpsError('unauthenticated', 'Unauthenticated'); + } + // We skip authenticating the token since tq functions are guarded by IAM. + const authToken = await https.unsafeDecodeIdToken(token); + context.auth = { uid: authToken.uid, token: authToken, - }, - }; + }; + } const data: Req = https.decode(req.body.data); if (handler.length === 2) { From cc18326068278dcf2f5e15bf38d8ca5f5aaa9ff2 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Thu, 23 Jun 2022 13:38:08 -0400 Subject: [PATCH 118/370] Auth Blocking Triggers Bugbash (#1140) * adding run.app audience for v2 functions * linter & formatter --- src/common/providers/identity.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/common/providers/identity.ts b/src/common/providers/identity.ts index 948c73ce9..2c0ce0a2e 100644 --- a/src/common/providers/identity.ts +++ b/src/common/providers/identity.ts @@ -835,9 +835,13 @@ export function wrapHandler( 'skipTokenVerification' ) ? unsafeDecodeAuthBlockingToken(req.body.data.jwt) + : handler.length === 2 + ? await apps() + .admin.auth() + ._verifyAuthBlockingToken(req.body.data.jwt) : await apps() .admin.auth() - ._verifyAuthBlockingToken(req.body.data.jwt); + ._verifyAuthBlockingToken(req.body.data.jwt, 'run.app'); const authUserRecord = parseAuthUserRecord(decodedPayload.user_record); const authEventContext = parseAuthEventContext(decodedPayload, projectId); From 96f9e8646fc22559a1a28fda5213c726df18e7fd Mon Sep 17 00:00:00 2001 From: Jasper Concepcion Date: Tue, 28 Jun 2022 06:35:43 +0800 Subject: [PATCH 119/370] add firebase-admin@^11.0.0 to peer dependencies (#1151) * add firebase-admin@11.x.x to peer dependencies * Add changelog entry. Co-authored-by: Daniel Lee --- CHANGELOG.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d516b215..bc0bbf514 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,3 @@ - Adds RTDB Triggers for v2 functions (#1127) +- Adds support for Firebase Admin SDK v11 (#1151) - Fixes bug where emulated task queue function required auth header (#1154) diff --git a/package.json b/package.json index 03f56c851..7363f41df 100644 --- a/package.json +++ b/package.json @@ -229,7 +229,7 @@ "yargs": "^15.3.1" }, "peerDependencies": { - "firebase-admin": "^8.0.0 || ^9.0.0 || ^10.0.0" + "firebase-admin": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0" }, "engines": { "node": "^8.13.0 || >=10.10.0" From 139e1d94d585c26a5d76586685a976a5643df869 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Tue, 28 Jun 2022 00:20:36 -0400 Subject: [PATCH 120/370] Enhance RTDB v2 Triggers (#1153) * moving change into v2 core * moving Change to common and fixing imports * fix tsdoc * rexporting Change from v1&v2 * remove unused functions from Change namespace * fixing argument * rename interface to onValue* and rework Change Co-authored-by: Thomas Bouldin --- spec/common/change.spec.ts | 120 +++++++++++++++++++++++++++++ spec/v1/cloud-functions.spec.ts | 97 ----------------------- spec/v2/providers/database.spec.ts | 24 +++--- src/cloud-functions.ts | 89 --------------------- src/common/change.ts | 109 ++++++++++++++++++++++++++ src/providers/database.ts | 2 +- src/providers/firestore.ts | 2 +- src/v2/core.ts | 3 + src/v2/providers/database.ts | 120 +++++++++++++++++++++++++---- 9 files changed, 353 insertions(+), 213 deletions(-) create mode 100644 spec/common/change.spec.ts create mode 100644 src/common/change.ts diff --git a/spec/common/change.spec.ts b/spec/common/change.spec.ts new file mode 100644 index 000000000..258fc8463 --- /dev/null +++ b/spec/common/change.spec.ts @@ -0,0 +1,120 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as change from '../../src/common/change'; + +describe('Change', () => { + describe('applyFieldMask', () => { + const after = { + foo: 'bar', + num: 2, + obj: { + a: 1, + b: 2, + }, + }; + + it('should handle deleted values', () => { + const sparseBefore = { baz: 'qux' }; + const fieldMask = 'baz'; + expect( + change.applyFieldMask(sparseBefore, after, fieldMask) + ).to.deep.equal({ + foo: 'bar', + num: 2, + obj: { + a: 1, + b: 2, + }, + baz: 'qux', + }); + }); + + it('should handle created values', () => { + const sparseBefore = {}; + const fieldMask = 'num,obj.a'; + expect( + change.applyFieldMask(sparseBefore, after, fieldMask) + ).to.deep.equal({ + foo: 'bar', + obj: { + b: 2, + }, + }); + }); + + it('should handle mutated values', () => { + const sparseBefore = { + num: 3, + obj: { + a: 3, + }, + }; + const fieldMask = 'num,obj.a'; + expect( + change.applyFieldMask(sparseBefore, after, fieldMask) + ).to.deep.equal({ + foo: 'bar', + num: 3, + obj: { + a: 3, + b: 2, + }, + }); + }); + }); + + describe('fromJSON', () => { + it('should create a Change object with a `before` and `after`', () => { + const created = change.Change.fromJSON({ + before: { foo: 'bar' }, + after: { foo: 'faz' }, + }); + expect(created instanceof change.Change).to.equal(true); + expect(created.before).to.deep.equal({ foo: 'bar' }); + expect(created.after).to.deep.equal({ foo: 'faz' }); + }); + + it('should apply the customizer function to `before` and `after`', () => { + function customizer(input: any) { + input.another = 'value'; + return input as T; + } + const created = change.Change.fromJSON( + { + before: { foo: 'bar' }, + after: { foo: 'faz' }, + }, + customizer + ); + expect(created.before).to.deep.equal({ + foo: 'bar', + another: 'value', + }); + expect(created.after).to.deep.equal({ + foo: 'faz', + another: 'value', + }); + }); + }); +}); diff --git a/spec/v1/cloud-functions.spec.ts b/spec/v1/cloud-functions.spec.ts index fec1bd580..336c00282 100644 --- a/spec/v1/cloud-functions.spec.ts +++ b/spec/v1/cloud-functions.spec.ts @@ -24,7 +24,6 @@ import { expect } from 'chai'; import * as _ from 'lodash'; import { - Change, Event, EventContext, makeCloudFunction, @@ -354,99 +353,3 @@ describe('makeAuth and makeAuthType', () => { }); }); }); - -describe('Change', () => { - describe('applyFieldMask', () => { - const after = { - foo: 'bar', - num: 2, - obj: { - a: 1, - b: 2, - }, - }; - - it('should handle deleted values', () => { - const sparseBefore = { baz: 'qux' }; - const fieldMask = 'baz'; - expect( - Change.applyFieldMask(sparseBefore, after, fieldMask) - ).to.deep.equal({ - foo: 'bar', - num: 2, - obj: { - a: 1, - b: 2, - }, - baz: 'qux', - }); - }); - - it('should handle created values', () => { - const sparseBefore = {}; - const fieldMask = 'num,obj.a'; - expect( - Change.applyFieldMask(sparseBefore, after, fieldMask) - ).to.deep.equal({ - foo: 'bar', - obj: { - b: 2, - }, - }); - }); - - it('should handle mutated values', () => { - const sparseBefore = { - num: 3, - obj: { - a: 3, - }, - }; - const fieldMask = 'num,obj.a'; - expect( - Change.applyFieldMask(sparseBefore, after, fieldMask) - ).to.deep.equal({ - foo: 'bar', - num: 3, - obj: { - a: 3, - b: 2, - }, - }); - }); - }); - - describe('fromJSON', () => { - it('should create a Change object with a `before` and `after`', () => { - const created = Change.fromJSON({ - before: { foo: 'bar' }, - after: { foo: 'faz' }, - }); - expect(created instanceof Change).to.equal(true); - expect(created.before).to.deep.equal({ foo: 'bar' }); - expect(created.after).to.deep.equal({ foo: 'faz' }); - }); - - it('should apply the customizer function to `before` and `after`', () => { - function customizer(input: any) { - _.set(input, 'another', 'value'); - return input as T; - } - const created = Change.fromJSON( - { - before: { foo: 'bar' }, - after: { foo: 'faz' }, - }, - customizer - ); - expect(created.before).to.deep.equal({ - foo: 'bar', - another: 'value', - }); - expect(created.after).to.deep.equal({ - foo: 'faz', - another: 'value', - }); - }); - }); -}); diff --git a/spec/v2/providers/database.spec.ts b/spec/v2/providers/database.spec.ts index 58d4285ab..f700c58d3 100644 --- a/spec/v2/providers/database.spec.ts +++ b/spec/v2/providers/database.spec.ts @@ -363,9 +363,9 @@ describe('database', () => { }); }); - describe('onRefWritten', () => { + describe('onValueWritten', () => { it('should create a function with a reference', () => { - const func = database.onRefWritten('/foo/{bar}/', (event) => 2); + const func = database.onValueWritten('/foo/{bar}/', (event) => 2); expect(func.__endpoint).to.deep.equal({ platform: 'gcfv2', @@ -383,7 +383,7 @@ describe('database', () => { }); it('should create a function with opts', () => { - const func = database.onRefWritten( + const func = database.onValueWritten( { ref: '/foo/{path=**}/{bar}/', instance: 'my-instance', @@ -414,9 +414,9 @@ describe('database', () => { }); }); - describe('onRefCreated', () => { + describe('onValueCreated', () => { it('should create a function with a reference', () => { - const func = database.onRefCreated('/foo/{bar}/', (event) => 2); + const func = database.onValueCreated('/foo/{bar}/', (event) => 2); expect(func.__endpoint).to.deep.equal({ platform: 'gcfv2', @@ -434,7 +434,7 @@ describe('database', () => { }); it('should create a function with opts', () => { - const func = database.onRefCreated( + const func = database.onValueCreated( { ref: '/foo/{path=**}/{bar}/', instance: 'my-instance', @@ -465,9 +465,9 @@ describe('database', () => { }); }); - describe('onRefUpdated', () => { + describe('onValueUpdated', () => { it('should create a function with a reference', () => { - const func = database.onRefUpdated('/foo/{bar}/', (event) => 2); + const func = database.onValueUpdated('/foo/{bar}/', (event) => 2); expect(func.__endpoint).to.deep.equal({ platform: 'gcfv2', @@ -485,7 +485,7 @@ describe('database', () => { }); it('should create a function with opts', () => { - const func = database.onRefUpdated( + const func = database.onValueUpdated( { ref: '/foo/{path=**}/{bar}/', instance: 'my-instance', @@ -516,9 +516,9 @@ describe('database', () => { }); }); - describe('onRefDeleted', () => { + describe('onValueDeleted', () => { it('should create a function with a reference', () => { - const func = database.onRefDeleted('/foo/{bar}/', (event) => 2); + const func = database.onValueDeleted('/foo/{bar}/', (event) => 2); expect(func.__endpoint).to.deep.equal({ platform: 'gcfv2', @@ -536,7 +536,7 @@ describe('database', () => { }); it('should create a function with opts', () => { - const func = database.onRefDeleted( + const func = database.onValueDeleted( { ref: '/foo/{path=**}/{bar}/', instance: 'my-instance', diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index a362230fb..f866faa22 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -155,95 +155,6 @@ export interface EventContext { timestamp: string; } -/** - * The Functions interface for events that change state, such as - * Realtime Database or Cloud Firestore `onWrite` and `onUpdate`. - * - * For more information about the format used to construct `Change` objects, see - * [`cloud-functions.ChangeJson`](/docs/reference/functions/cloud_functions_.changejson). - * - */ -export class Change { - constructor(public before: T, public after: T) {} -} - -/** - * `ChangeJson` is the JSON format used to construct a Change object. - */ -export interface ChangeJson { - /** - * Key-value pairs representing state of data after the change. - */ - after?: any; - /** - * Key-value pairs representing state of data before the change. If - * `fieldMask` is set, then only fields that changed are present in `before`. - */ - before?: any; - /** - * @hidden - * Comma-separated string that represents names of fields that changed. - */ - fieldMask?: string; -} - -export namespace Change { - /** @hidden */ - function reinterpretCast(x: any) { - return x as T; - } - - /** - * @hidden - * Factory method for creating a Change from a `before` object and an `after` - * object. - */ - export function fromObjects(before: T, after: T) { - return new Change(before, after); - } - - /** - * @hidden - * Factory method for creating a Change from a JSON and an optional customizer - * function to be applied to both the `before` and the `after` fields. - */ - export function fromJSON( - json: ChangeJson, - customizer: (x: any) => T = reinterpretCast - ): Change { - let before = { ...json.before }; - if (json.fieldMask) { - before = applyFieldMask(before, json.after, json.fieldMask); - } - - return Change.fromObjects( - customizer(before || {}), - customizer(json.after || {}) - ); - } - - /** @hidden */ - export function applyFieldMask( - sparseBefore: any, - after: any, - fieldMask: string - ) { - const before = { ...after }; - const masks = fieldMask.split(','); - - masks.forEach((mask) => { - const val = _.get(sparseBefore, mask); - if (typeof val === 'undefined') { - _.unset(before, mask); - } else { - _.set(before, mask, val); - } - }); - - return before; - } -} - /** * Resource is a standard format for defining a resource * (google.rpc.context.AttributeContext.Resource). In Cloud Functions, it is the diff --git a/src/common/change.ts b/src/common/change.ts new file mode 100644 index 000000000..c0180e0a0 --- /dev/null +++ b/src/common/change.ts @@ -0,0 +1,109 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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. + +/** + * `ChangeJson` is the JSON format used to construct a Change object. + */ +export interface ChangeJson { + /** + * Key-value pairs representing state of data after the change. + */ + after?: any; + /** + * Key-value pairs representing state of data before the change. If + * `fieldMask` is set, then only fields that changed are present in `before`. + */ + before?: any; + /** + * @hidden + * Comma-separated string that represents names of fields that changed. + */ + fieldMask?: string; +} + +/** @hidden */ +export function applyFieldMask( + sparseBefore: any, + after: any, + fieldMask: string +) { + const before = { ...after }; + const masks = fieldMask.split(','); + + for (const mask of masks) { + const parts = mask.split('.'); + const head = parts[0]; + const tail = parts.slice(1).join('.'); + if (parts.length > 1) { + before[head] = applyFieldMask(sparseBefore?.[head], after[head], tail); + continue; + } + const val = sparseBefore?.[head]; + if (typeof val === 'undefined') { + delete before[mask]; + } else { + before[mask] = val; + } + } + + return before; +} + +/** + * The Functions interface for events that change state, such as + * Realtime Database or Cloud Firestore `onWrite` and `onUpdate`. + * + * For more information about the format used to construct `Change` objects, see + * {@link ChangeJson} below. + * + */ +export class Change { + /** + * @hidden + * Factory method for creating a Change from a `before` object and an `after` + * object. + */ + static fromObjects(before: T, after: T) { + return new Change(before, after); + } + + /** + * @hidden + * Factory method for creating a Change from a JSON and an optional customizer + * function to be applied to both the `before` and the `after` fields. + */ + static fromJSON( + json: ChangeJson, + customizer: (x: any) => T = (x) => x as T + ): Change { + let before = { ...json.before }; + if (json.fieldMask) { + before = applyFieldMask(before, json.after, json.fieldMask); + } + + return Change.fromObjects( + customizer(before || {}), + customizer(json.after || {}) + ); + } + constructor(public before: T, public after: T) {} +} diff --git a/src/providers/database.ts b/src/providers/database.ts index 5b7dc59f5..39feaf0e8 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -22,12 +22,12 @@ import { apps } from '../apps'; import { - Change, CloudFunction, Event, EventContext, makeCloudFunction, } from '../cloud-functions'; +import { Change } from '../common/change'; import { DataSnapshot } from '../common/providers/database'; import { firebaseConfig } from '../config'; import { DeploymentOptions } from '../function-configuration'; diff --git a/src/providers/firestore.ts b/src/providers/firestore.ts index 620040d2b..73d8eed13 100644 --- a/src/providers/firestore.ts +++ b/src/providers/firestore.ts @@ -26,12 +26,12 @@ import * as _ from 'lodash'; import { posix } from 'path'; import { apps } from '../apps'; import { - Change, CloudFunction, Event, EventContext, makeCloudFunction, } from '../cloud-functions'; +import { Change } from '../common/change'; import { dateToTimestampProto } from '../encoder'; import { DeploymentOptions } from '../function-configuration'; import * as logger from '../logger'; diff --git a/src/v2/core.ts b/src/v2/core.ts index 5e71d2458..7bd88fc8d 100644 --- a/src/v2/core.ts +++ b/src/v2/core.ts @@ -25,8 +25,11 @@ * @packageDocumentation */ +import { Change } from '../common/change'; import { ManifestEndpoint } from '../runtime/manifest'; +export { Change }; + /** @internal */ export interface TriggerAnnotation { platform?: string; diff --git a/src/v2/providers/database.ts b/src/v2/providers/database.ts index e0b410422..1ab09f666 100644 --- a/src/v2/providers/database.ts +++ b/src/v2/providers/database.ts @@ -21,7 +21,7 @@ // SOFTWARE. import { apps } from '../../apps'; -import { Change } from '../../cloud-functions'; +import { Change } from '../../common/change'; import { DataSnapshot } from '../../common/providers/database'; import { ManifestEndpoint } from '../../runtime/manifest'; import { normalizePath } from '../../utilities/path'; @@ -84,12 +84,106 @@ export interface ReferenceOptions extends options.EventHandlerOptions { * Examples: '/foo/bar', '/foo/{bar}' */ ref: string; + /** * Specify the handler to trigger on a database instance(s). * If present, this value can either be a single instance or a pattern. * Examples: 'my-instance-1', '{instance}' */ instance?: string; + + /** + * Region where functions should be deployed. + */ + region?: options.SupportedRegion | string; + + /** + * Amount of memory to allocate to a function. + * A value of null restores the defaults of 256MB. + */ + memory?: options.MemoryOption | null; + + /** + * Timeout for the function in sections, possible values are 0 to 540. + * HTTPS functions can specify a higher timeout. + * A value of null restores the default of 60s + * The minimum timeout for a gen 2 function is 1s. The maximum timeout for a + * function depends on the type of function: Event handling functions have a + * maximum timeout of 540s (9 minutes). HTTPS and callable functions have a + * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum + * timeout of 1,800s (30 minutes) + */ + timeoutSeconds?: number | null; + + /** + * Min number of actual instances to be running at a given time. + * Instances will be billed for memory allocation and 10% of CPU allocation + * while idle. + * A value of null restores the default min instances. + */ + minInstances?: number | null; + + /** + * Max number of instances to be running in parallel. + * A value of null restores the default max instances. + */ + maxInstances?: number | null; + + /** + * Number of requests a function can serve at once. + * Can only be applied to functions running on Cloud Functions v2. + * A value of null restores the default concurrency (80 when CPU >= 1, 1 otherwise). + * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. + * The maximum value for concurrency is 1,000. + */ + concurrency?: number | null; + + /** + * Fractional number of CPUs to allocate to a function. + * Defaults to 1 for functions with <= 2GB RAM and increases for larger memory sizes. + * This is different from the defaults when using the gcloud utility and is different from + * the fixed amount assigned in Google Cloud Functions generation 1. + * To revert to the CPU amounts used in gcloud or in Cloud Functions generation 1, set this + * to the value "gcf_gen1" + */ + cpu?: number | 'gcf_gen1'; + + /** + * Connect cloud function to specified VPC connector. + * A value of null removes the VPC connector + */ + vpcConnector?: string | null; + + /** + * Egress settings for VPC connector. + * A value of null turns off VPC connector egress settings + */ + vpcConnectorEgressSettings?: options.VpcEgressSetting | null; + + /** + * Specific service account for the function to run as. + * A value of null restores the default service account. + */ + serviceAccount?: string | null; + + /** + * Ingress settings which control where this function can be called from. + * A value of null turns off ingress settings. + */ + ingressSettings?: options.IngressSetting | null; + + /** + * User labels to set on the function. + */ + labels?: Record; + + /* + * Secrets to bind to a function. + */ + secrets?: string[]; + + /** Whether failed executions should be delivered again. */ + retry?: boolean; } /** @@ -98,7 +192,7 @@ export interface ReferenceOptions extends options.EventHandlerOptions { * @param reference - The database reference path to trigger on. * @param handler - Event handler which is run every time a Realtime Database create, update, or delete occurs. */ -export function onRefWritten( +export function onValueWritten( reference: string, handler: (event: DatabaseEvent>) => any | Promise ): CloudFunction>>; @@ -109,7 +203,7 @@ export function onRefWritten( * @param opts - Options that can be set on an individual event-handling function. * @param handler - Event handler which is run every time a Realtime Database create, update, or delete occurs. */ -export function onRefWritten( +export function onValueWritten( opts: ReferenceOptions, handler: (event: DatabaseEvent>) => any | Promise ): CloudFunction>>; @@ -120,7 +214,7 @@ export function onRefWritten( * @param referenceOrOpts - Options or a string reference. * @param handler - Event handler which is run every time a Realtime Database create, update, or delete occurs. */ -export function onRefWritten( +export function onValueWritten( referenceOrOpts: string | ReferenceOptions, handler: (event: DatabaseEvent>) => any | Promise ): CloudFunction>> { @@ -133,7 +227,7 @@ export function onRefWritten( * @param reference - The database reference path to trigger on. * @param handler - Event handler which is run every time a Realtime Database create occurs. */ -export function onRefCreated( +export function onValueCreated( reference: string, handler: (event: DatabaseEvent) => any | Promise ): CloudFunction>; @@ -144,7 +238,7 @@ export function onRefCreated( * @param opts - Options that can be set on an individual event-handling function. * @param handler - Event handler which is run every time a Realtime Database create occurs. */ -export function onRefCreated( +export function onValueCreated( opts: ReferenceOptions, handler: (event: DatabaseEvent) => any | Promise ): CloudFunction>; @@ -155,7 +249,7 @@ export function onRefCreated( * @param referenceOrOpts - Options or a string reference. * @param handler - Event handler which is run every time a Realtime Database create occurs. */ -export function onRefCreated( +export function onValueCreated( referenceOrOpts: string | ReferenceOptions, handler: (event: DatabaseEvent) => any | Promise ): CloudFunction> { @@ -168,7 +262,7 @@ export function onRefCreated( * @param reference - The database reference path to trigger on. * @param handler - Event handler which is run every time a Realtime Database update occurs. */ -export function onRefUpdated( +export function onValueUpdated( reference: string, handler: (event: DatabaseEvent>) => any | Promise ): CloudFunction>>; @@ -179,7 +273,7 @@ export function onRefUpdated( * @param opts - Options that can be set on an individual event-handling function. * @param handler - Event handler which is run every time a Realtime Database update occurs. */ -export function onRefUpdated( +export function onValueUpdated( opts: ReferenceOptions, handler: (event: DatabaseEvent>) => any | Promise ): CloudFunction>>; @@ -190,7 +284,7 @@ export function onRefUpdated( * @param referenceOrOpts - Options or a string reference. * @param handler - Event handler which is run every time a Realtime Database update occurs. */ -export function onRefUpdated( +export function onValueUpdated( referenceOrOpts: string | ReferenceOptions, handler: (event: DatabaseEvent>) => any | Promise ): CloudFunction>> { @@ -203,7 +297,7 @@ export function onRefUpdated( * @param reference - The database reference path to trigger on. * @param handler - Event handler which is run every time a Realtime Database deletion occurs. */ -export function onRefDeleted( +export function onValueDeleted( reference: string, handler: (event: DatabaseEvent) => any | Promise ): CloudFunction>; @@ -214,7 +308,7 @@ export function onRefDeleted( * @param opts - Options that can be set on an individual event-handling function. * @param handler - Event handler which is run every time a Realtime Database deletion occurs. */ -export function onRefDeleted( +export function onValueDeleted( opts: ReferenceOptions, handler: (event: DatabaseEvent) => any | Promise ): CloudFunction>; @@ -225,7 +319,7 @@ export function onRefDeleted( * @param referenceOrOpts - Options or a string reference. * @param handler - Event handler which is run every time a Realtime Database deletion occurs. */ -export function onRefDeleted( +export function onValueDeleted( referenceOrOpts: string | ReferenceOptions, handler: (event: DatabaseEvent) => any | Promise ): CloudFunction> { From a00ad925ce5a24124d5354d1ff533bb7bd0f8c76 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Tue, 28 Jun 2022 15:55:08 -0400 Subject: [PATCH 121/370] revert datasnapshot (#1156) --- spec/v1/providers/database.spec.ts | 143 ++--------------------------- src/common/providers/database.ts | 98 ++++++++------------ 2 files changed, 46 insertions(+), 195 deletions(-) diff --git a/spec/v1/providers/database.spec.ts b/spec/v1/providers/database.spec.ts index 30d24ea3c..304c1d1fd 100644 --- a/spec/v1/providers/database.spec.ts +++ b/spec/v1/providers/database.spec.ts @@ -639,6 +639,10 @@ describe('Database Functions', () => { expect(subject.val()).to.equal(0); populate({ myKey: 0 }); expect(subject.val()).to.deep.equal({ myKey: 0 }); + + // Null values are still reported as null. + populate({ myKey: null }); + expect(subject.val()).to.deep.equal({ myKey: null }); }); // Regression test: .val() was returning array of nulls when there's a property called length (BUG#37683995) @@ -646,45 +650,6 @@ describe('Database Functions', () => { populate({ length: 3, foo: 'bar' }); expect(subject.val()).to.deep.equal({ length: 3, foo: 'bar' }); }); - - it('should deal with null-values appropriately', () => { - populate(null); - expect(subject.val()).to.be.null; - - populate({ myKey: null }); - expect(subject.val()).to.be.null; - }); - - it('should deal with empty object values appropriately', () => { - populate({}); - expect(subject.val()).to.be.null; - - populate({ myKey: {} }); - expect(subject.val()).to.be.null; - - populate({ myKey: { child: null } }); - expect(subject.val()).to.be.null; - }); - - it('should deal with empty array values appropriately', () => { - populate([]); - expect(subject.val()).to.be.null; - - populate({ myKey: [] }); - expect(subject.val()).to.be.null; - - populate({ myKey: [null] }); - expect(subject.val()).to.be.null; - - populate({ myKey: [{}] }); - expect(subject.val()).to.be.null; - - populate({ myKey: [{ myKey: null }] }); - expect(subject.val()).to.be.null; - - populate({ myKey: [{ myKey: {} }] }); - expect(subject.val()).to.be.null; - }); }); describe('#child(): DataSnapshot', () => { @@ -711,37 +676,14 @@ describe('Database Functions', () => { }); it('should be false for a non-existent value', () => { - populate({ a: { b: 'c', nullChild: null } }); + populate({ a: { b: 'c' } }); expect(subject.child('d').exists()).to.be.false; - expect(subject.child('nullChild').exists()).to.be.false; }); it('should be false for a value pathed beyond a leaf', () => { populate({ a: { b: 'c' } }); expect(subject.child('a/b/c').exists()).to.be.false; }); - - it('should be false for an empty object value', () => { - populate({ a: {} }); - expect(subject.child('a').exists()).to.be.false; - - populate({ a: { child: null } }); - expect(subject.child('a').exists()).to.be.false; - - populate({ a: { child: {} } }); - expect(subject.child('a').exists()).to.be.false; - }); - - it('should be false for an empty array value', () => { - populate({ a: [] }); - expect(subject.child('a').exists()).to.be.false; - - populate({ a: [null] }); - expect(subject.child('a').exists()).to.be.false; - - populate({ a: [{}] }); - expect(subject.child('a').exists()).to.be.false; - }); }); describe('#forEach(action: (a: DataSnapshot) => boolean): boolean', () => { @@ -770,17 +712,6 @@ describe('Database Functions', () => { expect(subject.forEach(counter)).to.equal(false); expect(count).to.eq(0); - - populate({ - a: 'foo', - nullChild: null, - emptyObjectChild: {}, - emptyArrayChild: [], - }); - count = 0; - - expect(subject.forEach(counter)).to.equal(false); - expect(count).to.eq(1); }); it('should cancel further enumeration if callback returns true', () => { @@ -820,51 +751,13 @@ describe('Database Functions', () => { describe('#numChildren()', () => { it('should be key count for objects', () => { - populate({ - a: 'b', - c: 'd', - nullChild: null, - emptyObjectChild: {}, - emptyArrayChild: [], - }); + populate({ a: 'b', c: 'd' }); expect(subject.numChildren()).to.eq(2); }); it('should be 0 for non-objects', () => { populate(23); expect(subject.numChildren()).to.eq(0); - - populate({ - nullChild: null, - emptyObjectChild: {}, - emptyArrayChild: [], - }); - expect(subject.numChildren()).to.eq(0); - }); - }); - - describe('#hasChildren()', () => { - it('should true for objects', () => { - populate({ - a: 'b', - c: 'd', - nullChild: null, - emptyObjectChild: {}, - emptyArrayChild: [], - }); - expect(subject.hasChildren()).to.be.true; - }); - - it('should be false for non-objects', () => { - populate(23); - expect(subject.hasChildren()).to.be.false; - - populate({ - nullChild: null, - emptyObjectChild: {}, - emptyArrayChild: [], - }); - expect(subject.hasChildren()).to.be.false; }); }); @@ -876,17 +769,9 @@ describe('Database Functions', () => { }); it('should return false if a child is missing', () => { - populate({ - a: 'b', - nullChild: null, - emptyObjectChild: {}, - emptyArrayChild: [], - }); + populate({ a: 'b' }); expect(subject.hasChild('c')).to.be.false; expect(subject.hasChild('a/b')).to.be.false; - expect(subject.hasChild('nullChild')).to.be.false; - expect(subject.hasChild('emptyObjectChild')).to.be.false; - expect(subject.hasChild('emptyArrayChild')).to.be.false; }); }); @@ -916,21 +801,11 @@ describe('Database Functions', () => { describe('#toJSON(): Object', () => { it('should return the current value', () => { - populate({ - a: 'b', - nullChild: null, - emptyObjectChild: {}, - emptyArrayChild: [], - }); + populate({ a: 'b' }); expect(subject.toJSON()).to.deep.equal(subject.val()); }); it('should be stringifyable', () => { - populate({ - a: 'b', - nullChild: null, - emptyObjectChild: {}, - emptyArrayChild: [], - }); + populate({ a: 'b' }); expect(JSON.stringify(subject)).to.deep.equal('{"a":"b"}'); }); }); diff --git a/src/common/providers/database.ts b/src/common/providers/database.ts index f14ce8b1d..f1c41d617 100644 --- a/src/common/providers/database.ts +++ b/src/common/providers/database.ts @@ -21,13 +21,13 @@ // SOFTWARE. import * as firebase from 'firebase-admin'; -import { firebaseConfig } from '../../config'; +import * as _ from 'lodash'; import { joinPath, pathParts } from '../../utilities/path'; /** - * Interface representing a Firebase Realtime database data snapshot. + * Interface representing a Firebase Realtime Database data snapshot. */ -export class DataSnapshot implements firebase.database.DataSnapshot { +export class DataSnapshot { public instance: string; /** @hidden */ @@ -44,11 +44,10 @@ export class DataSnapshot implements firebase.database.DataSnapshot { constructor( data: any, - path?: string, // path is undefined for the database root + path?: string, // path will be undefined for the database root private app?: firebase.app.App, instance?: string ) { - const config = firebaseConfig(); if (app?.options?.databaseURL?.startsWith('http:')) { // In this case we're dealing with an emulator this.instance = app.options.databaseURL; @@ -57,13 +56,9 @@ export class DataSnapshot implements firebase.database.DataSnapshot { this.instance = instance; } else if (app) { this.instance = app.options.databaseURL; - } else if (config.databaseURL) { - this.instance = config.databaseURL; } else if (process.env.GCLOUD_PROJECT) { this.instance = - 'https://' + - process.env.GCLOUD_PROJECT + - '-default-rtdb.firebaseio.com'; + 'https://' + process.env.GCLOUD_PROJECT + '.firebaseio.com'; } this._path = path; @@ -72,7 +67,7 @@ export class DataSnapshot implements firebase.database.DataSnapshot { /** * Returns a [`Reference`](/docs/reference/admin/node/admin.database.Reference) - * to the database location where the triggering write occurred. Has + * to the Database location where the triggering write occurred. Has * full read and write access. */ get ref(): firebase.database.Reference { @@ -92,14 +87,13 @@ export class DataSnapshot implements firebase.database.DataSnapshot { /** * The key (last part of the path) of the location of this `DataSnapshot`. * - * The last token in a database location is considered its key. For example, + * The last token in a Database location is considered its key. For example, * "ada" is the key for the `/users/ada/` node. Accessing the key on any - * `DataSnapshot` returns the key for the location that generated it. - * However, accessing the key on the root URL of a database returns `null`. + * `DataSnapshot` will return the key for the location that generated it. + * However, accessing the key on the root URL of a Database will return `null`. */ - get key(): string | null { - const segments = pathParts(this._fullPath()); - const last = segments[segments.length - 1]; + get key(): string { + const last = _.last(pathParts(this._fullPath())); return !last || last === '' ? null : last; } @@ -111,25 +105,24 @@ export class DataSnapshot implements firebase.database.DataSnapshot { * return `null`, indicating that the `DataSnapshot` is empty (contains no * data). * - * @return The snapshot's contents as a JavaScript value (Object, + * @return The DataSnapshot's contents as a JavaScript value (Object, * Array, string, number, boolean, or `null`). */ val(): any { const parts = pathParts(this._childPath); - let source = this._data; - if (parts.length) { - for (const part of parts) { - source = source[part]; - } - } - const node = source ?? null; - + const source = this._data; + const node = _.cloneDeep( + parts.length ? _.get(source, parts, null) : source + ); return this._checkAndConvertToArray(node); } /** * Exports the entire contents of the `DataSnapshot` as a JavaScript object. * + * The `exportVal()` method is similar to `val()`, except priority information + * is included (if available), making it suitable for backing up your data. + * * @return The contents of the `DataSnapshot` as a JavaScript value * (Object, Array, string, number, boolean, or `null`). */ @@ -157,14 +150,7 @@ export class DataSnapshot implements firebase.database.DataSnapshot { * @return `true` if this `DataSnapshot` contains any data; otherwise, `false`. */ exists(): boolean { - const val = this.val(); - if (!val || val === null) { - return false; - } - if (typeof val === 'object' && Object.keys(val).length === 0) { - return false; - } - return true; + return !_.isNull(this.val()); } /** @@ -191,22 +177,25 @@ export class DataSnapshot implements firebase.database.DataSnapshot { * JavaScript object returned by `val()` is not guaranteed to match the ordering * on the server nor the ordering of `child_added` events. That is where * `forEach()` comes in handy. It guarantees the children of a `DataSnapshot` - * can be iterated in their query order. + * will be iterated in their query order. * * If no explicit `orderBy*()` method is used, results are returned * ordered by key (unless priorities are used, in which case, results are * returned by priority). * - * @param action A function that is called for each child `DataSnapshot`. + * @param action A function that will be called for each child `DataSnapshot`. * The callback can return `true` to cancel further enumeration. * * @return `true` if enumeration was canceled due to your callback * returning `true`. */ forEach(action: (a: DataSnapshot) => boolean | void): boolean { - const val = this.val() || {}; - if (typeof val === 'object') { - return Object.keys(val).some((key) => action(this.child(key)) === true); + const val = this.val(); + if (_.isPlainObject(val)) { + return _.some( + val, + (value, key: string) => action(this.child(key)) === true + ); } return false; } @@ -229,16 +218,14 @@ export class DataSnapshot implements firebase.database.DataSnapshot { * You can use `hasChildren()` to determine if a `DataSnapshot` has any * children. If it does, you can enumerate them using `forEach()`. If it * doesn't, then either this snapshot contains a primitive value (which can be - * retrieved with `val()`) or it is empty (in which case, `val()` returns + * retrieved with `val()`) or it is empty (in which case, `val()` will return * `null`). * * @return `true` if this snapshot has any children; else `false`. */ hasChildren(): boolean { const val = this.val(); - return ( - val !== null && typeof val === 'object' && Object.keys(val).length > 0 - ); + return _.isPlainObject(val) && _.keys(val).length > 0; } /** @@ -248,9 +235,7 @@ export class DataSnapshot implements firebase.database.DataSnapshot { */ numChildren(): number { const val = this.val(); - return val !== null && typeof val === 'object' - ? Object.keys(val).length - : 0; + return _.isPlainObject(val) ? Object.keys(val).length : 0; } /** @@ -282,12 +267,7 @@ export class DataSnapshot implements firebase.database.DataSnapshot { continue; } const childNode = node[key]; - const v = this._checkAndConvertToArray(childNode); - if (v === null) { - // Empty child node - continue; - } - obj[key] = v; + obj[key] = this._checkAndConvertToArray(childNode); numKeys++; const integerRegExp = /^(0|[1-9]\d*)$/; if (allIntegerKeys && integerRegExp.test(key)) { @@ -297,17 +277,12 @@ export class DataSnapshot implements firebase.database.DataSnapshot { } } - if (numKeys === 0) { - // Empty node - return null; - } - if (allIntegerKeys && maxKey < 2 * numKeys) { // convert to array. const array: any = []; - for (const key of Object.keys(obj)) { - array[key] = obj[key]; - } + _.forOwn(obj, (val, key) => { + array[key] = val; + }); return array; } @@ -333,6 +308,7 @@ export class DataSnapshot implements firebase.database.DataSnapshot { /** @hidden */ private _fullPath(): string { - return (this._path || '') + '/' + (this._childPath || ''); + const out = (this._path || '') + '/' + (this._childPath || ''); + return out; } } From 47f72d807454eaa1f410a7247c67cf291849f10c Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Wed, 29 Jun 2022 12:07:58 -0400 Subject: [PATCH 122/370] Re-export Change from v1 & v2 root (#1158) - Re-export Change from the v1 & v2 index.ts - In v1, we can export from `cloud-functions.ts` since that module is exported from the root --- src/cloud-functions.ts | 2 ++ src/v2/index.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index f866faa22..c90bdaea4 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -39,6 +39,8 @@ import { } from './common/encoding'; import { ManifestEndpoint, ManifestRequiredAPI } from './runtime/manifest'; +export { Change } from './common/change'; + /** @hidden */ const WILDCARD_REGEX = new RegExp('{[^/{}]*}', 'g'); diff --git a/src/v2/index.ts b/src/v2/index.ts index d2161ab71..dc460cd52 100644 --- a/src/v2/index.ts +++ b/src/v2/index.ts @@ -60,4 +60,6 @@ export { EventHandlerOptions, } from './options'; +export { Change } from '../common/change'; + export { CloudFunction, CloudEvent } from './core'; From 29ad81ea2c811cf60c209bc633de4fbbcd2fce20 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Wed, 29 Jun 2022 13:53:46 -0400 Subject: [PATCH 123/370] fixing docs (#1159) --- src/v2/providers/database.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/v2/providers/database.ts b/src/v2/providers/database.ts index 1ab09f666..acfdfb76d 100644 --- a/src/v2/providers/database.ts +++ b/src/v2/providers/database.ts @@ -88,7 +88,8 @@ export interface ReferenceOptions extends options.EventHandlerOptions { /** * Specify the handler to trigger on a database instance(s). * If present, this value can either be a single instance or a pattern. - * Examples: 'my-instance-1', '{instance}' + * Examples: 'my-instance-1', 'my-instance-*' + * Note: The capture syntax cannot be used for 'instance'. */ instance?: string; From a0faf00feab3f3ce0b13d11ffe57921737c9ee1e Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 29 Jun 2022 18:49:17 +0000 Subject: [PATCH 124/370] 3.22.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 c11eebe69..324031632 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.21.2", + "version": "3.22.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 7363f41df..0769610d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.21.2", + "version": "3.22.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 29903132dfddf44f8c051e31f1e0a240e5ff0578 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 29 Jun 2022 18:49:19 +0000 Subject: [PATCH 125/370] [firebase-release] Removed change log and reset repo after 3.22.0 release --- CHANGELOG.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc0bbf514..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +0,0 @@ -- Adds RTDB Triggers for v2 functions (#1127) -- Adds support for Firebase Admin SDK v11 (#1151) -- Fixes bug where emulated task queue function required auth header (#1154) From c9c55cf99368eb7c0badf74f26953b1b28b5c7fb Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 15 Jul 2022 14:11:47 -0700 Subject: [PATCH 126/370] Fix issues with outdated package-lock.json file. (#1170) Our `package-lock.json` file is outdated and our dependencies are a bit jumbled up. I didn't notice any issue when using `nodejs14` but when using `nodejs16` reveals several issues that I've tried to address in the PR: 1. Update `typedoc` dependency to avoid impossible dep resolution (first reported https://github.com/firebase/firebase-functions/pull/1169) 2. Regenerate `package-lock.json`. Looks like it's "behind" package.json. I took this opportunity to use `lockfileVersion: 2` which is the default if you use `nodejs16` 3. Using `firebase-admin` v10.3.0 broke some unit test we had. Fixed in 2ee9549120ce0ec3a069c657d47c08c68ee1b53f. --- package-lock.json | 7302 ++++++++++++++++++++++----- package.json | 6 +- spec/common/providers/https.spec.ts | 2 +- spec/fixtures/mockrequest.ts | 2 +- 4 files changed, 5926 insertions(+), 1386 deletions(-) diff --git a/package-lock.json b/package-lock.json index 324031632..c778ba086 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,5779 @@ { "name": "firebase-functions", "version": "3.22.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "firebase-functions", + "version": "3.22.0", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.5", + "@types/express": "4.17.3", + "cors": "^2.8.5", + "express": "^4.17.1", + "lodash": "^4.17.14", + "node-fetch": "^2.6.7" + }, + "bin": { + "firebase-functions": "lib/bin/firebase-functions.js" + }, + "devDependencies": { + "@firebase/api-documenter": "^0.1.2", + "@microsoft/api-documenter": "^7.13.45", + "@microsoft/api-extractor": "^7.18.7", + "@types/chai": "^4.1.7", + "@types/chai-as-promised": "^7.1.0", + "@types/jsonwebtoken": "^8.3.2", + "@types/lodash": "^4.14.135", + "@types/mocha": "^5.2.7", + "@types/mock-require": "^2.0.0", + "@types/nock": "^10.0.3", + "@types/node": "^8.10.50", + "@types/node-fetch": "^3.0.3", + "@types/sinon": "^7.0.13", + "api-extractor-model-me": "^0.1.1", + "chai": "^4.2.0", + "chai-as-promised": "^7.1.1", + "child-process-promise": "^2.2.1", + "firebase-admin": "^10.3.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", + "nock": "^10.0.6", + "node-fetch": "^2.6.7", + "portfinder": "^1.0.28", + "prettier": "^1.18.2", + "semver": "^7.3.5", + "sinon": "^7.3.2", + "ts-node": "^10.4.0", + "tslint": "^5.18.0", + "tslint-config-prettier": "^1.18.0", + "tslint-no-unused-expression-chai": "^0.1.4", + "tslint-plugin-prettier": "^2.0.1", + "typedoc": "0.23.7", + "typescript": "^4.3.5", + "yargs": "^15.3.1" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + }, + "peerDependencies": { + "firebase-admin": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.16.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.17.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cspotcode/source-map-consumer": { + "version": "0.8.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-consumer": "0.8.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@fastify/busboy": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "text-decoding": "^1.0.0" + }, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/@firebase/api-documenter": { + "version": "0.1.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@microsoft/tsdoc": "0.12.24", + "@rushstack/node-core-library": "3.36.0", + "@rushstack/ts-command-line": "4.7.8", + "api-extractor-model-me": "0.1.1", + "colors": "~1.2.1", + "js-yaml": "4.0.0", + "resolve": "~1.17.0", + "tslib": "^2.1.0" + }, + "bin": { + "api-documenter-fire": "dist/start.js" + } + }, + "node_modules/@firebase/api-documenter/node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@firebase/api-documenter/node_modules/js-yaml": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@firebase/api-documenter/node_modules/resolve": { + "version": "1.17.0", + "dev": true, + "license": "MIT", + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.7.0", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.1.6", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.5.17", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@firebase/util": "1.6.3", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database": { + "version": "0.13.3", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@firebase/auth-interop-types": "0.1.6", + "@firebase/component": "0.5.17", + "@firebase/logger": "0.3.3", + "@firebase/util": "1.6.3", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "0.2.3", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.5.17", + "@firebase/database": "0.13.3", + "@firebase/database-types": "0.9.11", + "@firebase/logger": "0.3.3", + "@firebase/util": "1.6.3", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "0.9.11", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-types": "0.7.0", + "@firebase/util": "1.6.3" + } + }, + "node_modules/@firebase/logger": { + "version": "0.3.3", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/util": { + "version": "1.6.3", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@google-cloud/firestore": { + "version": "4.15.1", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^2.24.1", + "protobufjs": "^6.8.6" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@google-cloud/paginator": { + "version": "3.0.7", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@google-cloud/projectify": { + "version": "2.1.1", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@google-cloud/promisify": { + "version": "2.0.4", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@google-cloud/storage": { + "version": "5.20.5", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@google-cloud/paginator": "^3.0.7", + "@google-cloud/projectify": "^2.0.0", + "@google-cloud/promisify": "^2.0.0", + "abort-controller": "^3.0.0", + "arrify": "^2.0.0", + "async-retry": "^1.3.3", + "compressible": "^2.0.12", + "configstore": "^5.0.0", + "duplexify": "^4.0.0", + "ent": "^2.2.0", + "extend": "^3.0.2", + "gaxios": "^4.0.0", + "google-auth-library": "^7.14.1", + "hash-stream-validation": "^0.2.2", + "mime": "^3.0.0", + "mime-types": "^2.0.8", + "p-limit": "^3.0.1", + "pumpify": "^2.0.0", + "retry-request": "^4.2.2", + "stream-events": "^1.0.4", + "teeny-request": "^7.1.3", + "uuid": "^8.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@google-cloud/storage/node_modules/mime": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.6.7", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@grpc/proto-loader": "^0.6.4", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/grpc-js/node_modules/@types/node": { + "version": "18.0.4", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@grpc/proto-loader": { + "version": "0.6.13", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@types/long": "^4.0.1", + "lodash.camelcase": "^4.3.0", + "long": "^4.0.0", + "protobufjs": "^6.11.3", + "yargs": "^16.2.0" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader/node_modules/yargs": { + "version": "16.2.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@microsoft/api-documenter": { + "version": "7.17.11", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/api-extractor-model": "7.17.2", + "@microsoft/tsdoc": "0.14.1", + "@rushstack/node-core-library": "3.45.4", + "@rushstack/ts-command-line": "4.10.10", + "colors": "~1.2.1", + "js-yaml": "~3.13.1", + "resolve": "~1.17.0" + }, + "bin": { + "api-documenter": "bin/api-documenter" + } + }, + "node_modules/@microsoft/api-documenter/node_modules/@microsoft/tsdoc": { + "version": "0.14.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/api-documenter/node_modules/@rushstack/node-core-library": { + "version": "3.45.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "12.20.24", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "timsort": "~0.3.0", + "z-schema": "~5.0.2" + } + }, + "node_modules/@microsoft/api-documenter/node_modules/@rushstack/ts-command-line": { + "version": "4.10.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "colors": "~1.2.1", + "string-argv": "~0.3.1" + } + }, + "node_modules/@microsoft/api-documenter/node_modules/@types/node": { + "version": "12.20.24", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/api-documenter/node_modules/js-yaml": { + "version": "3.13.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@microsoft/api-documenter/node_modules/resolve": { + "version": "1.17.0", + "dev": true, + "license": "MIT", + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@microsoft/api-documenter/node_modules/validator": { + "version": "13.7.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/@microsoft/api-documenter/node_modules/z-schema": { + "version": "5.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^2.20.3" + } + }, + "node_modules/@microsoft/api-extractor": { + "version": "7.23.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/api-extractor-model": "7.17.2", + "@microsoft/tsdoc": "0.14.1", + "@microsoft/tsdoc-config": "~0.16.1", + "@rushstack/node-core-library": "3.45.4", + "@rushstack/rig-package": "0.3.11", + "@rushstack/ts-command-line": "4.10.10", + "colors": "~1.2.1", + "lodash": "~4.17.15", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "source-map": "~0.6.1", + "typescript": "~4.6.3" + }, + "bin": { + "api-extractor": "bin/api-extractor" + } + }, + "node_modules/@microsoft/api-extractor-model": { + "version": "7.17.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.14.1", + "@microsoft/tsdoc-config": "~0.16.1", + "@rushstack/node-core-library": "3.45.4" + } + }, + "node_modules/@microsoft/api-extractor-model/node_modules/@microsoft/tsdoc": { + "version": "0.14.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/api-extractor-model/node_modules/@rushstack/node-core-library": { + "version": "3.45.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "12.20.24", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "timsort": "~0.3.0", + "z-schema": "~5.0.2" + } + }, + "node_modules/@microsoft/api-extractor-model/node_modules/@types/node": { + "version": "12.20.24", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/api-extractor-model/node_modules/resolve": { + "version": "1.17.0", + "dev": true, + "license": "MIT", + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@microsoft/api-extractor-model/node_modules/validator": { + "version": "13.7.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/@microsoft/api-extractor-model/node_modules/z-schema": { + "version": "5.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^2.20.3" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/@microsoft/tsdoc": { + "version": "0.14.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/api-extractor/node_modules/@rushstack/node-core-library": { + "version": "3.45.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "12.20.24", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "timsort": "~0.3.0", + "z-schema": "~5.0.2" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/@rushstack/ts-command-line": { + "version": "4.10.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "colors": "~1.2.1", + "string-argv": "~0.3.1" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/@types/node": { + "version": "12.20.24", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/api-extractor/node_modules/resolve": { + "version": "1.17.0", + "dev": true, + "license": "MIT", + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/validator": { + "version": "13.7.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/z-schema": { + "version": "5.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^2.20.3" + } + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.12.24", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.16.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.14.1", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/@microsoft/tsdoc": { + "version": "0.14.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/tsdoc-config/node_modules/resolve": { + "version": "1.19.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@panva/asn1.js": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@rushstack/node-core-library": { + "version": "3.36.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "10.17.13", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "timsort": "~0.3.0", + "z-schema": "~3.18.3" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/@types/node": { + "version": "10.17.13", + "dev": true, + "license": "MIT" + }, + "node_modules/@rushstack/node-core-library/node_modules/resolve": { + "version": "1.17.0", + "dev": true, + "license": "MIT", + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@rushstack/rig-package": { + "version": "0.3.11", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "~1.17.0", + "strip-json-comments": "~3.1.1" + } + }, + "node_modules/@rushstack/rig-package/node_modules/resolve": { + "version": "1.17.0", + "dev": true, + "license": "MIT", + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@rushstack/rig-package/node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@rushstack/ts-command-line": { + "version": "4.7.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "colors": "~1.2.1", + "string-argv": "~0.3.1" + } + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.3", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/formatio": { + "version": "3.2.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1", + "@sinonjs/samsam": "^3.1.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "3.3.3", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.3.0", + "array-from": "^2.1.1", + "lodash": "^4.17.15" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.1", + "dev": true, + "license": "(Unlicense OR Apache-2.0)" + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.9", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/argparse": { + "version": "1.0.38", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/body-parser/node_modules/@types/node": { + "version": "17.0.29", + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "4.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai-as-promised": { + "version": "7.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect/node_modules/@types/node": { + "version": "17.0.29", + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.12", + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "4.17.3", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.28", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/express-serve-static-core/node_modules/@types/node": { + "version": "17.0.29", + "license": "MIT" + }, + "node_modules/@types/jsonwebtoken": { + "version": "8.5.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/lodash": { + "version": "4.14.182", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/long": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "license": "MIT" + }, + "node_modules/@types/mocha": { + "version": "5.2.7", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mock-require": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/nock": { + "version": "10.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "8.10.66", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node-fetch": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "node-fetch": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "license": "MIT" + }, + "node_modules/@types/serve-static": { + "version": "1.13.10", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static/node_modules/@types/node": { + "version": "17.0.29", + "license": "MIT" + }, + "node_modules/@types/sinon": { + "version": "7.5.2", + "dev": true, + "license": "MIT" + }, + "node_modules/abab": { + "version": "2.0.6", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.7.1", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "3.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/api-extractor-model-me": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.12.24", + "@rushstack/node-core-library": "3.36.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/array-from": { + "version": "2.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/arrify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/async": { + "version": "2.6.4", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/async-retry": { + "version": "1.3.3", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/bignumber.js": { + "version": "9.0.2", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/bn.js": { + "version": "4.12.0", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.0", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "dev": true, + "license": "ISC" + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/builtin-modules": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chai": { + "version": "4.3.6", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai-as-promised": { + "version": "7.1.1", + "dev": true, + "license": "WTFPL", + "dependencies": { + "check-error": "^1.0.2" + }, + "peerDependencies": { + "chai": ">= 2.1.2 < 5" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/chalk/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/check-error": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/child-process-promise": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^4.0.2", + "node-version": "^1.0.0", + "promise-polyfill": "^6.0.1" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/colors": { + "version": "1.2.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "dev": true, + "license": "MIT" + }, + "node_modules/compressible": { + "version": "2.0.18", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/configstore": { + "version": "5.0.1", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "dev": true, + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/data-urls/node_modules/webidl-conversions": { + "version": "6.1.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=10.4" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "8.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-eql": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/deep-equal": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/define-properties": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/diff": { + "version": "3.5.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/domexception": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexify": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/elliptic": { + "version": "6.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "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" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/ent": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/es-abstract": { + "version": "1.19.5", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-abstract/node_modules/object.assign": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.0.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "2.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.1", + "jest-docblock": "^21.0.0" + }, + "engines": { + "node": ">=4.0.0" + }, + "peerDependencies": { + "prettier": ">= 0.11.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.18.0", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.2.0", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-text-encoding": { + "version": "1.0.4", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/firebase-admin": { + "version": "10.3.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@fastify/busboy": "^1.1.0", + "@firebase/database-compat": "^0.2.0", + "@firebase/database-types": "^0.9.7", + "@types/node": ">=12.12.47", + "jsonwebtoken": "^8.5.1", + "jwks-rsa": "^2.0.2", + "node-forge": "^1.3.1", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "optionalDependencies": { + "@google-cloud/firestore": "^4.15.1", + "@google-cloud/storage": "^5.18.3" + } + }, + "node_modules/firebase-admin/node_modules/@types/node": { + "version": "18.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/flat": { + "version": "4.1.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "is-buffer": "~2.0.3" + }, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/form-data": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gaxios": { + "version": "4.3.3", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.7" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gcp-metadata": { + "version": "4.3.1", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "gaxios": "^4.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "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" + }, + "engines": { + "node": "*" + } + }, + "node_modules/google-auth-library": { + "version": "7.14.1", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^4.0.0", + "gcp-metadata": "^4.2.0", + "gtoken": "^5.0.4", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/google-auth-library/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/google-auth-library/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/google-gax": { + "version": "2.30.5", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@grpc/grpc-js": "~1.6.0", + "@grpc/proto-loader": "^0.6.12", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "fast-text-encoding": "^1.0.3", + "google-auth-library": "^7.14.0", + "is-stream-ended": "^0.1.4", + "node-fetch": "^2.6.1", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^0.1.8", + "protobufjs": "6.11.3", + "retry-request": "^4.0.0" + }, + "bin": { + "compileProtos": "build/tools/compileProtos.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/google-p12-pem": { + "version": "3.1.4", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-forge": "^1.3.1" + }, + "bin": { + "gp12-pem": "build/src/bin/gp12-pem.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "dev": true, + "license": "ISC" + }, + "node_modules/growl": { + "version": "1.10.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.x" + } + }, + "node_modules/gtoken": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "gaxios": "^4.0.0", + "google-p12-pem": "^3.1.3", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/has": { + "version": "1.0.3", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-stream-validation": { + "version": "0.2.4", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/hash.js": { + "version": "1.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/he": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-lazy": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/is-callable": { + "version": "1.2.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.9.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream-ended": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/is-string": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/jest-docblock": { + "version": "21.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/jju": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/jose": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@panva/asn1.js": "^1.0.0" + }, + "engines": { + "node": ">=10.13.0 < 13 || >=13.7.0" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "16.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "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.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/@tootallnate/once": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/jsdom/node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/http-proxy-agent": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jsdom/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "6.1.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=10.4" + } + }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "8.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/jsonc-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", + "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "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" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "5.7.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/just-extend": { + "version": "4.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jwa": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwk-to-pem": { + "version": "2.0.5", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "asn1.js": "^5.3.0", + "elliptic": "^6.5.4", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwks-rsa": { + "version": "2.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "^4.17.13", + "@types/jsonwebtoken": "^8.5.8", + "debug": "^4.3.4", + "jose": "^2.0.5", + "limiter": "^1.1.5", + "lru-memoizer": "^2.1.4" + }, + "engines": { + "node": ">=10 < 13 || >=14" + } + }, + "node_modules/jwks-rsa/node_modules/@types/express": { + "version": "4.17.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/jwks-rsa/node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/jwks-rsa/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/jws": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/limiter": { + "version": "1.1.5", + "dev": true + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lolex": { + "version": "4.2.0", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/long": { + "version": "4.0.0", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, + "node_modules/loupe": { + "version": "2.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.0" + } + }, + "node_modules/lru-cache": { + "version": "4.1.5", + "dev": true, + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/lru-memoizer": { + "version": "2.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "~4.0.0" + } + }, + "node_modules/lru-memoizer/node_modules/lru-cache": { + "version": "4.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.1", + "yallist": "^2.0.0" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "dev": true, + "license": "MIT" + }, + "node_modules/make-dir": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "dev": true, + "license": "ISC" + }, + "node_modules/marked": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.18.tgz", + "integrity": "sha512-wbLDJ7Zh0sqA0Vdg6aqlbT+yPxqLblpAZh1mK2+AO2twQkPywvvqQNfEPVwSSRjZ7dZcdeVBIAgiO7MMp3Dszw==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "dev": true, + "license": "MIT" + }, + "node_modules/mkdirp": { + "version": "0.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mocha": { + "version": "6.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "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" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/mocha/node_modules/ansi-regex": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/mocha/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/mocha/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/debug": { + "version": "3.2.6", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/mocha/node_modules/emoji-regex": { + "version": "7.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "3.13.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/string-width": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/y18n": { + "version": "4.0.3", + "dev": true, + "license": "ISC" + }, + "node_modules/mocha/node_modules/yargs": { + "version": "13.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "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" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "13.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/mock-require": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "get-caller-file": "^1.0.2", + "normalize-path": "^2.1.1" + }, + "engines": { + "node": ">=4.3.0" + } + }, + "node_modules/mock-require/node_modules/get-caller-file": { + "version": "1.0.3", + "dev": true, + "license": "ISC" + }, + "node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nise": { + "version": "1.5.3", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@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" + } + }, + "node_modules/nise/node_modules/lolex": { + "version": "5.1.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/nise/node_modules/path-to-regexp": { + "version": "1.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/nock": { + "version": "10.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "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" + }, + "engines": { + "node": ">= 6.0" + } + }, + "node_modules/nock/node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nock/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/nock/node_modules/semver": { + "version": "5.7.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/node-environment-flags": { + "version": "1.0.5", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + } + }, + "node_modules/node-environment-flags/node_modules/semver": { + "version": "5.7.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "dev": true, + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-version": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/normalize-path": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nwsapi": { + "version": "2.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.12.0", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "license": "MIT" + }, + "node_modules/pathval": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/portfinder": { + "version": "1.0.28", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/portfinder/node_modules/mkdirp": { + "version": "0.5.6", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/portfinder/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "1.19.1", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/promise-polyfill": { + "version": "6.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/propagate": { + "version": "1.0.0", + "dev": true, + "engines": [ + "node >= 0.8.1" + ], + "license": "MIT" + }, + "node_modules/proto3-json-serializer": { + "version": "0.1.9", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "protobufjs": "^6.11.2" + } + }, + "node_modules/protobufjs": { + "version": "6.11.3", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "@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" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, + "node_modules/protobufjs/node_modules/@types/node": { + "version": "18.0.4", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/psl": { + "version": "1.8.0", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "duplexify": "^4.1.1", + "inherits": "^2.0.3", + "pump": "^3.0.0" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.10.3", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/resolve": { + "version": "1.22.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/retry-request": { + "version": "4.2.2", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "^4.1.1", + "extend": "^3.0.2" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/retry-request/node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/retry-request/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/saxes": { + "version": "5.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.3.7", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/send": { + "version": "0.18.0", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "license": "MIT", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "license": "ISC" + }, + "node_modules/shiki": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", + "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-oniguruma": "^1.6.1", + "vscode-textmate": "5.2.0" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/sinon": { + "version": "7.5.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@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" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/statuses": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-events": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-argv": { + "version": "0.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stubs": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/supports-color": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/teeny-request": { + "version": "7.2.0", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "stream-events": "^1.0.5", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/text-decoding": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/timsort": { + "version": "0.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/ts-node": { + "version": "10.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.0", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/acorn-walk": { + "version": "8.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "dev": true, + "license": "0BSD" + }, + "node_modules/tslint": { + "version": "5.20.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@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" + }, + "bin": { + "tslint": "bin/tslint" + }, + "engines": { + "node": ">=4.8.0" + }, + "peerDependencies": { + "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev" + } + }, + "node_modules/tslint-config-prettier": { + "version": "1.18.0", + "dev": true, + "license": "MIT", + "bin": { + "tslint-config-prettier-check": "bin/check.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/tslint-no-unused-expression-chai": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "tsutils": "^3.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "tslint": ">=5.1.0" + } + }, + "node_modules/tslint-no-unused-expression-chai/node_modules/tslib": { + "version": "1.14.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/tslint-no-unused-expression-chai/node_modules/tsutils": { + "version": "3.21.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tslint-plugin-prettier": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-plugin-prettier": "^2.2.0", + "lines-and-columns": "^1.1.6", + "tslib": "^1.7.1" + }, + "engines": { + "node": ">= 4" + }, + "peerDependencies": { + "prettier": "^1.9.0 || ^2.0.0", + "tslint": "^5.0.0 || ^6.0.0" + } + }, + "node_modules/tslint-plugin-prettier/node_modules/tslib": { + "version": "1.14.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/tslint/node_modules/diff": { + "version": "4.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslint/node_modules/semver": { + "version": "5.7.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/tslint/node_modules/tslib": { + "version": "1.14.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsutils": { + "version": "2.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, + "peerDependencies": { + "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typedoc": { + "version": "0.23.7", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.7.tgz", + "integrity": "sha512-Vl/Yh4KYBaxQQOYImBgLQDX61hfaA7XaP/DZVd/w7rQvaqLEsdQH6gEMK8CMjyHo0bSzVnNYwxtD1KPSENoWug==", + "dev": true, + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.0.16", + "minimatch": "^5.1.0", + "shiki": "^0.10.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 14.14" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/typescript": { + "version": "4.6.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/validator": { + "version": "8.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vscode-oniguruma": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", + "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", + "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "dev": true + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/wide-align": { + "version": "1.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/wide-align/node_modules/ansi-regex": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/strip-ansi": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.5.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "2.1.2", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "15.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "dev": true, + "license": "ISC", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "1.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "flat": "^4.1.0", + "lodash": "^4.17.15", + "yargs": "^13.3.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs-unparser/node_modules/ansi-regex": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs-unparser/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs-unparser/node_modules/cliui": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/yargs-unparser/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/yargs-unparser/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs-unparser/node_modules/emoji-regex": { + "version": "7.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs-unparser/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs-unparser/node_modules/string-width": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs-unparser/node_modules/strip-ansi": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs-unparser/node_modules/wrap-ansi": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs-unparser/node_modules/y18n": { + "version": "4.0.3", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs-unparser/node_modules/yargs": { + "version": "13.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "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" + } + }, + "node_modules/yargs-unparser/node_modules/yargs-parser": { + "version": "13.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/yargs/node_modules/cliui": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/yargs/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/wrap-ansi": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/y18n": { + "version": "4.0.3", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "18.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/z-schema": { + "version": "3.18.4", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.get": "^4.0.0", + "lodash.isequal": "^4.0.0", + "validator": "^8.0.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "optionalDependencies": { + "commander": "^2.7.1" + } + } + }, "dependencies": { "@babel/code-frame": { "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", "dev": true, "requires": { "@babel/highlight": "^7.16.7" @@ -15,14 +5781,10 @@ }, "@babel/helper-validator-identifier": { "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", "dev": true }, "@babel/highlight": { "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", - "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -32,23 +5794,24 @@ }, "@cspotcode/source-map-consumer": { "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", "dev": true }, "@cspotcode/source-map-support": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", "dev": true, "requires": { "@cspotcode/source-map-consumer": "0.8.0" } }, + "@fastify/busboy": { + "version": "1.1.0", + "dev": true, + "requires": { + "text-decoding": "^1.0.0" + } + }, "@firebase/api-documenter": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@firebase/api-documenter/-/api-documenter-0.1.2.tgz", - "integrity": "sha512-aDofRZebqbMzrbo5WAi9f21qUTzhIub7yOszirik3AwujqOzcUr1F7lIFrI41686JD1Zw56lLL/B5EWZTwvVjA==", "dev": true, "requires": { "@microsoft/tsdoc": "0.12.24", @@ -63,14 +5826,10 @@ "dependencies": { "argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "js-yaml": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", - "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -78,8 +5837,6 @@ }, "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" @@ -89,77 +5846,62 @@ }, "@firebase/app-types": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.7.0.tgz", - "integrity": "sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg==", "dev": true }, "@firebase/auth-interop-types": { "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.6.tgz", - "integrity": "sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g==", - "dev": true + "dev": true, + "requires": {} }, "@firebase/component": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.13.tgz", - "integrity": "sha512-hxhJtpD8Ppf/VU2Rlos6KFCEV77TGIGD5bJlkPK1+B/WUe0mC6dTjW7KhZtXTc+qRBp9nFHWcsIORnT8liHP9w==", + "version": "0.5.17", "dev": true, "requires": { - "@firebase/util": "1.5.2", + "@firebase/util": "1.6.3", "tslib": "^2.1.0" } }, "@firebase/database": { - "version": "0.12.8", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.12.8.tgz", - "integrity": "sha512-JBQVfFLzfhxlQbl4OU6ov9fdsddkytBQdtSSR49cz48homj38ccltAhK6seum+BI7f28cV2LFHF9672lcN+qxA==", + "version": "0.13.3", "dev": true, "requires": { "@firebase/auth-interop-types": "0.1.6", - "@firebase/component": "0.5.13", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", + "@firebase/component": "0.5.17", + "@firebase/logger": "0.3.3", + "@firebase/util": "1.6.3", "faye-websocket": "0.11.4", "tslib": "^2.1.0" } }, "@firebase/database-compat": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.1.8.tgz", - "integrity": "sha512-dhXr5CSieBuKNdU96HgeewMQCT9EgOIkfF1GNy+iRrdl7BWLxmlKuvLfK319rmIytSs/vnCzcD9uqyxTeU/A3A==", + "version": "0.2.3", "dev": true, "requires": { - "@firebase/component": "0.5.13", - "@firebase/database": "0.12.8", - "@firebase/database-types": "0.9.7", - "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", + "@firebase/component": "0.5.17", + "@firebase/database": "0.13.3", + "@firebase/database-types": "0.9.11", + "@firebase/logger": "0.3.3", + "@firebase/util": "1.6.3", "tslib": "^2.1.0" } }, "@firebase/database-types": { - "version": "0.9.7", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.9.7.tgz", - "integrity": "sha512-EFhgL89Fz6DY3kkB8TzdHvdu8XaqqvzcF2DLVOXEnQ3Ms7L755p5EO42LfxXoJqb9jKFvgLpFmKicyJG25WFWw==", + "version": "0.9.11", "dev": true, "requires": { "@firebase/app-types": "0.7.0", - "@firebase/util": "1.5.2" + "@firebase/util": "1.6.3" } }, "@firebase/logger": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.3.2.tgz", - "integrity": "sha512-lzLrcJp9QBWpo40OcOM9B8QEtBw2Fk1zOZQdvv+rWS6gKmhQBCEMc4SMABQfWdjsylBcDfniD1Q+fUX1dcBTXA==", + "version": "0.3.3", "dev": true, "requires": { "tslib": "^2.1.0" } }, "@firebase/util": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.5.2.tgz", - "integrity": "sha512-YvBH2UxFcdWG2HdFnhxZptPl2eVFlpOyTH66iDo13JPEYraWzWToZ5AMTtkyRHVmu7sssUpQlU9igy1KET7TOw==", + "version": "1.6.3", "dev": true, "requires": { "tslib": "^2.1.0" @@ -167,8 +5909,6 @@ }, "@google-cloud/firestore": { "version": "4.15.1", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.15.1.tgz", - "integrity": "sha512-2PWsCkEF1W02QbghSeRsNdYKN1qavrHBP3m72gPDMHQSYrGULOaTi7fSJquQmAtc4iPVB2/x6h80rdLHTATQtA==", "dev": true, "optional": true, "requires": { @@ -180,8 +5920,6 @@ }, "@google-cloud/paginator": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.7.tgz", - "integrity": "sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ==", "dev": true, "optional": true, "requires": { @@ -191,22 +5929,16 @@ }, "@google-cloud/projectify": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.1.1.tgz", - "integrity": "sha512-+rssMZHnlh0twl122gXY4/aCrk0G1acBqkHFfYddtsqpYXGxA29nj9V5V9SfC+GyOG00l650f6lG9KL+EpFEWQ==", "dev": true, "optional": true }, "@google-cloud/promisify": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.4.tgz", - "integrity": "sha512-j8yRSSqswWi1QqUGKVEKOG03Q7qOoZP6/h2zN2YO+F5h2+DHU0bSrHCK9Y7lo2DI9fBd8qGAw795sf+3Jva4yA==", "dev": true, "optional": true }, "@google-cloud/storage": { - "version": "5.19.4", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.19.4.tgz", - "integrity": "sha512-Jz7ugcPHhsEmMVvIxM9uoBsdEbKIYwDkh3u07tifsIymEWs47F4/D6+/Tv/W7kLhznqjyOjVJ/0frtBeIC0lJA==", + "version": "5.20.5", "dev": true, "optional": true, "requires": { @@ -218,12 +5950,10 @@ "async-retry": "^1.3.3", "compressible": "^2.0.12", "configstore": "^5.0.0", - "date-and-time": "^2.0.0", "duplexify": "^4.0.0", "ent": "^2.2.0", "extend": "^3.0.2", "gaxios": "^4.0.0", - "get-stream": "^6.0.0", "google-auth-library": "^7.14.1", "hash-stream-validation": "^0.2.2", "mime": "^3.0.0", @@ -231,16 +5961,14 @@ "p-limit": "^3.0.1", "pumpify": "^2.0.0", "retry-request": "^4.2.2", - "snakeize": "^0.1.0", "stream-events": "^1.0.4", "teeny-request": "^7.1.3", + "uuid": "^8.0.0", "xdg-basedir": "^4.0.0" }, "dependencies": { "mime": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", "dev": true, "optional": true } @@ -248,8 +5976,6 @@ }, "@grpc/grpc-js": { "version": "1.6.7", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.7.tgz", - "integrity": "sha512-eBM03pu9hd3VqDQG+kHahiG1x80RGkkqqRb1Pchcwqej/KkAH95gAvKs6laqaHCycYaPK+TKuNQnOz9UXYA8qw==", "dev": true, "optional": true, "requires": { @@ -258,32 +5984,26 @@ }, "dependencies": { "@types/node": { - "version": "17.0.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.29.tgz", - "integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==", + "version": "18.0.4", "dev": true, "optional": true } } }, "@grpc/proto-loader": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.9.tgz", - "integrity": "sha512-UlcCS8VbsU9d3XTXGiEVFonN7hXk+oMXZtoHHG2oSA1/GcDP1q6OUgs20PzHDGizzyi8ufGSUDlk3O2NyY7leg==", + "version": "0.6.13", "dev": true, "optional": true, "requires": { "@types/long": "^4.0.1", "lodash.camelcase": "^4.3.0", "long": "^4.0.0", - "protobufjs": "^6.10.0", + "protobufjs": "^6.11.3", "yargs": "^16.2.0" }, "dependencies": { "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": { @@ -300,8 +6020,6 @@ }, "@microsoft/api-documenter": { "version": "7.17.11", - "resolved": "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.17.11.tgz", - "integrity": "sha512-VdCu4eG5RISGEiG/hFO3jaN3gEgMZVXZRFpch/PQzrLO3Vh2QhZTz2LsZ2SYyb7UwBZCK9Z76ULv5cGoflylNA==", "dev": true, "requires": { "@microsoft/api-extractor-model": "7.17.2", @@ -315,14 +6033,10 @@ "dependencies": { "@microsoft/tsdoc": { "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz", - "integrity": "sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw==", "dev": true }, "@rushstack/node-core-library": { "version": "3.45.4", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.4.tgz", - "integrity": "sha512-FMoEQWjK7nWAO2uFgV1eVpVhY9ZDGOdIIomi9zTej64cKJ+8/Nvu+ny0xKaUDEjw/ALftN2D2ml7L0RDpW/Z9g==", "dev": true, "requires": { "@types/node": "12.20.24", @@ -338,8 +6052,6 @@ }, "@rushstack/ts-command-line": { "version": "4.10.10", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.10.tgz", - "integrity": "sha512-F+MH7InPDXqX40qvvcEsnvPpmg566SBpfFqj2fcCh8RjM6AyOoWlXc8zx7giBD3ZN85NVAEjZAgrcLU0z+R2yg==", "dev": true, "requires": { "@types/argparse": "1.0.38", @@ -350,14 +6062,10 @@ }, "@types/node": { "version": "12.20.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.24.tgz", - "integrity": "sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ==", "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", @@ -366,8 +6074,6 @@ }, "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" @@ -375,14 +6081,10 @@ }, "validator": { "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", "dev": true }, "z-schema": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.3.tgz", - "integrity": "sha512-sGvEcBOTNum68x9jCpCVGPFJ6mWnkD0YxOcddDlJHRx3tKdB2q8pCHExMVZo/AV/6geuVJXG7hljDaWG8+5GDw==", "dev": true, "requires": { "commander": "^2.20.3", @@ -395,8 +6097,6 @@ }, "@microsoft/api-extractor": { "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.23.0.tgz", - "integrity": "sha512-fbdX05RVE1EMA7nvyRHuS9nx1pryhjgURDx6pQlE/9yOXQ5PO7MpYdfWGaRsQwyYuU3+tPxgro819c0R3AK6KA==", "dev": true, "requires": { "@microsoft/api-extractor-model": "7.17.2", @@ -415,14 +6115,10 @@ "dependencies": { "@microsoft/tsdoc": { "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz", - "integrity": "sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw==", "dev": true }, "@rushstack/node-core-library": { "version": "3.45.4", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.4.tgz", - "integrity": "sha512-FMoEQWjK7nWAO2uFgV1eVpVhY9ZDGOdIIomi9zTej64cKJ+8/Nvu+ny0xKaUDEjw/ALftN2D2ml7L0RDpW/Z9g==", "dev": true, "requires": { "@types/node": "12.20.24", @@ -438,8 +6134,6 @@ }, "@rushstack/ts-command-line": { "version": "4.10.10", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.10.tgz", - "integrity": "sha512-F+MH7InPDXqX40qvvcEsnvPpmg566SBpfFqj2fcCh8RjM6AyOoWlXc8zx7giBD3ZN85NVAEjZAgrcLU0z+R2yg==", "dev": true, "requires": { "@types/argparse": "1.0.38", @@ -450,14 +6144,10 @@ }, "@types/node": { "version": "12.20.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.24.tgz", - "integrity": "sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ==", "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" @@ -465,14 +6155,10 @@ }, "validator": { "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", "dev": true }, "z-schema": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.3.tgz", - "integrity": "sha512-sGvEcBOTNum68x9jCpCVGPFJ6mWnkD0YxOcddDlJHRx3tKdB2q8pCHExMVZo/AV/6geuVJXG7hljDaWG8+5GDw==", "dev": true, "requires": { "commander": "^2.20.3", @@ -485,8 +6171,6 @@ }, "@microsoft/api-extractor-model": { "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.17.2.tgz", - "integrity": "sha512-fYfCeBeLm7jnZligC64qHiH4/vzswFLDfyPpX+uKO36OI2kIeMHrYG0zaezmuinKvE4vg1dAz38zZeDbPvBKGg==", "dev": true, "requires": { "@microsoft/tsdoc": "0.14.1", @@ -496,14 +6180,10 @@ "dependencies": { "@microsoft/tsdoc": { "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz", - "integrity": "sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw==", "dev": true }, "@rushstack/node-core-library": { "version": "3.45.4", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.4.tgz", - "integrity": "sha512-FMoEQWjK7nWAO2uFgV1eVpVhY9ZDGOdIIomi9zTej64cKJ+8/Nvu+ny0xKaUDEjw/ALftN2D2ml7L0RDpW/Z9g==", "dev": true, "requires": { "@types/node": "12.20.24", @@ -519,14 +6199,10 @@ }, "@types/node": { "version": "12.20.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.24.tgz", - "integrity": "sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ==", "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" @@ -534,14 +6210,10 @@ }, "validator": { "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", "dev": true }, "z-schema": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.3.tgz", - "integrity": "sha512-sGvEcBOTNum68x9jCpCVGPFJ6mWnkD0YxOcddDlJHRx3tKdB2q8pCHExMVZo/AV/6geuVJXG7hljDaWG8+5GDw==", "dev": true, "requires": { "commander": "^2.20.3", @@ -554,14 +6226,10 @@ }, "@microsoft/tsdoc": { "version": "0.12.24", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.24.tgz", - "integrity": "sha512-Mfmij13RUTmHEMi9vRUhMXD7rnGR2VvxeNYtaGtaJ4redwwjT4UXYJ+nzmVJF7hhd4pn/Fx5sncDKxMVFJSWPg==", "dev": true }, "@microsoft/tsdoc-config": { "version": "0.16.1", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.1.tgz", - "integrity": "sha512-2RqkwiD4uN6MLnHFljqBlZIXlt/SaUT6cuogU1w2ARw4nKuuppSmR0+s+NC+7kXBQykd9zzu0P4HtBpZT5zBpQ==", "dev": true, "requires": { "@microsoft/tsdoc": "0.14.1", @@ -572,14 +6240,10 @@ "dependencies": { "@microsoft/tsdoc": { "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz", - "integrity": "sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw==", "dev": true }, "resolve": { "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "dev": true, "requires": { "is-core-module": "^2.1.0", @@ -590,42 +6254,30 @@ }, "@panva/asn1.js": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", - "integrity": "sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==", "dev": true }, "@protobufjs/aspromise": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=", "dev": true, "optional": true }, "@protobufjs/base64": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", "dev": true, "optional": true }, "@protobufjs/codegen": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", "dev": true, "optional": true }, "@protobufjs/eventemitter": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=", "dev": true, "optional": true }, "@protobufjs/fetch": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", "dev": true, "optional": true, "requires": { @@ -635,43 +6287,31 @@ }, "@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 }, "@rushstack/node-core-library": { "version": "3.36.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.36.0.tgz", - "integrity": "sha512-bID2vzXpg8zweXdXgQkKToEdZwVrVCN9vE9viTRk58gqzYaTlz4fMId6V3ZfpXN6H0d319uGi2KDlm+lUEeqCg==", "dev": true, "requires": { "@types/node": "10.17.13", @@ -687,14 +6327,10 @@ "dependencies": { "@types/node": { "version": "10.17.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz", - "integrity": "sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==", "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" @@ -704,8 +6340,6 @@ }, "@rushstack/rig-package": { "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.11.tgz", - "integrity": "sha512-uI1/g5oQPtyrT9nStoyX/xgZSLa2b+srRFaDk3r1eqC7zA5th4/bvTGl2QfV3C9NcP+coSqmk5mFJkUfH6i3Lw==", "dev": true, "requires": { "resolve": "~1.17.0", @@ -714,8 +6348,6 @@ "dependencies": { "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" @@ -723,16 +6355,12 @@ }, "strip-json-comments": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true } } }, "@rushstack/ts-command-line": { "version": "4.7.8", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.7.8.tgz", - "integrity": "sha512-8ghIWhkph7NnLCMDJtthpsb7TMOsVGXVDvmxjE/CeklTqjbbUFBjGXizJfpbEkRQTELuZQ2+vGn7sGwIWKN2uA==", "dev": true, "requires": { "@types/argparse": "1.0.38", @@ -743,8 +6371,6 @@ }, "@sinonjs/commons": { "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", "dev": true, "requires": { "type-detect": "4.0.8" @@ -752,8 +6378,6 @@ }, "@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", @@ -762,8 +6386,6 @@ }, "@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", @@ -773,73 +6395,51 @@ }, "@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": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true, "optional": true }, "@tsconfig/node10": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", "dev": true }, "@tsconfig/node12": { "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", "dev": true }, "@tsconfig/node14": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", "dev": true }, "@tsconfig/node16": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "dev": true }, "@types/argparse": { "version": "1.0.38", - "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", - "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", "dev": true }, "@types/body-parser": { "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", "requires": { "@types/connect": "*", "@types/node": "*" }, "dependencies": { "@types/node": { - "version": "17.0.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.29.tgz", - "integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==" + "version": "17.0.29" } } }, "@types/chai": { "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.1.tgz", - "integrity": "sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ==", "dev": true }, "@types/chai-as-promised": { "version": "7.1.5", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz", - "integrity": "sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ==", "dev": true, "requires": { "@types/chai": "*" @@ -847,48 +6447,28 @@ }, "@types/connect": { "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", "requires": { "@types/node": "*" }, "dependencies": { "@types/node": { - "version": "17.0.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.29.tgz", - "integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==" + "version": "17.0.29" } } }, "@types/cors": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", - "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + "version": "2.8.12" }, "@types/express": { "version": "4.17.3", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.3.tgz", - "integrity": "sha512-I8cGRJj3pyOLs/HndoP+25vOqhqWkAZsWMEmq1qXy/b/M3ppufecUwaK2/TVDVxcV61/iSdhykUjQQ2DLSrTdg==", "requires": { "@types/body-parser": "*", "@types/express-serve-static-core": "*", "@types/serve-static": "*" } }, - "@types/express-jwt": { - "version": "0.0.42", - "resolved": "https://registry.npmjs.org/@types/express-jwt/-/express-jwt-0.0.42.tgz", - "integrity": "sha512-WszgUddvM1t5dPpJ3LhWNH8kfNN8GPIBrAGxgIYXVCEGx6Bx4A036aAuf/r5WH9DIEdlmp7gHOYvSM6U87B0ag==", - "dev": true, - "requires": { - "@types/express": "*", - "@types/express-unless": "*" - } - }, "@types/express-serve-static-core": { "version": "4.17.28", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", - "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", "requires": { "@types/node": "*", "@types/qs": "*", @@ -896,25 +6476,12 @@ }, "dependencies": { "@types/node": { - "version": "17.0.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.29.tgz", - "integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==" + "version": "17.0.29" } } }, - "@types/express-unless": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.3.tgz", - "integrity": "sha512-TyPLQaF6w8UlWdv4gj8i46B+INBVzURBNRahCozCSXfsK2VTlL1wNyTlMKw817VHygBtlcl5jfnPadlydr06Yw==", - "dev": true, - "requires": { - "@types/express": "*" - } - }, "@types/jsonwebtoken": { "version": "8.5.8", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.8.tgz", - "integrity": "sha512-zm6xBQpFDIDM6o9r6HSgDeIcLy82TKWctCXEPbJJcXb5AKmi5BNNdLXneixK4lplX3PqIVcwLBCGE/kAGnlD4A==", "dev": true, "requires": { "@types/node": "*" @@ -922,32 +6489,22 @@ }, "@types/lodash": { "version": "4.14.182", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz", - "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==", "dev": true }, "@types/long": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", "dev": true, "optional": true }, "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + "version": "1.3.2" }, "@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.1", - "resolved": "https://registry.npmjs.org/@types/mock-require/-/mock-require-2.0.1.tgz", - "integrity": "sha512-O7U5DVGboY/Crueb5/huUCIRjKtRVRaLmRDbZJBlDQgJn966z3aiFDN+6AtYviu2ExwMkl34LjT/IiC0OPtKuQ==", "dev": true, "requires": { "@types/node": "*" @@ -955,8 +6512,6 @@ }, "@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": "*" @@ -964,61 +6519,43 @@ }, "@types/node": { "version": "8.10.66", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", - "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", "dev": true }, "@types/node-fetch": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-3.0.3.tgz", - "integrity": "sha512-HhggYPH5N+AQe/OmN6fmhKmRRt2XuNJow+R3pQwJxOOF9GuwM7O2mheyGeIrs5MOIeNjDEdgdoyHBOrFeJBR3g==", "dev": true, "requires": { "node-fetch": "*" } }, "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + "version": "6.9.7" }, "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + "version": "1.2.4" }, "@types/serve-static": { "version": "1.13.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", - "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", "requires": { "@types/mime": "^1", "@types/node": "*" }, "dependencies": { "@types/node": { - "version": "17.0.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.29.tgz", - "integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==" + "version": "17.0.29" } } }, "@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.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "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": { @@ -1027,8 +6564,6 @@ }, "accepts": { "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "requires": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -1036,14 +6571,10 @@ }, "acorn": { "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", "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", @@ -1052,22 +6583,16 @@ "dependencies": { "acorn": { "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true } } }, "acorn-walk": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", "dev": true }, "agent-base": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "requires": { "debug": "4" @@ -1075,8 +6600,6 @@ "dependencies": { "debug": { "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -1084,16 +6607,12 @@ }, "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 } } }, "ajv": { "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -1104,20 +6623,14 @@ }, "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": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": 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, "requires": { "color-convert": "^2.0.1" @@ -1125,14 +6638,10 @@ }, "any-promise": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", "dev": true }, "api-extractor-model-me": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/api-extractor-model-me/-/api-extractor-model-me-0.1.1.tgz", - "integrity": "sha512-Ez801ZMADfkseOWNRFquvyQYDm3D9McpxfkKMWL6JFCGcpub0miJ+TFNphIR1nSZbrsxz3kIeOovNMY4VlL6Bw==", "dev": true, "requires": { "@microsoft/tsdoc": "0.12.24", @@ -1141,41 +6650,29 @@ }, "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": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { "sprintf-js": "~1.0.2" } }, "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "version": "1.1.1" }, "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.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", @@ -1186,14 +6683,10 @@ }, "assertion-error": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, "async": { "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, "requires": { "lodash": "^4.17.14" @@ -1201,8 +6694,6 @@ }, "async-retry": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", - "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "dev": true, "optional": true, "requires": { @@ -1211,40 +6702,28 @@ }, "asynckit": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, "balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "base64-js": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, "optional": true }, "bignumber.js": { "version": "9.0.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", - "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==", "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.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", "requires": { "bytes": "3.1.2", "content-type": "~1.0.4", @@ -1262,8 +6741,6 @@ }, "brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -1272,43 +6749,29 @@ }, "brorand": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", "dev": true }, "browser-process-hrtime": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", "dev": true }, "browser-stdout": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "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 }, "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.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + "version": "3.1.2" }, "call-bind": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "requires": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -1316,14 +6779,10 @@ }, "camelcase": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "chai": { "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", "dev": true, "requires": { "assertion-error": "^1.1.0", @@ -1337,8 +6796,6 @@ }, "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" @@ -1346,8 +6803,6 @@ }, "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", @@ -1357,8 +6812,6 @@ "dependencies": { "ansi-styles": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -1366,8 +6819,6 @@ }, "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" @@ -1375,14 +6826,10 @@ }, "color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "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" @@ -1392,14 +6839,10 @@ }, "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", @@ -1409,8 +6852,6 @@ }, "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": { @@ -1421,8 +6862,6 @@ }, "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" @@ -1430,20 +6869,14 @@ }, "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 }, "colors": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", - "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", "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" @@ -1451,14 +6884,10 @@ }, "commander": { "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "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": { @@ -1467,14 +6896,10 @@ }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "configstore": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", "dev": true, "optional": true, "requires": { @@ -1488,31 +6913,21 @@ }, "content-disposition": { "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "requires": { "safe-buffer": "5.2.1" } }, "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "version": "1.0.4" }, "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + "version": "0.5.0" }, "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "version": "1.0.6" }, "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" @@ -1520,14 +6935,10 @@ }, "create-require": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, "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", @@ -1536,21 +6947,15 @@ }, "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" @@ -1558,16 +6963,12 @@ "dependencies": { "cssom": { "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", "dev": true } } }, "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", @@ -1577,8 +6978,6 @@ "dependencies": { "tr46": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", "dev": true, "requires": { "punycode": "^2.1.1" @@ -1586,14 +6985,10 @@ }, "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 }, "whatwg-url": { "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", "dev": true, "requires": { "lodash": "^4.7.0", @@ -1603,37 +6998,22 @@ } } }, - "date-and-time": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-2.3.1.tgz", - "integrity": "sha512-OaIRmSJXifwEN21rMVVDs0Kz8uhJ3wWPYd86atkRiqN54liaMQYEbbrgjZQea75YXOBWL4ZFb3rG/waenw1TEg==", - "dev": true, - "optional": true - }, "debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "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.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", "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" @@ -1641,8 +7021,6 @@ }, "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", @@ -1655,14 +7033,10 @@ }, "deep-is": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, "define-properties": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", "dev": true, "requires": { "has-property-descriptors": "^1.0.0", @@ -1671,39 +7045,20 @@ }, "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": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + "version": "2.0.0" }, "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "dicer": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.1.tgz", - "integrity": "sha512-ObioMtXnmjYs3aRtpIJt9rgQSPCIhKVkFPip+E9GUDyWl8N435znUxK/JfNwGZJ2wnn5JKQ7Ly3vOK5Q5dylGA==", - "dev": true, - "requires": { - "streamsearch": "^1.1.0" - } + "version": "1.2.0" }, "diff": { "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "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" @@ -1711,16 +7066,12 @@ "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": { @@ -1729,8 +7080,6 @@ }, "duplexify": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", - "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", "dev": true, "optional": true, "requires": { @@ -1742,22 +7091,16 @@ }, "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=" + "version": "1.1.1" }, "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", @@ -1771,19 +7114,13 @@ }, "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 }, "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "version": "1.0.2" }, "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": { @@ -1792,15 +7129,11 @@ }, "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.19.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", - "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -1827,8 +7160,6 @@ "dependencies": { "object.assign": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { "call-bind": "^1.0.0", @@ -1841,8 +7172,6 @@ }, "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", @@ -1852,26 +7181,18 @@ }, "escalade": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, "optional": true }, "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "version": "1.0.3" }, "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 }, "escodegen": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", "dev": true, "requires": { "esprima": "^4.0.1", @@ -1883,8 +7204,6 @@ }, "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", @@ -1893,38 +7212,26 @@ }, "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 }, "estraverse": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "esutils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "version": "1.8.1" }, "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.18.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.0.tgz", - "integrity": "sha512-EJEXxiTQJS3lIPrU1AE2vRuT7X7E+0KBbpm5GSoK524yl0K8X+er8zS2P14E64eqsVNoWbMCT7MpmQ+ErAhgRg==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -1961,46 +7268,32 @@ }, "extend": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true, "optional": 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==", + "version": "1.0.4", "dev": true, "optional": true }, "faye-websocket": { "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", "dev": true, "requires": { "websocket-driver": ">=0.5.1" @@ -2008,8 +7301,6 @@ }, "finalhandler": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -2022,42 +7313,35 @@ }, "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": "10.1.0", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-10.1.0.tgz", - "integrity": "sha512-4i4wu+EFgNfY4+D4DxXkZcmbD832ozUMNvHMkOFQrf8upyp51n6jrDJS+wLok9sd62yeqcImbnsLOympGlISPA==", + "version": "10.3.0", "dev": true, "requires": { - "@firebase/database-compat": "^0.1.1", - "@firebase/database-types": "^0.9.3", + "@fastify/busboy": "^1.1.0", + "@firebase/database-compat": "^0.2.0", + "@firebase/database-types": "^0.9.7", "@google-cloud/firestore": "^4.15.1", "@google-cloud/storage": "^5.18.3", "@types/node": ">=12.12.47", - "dicer": "^0.3.0", "jsonwebtoken": "^8.5.1", "jwks-rsa": "^2.0.2", - "node-forge": "^1.3.1" + "node-forge": "^1.3.1", + "uuid": "^8.3.2" }, "dependencies": { "@types/node": { - "version": "17.0.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.29.tgz", - "integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==", + "version": "18.0.4", "dev": true } } }, "flat": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", - "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", "dev": true, "requires": { "is-buffer": "~2.0.3" @@ -2065,8 +7349,6 @@ }, "form-data": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -2075,19 +7357,13 @@ } }, "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + "version": "0.2.0" }, "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "version": "0.5.2" }, "fs-extra": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -2097,32 +7373,22 @@ }, "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==" + "version": "1.1.1" }, "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 }, "functions-have-names": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true }, "gaxios": { "version": "4.3.3", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.3.3.tgz", - "integrity": "sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA==", "dev": true, "optional": true, "requires": { @@ -2135,8 +7401,6 @@ }, "gcp-metadata": { "version": "4.3.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.1.tgz", - "integrity": "sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==", "dev": true, "optional": true, "requires": { @@ -2146,37 +7410,22 @@ }, "get-caller-file": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "get-func-name": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, "get-intrinsic": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1" } }, - "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 - }, "get-symbol-description": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -2185,8 +7434,6 @@ }, "glob": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -2199,8 +7446,6 @@ }, "google-auth-library": { "version": "7.14.1", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.1.tgz", - "integrity": "sha512-5Rk7iLNDFhFeBYc3s8l1CqzbEBcdhwR193RlD4vSNFajIcINKI8W8P0JLmBpwymHqqWbX34pJDQu39cSy/6RsA==", "dev": true, "optional": true, "requires": { @@ -2217,8 +7462,6 @@ "dependencies": { "lru-cache": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "optional": true, "requires": { @@ -2227,22 +7470,18 @@ }, "yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, "optional": true } } }, "google-gax": { - "version": "2.30.2", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.30.2.tgz", - "integrity": "sha512-BCNCT26kb0iC52zj2SosyOZMhI5sVfXuul1h0Aw5uT9nGAbmS5eOvQ49ft53ft6XotDj11sUSDV6XESEiQqCqg==", + "version": "2.30.5", "dev": true, "optional": true, "requires": { "@grpc/grpc-js": "~1.6.0", - "@grpc/proto-loader": "^0.6.1", + "@grpc/proto-loader": "^0.6.12", "@types/long": "^4.0.0", "abort-controller": "^3.0.0", "duplexify": "^4.0.0", @@ -2252,14 +7491,12 @@ "node-fetch": "^2.6.1", "object-hash": "^3.0.0", "proto3-json-serializer": "^0.1.8", - "protobufjs": "6.11.2", + "protobufjs": "6.11.3", "retry-request": "^4.0.0" } }, "google-p12-pem": { "version": "3.1.4", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.1.4.tgz", - "integrity": "sha512-HHuHmkLgwjdmVRngf5+gSmpkyaRI6QmOg77J8tkNBHhNEI62sGHyw4/+UkgyZEI7h84NbWprXDJ+sa3xOYFvTg==", "dev": true, "optional": true, "requires": { @@ -2268,20 +7505,14 @@ }, "graceful-fs": { "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "growl": { "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, "gtoken": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.3.2.tgz", - "integrity": "sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ==", "dev": true, "optional": true, "requires": { @@ -2290,57 +7521,32 @@ "jws": "^4.0.0" } }, - "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "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" - } - }, "has": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "requires": { "function-bind": "^1.1.1" } }, "has-bigints": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "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 }, "has-property-descriptors": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", "dev": true, "requires": { "get-intrinsic": "^1.1.1" } }, "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + "version": "1.0.3" }, "has-tostringtag": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "dev": true, "requires": { "has-symbols": "^1.0.2" @@ -2348,15 +7554,11 @@ }, "hash-stream-validation": { "version": "0.2.4", - "resolved": "https://registry.npmjs.org/hash-stream-validation/-/hash-stream-validation-0.2.4.tgz", - "integrity": "sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ==", "dev": true, "optional": true }, "hash.js": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -2365,14 +7567,10 @@ }, "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 }, "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", @@ -2382,8 +7580,6 @@ }, "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" @@ -2391,8 +7587,6 @@ }, "http-errors": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "requires": { "depd": "2.0.0", "inherits": "2.0.4", @@ -2402,15 +7596,11 @@ } }, "http-parser-js": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", - "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", + "version": "0.5.8", "dev": true }, "http-proxy-agent": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, "optional": true, "requires": { @@ -2421,8 +7611,6 @@ "dependencies": { "debug": { "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "optional": true, "requires": { @@ -2431,8 +7619,6 @@ }, "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 } @@ -2440,8 +7626,6 @@ }, "https-proxy-agent": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "requires": { "agent-base": "6", @@ -2450,8 +7634,6 @@ "dependencies": { "debug": { "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -2459,37 +7641,27 @@ }, "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 } } }, "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" } }, "import-lazy": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", "dev": true }, "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", @@ -2497,14 +7669,10 @@ } }, "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "version": "2.0.4" }, "internal-slot": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", "dev": true, "requires": { "get-intrinsic": "^1.1.0", @@ -2513,14 +7681,10 @@ } }, "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + "version": "1.9.1" }, "is-arguments": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -2529,8 +7693,6 @@ }, "is-bigint": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, "requires": { "has-bigints": "^1.0.1" @@ -2538,8 +7700,6 @@ }, "is-boolean-object": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -2548,20 +7708,14 @@ }, "is-buffer": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", "dev": true }, "is-callable": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", "dev": true }, "is-core-module": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "dev": true, "requires": { "has": "^1.0.3" @@ -2569,8 +7723,6 @@ }, "is-date-object": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, "requires": { "has-tostringtag": "^1.0.0" @@ -2578,20 +7730,14 @@ }, "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 }, "is-negative-zero": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true }, "is-number-object": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, "requires": { "has-tostringtag": "^1.0.0" @@ -2599,21 +7745,15 @@ }, "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.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true }, "is-regex": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -2622,8 +7762,6 @@ }, "is-shared-array-buffer": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dev": true, "requires": { "call-bind": "^1.0.2" @@ -2631,22 +7769,16 @@ }, "is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "optional": true }, "is-stream-ended": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", - "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==", "dev": true, "optional": true }, "is-string": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, "requires": { "has-tostringtag": "^1.0.0" @@ -2654,8 +7786,6 @@ }, "is-symbol": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, "requires": { "has-symbols": "^1.0.2" @@ -2663,15 +7793,11 @@ }, "is-typedarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true, "optional": true }, "is-weakref": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, "requires": { "call-bind": "^1.0.2" @@ -2679,32 +7805,22 @@ }, "isarray": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", "dev": true }, "isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, "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 }, "jju": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", - "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=", "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" @@ -2712,14 +7828,10 @@ }, "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.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -2728,8 +7840,6 @@ }, "jsdom": { "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", "dev": true, "requires": { "abab": "^2.0.5", @@ -2763,14 +7873,10 @@ "dependencies": { "@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 }, "debug": { "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -2778,8 +7884,6 @@ }, "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, "requires": { "@tootallnate/once": "1", @@ -2789,14 +7893,10 @@ }, "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 }, "tr46": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", "dev": true, "requires": { "punycode": "^2.1.1" @@ -2804,14 +7904,10 @@ }, "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 }, "whatwg-url": { "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", "dev": true, "requires": { "lodash": "^4.7.0", @@ -2823,8 +7919,6 @@ }, "json-bigint": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", "dev": true, "optional": true, "requires": { @@ -2833,26 +7927,20 @@ }, "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 }, "jsonc-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", + "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", "dev": true }, "jsonfile": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, "requires": { "graceful-fs": "^4.1.6" @@ -2860,8 +7948,6 @@ }, "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", @@ -2878,8 +7964,6 @@ "dependencies": { "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", @@ -2889,8 +7973,6 @@ }, "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", @@ -2899,28 +7981,20 @@ }, "ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "semver": { "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } }, "just-extend": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", "dev": true }, "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": { @@ -2931,8 +8005,6 @@ }, "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", @@ -2941,22 +8013,29 @@ } }, "jwks-rsa": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.1.0.tgz", - "integrity": "sha512-GKOSDBWWBCiQTzawei6mEdRQvji5gecj8F9JwMt0ZOPnBPSmTjo5CKFvvbhE7jGPkU159Cpi0+OTLuABFcNOQQ==", + "version": "2.1.4", "dev": true, "requires": { - "@types/express-jwt": "0.0.42", + "@types/express": "^4.17.13", + "@types/jsonwebtoken": "^8.5.8", "debug": "^4.3.4", "jose": "^2.0.5", "limiter": "^1.1.5", "lru-memoizer": "^2.1.4" }, "dependencies": { + "@types/express": { + "version": "4.17.13", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, "debug": { "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -2964,16 +8043,12 @@ }, "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": "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": { @@ -2983,8 +8058,6 @@ }, "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", @@ -2993,20 +8066,14 @@ }, "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.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "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", @@ -3014,81 +8081,55 @@ } }, "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "version": "4.17.21" }, "lodash.camelcase": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", "dev": true, "optional": true }, "lodash.clonedeep": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, "lodash.get": { "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", "dev": true }, "lodash.includes": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=", "dev": true }, "lodash.isboolean": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=", "dev": true }, "lodash.isequal": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", "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 }, "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" @@ -3096,21 +8137,15 @@ }, "lolex": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz", - "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==", "dev": true }, "long": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", "dev": true, "optional": true }, "loupe": { "version": "2.3.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", - "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", "dev": true, "requires": { "get-func-name": "^2.0.0" @@ -3118,8 +8153,6 @@ }, "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", @@ -3128,8 +8161,6 @@ }, "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", @@ -3138,8 +8169,6 @@ "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", @@ -3150,14 +8179,10 @@ }, "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": { @@ -3166,8 +8191,6 @@ "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 } @@ -3175,65 +8198,45 @@ }, "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": "2.1.3", - "resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz", - "integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.18.tgz", + "integrity": "sha512-wbLDJ7Zh0sqA0Vdg6aqlbT+yPxqLblpAZh1mK2+AO2twQkPywvvqQNfEPVwSSRjZ7dZcdeVBIAgiO7MMp3Dszw==", "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=" + "version": "0.3.0" }, "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "version": "1.0.1" }, "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + "version": "1.1.2" }, "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + "version": "1.6.0" }, "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "version": "1.52.0" }, "mime-types": { "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { "mime-db": "1.52.0" } }, "minimalistic-assert": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "dev": true }, "minimalistic-crypto-utils": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", "dev": true }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -3241,14 +8244,10 @@ }, "minimist": { "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "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" @@ -3256,8 +8255,6 @@ }, "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", @@ -3287,14 +8284,10 @@ "dependencies": { "ansi-regex": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "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" @@ -3302,8 +8295,6 @@ }, "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", @@ -3313,8 +8304,6 @@ }, "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" @@ -3322,14 +8311,10 @@ }, "color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "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" @@ -3337,20 +8322,14 @@ }, "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 }, "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 }, "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", @@ -3359,14 +8338,10 @@ }, "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 }, "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", @@ -3376,8 +8351,6 @@ }, "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" @@ -3385,8 +8358,6 @@ }, "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", @@ -3396,14 +8367,10 @@ }, "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", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, "requires": { "cliui": "^5.0.0", @@ -3420,8 +8387,6 @@ }, "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", @@ -3432,8 +8397,6 @@ }, "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", @@ -3442,21 +8405,15 @@ "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=" + "version": "2.0.0" }, "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", @@ -3465,20 +8422,10 @@ } }, "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" - }, - "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 + "version": "0.6.3" }, "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", @@ -3490,8 +8437,6 @@ "dependencies": { "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" @@ -3499,8 +8444,6 @@ }, "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" @@ -3510,8 +8453,6 @@ }, "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", @@ -3527,8 +8468,6 @@ "dependencies": { "debug": { "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -3536,22 +8475,16 @@ }, "ms": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "semver": { "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "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", @@ -3560,36 +8493,27 @@ "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.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, "requires": { "whatwg-url": "^5.0.0" } }, "node-forge": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", "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" @@ -3597,31 +8521,21 @@ }, "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 }, "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "version": "4.1.1" }, "object-hash": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "dev": true, "optional": true }, "object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" + "version": "1.12.0" }, "object-is": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -3630,14 +8544,10 @@ }, "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.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", @@ -3648,8 +8558,6 @@ }, "object.getownpropertydescriptors": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz", - "integrity": "sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -3659,16 +8567,12 @@ }, "on-finished": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "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" @@ -3676,8 +8580,6 @@ }, "optionator": { "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", "dev": true, "requires": { "deep-is": "~0.1.3", @@ -3690,8 +8592,6 @@ }, "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": { @@ -3700,8 +8600,6 @@ }, "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" @@ -3709,8 +8607,6 @@ "dependencies": { "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" @@ -3720,54 +8616,36 @@ }, "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": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + "version": "1.3.3" }, "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.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "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=" + "version": "0.1.7" }, "pathval": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true }, "portfinder": { "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", "dev": true, "requires": { "async": "^2.6.2", @@ -3777,8 +8655,6 @@ "dependencies": { "debug": { "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { "ms": "^2.1.1" @@ -3786,8 +8662,6 @@ }, "mkdirp": { "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "requires": { "minimist": "^1.2.6" @@ -3795,46 +8669,28 @@ }, "ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true } } }, "prelude-ls": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, "prettier": { "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, "promise-polyfill": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz", - "integrity": "sha1-36lpQ+qcEh/KTem1hoyznTRy4Fc=", "dev": true }, "propagate": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-1.0.0.tgz", - "integrity": "sha1-AMLa7t2iDofjeCs0Stuhzd1q1wk=", "dev": true }, "proto3-json-serializer": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-0.1.8.tgz", - "integrity": "sha512-ACilkB6s1U1gWnl5jtICpnDai4VCxmI9GFxuEaYdxtDG2oVI3sVFIUsvUZcQbJgtPM6p+zqKbjTKQZp6Y4FpQw==", + "version": "0.1.9", "dev": true, "optional": true, "requires": { @@ -3842,9 +8698,7 @@ } }, "protobufjs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", - "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", + "version": "6.11.3", "dev": true, "optional": true, "requires": { @@ -3864,9 +8718,7 @@ }, "dependencies": { "@types/node": { - "version": "17.0.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.29.tgz", - "integrity": "sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA==", + "version": "18.0.4", "dev": true, "optional": true } @@ -3874,8 +8726,6 @@ }, "proxy-addr": { "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "requires": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -3883,20 +8733,14 @@ }, "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": { @@ -3906,8 +8750,6 @@ }, "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": { @@ -3918,27 +8760,19 @@ }, "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.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", "requires": { "side-channel": "^1.0.4" } }, "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==" + "version": "1.2.1" }, "raw-body": { "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "requires": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -3948,8 +8782,6 @@ }, "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": { @@ -3960,8 +8792,6 @@ }, "regexp.prototype.flags": { "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -3971,26 +8801,18 @@ }, "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 }, "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.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "dev": true, "requires": { "is-core-module": "^2.8.1", @@ -4000,15 +8822,11 @@ }, "retry": { "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true, "optional": true }, "retry-request": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.2.2.tgz", - "integrity": "sha512-xA93uxUD/rogV7BV59agW/JHPGXeREMWiZc9jhcwY4YdZ7QOtC7qbomYg0n4wyk2lJhggjvKvhNX8wln/Aldhg==", "dev": true, "optional": true, "requires": { @@ -4018,8 +8836,6 @@ "dependencies": { "debug": { "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "optional": true, "requires": { @@ -4028,27 +8844,19 @@ }, "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.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "version": "5.2.1" }, "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "version": "2.1.2" }, "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" @@ -4056,8 +8864,6 @@ }, "semver": { "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -4065,8 +8871,6 @@ "dependencies": { "lru-cache": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { "yallist": "^4.0.0" @@ -4074,16 +8878,12 @@ }, "yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } } }, "send": { "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "requires": { "debug": "2.6.9", "depd": "2.0.0", @@ -4101,16 +8901,12 @@ }, "dependencies": { "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "version": "2.1.3" } } }, "serve-static": { "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", @@ -4120,19 +8916,15 @@ }, "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.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "version": "1.2.0" }, "shiki": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.15.tgz", - "integrity": "sha512-/Y0z9IzhJ8nD9nbceORCqu6NgT9X6I8Fk8c3SICHI5NbZRLdZYFaB233gwct9sU0vvSypyaL/qaKvzyQGJBZSw==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", + "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", "dev": true, "requires": { "jsonc-parser": "^3.0.0", @@ -4142,8 +8934,6 @@ }, "side-channel": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", "requires": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -4152,15 +8942,11 @@ }, "signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "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", @@ -4174,8 +8960,6 @@ "dependencies": { "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" @@ -4183,34 +8967,19 @@ } } }, - "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 }, "sprintf-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + "version": "2.0.1" }, "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": { @@ -4219,27 +8988,23 @@ }, "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": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "dev": true + "string_decoder": { + "version": "1.3.0", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.2.0" + } }, "string-argv": { "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", "dev": true }, "string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", @@ -4249,8 +9014,6 @@ }, "string.prototype.trimend": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -4259,28 +9022,14 @@ }, "string.prototype.trimstart": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" } }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, "strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { "ansi-regex": "^5.0.1" @@ -4288,21 +9037,15 @@ }, "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 }, "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" @@ -4310,20 +9053,14 @@ }, "supports-preserve-symlinks-flag": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, "symbol-tree": { "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, "teeny-request": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.2.0.tgz", - "integrity": "sha512-SyY0pek1zWsi0LRVAALem+avzMLc33MKW/JLLakdP4s9+D7+jHcy5x6P+h94g2QNZsAqQNfX5lsbd3WSeJXrrw==", "dev": true, "optional": true, "requires": { @@ -4334,10 +9071,12 @@ "uuid": "^8.0.0" } }, + "text-decoding": { + "version": "1.0.0", + "dev": true + }, "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" @@ -4345,8 +9084,6 @@ }, "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" @@ -4354,19 +9091,13 @@ }, "timsort": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", "dev": true }, "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + "version": "1.0.1" }, "tough-cookie": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", "dev": true, "requires": { "psl": "^1.1.33", @@ -4376,13 +9107,10 @@ }, "tr46": { "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + "dev": true }, "ts-node": { "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", - "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.7.0", @@ -4402,28 +9130,20 @@ "dependencies": { "acorn-walk": { "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true }, "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": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "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", @@ -4443,34 +9163,24 @@ "dependencies": { "diff": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, "semver": { "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "tslib": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true } } }, "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" @@ -4478,14 +9188,10 @@ "dependencies": { "tslib": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, "tsutils": { "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, "requires": { "tslib": "^1.8.1" @@ -4495,8 +9201,6 @@ }, "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", @@ -4506,16 +9210,12 @@ "dependencies": { "tslib": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true } } }, "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" @@ -4523,16 +9223,12 @@ "dependencies": { "tslib": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "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" @@ -4540,14 +9236,10 @@ }, "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" @@ -4555,8 +9247,6 @@ }, "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": { @@ -4564,61 +9254,43 @@ } }, "typedoc": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.21.2.tgz", - "integrity": "sha512-SR1ByJB3USg+jxoxwzMRP07g/0f/cQUE5t7gOh1iTUyjTPyJohu9YSKRlK+MSXXqlhIq+m0jkEHEG5HoY7/Adg==", + "version": "0.23.7", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.7.tgz", + "integrity": "sha512-Vl/Yh4KYBaxQQOYImBgLQDX61hfaA7XaP/DZVd/w7rQvaqLEsdQH6gEMK8CMjyHo0bSzVnNYwxtD1KPSENoWug==", "dev": true, "requires": { - "glob": "^7.1.7", - "handlebars": "^4.7.7", - "lodash": "^4.17.21", "lunr": "^2.3.9", - "marked": "^2.1.1", - "minimatch": "^3.0.0", - "progress": "^2.0.3", - "shiki": "^0.9.3", - "typedoc-default-themes": "^0.12.10" + "marked": "^4.0.16", + "minimatch": "^5.1.0", + "shiki": "^0.10.1" }, "dependencies": { - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", "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" + "brace-expansion": "^2.0.1" } } } }, - "typedoc-default-themes": { - "version": "0.12.10", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", - "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", - "dev": true - }, "typescript": { "version": "4.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", - "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "dev": true }, - "uglify-js": { - "version": "3.15.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz", - "integrity": "sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA==", - "dev": true, - "optional": true - }, "unbox-primitive": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -4629,8 +9301,6 @@ }, "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": { @@ -4639,19 +9309,13 @@ }, "universalify": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "version": "1.0.0" }, "uri-js": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { "punycode": "^2.1.0" @@ -4659,39 +9323,26 @@ }, "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=" + "version": "1.0.1" }, "uuid": { "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "optional": true + "dev": true }, "v8-compile-cache-lib": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, "validator": { "version": "8.2.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-8.2.0.tgz", - "integrity": "sha512-Yw5wW34fSv5spzTXNkokD6S6/Oq92d8q/t14TqsS3fAiA1RYnxSFSIZ+CY3n6PGGRCq5HhJTSepQvFUS2QUDxA==", "dev": true }, "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "version": "1.1.2" }, "vscode-oniguruma": { "version": "1.6.2", @@ -4707,8 +9358,6 @@ }, "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" @@ -4716,8 +9365,6 @@ }, "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" @@ -4725,13 +9372,10 @@ }, "webidl-conversions": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + "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", @@ -4741,14 +9385,10 @@ }, "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" @@ -4756,14 +9396,11 @@ }, "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": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -4771,8 +9408,6 @@ }, "which": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -4780,8 +9415,6 @@ }, "which-boxed-primitive": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, "requires": { "is-bigint": "^1.0.1", @@ -4793,14 +9426,10 @@ }, "which-module": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, "wide-align": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "requires": { "string-width": "^1.0.2 || 2" @@ -4808,20 +9437,14 @@ "dependencies": { "ansi-regex": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "string-width": { "version": "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", @@ -4830,8 +9453,6 @@ }, "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" @@ -4841,20 +9462,10 @@ }, "word-wrap": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "dev": true }, "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": { @@ -4865,14 +9476,10 @@ }, "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": { @@ -4884,46 +9491,33 @@ }, "ws": { "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "dev": true + "dev": true, + "requires": {} }, "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": "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", - "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", @@ -4941,8 +9535,6 @@ "dependencies": { "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", @@ -4952,8 +9544,6 @@ }, "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", @@ -4962,8 +9552,6 @@ }, "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" @@ -4971,8 +9559,6 @@ }, "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" @@ -4980,8 +9566,6 @@ }, "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" @@ -4989,14 +9573,10 @@ }, "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 }, "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", @@ -5006,14 +9586,10 @@ }, "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", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -5024,15 +9600,11 @@ }, "yargs-parser": { "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "optional": true }, "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", @@ -5042,14 +9614,10 @@ "dependencies": { "ansi-regex": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "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" @@ -5057,8 +9625,6 @@ }, "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", @@ -5068,8 +9634,6 @@ }, "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" @@ -5077,26 +9641,18 @@ }, "color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, "emoji-regex": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "string-width": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { "emoji-regex": "^7.0.1", @@ -5106,8 +9662,6 @@ }, "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" @@ -5115,8 +9669,6 @@ }, "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", @@ -5126,14 +9678,10 @@ }, "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", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, "requires": { "cliui": "^5.0.0", @@ -5150,8 +9698,6 @@ }, "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", @@ -5162,21 +9708,15 @@ }, "yn": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true }, "yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "optional": true }, "z-schema": { "version": "3.18.4", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.18.4.tgz", - "integrity": "sha512-DUOKC/IhbkdLKKiV89gw9DUauTV8U/8yJl1sjf6MtDmzevLKOF2duNJ495S3MFVjqZarr+qNGCPbkg4mu4PpLw==", "dev": true, "requires": { "commander": "^2.7.1", diff --git a/package.json b/package.json index 0769610d7..13cf1bbf1 100644 --- a/package.json +++ b/package.json @@ -168,7 +168,7 @@ "docgen:v2:toc": "ts-node docgen/toc.ts --input docgen/v2/markdown --output docgen/v2/markdown/toc --path /docs/functions/beta/reference", "docgen:v2:gen": "api-documenter-fire markdown -i docgen/v2 -o docgen/v2/markdown && npm run docgen:v2:toc", "docgen:v2": "npm run build && npm run docgen:v2:extract && npm run docgen:v2:gen", - "build:pack": "rm -rf lib && npm install && tsc -p tsconfig.release.json && npm pack", + "build:pack": "rm -rf lib && npm install --production && tsc -p tsconfig.release.json && npm pack", "build:release": "npm ci --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", @@ -205,7 +205,7 @@ "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "child-process-promise": "^2.2.1", - "firebase-admin": "^10.1.0", + "firebase-admin": "^10.3.0", "js-yaml": "^3.13.1", "jsdom": "^16.2.1", "jsonwebtoken": "^8.5.1", @@ -224,7 +224,7 @@ "tslint-config-prettier": "^1.18.0", "tslint-no-unused-expression-chai": "^0.1.4", "tslint-plugin-prettier": "^2.0.1", - "typedoc": "0.21.2", + "typedoc": "0.23.7", "typescript": "^4.3.5", "yargs": "^15.3.1" }, diff --git a/spec/common/providers/https.spec.ts b/spec/common/providers/https.spec.ts index 8916a371a..a87aa844d 100644 --- a/spec/common/providers/https.spec.ts +++ b/spec/common/providers/https.spec.ts @@ -361,7 +361,7 @@ describe('onCallHandler', () => { body: { result: null }, }, }); - mock.done(); + expect(mock.isDone()).to.be.true; }); it('should reject bad AppCheck token', async () => { diff --git a/spec/fixtures/mockrequest.ts b/spec/fixtures/mockrequest.ts index 5accb339e..a1f55c8e1 100644 --- a/spec/fixtures/mockrequest.ts +++ b/spec/fixtures/mockrequest.ts @@ -111,7 +111,7 @@ export function mockFetchAppCheckPublicJwks(): nock.Scope { }; return nock('https://firebaseappcheck.googleapis.com:443') - .get('/v1beta/jwks') + .get('/v1/jwks') .reply(200, mockedResponse); } From 7d2c8dc5a45261f64d74cae94be9ce53410364a8 Mon Sep 17 00:00:00 2001 From: Victor Fan Date: Tue, 19 Jul 2022 02:32:26 -0700 Subject: [PATCH 127/370] Allow users to present String and Integer params in a ManifestSpec (#1166) --- .../fixtures/sources/commonjs-params/index.js | 21 +++++++++++++++++ .../sources/commonjs-params/package.json | 3 +++ spec/runtime/loader.spec.ts | 5 ++++ src/runtime/loader.ts | 8 ++++++- src/runtime/manifest.ts | 3 +++ src/v2/params/index.ts | 1 + src/v2/params/types.ts | 23 ++++++++----------- 7 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 spec/fixtures/sources/commonjs-params/index.js create mode 100644 spec/fixtures/sources/commonjs-params/package.json diff --git a/spec/fixtures/sources/commonjs-params/index.js b/spec/fixtures/sources/commonjs-params/index.js new file mode 100644 index 000000000..d038a2f77 --- /dev/null +++ b/spec/fixtures/sources/commonjs-params/index.js @@ -0,0 +1,21 @@ +const functions = require("../../../../src/index"); +const functionsv2 = require("../../../../src/v2/index"); +const { defineString } = require("../../../../src/v2/params"); + +defineString("FOO"); + +exports.v1http = functions.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +exports.v1callable = functions.https.onCall(() => { + return "PASS"; +}); + +exports.v2http = functionsv2.https.onRequest((req, resp) => { + resp.status(200).send("PASS"); +}); + +exports.v2callable = functionsv2.https.onCall(() => { + return "PASS"; +}); diff --git a/spec/fixtures/sources/commonjs-params/package.json b/spec/fixtures/sources/commonjs-params/package.json new file mode 100644 index 000000000..91f8c11da --- /dev/null +++ b/spec/fixtures/sources/commonjs-params/package.json @@ -0,0 +1,3 @@ +{ + "name": "commonjs-params" +} diff --git a/spec/runtime/loader.spec.ts b/spec/runtime/loader.spec.ts index 9994557c4..bb959a3cb 100644 --- a/spec/runtime/loader.spec.ts +++ b/spec/runtime/loader.spec.ts @@ -310,6 +310,11 @@ describe('loadStack', () => { }, }, }, + { + name: 'has params', + modulePath: './spec/fixtures/sources/commonjs-params', + expected: { ...expected, params: [{ name: 'FOO', type: 'string' }] }, + }, ]; for (const tc of testcases) { diff --git a/src/runtime/loader.ts b/src/runtime/loader.ts index de31eefd3..4dd4b41d2 100644 --- a/src/runtime/loader.ts +++ b/src/runtime/loader.ts @@ -28,6 +28,8 @@ import { ManifestStack, } from './manifest'; +import * as params from '../v2/params'; + /** * Dynamically load import function to prevent TypeScript from * transpiling into a require. @@ -112,9 +114,13 @@ export async function loadStack(functionsDir: string): Promise { extractStack(mod, endpoints, requiredAPIs); - return { + const stack: ManifestStack = { endpoints, specVersion: 'v1alpha1', requiredAPIs: mergeRequiredAPIs(requiredAPIs), }; + if (params.declaredParams.length > 0) { + stack.params = params.declaredParams.map((p) => p.toSpec()); + } + return stack; } diff --git a/src/runtime/manifest.ts b/src/runtime/manifest.ts index 562d1a52f..d63088303 100644 --- a/src/runtime/manifest.ts +++ b/src/runtime/manifest.ts @@ -20,6 +20,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +import { ParamSpec } from '../v2/params/types'; + /** * An definition of a function as appears in the Manifest. */ @@ -87,6 +89,7 @@ export interface ManifestRequiredAPI { */ export interface ManifestStack { specVersion: 'v1alpha1'; + params?: ParamSpec[]; requiredAPIs: ManifestRequiredAPI[]; endpoints: Record; } diff --git a/src/v2/params/index.ts b/src/v2/params/index.ts index bf45e8c45..3f8a080b6 100644 --- a/src/v2/params/index.ts +++ b/src/v2/params/index.ts @@ -24,6 +24,7 @@ * @hidden * @alpha */ + import { BooleanParam, FloatParam, diff --git a/src/v2/params/types.ts b/src/v2/params/types.ts index 9612dc8dc..fc898c15c 100644 --- a/src/v2/params/types.ts +++ b/src/v2/params/types.ts @@ -28,16 +28,13 @@ export interface ParamSpec { default?: T; label?: string; description?: string; - valueType?: ParamValueType; + type: ParamValueType; } -export type ParamOptions = Omit< - ParamSpec, - 'name' | 'valueType' ->; +export type ParamOptions = Omit, 'name' | 'type'>; export class Param { - static valueType: ParamValueType = 'string'; + static type: ParamValueType = 'string'; constructor(readonly name: string, readonly options: ParamOptions = {}) {} @@ -61,7 +58,7 @@ export class Param { const out: ParamSpec = { name: this.name, ...this.options, - valueType: (this.constructor as typeof Param).valueType, + type: (this.constructor as typeof Param).type, }; if (this.options.default && typeof this.options.default !== 'string') { out.default = (this.options.default as @@ -78,7 +75,7 @@ export class StringParam extends Param { } export class IntParam extends Param { - static valueType: ParamValueType = 'int'; + static type: ParamValueType = 'int'; get value(): number { const intVal = parseInt( @@ -97,7 +94,7 @@ export class IntParam extends Param { } export class FloatParam extends Param { - static valueType: ParamValueType = 'float'; + static type: ParamValueType = 'float'; get value(): number { const floatVal = parseFloat( @@ -115,7 +112,7 @@ export class FloatParam extends Param { } export class BooleanParam extends Param { - static valueType: ParamValueType = 'boolean'; + static type: ParamValueType = 'boolean'; get value(): boolean { const lowerVal = ( @@ -137,7 +134,7 @@ export class BooleanParam extends Param { } export class ListParam extends Param { - static valueType: ParamValueType = 'list'; + static type: ParamValueType = 'list'; get value(): string[] { return typeof this.rawValue === 'string' @@ -148,7 +145,7 @@ export class ListParam extends Param { toSpec(): ParamSpec { const out: ParamSpec = { name: this.name, - valueType: 'list', + type: 'list', ...this.options, }; if (this.options.default && this.options.default.length > 0) { @@ -160,7 +157,7 @@ export class ListParam extends Param { } export class JSONParam extends Param { - static valueType: ParamValueType = 'json'; + static type: ParamValueType = 'json'; get value(): T { if (this.rawValue) { From 724a8b0a79271dac957acb7e077d208f590676af Mon Sep 17 00:00:00 2001 From: Kai Bolay Date: Tue, 19 Jul 2022 12:20:04 -0400 Subject: [PATCH 128/370] Add support for App Distribution in-app Feedback (#1167) Add `InAppFeedbackPayload` and `onNewInAppFeedbackPublished()`. --- .../providers/alerts/appDistribution.spec.ts | 83 ++++++++++++++ src/v2/providers/alerts/alerts.ts | 1 + src/v2/providers/alerts/appDistribution.ts | 105 ++++++++++++++++++ 3 files changed, 189 insertions(+) diff --git a/spec/v2/providers/alerts/appDistribution.spec.ts b/spec/v2/providers/alerts/appDistribution.spec.ts index 5fb112daa..9b699bc62 100644 --- a/spec/v2/providers/alerts/appDistribution.spec.ts +++ b/spec/v2/providers/alerts/appDistribution.spec.ts @@ -97,6 +97,89 @@ describe('appDistribution', () => { }); }); + describe('onInAppfeedbackPublished', () => { + it('should create a function with alertType & appId', () => { + const func = appDistribution.onInAppFeedbackPublished(APPID, myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + ...APP_EVENT_FILTER, + alerttype: appDistribution.inAppFeedbackAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with opts', () => { + const func = appDistribution.onInAppFeedbackPublished( + { ...FULL_OPTIONS }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alerttype: appDistribution.inAppFeedbackAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with appid in opts', () => { + const func = appDistribution.onInAppFeedbackPublished( + { ...FULL_OPTIONS, appId: APPID }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + ...APP_EVENT_FILTER, + alerttype: appDistribution.inAppFeedbackAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function without opts or appId', () => { + const func = appDistribution.onInAppFeedbackPublished(myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alerttype: appDistribution.inAppFeedbackAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with a run method', () => { + const func = appDistribution.onInAppFeedbackPublished( + APPID, + (event) => event + ); + + const res = func.run('input' as any); + + expect(res).to.equal('input'); + }); + }); + describe('getOptsAndApp', () => { it('should parse a string', () => { const [opts, appId] = appDistribution.getOptsAndApp(APPID); diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index 4cb0ac2de..556b2b77b 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -68,6 +68,7 @@ export type AlertType = | 'billing.planUpdate' | 'billing.automatedPlanUpdate' | 'appDistribution.newTesterIosDevice' + | 'appDistribution.inAppFeedback' | string; /** diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index c3f058cff..ffac2d43d 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -45,6 +45,36 @@ export interface NewTesterDevicePayload { testerDeviceIdentifier: string; } +/** + * The internal payload object for receiving in-app feedback from a tester. + * Payload is wrapped inside a `FirebaseAlertData` object. + */ +export interface InAppFeedbackPayload { + ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.AppDistroInAppFeedbackPayload'; + /** Resource name. Format: `projects/{project_number}/apps/{app_id}/releases/{release_id}/feedbackReports/{feedback_id}` */ + feedbackReport: string; + /** Name of the tester */ + testerName?: string; + /** Email of the tester */ + testerEmail: string; + /** + * Display version of the release. For an Android release, the display version + * is the `versionName`. For an iOS release, the display version is the + * `CFBundleShortVersionString`. + */ + displayVersion: string; + /** + * Build version of the release. For an Android release, the build version + * is the `versionCode`. For an iOS release, the build version is the + * `CFBundleVersion`. + */ + buildVersion: string; + /** Text entered by the tester */ + text: string; + /** URIs to download screenshot(s) */ + screenshotUris?: string[]; +} + /** * A custom CloudEvent for Firebase Alerts (with custom extension attributes). * @typeParam T - the data type for app distribution alerts that is wrapped in a `FirebaseAlertData` object. @@ -59,6 +89,8 @@ export interface AppDistributionEvent /** @internal */ export const newTesterIosDeviceAlert = 'appDistribution.newTesterIosDevice'; +/** @internal */ +export const inAppFeedbackAlert = 'appDistribution.inAppFeedback'; /** * Configuration for app distribution functions. @@ -234,6 +266,79 @@ export function onNewTesterIosDevicePublished( return func; } +/** + * Declares a function that can handle receiving new in-app feedback from a tester. + * @param handler - Event handler which is run every time new feedback is received. + * @returns A function that you can export and deploy. + */ +export function onInAppFeedbackPublished( + handler: ( + event: AppDistributionEvent + ) => any | Promise +): CloudFunction>; + +/** + * Declares a function that can handle receiving new in-app feedback from a tester. + * @param appId - A specific application the handler will trigger on. + * @param handler - Event handler which is run every time new feedback is received. + * @returns A function that you can export and deploy. + */ +export function onInAppFeedbackPublished( + appId: string, + handler: ( + event: AppDistributionEvent + ) => any | Promise +): CloudFunction>; + +/** + * Declares a function that can handle receiving new in-app feedback from a tester. + * @param opts - Options that can be set on the function. + * @param handler - Event handler which is run every time new feedback is received. + * @returns A function that you can export and deploy. + */ +export function onInAppFeedbackPublished( + opts: AppDistributionOptions, + handler: ( + event: AppDistributionEvent + ) => any | Promise +): CloudFunction>; + +/** + * Declares a function that can handle receiving new in-app feedback from a tester. + * @param appIdOrOptsOrHandler - A specific application, options, or an event-handling function. + * @param handler - Event handler which is run every time new feedback is received. + * @returns A function that you can export and deploy. + */ +export function onInAppFeedbackPublished( + appIdOrOptsOrHandler: + | string + | AppDistributionOptions + | (( + event: AppDistributionEvent + ) => any | Promise), + handler?: ( + event: AppDistributionEvent + ) => any | Promise +): CloudFunction> { + if (typeof appIdOrOptsOrHandler === 'function') { + handler = appIdOrOptsOrHandler as ( + event: AppDistributionEvent + ) => any | Promise; + appIdOrOptsOrHandler = {}; + } + + const [opts, appId] = getOptsAndApp(appIdOrOptsOrHandler); + + const func = (raw: CloudEvent) => { + return handler(raw as AppDistributionEvent); + }; + + func.run = handler; + func.__endpoint = getEndpointAnnotation(opts, inAppFeedbackAlert, appId); + + return func; +} + /** * Helper function to parse the function opts and appId. * @internal From 526dcac9f98dff1e6e07d5a2ca25f893662681b8 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 19 Jul 2022 22:23:43 -0700 Subject: [PATCH 129/370] Modify docgen script for v1. (#1174) --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 13cf1bbf1..ae4d09adb 100644 --- a/package.json +++ b/package.json @@ -162,7 +162,8 @@ "scripts": { "apidocs": "node docgen/generate-docs.js", "docgen:v1:extract": "api-extractor run -c docgen/api-extractor.v1.json --local", - "docgen:v1:gen": "api-documenter-fire markdown -i docgen/v1 -o docgen/v1/markdown && api-documenter-fire toc -i docgen/v1 -o docgen/v1/markdown/toc -p /docs/reference/functions", + "docgen:v1:toc": "ts-node docgen/toc.ts --input docgen/v1/markdown --output docgen/v1/markdown/toc --path /docs/functions/reference", + "docgen:v1:gen": "api-documenter-fire markdown -i docgen/v1 -o docgen/v1/markdown && npm run docgen:v1:toc", "docgen:v1": "npm run build && npm run docgen:v1:extract && npm run docgen:v1:gen", "docgen:v2:extract": "api-extractor run -c docgen/api-extractor.v2.json --local", "docgen:v2:toc": "ts-node docgen/toc.ts --input docgen/v2/markdown --output docgen/v2/markdown/toc --path /docs/functions/beta/reference", From 733e90069d427b27cbcab15e2832a43ab29908eb Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 20 Jul 2022 13:54:41 -0700 Subject: [PATCH 130/370] Update api-documenter to the latest version supporting new project (#1175) header. --- package-lock.json | 200 +++++++++++++++++++++++++++++++++++++++------- package.json | 4 +- 2 files changed, 172 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index c778ba086..d739d07c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "firebase-functions": "lib/bin/firebase-functions.js" }, "devDependencies": { - "@firebase/api-documenter": "^0.1.2", + "@firebase/api-documenter": "^0.2.0", "@microsoft/api-documenter": "^7.13.45", "@microsoft/api-extractor": "^7.18.7", "@types/chai": "^4.1.7", @@ -130,32 +130,88 @@ } }, "node_modules/@firebase/api-documenter": { - "version": "0.1.2", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/api-documenter/-/api-documenter-0.2.0.tgz", + "integrity": "sha512-WQcOP5TvtRWMfGkpJpKpyVDjcB2UYCZWFmQm/nXUYUdI6PZ/Im1yb2YydgpnSlhrZxz6C1YkYFGLYCrltks1Yw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@microsoft/tsdoc": "0.12.24", - "@rushstack/node-core-library": "3.36.0", - "@rushstack/ts-command-line": "4.7.8", + "@rushstack/node-core-library": "3.45.5", + "@rushstack/ts-command-line": "4.11.0", "api-extractor-model-me": "0.1.1", - "colors": "~1.2.1", - "js-yaml": "4.0.0", - "resolve": "~1.17.0", + "colors": "~1.4.0", + "js-yaml": "4.1.0", + "resolve": "~1.22.0", "tslib": "^2.1.0" }, "bin": { "api-documenter-fire": "dist/start.js" } }, + "node_modules/@firebase/api-documenter/node_modules/@rushstack/node-core-library": { + "version": "3.45.5", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.5.tgz", + "integrity": "sha512-KbN7Hp9vH3bD3YJfv6RnVtzzTAwGYIBl7y2HQLY4WEQqRbvE3LgI78W9l9X+cTAXCX//p0EeoiUYNTFdqJrMZg==", + "dev": true, + "dependencies": { + "@types/node": "12.20.24", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "timsort": "~0.3.0", + "z-schema": "~5.0.2" + } + }, + "node_modules/@firebase/api-documenter/node_modules/@rushstack/node-core-library/node_modules/colors": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", + "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@firebase/api-documenter/node_modules/@rushstack/node-core-library/node_modules/resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@firebase/api-documenter/node_modules/@types/node": { + "version": "12.20.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.24.tgz", + "integrity": "sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ==", + "dev": true + }, "node_modules/@firebase/api-documenter/node_modules/argparse": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@firebase/api-documenter/node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true, - "license": "Python-2.0" + "engines": { + "node": ">=0.1.90" + } }, "node_modules/@firebase/api-documenter/node_modules/js-yaml": { - "version": "4.0.0", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -163,15 +219,33 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@firebase/api-documenter/node_modules/resolve": { - "version": "1.17.0", + "node_modules/@firebase/api-documenter/node_modules/validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/@firebase/api-documenter/node_modules/z-schema": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.3.tgz", + "integrity": "sha512-sGvEcBOTNum68x9jCpCVGPFJ6mWnkD0YxOcddDlJHRx3tKdB2q8pCHExMVZo/AV/6geuVJXG7hljDaWG8+5GDw==", "dev": true, - "license": "MIT", "dependencies": { - "path-parse": "^1.0.6" + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^2.20.3" } }, "node_modules/@firebase/app-types": { @@ -840,9 +914,10 @@ } }, "node_modules/@rushstack/ts-command-line": { - "version": "4.7.8", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.11.0.tgz", + "integrity": "sha512-ptG9L0mjvJ5QtK11GsAFY+jGfsnqHDS6CY6Yw1xT7a9bhjfNYnf6UPwjV+pF6UgiucfNcMDNW9lkDLxvZKKxMg==", "dev": true, - "license": "MIT", "dependencies": { "@types/argparse": "1.0.38", "argparse": "~1.0.9", @@ -5811,35 +5886,98 @@ } }, "@firebase/api-documenter": { - "version": "0.1.2", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/api-documenter/-/api-documenter-0.2.0.tgz", + "integrity": "sha512-WQcOP5TvtRWMfGkpJpKpyVDjcB2UYCZWFmQm/nXUYUdI6PZ/Im1yb2YydgpnSlhrZxz6C1YkYFGLYCrltks1Yw==", "dev": true, "requires": { "@microsoft/tsdoc": "0.12.24", - "@rushstack/node-core-library": "3.36.0", - "@rushstack/ts-command-line": "4.7.8", + "@rushstack/node-core-library": "3.45.5", + "@rushstack/ts-command-line": "4.11.0", "api-extractor-model-me": "0.1.1", - "colors": "~1.2.1", - "js-yaml": "4.0.0", - "resolve": "~1.17.0", + "colors": "~1.4.0", + "js-yaml": "4.1.0", + "resolve": "~1.22.0", "tslib": "^2.1.0" }, "dependencies": { + "@rushstack/node-core-library": { + "version": "3.45.5", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.5.tgz", + "integrity": "sha512-KbN7Hp9vH3bD3YJfv6RnVtzzTAwGYIBl7y2HQLY4WEQqRbvE3LgI78W9l9X+cTAXCX//p0EeoiUYNTFdqJrMZg==", + "dev": true, + "requires": { + "@types/node": "12.20.24", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "timsort": "~0.3.0", + "z-schema": "~5.0.2" + }, + "dependencies": { + "colors": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", + "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", + "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" + } + } + } + }, + "@types/node": { + "version": "12.20.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.24.tgz", + "integrity": "sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ==", + "dev": true + }, "argparse": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true }, "js-yaml": { - "version": "4.0.0", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { "argparse": "^2.0.1" } }, - "resolve": { - "version": "1.17.0", + "validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "dev": true + }, + "z-schema": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.3.tgz", + "integrity": "sha512-sGvEcBOTNum68x9jCpCVGPFJ6mWnkD0YxOcddDlJHRx3tKdB2q8pCHExMVZo/AV/6geuVJXG7hljDaWG8+5GDw==", "dev": true, "requires": { - "path-parse": "^1.0.6" + "commander": "^2.20.3", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" } } } @@ -6360,7 +6498,9 @@ } }, "@rushstack/ts-command-line": { - "version": "4.7.8", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.11.0.tgz", + "integrity": "sha512-ptG9L0mjvJ5QtK11GsAFY+jGfsnqHDS6CY6Yw1xT7a9bhjfNYnf6UPwjV+pF6UgiucfNcMDNW9lkDLxvZKKxMg==", "dev": true, "requires": { "@types/argparse": "1.0.38", diff --git a/package.json b/package.json index ae4d09adb..ca4b9ba36 100644 --- a/package.json +++ b/package.json @@ -163,7 +163,7 @@ "apidocs": "node docgen/generate-docs.js", "docgen:v1:extract": "api-extractor run -c docgen/api-extractor.v1.json --local", "docgen:v1:toc": "ts-node docgen/toc.ts --input docgen/v1/markdown --output docgen/v1/markdown/toc --path /docs/functions/reference", - "docgen:v1:gen": "api-documenter-fire markdown -i docgen/v1 -o docgen/v1/markdown && npm run docgen:v1:toc", + "docgen:v1:gen": "api-documenter-fire markdown -i docgen/v1 -o docgen/v1/markdown --project functions && npm run docgen:v1:toc", "docgen:v1": "npm run build && npm run docgen:v1:extract && npm run docgen:v1:gen", "docgen:v2:extract": "api-extractor run -c docgen/api-extractor.v2.json --local", "docgen:v2:toc": "ts-node docgen/toc.ts --input docgen/v2/markdown --output docgen/v2/markdown/toc --path /docs/functions/beta/reference", @@ -189,7 +189,7 @@ "node-fetch": "^2.6.7" }, "devDependencies": { - "@firebase/api-documenter": "^0.1.2", + "@firebase/api-documenter": "^0.2.0", "@microsoft/api-documenter": "^7.13.45", "@microsoft/api-extractor": "^7.18.7", "@types/chai": "^4.1.7", From 9b6da2d8d12c70914e5ca6258c50b56239e4ad88 Mon Sep 17 00:00:00 2001 From: egilmorez Date: Wed, 20 Jul 2022 15:03:57 -0700 Subject: [PATCH 131/370] Fixing path I should have noticed in review. (#1176) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ca4b9ba36..000d85096 100644 --- a/package.json +++ b/package.json @@ -162,7 +162,7 @@ "scripts": { "apidocs": "node docgen/generate-docs.js", "docgen:v1:extract": "api-extractor run -c docgen/api-extractor.v1.json --local", - "docgen:v1:toc": "ts-node docgen/toc.ts --input docgen/v1/markdown --output docgen/v1/markdown/toc --path /docs/functions/reference", + "docgen:v1:toc": "ts-node docgen/toc.ts --input docgen/v1/markdown --output docgen/v1/markdown/toc --path /docs/reference/functions", "docgen:v1:gen": "api-documenter-fire markdown -i docgen/v1 -o docgen/v1/markdown --project functions && npm run docgen:v1:toc", "docgen:v1": "npm run build && npm run docgen:v1:extract && npm run docgen:v1:gen", "docgen:v2:extract": "api-extractor run -c docgen/api-extractor.v2.json --local", From 1877e980d72d565807482a314c51b8651bf9a554 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 26 Jul 2022 14:59:20 -0700 Subject: [PATCH 132/370] Fix integration tests (#1179) One notable change made here is that we generate and include ID tokens in requests to http/callable functions since we aren't allowed to run public functions on internal GCP project. --- integration_test/README.md | 13 +++++- integration_test/firebase.json | 3 ++ integration_test/functions/src/index.ts | 43 +++++++++++-------- .../functions/src/v1/https-tests.ts | 17 +++++--- .../functions/src/v2/https-tests.ts | 2 +- integration_test/functions/tsconfig.json | 2 +- integration_test/run_tests.sh | 4 +- package.json | 2 +- 8 files changed, 54 insertions(+), 32 deletions(-) diff --git a/integration_test/README.md b/integration_test/README.md index c6a0ca32d..3b0f5413f 100644 --- a/integration_test/README.md +++ b/integration_test/README.md @@ -8,6 +8,15 @@ Run the integration test as follows: ./run_tests.sh [] ``` -If just one project_id is provided, the both the node6 and node8 tests will be run on that project, in series. If two project_ids are provided, the node6 tests will be run on the first project and the node8 tests will be run on the second one, in parallel. +Test runs cycles of testing, once for Node.js 14 and another for Node.js 16. -The tests run fully automatically, and will print the result on standard out. The integration test for HTTPS is that it properly kicks off other integration tests and returns a result. From there the other integration test suites will write their results back to the database, where you can check the detailed results if you'd like. +Test uses locally installed firebase to invoke commands for deploying function. The test also requires that you have +gcloud CLI installed and authenticated (`gcloud auth login`). + +Integration test is triggered by invoking HTTP function integrationTest which in turns invokes each function trigger +by issuing actions necessary to trigger it (e.g. write to storage bucket). + +### Debugging + +The status and result of each test is stored in RTDB of the project used for testing. You can also inspect Cloud Logging +for more clues. diff --git a/integration_test/firebase.json b/integration_test/firebase.json index ce496e265..382d79c84 100644 --- a/integration_test/firebase.json +++ b/integration_test/firebase.json @@ -5,5 +5,8 @@ "firestore": { "rules": "firestore.rules", "indexes": "firestore.indexes.json" + }, + "functions": { + "predeploy": ["npm --prefix \"$RESOURCE_DIR\" run build"] } } diff --git a/integration_test/functions/src/index.ts b/integration_test/functions/src/index.ts index e8c6918a5..3a5fd8474 100644 --- a/integration_test/functions/src/index.ts +++ b/integration_test/functions/src/index.ts @@ -1,4 +1,5 @@ import { PubSub } from '@google-cloud/pubsub'; +import { GoogleAuth } from 'google-auth-library'; import { Request, Response } from 'express'; import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; @@ -22,17 +23,19 @@ const firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG); admin.initializeApp(); async function callHttpsTrigger(name: string, data: any) { - const resp = await fetch( - `https://${REGION}-${firebaseConfig.projectId}.cloudfunctions.net/${name}`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ data }), - } + const url = `https://${REGION}-${firebaseConfig.projectId}.cloudfunctions.net/${name}`; + const client = await new GoogleAuth().getIdTokenClient( + '32555940559.apps.googleusercontent.com' ); - if (!resp.ok) { + const resp = await client.request({ + url, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ data }), + }); + if (resp.status > 200) { throw Error(resp.statusText); } } @@ -42,7 +45,7 @@ async function callV2HttpsTrigger( data: any, accessToken: string ) { - let resp = await fetch( + const getFnResp = await fetch( `https://cloudfunctions.googleapis.com/v2beta/projects/${firebaseConfig.projectId}/locations/${REGION}/functions/${name}`, { headers: { @@ -50,23 +53,28 @@ async function callV2HttpsTrigger( }, } ); - if (!resp.ok) { - throw new Error(resp.statusText); + if (!getFnResp.ok) { + throw new Error(getFnResp.statusText); } - const fn = await resp.json(); + const fn = await getFnResp.json(); const uri = fn.serviceConfig?.uri; if (!uri) { throw new Error(`Cannot call v2 https trigger ${name} - no uri found`); } - resp = await fetch(uri, { + + const client = await new GoogleAuth().getIdTokenClient( + '32555940559.apps.googleusercontent.com' + ); + const invokeFnREsp = await client.request({ + url: uri, method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ data }), }); - if (!resp.ok) { - throw new Error(resp.statusText); + if (invokeFnREsp.status > 200) { + throw Error(invokeFnREsp.statusText); } } @@ -170,6 +178,7 @@ export const integrationTests: any = functions .region(REGION) .runWith({ timeoutSeconds: 540, + invoker: 'private', }) .https.onRequest(async (req: Request, resp: Response) => { const testId = admin diff --git a/integration_test/functions/src/v1/https-tests.ts b/integration_test/functions/src/v1/https-tests.ts index 8acf8932f..bce05f341 100644 --- a/integration_test/functions/src/v1/https-tests.ts +++ b/integration_test/functions/src/v1/https-tests.ts @@ -2,10 +2,13 @@ import * as functions from 'firebase-functions'; import { REGION } from '../region'; import { expectEq, TestSuite } from '../testing'; -export const callableTests: any = functions.region(REGION).https.onCall((d) => { - return new TestSuite('https onCall') - .it('should have the correct data', (data: any) => - expectEq(data?.foo, 'bar') - ) - .run(d.testId, d); -}); +export const callableTests: any = functions + .runWith({ invoker: 'private' }) + .region(REGION) + .https.onCall((d) => { + return new TestSuite('https onCall') + .it('should have the correct data', (data: any) => + expectEq(data?.foo, 'bar') + ) + .run(d.testId, d); + }); diff --git a/integration_test/functions/src/v2/https-tests.ts b/integration_test/functions/src/v2/https-tests.ts index 4936c48ea..448464ef0 100644 --- a/integration_test/functions/src/v2/https-tests.ts +++ b/integration_test/functions/src/v2/https-tests.ts @@ -1,7 +1,7 @@ import { onCall } from 'firebase-functions/v2/https'; import { expectEq, TestSuite } from '../testing'; -export const callabletests = onCall((req) => { +export const callabletests = onCall({ invoker: 'private' }, (req) => { return new TestSuite('v2 https onCall') .it('should have the correct data', (data: any) => expectEq(data?.foo, 'bar') diff --git a/integration_test/functions/tsconfig.json b/integration_test/functions/tsconfig.json index a5bd57033..77fb279d5 100644 --- a/integration_test/functions/tsconfig.json +++ b/integration_test/functions/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "lib": ["es6", "dom"], "module": "commonjs", - "target": "es6", + "target": "es2020", "noImplicitAny": false, "outDir": "lib", "declaration": true, diff --git a/integration_test/run_tests.sh b/integration_test/run_tests.sh index 814ddc492..9098c912c 100755 --- a/integration_test/run_tests.sh +++ b/integration_test/run_tests.sh @@ -74,8 +74,6 @@ function delete_all_functions { } function deploy { - cd "${DIR}" - ./functions/node_modules/.bin/tsc -p functions/ # Deploy functions, and security rules for database and Firestore. If the deploy fails, retry twice if [[ "${TOKEN}" == "" ]]; then for i in 1 2 3; do firebase deploy --project="${PROJECT_ID}" --only functions,database,firestore && break; done @@ -97,7 +95,7 @@ function run_tests { TEST_URL="https://${FIREBASE_FUNCTIONS_TEST_REGION}-${PROJECT_ID}.${TEST_DOMAIN}/integrationTests" echo "${TEST_URL}" - curl --fail "${TEST_URL}" + curl --fail -H "Authorization: Bearer $(gcloud auth print-identity-token)" "${TEST_URL}" } function cleanup { diff --git a/package.json b/package.json index 000d85096..e6c277745 100644 --- a/package.json +++ b/package.json @@ -169,7 +169,7 @@ "docgen:v2:toc": "ts-node docgen/toc.ts --input docgen/v2/markdown --output docgen/v2/markdown/toc --path /docs/functions/beta/reference", "docgen:v2:gen": "api-documenter-fire markdown -i docgen/v2 -o docgen/v2/markdown && npm run docgen:v2:toc", "docgen:v2": "npm run build && npm run docgen:v2:extract && npm run docgen:v2:gen", - "build:pack": "rm -rf lib && npm install --production && tsc -p tsconfig.release.json && npm pack", + "build:pack": "rm -rf lib && npm install && tsc -p tsconfig.release.json && npm pack", "build:release": "npm ci --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", From 6edbb95a9b7fba1b8015eeb05785637581bfe1f3 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 29 Jul 2022 12:08:54 -0700 Subject: [PATCH 133/370] Automatically run integration test on PR merge (#1182) Setup Github Workflow to automatically run integration test on commits to master branch. The test can be triggered manually by visiting https://github.com/firebase/firebase-functions/actions/workflows/postmerge.yaml. You can also manually trigger the test via: ``` GCLOUD_PROJECT= npm run test:postmerge ``` Make sure your test project has all necessary resources enambled (i.e. enable Firestore, Storage, RTDB, Auth, etc.) Only one test can run at a time, so I've enforced/disabled concurrency on the workflow. --- .github/workflows/postmerge.yaml | 55 ++++++++++++++++++++++++++++++++ integration_test/run_tests.sh | 34 ++++---------------- package.json | 3 +- 3 files changed, 64 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/postmerge.yaml diff --git a/.github/workflows/postmerge.yaml b/.github/workflows/postmerge.yaml new file mode 100644 index 000000000..f5105958c --- /dev/null +++ b/.github/workflows/postmerge.yaml @@ -0,0 +1,55 @@ +# Copyright 2022 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +name: Post-merge tests + +on: + workflow_dispatch: + workflow_run: + workflows: ['CI Tests'] + types: [completed] + branches: [master] + +concurrency: + group: postmerge-${{ github.ref }} + cancel-in-progress: true + +env: + CI: true + +jobs: + postmerge: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 16 + + - uses: google-github-actions/auth@v0 + with: + credentials_json: '${{ secrets.CF3_INTEGRATION_TEST_GOOGLE_CREDENTIALS }}' + create_credentials_file: true + + - name: 'Set up Cloud SDK' + uses: google-github-actions/setup-gcloud@v0 + + - name: 'Setup Firebase CLI' + run: npm i -g firebase-tools + + - name: 'Run integration test' + run: npm run test:postmerge + + - name: Print debug logs + if: failure() + run: find . -type f -name "*debug.log" | xargs cat diff --git a/integration_test/run_tests.sh b/integration_test/run_tests.sh index 9098c912c..681d2dc1e 100755 --- a/integration_test/run_tests.sh +++ b/integration_test/run_tests.sh @@ -3,26 +3,14 @@ # Exit immediately if a command exits with a non-zero status. set -e -function usage { - echo "Usage: ${0} []" - exit 1 -} +PROJECT_ID="${GCLOUD_PROJECT}" +TIMESTAMP=$(date +%s) -# This script takes in one required argument specifying a project_id and an -# optional arguement for a CI token that can be obtained by running -# `firebase login:ci` -# Example usage (from root dir) without token: -# ./integration_test/run_tests.sh chenky-test-proj -# Example usage (from root dir) with token: -# ./integration_test/run_tests.sh chenky-test-proj $TOKEN -if [[ "${1}" == "" ]]; then - usage +if [[ "${PROJECT_ID}" == "" ]]; then + echo "process.env.GCLOUD_PROJECT cannot be empty" + exit 1 fi -PROJECT_ID="${1}" -TIMESTAMP=$(date +%s) -TOKEN="${2}" - # Directory where this script lives. DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -64,22 +52,14 @@ function delete_all_functions { cd "${DIR}" # Try to delete, if there are errors it is because the project is already empty, # in that case do nothing. - if [[ "${TOKEN}" == "" ]]; then - firebase functions:delete integrationTests v1 v2 --force --project=$PROJECT_ID || : & - else - firebase functions:delete integrationTests v1 v2 --force --project=$PROJECT_ID --token=$TOKEN || : & - fi + firebase functions:delete integrationTests v1 v2 --force --project=$PROJECT_ID || : & wait announce "Project emptied." } function deploy { # Deploy functions, and security rules for database and Firestore. If the deploy fails, retry twice - if [[ "${TOKEN}" == "" ]]; then - for i in 1 2 3; do firebase deploy --project="${PROJECT_ID}" --only functions,database,firestore && break; done - else - for i in 1 2 3; do firebase deploy --project="${PROJECT_ID}" --token="${TOKEN}" --only functions,database,firestore && break; done - fi + for i in 1 2; do firebase deploy --project="${PROJECT_ID}" --only functions,database,firestore && break; done } function run_tests { diff --git a/package.json b/package.json index e6c277745..a8ea942e3 100644 --- a/package.json +++ b/package.json @@ -178,7 +178,8 @@ "lint": "tslint --config tslint.json --project tsconfig.json ", "lint:fix": "tslint --config tslint.json --fix --project tsconfig.json", "test": "mocha --file ./mocha/setup.ts \"spec/**/*.spec.ts\"", - "test:bin": "./scripts/bin-test/run.sh" + "test:bin": "./scripts/bin-test/run.sh", + "test:postmerge": "./integration_test/run_tests.sh" }, "dependencies": { "@types/cors": "^2.8.5", From d22ff201ed5c216da81085121146eb598a74a637 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 5 Aug 2022 10:28:49 -0700 Subject: [PATCH 134/370] Export Auth types to have them show up in the reference docs. (#1186) --- src/v2/providers/identity.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/v2/providers/identity.ts b/src/v2/providers/identity.ts index b68136d8e..80d1cf0b8 100644 --- a/src/v2/providers/identity.ts +++ b/src/v2/providers/identity.ts @@ -28,6 +28,7 @@ import { BlockingFunction } from '../../cloud-functions'; import { AuthBlockingEvent, + AuthUserRecord, AuthBlockingEventType, BeforeCreateResponse, BeforeSignInResponse, @@ -36,7 +37,7 @@ import { } from '../../common/providers/identity'; import * as options from '../options'; -export { HttpsError }; +export { AuthUserRecord, AuthBlockingEvent, HttpsError }; /** @hidden Internally used when parsing the options. */ interface InternalOptions { From 0e4ffb6f75d938d02372d0a87dcf281fbb178fe7 Mon Sep 17 00:00:00 2001 From: Josh Ladieu Date: Thu, 11 Aug 2022 12:46:25 -0400 Subject: [PATCH 135/370] Make `screenshotUris` non-optional. (#1188) If omitted, default to an empty list. --- src/v2/providers/alerts/appDistribution.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index ffac2d43d..c4511c066 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -72,7 +72,7 @@ export interface InAppFeedbackPayload { /** Text entered by the tester */ text: string; /** URIs to download screenshot(s) */ - screenshotUris?: string[]; + screenshotUris: string[]; } /** @@ -330,7 +330,10 @@ export function onInAppFeedbackPublished( const [opts, appId] = getOptsAndApp(appIdOrOptsOrHandler); const func = (raw: CloudEvent) => { - return handler(raw as AppDistributionEvent); + const event = raw as AppDistributionEvent; + // Consolidate the case of empty array and null array + event.data.payload.screenshotUris = event.data.payload.screenshotUris || []; + return handler(event); }; func.run = handler; From f751f5ce7d5ba7b940a6d39f1631bfb17cf36e15 Mon Sep 17 00:00:00 2001 From: Josh Ladieu Date: Thu, 11 Aug 2022 14:10:46 -0400 Subject: [PATCH 136/370] Update `InAppFeedbackPayload` (#1187) * Add `feedbackConsoleUri` * Clarify fast expiry of `screenshotUris` --- src/v2/providers/alerts/appDistribution.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index c4511c066..6e519a23e 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -53,6 +53,8 @@ export interface InAppFeedbackPayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.AppDistroInAppFeedbackPayload'; /** Resource name. Format: `projects/{project_number}/apps/{app_id}/releases/{release_id}/feedbackReports/{feedback_id}` */ feedbackReport: string; + /** Deep link back to the Firebase console. */ + feedbackConsoleUri: string; /** Name of the tester */ testerName?: string; /** Email of the tester */ @@ -71,7 +73,7 @@ export interface InAppFeedbackPayload { buildVersion: string; /** Text entered by the tester */ text: string; - /** URIs to download screenshot(s) */ + /** URIs to download screenshot(s). These URIs are fast expiring. */ screenshotUris: string[]; } From f9ecee4214e27bbe0f9e52323729506eba150a50 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Tue, 23 Aug 2022 13:28:48 -0400 Subject: [PATCH 137/370] Fixes a bug that disallowed setting customClaims and/or sessionClaims in blocking functions (#1199) * adding customClaims and sessionClaims to updateMask & fix tests * add changelog --- CHANGELOG.md | 1 + integration_test/functions/src/index.ts | 2 +- spec/common/providers/identity.spec.ts | 15 +-------------- src/common/providers/identity.ts | 3 --- src/v2/providers/identity.ts | 2 +- 5 files changed, 4 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..d61eb5f0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Fixes a bug that disallowed setting customClaims and/or sessionClaims in blocking functions (#1199). diff --git a/integration_test/functions/src/index.ts b/integration_test/functions/src/index.ts index 3a5fd8474..a880c64ea 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 { GoogleAuth } from 'google-auth-library'; import { Request, Response } from 'express'; import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; import * as fs from 'fs'; +import { GoogleAuth } from 'google-auth-library'; import fetch from 'node-fetch'; import * as v1 from './v1'; diff --git a/spec/common/providers/identity.spec.ts b/spec/common/providers/identity.spec.ts index 592d14767..2b2ea71e9 100644 --- a/spec/common/providers/identity.spec.ts +++ b/spec/common/providers/identity.spec.ts @@ -765,19 +765,6 @@ describe('identity', () => { expect(identity.getUpdateMask()).to.eq(''); }); - it('should return empty on only customClaims and sessionClaims', () => { - const response = { - customClaims: { - claim1: 'abc', - }, - sessionClaims: { - claim2: 'def', - }, - }; - - expect(identity.getUpdateMask(response)).to.eq(''); - }); - it('should return the right claims on a response', () => { const response = { displayName: 'john', @@ -793,7 +780,7 @@ describe('identity', () => { }; expect(identity.getUpdateMask(response)).to.eq( - 'displayName,disabled,emailVerified,photoURL' + 'displayName,disabled,emailVerified,photoURL,customClaims,sessionClaims' ); }); }); diff --git a/src/common/providers/identity.ts b/src/common/providers/identity.ts index 2c0ce0a2e..0a376be9e 100644 --- a/src/common/providers/identity.ts +++ b/src/common/providers/identity.ts @@ -799,9 +799,6 @@ export function getUpdateMask( } const updateMask: string[] = []; for (const key in authResponse) { - if (key === 'customClaims' || key === 'sessionClaims') { - continue; - } if ( authResponse.hasOwnProperty(key) && typeof authResponse[key] !== 'undefined' diff --git a/src/v2/providers/identity.ts b/src/v2/providers/identity.ts index 80d1cf0b8..eb03c747b 100644 --- a/src/v2/providers/identity.ts +++ b/src/v2/providers/identity.ts @@ -28,8 +28,8 @@ import { BlockingFunction } from '../../cloud-functions'; import { AuthBlockingEvent, - AuthUserRecord, AuthBlockingEventType, + AuthUserRecord, BeforeCreateResponse, BeforeSignInResponse, HttpsError, From c0e0361ef869f5d8b15fe2a25c74355ebc87e09b Mon Sep 17 00:00:00 2001 From: Kai Bolay Date: Wed, 24 Aug 2022 17:25:22 -0400 Subject: [PATCH 138/370] Change payload based on API council feedback: (#1209) - combine `display_version` and `build_version` into `app_version`. - change repeated field `screenshot_uris` to (optional) field `screenshot_uri`. --- src/v2/providers/alerts/appDistribution.ts | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index 6e519a23e..512c3e95a 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -60,21 +60,14 @@ export interface InAppFeedbackPayload { /** Email of the tester */ testerEmail: string; /** - * Display version of the release. For an Android release, the display version - * is the `versionName`. For an iOS release, the display version is the - * `CFBundleShortVersionString`. + * Version consisting of `versionName` and `versionCode` for Android and + * `CFBundleShortVersionString` and `CFBundleVersion` for iOS. */ - displayVersion: string; - /** - * Build version of the release. For an Android release, the build version - * is the `versionCode`. For an iOS release, the build version is the - * `CFBundleVersion`. - */ - buildVersion: string; + appVersion: string; /** Text entered by the tester */ text: string; - /** URIs to download screenshot(s). These URIs are fast expiring. */ - screenshotUris: string[]; + /** URI to download screenshot. This URIs are is fast expiring. */ + screenshotUri?: string; } /** @@ -332,10 +325,7 @@ export function onInAppFeedbackPublished( const [opts, appId] = getOptsAndApp(appIdOrOptsOrHandler); const func = (raw: CloudEvent) => { - const event = raw as AppDistributionEvent; - // Consolidate the case of empty array and null array - event.data.payload.screenshotUris = event.data.payload.screenshotUris || []; - return handler(event); + return handler(raw as AppDistributionEvent); }; func.run = handler; From eed7d59e33ae0ff131070fc8facee839ad73d977 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Thu, 25 Aug 2022 12:52:12 -0400 Subject: [PATCH 139/370] Add v2 Schedule Triggers (#1177) Adds schedule triggers to the v2 namespace. - Replaces pub/sub with an http underlying function - Updated syntax to match with the other v2 triggers This change does not add the `invoker` property to the container contract. ``` export const sch = v2.scheduler.onSchedule("* * * * *", () => {}); export const sch = v2.scheduler.onSchedule( { schedule: "* * * * *", timeZone: "utc", }, () => {} ); ``` --- CHANGELOG.md | 1 + integration_test/functions/src/index.ts | 28 + integration_test/functions/src/v2/index.ts | 1 + .../functions/src/v2/scheduled-tests.ts | 23 + package.json | 6 +- spec/v2/providers/scheduler.spec.ts | 140 +++++ src/common/timezone.ts | 542 ++++++++++++++++++ src/runtime/manifest.ts | 8 +- src/v2/index.ts | 2 + src/v2/providers/scheduler.ts | 210 +++++++ v2/scheduler.js | 26 + 11 files changed, 982 insertions(+), 5 deletions(-) create mode 100644 integration_test/functions/src/v2/scheduled-tests.ts create mode 100644 spec/v2/providers/scheduler.spec.ts create mode 100644 src/common/timezone.ts create mode 100644 src/v2/providers/scheduler.ts create mode 100644 v2/scheduler.js diff --git a/CHANGELOG.md b/CHANGELOG.md index d61eb5f0d..8dd094b29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ - Fixes a bug that disallowed setting customClaims and/or sessionClaims in blocking functions (#1199). +- Add v2 Schedule Triggers (#1177). diff --git a/integration_test/functions/src/index.ts b/integration_test/functions/src/index.ts index a880c64ea..43378bf42 100644 --- a/integration_test/functions/src/index.ts +++ b/integration_test/functions/src/index.ts @@ -101,6 +101,32 @@ async function callScheduleTrigger( return; } +async function callV2ScheduleTrigger( + functionName: string, + region: string, + accessToken: string +) { + const response = await fetch( + `https://cloudscheduler.googleapis.com/v1/projects/${firebaseConfig.projectId}/locations/us-central1/jobs/firebase-schedule-${functionName}-${region}:run`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${accessToken}`, + }, + } + ); + if (!response.ok) { + throw new Error(`Failed request with status ${response.status}!`); + } + const data = await response.text(); + functions.logger.log( + `Successfully scheduled v2 function ${functionName}`, + data + ); + return; +} + async function updateRemoteConfig( testId: string, accessToken: string @@ -171,6 +197,8 @@ function v2Tests(testId: string, accessToken: string): Array> { return [ // Invoke a callable HTTPS trigger. callV2HttpsTrigger('v2-callabletests', { foo: 'bar', testId }, accessToken), + // Invoke a scheduled trigger. + callV2ScheduleTrigger('v2-schedule', 'us-central1', accessToken), ]; } diff --git a/integration_test/functions/src/v2/index.ts b/integration_test/functions/src/v2/index.ts index def01bfbb..6b8f55e2a 100644 --- a/integration_test/functions/src/v2/index.ts +++ b/integration_test/functions/src/v2/index.ts @@ -3,3 +3,4 @@ import { REGION } from '../region'; setGlobalOptions({ region: REGION }); export * from './https-tests'; +export * from './scheduled-tests'; diff --git a/integration_test/functions/src/v2/scheduled-tests.ts b/integration_test/functions/src/v2/scheduled-tests.ts new file mode 100644 index 000000000..ffffb386c --- /dev/null +++ b/integration_test/functions/src/v2/scheduled-tests.ts @@ -0,0 +1,23 @@ +import * as admin from 'firebase-admin'; +import { onSchedule } from 'firebase-functions/v2/scheduler'; +import { REGION } from '../region'; +import { success, TestSuite } from '../testing'; + +export const schedule: any = onSchedule( + { + schedule: 'every 10 hours', + region: REGION, + }, + async (event) => { + const db = admin.database(); + const snap = await db + .ref('testRuns') + .orderByChild('timestamp') + .limitToLast(1) + .once('value'); + const testId = Object.keys(snap.val())[0]; + return new TestSuite('scheduler scheduleOnRun') + .it('should trigger when the scheduler fires', () => success()) + .run(testId, null); + } +); diff --git a/package.json b/package.json index a8ea942e3..69265b485 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,8 @@ "./v2/alerts/crashlytics": "./lib/v2/providers/alerts/crashlytics.js", "./v2/eventarc": "./lib/v2/providers/eventarc.js", "./v2/identity": "./lib/v2/providers/identity.js", - "./v2/database": "./lib/v2/providers/database.js" + "./v2/database": "./lib/v2/providers/database.js", + "./v2/scheduler": "./lib/v2/providers/scheduler.js" }, "typesVersions": { "*": { @@ -153,6 +154,9 @@ ], "v2/tasks": [ "lib/v2/providers/tasks" + ], + "v2/scheduler": [ + "lib/v2/providers/scheduler" ] } }, diff --git a/spec/v2/providers/scheduler.spec.ts b/spec/v2/providers/scheduler.spec.ts new file mode 100644 index 000000000..7d690cbe5 --- /dev/null +++ b/spec/v2/providers/scheduler.spec.ts @@ -0,0 +1,140 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as schedule from '../../../src/v2/providers/scheduler'; + +describe('schedule', () => { + describe('getOpts', () => { + it('should handle a schedule', () => { + expect(schedule.getOpts('* * * * *')).to.deep.eq({ + schedule: '* * * * *', + opts: {}, + }); + }); + + it('should handle full options', () => { + const options: schedule.ScheduleOptions = { + schedule: '* * * * *', + timeZone: 'utc', + retryCount: 3, + maxRetrySeconds: 1, + minBackoffSeconds: 2, + maxBackoffSeconds: 3, + maxDoublings: 4, + memory: '128MiB', + region: 'us-central1', + }; + + expect(schedule.getOpts(options)).to.deep.eq({ + schedule: '* * * * *', + timeZone: 'utc', + retryCount: 3, + maxRetrySeconds: 1, + minBackoffSeconds: 2, + maxBackoffSeconds: 3, + maxDoublings: 4, + opts: { + ...options, + memory: '128MiB', + region: 'us-central1', + }, + }); + }); + }); + + describe('onSchedule', () => { + it('should create a schedule function given a schedule', () => { + const schfn = schedule.onSchedule('* * * * *', (event) => console.log(1)); + + expect(schfn.__endpoint).to.deep.eq({ + platform: 'gcfv2', + labels: {}, + scheduleTrigger: { + schedule: '* * * * *', + retryConfig: {}, + }, + }); + expect(schfn.__requiredAPIs).to.deep.eq([ + { + api: 'cloudscheduler.googleapis.com', + reason: 'Needed for scheduled functions.', + }, + ]); + }); + + it('should create a schedule function given options', () => { + const schfn = schedule.onSchedule( + { + schedule: '* * * * *', + timeZone: 'utc', + retryCount: 3, + maxRetrySeconds: 10, + minBackoffSeconds: 11, + maxBackoffSeconds: 12, + maxDoublings: 2, + region: 'us-central1', + labels: { key: 'val' }, + }, + (event) => {} + ); + + expect(schfn.__endpoint).to.deep.eq({ + platform: 'gcfv2', + labels: { key: 'val' }, + region: ['us-central1'], + scheduleTrigger: { + schedule: '* * * * *', + timeZone: 'utc', + retryConfig: { + retryCount: 3, + maxRetrySeconds: 10, + minBackoffSeconds: 11, + maxBackoffSeconds: 12, + maxDoublings: 2, + }, + }, + }); + expect(schfn.__requiredAPIs).to.deep.eq([ + { + api: 'cloudscheduler.googleapis.com', + reason: 'Needed for scheduled functions.', + }, + ]); + }); + + it('should have a .run method', () => { + const testObj = { + foo: 'bar', + }; + const schfn = schedule.onSchedule('* * * * *', (event) => { + testObj.foo = 'newBar'; + }); + + schfn.run('input' as any); + + expect(testObj).to.deep.eq({ + foo: 'newBar', + }); + }); + }); +}); diff --git a/src/common/timezone.ts b/src/common/timezone.ts new file mode 100644 index 000000000..5af4980fc --- /dev/null +++ b/src/common/timezone.ts @@ -0,0 +1,542 @@ +export const tzDatabase: Record = { + 'Africa/Abidjan': '+00:00', + 'Africa/Accra': '+00:00', + 'Africa/Addis_Ababa': '+03:00', + 'Africa/Algiers': '+01:00', + 'Africa/Asmara': '+03:00', + 'Africa/Asmera': '+03:00', + 'Africa/Bamako': '+00:00', + 'Africa/Bangui': '+01:00', + 'Africa/Banjul': '+00:00', + 'Africa/Blantyre': '+02:00', + 'Africa/Brazzaville': '+01:00', + 'Africa/Bujumbura': '+02:00', + 'Africa/Cairo': '+02:00', + 'Africa/Casablanca': '+00:00', + 'Africa/Ceuta': '+01:00', + 'Africa/Conakry': '+00:00', + 'Africa/Dakar': '+00:00', + 'Africa/Dar_es_Salaam': '+03:00', + 'Africa/Djibouti': '+03:00', + 'Africa/Douala': '+01:00', + 'Africa/El_Aaiun': '+00:00', + 'Africa/Freetown': '+00:00', + 'Africa/Gaborone': '+02:00', + 'Africa/Harare': '+02:00', + 'Africa/Johannesburg': '+02:00', + 'Africa/Juba': '+03:00', + 'Africa/Kampala': '+03:00', + 'Africa/Khartoum': '+03:00', + 'Africa/Kigali': '+02:00', + 'Africa/Kinshasa': '+01:00', + 'Africa/Lagos': '+01:00', + 'Africa/Libreville': '+01:00', + 'Africa/Lome': '+00:00', + 'Africa/Luanda': '+01:00', + 'Africa/Lubumbashi': '+02:00', + 'Africa/Lusaka': '+02:00', + 'Africa/Malabo': '+01:00', + 'Africa/Maputo': '+02:00', + 'Africa/Maseru': '+02:00', + 'Africa/Mbabane': '+02:00', + 'Africa/Mogadishu': '+03:00', + 'Africa/Monrovia': '+00:00', + 'Africa/Nairobi': '+03:00', + 'Africa/Ndjamena': '+01:00', + 'Africa/Niamey': '+01:00', + 'Africa/Nouakchott': '+00:00', + 'Africa/Ouagadougou': '+00:00', + 'Africa/Porto-Novo': '+01:00', + 'Africa/Sao_Tome': '+00:00', + 'Africa/Timbuktu': '+00:00', + 'Africa/Tripoli': '+02:00', + 'Africa/Tunis': '+01:00', + 'Africa/Windhoek': '+01:00', + 'America/Adak': '-10:00', + 'America/Anchorage': '-09:00', + 'America/Anguilla': '-04:00', + 'America/Antigua': '-04:00', + 'America/Araguaina': '-03:00', + 'America/Argentina/Buenos_Aires': '-03:00', + 'America/Argentina/Catamarca': '-03:00', + 'America/Argentina/ComodRivadavia': '-03:00', + 'America/Argentina/Cordoba': '-03:00', + 'America/Argentina/Jujuy': '-03:00', + 'America/Argentina/La_Rioja': '-03:00', + 'America/Argentina/Mendoza': '-03:00', + 'America/Argentina/Rio_Gallegos': '-03:00', + 'America/Argentina/Salta': '-03:00', + 'America/Argentina/San_Juan': '-03:00', + 'America/Argentina/San_Luis': '-03:00', + 'America/Argentina/Tucuman': '-03:00', + 'America/Argentina/Ushuaia': '-03:00', + 'America/Aruba': '-04:00', + 'America/Asuncion': '-04:00', + 'America/Atikokan': '-05:00', + 'America/Atka': '-10:00', + 'America/Bahia': '-03:00', + 'America/Bahia_Banderas': '-06:00', + 'America/Barbados': '-04:00', + 'America/Belem': '-03:00', + 'America/Belize': '-06:00', + 'America/Blanc-Sablon': '-04:00', + 'America/Boa_Vista': '-04:00', + 'America/Bogota': '-05:00', + 'America/Boise': '-07:00', + 'America/Buenos_Aires': '-03:00', + 'America/Cambridge_Bay': '-07:00', + 'America/Campo_Grande': '-04:00', + 'America/Cancun': '-06:00', + 'America/Caracas': '-04:30', + 'America/Catamarca': '-03:00', + 'America/Cayenne': '-03:00', + 'America/Cayman': '-05:00', + 'America/Chicago': '-06:00', + 'America/Chihuahua': '-07:00', + 'America/Coral_Harbour': '-05:00', + 'America/Cordoba': '-03:00', + 'America/Costa_Rica': '-06:00', + 'America/Creston': '-07:00', + 'America/Cuiaba': '-04:00', + 'America/Curacao': '-04:00', + 'America/Danmarkshavn': '+00:00', + 'America/Dawson': '-08:00', + 'America/Dawson_Creek': '-07:00', + 'America/Denver': '-07:00', + 'America/Detroit': '-05:00', + 'America/Dominica': '-04:00', + 'America/Edmonton': '-07:00', + 'America/Eirunepe': '-05:00', + 'America/El_Salvador': '-06:00', + 'America/Ensenada': '-08:00', + 'America/Fort_Wayne': '-05:00', + 'America/Fortaleza': '-03:00', + 'America/Glace_Bay': '-04:00', + 'America/Godthab': '-03:00', + 'America/Goose_Bay': '-04:00', + 'America/Grand_Turk': '-05:00', + 'America/Grenada': '-04:00', + 'America/Guadeloupe': '-04:00', + 'America/Guatemala': '-06:00', + 'America/Guayaquil': '-05:00', + 'America/Guyana': '-04:00', + 'America/Halifax': '-04:00', + 'America/Havana': '-05:00', + 'America/Hermosillo': '-07:00', + 'America/Indiana/Indianapolis': '-05:00', + 'America/Indiana/Knox': '-06:00', + 'America/Indiana/Marengo': '-05:00', + 'America/Indiana/Petersburg': '-05:00', + 'America/Indiana/Tell_City': '-06:00', + 'America/Indiana/Valparaiso': '-06:00', + 'America/Indiana/Vevay': '-05:00', + 'America/Indiana/Vincennes': '-05:00', + 'America/Indiana/Winamac': '-05:00', + 'America/Indianapolis': '-05:00', + 'America/Inuvik': '-07:00', + 'America/Iqaluit': '-05:00', + 'America/Jamaica': '-05:00', + 'America/Jujuy': '-03:00', + 'America/Juneau': '-09:00', + 'America/Kentucky/Louisville': '-05:00', + 'America/Kentucky/Monticello': '-05:00', + 'America/Knox_IN': '-06:00', + 'America/Kralendijk': '-04:00', + 'America/La_Paz': '-04:00', + 'America/Lima': '-05:00', + 'America/Los_Angeles': '-08:00', + 'America/Louisville': '-05:00', + 'America/Lower_Princes': '-04:00', + 'America/Maceio': '-03:00', + 'America/Managua': '-06:00', + 'America/Manaus': '-04:00', + 'America/Marigot': '-04:00', + 'America/Martinique': '-04:00', + 'America/Matamoros': '-06:00', + 'America/Mazatlan': '-07:00', + 'America/Mendoza': '-03:00', + 'America/Menominee': '-06:00', + 'America/Merida': '-06:00', + 'America/Metlakatla': '-08:00', + 'America/Mexico_City': '-06:00', + 'America/Miquelon': '-03:00', + 'America/Moncton': '-04:00', + 'America/Monterrey': '-06:00', + 'America/Montevideo': '-03:00', + 'America/Montreal': '-05:00', + 'America/Montserrat': '-04:00', + 'America/Nassau': '-05:00', + 'America/New_York': '-05:00', + 'America/Nipigon': '-05:00', + 'America/Nome': '-09:00', + 'America/Noronha': '-02:00', + 'America/North_Dakota/Beulah': '-06:00', + 'America/North_Dakota/Center': '-06:00', + 'America/North_Dakota/New_Salem': '-06:00', + 'America/Ojinaga': '-07:00', + 'America/Panama': '-05:00', + 'America/Pangnirtung': '-05:00', + 'America/Paramaribo': '-03:00', + 'America/Phoenix': '-07:00', + 'America/Port_of_Spain': '-04:00', + 'America/Port-au-Prince': '-05:00', + 'America/Porto_Acre': '-05:00', + 'America/Porto_Velho': '-04:00', + 'America/Puerto_Rico': '-04:00', + 'America/Rainy_River': '-06:00', + 'America/Rankin_Inlet': '-06:00', + 'America/Recife': '-03:00', + 'America/Regina': '-06:00', + 'America/Resolute': '-06:00', + 'America/Rio_Branco': '-05:00', + 'America/Rosario': '-03:00', + 'America/Santa_Isabel': '-08:00', + 'America/Santarem': '-03:00', + 'America/Santiago': '-03:00', + 'America/Santo_Domingo': '-04:00', + 'America/Sao_Paulo': '-03:00', + 'America/Scoresbysund': '-01:00', + 'America/Shiprock': '-07:00', + 'America/Sitka': '-09:00', + 'America/St_Barthelemy': '-04:00', + 'America/St_Johns': '-03:30', + 'America/St_Kitts': '-04:00', + 'America/St_Lucia': '-04:00', + 'America/St_Thomas': '-04:00', + 'America/St_Vincent': '-04:00', + 'America/Swift_Current': '-06:00', + 'America/Tegucigalpa': '-06:00', + 'America/Thule': '-04:00', + 'America/Thunder_Bay': '-05:00', + 'America/Tijuana': '-08:00', + 'America/Toronto': '-05:00', + 'America/Tortola': '-04:00', + 'America/Vancouver': '-08:00', + 'America/Virgin': '-04:00', + 'America/Whitehorse': '-08:00', + 'America/Winnipeg': '-06:00', + 'America/Yakutat': '-09:00', + 'America/Yellowknife': '-07:00', + 'Antarctica/Casey': '+11:00', + 'Antarctica/Davis': '+05:00', + 'Antarctica/DumontDUrville': '+10:00', + 'Antarctica/Macquarie': '+11:00', + 'Antarctica/Mawson': '+05:00', + 'Antarctica/McMurdo': '+12:00', + 'Antarctica/Palmer': '-04:00', + 'Antarctica/Rothera': '-03:00', + 'Antarctica/South_Pole': '+12:00', + 'Antarctica/Syowa': '+03:00', + 'Antarctica/Troll': '+00:00', + 'Antarctica/Vostok': '+06:00', + 'Arctic/Longyearbyen': '+01:00', + 'Asia/Aden': '+03:00', + 'Asia/Almaty': '+06:00', + 'Asia/Amman': '+02:00', + 'Asia/Anadyr': '+12:00', + 'Asia/Aqtau': '+05:00', + 'Asia/Aqtobe': '+05:00', + 'Asia/Ashgabat': '+05:00', + 'Asia/Ashkhabad': '+05:00', + 'Asia/Baghdad': '+03:00', + 'Asia/Bahrain': '+03:00', + 'Asia/Baku': '+04:00', + 'Asia/Bangkok': '+07:00', + 'Asia/Beirut': '+02:00', + 'Asia/Bishkek': '+06:00', + 'Asia/Brunei': '+08:00', + 'Asia/Calcutta': '+05:30', + 'Asia/Choibalsan': '+08:00', + 'Asia/Chongqing': '+08:00', + 'Asia/Chungking': '+08:00', + 'Asia/Colombo': '+05:30', + 'Asia/Dacca': '+06:00', + 'Asia/Damascus': '+02:00', + 'Asia/Dhaka': '+06:00', + 'Asia/Dili': '+09:00', + 'Asia/Dubai': '+04:00', + 'Asia/Dushanbe': '+05:00', + 'Asia/Gaza': '+02:00', + 'Asia/Harbin': '+08:00', + 'Asia/Hebron': '+02:00', + 'Asia/Ho_Chi_Minh': '+07:00', + 'Asia/Hong_Kong': '+08:00', + 'Asia/Hovd': '+07:00', + 'Asia/Irkutsk': '+08:00', + 'Asia/Istanbul': '+02:00', + 'Asia/Jakarta': '+07:00', + 'Asia/Jayapura': '+09:00', + 'Asia/Jerusalem': '+02:00', + 'Asia/Kabul': '+04:30', + 'Asia/Kamchatka': '+12:00', + 'Asia/Karachi': '+05:00', + 'Asia/Kashgar': '+08:00', + 'Asia/Kathmandu': '+05:45', + 'Asia/Katmandu': '+05:45', + 'Asia/Khandyga': '+09:00', + 'Asia/Kolkata': '+05:30', + 'Asia/Krasnoyarsk': '+07:00', + 'Asia/Kuala_Lumpur': '+08:00', + 'Asia/Kuching': '+08:00', + 'Asia/Kuwait': '+03:00', + 'Asia/Macao': '+08:00', + 'Asia/Macau': '+08:00', + 'Asia/Magadan': '+10:00', + 'Asia/Makassar': '+08:00', + 'Asia/Manila': '+08:00', + 'Asia/Muscat': '+04:00', + 'Asia/Nicosia': '+02:00', + 'Asia/Novokuznetsk': '+07:00', + 'Asia/Novosibirsk': '+06:00', + 'Asia/Omsk': '+06:00', + 'Asia/Oral': '+05:00', + 'Asia/Phnom_Penh': '+07:00', + 'Asia/Pontianak': '+07:00', + 'Asia/Pyongyang': '+09:00', + 'Asia/Qatar': '+03:00', + 'Asia/Qyzylorda': '+06:00', + 'Asia/Rangoon': '+06:30', + 'Asia/Riyadh': '+03:00', + 'Asia/Saigon': '+07:00', + 'Asia/Sakhalin': '+11:00', + 'Asia/Samarkand': '+05:00', + 'Asia/Seoul': '+09:00', + 'Asia/Shanghai': '+08:00', + 'Asia/Singapore': '+08:00', + 'Asia/Taipei': '+08:00', + 'Asia/Tashkent': '+05:00', + 'Asia/Tbilisi': '+04:00', + 'Asia/Tehran': '+03:30', + 'Asia/Tel_Aviv': '+02:00', + 'Asia/Thimbu': '+06:00', + 'Asia/Thimphu': '+06:00', + 'Asia/Tokyo': '+09:00', + 'Asia/Ujung_Pandang': '+08:00', + 'Asia/Ulaanbaatar': '+08:00', + 'Asia/Ulan_Bator': '+08:00', + 'Asia/Urumqi': '+08:00', + 'Asia/Ust-Nera': '+10:00', + 'Asia/Vientiane': '+07:00', + 'Asia/Vladivostok': '+10:00', + 'Asia/Yakutsk': '+09:00', + 'Asia/Yekaterinburg': '+05:00', + 'Asia/Yerevan': '+04:00', + 'Atlantic/Azores': '-01:00', + 'Atlantic/Bermuda': '-04:00', + 'Atlantic/Canary': '+00:00', + 'Atlantic/Cape_Verde': '-01:00', + 'Atlantic/Faeroe': '+00:00', + 'Atlantic/Faroe': '+00:00', + 'Atlantic/Jan_Mayen': '+01:00', + 'Atlantic/Madeira': '+00:00', + 'Atlantic/Reykjavik': '+00:00', + 'Atlantic/South_Georgia': '-02:00', + 'Atlantic/St_Helena': '+00:00', + 'Atlantic/Stanley': '-03:00', + 'Australia/ACT': '+10:00', + 'Australia/Adelaide': '+09:30', + 'Australia/Brisbane': '+10:00', + 'Australia/Broken_Hill': '+09:30', + 'Australia/Canberra': '+10:00', + 'Australia/Currie': '+10:00', + 'Australia/Darwin': '+09:30', + 'Australia/Eucla': '+08:45', + 'Australia/Hobart': '+10:00', + 'Australia/LHI': '+10:30', + 'Australia/Lindeman': '+10:00', + 'Australia/Lord_Howe': '+10:30', + 'Australia/Melbourne': '+10:00', + 'Australia/North': '+09:30', + 'Australia/NSW': '+10:00', + 'Australia/Perth': '+08:00', + 'Australia/Queensland': '+10:00', + 'Australia/South': '+09:30', + 'Australia/Sydney': '+10:00', + 'Australia/Tasmania': '+10:00', + 'Australia/Victoria': '+10:00', + 'Australia/West': '+08:00', + 'Australia/Yancowinna': '+09:30', + 'Brazil/Acre': '-05:00', + 'Brazil/DeNoronha': '-02:00', + 'Brazil/East': '-03:00', + 'Brazil/West': '-04:00', + 'Canada/Atlantic': '-04:00', + 'Canada/Central': '-06:00', + 'Canada/Eastern': '-05:00', + 'Canada/East-Saskatchewan': '-06:00', + 'Canada/Mountain': '-07:00', + 'Canada/Newfoundland': '-03:30', + 'Canada/Pacific': '-08:00', + 'Canada/Saskatchewan': '-06:00', + 'Canada/Yukon': '-08:00', + 'Chile/Continental': '-03:00', + 'Chile/EasterIsland': '-05:00', + Cuba: '-05:00', + Egypt: '+02:00', + Eire: '+00:00', + 'Etc/GMT': '+00:00', + 'Etc/GMT+0': '+00:00', + 'Etc/UCT': '+00:00', + 'Etc/Universal': '+00:00', + 'Etc/UTC': '+00:00', + 'Etc/Zulu': '+00:00', + 'Europe/Amsterdam': '+01:00', + 'Europe/Andorra': '+01:00', + 'Europe/Athens': '+02:00', + 'Europe/Belfast': '+00:00', + 'Europe/Belgrade': '+01:00', + 'Europe/Berlin': '+01:00', + 'Europe/Bratislava': '+01:00', + 'Europe/Brussels': '+01:00', + 'Europe/Bucharest': '+02:00', + 'Europe/Budapest': '+01:00', + 'Europe/Busingen': '+01:00', + 'Europe/Chisinau': '+02:00', + 'Europe/Copenhagen': '+01:00', + 'Europe/Dublin': '+00:00', + 'Europe/Gibraltar': '+01:00', + 'Europe/Guernsey': '+00:00', + 'Europe/Helsinki': '+02:00', + 'Europe/Isle_of_Man': '+00:00', + 'Europe/Istanbul': '+02:00', + 'Europe/Jersey': '+00:00', + 'Europe/Kaliningrad': '+02:00', + 'Europe/Kiev': '+02:00', + 'Europe/Lisbon': '+00:00', + 'Europe/Ljubljana': '+01:00', + 'Europe/London': '+00:00', + 'Europe/Luxembourg': '+01:00', + 'Europe/Madrid': '+01:00', + 'Europe/Malta': '+01:00', + 'Europe/Mariehamn': '+02:00', + 'Europe/Minsk': '+03:00', + 'Europe/Monaco': '+01:00', + 'Europe/Moscow': '+03:00', + 'Europe/Nicosia': '+02:00', + 'Europe/Oslo': '+01:00', + 'Europe/Paris': '+01:00', + 'Europe/Podgorica': '+01:00', + 'Europe/Prague': '+01:00', + 'Europe/Riga': '+02:00', + 'Europe/Rome': '+01:00', + 'Europe/Samara': '+04:00', + 'Europe/San_Marino': '+01:00', + 'Europe/Sarajevo': '+01:00', + 'Europe/Simferopol': '+03:00', + 'Europe/Skopje': '+01:00', + 'Europe/Sofia': '+02:00', + 'Europe/Stockholm': '+01:00', + 'Europe/Tallinn': '+02:00', + 'Europe/Tirane': '+01:00', + 'Europe/Tiraspol': '+02:00', + 'Europe/Uzhgorod': '+02:00', + 'Europe/Vaduz': '+01:00', + 'Europe/Vatican': '+01:00', + 'Europe/Vienna': '+01:00', + 'Europe/Vilnius': '+02:00', + 'Europe/Volgograd': '+03:00', + 'Europe/Warsaw': '+01:00', + 'Europe/Zagreb': '+01:00', + 'Europe/Zaporozhye': '+02:00', + 'Europe/Zurich': '+01:00', + GB: '+00:00', + 'GB-Eire': '+00:00', + GMT: '+00:00', + 'GMT+0': '+00:00', + GMT0: '+00:00', + 'GMT-0': '+00:00', + Greenwich: '+00:00', + Hongkong: '+08:00', + Iceland: '+00:00', + 'Indian/Antananarivo': '+03:00', + 'Indian/Chagos': '+06:00', + 'Indian/Christmas': '+07:00', + 'Indian/Cocos': '+06:30', + 'Indian/Comoro': '+03:00', + 'Indian/Kerguelen': '+05:00', + 'Indian/Mahe': '+04:00', + 'Indian/Maldives': '+05:00', + 'Indian/Mauritius': '+04:00', + 'Indian/Mayotte': '+03:00', + 'Indian/Reunion': '+04:00', + Iran: '+03:30', + Israel: '+02:00', + Jamaica: '-05:00', + Japan: '+09:00', + Kwajalein: '+12:00', + Libya: '+02:00', + 'Mexico/BajaNorte': '-08:00', + 'Mexico/BajaSur': '-07:00', + 'Mexico/General': '-06:00', + Navajo: '-07:00', + NZ: '+12:00', + 'NZ-CHAT': '+12:45', + 'Pacific/Apia': '+13:00', + 'Pacific/Auckland': '+12:00', + 'Pacific/Chatham': '+12:45', + 'Pacific/Chuuk': '+10:00', + 'Pacific/Easter': '-06:00', + 'Pacific/Efate': '+11:00', + 'Pacific/Enderbury': '+13:00', + 'Pacific/Fakaofo': '+13:00', + 'Pacific/Fiji': '+12:00', + 'Pacific/Funafuti': '+12:00', + 'Pacific/Galapagos': '-06:00', + 'Pacific/Gambier': '-09:00', + 'Pacific/Guadalcanal': '+11:00', + 'Pacific/Guam': '+10:00', + 'Pacific/Honolulu': '-10:00', + 'Pacific/Johnston': '-10:00', + 'Pacific/Kiritimati': '+14:00', + 'Pacific/Kosrae': '+11:00', + 'Pacific/Kwajalein': '+12:00', + 'Pacific/Majuro': '+12:00', + 'Pacific/Marquesas': '-09:30', + 'Pacific/Midway': '-11:00', + 'Pacific/Nauru': '+12:00', + 'Pacific/Niue': '-11:00', + 'Pacific/Norfolk': '+11:30', + 'Pacific/Noumea': '+11:00', + 'Pacific/Pago_Pago': '-11:00', + 'Pacific/Palau': '+09:00', + 'Pacific/Pitcairn': '-08:00', + 'Pacific/Pohnpei': '+11:00', + 'Pacific/Ponape': '+11:00', + 'Pacific/Port_Moresby': '+10:00', + 'Pacific/Rarotonga': '-10:00', + 'Pacific/Saipan': '+10:00', + 'Pacific/Samoa': '-11:00', + 'Pacific/Tahiti': '-10:00', + 'Pacific/Tarawa': '+12:00', + 'Pacific/Tongatapu': '+13:00', + 'Pacific/Truk': '+10:00', + 'Pacific/Wake': '+12:00', + 'Pacific/Wallis': '+12:00', + 'Pacific/Yap': '+10:00', + Poland: '+01:00', + Portugal: '+00:00', + PRC: '+08:00', + ROC: '+08:00', + ROK: '+09:00', + Singapore: '+08:00', + Turkey: '+02:00', + UCT: '+00:00', + Universal: '+00:00', + 'US/Alaska': '-09:00', + 'US/Aleutian': '-10:00', + 'US/Arizona': '-07:00', + 'US/Central': '-06:00', + 'US/Eastern': '-05:00', + 'US/East-Indiana': '-05:00', + 'US/Hawaii': '-10:00', + 'US/Indiana-Starke': '-06:00', + 'US/Michigan': '-05:00', + 'US/Mountain': '-07:00', + 'US/Pacific': '-08:00', + 'US/Samoa': '-11:00', + UTC: '+00:00', + 'W-SU': '+03:00', + Zulu: '+00:00', +}; + +export type timezone = keyof typeof tzDatabase; diff --git a/src/runtime/manifest.ts b/src/runtime/manifest.ts index d63088303..f3548c6d9 100644 --- a/src/runtime/manifest.ts +++ b/src/runtime/manifest.ts @@ -63,12 +63,12 @@ export interface ManifestEndpoint { scheduleTrigger?: { schedule?: string; - timezone?: string; + timeZone?: string; retryConfig?: { retryCount?: number; - maxRetryDuration?: string; - minBackoffDuration?: string; - maxBackoffDuration?: string; + maxRetrySeconds?: number; + minBackoffSeconds?: number; + maxBackoffSeconds?: number; maxDoublings?: number; }; }; diff --git a/src/v2/index.ts b/src/v2/index.ts index dc460cd52..d1054504a 100644 --- a/src/v2/index.ts +++ b/src/v2/index.ts @@ -35,6 +35,7 @@ import * as eventarc from './providers/eventarc'; import * as https from './providers/https'; import * as identity from './providers/identity'; import * as pubsub from './providers/pubsub'; +import * as scheduler from './providers/scheduler'; import * as storage from './providers/storage'; import * as tasks from './providers/tasks'; @@ -48,6 +49,7 @@ export { logger, tasks, eventarc, + scheduler, }; export { diff --git a/src/v2/providers/scheduler.ts b/src/v2/providers/scheduler.ts new file mode 100644 index 000000000..4c181ed11 --- /dev/null +++ b/src/v2/providers/scheduler.ts @@ -0,0 +1,210 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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 * as express from 'express'; +import { copyIfPresent } from '../../common/encoding'; +import { timezone } from '../../common/timezone'; +import * as logger from '../../logger'; +import { ManifestEndpoint, ManifestRequiredAPI } from '../../runtime/manifest'; +import * as options from '../options'; +import { HttpsFunction } from './https'; + +/** @hidden */ +interface ScheduleArgs { + schedule: string; + timeZone?: timezone; + retryCount?: number; + maxRetrySeconds?: number; + minBackoffSeconds?: number; + maxBackoffSeconds?: number; + maxDoublings?: number; + opts: options.GlobalOptions; +} + +/** @internal */ +export function getOpts(args: string | ScheduleOptions): ScheduleArgs { + if (typeof args === 'string') { + return { + schedule: args, + opts: {} as options.GlobalOptions, + }; + } + return { + schedule: args.schedule, + timeZone: args.timeZone, + retryCount: args.retryCount, + maxRetrySeconds: args.maxRetrySeconds, + minBackoffSeconds: args.minBackoffSeconds, + maxBackoffSeconds: args.maxBackoffSeconds, + maxDoublings: args.maxDoublings, + opts: args as options.GlobalOptions, + }; +} + +/** + * Interface representing a ScheduleEvent that is passed to the function handler. + */ +export interface ScheduledEvent { + /** + * The Cloud Scheduler job name. + * Populated via the X-CloudScheduler-JobName header. + * + * If invoked manually, this field is undefined. + */ + jobName?: string; + + /** + * For Cloud Scheduler jobs specified in the unix-cron format, + * this is the job schedule time in RFC3339 UTC "Zulu" format. + * Populated via the X-CloudScheduler-ScheduleTime header. + * + * If the schedule is manually triggered, this field will be + * the function execution time. + */ + scheduleTime: string; +} + +/** The Cloud Function type for Schedule triggers. */ +export interface ScheduleFunction extends HttpsFunction { + __requiredAPIs?: ManifestRequiredAPI[]; + run(data: ScheduledEvent): void | Promise; +} + +/** Options that can be set on a Schedule trigger. */ +export interface ScheduleOptions extends options.GlobalOptions { + /** The schedule, in Unix Crontab or AppEngine syntax. */ + schedule: string; + + /** The timezone that the schedule executes in. */ + timeZone?: timezone; + + /** The number of retry attempts for a failed run. */ + retryCount?: number; + + /** The time limit for retrying. */ + maxRetrySeconds?: number; + + /** The minimum time to wait before retying. */ + minBackoffSeconds?: number; + + /** The maximum time to wait before retrying. */ + maxBackoffSeconds?: number; + + /** The time between will double max doublings times. */ + maxDoublings?: number; +} + +/** + * Handler for scheduled functions. Triggered whenever the associated + * scheduler job sends a http request. + * @param schedule - The schedule, in Unix Crontab or AppEngine syntax. + * @param handler - A function to execute when triggered. + * @returns A function that you can export and deploy. + */ +export function onSchedule( + schedule: string, + handler: (req: ScheduledEvent) => void | Promise +): ScheduleFunction; + +/** + * Handler for scheduled functions. Triggered whenever the associated + * scheduler job sends a http request. + * @param options - Options to set on scheduled functions. + * @param handler - A function to execute when triggered. + * @returns A function that you can export and deploy. + */ +export function onSchedule( + options: ScheduleOptions, + handler: (req: ScheduledEvent) => void | Promise +): ScheduleFunction; + +/** + * Handler for scheduled functions. Triggered whenever the associated + * scheduler job sends a http request. + * @param args - Either a schedule or an object containing function options. + * @param handler - A function to execute when triggered. + * @returns A function that you can export and deploy. + */ +export function onSchedule( + args: string | ScheduleOptions, + handler: (req: ScheduledEvent) => void | Promise +): ScheduleFunction { + const separatedOpts = getOpts(args); + + const func: any = async ( + req: express.Request, + res: express.Response + ): Promise => { + const event: ScheduledEvent = { + jobName: req.header('X-CloudScheduler-JobName') || undefined, + scheduleTime: + req.header('X-CloudScheduler-ScheduleTime') || new Date().toISOString(), + }; + try { + await handler(event); + res.status(200).send(); + } catch (err) { + logger.error((err as Error).message); + res.status(500).send(); + } + }; + func.run = handler; + + const baseOptsEndpoint = options.optionsToEndpoint( + options.getGlobalOptions() + ); + const specificOptsEndpoint = options.optionsToEndpoint(separatedOpts.opts); + + const ep: ManifestEndpoint = { + platform: 'gcfv2', + ...baseOptsEndpoint, + ...specificOptsEndpoint, + labels: { + ...baseOptsEndpoint?.labels, + ...specificOptsEndpoint?.labels, + }, + scheduleTrigger: { + schedule: separatedOpts.schedule, + retryConfig: {}, + }, + }; + copyIfPresent(ep.scheduleTrigger, separatedOpts, 'timeZone'); + copyIfPresent( + ep.scheduleTrigger.retryConfig, + separatedOpts, + 'retryCount', + 'maxRetrySeconds', + 'minBackoffSeconds', + 'maxBackoffSeconds', + 'maxDoublings' + ); + func.__endpoint = ep; + + func.__requiredAPIs = [ + { + api: 'cloudscheduler.googleapis.com', + reason: 'Needed for scheduled functions.', + }, + ]; + + return func; +} diff --git a/v2/scheduler.js b/v2/scheduler.js new file mode 100644 index 000000000..ae33ba821 --- /dev/null +++ b/v2/scheduler.js @@ -0,0 +1,26 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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. + +// This file is not part of the firebase-functions SDK. It is used to silence the +// imports eslint plugin until it can understand import paths defined by node +// package exports. +// For more information, see github.com/import-js/eslint-plugin-import/issues/1810 From 7057c4db01e2cdcbf4878180436849cf13c466a4 Mon Sep 17 00:00:00 2001 From: Kai Bolay Date: Thu, 25 Aug 2022 22:42:25 -0400 Subject: [PATCH 140/370] fix comment (#1211) --- src/v2/providers/alerts/appDistribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index 512c3e95a..5b34c7b7c 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -66,7 +66,7 @@ export interface InAppFeedbackPayload { appVersion: string; /** Text entered by the tester */ text: string; - /** URI to download screenshot. This URIs are is fast expiring. */ + /** URI to download screenshot. This URI is fast expiring. */ screenshotUri?: string; } From d592847517c93b9240fbd2598bdf3dfefdb85948 Mon Sep 17 00:00:00 2001 From: Victor Fan Date: Mon, 29 Aug 2022 17:24:39 -0700 Subject: [PATCH 141/370] Insert Expression and Field types into GlobalOptions and inheriting types (#1171) This will plumb Param definitions and Expression types in Options to the CLI. --- package-lock.json | 5 +- .../fixtures/sources/commonjs-params/index.js | 12 +- spec/runtime/loader.spec.ts | 41 ++- spec/runtime/manifest.spec.ts | 67 ++++ spec/v2/params.spec.ts | 276 ---------------- src/bin/firebase-functions.ts | 3 +- src/common/providers/tasks.ts | 15 +- src/function-configuration.ts | 12 +- src/runtime/manifest.ts | 54 +++- src/v2/options.ts | 45 ++- src/v2/params/index.ts | 50 +-- src/v2/params/types.ts | 294 ++++++++++++------ src/v2/providers/alerts/alerts.ts | 11 +- src/v2/providers/alerts/appDistribution.ts | 11 +- src/v2/providers/alerts/crashlytics.ts | 11 +- src/v2/providers/database.ts | 13 +- src/v2/providers/eventarc.ts | 11 +- src/v2/providers/https.ts | 11 +- src/v2/providers/identity.ts | 11 +- src/v2/providers/pubsub.ts | 11 +- src/v2/providers/storage.ts | 11 +- src/v2/providers/tasks.ts | 13 +- 22 files changed, 506 insertions(+), 482 deletions(-) create mode 100644 spec/runtime/manifest.spec.ts delete mode 100644 spec/v2/params.spec.ts diff --git a/package-lock.json b/package-lock.json index d739d07c4..1f4742d4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2269,8 +2269,9 @@ }, "node_modules/firebase-admin": { "version": "10.3.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-10.3.0.tgz", + "integrity": "sha512-A0wgMLEjyVyUE+heyMJYqHRkPVjpebhOYsa47RHdrTM4ltApcx8Tn86sUmjqxlfh09gNnILAm7a8q5+FmgBYpg==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@fastify/busboy": "^1.1.0", "@firebase/database-compat": "^0.2.0", @@ -7460,6 +7461,8 @@ }, "firebase-admin": { "version": "10.3.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-10.3.0.tgz", + "integrity": "sha512-A0wgMLEjyVyUE+heyMJYqHRkPVjpebhOYsa47RHdrTM4ltApcx8Tn86sUmjqxlfh09gNnILAm7a8q5+FmgBYpg==", "dev": true, "requires": { "@fastify/busboy": "^1.1.0", diff --git a/spec/fixtures/sources/commonjs-params/index.js b/spec/fixtures/sources/commonjs-params/index.js index d038a2f77..5c783dd18 100644 --- a/spec/fixtures/sources/commonjs-params/index.js +++ b/spec/fixtures/sources/commonjs-params/index.js @@ -1,8 +1,16 @@ const functions = require("../../../../src/index"); const functionsv2 = require("../../../../src/v2/index"); -const { defineString } = require("../../../../src/v2/params"); +const params = require("../../../../src/v2/params"); -defineString("FOO"); +params.defineString("BORING"); +params.defineString("FOO", {input: {text: {validationRegex:"\w+"}}}); +params.defineString("BAR", {default: "{{ params.FOO }}", label: "asdf"}); +params.defineString("BAZ", {input: {select: {options: [{value: "a"}, {value: "b"}]}}}); + +params.defineInt("AN_INT", {default: 22}); +params.defineInt("ANOTHER_INT", {input: {select: {options:[{label: "a", value: -2}, {"label": "b", value: 2}]}}}); + +params.defineSecret("SUPER_SECRET_FLAG") exports.v1http = functions.https.onRequest((req, resp) => { resp.status(200).send("PASS"); diff --git a/spec/runtime/loader.spec.ts b/spec/runtime/loader.spec.ts index bb959a3cb..caae16d14 100644 --- a/spec/runtime/loader.spec.ts +++ b/spec/runtime/loader.spec.ts @@ -8,6 +8,7 @@ import { ManifestRequiredAPI, ManifestStack, } from '../../src/runtime/manifest'; +import { clearParams } from '../../src/v2/params'; describe('extractStack', () => { const httpFn = functions.https.onRequest(() => {}); @@ -104,6 +105,7 @@ describe('extractStack', () => { afterEach(() => { process.env.GCLOUD_PROJECT = prev; + clearParams(); }); it('extracts stack from a simple module', () => { @@ -313,7 +315,44 @@ describe('loadStack', () => { { name: 'has params', modulePath: './spec/fixtures/sources/commonjs-params', - expected: { ...expected, params: [{ name: 'FOO', type: 'string' }] }, + expected: { + ...expected, + params: [ + { name: 'BORING', type: 'string' }, + { + name: 'FOO', + type: 'string', + input: { text: { validationRegex: 'w+' } }, + }, + { + name: 'BAR', + type: 'string', + default: '{{ params.FOO }}', + label: 'asdf', + }, + { + name: 'BAZ', + type: 'string', + input: { + select: { options: [{ value: 'a' }, { value: 'b' }] }, + }, + }, + { name: 'AN_INT', type: 'int', default: 22 }, + { + name: 'ANOTHER_INT', + type: 'int', + input: { + select: { + options: [ + { label: 'a', value: -2 }, + { label: 'b', value: 2 }, + ], + }, + }, + }, + { name: 'SUPER_SECRET_FLAG', type: 'secret' }, + ], + }, }, ]; diff --git a/spec/runtime/manifest.spec.ts b/spec/runtime/manifest.spec.ts new file mode 100644 index 000000000..7710c54a6 --- /dev/null +++ b/spec/runtime/manifest.spec.ts @@ -0,0 +1,67 @@ +import { stackToWire, ManifestStack } from '../../src/runtime/manifest'; +import { expect } from 'chai'; +import * as params from '../../src/v2/params'; + +describe('stackToWire', () => { + afterEach(() => { + params.clearParams(); + }); + + it('converts Expression types in endpoint options to CEL', () => { + const intParam = params.defineInt('foo', { default: 11 }); + const stringParam = params.defineString('bar', { + default: 'America/Los_Angeles', + }); + + const stack: ManifestStack = { + endpoints: { + v2http: { + platform: 'gcfv2', + entryPoint: 'v2http', + labels: {}, + httpsTrigger: {}, + concurrency: intParam, + maxInstances: intParam.equals(24).then(-1, 1), + }, + v2schedule: { + platform: 'gcfv2', + entryPoint: 'v2callable', + labels: {}, + scheduleTrigger: { + schedule: stringParam + .equals('America/Mexico_City') + .then('mexico', 'usa'), + timeZone: stringParam, + }, + }, + }, + requiredAPIs: [], + specVersion: 'v1alpha1', + }; + const expected = { + endpoints: { + v2http: { + platform: 'gcfv2', + entryPoint: 'v2http', + labels: {}, + httpsTrigger: {}, + concurrency: '{{ params.foo }}', + maxInstances: '{{ params.foo == 24 ? -1 : 1 }}', + }, + v2schedule: { + platform: 'gcfv2', + entryPoint: 'v2callable', + labels: {}, + scheduleTrigger: { + schedule: + '{{ params.bar == "America/Mexico_City" ? "mexico" : "usa" }}', + timeZone: '{{ params.bar }}', + }, + }, + }, + requiredAPIs: [], + specVersion: 'v1alpha1', + }; + expect(stackToWire(stack)).to.deep.equal(expected); + }); +}); diff --git a/spec/v2/params.spec.ts b/spec/v2/params.spec.ts deleted file mode 100644 index 9885276cd..000000000 --- a/spec/v2/params.spec.ts +++ /dev/null @@ -1,276 +0,0 @@ -import { expect } from 'chai'; -import { - declaredParams, - defineBoolean, - defineFloat, - defineInt, - defineJSON, - defineList, - defineString, -} from '../../src/v2/params'; -import { ListParam, Param, ParamOptions } from '../../src/v2/params/types'; - -const TEST_PARAM = 'TEST_PARAM'; - -describe('params', () => { - beforeEach(() => { - delete process.env[TEST_PARAM]; - }); - - const VALUE_TESTS: Array<{ - method: (name: string, options: ParamOptions) => Param; - tests: Array<{ - title: string; - env?: string; - options?: ParamOptions; - expect?: any; - throws?: boolean; - }>; - }> = [ - { - method: defineString, - tests: [ - { - title: "should return a '' zero value when no value and no undefined", - expect: '', - }, - { - title: 'should return the default when no value and one is provided', - env: undefined, - options: { default: 'test_val' }, - expect: 'test_val', - }, - { - title: 'should return the value over the default', - env: 'expected_val', - options: { default: 'default_val' }, - expect: 'expected_val', - }, - ], - }, - { - method: defineInt, - tests: [ - { - title: 'should return 0 zero value', - expect: 0, - }, - { - title: 'should coerce a matching string into an int', - env: '25', - expect: 25, - }, - { - title: 'should return a matching default value', - options: { default: 10 }, - expect: 10, - }, - { - title: 'should throw when invalid value is passed', - env: 'not_a_number', - throws: true, - }, - ], - }, - { - method: defineFloat, - tests: [ - { - title: 'should return 0 zero value', - expect: 0, - }, - { - title: 'should coerce a matching string into a float', - env: '3.14', - expect: 3.14, - }, - { - title: 'should return a matching default value', - options: { default: 10.1 }, - expect: 10.1, - }, - { - title: 'should throw when invalid value is passed', - env: 'not_a_number', - throws: true, - }, - ], - }, - { - method: defineBoolean, - tests: [ - { - title: 'should return false zero value', - expect: false, - }, - { - title: 'should coerce "true" into true', - env: 'true', - expect: true, - }, - { - title: 'should coerce "TrUe" into true', - env: 'TrUe', - expect: true, - }, - { - title: 'should coerce "yes" into true', - env: 'yes', - expect: true, - }, - { - title: 'should coerce "1" into true', - env: '1', - expect: true, - }, - { - title: 'should coerce "false" into false', - env: 'false', - options: { default: true }, - expect: false, - }, - { - title: 'should coerce "FaLsE" into false', - env: 'FaLsE', - options: { default: true }, - expect: false, - }, - { - title: 'should coerce "no" into false', - env: 'no', - options: { default: true }, - expect: false, - }, - { - title: 'should coerce "0" into false', - env: '0', - options: { default: true }, - expect: false, - }, - { - title: 'should return a matching default value', - options: { default: true }, - expect: true, - }, - { - title: 'should error with non-true/false value', - env: 'foo', - throws: true, - }, - ], - }, - { - method: defineList, - tests: [ - { - title: 'should return [] zero value', - expect: [], - }, - { - title: 'should coerce comma-separated values into a string list', - env: 'first,second,third', - expect: ['first', 'second', 'third'], - }, - { - title: - 'should coerce a comma-and-space-separated values into a string list', - env: 'first, second, third', - expect: ['first', 'second', 'third'], - }, - { - title: 'should return a matching default value', - options: { default: ['a', 'b', 'c'] }, - expect: ['a', 'b', 'c'], - }, - ], - }, - { - method: defineJSON, - tests: [ - { - title: 'should return {} zero value', - expect: {}, - }, - { - title: 'should coerce objects from JSON', - env: '{"test":123}', - expect: { test: 123 }, - }, - { - title: 'should coerce arrays from JSON', - env: '["test",123]', - expect: ['test', 123], - }, - { - title: 'should return a matching default value', - options: { default: { test: 123 } }, - expect: { test: 123 }, - }, - { - title: 'should throw with invalid JSON', - env: '{"invalid":json', - throws: true, - }, - ], - }, - ]; - - for (const group of VALUE_TESTS) { - describe(`${group.method.name}().value`, () => { - for (const test of group.tests) { - it(test.title, () => { - if (typeof test.env !== 'undefined') { - process.env[TEST_PARAM] = test.env; - } - - if (test.throws) { - expect( - () => group.method(TEST_PARAM, test.options).value - ).to.throw(); - } else { - expect(group.method(TEST_PARAM, test.options).value).to.deep.eq( - test.expect - ); - } - }); - } - }); - } - - describe('Param', () => { - describe('#toSpec()', () => { - it('should cast non-string defaults to strings', () => { - expect(new Param(TEST_PARAM, { default: 123 }).toSpec().default).to.eq( - '123' - ); - }); - - it('should passthrough supplied options', () => { - expect( - new Param(TEST_PARAM, { description: 'hello expect' }).toSpec() - .description - ).to.eq('hello expect'); - }); - - it('ListParam should properly stringify its default', () => { - const spec = new ListParam(TEST_PARAM, { - default: ['a', 'b', 'c'], - }).toSpec(); - expect(spec.default).to.eq('a,b,c'); - }); - }); - }); - - it('should add a param to the declared params', () => { - const param = defineString(TEST_PARAM); - expect(declaredParams.find((p) => p === param)).to.eq(param); - }); - - it('should replace a samed-name param in the declared params', () => { - const oldParam = defineString(TEST_PARAM); - expect(declaredParams.find((p) => p === oldParam)).to.eq(oldParam); - const param = defineString(TEST_PARAM); - expect(declaredParams.find((p) => p === oldParam)).to.be.undefined; - expect(declaredParams.find((p) => p === param)).to.eq(param); - }); -}); diff --git a/src/bin/firebase-functions.ts b/src/bin/firebase-functions.ts index 646433fec..1be486b8c 100644 --- a/src/bin/firebase-functions.ts +++ b/src/bin/firebase-functions.ts @@ -24,6 +24,7 @@ import * as express from 'express'; import { loadStack } from '../runtime/loader'; +import { stackToWire } from '../runtime/manifest'; function printUsageAndExit() { console.error( @@ -63,7 +64,7 @@ if (process.env.FUNCTIONS_CONTROL_API === 'true') { try { const stack = await loadStack(functionsDir); res.setHeader('content-type', 'text/yaml'); - res.send(JSON.stringify(stack)); + res.send(JSON.stringify(stackToWire(stack))); } catch (e) { res .status(400) diff --git a/src/common/providers/tasks.ts b/src/common/providers/tasks.ts index 52360af48..814087f01 100644 --- a/src/common/providers/tasks.ts +++ b/src/common/providers/tasks.ts @@ -24,6 +24,7 @@ import * as express from 'express'; import * as firebase from 'firebase-admin'; import * as logger from '../../logger'; +import { Expression } from '../../v2/params'; import * as https from './https'; /** How a task should be retried in the event of a non-2xx return. */ @@ -32,31 +33,31 @@ export interface RetryConfig { * Maximum number of times a request should be attempted. * If left unspecified, will default to 3. */ - maxAttempts?: number; + maxAttempts?: number | Expression | null; /** * Maximum amount of time for retrying failed task. * If left unspecified will retry indefinitely. */ - maxRetrySeconds?: number; + maxRetrySeconds?: number | Expression | null; /** * The maximum amount of time to wait between attempts. * If left unspecified will default to 1hr. */ - maxBackoffSeconds?: number; + maxBackoffSeconds?: number | Expression | null; /** * The maximum number of times to double the backoff between * retries. If left unspecified will default to 16. */ - maxDoublings?: number; + maxDoublings?: number | Expression | null; /** * The minimum time to wait between attempts. If left unspecified * will default to 100ms. */ - minBackoffSeconds?: number; + minBackoffSeconds?: number | Expression | null; } /** How congestion control should be applied to the function. */ @@ -65,13 +66,13 @@ export interface RateLimits { * The maximum number of requests that can be outstanding at a time. * If left unspecified, will default to 1000. */ - maxConcurrentDispatches?: number; + maxConcurrentDispatches?: number | Expression | null; /** * The maximum number of requests that can be invoked per second. * If left unspecified, will default to 500. */ - maxDispatchesPerSecond?: number; + maxDispatchesPerSecond?: number | Expression | null; } /** Metadata about the authorization used to invoke a function. */ diff --git a/src/function-configuration.ts b/src/function-configuration.ts index 73cc64eac..6a352e880 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -1,3 +1,5 @@ +import { Expression } from './v2/params'; + /** * List of all regions supported by Cloud Functions. */ @@ -72,11 +74,11 @@ export const INGRESS_SETTINGS_OPTIONS = [ * Scheduler retry options. Applies only to scheduled functions. */ export interface ScheduleRetryConfig { - retryCount?: number; - maxRetryDuration?: string; - minBackoffDuration?: string; - maxBackoffDuration?: string; - maxDoublings?: number; + retryCount?: number | Expression | null; + maxRetryDuration?: string | Expression | null; + minBackoffDuration?: string | Expression | null; + maxBackoffDuration?: string | Expression | null; + maxDoublings?: number | Expression | null; } /** diff --git a/src/runtime/manifest.ts b/src/runtime/manifest.ts index f3548c6d9..062966985 100644 --- a/src/runtime/manifest.ts +++ b/src/runtime/manifest.ts @@ -20,6 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +import { Expression } from '../v2/params'; import { ParamSpec } from '../v2/params/types'; /** @@ -29,15 +30,15 @@ export interface ManifestEndpoint { entryPoint?: string; region?: string[]; platform?: string; - availableMemoryMb?: number; - maxInstances?: number; - minInstances?: number; - concurrency?: number; + availableMemoryMb?: number | Expression; + maxInstances?: number | Expression; + minInstances?: number | Expression; + concurrency?: number | Expression; serviceAccountEmail?: string; - timeoutSeconds?: number; + timeoutSeconds?: number | Expression; cpu?: number | 'gcf_gen1'; vpc?: { - connector: string; + connector: string | Expression; egressSettings?: string; }; labels?: Record; @@ -52,24 +53,24 @@ export interface ManifestEndpoint { callableTrigger?: {}; eventTrigger?: { - eventFilters: Record; - eventFilterPathPatterns?: Record; + eventFilters: Record>; + eventFilterPathPatterns?: Record>; channel?: string; eventType: string; - retry: boolean; + retry: boolean | Expression; region?: string; serviceAccountEmail?: string; }; scheduleTrigger?: { - schedule?: string; - timeZone?: string; + schedule?: string | Expression; + timeZone?: string | Expression; retryConfig?: { - retryCount?: number; - maxRetrySeconds?: number; - minBackoffSeconds?: number; - maxBackoffSeconds?: number; - maxDoublings?: number; + retryCount?: number | Expression; + maxRetrySeconds?: string | Expression; + minBackoffSeconds?: string | Expression; + maxBackoffSeconds?: string | Expression; + maxDoublings?: number | Expression; }; }; @@ -93,3 +94,24 @@ export interface ManifestStack { requiredAPIs: ManifestRequiredAPI[]; endpoints: Record; } + +/** + * Returns the JSON representation of a ManifestStack, which has CEL + * expressions in its options as object types, with its expressions + * transformed into the actual CEL strings. + * @internal + */ +export function stackToWire(stack: ManifestStack): Object { + let wireStack = stack as any; + let traverse = function traverse(obj: Object) { + for (const [key, val] of Object.entries(obj)) { + if (val instanceof Expression) { + obj[key] = val.toCEL(); + } else if (typeof val === 'object') { + traverse(val); + } + } + }; + traverse(wireStack.endpoints); + return wireStack; +} diff --git a/src/v2/options.ts b/src/v2/options.ts index c2ebf5248..b8a5a5dcd 100644 --- a/src/v2/options.ts +++ b/src/v2/options.ts @@ -34,7 +34,7 @@ import { import * as logger from '../logger'; import { ManifestEndpoint } from '../runtime/manifest'; import { TriggerAnnotation } from './core'; -import { declaredParams } from './params'; +import { declaredParams, Expression } from './params'; import { ParamSpec } from './params/types'; import { HttpsOptions } from './providers/https'; @@ -104,7 +104,7 @@ export interface GlobalOptions { * Amount of memory to allocate to a function. * A value of null restores the defaults of 256MB. */ - memory?: MemoryOption | null; + memory?: MemoryOption | Expression | null; /** * Timeout for the function in sections, possible values are 0 to 540. @@ -116,7 +116,7 @@ export interface GlobalOptions { * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum * timeout of 1,800s (30 minutes) */ - timeoutSeconds?: number | null; + timeoutSeconds?: number | Expression | null; /** * Min number of actual instances to be running at a given time. @@ -124,13 +124,13 @@ export interface GlobalOptions { * while idle. * A value of null restores the default min instances. */ - minInstances?: number | null; + minInstances?: number | Expression | null; /** * Max number of instances to be running in parallel. * A value of null restores the default max instances. */ - maxInstances?: number | null; + maxInstances?: number | Expression | null; /** * Number of requests a function can serve at once. @@ -139,7 +139,7 @@ export interface GlobalOptions { * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. * The maximum value for concurrency is 1,000. */ - concurrency?: number | null; + concurrency?: number | Expression | null; /** * Fractional number of CPUs to allocate to a function. @@ -217,8 +217,23 @@ export function getGlobalOptions(): GlobalOptions { * Additional fields that can be set on any event-handling Cloud Function. */ export interface EventHandlerOptions extends GlobalOptions { + eventType?: string; + + eventFilters?: Record>; + eventFilterPathPatterns?: Record>; + /** Whether failed executions should be delivered again. */ - retry?: boolean; + retry?: boolean | Expression | null; + + /** Region of the EventArc trigger. */ + //region?: string | Expression | null; + region?: string; + + /** The service account that EventArc should use to invoke this function. Requires the P4SA to have ActAs permission on this service account. */ + serviceAccount?: string | null; + + /** The name of the channel where the function receives events. */ + channel?: string; } /** @@ -246,8 +261,8 @@ export function optionsToTriggerAnnotations( opts, 'availableMemoryMb', 'memory', - (mem: MemoryOption) => { - return MemoryOptionToMB[mem]; + (mem: MemoryOption | Expression): number | Expression => { + return typeof mem === 'object' ? mem : MemoryOptionToMB[mem]; } ); convertIfPresent(annotation, opts, 'regions', 'region', (region) => { @@ -308,9 +323,15 @@ export function optionsToEndpoint( convertIfPresent(vpc, opts, 'egressSettings', 'vpcConnectorEgressSettings'); endpoint.vpc = vpc; } - convertIfPresent(endpoint, opts, 'availableMemoryMb', 'memory', (mem) => { - return MemoryOptionToMB[mem]; - }); + convertIfPresent( + endpoint, + opts, + 'availableMemoryMb', + 'memory', + (mem: MemoryOption | Expression): number | Expression => { + return typeof mem === 'object' ? mem : MemoryOptionToMB[mem]; + } + ); convertIfPresent(endpoint, opts, 'region', 'region', (region) => { if (typeof region === 'string') { return [region]; diff --git a/src/v2/params/index.ts b/src/v2/params/index.ts index 3f8a080b6..67080f308 100644 --- a/src/v2/params/index.ts +++ b/src/v2/params/index.ts @@ -29,23 +29,25 @@ import { BooleanParam, FloatParam, IntParam, - JSONParam, ListParam, Param, ParamOptions, StringParam, + SecretParam, + Expression, } from './types'; -export { ParamOptions }; +export { ParamOptions, Expression }; -export const declaredParams: Param[] = []; +type SecretOrExpr = Param | SecretParam; +export const declaredParams: SecretOrExpr[] = []; /** * Use a helper to manage the list such that params are uniquely * registered once only but order is preserved. * @internal */ -function registerParam(param: Param) { +function registerParam(param: SecretOrExpr) { for (let i = 0; i < declaredParams.length; i++) { if (declaredParams[i].name === param.name) { declaredParams.splice(i, 1); @@ -54,6 +56,29 @@ function registerParam(param: Param) { declaredParams.push(param); } +/** + * For testing. + * @internal + */ +export function clearParams() { + declaredParams.splice(0, declaredParams.length); +} + +/** + * Declares a secret param, that will persist values only in Cloud Secret Manager. + * Secrets are stored interally as bytestrings. Use ParamOptions.`as` to provide type + * hinting during parameter resolution. + * + * @param name The name of the environment variable to use to load the param. + * @param options Configuration options for the param. + * @returns A Param with a `string` return type for `.value`. + */ +export function defineSecret(name: string): SecretParam { + const param = new SecretParam(name); + registerParam(param); + return param; +} + /** * Declare a string param. * @@ -133,20 +158,3 @@ export function defineList( registerParam(param); return param; } - -/** - * Declare a JSON param. The associated environment variable will be treated - * as a JSON string when loading its value. - * - * @param name The name of the environment variable to use to load the param. - * @param options Configuration options for the param. - * @returns A Param with a specifiable return type for `.value`. - */ -export function defineJSON( - name: string, - options: ParamOptions = {} -): JSONParam { - const param = new JSONParam(name, options); - registerParam(param); - return param; -} diff --git a/src/v2/params/types.ts b/src/v2/params/types.ts index fc898c15c..16d194fda 100644 --- a/src/v2/params/types.ts +++ b/src/v2/params/types.ts @@ -20,129 +20,266 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +/* + * A CEL expression which can be evaluated during function deployment, and + * resolved to a value of the generic type parameter: i.e, you can pass + * an Expression as the value of an option that normally accepts numbers. + */ +export abstract class Expression< + T extends string | number | boolean | string[] +> { + // Returns the Expression's runtime value, based on the CLI's resolution of params. + value(): T { + throw new Error('Not implemented'); + } + + // Returns the Expression's representation as a braced CEL expression. + toCEL(): string { + return `{{ ${this.toString()} }}`; + } + + toJSON(): string { + return this.toString(); + } +} + +function quoteIfString( + literal: T +): T { + //TODO(vsfan@): CEL's string escape semantics are slightly different than Javascript's, what do we do here? + return typeof literal === 'string' ? (`"${literal}"` as T) : literal; +} + +/** + * A CEL expression corresponding to a ternary operator, e.g {{ cond ? ifTrue : ifFalse }} + */ +export class TernaryExpression< + T extends string | number | boolean | string[] +> extends Expression { + constructor( + private readonly test: Expression, + private readonly ifTrue: T, + private readonly ifFalse: T + ) { + super(); + this.ifTrue = ifTrue; + this.ifFalse = ifFalse; + } + + value(): T { + return !!this.test.value ? this.ifTrue : this.ifFalse; + } + + toString() { + return `${this.test} ? ${quoteIfString(this.ifTrue)} : ${quoteIfString( + this.ifFalse + )}`; + } +} + +/** + * A CEL expression that evaluates to boolean true or false based on a comparison + * between the value of another expression and a literal of that same type. + */ +export class CompareExpression< + T extends string | number | boolean | string[] +> extends Expression { + cmp: '==' | '>' | '>=' | '<' | '<='; + lhs: Expression; + rhs: T; + + constructor(cmp: '==' | '>' | '>=' | '<' | '<=', lhs: Expression, rhs: T) { + super(); + this.cmp = cmp; + this.lhs = lhs; + this.rhs = rhs; + } + + value(): boolean { + const left = this.lhs.value(); + switch (this.cmp) { + case '==': + return left === this.rhs; + case '>': + return left > this.rhs; + case '>=': + return left >= this.rhs; + case '<': + return left < this.rhs; + case '<=': + return left <= this.rhs; + default: + throw new Error('Unknown comparator ' + this.cmp); + } + } + + toString() { + return `${this.lhs} ${this.cmp} ${quoteIfString(this.rhs)}`; + } + + then(ifTrue: T, ifFalse: T) { + return new TernaryExpression(this, ifTrue, ifFalse); + } +} + /** @hidden */ -type ParamValueType = 'string' | 'list' | 'boolean' | 'int' | 'float' | 'json'; +type ParamValueType = + | 'string' + | 'list' + | 'boolean' + | 'int' + | 'float' + | 'secret'; + +type ParamInput = + | { text: TextInput } + | { select: SelectInput } + | { resource: ResourceInput }; + +/** + * Specifies that a Param's value should be determined by prompting the user + * to type it in interactively at deploy-time. Input that does not match the + * provided validationRegex, if present, will be retried. + */ +export interface TextInput { + example?: string; + validationRegex?: string; + validationErrorMessage?: string; +} + +/** + * Specifies that a Param's value should be determined by having the user + * select from a list containing all the project's resources of a certain + * type. Currently, only type:"storage.googleapis.com/Bucket" is supported. + */ +export interface ResourceInput { + resource: { + type: string; + }; +} -export interface ParamSpec { +/** + * Specifies that a Param's value should be determined by having the user select + * from a list of pre-canned options interactively at deploy-time. + */ +export interface SelectInput { + options: Array>; +} + +export interface SelectOptions { + label?: string; + value: T; +} + +export type ParamSpec = { name: string; default?: T; label?: string; description?: string; type: ParamValueType; -} + input?: ParamInput; +}; export type ParamOptions = Omit, 'name' | 'type'>; -export class Param { +export abstract class Param< + T extends string | number | boolean | string[] +> extends Expression { static type: ParamValueType = 'string'; - constructor(readonly name: string, readonly options: ParamOptions = {}) {} + constructor(readonly name: string, readonly options: ParamOptions = {}) { + super(); + } - get rawValue(): string | undefined { - return process.env[this.name]; + value(): T { + throw new Error('Not implemented'); } - get value(): any { - return this.rawValue || this.options.default || ''; + cmp(cmp: '==' | '>' | '>=' | '<' | '<=', rhs: T) { + return new CompareExpression(cmp, this, rhs); } - toString() { - return `{{params.${this.name}}}`; + equals(rhs: T) { + return this.cmp('==', rhs); } - toJSON() { - return this.toString(); + toString(): string { + return `params.${this.name}`; } - toSpec(): ParamSpec { + toSpec(): ParamSpec { const out: ParamSpec = { name: this.name, ...this.options, type: (this.constructor as typeof Param).type, }; - if (this.options.default && typeof this.options.default !== 'string') { - out.default = (this.options.default as - | { toString?: () => string } - | undefined)?.toString?.(); - } - return out as ParamSpec; + return out as ParamSpec; + } +} + +export class SecretParam { + name: string; + static type: ParamValueType = 'secret'; + + constructor(name: string) { + this.name = name; + } + + value(): string { + return process.env[this.name] || ''; + } + + toSpec(): ParamSpec { + return { + type: 'secret', + name: this.name, + }; } } export class StringParam extends Param { - // identical to the abstract class, just explicitly a string + value(): string { + return process.env[this.name] || ''; + } } export class IntParam extends Param { static type: ParamValueType = 'int'; - get value(): number { - const intVal = parseInt( - this.rawValue || this.options.default?.toString() || '0', - 10 - ); - if (Number.isNaN(intVal)) { - throw new Error( - `unable to load param "${this.name}", value ${JSON.stringify( - this.rawValue - )} could not be parsed as integer` - ); - } - return intVal; + value(): number { + return parseInt(process.env[this.name] || '0', 10) || 0; } } export class FloatParam extends Param { static type: ParamValueType = 'float'; - get value(): number { - const floatVal = parseFloat( - this.rawValue || this.options.default?.toString() || '0' - ); - if (Number.isNaN(floatVal)) { - throw new Error( - `unable to load param "${this.name}", value ${JSON.stringify( - this.rawValue - )} could not be parsed as float` - ); - } - return floatVal; + value(): number { + return parseFloat(process.env[this.name] || '0') || 0; } } -export class BooleanParam extends Param { +export class BooleanParam extends Param { static type: ParamValueType = 'boolean'; - get value(): boolean { - const lowerVal = ( - this.rawValue || - this.options.default?.toString() || - 'false' - ).toLowerCase(); - if ( - !['true', 'y', 'yes', '1', 'false', 'n', 'no', '0'].includes(lowerVal) - ) { - throw new Error( - `unable to load param "${this.name}", value ${JSON.stringify( - this.rawValue - )} could not be parsed as boolean` - ); - } - return ['true', 'y', 'yes', '1'].includes(lowerVal); + value(): boolean { + return !!process.env[this.name]; + } + + then(ifTrue: T, ifFalse: T) { + return new TernaryExpression(this, ifTrue, ifFalse); } } export class ListParam extends Param { static type: ParamValueType = 'list'; - get value(): string[] { - return typeof this.rawValue === 'string' - ? this.rawValue.split(/, ?/) - : this.options.default || []; + value(): string[] { + throw new Error('Not implemented'); } - toSpec(): ParamSpec { + toSpec(): ParamSpec { const out: ParamSpec = { name: this.name, type: 'list', @@ -152,25 +289,6 @@ export class ListParam extends Param { out.default = this.options.default.join(','); } - return out as ParamSpec; - } -} - -export class JSONParam extends Param { - static type: ParamValueType = 'json'; - - get value(): T { - if (this.rawValue) { - try { - return JSON.parse(this.rawValue!) as T; - } catch (e) { - throw new Error( - `unable to load param "${this.name}", value ${this.rawValue} could not be parsed as JSON: ${e.message}` - ); - } - } else if (this.options?.hasOwnProperty('default')) { - return this.options.default; - } - return {} as T; + return out as ParamSpec; } } diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index 556b2b77b..6953e44b8 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -23,6 +23,7 @@ import { ManifestEndpoint } from '../../../runtime/manifest'; import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; +import { Expression } from '../../params'; /** * The CloudEvent data emitted by Firebase Alerts. @@ -90,7 +91,7 @@ export interface FirebaseAlertOptions extends options.EventHandlerOptions { * Amount of memory to allocate to a function. * A value of null restores the defaults of 256MB. */ - memory?: options.MemoryOption | null; + memory?: options.MemoryOption | Expression | null; /** * Timeout for the function in sections, possible values are 0 to 540. @@ -102,7 +103,7 @@ export interface FirebaseAlertOptions extends options.EventHandlerOptions { * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum * timeout of 1,800s (30 minutes) */ - timeoutSeconds?: number | null; + timeoutSeconds?: number | Expression | null; /** * Min number of actual instances to be running at a given time. @@ -110,13 +111,13 @@ export interface FirebaseAlertOptions extends options.EventHandlerOptions { * while idle. * A value of null restores the default min instances. */ - minInstances?: number | null; + minInstances?: number | Expression | null; /** * Max number of instances to be running in parallel. * A value of null restores the default max instances. */ - maxInstances?: number | null; + maxInstances?: number | Expression | null; /** * Number of requests a function can serve at once. @@ -125,7 +126,7 @@ export interface FirebaseAlertOptions extends options.EventHandlerOptions { * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. * The maximum value for concurrency is 1,000. */ - concurrency?: number | null; + concurrency?: number | Expression | null; /** * Fractional number of CPUs to allocate to a function. diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index 5b34c7b7c..977af5450 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -28,6 +28,7 @@ import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; import { FirebaseAlertData, getEndpointAnnotation } from './alerts'; +import { Expression } from '../../params'; /** * The internal payload object for adding a new tester device to app distribution. @@ -103,7 +104,7 @@ export interface AppDistributionOptions extends options.EventHandlerOptions { * Amount of memory to allocate to a function. * A value of null restores the defaults of 256MB. */ - memory?: options.MemoryOption | null; + memory?: options.MemoryOption | Expression | null; /** * Timeout for the function in sections, possible values are 0 to 540. @@ -115,7 +116,7 @@ export interface AppDistributionOptions extends options.EventHandlerOptions { * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum * timeout of 1,800s (30 minutes) */ - timeoutSeconds?: number | null; + timeoutSeconds?: number | Expression | null; /** * Min number of actual instances to be running at a given time. @@ -123,13 +124,13 @@ export interface AppDistributionOptions extends options.EventHandlerOptions { * while idle. * A value of null restores the default min instances. */ - minInstances?: number | null; + minInstances?: number | Expression | null; /** * Max number of instances to be running in parallel. * A value of null restores the default max instances. */ - maxInstances?: number | null; + maxInstances?: number | Expression | null; /** * Number of requests a function can serve at once. @@ -138,7 +139,7 @@ export interface AppDistributionOptions extends options.EventHandlerOptions { * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. * The maximum value for concurrency is 1,000. */ - concurrency?: number | null; + concurrency?: number | Expression | null; /** * Fractional number of CPUs to allocate to a function. diff --git a/src/v2/providers/alerts/crashlytics.ts b/src/v2/providers/alerts/crashlytics.ts index 6f814320f..af3d58bdc 100644 --- a/src/v2/providers/alerts/crashlytics.ts +++ b/src/v2/providers/alerts/crashlytics.ts @@ -28,6 +28,7 @@ import { FirebaseAlertData, getEndpointAnnotation } from '.'; import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; +import { Expression } from '../../params'; /** Generic Crashlytics issue interface */ export interface Issue { @@ -182,7 +183,7 @@ export interface CrashlyticsOptions extends options.EventHandlerOptions { * Amount of memory to allocate to a function. * A value of null restores the defaults of 256MB. */ - memory?: options.MemoryOption | null; + memory?: options.MemoryOption | Expression | null; /** * Timeout for the function in sections, possible values are 0 to 540. @@ -194,7 +195,7 @@ export interface CrashlyticsOptions extends options.EventHandlerOptions { * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum * timeout of 1,800s (30 minutes) */ - timeoutSeconds?: number | null; + timeoutSeconds?: number | Expression | null; /** * Min number of actual instances to be running at a given time. @@ -202,13 +203,13 @@ export interface CrashlyticsOptions extends options.EventHandlerOptions { * while idle. * A value of null restores the default min instances. */ - minInstances?: number | null; + minInstances?: number | Expression | null; /** * Max number of instances to be running in parallel. * A value of null restores the default max instances. */ - maxInstances?: number | null; + maxInstances?: number | Expression | null; /** * Number of requests a function can serve at once. @@ -217,7 +218,7 @@ export interface CrashlyticsOptions extends options.EventHandlerOptions { * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. * The maximum value for concurrency is 1,000. */ - concurrency?: number | null; + concurrency?: number | Expression | null; /** * Fractional number of CPUs to allocate to a function. diff --git a/src/v2/providers/database.ts b/src/v2/providers/database.ts index acfdfb76d..2f3127dff 100644 --- a/src/v2/providers/database.ts +++ b/src/v2/providers/database.ts @@ -29,6 +29,7 @@ import { PathPattern } from '../../utilities/path-pattern'; import { applyChange } from '../../utils'; import { CloudEvent, CloudFunction } from '../core'; import * as options from '../options'; +import { Expression } from '../params'; export { DataSnapshot }; @@ -102,7 +103,7 @@ export interface ReferenceOptions extends options.EventHandlerOptions { * Amount of memory to allocate to a function. * A value of null restores the defaults of 256MB. */ - memory?: options.MemoryOption | null; + memory?: options.MemoryOption | Expression | null; /** * Timeout for the function in sections, possible values are 0 to 540. @@ -114,7 +115,7 @@ export interface ReferenceOptions extends options.EventHandlerOptions { * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum * timeout of 1,800s (30 minutes) */ - timeoutSeconds?: number | null; + timeoutSeconds?: number | Expression | null; /** * Min number of actual instances to be running at a given time. @@ -122,13 +123,13 @@ export interface ReferenceOptions extends options.EventHandlerOptions { * while idle. * A value of null restores the default min instances. */ - minInstances?: number | null; + minInstances?: number | Expression | null; /** * Max number of instances to be running in parallel. * A value of null restores the default max instances. */ - maxInstances?: number | null; + maxInstances?: number | Expression | null; /** * Number of requests a function can serve at once. @@ -137,7 +138,7 @@ export interface ReferenceOptions extends options.EventHandlerOptions { * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. * The maximum value for concurrency is 1,000. */ - concurrency?: number | null; + concurrency?: number | Expression | null; /** * Fractional number of CPUs to allocate to a function. @@ -184,7 +185,7 @@ export interface ReferenceOptions extends options.EventHandlerOptions { secrets?: string[]; /** Whether failed executions should be delivered again. */ - retry?: boolean; + retry?: boolean | Expression | null; } /** diff --git a/src/v2/providers/eventarc.ts b/src/v2/providers/eventarc.ts index fed753864..71e4b0174 100644 --- a/src/v2/providers/eventarc.ts +++ b/src/v2/providers/eventarc.ts @@ -29,6 +29,7 @@ import { convertIfPresent, copyIfPresent } from '../../common/encoding'; import { ManifestEndpoint } from '../../runtime/manifest'; import { CloudEvent, CloudFunction } from '../core'; import * as options from '../options'; +import { Expression } from '../params'; /** Options that can be set on an Eventarc trigger. */ export interface EventarcTriggerOptions extends options.EventHandlerOptions { @@ -67,7 +68,7 @@ export interface EventarcTriggerOptions extends options.EventHandlerOptions { * Amount of memory to allocate to a function. * A value of null restores the defaults of 256MB. */ - memory?: options.MemoryOption | null; + memory?: options.MemoryOption | Expression | null; /** * Timeout for the function in sections, possible values are 0 to 540. @@ -79,7 +80,7 @@ export interface EventarcTriggerOptions extends options.EventHandlerOptions { * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum * timeout of 1,800s (30 minutes) */ - timeoutSeconds?: number | null; + timeoutSeconds?: number | Expression | null; /** * Min number of actual instances to be running at a given time. @@ -87,13 +88,13 @@ export interface EventarcTriggerOptions extends options.EventHandlerOptions { * while idle. * A value of null restores the default min instances. */ - minInstances?: number | null; + minInstances?: number | Expression | null; /** * Max number of instances to be running in parallel. * A value of null restores the default max instances. */ - maxInstances?: number | null; + maxInstances?: number | Expression | null; /** * Number of requests a function can serve at once. @@ -102,7 +103,7 @@ export interface EventarcTriggerOptions extends options.EventHandlerOptions { * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. * The maximum value for concurrency is 1,000. */ - concurrency?: number | null; + concurrency?: number | Expression | null; /** * Fractional number of CPUs to allocate to a function. diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index dad7355cd..0ab9c531c 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -40,6 +40,7 @@ import { import { ManifestEndpoint } from '../../runtime/manifest'; import * as options from '../options'; import { GlobalOptions, SupportedRegion } from '../options'; +import { Expression } from '../params'; export { Request, CallableRequest, FunctionsErrorCode, HttpsError }; @@ -60,7 +61,7 @@ export interface HttpsOptions extends Omit { * Amount of memory to allocate to a function. * A value of null restores the defaults of 256MB. */ - memory?: options.MemoryOption | null; + memory?: options.MemoryOption | Expression | null; /** * Timeout for the function in sections, possible values are 0 to 540. @@ -72,7 +73,7 @@ export interface HttpsOptions extends Omit { * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum * timeout of 1,800s (30 minutes) */ - timeoutSeconds?: number | null; + timeoutSeconds?: number | Expression | null; /** * Min number of actual instances to be running at a given time. @@ -80,13 +81,13 @@ export interface HttpsOptions extends Omit { * while idle. * A value of null restores the default min instances. */ - minInstances?: number | null; + minInstances?: number | Expression | null; /** * Max number of instances to be running in parallel. * A value of null restores the default max instances. */ - maxInstances?: number | null; + maxInstances?: number | Expression | null; /** * Number of requests a function can serve at once. @@ -95,7 +96,7 @@ export interface HttpsOptions extends Omit { * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. * The maximum value for concurrency is 1,000. */ - concurrency?: number | null; + concurrency?: number | Expression | null; /** * Fractional number of CPUs to allocate to a function. diff --git a/src/v2/providers/identity.ts b/src/v2/providers/identity.ts index eb03c747b..ed823dd0f 100644 --- a/src/v2/providers/identity.ts +++ b/src/v2/providers/identity.ts @@ -36,6 +36,7 @@ import { wrapHandler, } from '../../common/providers/identity'; import * as options from '../options'; +import { Expression } from '../params'; export { AuthUserRecord, AuthBlockingEvent, HttpsError }; @@ -69,7 +70,7 @@ export interface BlockingOptions { * Amount of memory to allocate to a function. * A value of null restores the defaults of 256MB. */ - memory?: options.MemoryOption | null; + memory?: options.MemoryOption | Expression | null; /** * Timeout for the function in sections, possible values are 0 to 540. @@ -81,7 +82,7 @@ export interface BlockingOptions { * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum * timeout of 1,800s (30 minutes) */ - timeoutSeconds?: number | null; + timeoutSeconds?: number | Expression | null; /** * Min number of actual instances to be running at a given time. @@ -89,13 +90,13 @@ export interface BlockingOptions { * while idle. * A value of null restores the default min instances. */ - minInstances?: number | null; + minInstances?: number | Expression | null; /** * Max number of instances to be running in parallel. * A value of null restores the default max instances. */ - maxInstances?: number | null; + maxInstances?: number | Expression | null; /** * Number of requests a function can serve at once. @@ -104,7 +105,7 @@ export interface BlockingOptions { * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. * The maximum value for concurrency is 1,000. */ - concurrency?: number | null; + concurrency?: number | Expression | null; /** * Fractional number of CPUs to allocate to a function. diff --git a/src/v2/providers/pubsub.ts b/src/v2/providers/pubsub.ts index 380ed6946..6f04f5cec 100644 --- a/src/v2/providers/pubsub.ts +++ b/src/v2/providers/pubsub.ts @@ -29,6 +29,7 @@ import { copyIfPresent } from '../../common/encoding'; import { ManifestEndpoint } from '../../runtime/manifest'; import { CloudEvent, CloudFunction } from '../core'; import * as options from '../options'; +import { Expression } from '../params'; /** * Google Cloud Pub/Sub is a globally distributed message bus that automatically scales as you need it. @@ -164,7 +165,7 @@ export interface PubSubOptions extends options.EventHandlerOptions { * Amount of memory to allocate to a function. * A value of null restores the defaults of 256MB. */ - memory?: options.MemoryOption | null; + memory?: options.MemoryOption | Expression | null; /** * Timeout for the function in sections, possible values are 0 to 540. @@ -176,7 +177,7 @@ export interface PubSubOptions extends options.EventHandlerOptions { * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum * timeout of 1,800s (30 minutes) */ - timeoutSeconds?: number | null; + timeoutSeconds?: number | Expression | null; /** * Min number of actual instances to be running at a given time. @@ -184,13 +185,13 @@ export interface PubSubOptions extends options.EventHandlerOptions { * while idle. * A value of null restores the default min instances. */ - minInstances?: number | null; + minInstances?: number | Expression | null; /** * Max number of instances to be running in parallel. * A value of null restores the default max instances. */ - maxInstances?: number | null; + maxInstances?: number | Expression | null; /** * Number of requests a function can serve at once. @@ -199,7 +200,7 @@ export interface PubSubOptions extends options.EventHandlerOptions { * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. * The maximum value for concurrency is 1,000. */ - concurrency?: number | null; + concurrency?: number | Expression | null; /** * Fractional number of CPUs to allocate to a function. diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts index 33281abd1..fd8a2131e 100644 --- a/src/v2/providers/storage.ts +++ b/src/v2/providers/storage.ts @@ -30,6 +30,7 @@ import { firebaseConfig } from '../../config'; import { ManifestEndpoint } from '../../runtime/manifest'; import { CloudEvent, CloudFunction } from '../core'; import * as options from '../options'; +import { Expression } from '../params'; /** * An object within Google Cloud Storage. @@ -209,7 +210,7 @@ export interface StorageOptions extends options.EventHandlerOptions { * Amount of memory to allocate to a function. * A value of null restores the defaults of 256MB. */ - memory?: options.MemoryOption | null; + memory?: options.MemoryOption | Expression | null; /** * Timeout for the function in sections, possible values are 0 to 540. @@ -221,7 +222,7 @@ export interface StorageOptions extends options.EventHandlerOptions { * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum * timeout of 1,800s (30 minutes) */ - timeoutSeconds?: number | null; + timeoutSeconds?: number | Expression | null; /** * Min number of actual instances to be running at a given time. @@ -229,13 +230,13 @@ export interface StorageOptions extends options.EventHandlerOptions { * while idle. * A value of null restores the default min instances. */ - minInstances?: number | null; + minInstances?: number | Expression | null; /** * Max number of instances to be running in parallel. * A value of null restores the default max instances. */ - maxInstances?: number | null; + maxInstances?: number | Expression | null; /** * Number of requests a function can serve at once. @@ -244,7 +245,7 @@ export interface StorageOptions extends options.EventHandlerOptions { * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. * The maximum value for concurrency is 1,000. */ - concurrency?: number | null; + concurrency?: number | Expression | null; /** * Fractional number of CPUs to allocate to a function. diff --git a/src/v2/providers/tasks.ts b/src/v2/providers/tasks.ts index df3b9b8c2..9883aea3e 100644 --- a/src/v2/providers/tasks.ts +++ b/src/v2/providers/tasks.ts @@ -39,8 +39,9 @@ import { } from '../../common/providers/tasks'; import * as options from '../options'; import { HttpsFunction } from './https'; +import { Expression } from '../params'; -export { AuthData, RateLimits, Request, RetryConfig }; +export { AuthData, Request }; export interface TaskQueueOptions extends options.EventHandlerOptions { /** How a task should be retried in the event of a non-2xx return. */ @@ -66,7 +67,7 @@ export interface TaskQueueOptions extends options.EventHandlerOptions { * Amount of memory to allocate to a function. * A value of null restores the defaults of 256MB. */ - memory?: options.MemoryOption | null; + memory?: options.MemoryOption | Expression | null; /** * Timeout for the function in sections, possible values are 0 to 540. @@ -78,7 +79,7 @@ export interface TaskQueueOptions extends options.EventHandlerOptions { * maximum timeout of 36,00s (1 hour). Task queue functions have a maximum * timeout of 1,800s (30 minutes) */ - timeoutSeconds?: number | null; + timeoutSeconds?: number | Expression | null; /** * Min number of actual instances to be running at a given time. @@ -86,13 +87,13 @@ export interface TaskQueueOptions extends options.EventHandlerOptions { * while idle. * A value of null restores the default min instances. */ - minInstances?: number | null; + minInstances?: number | Expression | null; /** * Max number of instances to be running in parallel. * A value of null restores the default max instances. */ - maxInstances?: number | null; + maxInstances?: number | Expression | null; /** * Number of requests a function can serve at once. @@ -101,7 +102,7 @@ export interface TaskQueueOptions extends options.EventHandlerOptions { * Concurrency cannot be set to any value other than 1 if `cpu` is less than 1. * The maximum value for concurrency is 1,000. */ - concurrency?: number | null; + concurrency?: number | Expression | null; /** * Fractional number of CPUs to allocate to a function. From 941704dc5c3662f313f8717f19e10a7294918cc2 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 31 Aug 2022 20:33:26 +0000 Subject: [PATCH 142/370] 3.23.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1f4742d4c..b05fdebe8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-functions", - "version": "3.22.0", + "version": "3.23.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "firebase-functions", - "version": "3.22.0", + "version": "3.23.0", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index 69265b485..5ae1df4b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.22.0", + "version": "3.23.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 7783ae652f9acc09254230780e048d020e341e0b Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 31 Aug 2022 20:33:29 +0000 Subject: [PATCH 143/370] [firebase-release] Removed change log and reset repo after 3.23.0 release --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dd094b29..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +0,0 @@ -- Fixes a bug that disallowed setting customClaims and/or sessionClaims in blocking functions (#1199). -- Add v2 Schedule Triggers (#1177). From c18e832d9250a3ec839b61403f7977792889d451 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Wed, 14 Sep 2022 11:37:43 -0400 Subject: [PATCH 144/370] Adds performance monitoring triggers to v2 alerts (#1223) * rebasing master * add return to app distro * merge changelog * yank trace * add alert type to index * add link into tsdoc * fix get opts * linter * lineup with master * linter * adding logic to convert a payload to api council spec * fix wording of optional fields --- CHANGELOG.md | 1 + package.json | 4 + spec/runtime/manifest.spec.ts | 2 +- spec/v2/providers/alerts/performance.spec.ts | 168 ++++++++++++++++ src/runtime/manifest.ts | 4 +- src/v2/params/index.ts | 4 +- src/v2/params/types.ts | 8 +- src/v2/providers/alerts/alerts.ts | 1 + src/v2/providers/alerts/appDistribution.ts | 2 +- src/v2/providers/alerts/index.ts | 3 +- src/v2/providers/alerts/performance.ts | 197 +++++++++++++++++++ src/v2/providers/tasks.ts | 2 +- v2/alerts/performance.js | 26 +++ 13 files changed, 410 insertions(+), 12 deletions(-) create mode 100644 spec/v2/providers/alerts/performance.spec.ts create mode 100644 src/v2/providers/alerts/performance.ts create mode 100644 v2/alerts/performance.js diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..db12f0f5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Add performance monitoring triggers to v2 alerts (#1223). diff --git a/package.json b/package.json index 5ae1df4b8..a4a9711d5 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "./v2/alerts/appDistribution": "./lib/v2/providers/alerts/appDistribution.js", "./v2/alerts/billing": "./lib/v2/providers/alerts/billing.js", "./v2/alerts/crashlytics": "./lib/v2/providers/alerts/crashlytics.js", + "./v2/alerts/performance": "./lib/v2/providers/alerts/performance.js", "./v2/eventarc": "./lib/v2/providers/eventarc.js", "./v2/identity": "./lib/v2/providers/identity.js", "./v2/database": "./lib/v2/providers/database.js", @@ -125,6 +126,9 @@ "v2/alerts/crashlytics": [ "lib/v2/providers/alerts/crashlytics" ], + "v2/alerts/performance": [ + "lib/v2/providers/alerts/performance" + ], "v2/base": [ "lib/v2/base" ], diff --git a/spec/runtime/manifest.spec.ts b/spec/runtime/manifest.spec.ts index 7710c54a6..96a330914 100644 --- a/spec/runtime/manifest.spec.ts +++ b/spec/runtime/manifest.spec.ts @@ -1,5 +1,5 @@ -import { stackToWire, ManifestStack } from '../../src/runtime/manifest'; import { expect } from 'chai'; +import { ManifestStack, stackToWire } from '../../src/runtime/manifest'; import * as params from '../../src/v2/params'; describe('stackToWire', () => { diff --git a/spec/v2/providers/alerts/performance.spec.ts b/spec/v2/providers/alerts/performance.spec.ts new file mode 100644 index 000000000..99f99ddc7 --- /dev/null +++ b/spec/v2/providers/alerts/performance.spec.ts @@ -0,0 +1,168 @@ +import { expect } from 'chai'; +import * as alerts from '../../../../src/v2/providers/alerts'; +import * as performance from '../../../../src/v2/providers/alerts/performance'; +import { FULL_ENDPOINT, FULL_OPTIONS } from '../fixtures'; + +const APPID = '123456789'; +const myHandler = () => 42; + +const APP_EVENT_FILTER = { + appid: APPID, +}; + +describe('performance', () => { + describe('onThresholdAlertPublished', () => { + it('should create a function with alertType & appId', () => { + const func = performance.onThresholdAlertPublished(APPID, myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + ...APP_EVENT_FILTER, + alerttype: performance.thresholdAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with opts', () => { + const func = performance.onThresholdAlertPublished( + { ...FULL_OPTIONS }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alerttype: performance.thresholdAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with appid in opts', () => { + const func = performance.onThresholdAlertPublished( + { ...FULL_OPTIONS, appId: APPID }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...FULL_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + ...APP_EVENT_FILTER, + alerttype: performance.thresholdAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function without opts or appId', () => { + const func = performance.onThresholdAlertPublished(myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alerttype: performance.thresholdAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with a run method', () => { + const func = performance.onThresholdAlertPublished( + APPID, + (event) => event + ); + + const res = func.run('input' as any); + + expect(res).to.equal('input'); + }); + }); + + describe('getOptsAndApp', () => { + it('should parse a string', () => { + const [opts, appId] = performance.getOptsAndApp(APPID); + + expect(opts).to.deep.equal({}); + expect(appId).to.equal(APPID); + }); + + it('should parse an options object without appId', () => { + const myOpts: performance.PerformanceOptions = { + region: 'us-west1', + }; + + const [opts, appId] = performance.getOptsAndApp(myOpts); + + expect(opts).to.deep.equal({ region: 'us-west1' }); + expect(appId).to.be.undefined; + }); + + it('should parse an options object with appId', () => { + const myOpts: performance.PerformanceOptions = { + appId: APPID, + region: 'us-west1', + }; + + const [opts, appId] = performance.getOptsAndApp(myOpts); + + expect(opts).to.deep.equal({ region: 'us-west1' }); + expect(appId).to.equal(APPID); + }); + }); + + describe('convertPayload', () => { + it('should return the same payload', () => { + const payload = { + a: 'b', + conditionPercentile: 23, + appVersion: '3', + }; + + const convertedPayload = performance.convertPayload(payload as any); + + expect(convertedPayload).to.deep.eq(payload); + }); + + it('should return the same payload if the fields are undefined', () => { + const payload = { + a: 'b', + }; + + const convertedPayload = performance.convertPayload(payload as any); + + expect(convertedPayload).to.deep.eq({ + a: 'b', + }); + }); + + it('should remove fields', () => { + const payload = { + a: 'b', + conditionPercentile: 0, + appVersion: '', + }; + + const convertedPayload = performance.convertPayload(payload as any); + + expect(convertedPayload).to.deep.eq({ + a: 'b', + }); + }); + }); +}); diff --git a/src/runtime/manifest.ts b/src/runtime/manifest.ts index 062966985..d53165420 100644 --- a/src/runtime/manifest.ts +++ b/src/runtime/manifest.ts @@ -102,8 +102,8 @@ export interface ManifestStack { * @internal */ export function stackToWire(stack: ManifestStack): Object { - let wireStack = stack as any; - let traverse = function traverse(obj: Object) { + const wireStack = stack as any; + const traverse = function traverse(obj: Object) { for (const [key, val] of Object.entries(obj)) { if (val instanceof Expression) { obj[key] = val.toCEL(); diff --git a/src/v2/params/index.ts b/src/v2/params/index.ts index 67080f308..b638d68a5 100644 --- a/src/v2/params/index.ts +++ b/src/v2/params/index.ts @@ -27,14 +27,14 @@ import { BooleanParam, + Expression, FloatParam, IntParam, ListParam, Param, ParamOptions, - StringParam, SecretParam, - Expression, + StringParam, } from './types'; export { ParamOptions, Expression }; diff --git a/src/v2/params/types.ts b/src/v2/params/types.ts index 16d194fda..b138150db 100644 --- a/src/v2/params/types.ts +++ b/src/v2/params/types.ts @@ -46,7 +46,7 @@ export abstract class Expression< function quoteIfString( literal: T ): T { - //TODO(vsfan@): CEL's string escape semantics are slightly different than Javascript's, what do we do here? + // TODO(vsfan@): CEL's string escape semantics are slightly different than Javascript's, what do we do here? return typeof literal === 'string' ? (`"${literal}"` as T) : literal; } @@ -171,14 +171,14 @@ export interface SelectOptions { value: T; } -export type ParamSpec = { +export interface ParamSpec { name: string; default?: T; label?: string; description?: string; type: ParamValueType; input?: ParamInput; -}; +} export type ParamOptions = Omit, 'name' | 'type'>; @@ -219,8 +219,8 @@ export abstract class Param< } export class SecretParam { - name: string; static type: ParamValueType = 'secret'; + name: string; constructor(name: string) { this.name = name; diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index 6953e44b8..545e9bbcf 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -70,6 +70,7 @@ export type AlertType = | 'billing.automatedPlanUpdate' | 'appDistribution.newTesterIosDevice' | 'appDistribution.inAppFeedback' + | 'performance.threshold' | string; /** diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index 977af5450..695bfc4df 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -27,8 +27,8 @@ import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; -import { FirebaseAlertData, getEndpointAnnotation } from './alerts'; import { Expression } from '../../params'; +import { FirebaseAlertData, getEndpointAnnotation } from './alerts'; /** * The internal payload object for adding a new tester device to app distribution. diff --git a/src/v2/providers/alerts/index.ts b/src/v2/providers/alerts/index.ts index d16ef00fe..e0988f5b8 100644 --- a/src/v2/providers/alerts/index.ts +++ b/src/v2/providers/alerts/index.ts @@ -30,6 +30,7 @@ import * as appDistribution from './appDistribution'; import * as billing from './billing'; import * as crashlytics from './crashlytics'; +import * as performance from './performance'; -export { appDistribution, billing, crashlytics }; +export { appDistribution, billing, crashlytics, performance }; export * from './alerts'; diff --git a/src/v2/providers/alerts/performance.ts b/src/v2/providers/alerts/performance.ts new file mode 100644 index 000000000..7fa82439f --- /dev/null +++ b/src/v2/providers/alerts/performance.ts @@ -0,0 +1,197 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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. + +/** + * Cloud functions to handle Firebase Performance Monitoring events from Firebase Alerts. + * @packageDocumentation + */ + +import { FirebaseAlertData, getEndpointAnnotation } from '.'; +import { CloudEvent, CloudFunction } from '../../core'; +import { EventHandlerOptions } from '../../options'; + +/** + * The internal payload object for a performance threshold alert. + * Payload is wrapped inside a {@link FirebaseAlertData} object. + */ +export interface ThresholdAlertPayload { + /* Name of the trace or network request this alert is for (e.g. my_custom_trace, firebase.com/api/123) */ + eventName: string; + /* The resource type this alert is for (i.e. trace, network request, screen rendering, etc.) */ + eventType: string; + /* The metric type this alert is for (i.e. success rate, response time, duration, etc.) */ + metricType: string; + /* The number of events checked for this alert condition */ + numSamples: number; + /* The threshold value of the alert condition without units (e.g. "75", "2.1") */ + thresholdValue: number; + /* The unit for the alert threshold (e.g. "percent", "seconds") */ + thresholdUnit: string; + /* The percentile of the alert condition, can be 0 if percentile is not applicable to the alert condition and omitted; range: [1, 100] */ + conditionPercentile?: number; + /* The app version this alert was triggered for, can be omitted if the alert is for a network request (because the alert was checked against data from all versions of app) or a web app (where the app is versionless) */ + appVersion?: string; + /* The value that violated the alert condition (e.g. "76.5", "3") */ + violationValue: number; + /* The unit for the violation value (e.g. "percent", "seconds") */ + violationUnit: string; + /* The link to Fireconsole to investigate more into this alert */ + investigateUri: string; +} + +/** + * A custom CloudEvent for Firebase Alerts (with custom extension attributes). + * @typeParam T - the data type for performance alerts that is wrapped in a `FirebaseAlertData` object. + */ +export interface PerformanceEvent extends CloudEvent> { + /** The type of the alerts that got triggered. */ + alertType: string; + /** The Firebase App ID that’s associated with the alert. */ + appId: string; +} + +/** @internal */ +export const thresholdAlert = 'performance.threshold'; + +/** + * Configuration for app distribution functions. + */ +export interface PerformanceOptions extends EventHandlerOptions { + // Scope the function to trigger on a specific application. + appId?: string; +} + +/** + * Declares a function that can handle receiving performance threshold alerts. + * @param handler - Event handler which is run every time a threshold alert is received. + * @returns A function that you can export and deploy. + */ +export function onThresholdAlertPublished( + handler: ( + event: PerformanceEvent + ) => any | Promise +): CloudFunction>; + +/** + * Declares a function that can handle receiving performance threshold alerts. + * @param appId - A specific application the handler will trigger on. + * @param handler - Event handler which is run every time a threshold alert is received. + * @returns A function that you can export and deploy. + */ +export function onThresholdAlertPublished( + appId: string, + handler: ( + event: PerformanceEvent + ) => any | Promise +): CloudFunction>; + +/** + * Declares a function that can handle receiving performance threshold alerts. + * @param opts - Options that can be set on the function. + * @param handler - Event handler which is run every time a threshold alert is received. + * @returns A function that you can export and deploy. + */ +export function onThresholdAlertPublished( + opts: PerformanceOptions, + handler: ( + event: PerformanceEvent + ) => any | Promise +): CloudFunction>; + +/** + * Declares a function that can handle receiving performance threshold alerts. + * @param appIdOrOptsOrHandler - A specific application, options, or an event-handling function. + * @param handler - Event handler which is run every time a threshold alert is received. + * @returns A function that you can export and deploy. + */ +export function onThresholdAlertPublished( + appIdOrOptsOrHandler: + | string + | PerformanceOptions + | ((event: PerformanceEvent) => any | Promise), + handler?: ( + event: PerformanceEvent + ) => any | Promise +): CloudFunction> { + if (typeof appIdOrOptsOrHandler === 'function') { + handler = appIdOrOptsOrHandler as ( + event: PerformanceEvent + ) => any | Promise; + appIdOrOptsOrHandler = {}; + } + + const [opts, appId] = getOptsAndApp(appIdOrOptsOrHandler); + + const func = (raw: CloudEvent) => { + const event = raw as PerformanceEvent; + const convertedPayload = convertPayload(event.data.payload); + event.data.payload = convertedPayload; + return handler(event); + }; + + func.run = handler; + func.__endpoint = getEndpointAnnotation(opts, thresholdAlert, appId); + + return func; +} + +/** + * Helper function to parse the function opts and appId. + * @internal + */ +export function getOptsAndApp( + appIdOrOpts: string | PerformanceOptions +): [EventHandlerOptions, string | undefined] { + if (typeof appIdOrOpts === 'string') { + return [{}, appIdOrOpts]; + } + + const opts: EventHandlerOptions = { ...appIdOrOpts }; + const appId: string | undefined = appIdOrOpts.appId; + delete (opts as any).appId; + + return [opts, appId]; +} + +/** + * Helper function to convert the raw payload of a {@link PerformanceEvent} to a {@link ThresholdAlertPayload} + * @internal + */ +export function convertPayload( + raw: ThresholdAlertPayload +): ThresholdAlertPayload { + const payload: ThresholdAlertPayload = { ...raw }; + if ( + typeof payload.conditionPercentile !== 'undefined' && + payload.conditionPercentile === 0 + ) { + delete (payload as any).conditionPercentile; + } + if ( + typeof payload.appVersion !== 'undefined' && + payload.appVersion.length === 0 + ) { + delete (payload as any).appVersion; + } + + return payload; +} diff --git a/src/v2/providers/tasks.ts b/src/v2/providers/tasks.ts index 9883aea3e..efc99e273 100644 --- a/src/v2/providers/tasks.ts +++ b/src/v2/providers/tasks.ts @@ -38,8 +38,8 @@ import { RetryConfig, } from '../../common/providers/tasks'; import * as options from '../options'; -import { HttpsFunction } from './https'; import { Expression } from '../params'; +import { HttpsFunction } from './https'; export { AuthData, Request }; diff --git a/v2/alerts/performance.js b/v2/alerts/performance.js new file mode 100644 index 000000000..ae33ba821 --- /dev/null +++ b/v2/alerts/performance.js @@ -0,0 +1,26 @@ +// The MIT License (MIT) +// +// Copyright (c) 2022 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. + +// This file is not part of the firebase-functions SDK. It is used to silence the +// imports eslint plugin until it can understand import paths defined by node +// package exports. +// For more information, see github.com/import-js/eslint-plugin-import/issues/1810 From 65e66a21388ffe08f923e194d28295090f9cf195 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Mon, 19 Sep 2022 20:08:22 -0400 Subject: [PATCH 145/370] Converting alert type and app id to camel case in the CloudEvent (#1236) * converting to camel case * add fix for generic alerts --- spec/v2/providers/alerts/alerts.spec.ts | 36 ++++++++++++++++++++++ src/v2/providers/alerts/alerts.ts | 23 +++++++++++++- src/v2/providers/alerts/appDistribution.ts | 14 +++++++-- src/v2/providers/alerts/billing.ts | 8 +++-- src/v2/providers/alerts/crashlytics.ts | 8 +++-- src/v2/providers/alerts/performance.ts | 10 ++++-- 6 files changed, 89 insertions(+), 10 deletions(-) diff --git a/spec/v2/providers/alerts/alerts.spec.ts b/spec/v2/providers/alerts/alerts.spec.ts index 8c4741f43..592b2839e 100644 --- a/spec/v2/providers/alerts/alerts.spec.ts +++ b/spec/v2/providers/alerts/alerts.spec.ts @@ -1,4 +1,5 @@ import { expect } from 'chai'; +import { CloudEvent } from '../../../../src/v2'; import * as options from '../../../../src/v2/options'; import * as alerts from '../../../../src/v2/providers/alerts'; import { FULL_ENDPOINT, FULL_OPTIONS } from '../fixtures'; @@ -174,4 +175,39 @@ describe('alerts', () => { expect(appId).to.be.equal(myOpts.appId); }); }); + + describe('convertAlertAndApp', () => { + const event: CloudEvent = { + specversion: '1.0', + id: 'id', + source: 'source', + type: 'type', + time: 'now', + data: 'data', + }; + + it('should leave event unchanged if alerttype & appid are missing', () => { + const raw = { ...event }; + + const converted = alerts.convertAlertAndApp(raw); + + expect(raw).to.deep.eq(converted); + }); + + it('should convert alerttype & appid when present', () => { + const raw = { + ...event, + alerttype: 'my-alert', + appid: 'my-app', + }; + + const converted = alerts.convertAlertAndApp(raw); + + expect(converted).to.deep.eq({ + ...event, + alertType: 'my-alert', + appId: 'my-app', + }); + }); + }); }); diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index 545e9bbcf..c021c6ebd 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -207,7 +207,7 @@ export function onAlertPublished( const [opts, alertType, appId] = getOptsAndAlertTypeAndApp(alertTypeOrOpts); const func = (raw: CloudEvent) => { - return handler(raw as AlertEvent); + return handler(convertAlertAndApp(raw) as AlertEvent); }; func.run = handler; @@ -271,3 +271,24 @@ export function getOptsAndAlertTypeAndApp( } return [opts, alertType, appId]; } + +/** + * Helper function to covert alert type & app id in the CloudEvent to camel case. + * @internal + */ +export function convertAlertAndApp( + raw: CloudEvent +): CloudEvent { + const event = { ...raw }; + + if ('alerttype' in event) { + (event as any).alertType = (event as any).alerttype; + delete (event as any).alerttype; + } + if ('appid' in event) { + (event as any).appId = (event as any).appid; + delete (event as any).appid; + } + + return event; +} diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index 695bfc4df..3a9e7107c 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -28,7 +28,11 @@ import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; import { Expression } from '../../params'; -import { FirebaseAlertData, getEndpointAnnotation } from './alerts'; +import { + convertAlertAndApp, + FirebaseAlertData, + getEndpointAnnotation, +} from './alerts'; /** * The internal payload object for adding a new tester device to app distribution. @@ -253,7 +257,9 @@ export function onNewTesterIosDevicePublished( const [opts, appId] = getOptsAndApp(appIdOrOptsOrHandler); const func = (raw: CloudEvent) => { - return handler(raw as AppDistributionEvent); + return handler( + convertAlertAndApp(raw) as AppDistributionEvent + ); }; func.run = handler; @@ -326,7 +332,9 @@ export function onInAppFeedbackPublished( const [opts, appId] = getOptsAndApp(appIdOrOptsOrHandler); const func = (raw: CloudEvent) => { - return handler(raw as AppDistributionEvent); + return handler( + convertAlertAndApp(raw) as AppDistributionEvent + ); }; func.run = handler; diff --git a/src/v2/providers/alerts/billing.ts b/src/v2/providers/alerts/billing.ts index 4d42af79b..955fd8791 100644 --- a/src/v2/providers/alerts/billing.ts +++ b/src/v2/providers/alerts/billing.ts @@ -25,9 +25,13 @@ * @packageDocumentation */ -import { FirebaseAlertData, getEndpointAnnotation } from '.'; import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; +import { + convertAlertAndApp, + FirebaseAlertData, + getEndpointAnnotation, +} from './alerts'; /** * The internal payload object for billing plan updates. @@ -167,7 +171,7 @@ export function onOperation( } const func = (raw: CloudEvent) => { - return handler(raw as BillingEvent); + return handler(convertAlertAndApp(raw) as BillingEvent); }; func.run = handler; diff --git a/src/v2/providers/alerts/crashlytics.ts b/src/v2/providers/alerts/crashlytics.ts index af3d58bdc..1e6345b41 100644 --- a/src/v2/providers/alerts/crashlytics.ts +++ b/src/v2/providers/alerts/crashlytics.ts @@ -25,10 +25,14 @@ * @packageDocumentation */ -import { FirebaseAlertData, getEndpointAnnotation } from '.'; import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; import { Expression } from '../../params'; +import { + convertAlertAndApp, + FirebaseAlertData, + getEndpointAnnotation, +} from './alerts'; /** Generic Crashlytics issue interface */ export interface Issue { @@ -631,7 +635,7 @@ export function onOperation( ); const func = (raw: CloudEvent) => { - return handler(raw as CrashlyticsEvent); + return handler(convertAlertAndApp(raw) as CrashlyticsEvent); }; func.run = handler; diff --git a/src/v2/providers/alerts/performance.ts b/src/v2/providers/alerts/performance.ts index 7fa82439f..bc4ab22c9 100644 --- a/src/v2/providers/alerts/performance.ts +++ b/src/v2/providers/alerts/performance.ts @@ -25,9 +25,13 @@ * @packageDocumentation */ -import { FirebaseAlertData, getEndpointAnnotation } from '.'; import { CloudEvent, CloudFunction } from '../../core'; import { EventHandlerOptions } from '../../options'; +import { + convertAlertAndApp, + FirebaseAlertData, + getEndpointAnnotation, +} from './alerts'; /** * The internal payload object for a performance threshold alert. @@ -142,7 +146,9 @@ export function onThresholdAlertPublished( const [opts, appId] = getOptsAndApp(appIdOrOptsOrHandler); const func = (raw: CloudEvent) => { - const event = raw as PerformanceEvent; + const event = convertAlertAndApp(raw) as PerformanceEvent< + ThresholdAlertPayload + >; const convertedPayload = convertPayload(event.data.payload); event.data.payload = convertedPayload; return handler(event); From b93e397b32ae3884db79910a1c22242112514f90 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Wed, 21 Sep 2022 14:48:27 -0400 Subject: [PATCH 146/370] Don't delete fields on a non-breaking change release (#1238) --- spec/v2/providers/alerts/alerts.spec.ts | 2 ++ src/v2/providers/alerts/alerts.ts | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/v2/providers/alerts/alerts.spec.ts b/spec/v2/providers/alerts/alerts.spec.ts index 592b2839e..3decbd111 100644 --- a/spec/v2/providers/alerts/alerts.spec.ts +++ b/spec/v2/providers/alerts/alerts.spec.ts @@ -205,6 +205,8 @@ describe('alerts', () => { expect(converted).to.deep.eq({ ...event, + alerttype: 'my-alert', + appid: 'my-app', alertType: 'my-alert', appId: 'my-app', }); diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index c021c6ebd..b36fb0c55 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -283,11 +283,9 @@ export function convertAlertAndApp( if ('alerttype' in event) { (event as any).alertType = (event as any).alerttype; - delete (event as any).alerttype; } if ('appid' in event) { (event as any).appId = (event as any).appid; - delete (event as any).appid; } return event; From e191af7148e848197d9c83a5fd4131928e995fb4 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 21 Sep 2022 18:58:31 +0000 Subject: [PATCH 147/370] 3.24.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b05fdebe8..da000cde7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-functions", - "version": "3.23.0", + "version": "3.24.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "firebase-functions", - "version": "3.23.0", + "version": "3.24.0", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index a4a9711d5..838ad1658 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.23.0", + "version": "3.24.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From bd0fcbc595c15e38deea4de9e2ceabcbbc26a539 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 21 Sep 2022 18:58:33 +0000 Subject: [PATCH 148/370] [firebase-release] Removed change log and reset repo after 3.24.0 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db12f0f5d..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Add performance monitoring triggers to v2 alerts (#1223). From 1ac04adba97f5b17f31b06226dc6be8f5ec13b1d Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Wed, 21 Sep 2022 16:44:58 -0400 Subject: [PATCH 149/370] fix tsdoc comments (#1240) --- CHANGELOG.md | 1 + src/v2/providers/alerts/performance.ts | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..0df54b4ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Fix reference docs for performance monitoring. diff --git a/src/v2/providers/alerts/performance.ts b/src/v2/providers/alerts/performance.ts index bc4ab22c9..ef0a5f5e0 100644 --- a/src/v2/providers/alerts/performance.ts +++ b/src/v2/providers/alerts/performance.ts @@ -38,27 +38,27 @@ import { * Payload is wrapped inside a {@link FirebaseAlertData} object. */ export interface ThresholdAlertPayload { - /* Name of the trace or network request this alert is for (e.g. my_custom_trace, firebase.com/api/123) */ + /** Name of the trace or network request this alert is for (e.g. my_custom_trace, firebase.com/api/123) */ eventName: string; - /* The resource type this alert is for (i.e. trace, network request, screen rendering, etc.) */ + /** The resource type this alert is for (i.e. trace, network request, screen rendering, etc.) */ eventType: string; - /* The metric type this alert is for (i.e. success rate, response time, duration, etc.) */ + /** The metric type this alert is for (i.e. success rate, response time, duration, etc.) */ metricType: string; - /* The number of events checked for this alert condition */ + /** The number of events checked for this alert condition */ numSamples: number; - /* The threshold value of the alert condition without units (e.g. "75", "2.1") */ + /** The threshold value of the alert condition without units (e.g. "75", "2.1") */ thresholdValue: number; - /* The unit for the alert threshold (e.g. "percent", "seconds") */ + /** The unit for the alert threshold (e.g. "percent", "seconds") */ thresholdUnit: string; - /* The percentile of the alert condition, can be 0 if percentile is not applicable to the alert condition and omitted; range: [1, 100] */ + /** The percentile of the alert condition, can be 0 if percentile is not applicable to the alert condition and omitted; range: [1, 100] */ conditionPercentile?: number; - /* The app version this alert was triggered for, can be omitted if the alert is for a network request (because the alert was checked against data from all versions of app) or a web app (where the app is versionless) */ + /** The app version this alert was triggered for, can be omitted if the alert is for a network request (because the alert was checked against data from all versions of app) or a web app (where the app is versionless) */ appVersion?: string; - /* The value that violated the alert condition (e.g. "76.5", "3") */ + /** The value that violated the alert condition (e.g. "76.5", "3") */ violationValue: number; - /* The unit for the violation value (e.g. "percent", "seconds") */ + /** The unit for the violation value (e.g. "percent", "seconds") */ violationUnit: string; - /* The link to Fireconsole to investigate more into this alert */ + /** The link to Fireconsole to investigate more into this alert */ investigateUri: string; } @@ -80,7 +80,7 @@ export const thresholdAlert = 'performance.threshold'; * Configuration for app distribution functions. */ export interface PerformanceOptions extends EventHandlerOptions { - // Scope the function to trigger on a specific application. + /** Scope the function to trigger on a specific application. */ appId?: string; } From cf27ac6b0b50a50b56bab37c9f3b5fd755d14229 Mon Sep 17 00:00:00 2001 From: egilmorez Date: Wed, 21 Sep 2022 14:20:26 -0700 Subject: [PATCH 150/370] Adding required --project flag to v2 docgen script. (#1239) Co-authored-by: Daniel Lee --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 838ad1658..0e69c233e 100644 --- a/package.json +++ b/package.json @@ -175,7 +175,7 @@ "docgen:v1": "npm run build && npm run docgen:v1:extract && npm run docgen:v1:gen", "docgen:v2:extract": "api-extractor run -c docgen/api-extractor.v2.json --local", "docgen:v2:toc": "ts-node docgen/toc.ts --input docgen/v2/markdown --output docgen/v2/markdown/toc --path /docs/functions/beta/reference", - "docgen:v2:gen": "api-documenter-fire markdown -i docgen/v2 -o docgen/v2/markdown && npm run docgen:v2:toc", + "docgen:v2:gen": "api-documenter-fire markdown -i docgen/v2 -o docgen/v2/markdown --project functions && npm run docgen:v2:toc", "docgen:v2": "npm run build && npm run docgen:v2:extract && npm run docgen:v2:gen", "build:pack": "rm -rf lib && npm install && tsc -p tsconfig.release.json && npm pack", "build:release": "npm ci --production && npm install --no-save typescript firebase-admin && tsc -p tsconfig.release.json", From cc6e28e6ed807cef61add89ac7c7d09dd754d876 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 29 Sep 2022 16:34:28 -0700 Subject: [PATCH 151/370] Fix bug where function configuration with null couldn't be deployed. (#1246) --- CHANGELOG.md | 1 + spec/runtime/manifest.spec.ts | 30 ++++++++++++++++++++++++++++++ src/runtime/manifest.ts | 2 +- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0df54b4ec..f6a8ba1bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ - Fix reference docs for performance monitoring. +- Fix bug where function configuration wil null values couldn't be deployed. (#1246) diff --git a/spec/runtime/manifest.spec.ts b/spec/runtime/manifest.spec.ts index 96a330914..382a0b41e 100644 --- a/spec/runtime/manifest.spec.ts +++ b/spec/runtime/manifest.spec.ts @@ -7,6 +7,36 @@ describe('stackToWire', () => { params.clearParams(); }); + it('converts stack with null values values', () => { + const stack: ManifestStack = { + endpoints: { + v2http: { + platform: 'gcfv2', + entryPoint: 'v2http', + labels: {}, + httpsTrigger: {}, + maxInstances: null, + }, + }, + requiredAPIs: [], + specVersion: 'v1alpha1', + }; + const expected = { + endpoints: { + v2http: { + platform: 'gcfv2', + entryPoint: 'v2http', + labels: {}, + httpsTrigger: {}, + maxInstances: null, + }, + }, + requiredAPIs: [], + specVersion: 'v1alpha1', + }; + expect(stackToWire(stack)).to.deep.equal(expected); + }); + it('converts Expression types in endpoint options to CEL', () => { const intParam = params.defineInt('foo', { default: 11 }); const stringParam = params.defineString('bar', { diff --git a/src/runtime/manifest.ts b/src/runtime/manifest.ts index d53165420..553854ae0 100644 --- a/src/runtime/manifest.ts +++ b/src/runtime/manifest.ts @@ -107,7 +107,7 @@ export function stackToWire(stack: ManifestStack): Object { for (const [key, val] of Object.entries(obj)) { if (val instanceof Expression) { obj[key] = val.toCEL(); - } else if (typeof val === 'object') { + } else if (typeof val === 'object' && val !== null) { traverse(val); } } From 3c5392dfee2ab44d30bced291912821ab544d0a3 Mon Sep 17 00:00:00 2001 From: Kai Bolay Date: Thu, 29 Sep 2022 21:29:19 -0400 Subject: [PATCH 152/370] Hide documentation for in-app feedback (#1245) --- src/v2/providers/alerts/appDistribution.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index 3a9e7107c..3446379e4 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -53,6 +53,8 @@ export interface NewTesterDevicePayload { /** * The internal payload object for receiving in-app feedback from a tester. * Payload is wrapped inside a `FirebaseAlertData` object. + * + * @alpha */ export interface InAppFeedbackPayload { ['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.AppDistroInAppFeedbackPayload'; @@ -272,6 +274,8 @@ export function onNewTesterIosDevicePublished( * Declares a function that can handle receiving new in-app feedback from a tester. * @param handler - Event handler which is run every time new feedback is received. * @returns A function that you can export and deploy. + * + * @alpha */ export function onInAppFeedbackPublished( handler: ( @@ -284,6 +288,8 @@ export function onInAppFeedbackPublished( * @param appId - A specific application the handler will trigger on. * @param handler - Event handler which is run every time new feedback is received. * @returns A function that you can export and deploy. + * + * @alpha */ export function onInAppFeedbackPublished( appId: string, @@ -297,6 +303,8 @@ export function onInAppFeedbackPublished( * @param opts - Options that can be set on the function. * @param handler - Event handler which is run every time new feedback is received. * @returns A function that you can export and deploy. + * + * @alpha */ export function onInAppFeedbackPublished( opts: AppDistributionOptions, @@ -310,6 +318,8 @@ export function onInAppFeedbackPublished( * @param appIdOrOptsOrHandler - A specific application, options, or an event-handling function. * @param handler - Event handler which is run every time new feedback is received. * @returns A function that you can export and deploy. + * + * @alpha */ export function onInAppFeedbackPublished( appIdOrOptsOrHandler: From e4bda7d683b8dda345e89c7a1daedc9d71f7f88d Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 30 Sep 2022 18:01:59 +0000 Subject: [PATCH 153/370] 3.24.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index da000cde7..9c9e905c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-functions", - "version": "3.24.0", + "version": "3.24.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "firebase-functions", - "version": "3.24.0", + "version": "3.24.1", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index 0e69c233e..56e1ce4f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.24.0", + "version": "3.24.1", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From b096107ab8f00fb89c279cc8a07780678c946bd7 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 30 Sep 2022 18:02:02 +0000 Subject: [PATCH 154/370] [firebase-release] Removed change log and reset repo after 3.24.1 release --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6a8ba1bc..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +0,0 @@ -- Fix reference docs for performance monitoring. -- Fix bug where function configuration wil null values couldn't be deployed. (#1246) From ffa3a574087e12e0fd866d504328b6a6e3d7f344 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 13 Oct 2022 12:03:42 -0700 Subject: [PATCH 155/370] Functions SDK v4 (#1161) ### Breaking Changes - Deprecated `allowInvalidAppCheckToken` option. Instead use`enforceAppCheck`. - App Check enforcement on callable functions is disabled by default in v4. - Requests containing invalid App Check tokens won't be denied unless you - explicitly enable App Check enforcement using the new `enforceAppCheck` option. - Furthermore, when enforcement is enabled, callable functions will deny - all requests without App Check tokens. - Dropped support for Node.js versions 8, 10, and 12. - Dropped support for Admin SDK versions 8 and 9. - Removed the `functions.handler` namespace. - `DataSnapshot` passed to the Firebase Realtime Database trigger now matches the `DataSnapshot` returned by the Admin SDK, with null values removed. - Removed `__trigger` object on function handlers. - Reorganized source code location. This affects only apps that directly import files instead of using the recommend entry points specified in the - Reworked the `apps` library and removed `lodash` as a runtime dependency. - Unspecified function configuration value will be reset to platform default. Use `preserveExternalChanges` to prevent this behavior. ### Enhancements - Logs created with the `functions.logger` package in v2 functions are now annotated with each request's trace ID, making it easy to correlate log entries with the incoming request. Trace IDs are especially useful for cases where 2nd gen's concurrency feature permits a function to handle multiple requests at any given time. See [Correlate log entries](https://cloud.google.com/logging/docs/view/correlate-logs) to learn more. - `functions.logger.error` now always outputs an error object and is included in Google Cloud Error Reporting. - The logging severity of Auth/App Check token validation has changed from `info` to `debug` level. - Event parameters for 2nd generation functions are now strongly typed, permitting stronger TypeScript types for matched parameters. - Add new params package to support parameterized environment configuration. See https://firebase.google.com/docs/functions/config-env for more information. - Add new `functions.RESET_VALUE` and `functions.v2.options.RESET_VALUE` sentinel value for explicitly resetting function configuration to platform default. - Add new `preserveExternalChanges` option to prevent Firebase CLI from resetting unspecified configuration option to platform default --- .eslintignore | 11 + .eslintrc.js | 70 + .github/ISSUE_TEMPLATE/---report-a-bug.md | 8 +- .github/workflows/postmerge.yaml | 10 +- .github/workflows/test.yaml | 29 +- .mocharc.yaml | 4 +- .prettierignore | 7 +- .prettierrc | 5 - .prettierrc.js | 3 + CHANGELOG.md | 32 + README.md | 16 +- docgen/api-extractor.base.json | 728 ++-- docgen/api-extractor.v1.json | 2 +- docgen/generate-docs.js | 383 -- docgen/toc.ts | 216 +- docgen/type-aliases.json | 5 - docgen/typedoc.js | 26 - integration_test/firebase.json | 2 + integration_test/functions/src/index.ts | 173 +- integration_test/functions/src/region.ts | 2 +- integration_test/functions/src/testing.ts | 34 +- .../functions/src/v1/auth-tests.ts | 68 +- .../functions/src/v1/database-tests.ts | 47 +- .../functions/src/v1/firestore-tests.ts | 30 +- .../functions/src/v1/https-tests.ts | 14 +- integration_test/functions/src/v1/index.ts | 17 +- .../functions/src/v1/pubsub-tests.ts | 62 +- .../functions/src/v1/remoteConfig-tests.ts | 39 +- .../functions/src/v1/storage-tests.ts | 20 +- .../functions/src/v1/testLab-tests.ts | 18 +- .../functions/src/v1/testLab-utils.ts | 38 +- .../functions/src/v2/https-tests.ts | 12 +- integration_test/functions/src/v2/index.ts | 9 +- .../functions/src/v2/scheduled-tests.ts | 22 +- mocha/setup.ts | 6 +- package-lock.json | 3410 ++++++++++++++--- package.json | 125 +- scripts/bin-test/mocha-setup.ts | 4 +- .../sources/commonjs-preserve/index.js | 18 + .../sources/commonjs-preserve/package.json | 3 + scripts/bin-test/test.ts | 167 +- scripts/publish-container/cloudbuild.yaml | 6 +- scripts/publish/cloudbuild.yaml | 130 +- spec/common/change.spec.ts | 72 +- spec/common/config.spec.ts | 72 + spec/common/encoding.spec.ts | 48 +- spec/common/metaprogramming.ts | 25 + spec/common/options.ts | 36 + spec/common/params.spec.ts | 109 + spec/common/providers/https.spec.ts | 735 ++-- spec/common/providers/identity.spec.ts | 676 ++-- spec/common/providers/tasks.spec.ts | 130 +- spec/common/trace.spec.ts | 103 + spec/common/utilities/path-pattern.spec.ts | 129 + spec/common/utilities/path.spec.ts | 24 + spec/fixtures.ts | 64 + spec/fixtures/mockrequest.ts | 3 +- spec/fixtures/sources/commonjs-grouped/g1.js | 2 +- .../sources/commonjs-grouped/index.js | 4 +- .../sources/commonjs-main/functions.js | 4 +- .../fixtures/sources/commonjs-params/index.js | 35 +- spec/fixtures/sources/commonjs/index.js | 4 +- spec/helper.ts | 21 +- spec/logger.spec.ts | 120 +- spec/params/params.spec.ts | 292 ++ spec/runtime/loader.spec.ts | 251 +- spec/runtime/manifest.spec.ts | 139 +- spec/utilities/path-pattern.spec.ts | 145 - spec/utilities/path.spec.ts | 24 - spec/v1/apps.spec.ts | 143 - spec/v1/cloud-functions.spec.ts | 279 +- spec/v1/config.spec.ts | 110 +- spec/v1/function-builder.spec.ts | 356 +- spec/v1/providers/analytics.spec.input.ts | 64 +- spec/v1/providers/analytics.spec.ts | 272 +- spec/v1/providers/auth.spec.ts | 285 +- spec/v1/providers/database.spec.ts | 890 ++--- spec/v1/providers/firestore.spec.ts | 485 +-- spec/v1/providers/fixtures.ts | 50 + spec/v1/providers/https.spec.ts | 147 +- spec/v1/providers/pubsub.spec.ts | 408 +- spec/v1/providers/remoteConfig.spec.ts | 127 +- spec/v1/providers/storage.spec.ts | 531 +-- spec/v1/providers/tasks.spec.ts | 93 +- spec/v1/providers/testLab.spec.ts | 206 +- spec/v1/utils.spec.ts | 22 +- spec/v2/params.spec.ts | 0 spec/v2/providers/alerts/alerts.spec.ts | 121 +- .../providers/alerts/appDistribution.spec.ts | 102 +- spec/v2/providers/alerts/billing.spec.ts | 65 +- spec/v2/providers/alerts/crashlytics.spec.ts | 106 +- spec/v2/providers/alerts/performance.spec.ts | 85 +- spec/v2/providers/database.spec.ts | 426 +- spec/v2/providers/eventarc.spec.ts | 78 +- spec/v2/providers/fixtures.ts | 61 +- spec/v2/providers/https.spec.ts | 307 +- spec/v2/providers/identity.spec.ts | 145 +- spec/v2/providers/pubsub.spec.ts | 143 +- spec/v2/providers/remoteConfig.spec.ts | 77 + spec/v2/providers/scheduler.spec.ts | 103 +- spec/v2/providers/storage.spec.ts | 522 +-- spec/v2/providers/tasks.spec.ts | 121 +- spec/v2/providers/testLab.spec.ts | 74 + src/apps.ts | 134 - src/bin/firebase-functions.ts | 35 +- src/common/app.ts | 69 + src/common/change.ts | 29 +- src/common/config.ts | 57 + src/common/debug.ts | 4 +- src/common/encoding.ts | 43 +- src/common/options.ts | 48 + src/common/params.ts | 87 + src/common/providers/database.ts | 133 +- src/common/providers/https.ts | 396 +- src/common/providers/identity.ts | 218 +- src/common/providers/tasks.ts | 48 +- src/common/timezone.ts | 1076 +++--- src/common/trace.ts | 82 + src/{ => common}/utilities/assertions.ts | 4 +- src/{ => common/utilities}/encoder.ts | 2 +- src/{ => common}/utilities/path-pattern.ts | 41 +- src/{ => common}/utilities/path.ts | 12 +- .../common/utilities/utils.ts | 52 +- src/config.ts | 138 - src/function-configuration.ts | 180 - src/handler-builder.ts | 379 -- src/logger/common.ts | 24 +- src/logger/compat.ts | 30 +- src/logger/index.ts | 86 +- src/{v2 => }/params/index.ts | 74 +- src/params/types.ts | 434 +++ src/runtime/loader.ts | 47 +- src/runtime/manifest.ts | 233 +- src/setup.ts | 67 - src/types/global.d.ts | 1 + src/{ => v1}/cloud-functions.ts | 471 +-- src/v1/config.ts | 80 + src/{ => v1}/function-builder.ts | 214 +- src/v1/function-configuration.ts | 269 ++ src/v1/handler-builder.ts | 0 src/{ => v1}/index.ts | 42 +- src/{ => v1}/providers/analytics.ts | 175 +- src/{ => v1}/providers/auth.ts | 125 +- src/{ => v1}/providers/database.ts | 145 +- src/{ => v1}/providers/firestore.ts | 168 +- src/{ => v1}/providers/https.ts | 57 +- src/{ => v1}/providers/pubsub.ts | 78 +- src/{ => v1}/providers/remoteConfig.ts | 48 +- src/{ => v1}/providers/storage.ts | 108 +- src/{ => v1}/providers/tasks.ts | 64 +- src/{ => v1}/providers/testLab.ts | 245 +- src/v2/core.ts | 39 +- src/v2/index.ts | 31 +- src/v2/options.ts | 269 +- src/v2/params/types.ts | 294 -- src/v2/providers/alerts/alerts.ts | 80 +- src/v2/providers/alerts/appDistribution.ts | 120 +- src/v2/providers/alerts/billing.ts | 51 +- src/v2/providers/alerts/crashlytics.ts | 189 +- src/v2/providers/alerts/index.ts | 10 +- src/v2/providers/alerts/performance.ts | 50 +- src/v2/providers/database.ts | 249 +- src/v2/providers/eventarc.ts | 72 +- src/v2/providers/https.ts | 202 +- src/v2/providers/identity.ts | 122 +- src/v2/providers/pubsub.ts | 99 +- src/v2/providers/remoteConfig.ts | 154 + src/v2/providers/scheduler.ts | 98 +- src/v2/providers/storage.ts | 151 +- src/v2/providers/tasks.ts | 130 +- src/v2/providers/testLab.ts | 214 ++ src/v2/trace.ts | 33 + tsconfig.json | 6 +- tsconfig.release.json | 6 +- src/utils.ts => v2/remoteConfig.js | 28 +- v2/testLab.js | 26 + 176 files changed, 13348 insertions(+), 12229 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc.js delete mode 100644 .prettierrc create mode 100644 .prettierrc.js delete mode 100644 docgen/generate-docs.js delete mode 100644 docgen/type-aliases.json delete mode 100644 docgen/typedoc.js create mode 100644 scripts/bin-test/sources/commonjs-preserve/index.js create mode 100644 scripts/bin-test/sources/commonjs-preserve/package.json create mode 100644 spec/common/config.spec.ts create mode 100644 spec/common/metaprogramming.ts create mode 100644 spec/common/options.ts create mode 100644 spec/common/params.spec.ts create mode 100644 spec/common/trace.spec.ts create mode 100644 spec/common/utilities/path-pattern.spec.ts create mode 100644 spec/common/utilities/path.spec.ts create mode 100644 spec/fixtures.ts create mode 100644 spec/params/params.spec.ts delete mode 100644 spec/utilities/path-pattern.spec.ts delete mode 100644 spec/utilities/path.spec.ts delete mode 100644 spec/v1/apps.spec.ts create mode 100644 spec/v1/providers/fixtures.ts create mode 100644 spec/v2/params.spec.ts create mode 100644 spec/v2/providers/remoteConfig.spec.ts create mode 100644 spec/v2/providers/testLab.spec.ts delete mode 100644 src/apps.ts create mode 100644 src/common/app.ts create mode 100644 src/common/config.ts create mode 100644 src/common/options.ts create mode 100644 src/common/params.ts create mode 100644 src/common/trace.ts rename src/{ => common}/utilities/assertions.ts (79%) rename src/{ => common/utilities}/encoder.ts (97%) rename src/{ => common}/utilities/path-pattern.ts (80%) rename src/{ => common}/utilities/path.ts (74%) rename spec/v1/setup.spec.ts => src/common/utilities/utils.ts (58%) delete mode 100644 src/config.ts delete mode 100644 src/handler-builder.ts rename src/{v2 => }/params/index.ts (69%) create mode 100644 src/params/types.ts delete mode 100644 src/setup.ts rename src/{ => v1}/cloud-functions.ts (50%) create mode 100644 src/v1/config.ts rename src/{ => v1}/function-builder.ts (74%) create mode 100644 src/v1/function-configuration.ts create mode 100644 src/v1/handler-builder.ts rename src/{ => v1}/index.ts (61%) rename src/{ => v1}/providers/analytics.ts (76%) rename src/{ => v1}/providers/auth.ts (69%) rename src/{ => v1}/providers/database.ts (73%) rename src/{ => v1}/providers/firestore.ts (60%) rename src/{ => v1}/providers/https.ts (74%) rename src/{ => v1}/providers/pubsub.ts (75%) rename src/{ => v1}/providers/remoteConfig.ts (81%) rename src/{ => v1}/providers/storage.ts (77%) rename src/{ => v1}/providers/tasks.ts (75%) rename src/{ => v1}/providers/testLab.ts (55%) delete mode 100644 src/v2/params/types.ts create mode 100644 src/v2/providers/remoteConfig.ts create mode 100644 src/v2/providers/testLab.ts create mode 100644 src/v2/trace.ts rename src/utils.ts => v2/remoteConfig.js (68%) create mode 100644 v2/testLab.js diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..72d1288b0 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,11 @@ +lib +dev +node_modules +/coverage/ +/docgen/ +/v1/ +/v2/ +/logger/ +/dist/ +/spec/fixtures +/scripts/**/*.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..f225e5960 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,70 @@ +module.exports = { + env: { + es6: true, + node: true, + }, + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking", + "plugin:jsdoc/recommended", + "google", + "prettier", + ], + rules: { + "jsdoc/newline-after-description": "off", + "jsdoc/require-jsdoc": ["warn", { publicOnly: true }], + "no-restricted-globals": ["error", "name", "length"], + "prefer-arrow-callback": "error", + "prettier/prettier": "error", + "require-atomic-updates": "off", // This rule is so noisy and isn't useful: https://github.com/eslint/eslint/issues/11899 + "require-jsdoc": "off", // This rule is deprecated and superseded by jsdoc/require-jsdoc. + "valid-jsdoc": "off", // This is deprecated but included in recommended configs. + + "no-prototype-builtins": "warn", + "no-useless-escape": "warn", + "prefer-promise-reject-errors": "warn", + }, + overrides: [ + { + files: ["*.ts"], + rules: { + "jsdoc/require-param-type": "off", + "jsdoc/require-returns-type": "off", + + // Google style guide allows us to omit trivial parameters and returns + "jsdoc/require-param": "off", + "jsdoc/require-returns": "off", + + "@typescript-eslint/no-invalid-this": "error", + "@typescript-eslint/no-unused-vars": "error", // Unused vars should not exist. + "@typescript-eslint/no-misused-promises": "warn", // rule does not work with async handlers for express. + "no-invalid-this": "off", // Turned off in favor of @typescript-eslint/no-invalid-this. + "no-unused-vars": "off", // Off in favor of @typescript-eslint/no-unused-vars. + eqeqeq: ["error", "always", { null: "ignore" }], + camelcase: ["error", { properties: "never" }], // snake_case allowed in properties iif to satisfy an external contract / style + + // Ideally, all these warning should be error - let's fix them in the future. + "@typescript-eslint/no-unsafe-argument": "warn", + "@typescript-eslint/no-unsafe-assignment": "warn", + "@typescript-eslint/no-unsafe-call": "warn", + "@typescript-eslint/no-unsafe-member-access": "warn", + "@typescript-eslint/no-unsafe-return": "warn", + "@typescript-eslint/restrict-template-expressions": "warn", + }, + }, + { + files: ["*.spec.*"], + env: { + mocha: true, + }, + rules: {}, + }, + ], + globals: {}, + parserOptions: { + project: "tsconfig.json", + }, + plugins: ["prettier", "@typescript-eslint", "jsdoc"], + parser: "@typescript-eslint/parser", +}; diff --git a/.github/ISSUE_TEMPLATE/---report-a-bug.md b/.github/ISSUE_TEMPLATE/---report-a-bug.md index 3ad82bf60..abffad1b7 100644 --- a/.github/ISSUE_TEMPLATE/---report-a-bug.md +++ b/.github/ISSUE_TEMPLATE/---report-a-bug.md @@ -1,9 +1,9 @@ --- -name: '⚠️ Report a Bug' +name: "⚠️ Report a Bug" 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: '' +title: "" +labels: "" +assignees: "" ---