From c9c995dc3ee1a4954e7f524f4c482f5fe51340f8 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Wed, 13 Apr 2022 13:59:02 -0400 Subject: [PATCH 1/8] Implemented eventarc event publishing API (#1617) --- entrypoints.json | 4 + etc/firebase-admin.eventarc.api.md | 51 ++ package-lock.json | 968 ++++++++++++---------- package.json | 12 +- src/eventarc/cloudevent.ts | 95 +++ src/eventarc/eventarc-client-internal.ts | 157 ++++ src/eventarc/eventarc-utils.ts | 138 +++ src/eventarc/eventarc.ts | 198 +++++ src/eventarc/index.ts | 65 ++ test/unit/eventarc/eventarc-utils.spec.ts | 189 +++++ test/unit/eventarc/eventarc.spec.ts | 572 +++++++++++++ test/unit/index.spec.ts | 4 + 12 files changed, 2011 insertions(+), 442 deletions(-) create mode 100644 etc/firebase-admin.eventarc.api.md create mode 100644 src/eventarc/cloudevent.ts create mode 100644 src/eventarc/eventarc-client-internal.ts create mode 100644 src/eventarc/eventarc-utils.ts create mode 100644 src/eventarc/eventarc.ts create mode 100644 src/eventarc/index.ts create mode 100644 test/unit/eventarc/eventarc-utils.spec.ts create mode 100644 test/unit/eventarc/eventarc.spec.ts diff --git a/entrypoints.json b/entrypoints.json index 975db81888..55f2d766b2 100644 --- a/entrypoints.json +++ b/entrypoints.json @@ -55,5 +55,9 @@ "firebase-admin/remote-config": { "typings": "./lib/remote-config/index.d.ts", "dist": "./lib/remote-config/index.js" + }, + "firebase-admin/eventarc": { + "typings": "./lib/eventarc/index.d.ts", + "dist": "./lib/eventarc/index.js" } } diff --git a/etc/firebase-admin.eventarc.api.md b/etc/firebase-admin.eventarc.api.md new file mode 100644 index 0000000000..a070f8b391 --- /dev/null +++ b/etc/firebase-admin.eventarc.api.md @@ -0,0 +1,51 @@ +## API Report File for "firebase-admin.eventarc" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +/// + +import { Agent } from 'http'; + +// @public +export class Channel { + readonly allowedEventTypes?: string[]; + get eventarc(): Eventarc; + get name(): string; + publish(events: CloudEvent | CloudEvent[]): Promise; +} + +// @public +export interface ChannelOptions { + allowedEventTypes?: string[] | string | undefined; +} + +// @public +export interface CloudEvent { + [key: string]: any; + data?: object | string; + datacontenttype?: string; + id?: string; + source?: string; + specversion?: CloudEventVersion; + subject?: string; + time?: string; + type: string; +} + +// @public +export type CloudEventVersion = '1.0'; + +// @public +export class Eventarc { + // Warning: (ae-forgotten-export) The symbol "App" needs to be exported by the entry point index.d.ts + get app(): App; + channel(name: string, options?: ChannelOptions): Channel; + channel(options?: ChannelOptions): Channel; +} + +// @public +export function getEventarc(app?: App): Eventarc; + +``` diff --git a/package-lock.json b/package-lock.json index d6a48e21cf..4f4bb4adb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,60 +4,59 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.0" - } - }, - "@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" - } - }, "@babel/compat-data": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", - "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz", + "integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==", "dev": true }, "@babel/core": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz", - "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz", + "integrity": "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==", "dev": true, "requires": { - "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.7", - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.8", - "@babel/parser": "^7.17.8", + "@babel/generator": "^7.16.7", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helpers": "^7.16.7", + "@babel/parser": "^7.16.7", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.1.2", - "semver": "^6.3.0" + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "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" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } } }, "@babel/generator": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", - "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", + "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", "dev": true, "requires": { - "@babel/types": "^7.17.0", + "@babel/types": "^7.16.8", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -71,12 +70,12 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", - "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", + "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", "dev": true, "requires": { - "@babel/compat-data": "^7.17.7", + "@babel/compat-data": "^7.16.4", "@babel/helper-validator-option": "^7.16.7", "browserslist": "^4.17.5", "semver": "^6.3.0" @@ -130,28 +129,28 @@ } }, "@babel/helper-module-transforms": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", - "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", + "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-simple-access": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", "@babel/helper-validator-identifier": "^7.16.7", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" } }, "@babel/helper-simple-access": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", - "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", + "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", "dev": true, "requires": { - "@babel/types": "^7.17.0" + "@babel/types": "^7.16.7" } }, "@babel/helper-split-export-declaration": { @@ -176,20 +175,20 @@ "dev": true }, "@babel/helpers": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", - "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", + "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", "dev": true, "requires": { "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" } }, "@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz", + "integrity": "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -256,9 +255,9 @@ } }, "@babel/parser": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz", - "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz", + "integrity": "sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==", "dev": true }, "@babel/template": { @@ -270,26 +269,46 @@ "@babel/code-frame": "^7.16.7", "@babel/parser": "^7.16.7", "@babel/types": "^7.16.7" + }, + "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" + } + } } }, "@babel/traverse": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", - "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.8.tgz", + "integrity": "sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ==", "dev": true, "requires": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.3", + "@babel/generator": "^7.16.8", "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-function-name": "^7.16.7", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.3", - "@babel/types": "^7.17.0", + "@babel/parser": "^7.16.8", + "@babel/types": "^7.16.8", "debug": "^4.1.0", "globals": "^11.1.0" }, "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" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -299,9 +318,9 @@ } }, "@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz", + "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -374,27 +393,28 @@ } }, "@firebase/app": { - "version": "0.7.22", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.7.22.tgz", - "integrity": "sha512-v3AXSCwAvZyIFzOGgPAYtzjltm1M9R4U4yqsIBPf5B4ryaT1EGK+3ETZUOckNl5y2YwdKRJVPDDore+B2xg0Ug==", + "version": "0.7.18", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.7.18.tgz", + "integrity": "sha512-jomDaPaEQEWfFUqvxQw4TYSs2gCT2BN0Ec1//3CdMsc1NcppduS31bxsjhn3KdPbtx4opkaZ2FcA+buHtdw9dw==", "dev": true, "requires": { - "@firebase/component": "0.5.13", + "@firebase/component": "0.5.10", "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", + "@firebase/util": "1.4.3", + "idb": "3.0.2", "tslib": "^2.1.0" } }, "@firebase/app-compat": { - "version": "0.1.23", - "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.23.tgz", - "integrity": "sha512-c0QOhU2UVxZ7N5++nLQgKZ899ZC8+/ESa8VCzsQDwBw1T3MFAD1cG40KhB+CGtp/uYk/w6Jtk8k0xyZu6O2LOg==", + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.1.19.tgz", + "integrity": "sha512-a0TgAXcjF3htSdi10mRwAks1+73nwbmSMXzjlOQDYJ8t3HE7FvHxfB4hjuwHKfgr3MWZjcarsGKVr7LWhUAE8w==", "dev": true, "requires": { - "@firebase/app": "0.7.22", - "@firebase/component": "0.5.13", + "@firebase/app": "0.7.18", + "@firebase/component": "0.5.10", "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", + "@firebase/util": "1.4.3", "tslib": "^2.1.0" } }, @@ -404,14 +424,14 @@ "integrity": "sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg==" }, "@firebase/auth": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.19.12.tgz", - "integrity": "sha512-39/eJBmq5Ne+HoCJuQXlhaOH2e8qySxYUa5Z25mhcam8nmAMrBh7Ph1yZjUeSfLsSJiSXANMHK5dnVE+1TROXw==", + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.19.8.tgz", + "integrity": "sha512-pU3U8k70gXDYHjrIDlxnnfPkt6Eq1/61KikF7aps1ny8xmSyeUhbXxUbl2pvX5k7eK8uVQvm4uWFlPNJWMitww==", "dev": true, "requires": { - "@firebase/component": "0.5.13", + "@firebase/component": "0.5.10", "@firebase/logger": "0.3.2", - "@firebase/util": "1.5.2", + "@firebase/util": "1.4.3", "node-fetch": "2.6.7", "selenium-webdriver": "4.0.0-rc-1", "tslib": "^2.1.0" @@ -432,15 +452,15 @@ } }, "@firebase/auth-compat": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.2.12.tgz", - "integrity": "sha512-LKeKylktRj03xgW5ilSOW1c4AsMig15ogf5hDKa820t6Bp6MNabj8yq2TV0/Q4SP4Ox/yrTISJGVvk+TJuBecQ==", + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.2.8.tgz", + "integrity": "sha512-6gG8agS3LlSxnyObZ7TR1Ze41cJargpP+rGTuBz0WiOvrFcrMoZUjv+5oA5VvF2GiYVMvAzJImxmgYJhMse+GA==", "dev": true, "requires": { - "@firebase/auth": "0.19.12", + "@firebase/auth": "0.19.8", "@firebase/auth-types": "0.11.0", - "@firebase/component": "0.5.13", - "@firebase/util": "1.5.2", + "@firebase/component": "0.5.10", + "@firebase/util": "1.4.3", "node-fetch": "2.6.7", "selenium-webdriver": "^4.0.0-beta.2", "tslib": "^2.1.0" @@ -458,12 +478,12 @@ "dev": true }, "@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.10", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.10.tgz", + "integrity": "sha512-mzUpg6rsBbdQJvAdu1rNWabU3O7qdd+B+/ubE1b+pTbBKfw5ySRpRRE6sKcZ/oQuwLh0HHB6FRJHcylmI7jDzA==", "dev": true, "requires": { - "@firebase/util": "1.5.2", + "@firebase/util": "1.4.3", "tslib": "^2.1.0" } }, @@ -559,9 +579,9 @@ } }, "@firebase/util": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.5.2.tgz", - "integrity": "sha512-YvBH2UxFcdWG2HdFnhxZptPl2eVFlpOyTH66iDo13JPEYraWzWToZ5AMTtkyRHVmu7sssUpQlU9igy1KET7TOw==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.4.3.tgz", + "integrity": "sha512-gQJl6r0a+MElLQEyU8Dx0kkC2coPj67f/zKZrGR7z7WpLgVanhaCUqEsptwpwoxi9RMFIaebleG+C9xxoARq+Q==", "dev": true, "requires": { "tslib": "^2.1.0" @@ -635,9 +655,9 @@ } }, "@grpc/grpc-js": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.5.10.tgz", - "integrity": "sha512-++oAubX/7rJzlqH0ShyzDENNNDHYrlttdc3NM40KlaVQDcgGqQknuPoavmyTC+oNUDyxPCX5dHceKhfcgN3tiw==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.5.2.tgz", + "integrity": "sha512-JlBkWqm2qVtZTg6OQU9g0o9C3jR6Us0TekZlTVCESxq5wUbFUjrW5GijXPDpwLqdmabCRJ0xm9Ayyj+b9T9pow==", "optional": true, "requires": { "@grpc/proto-loader": "^0.6.4", @@ -777,38 +797,16 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, - "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, "@mapbox/node-pre-gyp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz", - "integrity": "sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz", + "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==", "dev": true, "requires": { - "detect-libc": "^2.0.0", + "detect-libc": "^1.0.3", "https-proxy-agent": "^5.0.0", "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", + "node-fetch": "^2.6.5", "nopt": "^5.0.0", "npmlog": "^5.0.1", "rimraf": "^3.0.2", @@ -828,35 +826,35 @@ } }, "@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==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.19.4.tgz", + "integrity": "sha512-iehC6YA3DGJvxTUaK7HUtQmP6hAQU07+Q/OR8TG4dVR6KpqCi9UPEVk8AgCvQkiK+6FbVEFQTx0qLuYk4EeuHg==", "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", + "@microsoft/api-extractor-model": "7.15.3", + "@microsoft/tsdoc": "0.13.2", + "@microsoft/tsdoc-config": "~0.15.2", + "@rushstack/node-core-library": "3.45.0", + "@rushstack/rig-package": "0.3.7", + "@rushstack/ts-command-line": "4.10.6", "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" + "typescript": "~4.5.2" }, "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==", + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz", + "integrity": "sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg==", "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==", + "version": "3.45.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.0.tgz", + "integrity": "sha512-YMuIJl19vQT1+g/OU9mLY6T5ZBT9uDlmeXExDQACpGuxTJW+LHNbk/lRX+eCApQI2eLBlaL4U68r3kZlqwbdmw==", "dev": true, "requires": { "@types/node": "12.20.24", @@ -871,9 +869,9 @@ } }, "@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==", + "version": "4.10.6", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.6.tgz", + "integrity": "sha512-Y3GkUag39sTIlukDg9mUp8MCHrrlJ27POrBNRQGc/uF+VVgX8M7zMzHch5zP6O1QVquWgD7Engdpn2piPYaS/g==", "dev": true, "requires": { "@types/argparse": "1.0.38", @@ -889,18 +887,18 @@ "dev": true }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "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" } }, "typescript": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", - "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", + "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", "dev": true }, "validator": { @@ -910,12 +908,12 @@ "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==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.2.tgz", + "integrity": "sha512-40TH47ukMHq5HrzkeVE40Ad7eIDKaRV2b+Qpi2prLc9X9eFJFzV7tMe5aH12e6avaSS/u5l653EQOv+J9PirPw==", "dev": true, "requires": { - "commander": "^2.20.3", + "commander": "^2.7.1", "lodash.get": "^4.4.2", "lodash.isequal": "^4.5.0", "validator": "^13.7.0" @@ -924,26 +922,26 @@ } }, "@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==", + "version": "7.15.3", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.15.3.tgz", + "integrity": "sha512-NkSjolmSI7NGvbdz0Y7kjQfdpD+j9E5CwXTxEyjDqxd10MI7GXV8DnAsQ57GFJcgHKgTjf2aUnYfMJ9w3aMicw==", "dev": true, "requires": { - "@microsoft/tsdoc": "0.14.1", - "@microsoft/tsdoc-config": "~0.16.1", - "@rushstack/node-core-library": "3.45.4" + "@microsoft/tsdoc": "0.13.2", + "@microsoft/tsdoc-config": "~0.15.2", + "@rushstack/node-core-library": "3.45.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==", + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz", + "integrity": "sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg==", "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==", + "version": "3.45.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.0.tgz", + "integrity": "sha512-YMuIJl19vQT1+g/OU9mLY6T5ZBT9uDlmeXExDQACpGuxTJW+LHNbk/lRX+eCApQI2eLBlaL4U68r3kZlqwbdmw==", "dev": true, "requires": { "@types/node": "12.20.24", @@ -964,9 +962,9 @@ "dev": true }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "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" @@ -979,12 +977,12 @@ "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==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.2.tgz", + "integrity": "sha512-40TH47ukMHq5HrzkeVE40Ad7eIDKaRV2b+Qpi2prLc9X9eFJFzV7tMe5aH12e6avaSS/u5l653EQOv+J9PirPw==", "dev": true, "requires": { - "commander": "^2.20.3", + "commander": "^2.7.1", "lodash.get": "^4.4.2", "lodash.isequal": "^4.5.0", "validator": "^13.7.0" @@ -999,21 +997,21 @@ "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==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.15.2.tgz", + "integrity": "sha512-mK19b2wJHSdNf8znXSMYVShAHktVr/ib0Ck2FA3lsVBSEhSI/TfXT7DJQkAYgcztTuwazGcg58ZjYdk0hTCVrA==", "dev": true, "requires": { - "@microsoft/tsdoc": "0.14.1", + "@microsoft/tsdoc": "0.13.2", "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==", + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz", + "integrity": "sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg==", "dev": true }, "resolve": { @@ -1158,9 +1156,9 @@ } }, "@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==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.7.tgz", + "integrity": "sha512-pzMsTSeTC8IiZ6EJLr53gGMvhT4oLWH+hxD7907cHyWuIUlEXFtu/2pK25vUQT13nKp5DJCWxXyYoGRk/h6rtA==", "dev": true, "requires": { "resolve": "~1.17.0", @@ -1189,9 +1187,9 @@ } }, "@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", + "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", "dev": true, "requires": { "@sinonjs/commons": "^1.7.0" @@ -1287,9 +1285,9 @@ "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==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.4.tgz", + "integrity": "sha512-1y3L1cHePcIm5vXkh1DSGf/zQq5n5xDKG1fpCvf18+uOkpce0Z1ozNFPkyWsVswK7ntN1sZBw3oU6gmN+pDUcA==", "dev": true, "requires": { "@types/chai": "*" @@ -1334,9 +1332,9 @@ } }, "@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==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.2.tgz", + "integrity": "sha512-Q74UyYRX/zIgl1HSp9tUX2PlG8glkVm+59r7aK4KGKzC5jqKIOX6rrVLRQrzpZUQ84VukHtRoeAuon2nIssHPQ==", "requires": { "@types/express": "*" } @@ -1348,9 +1346,9 @@ "dev": true }, "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, "@types/jsonwebtoken": { @@ -1363,9 +1361,9 @@ } }, "@types/lodash": { - "version": "4.14.182", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz", - "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==", + "version": "4.14.178", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz", + "integrity": "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==", "dev": true }, "@types/long": { @@ -1407,9 +1405,9 @@ } }, "@types/node": { - "version": "17.0.31", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz", - "integrity": "sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==" + "version": "17.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz", + "integrity": "sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==" }, "@types/qs": { "version": "6.9.7", @@ -1453,12 +1451,12 @@ } }, "@types/sinon": { - "version": "10.0.11", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.11.tgz", - "integrity": "sha512-dmZsHlBsKUtBpHriNjlK0ndlvEh8dcb9uV9Afsbt89QIyydpC7NcR+nWlAhASfy3GHnxTl4FX/aKE7XZUt/B4g==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.6.tgz", + "integrity": "sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg==", "dev": true, "requires": { - "@types/sinonjs__fake-timers": "*" + "@sinonjs/fake-timers": "^7.1.0" } }, "@types/sinon-chai": { @@ -1471,27 +1469,27 @@ "@types/sinon": "*" } }, - "@types/sinonjs__fake-timers": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", - "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", - "dev": true - }, "@types/tough-cookie": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz", "integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg==", "dev": true }, + "@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.21.0.tgz", - "integrity": "sha512-fTU85q8v5ZLpoZEyn/u1S2qrFOhi33Edo2CZ0+q1gDaWWm0JuPh3bgOyU8lM0edIEYgKLDkPFiZX2MOupgjlyg==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.11.0.tgz", + "integrity": "sha512-HJh33bgzXe6jGRocOj4FmefD7hRY4itgjzOrSs3JPrTNXsX7j5+nQPciAUj/1nZtwo2kAc3C75jZO+T23gzSGw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.21.0", - "@typescript-eslint/type-utils": "5.21.0", - "@typescript-eslint/utils": "5.21.0", + "@typescript-eslint/scope-manager": "5.11.0", + "@typescript-eslint/type-utils": "5.11.0", + "@typescript-eslint/utils": "5.11.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", @@ -1499,75 +1497,37 @@ "semver": "^7.3.5", "tsutils": "^3.21.0" }, - "dependencies": { - "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" - } - } - } - }, - "@typescript-eslint/parser": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.22.0.tgz", - "integrity": "sha512-piwC4krUpRDqPaPbFaycN70KCP87+PC5WZmrWs+DlVOxxmF+zI6b6hETv7Quy4s9wbkV16ikMeZgXsvzwI3icQ==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.22.0", - "@typescript-eslint/types": "5.22.0", - "@typescript-eslint/typescript-estree": "5.22.0", - "debug": "^4.3.2" - }, "dependencies": { "@typescript-eslint/scope-manager": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.22.0.tgz", - "integrity": "sha512-yA9G5NJgV5esANJCO0oF15MkBO20mIskbZ8ijfmlKIvQKg0ynVKfHZ15/nhAJN5m8Jn3X5qkwriQCiUntC9AbA==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.11.0.tgz", + "integrity": "sha512-z+K4LlahDFVMww20t/0zcA7gq/NgOawaLuxgqGRVKS0PiZlCTIUtX0EJbC0BK1JtR4CelmkPK67zuCgpdlF4EA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.22.0", - "@typescript-eslint/visitor-keys": "5.22.0" + "@typescript-eslint/types": "5.11.0", + "@typescript-eslint/visitor-keys": "5.11.0" } }, "@typescript-eslint/types": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.22.0.tgz", - "integrity": "sha512-T7owcXW4l0v7NTijmjGWwWf/1JqdlWiBzPqzAWhobxft0SiEvMJB56QXmeCQjrPuM8zEfGUKyPQr/L8+cFUBLw==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.11.0.tgz", + "integrity": "sha512-cxgBFGSRCoBEhvSVLkKw39+kMzUKHlJGVwwMbPcTZX3qEhuXhrjwaZXWMxVfxDgyMm+b5Q5b29Llo2yow8Y7xQ==", "dev": true }, - "@typescript-eslint/typescript-estree": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.22.0.tgz", - "integrity": "sha512-EyBEQxvNjg80yinGE2xdhpDYm41so/1kOItl0qrjIiJ1kX/L/L8WWGmJg8ni6eG3DwqmOzDqOhe6763bF92nOw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.22.0", - "@typescript-eslint/visitor-keys": "5.22.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, "@typescript-eslint/visitor-keys": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.22.0.tgz", - "integrity": "sha512-DbgTqn2Dv5RFWluG88tn0pP6Ex0ROF+dpDO1TNNZdRtLjUr6bdznjA6f/qNqJLjd2PgguAES2Zgxh/JzwzETDg==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.11.0.tgz", + "integrity": "sha512-E8w/vJReMGuloGxJDkpPlGwhxocxOpSVgSvjiLO5IxZPmxZF30weOeJYyPSEACwM+X4NziYS9q+WkN/2DHYQwA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.22.0", + "@typescript-eslint/types": "5.11.0", "eslint-visitor-keys": "^3.0.0" } }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "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" @@ -1575,41 +1535,53 @@ } } }, + "@typescript-eslint/parser": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.12.0.tgz", + "integrity": "sha512-MfSwg9JMBojMUoGjUmX+D2stoQj1CBYTCP0qnnVtu9A+YQXVKNtLjasYh+jozOcrb/wau8TCfWOkQTiOAruBog==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.12.0", + "@typescript-eslint/types": "5.12.0", + "@typescript-eslint/typescript-estree": "5.12.0", + "debug": "^4.3.2" + } + }, "@typescript-eslint/scope-manager": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.21.0.tgz", - "integrity": "sha512-XTX0g0IhvzcH/e3393SvjRCfYQxgxtYzL3UREteUneo72EFlt7UNoiYnikUtmGVobTbhUDByhJ4xRBNe+34kOQ==", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.12.0.tgz", + "integrity": "sha512-GAMobtIJI8FGf1sLlUWNUm2IOkIjvn7laFWyRx7CLrv6nLBI7su+B7lbStqVlK5NdLvHRFiJo2HhiDF7Ki01WQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.21.0", - "@typescript-eslint/visitor-keys": "5.21.0" + "@typescript-eslint/types": "5.12.0", + "@typescript-eslint/visitor-keys": "5.12.0" } }, "@typescript-eslint/type-utils": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.21.0.tgz", - "integrity": "sha512-MxmLZj0tkGlkcZCSE17ORaHl8Th3JQwBzyXL/uvC6sNmu128LsgjTX0NIzy+wdH2J7Pd02GN8FaoudJntFvSOw==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.11.0.tgz", + "integrity": "sha512-wDqdsYO6ofLaD4DsGZ0jGwxp4HrzD2YKulpEZXmgN3xo4BHJwf7kq49JTRpV0Gx6bxkSUmc9s0EIK1xPbFFpIA==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.21.0", + "@typescript-eslint/utils": "5.11.0", "debug": "^4.3.2", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.21.0.tgz", - "integrity": "sha512-XnOOo5Wc2cBlq8Lh5WNvAgHzpjnEzxn4CJBwGkcau7b/tZ556qrWXQz4DJyChYg8JZAD06kczrdgFPpEQZfDsA==", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.12.0.tgz", + "integrity": "sha512-JowqbwPf93nvf8fZn5XrPGFBdIK8+yx5UEGs2QFAYFI8IWYfrzz+6zqlurGr2ctShMaJxqwsqmra3WXWjH1nRQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.21.0.tgz", - "integrity": "sha512-Y8Y2T2FNvm08qlcoSMoNchh9y2Uj3QmjtwNMdRQkcFG7Muz//wfJBGBxh8R7HAGQFpgYpdHqUpEoPQk+q9Kjfg==", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.12.0.tgz", + "integrity": "sha512-Dd9gVeOqt38QHR0BEA8oRaT65WYqPYbIc5tRFQPkfLquVEFPD1HAtbZT98TLBkEcCkvwDYOAvuSvAD9DnQhMfQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.21.0", - "@typescript-eslint/visitor-keys": "5.21.0", + "@typescript-eslint/types": "5.12.0", + "@typescript-eslint/visitor-keys": "5.12.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", @@ -1618,9 +1590,9 @@ }, "dependencies": { "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "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" @@ -1629,26 +1601,78 @@ } }, "@typescript-eslint/utils": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.21.0.tgz", - "integrity": "sha512-q/emogbND9wry7zxy7VYri+7ydawo2HDZhRZ5k6yggIvXa7PvBbAAZ4PFH/oZLem72ezC4Pr63rJvDK/sTlL8Q==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.11.0.tgz", + "integrity": "sha512-g2I480tFE1iYRDyMhxPAtLQ9HAn0jjBtipgTCZmd9I9s11OV8CTsG+YfFciuNDcHqm4csbAgC2aVZCHzLxMSUw==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.21.0", - "@typescript-eslint/types": "5.21.0", - "@typescript-eslint/typescript-estree": "5.21.0", + "@typescript-eslint/scope-manager": "5.11.0", + "@typescript-eslint/types": "5.11.0", + "@typescript-eslint/typescript-estree": "5.11.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.11.0.tgz", + "integrity": "sha512-z+K4LlahDFVMww20t/0zcA7gq/NgOawaLuxgqGRVKS0PiZlCTIUtX0EJbC0BK1JtR4CelmkPK67zuCgpdlF4EA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.11.0", + "@typescript-eslint/visitor-keys": "5.11.0" + } + }, + "@typescript-eslint/types": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.11.0.tgz", + "integrity": "sha512-cxgBFGSRCoBEhvSVLkKw39+kMzUKHlJGVwwMbPcTZX3qEhuXhrjwaZXWMxVfxDgyMm+b5Q5b29Llo2yow8Y7xQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.11.0.tgz", + "integrity": "sha512-yVH9hKIv3ZN3lw8m/Jy5I4oXO4ZBMqijcXCdA4mY8ull6TPTAoQnKKrcZ0HDXg7Bsl0Unwwx7jcXMuNZc0m4lg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.11.0", + "@typescript-eslint/visitor-keys": "5.11.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.11.0.tgz", + "integrity": "sha512-E8w/vJReMGuloGxJDkpPlGwhxocxOpSVgSvjiLO5IxZPmxZF30weOeJYyPSEACwM+X4NziYS9q+WkN/2DHYQwA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.11.0", + "eslint-visitor-keys": "^3.0.0" + } + }, + "semver": { + "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" + } + } } }, "@typescript-eslint/visitor-keys": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.21.0.tgz", - "integrity": "sha512-SX8jNN+iHqAF0riZQMkm7e8+POXa/fXw5cxL+gjpyP+FI+JVNhii53EmQgDAfDcBpFekYSlO0fGytMQwRiMQCA==", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.12.0.tgz", + "integrity": "sha512-cFwTlgnMV6TgezQynx2c/4/tx9Tufbuo9LPzmWqyRC3QC4qTGkAG1C6pBr0/4I10PAI/FlYunI3vJjIcu+ZHMg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.21.0", + "@typescript-eslint/types": "5.12.0", "eslint-visitor-keys": "^3.0.0" } }, @@ -1722,13 +1746,10 @@ } }, "ansi-colors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", - "dev": true, - "requires": { - "ansi-wrap": "^0.1.0" - } + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true }, "ansi-cyan": { "version": "0.1.1", @@ -2339,15 +2360,15 @@ "dev": true }, "browserslist": { - "version": "4.20.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", - "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001317", - "electron-to-chromium": "^1.4.84", + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", "escalade": "^3.1.1", - "node-releases": "^2.0.2", + "node-releases": "^2.0.1", "picocolors": "^1.0.0" } }, @@ -2431,9 +2452,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001323", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001323.tgz", - "integrity": "sha512-e4BF2RlCVELKx8+RmklSEIVub1TWrmdhvA5kEUueummz1XyySW0DVk+3x9HyhU9MuWTa2BhqLgEuEmUwASAdCA==", + "version": "1.0.30001299", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001299.tgz", + "integrity": "sha512-iujN4+x7QzqA2NCSrS5VUy+4gLmRd4xv6vbBBsmfVqTx8bLAD8097euLqQgKxSVLvxjSDcvF1T/i9ocgnUFexw==", "dev": true }, "caseless": { @@ -2443,16 +2464,15 @@ "dev": true }, "chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", "dev": true, "requires": { "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" } @@ -2466,6 +2486,15 @@ "check-error": "^1.0.2" } }, + "chai-exclude": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chai-exclude/-/chai-exclude-2.1.0.tgz", + "integrity": "sha512-IBnm50Mvl3O1YhPpTgbU8MK0Gw7NHcb18WT2TxGdPKOMtdtZVKLHmQwdvOF7mTlHVQStbXuZKFwkevFtbHjpVg==", + "dev": true, + "requires": { + "fclone": "^1.0.11" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2984,15 +3013,15 @@ } }, "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==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-2.1.0.tgz", + "integrity": "sha512-X/b2gM7e8zQ7siiE0DhRLeNMpuCkIqec5Jnx4GMk/HWB71a6e5Lz48mH9/GIS/hpLsBRFZfMF1gjXBkY0vq5oA==", "optional": true }, "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "requires": { "ms": "2.1.2" } @@ -3149,9 +3178,9 @@ "dev": true }, "detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true }, "dicer": { @@ -3236,9 +3265,9 @@ } }, "electron-to-chromium": { - "version": "1.4.103", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.103.tgz", - "integrity": "sha512-c/uKWR1Z/W30Wy/sx3dkZoj4BijbXX85QKWu9jJfjho3LBAXNEGAEW3oWiGb+dotA6C6BzCTxL2/aLes7jlUeg==", + "version": "1.4.44", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.44.tgz", + "integrity": "sha512-tHGWiUUmY7GABK8+DNcr474cnZDTzD8x1736SlDosVH8+/vRJeqfaIBAEHFtMjddz/0T4rKKYsxEc8BwQRdBpw==", "dev": true }, "emoji-regex": { @@ -3270,9 +3299,9 @@ } }, "es-abstract": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.2.tgz", - "integrity": "sha512-gfSBJoZdlL2xRiOCy0g8gLMryhoe1TlimjzU99L/31Z8QEGIhVQI+EWwt5lT+AuU9SnorVupXFqqOGqGfsyO6w==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -3281,15 +3310,15 @@ "get-intrinsic": "^1.1.1", "get-symbol-description": "^1.0.0", "has": "^1.0.3", - "has-symbols": "^1.0.3", + "has-symbols": "^1.0.2", "internal-slot": "^1.0.3", "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.2", + "is-negative-zero": "^2.0.1", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.1", "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.0", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", "object-keys": "^1.1.1", "object.assign": "^4.1.2", "string.prototype.trimend": "^1.0.4", @@ -3309,14 +3338,14 @@ } }, "es5-ext": { - "version": "0.10.59", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.59.tgz", - "integrity": "sha512-cOgyhW0tIJyQY1Kfw6Kr0viu9ZlUctVchRMZ7R0HiH3dxTSp5zJDLecwxUqPUrGKMsgBI1wd1FL+d9Jxfi4cLw==", + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", "dev": true, "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" } }, "es6-error": { @@ -3645,9 +3674,9 @@ }, "dependencies": { "type": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", - "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", + "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==", "dev": true } } @@ -3767,9 +3796,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.10.tgz", + "integrity": "sha512-s9nFhFnvR63wls6/kM88kQqDhMu0AfdjqouE2l5GVQPbqLgyFjjU5ry/r2yKsJxpb9Py1EYNqieFrmMaX4v++A==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -3814,6 +3843,12 @@ "websocket-driver": ">=0.5.1" } }, + "fclone": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz", + "integrity": "sha1-EOhdo4v+p/xZk0HClu4ddyZu5kA=", + "dev": true + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -4019,9 +4054,9 @@ } }, "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", + "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", "dev": true }, "flush-write-stream": { @@ -4528,9 +4563,9 @@ } }, "google-gax": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.30.1.tgz", - "integrity": "sha512-AR00wrunctUqwKQFl15Yq5bo9NuFLnT0zguZYCf8eAqoOUMbxn9V1L0ONCtV4+P9z7sLu+cjtgl+5b4eRZvktg==", + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.29.4.tgz", + "integrity": "sha512-3o6cByD2fE1yIc6i1gpKMQlJStqlvu8Sa/Ly/HCQ6GPHpltpVfkTT4KVj2YLVa7WTSDoGbsLBDmJ1KfN1uNiRw==", "optional": true, "requires": { "@grpc/grpc-js": "~1.5.0", @@ -4539,11 +4574,11 @@ "abort-controller": "^3.0.0", "duplexify": "^4.0.0", "fast-text-encoding": "^1.0.3", - "google-auth-library": "^7.14.0", + "google-auth-library": "^7.6.1", "is-stream-ended": "^0.1.4", "node-fetch": "^2.6.1", - "object-hash": "^3.0.0", - "proto3-json-serializer": "^0.1.8", + "object-hash": "^2.1.1", + "proto3-json-serializer": "^0.1.7", "protobufjs": "6.11.2", "retry-request": "^4.0.0" } @@ -4563,13 +4598,13 @@ "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" }, "gtoken": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.3.2.tgz", - "integrity": "sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.3.1.tgz", + "integrity": "sha512-yqOREjzLHcbzz1UrQoxhBtpk8KjrVhuqPE7od1K2uhyxG2BHjKZetlbLw/SPZak/QqTIQW+addS+EcjqQsZbwQ==", "optional": true, "requires": { "gaxios": "^4.0.0", - "google-p12-pem": "^3.1.3", + "google-p12-pem": "^3.0.3", "jws": "^4.0.0" } }, @@ -4585,6 +4620,15 @@ "vinyl-fs": "^3.0.0" }, "dependencies": { + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -4838,9 +4882,9 @@ "dev": true }, "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.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", "dev": true }, "has-tostringtag": { @@ -5010,6 +5054,12 @@ "debug": "4" } }, + "idb": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/idb/-/idb-3.0.2.tgz", + "integrity": "sha512-+FLa/0sTXqyux0o6C+i2lOR0VoS60LU/jzUo5xjfY6+7sEEgy4Gz1O7yFBXvjd7N0NyIGWIRg8DcQSLEG+VSPw==", + "dev": true + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -5545,9 +5595,9 @@ } }, "istanbul-reports": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", - "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.3.tgz", + "integrity": "sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -5643,10 +5693,13 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", - "dev": true + "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" + } }, "jsonfile": { "version": "4.0.0", @@ -5713,9 +5766,9 @@ } }, "jszip": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.9.1.tgz", - "integrity": "sha512-H9A60xPqJ1CuC4Ka6qxzXZeU8aNmgOeP5IFqwJbQQwtu2EUYxota3LdsiZWplF7Wgd9tkAd0mdu36nceSaPuYw==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz", + "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==", "dev": true, "requires": { "lie": "~3.3.0", @@ -5789,6 +5842,16 @@ "jose": "^2.0.5", "limiter": "^1.1.5", "lru-memoizer": "^2.1.4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + } } }, "jws": { @@ -6061,15 +6124,6 @@ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", "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": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -6298,13 +6352,13 @@ "dev": true }, "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "braces": "^3.0.1", + "picomatch": "^2.2.3" } }, "mime": { @@ -6314,22 +6368,22 @@ "optional": true }, "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.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" }, "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==", + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", "requires": { - "mime-db": "1.52.0" + "mime-db": "1.51.0" } }, "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "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" @@ -6417,12 +6471,6 @@ "yargs-unparser": "2.0.0" }, "dependencies": { - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, "anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -6470,6 +6518,23 @@ "readdirp": "~3.6.0" } }, + "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" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -6641,9 +6706,9 @@ "dev": true }, "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, "nice-try": { @@ -6666,9 +6731,9 @@ } }, "nock": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.2.4.tgz", - "integrity": "sha512-8GPznwxcPNCH/h8B+XZcKjYPXnUV5clOKCjAqyjsiqA++MpNx9E9+t8YPp0MbThO+KauRo7aZJ1WuIZmOrT2Ug==", + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.2.2.tgz", + "integrity": "sha512-PcBHuvl9i6zfaJ50A7LS55oU+nFLv8htXIhffJO+FxyfibdZ4jEvd9kTuvkrJireBFIGMZ+oUIRpMK5gU9h//g==", "dev": true, "requires": { "debug": "^4.1.0", @@ -6706,9 +6771,9 @@ } }, "node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", "dev": true }, "node-version": { @@ -7162,9 +7227,9 @@ } }, "object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", "optional": true }, "object-inspect": { @@ -7596,6 +7661,17 @@ "arr-diff": "^4.0.0", "arr-union": "^3.1.0", "extend-shallow": "^3.0.2" + }, + "dependencies": { + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + } } }, "posix-character-classes": { @@ -7644,9 +7720,9 @@ "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.7", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-0.1.7.tgz", + "integrity": "sha512-91Yn0rgRL/diKZABrQIVnOm7k3HttbxfP5nm0xMjHctDbCNqaLkGc6O25bwc5Y7WmpxfUdYfeidbhWoyO1aJfA==", "optional": true, "requires": { "protobufjs": "^6.11.2" @@ -8408,9 +8484,9 @@ } }, "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==" + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" }, "sinon": { "version": "13.0.2", @@ -8424,6 +8500,17 @@ "diff": "^5.0.0", "nise": "^5.1.1", "supports-color": "^7.2.0" + }, + "dependencies": { + "@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + } } }, "sinon-chai": { @@ -9085,9 +9172,9 @@ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, "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==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.5.0.tgz", + "integrity": "sha512-6kEJKwVxAJ35W4akuiysfKwKmjkbYxwQMTBaAxo9KKAx/Yd26mPUyhGz3ji+EsJoAgrLqVsYHNuuYwQe22lbtw==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.7.0", @@ -9359,8 +9446,7 @@ "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "optional": true + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "v8-compile-cache": { "version": "2.3.0", @@ -9687,9 +9773,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yargs": { - "version": "17.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", - "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", + "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", "dev": true, "requires": { "cliui": "^7.0.2", @@ -9702,9 +9788,9 @@ }, "dependencies": { "yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", "dev": true } } diff --git a/package.json b/package.json index 1a4d967a34..f0cd1c6e3f 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,9 @@ "auth": [ "lib/auth" ], + "eventarc": [ + "lib/eventarc" + ], "database": [ "lib/database" ], @@ -121,6 +124,10 @@ "require": "./lib/database/index.js", "import": "./lib/esm/database/index.js" }, + "./eventarc": { + "require": "./lib/eventarc/index.js", + "import": "./lib/esm/eventarc/index.js" + }, "./firestore": { "require": "./lib/firestore/index.js", "import": "./lib/esm/firestore/index.js" @@ -165,7 +172,8 @@ "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" }, "optionalDependencies": { "@google-cloud/firestore": "^4.15.1", @@ -190,11 +198,13 @@ "@types/request-promise": "^4.1.41", "@types/sinon": "^10.0.2", "@types/sinon-chai": "^3.0.0", + "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", "bcrypt": "^5.0.0", "chai": "^4.2.0", "chai-as-promised": "^7.0.0", + "chai-exclude": "^2.1.0", "chalk": "^4.1.1", "child-process-promise": "^2.2.1", "del": "^6.0.0", diff --git a/src/eventarc/cloudevent.ts b/src/eventarc/cloudevent.ts new file mode 100644 index 0000000000..1c2fbdf66f --- /dev/null +++ b/src/eventarc/cloudevent.ts @@ -0,0 +1,95 @@ + +/*! + * @license + * 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. + */ + +/** + * A CloudEvent version. + */ +export type CloudEventVersion = '1.0'; + +/** + * A CloudEvent describes event data. + * + * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md + */ +export interface CloudEvent { + + /** + * Event identified. If not provided will be auto-generated with a UUID. + * + * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#id + */ + id?: string; + + /** + * Identifies the context in which an event happened. If not provided the value of `EVENTARC_CLOUD_EVENT_SOURCE` + * environment variable will be used and if that is not set a validation error will be thrown. + * + * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#source-1 + */ + source?: string; + + /** + * The version of the CloudEvents specification which the event uses. If not provided will be set to `1.0` -- + * the only supported value. + * + * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#specversion + */ + specversion?: CloudEventVersion; + + /** + * Type of the event. Should be prefixed with a reverse-DNS name (`com.my-org.v1.something.happended`). + * + * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#type + */ + type: string; + + /** + * Subject (context) of the event in the context of the event producer. + * + * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#subject + */ + subject?: string; + + /** + * MIME type of the data being sent with the event in the `data` field. Only `application/json` and `text/plain` + * are currently supported. If not specified wil be automatically inferred from the type of provided data. + * + * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#datacontenttype + */ + datacontenttype?: string; + + /** + * Timestamp of when the occurrence happened. Must in ISO time format. If not specified current time (at the + * moment of publishing) will be used. + * + * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#time + */ + time?: string; + + /** + * Data payload of the event. Objects will be strigified with JSON and strings will be passed along as-is. + */ + data?: object | string; + + /** + * Custom attributes/extensions. Must be strings. Will be added to the event as is. + * + * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#extension-context-attributes + */ + [key: string]: any; + } diff --git a/src/eventarc/eventarc-client-internal.ts b/src/eventarc/eventarc-client-internal.ts new file mode 100644 index 0000000000..7277a7f5af --- /dev/null +++ b/src/eventarc/eventarc-client-internal.ts @@ -0,0 +1,157 @@ +/*! + * @license + * 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. + */ + +import * as validator from '../utils/validator'; +import { FirebaseEventarcError, toCloudEventProtoFormat } from './eventarc-utils'; +import { App } from '../app'; +import { Channel } from './eventarc'; +import { + HttpRequestConfig, HttpClient, HttpError, AuthorizedHttpClient +} from '../utils/api-request'; +import { FirebaseApp } from '../app/firebase-app'; +import * as utils from '../utils'; +import { PrefixedFirebaseError } from '../utils/error'; +import { CloudEvent } from './cloudevent'; + +const EVENTARC_API = 'https://eventarcpublishing.googleapis.com/v1'; +const FIREBASE_VERSION_HEADER = { + 'X-Firebase-Client': `fire-admin-node/${utils.getSdkVersion()}`, +}; +const CHANNEL_NAME_REGEX = /^(projects\/([^/]+)\/)?locations\/([^/]+)\/channels\/([^/]+)$/; +const DEFAULT_CHANNEL_REGION = 'us-central1'; + +/** + * Class that facilitates sending requests to the Eventarc backend API. + * + * @internal + */ +export class EventarcApiClient { + private readonly httpClient: HttpClient; + private projectId?: string; + private readonly resolvedChannelName: Promise; + + constructor(private readonly app: App, private readonly channel: Channel) { + if (!validator.isNonNullObject(app) || !('options' in app)) { + throw new FirebaseEventarcError( + 'invalid-argument', + 'First argument passed to Channel() must be a valid Eventarc service instance.'); + } + this.httpClient = new AuthorizedHttpClient(app as FirebaseApp); + this.resolvedChannelName = this.resolveChannelName(channel.name); + } + + private getProjectId(): Promise { + if (this.projectId) { + return Promise.resolve(this.projectId); + } + return utils.findProjectId(this.app) + .then((projectId) => { + if (!validator.isNonEmptyString(projectId)) { + throw new FirebaseEventarcError( + 'unknown-error', + 'Failed to determine project ID. Initialize the ' + + 'SDK with service account credentials or set project ID as an app option. ' + + 'Alternatively, set the GOOGLE_CLOUD_PROJECT environment variable.'); + } + this.projectId = projectId; + return projectId; + }); + } + + /** + * Publishes provided events to this channel. If channel was created with + * `allowedEventsTypes` and event type is not on that list, the event will + * be ignored. + * + * The following CloudEvent fields will be auto-populated if not set: + * * specversion - `1.0` + * * id - uuidv4() + * * source - will be populated with `process.env.EVENTARC_CLOUD_EVENT_SOURCE` and + * if not set an error will be thrown. + * + * @param events - CloudEvent to publish to the channel. + */ + public async publish(events: CloudEvent | CloudEvent[]): Promise { + if (!Array.isArray(events)) { + events = [events as CloudEvent]; + } + return this.publishToEventarcApi( + await this.resolvedChannelName, + events + .filter(e => typeof this.channel.allowedEventTypes === 'undefined' || + this.channel.allowedEventTypes.includes(e.type)) + .map(toCloudEventProtoFormat)); + } + + private async publishToEventarcApi(channel:string, events: CloudEvent[]): Promise { + if (events.length === 0) { + return; + } + const request: HttpRequestConfig = { + method: 'POST', + url: `${EVENTARC_API}/${channel}:publishEvents`, + data: JSON.stringify({ events }), + }; + return this.sendRequest(request); + } + + private sendRequest(request: HttpRequestConfig): Promise { + request.headers = FIREBASE_VERSION_HEADER; + return this.httpClient.send(request) + .then(() => undefined) + .catch((err) => { + throw this.toFirebaseError(err); + }); + } + + private toFirebaseError(err: HttpError): PrefixedFirebaseError { + if (err instanceof PrefixedFirebaseError) { + return err; + } + + const response = err.response; + return new FirebaseEventarcError( + 'unknown-error', + `Unexpected response with status: ${response.status} and body: ${response.text}`); + } + + private resolveChannelName(name: string): Promise { + if (!name.includes('/')) { + const location = DEFAULT_CHANNEL_REGION; + const channelId = name; + return this.resolveChannelNameProjectId(location, channelId); + } else { + const match = CHANNEL_NAME_REGEX.exec(name); + if (match === null || match.length < 4) { + throw new FirebaseEventarcError('invalid-argument', 'Invalid channel name format.'); + } + const projectId = match[2]; + const location = match[3]; + const channelId = match[4]; + if (validator.isNonEmptyString(projectId)) { + return Promise.resolve(`projects/${projectId}/locations/${location}/channels/${channelId}`); + } else { + return this.resolveChannelNameProjectId(location, channelId); + } + } + } + + private async resolveChannelNameProjectId(location: string, channelId: string): Promise { + const projectId = await this.getProjectId(); + return `projects/${projectId}/locations/${location}/channels/${channelId}`; + } +} diff --git a/src/eventarc/eventarc-utils.ts b/src/eventarc/eventarc-utils.ts new file mode 100644 index 0000000000..8a5a001753 --- /dev/null +++ b/src/eventarc/eventarc-utils.ts @@ -0,0 +1,138 @@ +/*! + * @license + * 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. + */ + +import { PrefixedFirebaseError } from '../utils/error'; +import { CloudEvent } from './cloudevent'; +import { v4 as uuid } from 'uuid'; +import * as validator from '../utils/validator'; + +// List of CloudEvent properties that are handled "by hand" and should be skipped by +// automatic attribute copy. +const TOP_LEVEL_CE_ATTRS: string[] = + ['id', 'type', 'specversion', 'source', 'data', 'time', 'datacontenttype', 'subject']; + +export type EventarcErrorCode = 'unknown-error' | 'invalid-argument' + +/** + * Firebase Eventarc error code structure. This extends PrefixedFirebaseError. + * + * @param code - The error code. + * @param message - The error message. + * @constructor + */ +export class FirebaseEventarcError extends PrefixedFirebaseError { + constructor(code: EventarcErrorCode, message: string) { + super('eventarc', code, message); + } +} + +export function toCloudEventProtoFormat(ce: CloudEvent): any { + const source = ce.source ?? process.env.EVENTARC_CLOUD_EVENT_SOURCE; + if (typeof source === 'undefined' || !validator.isNonEmptyString(source)) { + throw new FirebaseEventarcError('invalid-argument', "CloudEvent 'source' is required."); + } + if (!validator.isNonEmptyString(ce.type)) { + throw new FirebaseEventarcError('invalid-argument', "CloudEvent 'type' is required."); + } + const out: Record = { + '@type': 'type.googleapis.com/io.cloudevents.v1.CloudEvent', + 'id': ce.id ?? uuid(), + 'type': ce.type, + 'specVersion': ce.specversion ?? '1.0', + 'source': source + } + + if (typeof ce.time !== 'undefined') { + if (!validator.isISODateString(ce.time)) { + throw new FirebaseEventarcError( + 'invalid-argument', "CloudEvent 'tyme' must be in ISO date format."); + } + setAttribute(out, 'time', { + 'ceTimestamp': ce.time + }); + } else { + setAttribute(out, 'time', { + 'ceTimestamp': new Date().toISOString() + }); + } + if (typeof ce.datacontenttype !== 'undefined') { + if (!validator.isNonEmptyString(ce.datacontenttype)) { + throw new FirebaseEventarcError( + 'invalid-argument', + "CloudEvent 'datacontenttype' if specified must be non-empty string."); + } + setAttribute(out, 'datacontenttype', { + 'ceString': ce.datacontenttype + }); + } + if (ce.subject) { + if (!validator.isNonEmptyString(ce.datacontenttype)) { + throw new FirebaseEventarcError( + 'invalid-argument', + "CloudEvent 'subject' if specified must be non-empty string."); + } + setAttribute(out, 'subject', { + 'ceString': ce.subject + }); + } + + if (typeof ce.data === 'undefined') { + throw new FirebaseEventarcError('invalid-argument', "CloudEvent 'data' is required."); + } + if (validator.isObject(ce.data)) { + out['textData'] = JSON.stringify(ce.data); + if (!ce.datacontenttype) { + setAttribute(out, 'datacontenttype', { + 'ceString': 'application/json' + }); + } + } else if (validator.isNonEmptyString(ce.data)) { + out['textData'] = ce.data; + if (!ce.datacontenttype) { + setAttribute(out, 'datacontenttype', { + 'ceString': 'text/plain' + }); + } + } else { + throw new FirebaseEventarcError( + 'invalid-argument', + `CloudEvent 'data' must be string or an object (which will be converted to JSON), got '${typeof ce.data}'.`); + } + + for (const attr in ce) { + if (TOP_LEVEL_CE_ATTRS.includes(attr)) { + continue; + } + if (!validator.isNonEmptyString(ce[attr])) { + throw new FirebaseEventarcError( + 'invalid-argument', + `CloudEvent extension attributes ('${attr}') must be string.`); + } + setAttribute(out, attr, { + 'ceString': ce[attr] + }); + } + + return out; +} + +function setAttribute(event: any, attr: string, value: any): void { + if (!Object.prototype.hasOwnProperty.call(event, 'attributes')) { + event.attributes = {}; + } + event['attributes'][attr] = value; +} diff --git a/src/eventarc/eventarc.ts b/src/eventarc/eventarc.ts new file mode 100644 index 0000000000..214d69d1d7 --- /dev/null +++ b/src/eventarc/eventarc.ts @@ -0,0 +1,198 @@ +/*! + * @license + * 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. + */ + +import { App } from '../app'; +import * as validator from '../utils/validator'; +import { FirebaseEventarcError } from './eventarc-utils'; +import { CloudEvent } from './cloudevent'; +import { EventarcApiClient } from './eventarc-client-internal'; + +/** + * Channel options interface. + */ +export interface ChannelOptions { + /** + * An array of allowed event types. If specified, publishing events of + * unknown types will be a no op. When not provided, no event filtering is + * performed. + */ + allowedEventTypes?: string[] | string | undefined +} + +/** + * Eventarc service bound to the provided app. + */ +export class Eventarc { + + private readonly appInternal: App; + + /** + * @internal + */ + constructor(app: App) { + if (!validator.isNonNullObject(app) || !('options' in app)) { + throw new FirebaseEventarcError( + 'invalid-argument', + 'First argument passed to Eventarc() must be a valid Firebase app instance.', + ); + } + + this.appInternal = app; + } + + /** + * The {@link firebase-admin.app#App} associated with the current `Eventarc` service + * instance. + * + * @example + * ```javascript + * var app = eventarc.app; + * ``` + */ + get app(): App { + return this.appInternal; + } + + /** + * Creates a reference to the Eventarc channel using the provided channel resource name. + * The channel resource name 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}` + * + * @param name - Channel resource name. + * @param options - (optional) additional channel options + * @returns Eventarc channel reference for publishing events. + */ + public channel(name: string, options?: ChannelOptions): Channel; + + /** + * Create a reference to the default Firebase channel: + * `locations/us-central1/channels/firebase` + * + * @param options - (optional) additional channel options + * @returns Eventarc channel reference for publishing events. + */ + public channel(options?: ChannelOptions): Channel; + + public channel(nameOrOptions?: string | ChannelOptions, options?: ChannelOptions): Channel { + let channel: string; + let opts: ChannelOptions; + if (validator.isNonEmptyString(nameOrOptions)) { + channel = nameOrOptions; + } else { + channel = 'locations/us-central1/channels/firebase'; + } + + if (validator.isNonNullObject(nameOrOptions)) { + opts = nameOrOptions as ChannelOptions; + } else { + opts = options as ChannelOptions; + } + let allowedEventTypes : string[] | undefined = undefined; + if (typeof opts?.allowedEventTypes === 'string') { + allowedEventTypes = opts.allowedEventTypes.split(','); + } else if (validator.isArray(opts?.allowedEventTypes)) { + allowedEventTypes = opts?.allowedEventTypes as string[]; + } else if (typeof opts?.allowedEventTypes !== 'undefined') { + throw new FirebaseEventarcError( + 'invalid-argument', + 'AllowedEventTypes must be either an array of strings or a comma separated string.', + ); + } + return new Channel(this, channel, allowedEventTypes); + } +} + +/** + * Eventarc Channel. + */ +export class Channel { + private readonly eventarcInternal: Eventarc; + private nameInternal: string; + + /** + * List if event types allowed by this channel for publishing. Other event types will be ignored. + */ + public readonly allowedEventTypes?: string[] + + private readonly client: EventarcApiClient; + + /** + * @internal + */ + constructor(eventarc: Eventarc, name: string, allowedEventTypes?: string[]) { + if (!validator.isNonNullObject(eventarc)) { + throw new FirebaseEventarcError( + 'invalid-argument', + 'First argument passed to Channel() must be a valid Eventarc service instance.', + ); + } + if (!validator.isNonEmptyString(name)) { + throw new FirebaseEventarcError( + 'invalid-argument', 'name is required.', + ); + } + + this.nameInternal = name; + this.eventarcInternal = eventarc; + this.allowedEventTypes = allowedEventTypes; + this.client = new EventarcApiClient(eventarc.app, this); + } + + /** + * The {@link firebase-admin.eventarc#Eventarc} service instance associated with the current `Channel`. + * + * @example + * ```javascript + * var app = channel.eventarc; + * ``` + */ + get eventarc(): Eventarc { + return this.eventarcInternal; + } + + /** + * The channel name as provided during channel creation except if it was not specifed then the default + * channel name will be returned ('locations/us-central1/channels/firebase'). + */ + get name(): string { + return this.nameInternal; + } + + /** + * Publishes provided events to this channel. If channel was created with + * `allowedEventTypes` and event type is not on that list, the event will + * be ignored. + * + * The following CloudEvent fields will be auto-populated if not set: + * * specversion - `1.0` + * * id - uuidv4() + * * source - will be populated with `process.env.EVENTARC_CLOUD_EVENT_SOURCE` and + * if not set an error will be thrown. + * + * @param events - CloudEvent to publish to the channel. + */ + public publish(events: CloudEvent | CloudEvent[]): Promise { + return this.client.publish(events); + } +} diff --git a/src/eventarc/index.ts b/src/eventarc/index.ts new file mode 100644 index 0000000000..d1e6fc79bd --- /dev/null +++ b/src/eventarc/index.ts @@ -0,0 +1,65 @@ +/*! + * @license + * 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. + */ + +/** + * Firebase Eventarc. + * + * @packageDocumentation + */ + +import { App, getApp } from '../app'; +import { FirebaseApp } from '../app/firebase-app'; + +import { Eventarc } from './eventarc'; + +export { CloudEvent, CloudEventVersion } from './cloudevent'; +export { Eventarc, Channel, ChannelOptions } from './eventarc'; + +/** + * Gets the {@link Eventarc} service for the default app or a given app. + * + * `getEventarc()` can be called with no arguments to access the default + * app's `Eventarc` service or as `getEventarc(app)` to access the + * `Eventarc` service associated with specific app. + * + * @example + * ```javascript + * // Get the Eventarc service for the default app + * const defaultEventarc = getEventarc(); + * ``` + * + * @example + * ```javascript + * // Get the Eventarc service for a given app + * const otherEventarc = getEventarc(otherApp); + * ``` + * + * @param app - Optional app whose `Eventarc` service will be returned. + * If not provided, the default `Eventarc` service will be returned. + * + * @returns The default `Eventarc` service if no + * app is provided or the `Eventarc` service associated with the provided + * app. + */ +export function getEventarc(app?: App): Eventarc { + if (typeof app === 'undefined') { + app = getApp(); + } + + const firebaseApp: FirebaseApp = app as FirebaseApp; + return firebaseApp.getOrInitService('eventarc', (app) => new Eventarc(app)); +} diff --git a/test/unit/eventarc/eventarc-utils.spec.ts b/test/unit/eventarc/eventarc-utils.spec.ts new file mode 100644 index 0000000000..b39127caf9 --- /dev/null +++ b/test/unit/eventarc/eventarc-utils.spec.ts @@ -0,0 +1,189 @@ +/*! + * @license + * 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. + */ + +'use strict'; + +import * as sinon from 'sinon'; +import * as utils from '../../../src/eventarc/eventarc-utils'; +import * as chai from 'chai'; +import chaiExclude from 'chai-exclude'; + +const expect = chai.expect; +chai.use(chaiExclude); + +describe('eventarc-utils', () => { + before(() => { + sinon + .stub(Date.prototype, 'toISOString') + .returns('2022-03-16T20:20:42.212Z'); + }); + + after(() => { + sinon.restore(); + }); + + afterEach(() => { + delete process.env.EVENTARC_CLOUD_EVENT_SOURCE; + }); + + describe('toCloudEventProtoFormat', () => { + it('converts cloud event to proto format', () => { + expect(utils.toCloudEventProtoFormat({ + type: 'some.custom.event', + specversion: '1.0', + datacontenttype: 'application/json', + id: 'user-provided-id', + data: { + hello: 'world' + }, + source: '/my/functions', + time: new Date().toISOString(), + customattr: 'custom value', + })).to.deep.eq({ + '@type': 'type.googleapis.com/io.cloudevents.v1.CloudEvent', + 'attributes': { + 'customattr': { + 'ceString': 'custom value' + }, + 'datacontenttype': { + 'ceString': 'application/json' + }, + 'time': { + 'ceTimestamp': '2022-03-16T20:20:42.212Z' + } + }, + 'id': 'user-provided-id', + 'source': '/my/functions', + 'specVersion': '1.0', + 'textData': '{"hello":"world"}', + 'type': 'some.custom.event', + }); + }); + + it('populates specversion if not provided', () => { + const got = utils.toCloudEventProtoFormat({ + type: 'some.custom.event', + datacontenttype: 'application/json', + data: { + hello: 'world' + }, + source: '/my/functions', + time: new Date().toISOString(), + }); + expect(got['specVersion']).to.eq('1.0'); + }); + + it('populates time if not provided', () => { + const got = utils.toCloudEventProtoFormat({ + specversion: '1.0', + type: 'some.custom.event', + datacontenttype: 'application/json', + data: { + hello: 'world' + }, + source: '/my/functions', + }); + expect(got['attributes']['time']).to.deep.eq({ + 'ceTimestamp': '2022-03-16T20:20:42.212Z' + }); + }); + + it('populates id if not provided', () => { + const got = utils.toCloudEventProtoFormat({ + type: 'some.custom.event', + id: 'user-provided-id', + datacontenttype: 'application/json', + data: { + hello: 'world' + }, + source: '/my/functions', + time: new Date().toISOString(), + }); + // Couldn't figure out how to stub uuid, so just checking for presense. + expect(got).to.haveOwnProperty('id'); + }); + + it('populates source from EVENTARC_CLOUD_EVENT_SOURCE env var if not set', () => { + process.env.EVENTARC_CLOUD_EVENT_SOURCE = '//source/from/env/var'; + const got = utils.toCloudEventProtoFormat({ + specversion: '1.0', + type: 'some.custom.event', + datacontenttype: 'application/json', + data: { + hello: 'world' + }, + }); + expect(got['source']).to.eq('//source/from/env/var'); + }); + + it('throws invalid argument when source not set', () => { + expect(() => utils.toCloudEventProtoFormat({ + type: 'some.custom.event', + datacontenttype: 'application/json', + data: { + hello: 'world' + }, + time: new Date().toISOString(), + })).throws("CloudEvent 'source' is required."); + }); + + it('throws invalid argument when custom attr not string', () => { + expect(() => utils.toCloudEventProtoFormat({ + type: 'some.custom.event', + datacontenttype: 'application/json', + data: { + hello: 'world' + }, + source: '/my/functions', + time: new Date().toISOString(), + customattr: 123, + })).throws("CloudEvent extension attributes ('customattr') must be string"); + }); + + it('populates converts object data to JSON and sets datacontenttype', () => { + const got = utils.toCloudEventProtoFormat({ + type: 'some.custom.event', + id: 'user-provided-id', + data: { + hello: 'world' + }, + source: '/my/functions', + time: new Date().toISOString(), + }); + // Couldn't figure out how to stub uuid, so just checking for presense. + expect(got['textData']).to.eq('{"hello":"world"}'); + expect(got['attributes']['datacontenttype']).to.deep.eq({ + 'ceString': 'application/json' + }); + }); + + it('populates string data and sets datacontenttype', () => { + const got = utils.toCloudEventProtoFormat({ + type: 'some.custom.event', + id: 'user-provided-id', + data: 'hello world', + source: '/my/functions', + time: new Date().toISOString(), + }); + // Couldn't figure out how to stub uuid, so just checking for presense. + expect(got['textData']).to.eq('hello world'); + expect(got['attributes']['datacontenttype']).to.deep.eq({ + 'ceString': 'text/plain' + }); + }); + }); +}); diff --git a/test/unit/eventarc/eventarc.spec.ts b/test/unit/eventarc/eventarc.spec.ts new file mode 100644 index 0000000000..1bbddd5a4d --- /dev/null +++ b/test/unit/eventarc/eventarc.spec.ts @@ -0,0 +1,572 @@ +/*! + * @license + * 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. + */ + +'use strict'; + +import * as sinon from 'sinon'; +import { Channel, Eventarc } from '../../../src/eventarc'; +import { toCloudEventProtoFormat } from '../../../src/eventarc/eventarc-utils'; +import { CloudEvent } from '../../../src/eventarc/cloudevent'; +import { HttpClient } from '../../../src/utils/api-request'; +import { FirebaseApp } from '../../../src/app/firebase-app'; +import * as mocks from '../../resources/mocks'; +import * as utils from '../utils'; +import * as chai from 'chai'; +import chaiExclude from 'chai-exclude'; +import { getSdkVersion } from '../../../src/utils/index'; + +const expect = chai.expect; +chai.use(chaiExclude); + +const TEST_EVENT1 : CloudEvent = { + type: 'some.custom.event1', + specversion: '1.0', + id: 'user-provided-id-1', + data: 'hello world', + source: '/my/functions', + time: '2011-11-11T11:11:11.111Z', +}; +const TEST_EVENT1_SERIALIZED = JSON.stringify(toCloudEventProtoFormat(TEST_EVENT1)); + +const TEST_EVENT2 : CloudEvent = { + type: 'some.custom.event2', + specversion: '1.0', + id: 'user-provided-id-2', + data: 'hello world', + source: '/my/functions', + time: '2011-11-11T11:11:11.111Z', +}; +const TEST_EVENT2_SERIALIZED = JSON.stringify(toCloudEventProtoFormat(TEST_EVENT2)); + +describe('eventarc', () => { + let mockApp: FirebaseApp; + let eventarc: Eventarc; + + before(() => { + mockApp = mocks.app(); + eventarc = new Eventarc(mockApp); + }); + + after(() => { + sinon.restore(); + }); + + afterEach(() => { + delete process.env.EVENTARC_CLOUD_EVENT_SOURCE; + }); + + describe('Eventarc', () => { + it('inintializes Eventarc object', () => { + expect(eventarc.app).eq(mockApp); + }); + }); + + it('throws invalid argument with creating channel with invalid name', () => { + expect(() => eventarc.channel('foo/bar')) + .throws('Invalid channel name format.'); + expect(() => eventarc.channel('foo/bar/baz')) + .throws('Invalid channel name format.'); + expect(() => eventarc.channel('channels/foo')) + .throws('Invalid channel name format.'); + expect(() => eventarc.channel('us-central1/channels/foo')) + .throws('Invalid channel name format.'); + expect(() => eventarc.channel('projectid/locations/us-central1/channels/foo')) + .throws('Invalid channel name format.'); + expect(() => eventarc.channel('v1/projects/projectid/locations/us-central1/channels/foo')) + .throws('Invalid channel name format.'); + expect(() => eventarc.channel('projects/projectid/channels/foo')) + .throws('Invalid channel name format.'); + expect(() => eventarc.channel('projects/projectid/locations/us-central1')) + .throws('Invalid channel name format.'); + expect(() => eventarc.channel('projects/projectid/locations_us-central1/channels/foo')) + .throws('Invalid channel name format.'); + }); + + describe('default Channel', () => { + let channel : Channel; + let mockAccessToken: string; + let httpStub: sinon.SinonStub; + let accessTokenStub: sinon.SinonStub; + + before(() => { + channel = eventarc.channel(); + mockAccessToken = utils.generateRandomAccessToken(); + accessTokenStub = utils.stubGetAccessToken(mockAccessToken); + }); + + after(() => { + accessTokenStub?.restore(); + }); + + afterEach(() => { + httpStub?.restore(); + }); + + it('inintializes Channel object', () => { + expect(channel.eventarc).eq(eventarc); + expect(channel.name).eq('locations/us-central1/channels/firebase'); + expect(channel.allowedEventTypes).is.undefined; + }); + + it('publishes single event to the API', async () => { + httpStub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({})); + + await channel.publish(TEST_EVENT1); + + expect(httpStub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-central1/channels/firebase:publishEvents', + data: `{"events":[${TEST_EVENT1_SERIALIZED}]}`, + headers: { + 'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(), + Authorization: 'Bearer ' + mockAccessToken + } + }); + }); + + it('publishes multiple events to the API', async () => { + httpStub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({})); + + await channel.publish([TEST_EVENT1, TEST_EVENT2]); + + expect(httpStub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-central1/channels/firebase:publishEvents', + data: `{"events":[${TEST_EVENT1_SERIALIZED},${TEST_EVENT2_SERIALIZED}]}`, + headers: { + 'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(), + Authorization: 'Bearer ' + mockAccessToken + } + }); + }); + }); + + describe('full resource name Channel', () => { + let channel : Channel; + let mockAccessToken: string; + let httpStub: sinon.SinonStub; + let accessTokenStub: sinon.SinonStub; + + before(() => { + channel = eventarc.channel('projects/other-project-id/locations/us-west1/channels/my-channel2'); + mockAccessToken = utils.generateRandomAccessToken(); + accessTokenStub = utils.stubGetAccessToken(mockAccessToken); + }); + + after(() => { + accessTokenStub?.restore(); + }); + + afterEach(() => { + httpStub?.restore(); + }); + + it('inintializes Channel object', () => { + expect(channel.eventarc).eq(eventarc); + expect(channel.name).eq('projects/other-project-id/locations/us-west1/channels/my-channel2'); + expect(channel.allowedEventTypes).is.undefined; + }); + + it('publishes single event to the API', async () => { + httpStub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({})); + + await channel.publish(TEST_EVENT1); + + expect(httpStub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: 'https://eventarcpublishing.googleapis.com/v1/projects/other-project-id/locations/us-west1/channels/my-channel2:publishEvents', + data: `{"events":[${TEST_EVENT1_SERIALIZED}]}`, + headers: { + 'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(), + Authorization: 'Bearer ' + mockAccessToken + } + }); + }); + + it('publishes multiple events to the API', async () => { + httpStub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({})); + + await channel.publish([TEST_EVENT1, TEST_EVENT2]); + + expect(httpStub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: 'https://eventarcpublishing.googleapis.com/v1/projects/other-project-id/locations/us-west1/channels/my-channel2:publishEvents', + data: `{"events":[${TEST_EVENT1_SERIALIZED},${TEST_EVENT2_SERIALIZED}]}`, + headers: { + 'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(), + Authorization: 'Bearer ' + mockAccessToken + } + }); + }); + }); + + describe('partial (no project) Channel', () => { + let channel : Channel; + let mockAccessToken: string; + let httpStub: sinon.SinonStub; + let accessTokenStub: sinon.SinonStub; + + before(() => { + channel = eventarc.channel('locations/us-west1/channels/my-channel'); + mockAccessToken = utils.generateRandomAccessToken(); + accessTokenStub = utils.stubGetAccessToken(mockAccessToken); + }); + + after(() => { + accessTokenStub?.restore(); + }); + + afterEach(() => { + httpStub?.restore(); + }); + + it('inintializes Channel object', () => { + expect(channel.eventarc).eq(eventarc); + expect(channel.name).eq('locations/us-west1/channels/my-channel'); + expect(channel.allowedEventTypes).is.undefined; + }); + + it('publishes single event to the API', async () => { + httpStub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({})); + + await channel.publish(TEST_EVENT1); + + expect(httpStub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-west1/channels/my-channel:publishEvents', + data: `{"events":[${TEST_EVENT1_SERIALIZED}]}`, + headers: { + 'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(), + Authorization: 'Bearer ' + mockAccessToken + } + }); + }); + + it('publishes multiple events to the API', async () => { + httpStub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({})); + + await channel.publish([TEST_EVENT1, TEST_EVENT2]); + + expect(httpStub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-west1/channels/my-channel:publishEvents', + data: `{"events":[${TEST_EVENT1_SERIALIZED},${TEST_EVENT2_SERIALIZED}]}`, + headers: { + 'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(), + Authorization: 'Bearer ' + mockAccessToken + } + }); + }); + }); + + describe('partial (channel id only) Channel', () => { + let channel : Channel; + let mockAccessToken: string; + let httpStub: sinon.SinonStub; + let accessTokenStub: sinon.SinonStub; + + before(() => { + channel = eventarc.channel('my-channel'); + mockAccessToken = utils.generateRandomAccessToken(); + accessTokenStub = utils.stubGetAccessToken(mockAccessToken); + }); + + after(() => { + accessTokenStub?.restore(); + }); + + afterEach(() => { + httpStub?.restore(); + }); + + it('inintializes Channel object', () => { + expect(channel.eventarc).eq(eventarc); + expect(channel.name).eq('my-channel'); + expect(channel.allowedEventTypes).is.undefined; + }); + + it('publishes single event to the API', async () => { + httpStub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({})); + + await channel.publish(TEST_EVENT1); + + expect(httpStub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-central1/channels/my-channel:publishEvents', + data: `{"events":[${TEST_EVENT1_SERIALIZED}]}`, + headers: { + 'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(), + Authorization: 'Bearer ' + mockAccessToken + } + }); + }); + + it('publishes multiple events to the API', async () => { + httpStub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({})); + + await channel.publish([TEST_EVENT1, TEST_EVENT2]); + + expect(httpStub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-central1/channels/my-channel:publishEvents', + data: `{"events":[${TEST_EVENT1_SERIALIZED},${TEST_EVENT2_SERIALIZED}]}`, + headers: { + 'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(), + Authorization: 'Bearer ' + mockAccessToken + } + }); + }); + }); + + describe('Channel with empty allowed events', () => { + let channel : Channel; + let mockAccessToken: string; + let httpStub: sinon.SinonStub; + let accessTokenStub: sinon.SinonStub; + + before(() => { + channel = eventarc.channel({ allowedEventTypes: [] }); + mockAccessToken = utils.generateRandomAccessToken(); + accessTokenStub = utils.stubGetAccessToken(mockAccessToken); + }); + + after(() => { + accessTokenStub?.restore(); + }); + + afterEach(() => { + httpStub?.restore(); + }); + + it('inintializes Channel object', () => { + expect(channel.eventarc).eq(eventarc); + expect(channel.allowedEventTypes).is.empty; + }); + + it('filters out event and publishes none', async () => { + httpStub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({})); + + await channel.publish(TEST_EVENT1); + + expect(httpStub).to.not.have.been.called; + }); + + it('filters out all event and publishes none', async () => { + httpStub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({})); + + await channel.publish([TEST_EVENT1, { type: 'foo' }]); + + expect(httpStub).to.not.have.been.called; + }); + }); + + describe('Channel with channel and empty allowed events', () => { + let channel : Channel; + let mockAccessToken: string; + let httpStub: sinon.SinonStub; + let accessTokenStub: sinon.SinonStub; + + before(() => { + channel = eventarc.channel( + 'adasdas', + { allowedEventTypes: [] }); + mockAccessToken = utils.generateRandomAccessToken(); + accessTokenStub = utils.stubGetAccessToken(mockAccessToken); + }); + + after(() => { + accessTokenStub?.restore(); + }); + + afterEach(() => { + httpStub?.restore(); + }); + + it('inintializes Channel object', () => { + expect(channel.eventarc).eq(eventarc); + expect(channel.allowedEventTypes).is.empty; + }); + + it('filters out event and publishes none', async () => { + httpStub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({})); + + await channel.publish(TEST_EVENT1); + + expect(httpStub).to.not.have.been.called; + }); + + it('filters out all event and publishes none', async () => { + httpStub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({})); + + await channel.publish([TEST_EVENT1, { type: 'foo' }]); + + expect(httpStub).to.not.have.been.called; + }); + }); + + describe('Channel with allowed events', () => { + let channel : Channel; + let mockAccessToken: string; + let httpStub: sinon.SinonStub; + let accessTokenStub: sinon.SinonStub; + + before(() => { + channel = eventarc.channel({ allowedEventTypes: ['some.custom.event1'] }); + mockAccessToken = utils.generateRandomAccessToken(); + accessTokenStub = utils.stubGetAccessToken(mockAccessToken); + }); + + after(() => { + accessTokenStub?.restore(); + }); + + afterEach(() => { + httpStub?.restore(); + }); + + it('inintializes Channel object', () => { + expect(channel.eventarc).eq(eventarc); + expect(channel.allowedEventTypes).deep.eq(['some.custom.event1']); + }); + + it('publishes events with allowed type', async () => { + httpStub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({})); + + await channel.publish(TEST_EVENT1); + + expect(httpStub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-central1/channels/firebase:publishEvents', + data: `{"events":[${TEST_EVENT1_SERIALIZED}]}`, + headers: { + 'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(), + Authorization: 'Bearer ' + mockAccessToken + } + }); + }); + + it('publishes events with allowed type and filters out others', async () => { + httpStub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({})); + + await channel.publish([TEST_EVENT1, { + type: 'some.custom.event2' + }]); + + expect(httpStub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-central1/channels/firebase:publishEvents', + data: `{"events":[${TEST_EVENT1_SERIALIZED}]}`, + headers: { + 'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(), + Authorization: 'Bearer ' + mockAccessToken + } + }); + }); + }); + + describe('Channel with allowed events as string', () => { + let channel : Channel; + let mockAccessToken: string; + let httpStub: sinon.SinonStub; + let accessTokenStub: sinon.SinonStub; + + before(() => { + channel = eventarc.channel({ allowedEventTypes: 'some.custom.event1,some.other.event.type' }); + mockAccessToken = utils.generateRandomAccessToken(); + accessTokenStub = utils.stubGetAccessToken(mockAccessToken); + }); + + after(() => { + accessTokenStub?.restore(); + }); + + afterEach(() => { + httpStub?.restore(); + }); + + it('inintializes Channel object', () => { + expect(channel.eventarc).eq(eventarc); + expect(channel.allowedEventTypes).deep.eq(['some.custom.event1', 'some.other.event.type']); + }); + + it('publishes events with allowed type', async () => { + httpStub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({})); + + await channel.publish(TEST_EVENT1); + + expect(httpStub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-central1/channels/firebase:publishEvents', + data: `{"events":[${TEST_EVENT1_SERIALIZED}]}`, + headers: { + 'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(), + Authorization: 'Bearer ' + mockAccessToken + } + }); + }); + + it('publishes events with allowed type and filters out others', async () => { + httpStub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom({})); + + await channel.publish([TEST_EVENT1, { + type: 'some.custom.event2' + }]); + + expect(httpStub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: 'https://eventarcpublishing.googleapis.com/v1/projects/project_id/locations/us-central1/channels/firebase:publishEvents', + data: `{"events":[${TEST_EVENT1_SERIALIZED}]}`, + headers: { + 'X-Firebase-Client': 'fire-admin-node/' + getSdkVersion(), + Authorization: 'Bearer ' + mockAccessToken + } + }); + }); + }); +}); diff --git a/test/unit/index.spec.ts b/test/unit/index.spec.ts index ade3503069..ca1f63adee 100644 --- a/test/unit/index.spec.ts +++ b/test/unit/index.spec.ts @@ -103,3 +103,7 @@ import './app-check/app-check.spec'; import './app-check/app-check-api-client-internal.spec'; import './app-check/token-generator.spec'; import './app-check/token-verifier.spec.ts'; + +// Eventarc +import './eventarc/eventarc.spec'; +import './eventarc/eventarc-utils.spec'; From 217a52e7af91d3e8c300fc47c572bdb96f51eb00 Mon Sep 17 00:00:00 2001 From: PavelJ Date: Thu, 14 Apr 2022 17:23:51 -0400 Subject: [PATCH 2/8] Doc fixes. --- src/eventarc/cloudevent.ts | 10 +++++----- src/eventarc/eventarc.ts | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/eventarc/cloudevent.ts b/src/eventarc/cloudevent.ts index 1c2fbdf66f..639996ff98 100644 --- a/src/eventarc/cloudevent.ts +++ b/src/eventarc/cloudevent.ts @@ -29,15 +29,15 @@ export type CloudEventVersion = '1.0'; export interface CloudEvent { /** - * Event identified. If not provided will be auto-generated with a UUID. + * Identifier for the event. If not provided will be auto-generated with a UUID. * * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#id */ id?: string; /** - * Identifies the context in which an event happened. If not provided the value of `EVENTARC_CLOUD_EVENT_SOURCE` - * environment variable will be used and if that is not set a validation error will be thrown. + * Identifies the context in which an event happened. If not provided, the value of `EVENTARC_CLOUD_EVENT_SOURCE` + * environment variable is used and if that is not set, a validation error is thrown. * * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#source-1 */ @@ -67,14 +67,14 @@ export interface CloudEvent { /** * MIME type of the data being sent with the event in the `data` field. Only `application/json` and `text/plain` - * are currently supported. If not specified wil be automatically inferred from the type of provided data. + * are currently supported. If not specified will be automatically inferred from the type of provided data. * * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#datacontenttype */ datacontenttype?: string; /** - * Timestamp of when the occurrence happened. Must in ISO time format. If not specified current time (at the + * Timestamp of the event. Must be in ISO time format. If not specified current time (at the * moment of publishing) will be used. * * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#time diff --git a/src/eventarc/eventarc.ts b/src/eventarc/eventarc.ts index 214d69d1d7..bebe1d6911 100644 --- a/src/eventarc/eventarc.ts +++ b/src/eventarc/eventarc.ts @@ -55,7 +55,7 @@ export class Eventarc { } /** - * The {@link firebase-admin.app#App} associated with the current `Eventarc` service + * The {@link firebase-admin.app#App} associated with the current Eventarc service * instance. * * @example @@ -69,13 +69,13 @@ export class Eventarc { /** * Creates a reference to the Eventarc channel using the provided channel resource name. - * The channel resource name Can be either: + * The channel resource name. 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 + * * partial channel ID, in which case the runtime project ID of the * function and `us-central1` as location will be used: * `{channel-id}` * @@ -172,8 +172,8 @@ export class Channel { } /** - * The channel name as provided during channel creation except if it was not specifed then the default - * channel name will be returned ('locations/us-central1/channels/firebase'). + * The channel name as provided during channel creation. If it was not specifed then the default + * channel name is returned ('locations/us-central1/channels/firebase'). */ get name(): string { return this.nameInternal; From 4e09c4406163ee0e20f0108261aaa02cf69aed97 Mon Sep 17 00:00:00 2001 From: PavelJ Date: Thu, 14 Apr 2022 18:23:46 -0400 Subject: [PATCH 3/8] another global docs pass --- src/eventarc/cloudevent.ts | 14 +++++++------- src/eventarc/eventarc.ts | 23 ++++++++--------------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/eventarc/cloudevent.ts b/src/eventarc/cloudevent.ts index 639996ff98..bf8c9c9aff 100644 --- a/src/eventarc/cloudevent.ts +++ b/src/eventarc/cloudevent.ts @@ -29,7 +29,7 @@ export type CloudEventVersion = '1.0'; export interface CloudEvent { /** - * Identifier for the event. If not provided will be auto-generated with a UUID. + * Identifier for the event. If not provided, it is auto-populated with a UUID. * * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#id */ @@ -44,7 +44,7 @@ export interface CloudEvent { source?: string; /** - * The version of the CloudEvents specification which the event uses. If not provided will be set to `1.0` -- + * The version of the CloudEvents specification which the event uses. If not provided, is set to `1.0` -- * the only supported value. * * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#specversion @@ -67,27 +67,27 @@ export interface CloudEvent { /** * MIME type of the data being sent with the event in the `data` field. Only `application/json` and `text/plain` - * are currently supported. If not specified will be automatically inferred from the type of provided data. + * are currently supported. If not specified, it is automatically inferred from the type of provided data. * * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#datacontenttype */ datacontenttype?: string; /** - * Timestamp of the event. Must be in ISO time format. If not specified current time (at the - * moment of publishing) will be used. + * Timestamp of the event. Must be in ISO time format. If not specified, current time (at the moment of publishing) + * is used. * * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#time */ time?: string; /** - * Data payload of the event. Objects will be strigified with JSON and strings will be passed along as-is. + * Data payload of the event. Objects are strigified with JSON and strings are be passed along as-is. */ data?: object | string; /** - * Custom attributes/extensions. Must be strings. Will be added to the event as is. + * Custom attributes/extensions. Must be strings. Added to the event as is. * * @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#extension-context-attributes */ diff --git a/src/eventarc/eventarc.ts b/src/eventarc/eventarc.ts index bebe1d6911..08d7ed7ace 100644 --- a/src/eventarc/eventarc.ts +++ b/src/eventarc/eventarc.ts @@ -27,7 +27,7 @@ import { EventarcApiClient } from './eventarc-client-internal'; export interface ChannelOptions { /** * An array of allowed event types. If specified, publishing events of - * unknown types will be a no op. When not provided, no event filtering is + * unknown types is a no op. When not provided, no event filtering is * performed. */ allowedEventTypes?: string[] | string | undefined @@ -73,10 +73,10 @@ export class Eventarc { * * 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: + * the runtime project ID of the function is 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: + * function and `us-central1` as location is used: * `{channel-id}` * * @param name - Channel resource name. @@ -131,7 +131,7 @@ export class Channel { private nameInternal: string; /** - * List if event types allowed by this channel for publishing. Other event types will be ignored. + * List if event types allowed by this channel for publishing. Other event types are ignored. */ public readonly allowedEventTypes?: string[] @@ -172,23 +172,16 @@ export class Channel { } /** - * The channel name as provided during channel creation. If it was not specifed then the default - * channel name is returned ('locations/us-central1/channels/firebase'). + * The channel name as provided during channel creation. If it was not specifed, the default channel name is returned + * ('locations/us-central1/channels/firebase'). */ get name(): string { return this.nameInternal; } /** - * Publishes provided events to this channel. If channel was created with - * `allowedEventTypes` and event type is not on that list, the event will - * be ignored. - * - * The following CloudEvent fields will be auto-populated if not set: - * * specversion - `1.0` - * * id - uuidv4() - * * source - will be populated with `process.env.EVENTARC_CLOUD_EVENT_SOURCE` and - * if not set an error will be thrown. + * Publishes provided events to this channel. If channel was created with `allowedEventTypes` and event type is not + * on that list, the event is ignored. * * @param events - CloudEvent to publish to the channel. */ From 3af7efa92dfea53d462fb6ab791fd38dc6bbc0d3 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 14 Apr 2022 19:40:10 -0400 Subject: [PATCH 4/8] Update src/eventarc/eventarc.ts Co-authored-by: Kevin Cheung --- src/eventarc/eventarc.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/eventarc/eventarc.ts b/src/eventarc/eventarc.ts index 08d7ed7ace..361d2b9973 100644 --- a/src/eventarc/eventarc.ts +++ b/src/eventarc/eventarc.ts @@ -69,19 +69,19 @@ export class Eventarc { /** * Creates a reference to the Eventarc channel using the provided channel resource name. - * The channel resource name. Can be either: - * * fully qualified channel resource name: + * The channel resource name can be either: + * * A fully qualified channel resource name: * `projects/{project}/locations/{location}/channels/{channel-id}` - * * partial resource name with location and channel ID, in which case + * * A partial resource name with location and channel ID, in which case * the runtime project ID of the function is used: * `locations/{location}/channels/{channel-id}` - * * partial channel ID, in which case the runtime project ID of the + * * A partial channel ID, in which case the runtime project ID of the * function and `us-central1` as location is used: * `{channel-id}` * * @param name - Channel resource name. * @param options - (optional) additional channel options - * @returns Eventarc channel reference for publishing events. + * @returns An Eventarc channel reference for publishing events. */ public channel(name: string, options?: ChannelOptions): Channel; From c3a9a7ebb977f4bbb7b2918ceecafb79fe8dfea3 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 14 Apr 2022 19:40:20 -0400 Subject: [PATCH 5/8] Update src/eventarc/eventarc.ts Co-authored-by: Kevin Cheung --- src/eventarc/eventarc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eventarc/eventarc.ts b/src/eventarc/eventarc.ts index 361d2b9973..363679a375 100644 --- a/src/eventarc/eventarc.ts +++ b/src/eventarc/eventarc.ts @@ -131,7 +131,7 @@ export class Channel { private nameInternal: string; /** - * List if event types allowed by this channel for publishing. Other event types are ignored. + * List of event types allowed by this channel for publishing. Other event types are ignored. */ public readonly allowedEventTypes?: string[] From 249707a49d461757248f345575b0ac8fee697ddb Mon Sep 17 00:00:00 2001 From: PavelJ Date: Fri, 22 Apr 2022 09:00:48 -0400 Subject: [PATCH 6/8] Fixed a bug/typo in the subject field validation --- src/eventarc/eventarc-utils.ts | 2 +- test/unit/eventarc/eventarc-utils.spec.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/eventarc/eventarc-utils.ts b/src/eventarc/eventarc-utils.ts index 8a5a001753..e4f3308c02 100644 --- a/src/eventarc/eventarc-utils.ts +++ b/src/eventarc/eventarc-utils.ts @@ -80,7 +80,7 @@ export function toCloudEventProtoFormat(ce: CloudEvent): any { }); } if (ce.subject) { - if (!validator.isNonEmptyString(ce.datacontenttype)) { + if (!validator.isNonEmptyString(ce.subject)) { throw new FirebaseEventarcError( 'invalid-argument', "CloudEvent 'subject' if specified must be non-empty string."); diff --git a/test/unit/eventarc/eventarc-utils.spec.ts b/test/unit/eventarc/eventarc-utils.spec.ts index b39127caf9..d2f18b7d96 100644 --- a/test/unit/eventarc/eventarc-utils.spec.ts +++ b/test/unit/eventarc/eventarc-utils.spec.ts @@ -45,6 +45,7 @@ describe('eventarc-utils', () => { expect(utils.toCloudEventProtoFormat({ type: 'some.custom.event', specversion: '1.0', + subject: 'context', datacontenttype: 'application/json', id: 'user-provided-id', data: { @@ -64,6 +65,9 @@ describe('eventarc-utils', () => { }, 'time': { 'ceTimestamp': '2022-03-16T20:20:42.212Z' + }, + 'subject': { + 'ceString': 'context' } }, 'id': 'user-provided-id', From 9beb6feb705d69f38cbd82bd1bdf41f44f1e1dd6 Mon Sep 17 00:00:00 2001 From: PavelJ Date: Mon, 25 Apr 2022 15:14:32 -0400 Subject: [PATCH 7/8] Another docs pass (typos and hunting down remaining wills) --- src/eventarc/cloudevent.ts | 2 +- src/eventarc/eventarc-client-internal.ts | 11 +++++------ src/eventarc/eventarc-utils.ts | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/eventarc/cloudevent.ts b/src/eventarc/cloudevent.ts index bf8c9c9aff..9fa0749f2e 100644 --- a/src/eventarc/cloudevent.ts +++ b/src/eventarc/cloudevent.ts @@ -82,7 +82,7 @@ export interface CloudEvent { time?: string; /** - * Data payload of the event. Objects are strigified with JSON and strings are be passed along as-is. + * Data payload of the event. Objects are stringified with JSON and strings are be passed along as-is. */ data?: object | string; diff --git a/src/eventarc/eventarc-client-internal.ts b/src/eventarc/eventarc-client-internal.ts index 7277a7f5af..e265480024 100644 --- a/src/eventarc/eventarc-client-internal.ts +++ b/src/eventarc/eventarc-client-internal.ts @@ -73,15 +73,14 @@ export class EventarcApiClient { } /** - * Publishes provided events to this channel. If channel was created with - * `allowedEventsTypes` and event type is not on that list, the event will - * be ignored. + * Publishes provided events to this channel. If channel was created with `allowedEventsTypes` and event type + * is not on that list, the event is ignored. * - * The following CloudEvent fields will be auto-populated if not set: + * The following CloudEvent fields are auto-populated if not set: * * specversion - `1.0` * * id - uuidv4() - * * source - will be populated with `process.env.EVENTARC_CLOUD_EVENT_SOURCE` and - * if not set an error will be thrown. + * * source - populated with `process.env.EVENTARC_CLOUD_EVENT_SOURCE` and + * if not set an error is thrown. * * @param events - CloudEvent to publish to the channel. */ diff --git a/src/eventarc/eventarc-utils.ts b/src/eventarc/eventarc-utils.ts index e4f3308c02..6bf6531285 100644 --- a/src/eventarc/eventarc-utils.ts +++ b/src/eventarc/eventarc-utils.ts @@ -110,7 +110,7 @@ export function toCloudEventProtoFormat(ce: CloudEvent): any { } else { throw new FirebaseEventarcError( 'invalid-argument', - `CloudEvent 'data' must be string or an object (which will be converted to JSON), got '${typeof ce.data}'.`); + `CloudEvent 'data' must be string or an object (which are converted to JSON), got '${typeof ce.data}'.`); } for (const attr in ce) { From 7ae4c339da15f8090bf525eb25f4960ab940dd3c Mon Sep 17 00:00:00 2001 From: PavelJ Date: Mon, 25 Apr 2022 15:17:22 -0400 Subject: [PATCH 8/8] Update package-lock.json --- package-lock.json | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/package-lock.json b/package-lock.json index 4f4bb4adb7..bb4447ca91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -403,6 +403,27 @@ "@firebase/util": "1.4.3", "idb": "3.0.2", "tslib": "^2.1.0" + }, + "dependencies": { + "@firebase/component": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.10.tgz", + "integrity": "sha512-mzUpg6rsBbdQJvAdu1rNWabU3O7qdd+B+/ubE1b+pTbBKfw5ySRpRRE6sKcZ/oQuwLh0HHB6FRJHcylmI7jDzA==", + "dev": true, + "requires": { + "@firebase/util": "1.4.3", + "tslib": "^2.1.0" + } + }, + "@firebase/util": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.4.3.tgz", + "integrity": "sha512-gQJl6r0a+MElLQEyU8Dx0kkC2coPj67f/zKZrGR7z7WpLgVanhaCUqEsptwpwoxi9RMFIaebleG+C9xxoARq+Q==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + } } }, "@firebase/app-compat": { @@ -416,6 +437,27 @@ "@firebase/logger": "0.3.2", "@firebase/util": "1.4.3", "tslib": "^2.1.0" + }, + "dependencies": { + "@firebase/component": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.10.tgz", + "integrity": "sha512-mzUpg6rsBbdQJvAdu1rNWabU3O7qdd+B+/ubE1b+pTbBKfw5ySRpRRE6sKcZ/oQuwLh0HHB6FRJHcylmI7jDzA==", + "dev": true, + "requires": { + "@firebase/util": "1.4.3", + "tslib": "^2.1.0" + } + }, + "@firebase/util": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.4.3.tgz", + "integrity": "sha512-gQJl6r0a+MElLQEyU8Dx0kkC2coPj67f/zKZrGR7z7WpLgVanhaCUqEsptwpwoxi9RMFIaebleG+C9xxoARq+Q==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + } } }, "@firebase/app-types": {