diff --git a/.github/workflows/postmerge.yaml b/.github/workflows/postmerge.yaml index 15835d76a..543f77c2c 100644 --- a/.github/workflows/postmerge.yaml +++ b/.github/workflows/postmerge.yaml @@ -34,7 +34,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 - uses: google-github-actions/auth@v0 with: diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index a02370d1f..686953002 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -30,9 +30,8 @@ jobs: strategy: matrix: node-version: - - 14.x - - 16.x - 18.x + - 20.x steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 @@ -51,9 +50,8 @@ jobs: strategy: matrix: node-version: - - 14.x - - 16.x - 18.x + - 20.x steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 767a26aeb..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +0,0 @@ -- Remove HTTP server shutdown message. (#1457) -- Add features to task queue functions. (#1423) -- Add traces to V2 Firestore trigger logs. (#1440) diff --git a/integration_test/package.json.template b/integration_test/package.json.template index 07585cbe2..42cdf121c 100644 --- a/integration_test/package.json.template +++ b/integration_test/package.json.template @@ -13,7 +13,7 @@ "main": "lib/index.js", "devDependencies": { "@types/node-fetch": "^2.6.1", - "typescript": "~4.2.2" + "typescript": "^4.3.5" }, "engines": { "node": "__NODE_VERSION__" diff --git a/package-lock.json b/package-lock.json index e4c6631e9..d311b8583 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,18 @@ { "name": "firebase-functions", - "version": "4.4.1", + "version": "5.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "firebase-functions", - "version": "4.4.1", + "version": "5.0.1", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", "@types/express": "4.17.3", "cors": "^2.8.5", "express": "^4.17.1", - "node-fetch": "^2.6.7", "protobufjs": "^7.2.2" }, "bin": { @@ -43,7 +42,7 @@ "eslint-config-prettier": "^8.3.0", "eslint-plugin-jsdoc": "^39.2.9", "eslint-plugin-prettier": "^4.0.0", - "firebase-admin": "^11.8.0", + "firebase-admin": "^12.1.0", "js-yaml": "^3.13.1", "jsdom": "^16.2.1", "jsonwebtoken": "^9.0.0", @@ -66,7 +65,7 @@ "node": ">=14.10.0" }, "peerDependencies": { - "firebase-admin": "^10.0.0 || ^11.0.0" + "firebase-admin": "^11.10.0 || ^12.0.0" } }, "node_modules/@babel/parser": { @@ -182,13 +181,10 @@ } }, "node_modules/@fastify/busboy": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-1.2.1.tgz", - "integrity": "sha512-7PQA7EH43S0CxcOa9OeAnaeA0oQ+e/DHNPZwSQM9CQHW76jle5+OvLdibRp/Aafs9KXbLhxyjOTkRjWUbQEd3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", "dev": true, - "dependencies": { - "text-decoding": "^1.0.0" - }, "engines": { "node": ">=14" } @@ -230,6 +226,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.0.tgz", + "integrity": "sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg==", + "dev": true + }, "node_modules/@firebase/app-types": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", @@ -253,11 +255,12 @@ } }, "node_modules/@firebase/database": { - "version": "0.14.4", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.14.4.tgz", - "integrity": "sha512-+Ea/IKGwh42jwdjCyzTmeZeLM3oy1h0mFPsTy6OqCWzcu/KFqRAr5Tt1HRCOBlNOdbh84JPZC47WLU18n2VbxQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.2.tgz", + "integrity": "sha512-8X6NBJgUQzDz0xQVaCISoOLINKat594N2eBbMR3Mu/MH/ei4WM+aAMlsNzngF22eljXu1SILP5G3evkyvsG3Ng==", "dev": true, "dependencies": { + "@firebase/app-check-interop-types": "0.3.0", "@firebase/auth-interop-types": "0.2.1", "@firebase/component": "0.6.4", "@firebase/logger": "0.4.0", @@ -267,23 +270,23 @@ } }, "node_modules/@firebase/database-compat": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.3.4.tgz", - "integrity": "sha512-kuAW+l+sLMUKBThnvxvUZ+Q1ZrF/vFJ58iUY9kAcbX48U03nVzIF6Tmkf0p3WVQwMqiXguSgtOPIB6ZCeF+5Gg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.2.tgz", + "integrity": "sha512-09ryJnXDvuycsxn8aXBzLhBTuCos3HEnCOBWY6hosxfYlNCGnLvG8YMlbSAt5eNhf7/00B095AEfDsdrrLjxqA==", "dev": true, "dependencies": { "@firebase/component": "0.6.4", - "@firebase/database": "0.14.4", - "@firebase/database-types": "0.10.4", + "@firebase/database": "1.0.2", + "@firebase/database-types": "1.0.0", "@firebase/logger": "0.4.0", "@firebase/util": "1.9.3", "tslib": "^2.1.0" } }, "node_modules/@firebase/database-types": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.10.4.tgz", - "integrity": "sha512-dPySn0vJ/89ZeBac70T+2tWWPiJXWbmRygYv0smT5TfE3hDrQ09eKMF3Y+vMlTdrMWq7mUdYW5REWPSGH4kAZQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.0.tgz", + "integrity": "sha512-SjnXStoE0Q56HcFgNQ+9SsmJc0c8TqGARdI/T44KXy+Ets3r6x/ivhQozT66bMnCEjJRywYoxNurRTMlZF8VNg==", "dev": true, "dependencies": { "@firebase/app-types": "0.9.0", @@ -309,25 +312,25 @@ } }, "node_modules/@google-cloud/firestore": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-6.5.0.tgz", - "integrity": "sha512-U0QwG6pEQxO5c0v0eUylswozmuvlvz7iXSW+I18jzqR2hAFrUq2Weu1wm3NaH8wGD4ZL7W9Be4cMHG5CYU8LuQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.1.0.tgz", + "integrity": "sha512-kkTC0Sb9r2lONuFF8Tr2wFfBfk0DT1/EKcTKOhsuoXUVClv3jCqGYVPtHgQsHFjdOsubS+tx9G5D5WG+obB2DA==", "dev": true, "optional": true, "dependencies": { "fast-deep-equal": "^3.1.1", "functional-red-black-tree": "^1.0.1", - "google-gax": "^3.5.7", - "protobufjs": "^7.0.0" + "google-gax": "^4.0.4", + "protobufjs": "^7.2.5" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, "node_modules/@google-cloud/paginator": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.7.tgz", - "integrity": "sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.0.tgz", + "integrity": "sha512-87aeg6QQcEPxGCOthnpUjvw4xAZ57G7pL8FS0C4e/81fr3FjkpUpibf1s2v5XGyGhUVGF4Jfg7yEcxqn2iUw1w==", "dev": true, "optional": true, "dependencies": { @@ -335,56 +338,56 @@ "extend": "^3.0.2" }, "engines": { - "node": ">=10" + "node": ">=14.0.0" } }, "node_modules/@google-cloud/projectify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-3.0.0.tgz", - "integrity": "sha512-HRkZsNmjScY6Li8/kb70wjGlDDyLkVk3KvoEo9uIoxSjYLJasGiCch9+PqRVDOCGUFvEIqyogl+BeqILL4OJHA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", + "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", "dev": true, "optional": true, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, "node_modules/@google-cloud/promisify": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-3.0.1.tgz", - "integrity": "sha512-z1CjRjtQyBOYL+5Qr9DdYIfrdLBe746jRTYfaYU6MeXkqp7UfYs/jX16lFFVzZ7PGEJvqZNqYUEtb1mvDww4pA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", + "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", "dev": true, "optional": true, "engines": { - "node": ">=12" + "node": ">=14" } }, "node_modules/@google-cloud/storage": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-6.10.0.tgz", - "integrity": "sha512-MaFNtMxPpnv6c43HcRsJTUiYhXgcjy+mshLyZpfGKMpE2vJ8C1mBFK/ZrlcPBt47ZK0tz9p/mNTyvi8dRsdKPw==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.7.0.tgz", + "integrity": "sha512-EMCEY+6JiIkx7Dt8NXVGGjy1vRdSGdHkoqZoqjJw7cEBkT7ZkX0c7puedfn1MamnzW5SX4xoa2jVq5u7OWBmkQ==", "dev": true, "optional": true, "dependencies": { - "@google-cloud/paginator": "^3.0.7", - "@google-cloud/projectify": "^3.0.0", - "@google-cloud/promisify": "^3.0.0", + "@google-cloud/paginator": "^5.0.0", + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", "abort-controller": "^3.0.0", "async-retry": "^1.3.3", "compressible": "^2.0.12", "duplexify": "^4.0.0", "ent": "^2.2.0", - "extend": "^3.0.2", - "gaxios": "^5.0.0", - "google-auth-library": "^8.0.1", + "fast-xml-parser": "^4.3.0", + "gaxios": "^6.0.2", + "google-auth-library": "^9.0.0", "mime": "^3.0.0", "mime-types": "^2.0.8", "p-limit": "^3.0.1", - "retry-request": "^5.0.0", - "teeny-request": "^8.0.0", + "retry-request": "^7.0.0", + "teeny-request": "^9.0.0", "uuid": "^8.0.0" }, "engines": { - "node": ">=12" + "node": ">=14" } }, "node_modules/@google-cloud/storage/node_modules/uuid": { @@ -398,13 +401,13 @@ } }, "node_modules/@grpc/grpc-js": { - "version": "1.8.14", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.8.14.tgz", - "integrity": "sha512-w84maJ6CKl5aApCMzFll0hxtFNT6or9WwMslobKaqWUEf1K+zhlL43bSQhFreyYWIWR+Z0xnVFC1KtLm4ZpM/A==", + "version": "1.9.13", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.13.tgz", + "integrity": "sha512-OEZZu9v9AA+7/tghMDE8o5DAMD5THVnwSqDWuh7PPYO5287rTyqy0xEHT6/e4pbqSrhyLPdQFsam4TwFQVVIIw==", "dev": true, "optional": true, "dependencies": { - "@grpc/proto-loader": "^0.7.0", + "@grpc/proto-loader": "^0.7.8", "@types/node": ">=12.12.47" }, "engines": { @@ -412,16 +415,15 @@ } }, "node_modules/@grpc/proto-loader": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.7.tgz", - "integrity": "sha512-1TIeXOi8TuSCQprPItwoMymZXxWT0CPxUhkrkeCUH+D8U7QDwQ6b7SUz2MaLuWM2llT+J/TVFLmQI5KtML3BhQ==", + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", + "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", "dev": true, "optional": true, "dependencies": { - "@types/long": "^4.0.1", "lodash.camelcase": "^4.3.0", - "long": "^4.0.0", - "protobufjs": "^7.0.0", + "long": "^5.0.0", + "protobufjs": "^7.2.4", "yargs": "^17.7.2" }, "bin": { @@ -1084,6 +1086,13 @@ "@types/node": "*" } }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "dev": true, + "optional": true + }, "node_modules/@types/chai": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", @@ -1135,17 +1144,6 @@ "@types/range-parser": "*" } }, - "node_modules/@types/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", - "dev": true, - "optional": true, - "dependencies": { - "@types/minimatch": "^5.1.2", - "@types/node": "*" - } - }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -1195,13 +1193,6 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, - "node_modules/@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true, - "optional": true - }, "node_modules/@types/mocha": { "version": "5.2.7", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", @@ -1251,15 +1242,32 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, - "node_modules/@types/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ==", + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", "dev": true, "optional": true, "dependencies": { - "@types/glob": "*", - "@types/node": "*" + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/request/node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "optional": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" } }, "node_modules/@types/semver": { @@ -1283,6 +1291,13 @@ "integrity": "sha512-T+m89VdXj/eidZyejvmoP9jivXgBDdkOSBVQjU9kF349NEx10QdPNGxHeZUaj1IlJ32/ewdyXJjnJxyxJroYwg==", "dev": true }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "optional": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.55.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz", @@ -1836,13 +1851,12 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "optional": true + ] }, "node_modules/bignumber.js": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", - "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", "dev": true, "optional": true, "engines": { @@ -1858,6 +1872,17 @@ "node": ">=8" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -1946,6 +1971,30 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -2110,6 +2159,12 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, "node_modules/cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -2328,6 +2383,21 @@ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", "dev": true }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/deep-eql": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", @@ -2340,6 +2410,15 @@ "node": ">=6" } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2372,6 +2451,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -2487,7 +2575,6 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, - "optional": true, "dependencies": { "once": "^1.4.0" } @@ -2938,6 +3025,15 @@ "node": ">=6" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -2999,6 +3095,20 @@ "dev": true, "optional": true }, + "node_modules/farmhash": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/farmhash/-/farmhash-3.3.1.tgz", + "integrity": "sha512-XUizHanzlr/v7suBr/o85HSakOoWh6HKXZjFYl5C2+Gj0f0rkw+XTUZzrd9odDsgI9G5tRUcF4wSbKaX04T0DQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^5.1.0", + "prebuild-install": "^7.1.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3051,12 +3161,28 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, - "node_modules/fast-text-encoding": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz", - "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==", + "node_modules/fast-xml-parser": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.2.tgz", + "integrity": "sha512-rmrXUXwbJedoXkStenj1kkljNF7ugn5ZjR9FJcwmCfcCbtOMDghPajbc+Tck6vE6F5XsDmx+Pr2le9fw8+pXBg==", "dev": true, - "optional": true + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "optional": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } }, "node_modules/fastq": { "version": "1.15.0", @@ -3150,17 +3276,19 @@ } }, "node_modules/firebase-admin": { - "version": "11.8.0", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-11.8.0.tgz", - "integrity": "sha512-RxO0wWDnuqVikXExhVjnhVaaXpziKCad4D1rOX5c1WJdk1jAu9hfE4rbrFKZQZgF1okZS04kgCBIFJro7xn8NQ==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-12.1.0.tgz", + "integrity": "sha512-bU7uPKMmIXAihWxntpY/Ma9zucn5y3ec+HQPqFQ/zcEfP9Avk9E/6D8u+yT/VwKHNZyg7yDVWOoJi73TIdR4Ww==", "dev": true, "dependencies": { - "@fastify/busboy": "^1.2.1", - "@firebase/database-compat": "^0.3.4", - "@firebase/database-types": "^0.10.4", - "@types/node": ">=12.12.47", + "@fastify/busboy": "^2.1.0", + "@firebase/database-compat": "^1.0.2", + "@firebase/database-types": "^1.0.0", + "@types/node": "^20.10.3", + "farmhash": "^3.3.0", "jsonwebtoken": "^9.0.0", "jwks-rsa": "^3.0.1", + "long": "^5.2.3", "node-forge": "^1.3.1", "uuid": "^9.0.0" }, @@ -3168,8 +3296,17 @@ "node": ">=14" }, "optionalDependencies": { - "@google-cloud/firestore": "^6.5.0", - "@google-cloud/storage": "^6.9.5" + "@google-cloud/firestore": "^7.1.0", + "@google-cloud/storage": "^7.7.0" + } + }, + "node_modules/firebase-admin/node_modules/@types/node": { + "version": "20.10.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz", + "integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" } }, "node_modules/flat": { @@ -3230,6 +3367,12 @@ "node": ">= 0.6" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, "node_modules/fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", @@ -3277,33 +3420,60 @@ "optional": true }, "node_modules/gaxios": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.0.tgz", - "integrity": "sha512-aezGIjb+/VfsJtIcHGcBSerNEDdfdHeMros+RbYbGpmonKWQCOVOes0LVZhn1lDtIgq55qq0HaxymIoae3Fl/A==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.1.1.tgz", + "integrity": "sha512-bw8smrX+XlAoo9o1JAksBwX+hi/RG15J+NTSxmNPIclKC3ZVK6C2afwY8OSdRvOK0+ZLecUJYtj2MmjOt3Dm0w==", "dev": true, "optional": true, "dependencies": { "extend": "^3.0.2", - "https-proxy-agent": "^5.0.0", + "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", - "node-fetch": "^2.6.7" + "node-fetch": "^2.6.9" }, "engines": { - "node": ">=12" + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "optional": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/gaxios/node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "optional": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" } }, "node_modules/gcp-metadata": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.2.0.tgz", - "integrity": "sha512-aFhhvvNycky2QyhG+dcfEdHBF0FRbYcf39s6WNHUDysKSrbJ5vuFbjydxBcmewtXeV248GP8dWT3ByPNxsyHCw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", + "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", "dev": true, "optional": true, "dependencies": { - "gaxios": "^5.0.0", + "gaxios": "^6.0.0", "json-bigint": "^1.0.0" }, "engines": { - "node": ">=12" + "node": ">=14" } }, "node_modules/get-caller-file": { @@ -3334,6 +3504,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true + }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -3402,91 +3578,44 @@ } }, "node_modules/google-auth-library": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.8.0.tgz", - "integrity": "sha512-0iJn7IDqObDG5Tu9Tn2WemmJ31ksEa96IyK0J0OZCpTh6CrC6FrattwKX87h3qKVuprCJpdOGKc1Xi8V0kMh8Q==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.4.1.tgz", + "integrity": "sha512-Chs7cuzDuav8W/BXOoRgSXw4u0zxYtuqAHETDR5Q6dG1RwNwz7NUKjsDDHAsBV3KkiiJBtJqjbzy1XU1L41w1g==", "dev": true, "optional": true, "dependencies": { - "arrify": "^2.0.0", "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", - "fast-text-encoding": "^1.0.0", - "gaxios": "^5.0.0", - "gcp-metadata": "^5.2.0", - "gtoken": "^6.1.0", - "jws": "^4.0.0", - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/google-auth-library/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "optional": true, - "dependencies": { - "yallist": "^4.0.0" + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" } }, - "node_modules/google-auth-library/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "optional": true - }, "node_modules/google-gax": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-3.6.0.tgz", - "integrity": "sha512-2fyb61vWxUonHiArRNJQmE4tx5oY1ni8VPo08fzII409vDSCWG7apDX4qNOQ2GXXT82gLBn3d3P1Dydh7pWjyw==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.0.5.tgz", + "integrity": "sha512-yLoYtp4zE+8OQA74oBEbNkbzI6c95W01JSL7RqC8XERKpRvj3ytZp1dgnbA6G9aRsc8pZB25xWYBcCmrbYOEhA==", "dev": true, "optional": true, "dependencies": { - "@grpc/grpc-js": "~1.8.0", + "@grpc/grpc-js": "~1.9.6", "@grpc/proto-loader": "^0.7.0", "@types/long": "^4.0.0", - "@types/rimraf": "^3.0.2", "abort-controller": "^3.0.0", "duplexify": "^4.0.0", - "fast-text-encoding": "^1.0.3", - "google-auth-library": "^8.0.2", - "is-stream-ended": "^0.1.4", + "google-auth-library": "^9.0.0", "node-fetch": "^2.6.1", "object-hash": "^3.0.0", - "proto3-json-serializer": "^1.0.0", - "protobufjs": "7.2.3", - "protobufjs-cli": "1.1.1", - "retry-request": "^5.0.0" - }, - "bin": { - "compileProtos": "build/tools/compileProtos.js", - "minifyProtoJson": "build/tools/minify.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/google-p12-pem": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz", - "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==", - "dev": true, - "optional": true, - "dependencies": { - "node-forge": "^1.3.1" - }, - "bin": { - "gp12-pem": "build/src/bin/gp12-pem.js" + "proto3-json-serializer": "^2.0.0", + "protobufjs": "7.2.5", + "retry-request": "^7.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14" } }, "node_modules/graceful-fs": { @@ -3502,18 +3631,17 @@ "dev": true }, "node_modules/gtoken": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz", - "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.0.1.tgz", + "integrity": "sha512-KcFVtoP1CVFtQu0aSk3AyAt2og66PFhZAlkUOuWKwzMLoulHXG5W5wE5xAnHb+yl3/wEFoqGW7/cDGMU8igDZQ==", "dev": true, "optional": true, "dependencies": { - "gaxios": "^5.0.1", - "google-p12-pem": "^4.0.0", + "gaxios": "^6.0.0", "jws": "^4.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, "node_modules/has": { @@ -3648,6 +3776,26 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -3706,6 +3854,12 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -3814,13 +3968,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-stream-ended": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", - "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==", - "dev": true, - "optional": true - }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -4252,11 +4399,9 @@ "dev": true }, "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "dev": true, - "optional": true + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, "node_modules/loupe": { "version": "2.3.6", @@ -4428,6 +4573,18 @@ "node": ">= 0.6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -4473,6 +4630,12 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, "node_modules/mocha": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", @@ -4691,6 +4854,12 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -4757,6 +4926,24 @@ "node": ">= 10.13" } }, + "node_modules/node-abi": { + "version": "3.62.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.62.0.tgz", + "integrity": "sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "dev": true + }, "node_modules/node-fetch": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", @@ -5051,6 +5238,32 @@ "ms": "^2.1.1" } }, + "node_modules/prebuild-install": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", + "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5103,22 +5316,22 @@ } }, "node_modules/proto3-json-serializer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-1.1.1.tgz", - "integrity": "sha512-AwAuY4g9nxx0u52DnSMkqqgyLHaW/XaPLtaAo3y/ZCfeaQB/g4YDH4kb8Wc/mWzWvu0YjOznVnfn373MVZZrgw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.0.tgz", + "integrity": "sha512-FB/YaNrpiPkyQNSNPilpn8qn0KdEfkgmJ9JP93PQyF/U4bAiXY5BiUdDhiDO4S48uSQ6AesklgVlrKiqZPzegw==", "dev": true, "optional": true, "dependencies": { "protobufjs": "^7.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, "node_modules/protobufjs": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.3.tgz", - "integrity": "sha512-TtpvOqwB5Gdz/PQmOjgsrGH1nHjAQVCN7JG4A6r1sXRWESL5rNMAiRcBQlCAdKxZcAbstExQePYG8xof/JVRgg==", + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -5297,11 +5510,6 @@ "node": ">= 0.8.0" } }, - "node_modules/protobufjs/node_modules/long": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", - "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==" - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -5326,6 +5534,16 @@ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -5406,12 +5624,35 @@ "node": ">= 0.8" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, - "optional": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -5506,17 +5747,19 @@ } }, "node_modules/retry-request": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-5.0.2.tgz", - "integrity": "sha512-wfI3pk7EE80lCIXprqh7ym48IHYdwmAAzESdbU8Q9l7pnRCk9LEhpbOTNKjz6FARLm/Bl5m+4F0ABxOkYUujSQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.1.tgz", + "integrity": "sha512-ZI6vJp9rfB71mrZpw+n9p/B6HCsd7QJlSEQftZ+xfJzr3cQ9EPGKw1FF0BnViJ0fYREX6FhymBD2CARpmsFciQ==", "dev": true, "optional": true, "dependencies": { + "@types/request": "^2.48.8", "debug": "^4.1.1", - "extend": "^3.0.2" + "extend": "^3.0.2", + "teeny-request": "^9.0.0" }, "engines": { - "node": ">=12" + "node": ">=14" } }, "node_modules/reusify": { @@ -5756,6 +5999,51 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/sinon": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz", @@ -5877,7 +6165,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, - "optional": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -5929,6 +6216,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "dev": true, + "optional": true + }, "node_modules/stubs": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", @@ -5966,21 +6260,49 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/teeny-request": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-8.0.3.tgz", - "integrity": "sha512-jJZpA5He2y52yUhA7pyAGZlgQpcB+xLjcN0eUFxr9c8hP/H7uOXbBNVo/O0C/xVfJLJs680jvkFgVJEEvk9+ww==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", "dev": true, "optional": true, "dependencies": { "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.1", + "node-fetch": "^2.6.9", "stream-events": "^1.0.5", "uuid": "^9.0.0" }, "engines": { - "node": ">=12" + "node": ">=14" } }, "node_modules/teeny-request/node_modules/@tootallnate/once": { @@ -6008,12 +6330,6 @@ "node": ">= 6" } }, - "node_modules/text-decoding": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-decoding/-/text-decoding-1.0.0.tgz", - "integrity": "sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA==", - "dev": true - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -6203,6 +6519,18 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6285,6 +6613,12 @@ "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -6325,8 +6659,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "optional": true + "dev": true }, "node_modules/utils-merge": { "version": "1.0.1", @@ -6337,10 +6670,14 @@ } }, "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } @@ -6821,13 +7158,10 @@ "dev": true }, "@fastify/busboy": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-1.2.1.tgz", - "integrity": "sha512-7PQA7EH43S0CxcOa9OeAnaeA0oQ+e/DHNPZwSQM9CQHW76jle5+OvLdibRp/Aafs9KXbLhxyjOTkRjWUbQEd3Q==", - "dev": true, - "requires": { - "text-decoding": "^1.0.0" - } + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "dev": true }, "@firebase/api-documenter": { "version": "0.2.0", @@ -6862,6 +7196,12 @@ } } }, + "@firebase/app-check-interop-types": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.0.tgz", + "integrity": "sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg==", + "dev": true + }, "@firebase/app-types": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", @@ -6885,11 +7225,12 @@ } }, "@firebase/database": { - "version": "0.14.4", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.14.4.tgz", - "integrity": "sha512-+Ea/IKGwh42jwdjCyzTmeZeLM3oy1h0mFPsTy6OqCWzcu/KFqRAr5Tt1HRCOBlNOdbh84JPZC47WLU18n2VbxQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.2.tgz", + "integrity": "sha512-8X6NBJgUQzDz0xQVaCISoOLINKat594N2eBbMR3Mu/MH/ei4WM+aAMlsNzngF22eljXu1SILP5G3evkyvsG3Ng==", "dev": true, "requires": { + "@firebase/app-check-interop-types": "0.3.0", "@firebase/auth-interop-types": "0.2.1", "@firebase/component": "0.6.4", "@firebase/logger": "0.4.0", @@ -6899,23 +7240,23 @@ } }, "@firebase/database-compat": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.3.4.tgz", - "integrity": "sha512-kuAW+l+sLMUKBThnvxvUZ+Q1ZrF/vFJ58iUY9kAcbX48U03nVzIF6Tmkf0p3WVQwMqiXguSgtOPIB6ZCeF+5Gg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.2.tgz", + "integrity": "sha512-09ryJnXDvuycsxn8aXBzLhBTuCos3HEnCOBWY6hosxfYlNCGnLvG8YMlbSAt5eNhf7/00B095AEfDsdrrLjxqA==", "dev": true, "requires": { "@firebase/component": "0.6.4", - "@firebase/database": "0.14.4", - "@firebase/database-types": "0.10.4", + "@firebase/database": "1.0.2", + "@firebase/database-types": "1.0.0", "@firebase/logger": "0.4.0", "@firebase/util": "1.9.3", "tslib": "^2.1.0" } }, "@firebase/database-types": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.10.4.tgz", - "integrity": "sha512-dPySn0vJ/89ZeBac70T+2tWWPiJXWbmRygYv0smT5TfE3hDrQ09eKMF3Y+vMlTdrMWq7mUdYW5REWPSGH4kAZQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.0.tgz", + "integrity": "sha512-SjnXStoE0Q56HcFgNQ+9SsmJc0c8TqGARdI/T44KXy+Ets3r6x/ivhQozT66bMnCEjJRywYoxNurRTMlZF8VNg==", "dev": true, "requires": { "@firebase/app-types": "0.9.0", @@ -6941,22 +7282,22 @@ } }, "@google-cloud/firestore": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-6.5.0.tgz", - "integrity": "sha512-U0QwG6pEQxO5c0v0eUylswozmuvlvz7iXSW+I18jzqR2hAFrUq2Weu1wm3NaH8wGD4ZL7W9Be4cMHG5CYU8LuQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.1.0.tgz", + "integrity": "sha512-kkTC0Sb9r2lONuFF8Tr2wFfBfk0DT1/EKcTKOhsuoXUVClv3jCqGYVPtHgQsHFjdOsubS+tx9G5D5WG+obB2DA==", "dev": true, "optional": true, "requires": { "fast-deep-equal": "^3.1.1", "functional-red-black-tree": "^1.0.1", - "google-gax": "^3.5.7", - "protobufjs": "^7.0.0" + "google-gax": "^4.0.4", + "protobufjs": "^7.2.5" } }, "@google-cloud/paginator": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.7.tgz", - "integrity": "sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.0.tgz", + "integrity": "sha512-87aeg6QQcEPxGCOthnpUjvw4xAZ57G7pL8FS0C4e/81fr3FjkpUpibf1s2v5XGyGhUVGF4Jfg7yEcxqn2iUw1w==", "dev": true, "optional": true, "requires": { @@ -6965,42 +7306,42 @@ } }, "@google-cloud/projectify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-3.0.0.tgz", - "integrity": "sha512-HRkZsNmjScY6Li8/kb70wjGlDDyLkVk3KvoEo9uIoxSjYLJasGiCch9+PqRVDOCGUFvEIqyogl+BeqILL4OJHA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", + "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", "dev": true, "optional": true }, "@google-cloud/promisify": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-3.0.1.tgz", - "integrity": "sha512-z1CjRjtQyBOYL+5Qr9DdYIfrdLBe746jRTYfaYU6MeXkqp7UfYs/jX16lFFVzZ7PGEJvqZNqYUEtb1mvDww4pA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", + "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", "dev": true, "optional": true }, "@google-cloud/storage": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-6.10.0.tgz", - "integrity": "sha512-MaFNtMxPpnv6c43HcRsJTUiYhXgcjy+mshLyZpfGKMpE2vJ8C1mBFK/ZrlcPBt47ZK0tz9p/mNTyvi8dRsdKPw==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.7.0.tgz", + "integrity": "sha512-EMCEY+6JiIkx7Dt8NXVGGjy1vRdSGdHkoqZoqjJw7cEBkT7ZkX0c7puedfn1MamnzW5SX4xoa2jVq5u7OWBmkQ==", "dev": true, "optional": true, "requires": { - "@google-cloud/paginator": "^3.0.7", - "@google-cloud/projectify": "^3.0.0", - "@google-cloud/promisify": "^3.0.0", + "@google-cloud/paginator": "^5.0.0", + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", "abort-controller": "^3.0.0", "async-retry": "^1.3.3", "compressible": "^2.0.12", "duplexify": "^4.0.0", "ent": "^2.2.0", - "extend": "^3.0.2", - "gaxios": "^5.0.0", - "google-auth-library": "^8.0.1", + "fast-xml-parser": "^4.3.0", + "gaxios": "^6.0.2", + "google-auth-library": "^9.0.0", "mime": "^3.0.0", "mime-types": "^2.0.8", "p-limit": "^3.0.1", - "retry-request": "^5.0.0", - "teeny-request": "^8.0.0", + "retry-request": "^7.0.0", + "teeny-request": "^9.0.0", "uuid": "^8.0.0" }, "dependencies": { @@ -7014,27 +7355,26 @@ } }, "@grpc/grpc-js": { - "version": "1.8.14", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.8.14.tgz", - "integrity": "sha512-w84maJ6CKl5aApCMzFll0hxtFNT6or9WwMslobKaqWUEf1K+zhlL43bSQhFreyYWIWR+Z0xnVFC1KtLm4ZpM/A==", + "version": "1.9.13", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.13.tgz", + "integrity": "sha512-OEZZu9v9AA+7/tghMDE8o5DAMD5THVnwSqDWuh7PPYO5287rTyqy0xEHT6/e4pbqSrhyLPdQFsam4TwFQVVIIw==", "dev": true, "optional": true, "requires": { - "@grpc/proto-loader": "^0.7.0", + "@grpc/proto-loader": "^0.7.8", "@types/node": ">=12.12.47" } }, "@grpc/proto-loader": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.7.tgz", - "integrity": "sha512-1TIeXOi8TuSCQprPItwoMymZXxWT0CPxUhkrkeCUH+D8U7QDwQ6b7SUz2MaLuWM2llT+J/TVFLmQI5KtML3BhQ==", + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", + "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", "dev": true, "optional": true, "requires": { - "@types/long": "^4.0.1", "lodash.camelcase": "^4.3.0", - "long": "^4.0.0", - "protobufjs": "^7.0.0", + "long": "^5.0.0", + "protobufjs": "^7.2.4", "yargs": "^17.7.2" }, "dependencies": { @@ -7595,6 +7935,13 @@ "@types/node": "*" } }, + "@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "dev": true, + "optional": true + }, "@types/chai": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", @@ -7646,17 +7993,6 @@ "@types/range-parser": "*" } }, - "@types/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", - "dev": true, - "optional": true, - "requires": { - "@types/minimatch": "^5.1.2", - "@types/node": "*" - } - }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -7706,13 +8042,6 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, - "@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true, - "optional": true - }, "@types/mocha": { "version": "5.2.7", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", @@ -7761,15 +8090,31 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, - "@types/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ==", + "@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", "dev": true, "optional": true, "requires": { - "@types/glob": "*", - "@types/node": "*" + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + }, + "dependencies": { + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "optional": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } } }, "@types/semver": { @@ -7793,6 +8138,13 @@ "integrity": "sha512-T+m89VdXj/eidZyejvmoP9jivXgBDdkOSBVQjU9kF349NEx10QdPNGxHeZUaj1IlJ32/ewdyXJjnJxyxJroYwg==", "dev": true }, + "@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "optional": true + }, "@typescript-eslint/eslint-plugin": { "version": "5.55.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz", @@ -8179,13 +8531,12 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "optional": true + "dev": true }, "bignumber.js": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", - "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", "dev": true, "optional": true }, @@ -8195,6 +8546,17 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -8278,6 +8640,16 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -8397,6 +8769,12 @@ } } }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -8570,6 +8948,15 @@ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", "dev": true }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "requires": { + "mimic-response": "^3.1.0" + } + }, "deep-eql": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", @@ -8579,6 +8966,12 @@ "type-detect": "^4.0.0" } }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -8601,6 +8994,12 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, + "detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true + }, "diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -8700,7 +9099,6 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, - "optional": true, "requires": { "once": "^1.4.0" } @@ -9025,6 +9423,12 @@ "dev": true, "optional": true }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true + }, "express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -9085,6 +9489,16 @@ "dev": true, "optional": true }, + "farmhash": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/farmhash/-/farmhash-3.3.1.tgz", + "integrity": "sha512-XUizHanzlr/v7suBr/o85HSakOoWh6HKXZjFYl5C2+Gj0f0rkw+XTUZzrd9odDsgI9G5tRUcF4wSbKaX04T0DQ==", + "dev": true, + "requires": { + "node-addon-api": "^5.1.0", + "prebuild-install": "^7.1.2" + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -9133,12 +9547,15 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, - "fast-text-encoding": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz", - "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==", + "fast-xml-parser": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.2.tgz", + "integrity": "sha512-rmrXUXwbJedoXkStenj1kkljNF7ugn5ZjR9FJcwmCfcCbtOMDghPajbc+Tck6vE6F5XsDmx+Pr2le9fw8+pXBg==", "dev": true, - "optional": true + "optional": true, + "requires": { + "strnum": "^1.0.5" + } }, "fastq": { "version": "1.15.0", @@ -9216,21 +9633,34 @@ } }, "firebase-admin": { - "version": "11.8.0", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-11.8.0.tgz", - "integrity": "sha512-RxO0wWDnuqVikXExhVjnhVaaXpziKCad4D1rOX5c1WJdk1jAu9hfE4rbrFKZQZgF1okZS04kgCBIFJro7xn8NQ==", - "dev": true, - "requires": { - "@fastify/busboy": "^1.2.1", - "@firebase/database-compat": "^0.3.4", - "@firebase/database-types": "^0.10.4", - "@google-cloud/firestore": "^6.5.0", - "@google-cloud/storage": "^6.9.5", - "@types/node": ">=12.12.47", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-12.1.0.tgz", + "integrity": "sha512-bU7uPKMmIXAihWxntpY/Ma9zucn5y3ec+HQPqFQ/zcEfP9Avk9E/6D8u+yT/VwKHNZyg7yDVWOoJi73TIdR4Ww==", + "dev": true, + "requires": { + "@fastify/busboy": "^2.1.0", + "@firebase/database-compat": "^1.0.2", + "@firebase/database-types": "^1.0.0", + "@google-cloud/firestore": "^7.1.0", + "@google-cloud/storage": "^7.7.0", + "@types/node": "^20.10.3", + "farmhash": "^3.3.0", "jsonwebtoken": "^9.0.0", "jwks-rsa": "^3.0.1", + "long": "^5.2.3", "node-forge": "^1.3.1", "uuid": "^9.0.0" + }, + "dependencies": { + "@types/node": { + "version": "20.10.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz", + "integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==", + "dev": true, + "requires": { + "undici-types": "~5.26.4" + } + } } }, "flat": { @@ -9276,6 +9706,12 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, "fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", @@ -9313,26 +9749,49 @@ "optional": true }, "gaxios": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.0.tgz", - "integrity": "sha512-aezGIjb+/VfsJtIcHGcBSerNEDdfdHeMros+RbYbGpmonKWQCOVOes0LVZhn1lDtIgq55qq0HaxymIoae3Fl/A==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.1.1.tgz", + "integrity": "sha512-bw8smrX+XlAoo9o1JAksBwX+hi/RG15J+NTSxmNPIclKC3ZVK6C2afwY8OSdRvOK0+ZLecUJYtj2MmjOt3Dm0w==", "dev": true, "optional": true, "requires": { "extend": "^3.0.2", - "https-proxy-agent": "^5.0.0", + "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", - "node-fetch": "^2.6.7" + "node-fetch": "^2.6.9" + }, + "dependencies": { + "agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "optional": true, + "requires": { + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "optional": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + } } }, "gcp-metadata": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.2.0.tgz", - "integrity": "sha512-aFhhvvNycky2QyhG+dcfEdHBF0FRbYcf39s6WNHUDysKSrbJ5vuFbjydxBcmewtXeV248GP8dWT3ByPNxsyHCw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", + "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", "dev": true, "optional": true, "requires": { - "gaxios": "^5.0.0", + "gaxios": "^6.0.0", "json-bigint": "^1.0.0" } }, @@ -9358,6 +9817,12 @@ "has-symbols": "^1.0.3" } }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true + }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -9405,74 +9870,38 @@ } }, "google-auth-library": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.8.0.tgz", - "integrity": "sha512-0iJn7IDqObDG5Tu9Tn2WemmJ31ksEa96IyK0J0OZCpTh6CrC6FrattwKX87h3qKVuprCJpdOGKc1Xi8V0kMh8Q==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.4.1.tgz", + "integrity": "sha512-Chs7cuzDuav8W/BXOoRgSXw4u0zxYtuqAHETDR5Q6dG1RwNwz7NUKjsDDHAsBV3KkiiJBtJqjbzy1XU1L41w1g==", "dev": true, "optional": true, "requires": { - "arrify": "^2.0.0", "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", - "fast-text-encoding": "^1.0.0", - "gaxios": "^5.0.0", - "gcp-metadata": "^5.2.0", - "gtoken": "^6.1.0", - "jws": "^4.0.0", - "lru-cache": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "optional": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "optional": true - } + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" } }, "google-gax": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-3.6.0.tgz", - "integrity": "sha512-2fyb61vWxUonHiArRNJQmE4tx5oY1ni8VPo08fzII409vDSCWG7apDX4qNOQ2GXXT82gLBn3d3P1Dydh7pWjyw==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.0.5.tgz", + "integrity": "sha512-yLoYtp4zE+8OQA74oBEbNkbzI6c95W01JSL7RqC8XERKpRvj3ytZp1dgnbA6G9aRsc8pZB25xWYBcCmrbYOEhA==", "dev": true, "optional": true, "requires": { - "@grpc/grpc-js": "~1.8.0", + "@grpc/grpc-js": "~1.9.6", "@grpc/proto-loader": "^0.7.0", "@types/long": "^4.0.0", - "@types/rimraf": "^3.0.2", "abort-controller": "^3.0.0", "duplexify": "^4.0.0", - "fast-text-encoding": "^1.0.3", - "google-auth-library": "^8.0.2", - "is-stream-ended": "^0.1.4", + "google-auth-library": "^9.0.0", "node-fetch": "^2.6.1", "object-hash": "^3.0.0", - "proto3-json-serializer": "^1.0.0", - "protobufjs": "7.2.3", - "protobufjs-cli": "1.1.1", - "retry-request": "^5.0.0" - } - }, - "google-p12-pem": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz", - "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==", - "dev": true, - "optional": true, - "requires": { - "node-forge": "^1.3.1" + "proto3-json-serializer": "^2.0.0", + "protobufjs": "7.2.5", + "retry-request": "^7.0.0" } }, "graceful-fs": { @@ -9488,14 +9917,13 @@ "dev": true }, "gtoken": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz", - "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.0.1.tgz", + "integrity": "sha512-KcFVtoP1CVFtQu0aSk3AyAt2og66PFhZAlkUOuWKwzMLoulHXG5W5wE5xAnHb+yl3/wEFoqGW7/cDGMU8igDZQ==", "dev": true, "optional": true, "requires": { - "gaxios": "^5.0.1", - "google-p12-pem": "^4.0.0", + "gaxios": "^6.0.0", "jws": "^4.0.0" } }, @@ -9601,6 +10029,12 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, "ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -9644,6 +10078,12 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -9719,13 +10159,6 @@ "dev": true, "optional": true }, - "is-stream-ended": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", - "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==", - "dev": true, - "optional": true - }, "is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -10096,11 +10529,9 @@ "dev": true }, "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "dev": true, - "optional": true + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, "loupe": { "version": "2.3.6", @@ -10240,6 +10671,12 @@ "mime-db": "1.52.0" } }, + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -10276,6 +10713,12 @@ "minimist": "^1.2.6" } }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, "mocha": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", @@ -10448,6 +10891,12 @@ "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true }, + "napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -10510,6 +10959,21 @@ "propagate": "^2.0.0" } }, + "node-abi": { + "version": "3.62.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.62.0.tgz", + "integrity": "sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g==", + "dev": true, + "requires": { + "semver": "^7.3.5" + } + }, + "node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "dev": true + }, "node-fetch": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", @@ -10728,6 +11192,26 @@ } } }, + "prebuild-install": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", + "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", + "dev": true, + "requires": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -10762,9 +11246,9 @@ "dev": true }, "proto3-json-serializer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-1.1.1.tgz", - "integrity": "sha512-AwAuY4g9nxx0u52DnSMkqqgyLHaW/XaPLtaAo3y/ZCfeaQB/g4YDH4kb8Wc/mWzWvu0YjOznVnfn373MVZZrgw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.0.tgz", + "integrity": "sha512-FB/YaNrpiPkyQNSNPilpn8qn0KdEfkgmJ9JP93PQyF/U4bAiXY5BiUdDhiDO4S48uSQ6AesklgVlrKiqZPzegw==", "dev": true, "optional": true, "requires": { @@ -10772,9 +11256,9 @@ } }, "protobufjs": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.3.tgz", - "integrity": "sha512-TtpvOqwB5Gdz/PQmOjgsrGH1nHjAQVCN7JG4A6r1sXRWESL5rNMAiRcBQlCAdKxZcAbstExQePYG8xof/JVRgg==", + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -10788,13 +11272,6 @@ "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" - }, - "dependencies": { - "long": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", - "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==" - } } }, "protobufjs-cli": { @@ -10935,6 +11412,16 @@ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -10986,12 +11473,31 @@ "unpipe": "1.0.0" } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true + } + } + }, "readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, - "optional": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -11065,14 +11571,16 @@ "optional": true }, "retry-request": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-5.0.2.tgz", - "integrity": "sha512-wfI3pk7EE80lCIXprqh7ym48IHYdwmAAzESdbU8Q9l7pnRCk9LEhpbOTNKjz6FARLm/Bl5m+4F0ABxOkYUujSQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.1.tgz", + "integrity": "sha512-ZI6vJp9rfB71mrZpw+n9p/B6HCsd7QJlSEQftZ+xfJzr3cQ9EPGKw1FF0BnViJ0fYREX6FhymBD2CARpmsFciQ==", "dev": true, "optional": true, "requires": { + "@types/request": "^2.48.8", "debug": "^4.1.1", - "extend": "^3.0.2" + "extend": "^3.0.2", + "teeny-request": "^9.0.0" } }, "reusify": { @@ -11247,6 +11755,23 @@ "object-inspect": "^1.9.0" } }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true + }, + "simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "requires": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "sinon": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz", @@ -11352,7 +11877,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.2.0" } @@ -11389,6 +11913,13 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "dev": true, + "optional": true + }, "stubs": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", @@ -11417,16 +11948,41 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, "teeny-request": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-8.0.3.tgz", - "integrity": "sha512-jJZpA5He2y52yUhA7pyAGZlgQpcB+xLjcN0eUFxr9c8hP/H7uOXbBNVo/O0C/xVfJLJs680jvkFgVJEEvk9+ww==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", "dev": true, "optional": true, "requires": { "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.1", + "node-fetch": "^2.6.9", "stream-events": "^1.0.5", "uuid": "^9.0.0" }, @@ -11452,12 +12008,6 @@ } } }, - "text-decoding": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-decoding/-/text-decoding-1.0.0.tgz", - "integrity": "sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA==", - "dev": true - }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -11598,6 +12148,15 @@ } } }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -11652,6 +12211,12 @@ "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -11686,8 +12251,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "optional": true + "dev": true }, "utils-merge": { "version": "1.0.1", @@ -11695,9 +12259,9 @@ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "dev": true }, "v8-compile-cache-lib": { diff --git a/package.json b/package.json index 0c2c9b835..258be26ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "4.4.1", + "version": "5.0.1", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", @@ -110,6 +110,9 @@ "v2": [ "lib/v2" ], + "v2/core": [ + "lib/v2/core" + ], "v2/alerts": [ "lib/v2/providers/alerts" ], @@ -198,7 +201,6 @@ "@types/express": "4.17.3", "cors": "^2.8.5", "express": "^4.17.1", - "node-fetch": "^2.6.7", "protobufjs": "^7.2.2" }, "devDependencies": { @@ -225,7 +227,7 @@ "eslint-config-prettier": "^8.3.0", "eslint-plugin-jsdoc": "^39.2.9", "eslint-plugin-prettier": "^4.0.0", - "firebase-admin": "^11.8.0", + "firebase-admin": "^12.1.0", "js-yaml": "^3.13.1", "jsdom": "^16.2.1", "jsonwebtoken": "^9.0.0", @@ -245,7 +247,7 @@ "yargs": "^15.3.1" }, "peerDependencies": { - "firebase-admin": "^10.0.0 || ^11.0.0" + "firebase-admin": "^11.10.0 || ^12.0.0" }, "engines": { "node": ">=14.10.0" diff --git a/scripts/bin-test/run.sh b/scripts/bin-test/run.sh index c3e3da673..aa10b4286 100755 --- a/scripts/bin-test/run.sh +++ b/scripts/bin-test/run.sh @@ -12,8 +12,8 @@ for f in scripts/bin-test/sources/*; do fi done -## DEBUG -ls -la scripts/bin-test/sources/commonjs/node_modules +# Make sure firebase-functions binary is executable +chmod +x ./lib/bin/firebase-functions.js mocha \ --file ./scripts/bin-test/mocha-setup.ts \ diff --git a/scripts/bin-test/test.ts b/scripts/bin-test/test.ts index b15012d70..efc5a5127 100644 --- a/scripts/bin-test/test.ts +++ b/scripts/bin-test/test.ts @@ -6,7 +6,6 @@ import { expect } from "chai"; import * as yaml from "js-yaml"; import fetch from "node-fetch"; import * as portfinder from "portfinder"; -import * as semver from "semver"; const TIMEOUT_XL = 20_000; const TIMEOUT_L = 10_000; @@ -124,7 +123,7 @@ async function startBin( throw e; } return true; - }, TIMEOUT_M); + }, TIMEOUT_L); if (debug) { proc.stdout?.on("data", (data: unknown) => { @@ -139,7 +138,7 @@ async function startBin( return { port, cleanup: async () => { - process.kill(proc.pid); + process.kill(proc.pid, 9); await retryUntil(async () => { try { process.kill(proc.pid, 0); @@ -148,12 +147,15 @@ async function startBin( return Promise.resolve(true); } return Promise.resolve(false); - }, TIMEOUT_M); + }, TIMEOUT_L); }, }; } -describe("functions.yaml", () => { +describe("functions.yaml", function () { + // eslint-disable-next-line @typescript-eslint/no-invalid-this + this.timeout(TIMEOUT_XL); + function runTests(tc: Testcase) { let port: number; let cleanup: () => Promise; @@ -168,7 +170,10 @@ describe("functions.yaml", () => { await cleanup?.(); }); - it("functions.yaml returns expected Manifest", async () => { + it("functions.yaml returns expected Manifest", async function () { + // eslint-disable-next-line @typescript-eslint/no-invalid-this + this.timeout(TIMEOUT_M); + const res = await fetch(`http://localhost:${port}/__/functions.yaml`); const text = await res.text(); let parsed: any; @@ -181,7 +186,10 @@ describe("functions.yaml", () => { }); } - describe("commonjs", () => { + describe("commonjs", function () { + // eslint-disable-next-line @typescript-eslint/no-invalid-this + this.timeout(TIMEOUT_L); + const testcases: Testcase[] = [ { name: "basic", @@ -250,34 +258,35 @@ describe("functions.yaml", () => { runTests(tc); }); } - }).timeout(TIMEOUT_L); + }); - if (semver.gt(process.versions.node, "13.2.0")) { - describe("esm", () => { - const testcases: Testcase[] = [ - { - name: "basic", - modulePath: "./scripts/bin-test/sources/esm", - expected: BASE_STACK, - }, - { - name: "with main", + describe("esm", function () { + // eslint-disable-next-line @typescript-eslint/no-invalid-this + this.timeout(TIMEOUT_L); - modulePath: "./scripts/bin-test/sources/esm-main", - expected: BASE_STACK, - }, - { - name: "with .m extension", - modulePath: "./scripts/bin-test/sources/esm-ext", - expected: BASE_STACK, - }, - ]; + const testcases: Testcase[] = [ + { + name: "basic", + modulePath: "./scripts/bin-test/sources/esm", + expected: BASE_STACK, + }, + { + name: "with main", - for (const tc of testcases) { - describe(tc.name, () => { - runTests(tc); - }); - } - }).timeout(TIMEOUT_L); - } -}).timeout(TIMEOUT_XL); + modulePath: "./scripts/bin-test/sources/esm-main", + expected: BASE_STACK, + }, + { + name: "with .m extension", + modulePath: "./scripts/bin-test/sources/esm-ext", + expected: BASE_STACK, + }, + ]; + + for (const tc of testcases) { + describe(tc.name, () => { + runTests(tc); + }); + } + }); +}); diff --git a/spec/common/providers/identity.spec.ts b/spec/common/providers/identity.spec.ts index 2dfb6aa10..cfbaca770 100644 --- a/spec/common/providers/identity.spec.ts +++ b/spec/common/providers/identity.spec.ts @@ -210,8 +210,8 @@ describe("identity", () => { describe("parseMetadata", () => { const decodedMetadata = { - last_sign_in_time: 1476235905, - creation_time: 1476136676, + last_sign_in_time: 1476235905000, + creation_time: 1476136676000, }; const metadata = { lastSignInTime: new Date(1476235905000).toUTCString(), @@ -377,8 +377,8 @@ describe("identity", () => { photo_url: "https://lh3.googleusercontent.com/1234567890/photo.jpg", tokens_valid_after_time: 1476136676, metadata: { - last_sign_in_time: 1476235905, - creation_time: 1476136676, + last_sign_in_time: 1476235905000, + creation_time: 1476136676000, }, custom_claims: { admin: true, @@ -640,8 +640,8 @@ describe("identity", () => { photo_url: "https://lh3.googleusercontent.com/1234567890/photo.jpg", tokens_valid_after_time: 1476136676, metadata: { - last_sign_in_time: 1476235905, - creation_time: 1476136676, + last_sign_in_time: 1476235905000, + creation_time: 1476136676000, }, custom_claims: { admin: true, diff --git a/spec/v1/cloud-functions.spec.ts b/spec/v1/cloud-functions.spec.ts index 6ee3abc41..d85afbe2f 100644 --- a/spec/v1/cloud-functions.spec.ts +++ b/spec/v1/cloud-functions.spec.ts @@ -23,6 +23,7 @@ import { expect } from "chai"; import { + onInit, Event, EventContext, makeCloudFunction, @@ -41,6 +42,34 @@ describe("makeCloudFunction", () => { legacyEventType: "providers/provider/eventTypes/event", }; + it("calls init function", async () => { + const test: Event = { + context: { + eventId: "00000", + timestamp: "2016-11-04T21:29:03.496Z", + eventType: "provider.event", + resource: { + service: "provider", + name: "resource", + }, + }, + data: "data", + }; + const cf = makeCloudFunction({ + provider: "mock.provider", + eventType: "mock.event", + service: "service", + triggerResource: () => "resource", + handler: () => null, + }); + + let hello; + onInit(() => (hello = "world")); + expect(hello).is.undefined; + await cf(test.data, test.context); + expect(hello).equals("world"); + }); + it("should put a __trigger/__endpoint on the returned CloudFunction", () => { const cf = makeCloudFunction({ provider: "mock.provider", diff --git a/spec/v1/providers/analytics.spec.ts b/spec/v1/providers/analytics.spec.ts index 90a617686..98db1702f 100644 --- a/spec/v1/providers/analytics.spec.ts +++ b/spec/v1/providers/analytics.spec.ts @@ -85,7 +85,7 @@ describe("Analytics Functions", () => { }); describe("#dataConstructor", () => { - it("should handle an event with the appropriate fields", () => { + it("should handle an event with the appropriate fields", async () => { const cloudFunction = analytics .event("first_open") .onLog((data: analytics.AnalyticsEvent) => data); @@ -109,7 +109,7 @@ describe("Analytics Functions", () => { }, }; - return expect(cloudFunction(event.data, event.context)).to.eventually.deep.equal({ + await expect(cloudFunction(event.data, event.context)).to.eventually.deep.equal({ params: {}, user: { userId: "hi!", diff --git a/spec/v1/providers/database.spec.ts b/spec/v1/providers/database.spec.ts index 874b1de29..18d973b1d 100644 --- a/spec/v1/providers/database.spec.ts +++ b/spec/v1/providers/database.spec.ts @@ -458,6 +458,7 @@ describe("DataSnapshot", () => { populate(applyChange({ a: 23 }, { b: 33 })); expect(subject.child("a/b").val()).to.be.null; expect(subject.child("b/c").val()).to.be.null; + expect(subject.child("a/b/c").val()).to.be.null; }); it("should return a leaf value", () => { diff --git a/spec/v1/providers/https.spec.ts b/spec/v1/providers/https.spec.ts index 3fc736952..c3a7671c0 100644 --- a/spec/v1/providers/https.spec.ts +++ b/spec/v1/providers/https.spec.ts @@ -35,6 +35,7 @@ import { import { runHandler } from "../../helper"; import { MINIMAL_V1_ENDPOINT } from "../../fixtures"; import { CALLABLE_AUTH_HEADER, ORIGINAL_AUTH_HEADER } from "../../../src/common/providers/https"; +import { onInit } from "../../../src/v1"; describe("CloudHttpsBuilder", () => { describe("#onRequest", () => { @@ -70,6 +71,26 @@ describe("CloudHttpsBuilder", () => { expect(fn.__endpoint.timeoutSeconds).to.deep.equal(90); expect(fn.__endpoint.httpsTrigger.invoker).to.deep.equal(["private"]); }); + + it("calls init function", async () => { + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + const fn = functions.https.onRequest((_req, res) => { + res.send(200); + }); + const req = new MockRequest( + { + data: { foo: "bar" }, + }, + { + "content-type": "application/json", + } + ); + req.method = "POST"; + await runHandler(fn, req as any); + expect(hello).to.equal("world"); + }); }); }); @@ -114,7 +135,7 @@ describe("#onCall", () => { expect(fn.__endpoint.timeoutSeconds).to.deep.equal(90); }); - it("has a .run method", () => { + it("has a .run method", async () => { const cf = https.onCall((d, c) => { return { data: d, context: c }; }); @@ -127,7 +148,8 @@ describe("#onCall", () => { token: "token", }, }; - expect(cf.run(data, context)).to.deep.equal({ data, context }); + + await expect(cf.run(data, context)).to.eventually.deep.equal({ data, context }); }); // Regression test for firebase-functions#947 @@ -152,6 +174,25 @@ describe("#onCall", () => { expect(gotData).to.deep.equal({ foo: "bar" }); }); + it("should call initializer", async () => { + const func = https.onCall(() => null); + const req = new MockRequest( + { + data: {}, + }, + { + "content-type": "application/json", + } + ); + req.method = "POST"; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await runHandler(func, req as any); + expect(hello).to.equal("world"); + }); + // Test for firebase-tools#5210 it("should create context.auth for v1 emulated functions", async () => { sinon.stub(debug, "isDebugFeatureEnabled").withArgs("skipTokenVerification").returns(true); diff --git a/spec/v1/providers/remoteConfig.spec.ts b/spec/v1/providers/remoteConfig.spec.ts index f5fb427e6..e207b5de3 100644 --- a/spec/v1/providers/remoteConfig.spec.ts +++ b/spec/v1/providers/remoteConfig.spec.ts @@ -122,12 +122,14 @@ describe("RemoteConfig Functions", () => { delete process.env.GCLOUD_PROJECT; }); - it("should unwrap the version in the event", () => { - return Promise.all([ - cloudFunctionUpdate(event.data, event.context).then((data: any) => { - expect(data).to.deep.equal(constructVersion()); - }), - ]); + it("should unwrap the version in the event", async () => { + let hello; + functions.onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await cloudFunctionUpdate(event.data, event.context).then((data: any) => { + expect(data).to.deep.equal(constructVersion()); + }); + expect(hello).to.equal("world"); }); }); }); diff --git a/spec/v2/providers/alerts/alerts.spec.ts b/spec/v2/providers/alerts/alerts.spec.ts index 4476e121e..9f69f0555 100644 --- a/spec/v2/providers/alerts/alerts.spec.ts +++ b/spec/v2/providers/alerts/alerts.spec.ts @@ -1,5 +1,5 @@ import { expect } from "chai"; -import { CloudEvent } from "../../../../src/v2"; +import { CloudEvent, onInit } from "../../../../src/v2"; import * as options from "../../../../src/v2/options"; import * as alerts from "../../../../src/v2/providers/alerts"; import { FULL_OPTIONS } from "../fixtures"; @@ -211,4 +211,21 @@ describe("alerts", () => { }); }); }); + + it("calls init function", async () => { + const event: CloudEvent = { + specversion: "1.0", + id: "id", + source: "source", + type: "type", + time: "now", + data: "data", + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await alerts.onAlertPublished("alert", () => null)(event); + expect(hello).to.equal("world"); + }); }); diff --git a/spec/v2/providers/alerts/appDistribution.spec.ts b/spec/v2/providers/alerts/appDistribution.spec.ts index 7e2b7d0c6..045b84448 100644 --- a/spec/v2/providers/alerts/appDistribution.spec.ts +++ b/spec/v2/providers/alerts/appDistribution.spec.ts @@ -3,6 +3,7 @@ import * as alerts from "../../../../src/v2/providers/alerts"; import * as appDistribution from "../../../../src/v2/providers/alerts/appDistribution"; import { FULL_OPTIONS } from "../fixtures"; import { FULL_ENDPOINT, MINIMAL_V2_ENDPOINT } from "../../../fixtures"; +import { onInit } from "../../../../src/v2/core"; const APPID = "123456789"; const myHandler = () => 42; @@ -91,6 +92,16 @@ describe("appDistribution", () => { expect(res).to.equal("input"); }); + + it("calls init function", async () => { + const func = appDistribution.onNewTesterIosDevicePublished(APPID, (event) => event); + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await func({ data: "test" } as any); + expect(hello).to.equal("world"); + }); }); describe("onInAppfeedbackPublished", () => { @@ -172,6 +183,16 @@ describe("appDistribution", () => { expect(res).to.equal("input"); }); + + it("calls init function", async () => { + const func = appDistribution.onInAppFeedbackPublished(APPID, (event) => event); + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await func({ data: "test" } as any); + expect(hello).to.equal("world"); + }); }); describe("getOptsAndApp", () => { diff --git a/spec/v2/providers/alerts/billing.spec.ts b/spec/v2/providers/alerts/billing.spec.ts index d4be3403d..a0020f83b 100644 --- a/spec/v2/providers/alerts/billing.spec.ts +++ b/spec/v2/providers/alerts/billing.spec.ts @@ -3,6 +3,7 @@ import * as alerts from "../../../../src/v2/providers/alerts"; import * as billing from "../../../../src/v2/providers/alerts/billing"; import { FULL_OPTIONS } from "../fixtures"; import { FULL_ENDPOINT, MINIMAL_V2_ENDPOINT } from "../../../fixtures"; +import { onInit } from "../../../../src/v2/core"; const ALERT_TYPE = "new-alert-type"; const myHandler = () => 42; @@ -41,6 +42,16 @@ describe("billing", () => { }, }); }); + + it("calls init function", async () => { + const func = billing.onPlanAutomatedUpdatePublished((event) => event); + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await func({ data: "test" } as any); + expect(hello).to.equal("world"); + }); }); describe("onPlanAutomatedUpdatePublished", () => { @@ -76,6 +87,16 @@ describe("billing", () => { }, }); }); + + it("calls init function", async () => { + const func = billing.onPlanAutomatedUpdatePublished((event) => event); + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await func({ data: "test" } as any); + expect(hello).to.equal("world"); + }); }); describe("onOperation", () => { @@ -119,5 +140,15 @@ describe("billing", () => { expect(res).to.equal("input"); }); + + it("calls init function", async () => { + const func = billing.onOperation(ALERT_TYPE, (event) => event, undefined); + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await func({ data: "test" } as any); + expect(hello).to.equal("world"); + }); }); }); diff --git a/spec/v2/providers/alerts/crashlytics.spec.ts b/spec/v2/providers/alerts/crashlytics.spec.ts index fd4984b76..496f6f10c 100644 --- a/spec/v2/providers/alerts/crashlytics.spec.ts +++ b/spec/v2/providers/alerts/crashlytics.spec.ts @@ -3,6 +3,7 @@ import * as alerts from "../../../../src/v2/providers/alerts"; import * as crashlytics from "../../../../src/v2/providers/alerts/crashlytics"; import { FULL_OPTIONS } from "../fixtures"; import { FULL_ENDPOINT, MINIMAL_V2_ENDPOINT } from "../../../fixtures"; +import { onInit } from "../../../../src/v2/core"; const ALERT_TYPE = "new-alert-type"; const APPID = "123456789"; @@ -104,6 +105,16 @@ describe("crashlytics", () => { }, }); }); + + it("calls init function", async () => { + const func = crashlytics[method](APPID, myHandler); + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await func({ data: "crash" } as any); + expect(hello).to.equal("world"); + }); }); } diff --git a/spec/v2/providers/alerts/performance.spec.ts b/spec/v2/providers/alerts/performance.spec.ts index 667aa7ba5..01004e3f6 100644 --- a/spec/v2/providers/alerts/performance.spec.ts +++ b/spec/v2/providers/alerts/performance.spec.ts @@ -3,6 +3,7 @@ import * as alerts from "../../../../src/v2/providers/alerts"; import * as performance from "../../../../src/v2/providers/alerts/performance"; import { FULL_OPTIONS } from "../fixtures"; import { FULL_ENDPOINT, MINIMAL_V2_ENDPOINT } from "../../../fixtures"; +import { CloudEvent, onInit } from "../../../../src/v2/core"; const APPID = "123456789"; const myHandler = () => 42; @@ -45,6 +46,23 @@ describe("performance", () => { retry: false, }, }); + + it("calls init function", async () => { + const event: CloudEvent = { + specversion: "1.0", + id: "id", + source: "source", + type: "type", + time: "now", + data: "data", + }; + + let hello: string; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await performance.onThresholdAlertPublished(() => null)(event); + expect(hello).to.equal("world"); + }); }); it("should create a function with appid in opts", () => { diff --git a/spec/v2/providers/database.spec.ts b/spec/v2/providers/database.spec.ts index acd87644d..c5e16f747 100644 --- a/spec/v2/providers/database.spec.ts +++ b/spec/v2/providers/database.spec.ts @@ -25,6 +25,7 @@ import { PathPattern } from "../../../src/common/utilities/path-pattern"; import * as database from "../../../src/v2/providers/database"; import { expectType } from "../../common/metaprogramming"; import { MINIMAL_V2_ENDPOINT } from "../../fixtures"; +import { CloudEvent, onInit } from "../../../src/v2/core"; const RAW_RTDB_EVENT: database.RawRTDBCloudEvent = { data: { @@ -409,6 +410,23 @@ describe("database", () => { }, }); }); + + it("calls init function", async () => { + const event: CloudEvent = { + specversion: "1.0", + id: "id", + source: "source", + type: "type", + time: "now", + data: "data", + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await database.onValueWritten("path", () => null)(event); + expect(hello).to.equal("world"); + }); }); describe("onValueCreated", () => { @@ -469,6 +487,23 @@ describe("database", () => { }, }); }); + + it("calls init function", async () => { + const event: CloudEvent = { + specversion: "1.0", + id: "id", + source: "source", + type: "type", + time: "now", + data: "data", + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await database.onValueCreated("path", () => null)(event); + expect(hello).to.equal("world"); + }); }); describe("onValueUpdated", () => { @@ -526,6 +561,23 @@ describe("database", () => { }, }); }); + + it("calls init function", async () => { + const event: CloudEvent = { + specversion: "1.0", + id: "id", + source: "source", + type: "type", + time: "now", + data: "data", + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await database.onValueUpdated("path", () => null)(event); + expect(hello).to.equal("world"); + }); }); describe("onValueDeleted", () => { @@ -583,5 +635,22 @@ describe("database", () => { }, }); }); + + it("calls init function", async () => { + const event: CloudEvent = { + specversion: "1.0", + id: "id", + source: "source", + type: "type", + time: "now", + data: "data", + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await database.onValueDeleted("path", () => null)(event); + expect(hello).to.equal("world"); + }); }); }); diff --git a/spec/v2/providers/eventarc.spec.ts b/spec/v2/providers/eventarc.spec.ts index feb42b458..28696319a 100644 --- a/spec/v2/providers/eventarc.spec.ts +++ b/spec/v2/providers/eventarc.spec.ts @@ -25,6 +25,7 @@ import * as options from "../../../src/v2/options"; import * as eventarc from "../../../src/v2/providers/eventarc"; import { FULL_OPTIONS } from "./fixtures"; import { FULL_ENDPOINT, MINIMAL_V2_ENDPOINT } from "../../fixtures"; +import { CloudEvent, onInit } from "../../../src/v2/core"; const ENDPOINT_EVENT_TRIGGER = { eventType: "event-type", @@ -149,5 +150,22 @@ describe("v2/eventarc", () => { }, }); }); + + it("calls init function", async () => { + const event: CloudEvent = { + specversion: "1.0", + id: "id", + source: "source", + type: "type", + time: "now", + data: "data", + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await eventarc.onCustomEventPublished("type", () => null)(event); + expect(hello).to.equal("world"); + }); }); }); diff --git a/spec/v2/providers/firestore.spec.ts b/spec/v2/providers/firestore.spec.ts index 555b2fd3b..e5406bdb3 100644 --- a/spec/v2/providers/firestore.spec.ts +++ b/spec/v2/providers/firestore.spec.ts @@ -25,6 +25,8 @@ import { google } from "../../../protos/compiledFirestore"; import { Timestamp } from "firebase-admin/firestore"; import * as firestore from "../../../src/v2/providers/firestore"; import { PathPattern } from "../../../src/common/utilities/path-pattern"; +import { onInit } from "../../../src/v2/core"; +import * as params from "../../../src/params"; /** static-complied protobuf */ const DocumentEventData = google.events.cloud.firestore.v1.DocumentEventData; @@ -39,9 +41,9 @@ const eventBase = { dataschema: "https://github.com/googleapis/google-cloudevents/blob/main/proto/google/events/cloud/firestore/v1/data.proto", id: "379ad868-5ef9-4c84-a8ba-f75f1b056663", - source: "//firestore.googleapis.com/projects/my-project/databases/my-db", + source: "projects/my-project/databases/my-db/documents/d", subject: "documents/foo/fGRodw71mHutZ4wGDuT8", - specversion: "1.0", + specversion: "1.0" as const, time: "2023-03-10T18:20:43.677647Z", type: "google.cloud.firestore.document.v1.created", }; @@ -82,6 +84,15 @@ function makeEvent(data?: any): firestore.RawFirestoreEvent { } as firestore.RawFirestoreEvent; } +function makeAuthEvent(data?: any): firestore.RawFirestoreAuthEvent { + return { + ...eventBase, + data, + authid: "userId", + authtype: "unknown", + } as firestore.RawFirestoreAuthEvent; +} + const createdData = { value: { fields: { @@ -147,6 +158,20 @@ const writtenData = { const writtenProto = DocumentEventData.create(writtenData); describe("firestore", () => { + let docParam: params.Expression; + let nsParam: params.Expression; + let dbParam: params.Expression; + + before(() => { + docParam = params.defineString("DOCUMENT"); + nsParam = params.defineString("NAMESPACE"); + dbParam = params.defineString("DATABASE"); + }); + + after(() => { + params.clearParams(); + }); + describe("onDocumentWritten", () => { it("should create a func", () => { const expectedEp = makeExpectedEp( @@ -192,6 +217,46 @@ describe("firestore", () => { expect(func.run(true as any)).to.eq(2); expect(func.__endpoint).to.deep.eq(expectedEp); }); + + it("should create a func with param opts", () => { + const expectedEp = makeExpectedEp( + firestore.writtenEventType, + { + database: dbParam, + namespace: nsParam, + }, + { + document: docParam, + } + ); + + const func = firestore.onDocumentWritten( + { + database: dbParam, + namespace: nsParam, + document: docParam, + }, + () => true + ); + expect(func.__endpoint).to.deep.eq(expectedEp); + }); + + it("calls init function", async () => { + const event: firestore.RawFirestoreEvent = { + ...eventBase, + datacontenttype: "application/json", + data: { + oldValue: null, + value: null, + }, + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await firestore.onDocumentWritten("path", () => null)(event); + expect(hello).to.equal("world"); + }); }); describe("onDocumentCreated", () => { @@ -239,6 +304,46 @@ describe("firestore", () => { expect(func.run(true as any)).to.eq(2); expect(func.__endpoint).to.deep.eq(expectedEp); }); + + it("should create a func with param opts", () => { + const expectedEp = makeExpectedEp( + firestore.createdEventType, + { + database: dbParam, + namespace: nsParam, + }, + { + document: docParam, + } + ); + + const func = firestore.onDocumentCreated( + { + database: dbParam, + namespace: nsParam, + document: docParam, + }, + () => true + ); + expect(func.__endpoint).to.deep.eq(expectedEp); + }); + + it("calls init function", async () => { + const event: firestore.RawFirestoreEvent = { + ...eventBase, + datacontenttype: "application/json", + data: { + oldValue: null, + value: null, + }, + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await firestore.onDocumentCreated("type", () => null)(event); + expect(hello).to.equal("world"); + }); }); describe("onDocumentUpdated", () => { @@ -286,6 +391,46 @@ describe("firestore", () => { expect(func.run(true as any)).to.eq(2); expect(func.__endpoint).to.deep.eq(expectedEp); }); + + it("should create a func with param opts", () => { + const expectedEp = makeExpectedEp( + firestore.updatedEventType, + { + database: dbParam, + namespace: nsParam, + }, + { + document: docParam, + } + ); + + const func = firestore.onDocumentUpdated( + { + database: dbParam, + namespace: nsParam, + document: docParam, + }, + () => true + ); + expect(func.__endpoint).to.deep.eq(expectedEp); + }); + + it("calls init function", async () => { + const event: firestore.RawFirestoreEvent = { + ...eventBase, + datacontenttype: "application/json", + data: { + oldValue: null, + value: null, + }, + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await firestore.onDocumentUpdated("path", () => null)(event); + expect(hello).to.equal("world"); + }); }); describe("onDocumentDeleted", () => { @@ -333,6 +478,302 @@ describe("firestore", () => { expect(func.run(true as any)).to.eq(2); expect(func.__endpoint).to.deep.eq(expectedEp); }); + + it("should create a func with param opts", () => { + const expectedEp = makeExpectedEp( + firestore.deletedEventType, + { + database: dbParam, + namespace: nsParam, + }, + { + document: docParam, + } + ); + + const func = firestore.onDocumentDeleted( + { + database: dbParam, + namespace: nsParam, + document: docParam, + }, + () => true + ); + expect(func.__endpoint).to.deep.eq(expectedEp); + }); + + it("calls init function", async () => { + const event: firestore.RawFirestoreEvent = { + ...eventBase, + datacontenttype: "application/json", + data: { + oldValue: null, + value: null, + }, + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await firestore.onDocumentDeleted("path", () => null)(event); + expect(hello).to.equal("world"); + }); + }); + + describe("onDocumentWrittenWithAuthContext", () => { + it("should create a func", () => { + const expectedEp = makeExpectedEp( + firestore.writtenEventWithAuthContextType, + { + database: "(default)", + namespace: "(default)", + }, + { + document: "foo/{bar}", + } + ); + + const func = firestore.onDocumentWrittenWithAuthContext("foo/{bar}", () => 2); + + expect(func.run(true as any)).to.eq(2); + expect(func.__endpoint).to.deep.eq(expectedEp); + }); + + it("should create a func with opts", () => { + const expectedEp = makeExpectedEp( + firestore.writtenEventWithAuthContextType, + { + database: "my-db", + namespace: "my-ns", + }, + { + document: "foo/{bar}", + } + ); + expectedEp["region"] = ["us-central1"]; + + const func = firestore.onDocumentWrittenWithAuthContext( + { + region: "us-central1", + document: "foo/{bar}", + database: "my-db", + namespace: "my-ns", + }, + () => 2 + ); + + expect(func.run(true as any)).to.eq(2); + expect(func.__endpoint).to.deep.eq(expectedEp); + }); + + it("calls init function", async () => { + const event: firestore.RawFirestoreEvent = { + ...eventBase, + datacontenttype: "application/json", + data: { + oldValue: null, + value: null, + }, + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await firestore.onDocumentWrittenWithAuthContext("path", () => null)(event); + expect(hello).to.equal("world"); + }); + }); + + describe("onDocumentCreatedWithAuthContext", () => { + it("should create a func", () => { + const expectedEp = makeExpectedEp( + firestore.createdEventWithAuthContextType, + { + database: "(default)", + namespace: "(default)", + }, + { + document: "foo/{bar}", + } + ); + + const func = firestore.onDocumentCreatedWithAuthContext("foo/{bar}", () => 2); + + expect(func.run(true as any)).to.eq(2); + expect(func.__endpoint).to.deep.eq(expectedEp); + }); + + it("should create a func with opts", () => { + const expectedEp = makeExpectedEp( + firestore.createdEventWithAuthContextType, + { + database: "my-db", + namespace: "my-ns", + }, + { + document: "foo/{bar}", + } + ); + expectedEp["region"] = ["us-central1"]; + + const func = firestore.onDocumentCreatedWithAuthContext( + { + region: "us-central1", + document: "foo/{bar}", + database: "my-db", + namespace: "my-ns", + }, + () => 2 + ); + + expect(func.run(true as any)).to.eq(2); + expect(func.__endpoint).to.deep.eq(expectedEp); + }); + + it("calls init function", async () => { + const event: firestore.RawFirestoreEvent = { + ...eventBase, + datacontenttype: "application/json", + data: { + oldValue: null, + value: null, + }, + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await firestore.onDocumentCreatedWithAuthContext("path", () => null)(event); + expect(hello).to.equal("world"); + }); + }); + + describe("onDocumentUpdatedWithAuthContext", () => { + it("should create a func", () => { + const expectedEp = makeExpectedEp( + firestore.updatedEventWithAuthContextType, + { + database: "(default)", + namespace: "(default)", + }, + { + document: "foo/{bar}", + } + ); + + const func = firestore.onDocumentUpdatedWithAuthContext("foo/{bar}", () => 2); + + expect(func.run(true as any)).to.eq(2); + expect(func.__endpoint).to.deep.eq(expectedEp); + }); + + it("should create a func with opts", () => { + const expectedEp = makeExpectedEp( + firestore.updatedEventWithAuthContextType, + { + database: "my-db", + namespace: "my-ns", + }, + { + document: "foo/{bar}", + } + ); + expectedEp["region"] = ["us-central1"]; + + const func = firestore.onDocumentUpdatedWithAuthContext( + { + region: "us-central1", + document: "foo/{bar}", + database: "my-db", + namespace: "my-ns", + }, + () => 2 + ); + + expect(func.run(true as any)).to.eq(2); + expect(func.__endpoint).to.deep.eq(expectedEp); + }); + + it("calls init function", async () => { + const event: firestore.RawFirestoreEvent = { + ...eventBase, + datacontenttype: "application/json", + data: { + oldValue: null, + value: null, + }, + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await firestore.onDocumentUpdatedWithAuthContext("path", () => null)(event); + expect(hello).to.equal("world"); + }); + }); + + describe("onDocumentDeletedWithAuthContext", () => { + it("should create a func", () => { + const expectedEp = makeExpectedEp( + firestore.deletedEventWithAuthContextType, + { + database: "(default)", + namespace: "(default)", + }, + { + document: "foo/{bar}", + } + ); + + const func = firestore.onDocumentDeletedWithAuthContext("foo/{bar}", () => 2); + + expect(func.run(true as any)).to.eq(2); + expect(func.__endpoint).to.deep.eq(expectedEp); + }); + + it("should create a func with opts", () => { + const expectedEp = makeExpectedEp( + firestore.deletedEventWithAuthContextType, + { + database: "my-db", + namespace: "my-ns", + }, + { + document: "foo/{bar}", + } + ); + expectedEp["region"] = ["us-central1"]; + + const func = firestore.onDocumentDeletedWithAuthContext( + { + region: "us-central1", + document: "foo/{bar}", + database: "my-db", + namespace: "my-ns", + }, + () => 2 + ); + + expect(func.run(true as any)).to.eq(2); + expect(func.__endpoint).to.deep.eq(expectedEp); + }); + + it("calls init function", async () => { + const event: firestore.RawFirestoreEvent = { + ...eventBase, + datacontenttype: "application/json", + data: { + oldValue: null, + value: null, + }, + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await firestore.onDocumentDeletedWithAuthContext("path", () => null)(event); + expect(hello).to.equal("world"); + }); }); describe("getOpts", () => { @@ -544,6 +985,26 @@ describe("firestore", () => { expect(event.data.data()).to.deep.eq({ hello: "delete world" }); }); + + it("should make event from a created event with auth context", () => { + const event = firestore.makeFirestoreEvent( + firestore.createdEventWithAuthContextType, + makeAuthEvent(makeEncodedProtobuf(createdProto)), + firestore.makeParams("foo/fGRodw71mHutZ4wGDuT8", new PathPattern("foo/{bar}")) + ); + + expect(event.data.data()).to.deep.eq({ hello: "create world" }); + }); + + it("should include auth fields if provided in raw event", () => { + const event = firestore.makeFirestoreEvent( + firestore.createdEventWithAuthContextType, + makeAuthEvent(makeEncodedProtobuf(createdProto)), + firestore.makeParams("foo/fGRodw71mHutZ4wGDuT8", new PathPattern("foo/{bar}")) + ); + + expect(event).to.include({ authId: "userId", authType: "unknown" }); + }); }); describe("makeChangedFirestoreEvent", () => { @@ -577,6 +1038,15 @@ describe("firestore", () => { }); }); + it("should include auth fields if provided in raw event", () => { + const event = firestore.makeChangedFirestoreEvent( + makeAuthEvent(makeEncodedProtobuf(writtenProto)), + firestore.makeParams("foo/fGRodw71mHutZ4wGDuT8", new PathPattern("foo/{bar}")) + ); + + expect(event).to.include({ authId: "userId", authType: "unknown" }); + }); + describe("makeEndpoint", () => { it("should make an endpoint with a document path pattern", () => { const expectedEp = makeExpectedEp( @@ -594,7 +1064,7 @@ describe("firestore", () => { const ep = firestore.makeEndpoint( firestore.createdEventType, { region: "us-central1" }, - new PathPattern("foo/{bar}"), + "foo/{bar}", "my-db", "my-ns" ); @@ -617,7 +1087,7 @@ describe("firestore", () => { const ep = firestore.makeEndpoint( firestore.createdEventType, { region: "us-central1" }, - new PathPattern("foo/fGRodw71mHutZ4wGDuT8"), + "foo/fGRodw71mHutZ4wGDuT8", "my-db", "my-ns" ); diff --git a/spec/v2/providers/https.spec.ts b/spec/v2/providers/https.spec.ts index 64a37c7eb..643044338 100644 --- a/spec/v2/providers/https.spec.ts +++ b/spec/v2/providers/https.spec.ts @@ -29,6 +29,7 @@ import * as https from "../../../src/v2/providers/https"; import { expectedResponseHeaders, MockRequest } from "../../fixtures/mockrequest"; import { runHandler } from "../../helper"; import { FULL_ENDPOINT, MINIMAL_V2_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from "./fixtures"; +import { onInit } from "../../../src/v2/core"; describe("onRequest", () => { beforeEach(() => { @@ -270,6 +271,28 @@ describe("onRequest", () => { sinon.restore(); }); + + it("calls init function", async () => { + const func = https.onRequest((req, res) => { + res.status(200).send("Good"); + }); + const req = new MockRequest( + { + data: {}, + }, + { + "Access-Control-Request-Method": "POST", + "Access-Control-Request-Headers": "origin", + origin: "example.com", + } + ); + req.method = "OPTIONS"; + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await runHandler(func, req as any); + expect(hello).to.equal("world"); + }); }); describe("onCall", () => { @@ -363,7 +386,7 @@ describe("onCall", () => { }); }); - it("has a .run method", () => { + it("has a .run method", async () => { const cf = https.onCall((request) => { return request; }); @@ -376,7 +399,7 @@ describe("onCall", () => { token: "token", }, }; - expect(cf.run(request)).to.deep.equal(request); + await expect(cf.run(request)).to.eventually.deep.equal(request); }); it("should be an express handler", async () => { @@ -487,4 +510,25 @@ describe("onCall", () => { https.onCall((request: https.CallableRequest) => `Hello, ${request.data}`); https.onCall((request: https.CallableRequest) => `Hello, ${request.data}`); }); + + it("calls init function", async () => { + const func = https.onCall(() => 42); + + const req = new MockRequest( + { + data: {}, + }, + { + "content-type": "application/json", + origin: "example.com", + } + ); + req.method = "POST"; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await runHandler(func, req as any); + expect(hello).to.equal("world"); + }); }); diff --git a/spec/v2/providers/identity.spec.ts b/spec/v2/providers/identity.spec.ts index 7559a4133..bcd416d29 100644 --- a/spec/v2/providers/identity.spec.ts +++ b/spec/v2/providers/identity.spec.ts @@ -22,6 +22,9 @@ import { expect } from "chai"; import * as identity from "../../../src/v2/providers/identity"; import { MINIMAL_V2_ENDPOINT } from "../../fixtures"; +import { onInit } from "../../../src/v2/core"; +import { MockRequest } from "../../fixtures/mockrequest"; +import { runHandler } from "../../helper"; const BEFORE_CREATE_TRIGGER = { eventType: "providers/cloud.auth/eventTypes/user.beforeCreate", @@ -91,6 +94,27 @@ describe("identity", () => { }, ]); }); + + it("calls init function", async () => { + const func = identity.beforeUserCreated(() => null); + + const req = new MockRequest( + { + data: {}, + }, + { + "content-type": "application/json", + origin: "example.com", + } + ); + req.method = "POST"; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await runHandler(func, req as any); + expect(hello).to.equal("world"); + }); }); describe("beforeUserSignedIn", () => { @@ -135,6 +159,27 @@ describe("identity", () => { }, ]); }); + + it("calls init function", async () => { + const func = identity.beforeUserSignedIn(() => null); + + const req = new MockRequest( + { + data: {}, + }, + { + "content-type": "application/json", + origin: "example.com", + } + ); + req.method = "POST"; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await runHandler(func, req as any); + expect(hello).to.equal("world"); + }); }); describe("beforeOperation", () => { diff --git a/spec/v2/providers/pubsub.spec.ts b/spec/v2/providers/pubsub.spec.ts index 3b712044e..d498b1b42 100644 --- a/spec/v2/providers/pubsub.spec.ts +++ b/spec/v2/providers/pubsub.spec.ts @@ -134,7 +134,7 @@ describe("onMessagePublished", () => { expect(res).to.equal("input"); }); - it("should parse pubsub messages", () => { + it("should parse pubsub messages", async () => { let json: unknown; const messageJSON = { messageId: "uuid", @@ -161,7 +161,7 @@ describe("onMessagePublished", () => { return event; }); - const eventAgain = func(event); + const eventAgain = await func(event); // Deep equal uses JSON equality, so we'll still match even though // Message is a class and we passed an interface. diff --git a/spec/v2/providers/remoteConfig.spec.ts b/spec/v2/providers/remoteConfig.spec.ts index 5faf907ec..3b32ed111 100644 --- a/spec/v2/providers/remoteConfig.spec.ts +++ b/spec/v2/providers/remoteConfig.spec.ts @@ -24,13 +24,14 @@ import { expect } from "chai"; import * as remoteConfig from "../../../src/v2/providers/remoteConfig"; import * as options from "../../../src/v2/options"; import { MINIMAL_V2_ENDPOINT } from "../../fixtures"; +import { CloudEvent, onInit } from "../../../src/v2/core"; describe("onConfigUpdated", () => { afterEach(() => { options.setGlobalOptions({}); }); - it("should create a function with a handler", () => { + it("should create a function with a handler", async () => { const fn = remoteConfig.onConfigUpdated(() => 2); expect(fn.__endpoint).to.deep.eq({ @@ -43,10 +44,10 @@ describe("onConfigUpdated", () => { retry: false, }, }); - expect(fn.run(1 as any)).to.eq(2); + await expect(fn(1 as any)).to.eventually.eq(2); }); - it("should create a function with opts and a handler", () => { + it("should create a function with opts and a handler", async () => { options.setGlobalOptions({ memory: "512MiB", region: "us-west1", @@ -72,6 +73,23 @@ describe("onConfigUpdated", () => { retry: true, }, }); - expect(fn.run(1 as any)).to.eq(2); + await expect(fn(1 as any)).to.eventually.eq(2); + }); + + it("calls init function", async () => { + const event: CloudEvent = { + specversion: "1.0", + id: "id", + source: "source", + type: "type", + time: "now", + data: "data", + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await remoteConfig.onConfigUpdated(() => null)(event); + expect(hello).to.equal("world"); }); }); diff --git a/spec/v2/providers/scheduler.spec.ts b/spec/v2/providers/scheduler.spec.ts index 4f3e6f984..fcd03cf1f 100644 --- a/spec/v2/providers/scheduler.spec.ts +++ b/spec/v2/providers/scheduler.spec.ts @@ -25,6 +25,9 @@ import { ManifestEndpoint } from "../../../src/runtime/manifest"; import * as options from "../../../src/v2/options"; import * as schedule from "../../../src/v2/providers/scheduler"; import { MINIMAL_V2_ENDPOINT } from "../../fixtures"; +import { onInit } from "../../../src/v2/core"; +import { MockRequest } from "../../fixtures/mockrequest"; +import { runHandler } from "../../helper"; const MINIMAL_SCHEDULE_TRIGGER: ManifestEndpoint["scheduleTrigger"] = { schedule: "", @@ -187,5 +190,26 @@ describe("schedule", () => { foo: "newBar", }); }); + + it("calls init function", async () => { + const func = schedule.onSchedule("* * * * *", () => null); + + const req = new MockRequest( + { + data: {}, + }, + { + "content-type": "application/json", + origin: "example.com", + } + ); + req.method = "POST"; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await runHandler(func, req as any); + expect(hello).to.equal("world"); + }); }); }); diff --git a/spec/v2/providers/storage.spec.ts b/spec/v2/providers/storage.spec.ts index d5a699d70..06324e9ab 100644 --- a/spec/v2/providers/storage.spec.ts +++ b/spec/v2/providers/storage.spec.ts @@ -3,6 +3,7 @@ import * as config from "../../../src/common/config"; import * as options from "../../../src/v2/options"; import * as storage from "../../../src/v2/providers/storage"; import { FULL_ENDPOINT, MINIMAL_V2_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from "./fixtures"; +import { CloudEvent, onInit } from "../../../src/v2/core"; const EVENT_TRIGGER = { eventType: "event-type", @@ -312,6 +313,23 @@ describe("v2/storage", () => { region: ["us-west1"], }); }); + + it("calls init function", async () => { + const event: CloudEvent = { + specversion: "1.0", + id: "id", + source: "source", + type: "type", + time: "now", + data: "data", + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await storage.onObjectArchived("bucket", () => null)(event); + expect(hello).to.equal("world"); + }); }); describe("onObjectFinalized", () => { @@ -429,6 +447,23 @@ describe("v2/storage", () => { region: ["us-west1"], }); }); + + it("calls init function", async () => { + const event: CloudEvent = { + specversion: "1.0", + id: "id", + source: "source", + type: "type", + time: "now", + data: "data", + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await storage.onObjectFinalized("bucket", () => null)(event); + expect(hello).to.equal("world"); + }); }); describe("onObjectDeleted", () => { @@ -543,6 +578,23 @@ describe("v2/storage", () => { region: ["us-west1"], }); }); + + it("calls init function", async () => { + const event: CloudEvent = { + specversion: "1.0", + id: "id", + source: "source", + type: "type", + time: "now", + data: "data", + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await storage.onObjectDeleted("bucket", () => null)(event); + expect(hello).to.equal("world"); + }); }); describe("onObjectMetadataUpdated", () => { @@ -661,5 +713,22 @@ describe("v2/storage", () => { region: ["us-west1"], }); }); + + it("calls init function", async () => { + const event: CloudEvent = { + specversion: "1.0", + id: "id", + source: "source", + type: "type", + time: "now", + data: "data", + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await storage.onObjectMetadataUpdated("bucket", () => null)(event); + expect(hello).to.equal("world"); + }); }); }); diff --git a/spec/v2/providers/tasks.spec.ts b/spec/v2/providers/tasks.spec.ts index 1597a1947..46ffd7a0a 100644 --- a/spec/v2/providers/tasks.spec.ts +++ b/spec/v2/providers/tasks.spec.ts @@ -28,6 +28,7 @@ import { onTaskDispatched, Request } from "../../../src/v2/providers/tasks"; import { MockRequest } from "../../fixtures/mockrequest"; import { runHandler } from "../../helper"; import { FULL_ENDPOINT, MINIMAL_V2_ENDPOINT, FULL_OPTIONS, FULL_TRIGGER } from "./fixtures"; +import { onInit } from "../../../src/v2/core"; const MINIMIAL_TASK_QUEUE_TRIGGER: ManifestEndpoint["taskQueueTrigger"] = { rateLimits: { @@ -299,4 +300,25 @@ describe("onTaskDispatched", () => { console.log(`Hello, ${request.data}`); }); }); + + it("calls init function", async () => { + const func = onTaskDispatched(() => null); + + const req = new MockRequest( + { + data: {}, + }, + { + "content-type": "application/json", + origin: "example.com", + } + ); + req.method = "POST"; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await runHandler(func, req as any); + expect(hello).to.equal("world"); + }); }); diff --git a/spec/v2/providers/testLab.spec.ts b/spec/v2/providers/testLab.spec.ts index 15ff77248..15d649d44 100644 --- a/spec/v2/providers/testLab.spec.ts +++ b/spec/v2/providers/testLab.spec.ts @@ -24,6 +24,7 @@ import { expect } from "chai"; import * as testLab from "../../../src/v2/providers/testLab"; import * as options from "../../../src/v2/options"; import { MINIMAL_V2_ENDPOINT } from "../../fixtures"; +import { CloudEvent, onInit } from "../../../src/v2/core"; describe("onTestMatrixCompleted", () => { afterEach(() => { @@ -74,4 +75,21 @@ describe("onTestMatrixCompleted", () => { }); expect(fn.run(1 as any)).to.eq(2); }); + + it("calls init function", async () => { + const event: CloudEvent = { + specversion: "1.0", + id: "id", + source: "source", + type: "type", + time: "now", + data: "data", + }; + + let hello; + onInit(() => (hello = "world")); + expect(hello).to.be.undefined; + await testLab.onTestMatrixCompleted(() => null)(event); + expect(hello).to.equal("world"); + }); }); diff --git a/src/common/onInit.ts b/src/common/onInit.ts new file mode 100644 index 000000000..e1b32ca64 --- /dev/null +++ b/src/common/onInit.ts @@ -0,0 +1,39 @@ +import * as logger from "../logger"; + +let initCallback: (() => unknown) | null = null; +let didInit = false; + +/** + * Registers a callback that should be run when in a production environment + * before executing any functions code. + * Calling this function more than once leads to undefined behavior. + * @param callback initialization callback to be run before any function executes. + */ +export function onInit(callback: () => unknown) { + if (initCallback) { + logger.warn( + "Setting onInit callback more than once. Only the most recent callback will be called" + ); + } + initCallback = callback; + didInit = false; +} + +type Resolved = T extends Promise ? V : T; + +/** @internal */ +export function withInit unknown>(func: T) { + return async (...args: Parameters): Promise>> => { + if (!didInit) { + if (initCallback) { + await initCallback(); + } + didInit = true; + } + + // Note: This cast is actually inaccurate because it may be a promise, but + // it doesn't actually matter because the async function will promisify + // non-promises and forward promises. + return func(...args) as Resolved>; + }; +} diff --git a/src/common/params.ts b/src/common/params.ts index a35a870b3..e0b0b8537 100644 --- a/src/common/params.ts +++ b/src/common/params.ts @@ -20,6 +20,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +import { Expression } from "../params"; + /** * A type that splits literal string S with delimiter D. * @@ -78,10 +80,17 @@ export type Extract = Part extends `{${infer Param}=**}` * * For flexibility reasons, ParamsOf is Record */ -export type ParamsOf = +export type ParamsOf> = // if we have lost type information, revert back to an untyped dictionary - string extends PathPattern + PathPattern extends Expression + ? Record + : string extends PathPattern ? Record : { - [Key in Extract, "/">[number]>]: string; + // N.B. I'm not sure why PathPattern isn't detected to not be an + // Expression per the check above. Since we have the check above + // The Exclude call should be safe. + [Key in Extract< + Split>>, "/">[number] + >]: string; }; diff --git a/src/common/providers/database.ts b/src/common/providers/database.ts index 33250f4c0..d73bb5503 100644 --- a/src/common/providers/database.ts +++ b/src/common/providers/database.ts @@ -25,6 +25,14 @@ import * as database from "firebase-admin/database"; import { firebaseConfig } from "../../common/config"; import { joinPath, pathParts } from "../../common/utilities/path"; +/** + * Pulled from @firebase/database-types, make sure the interface is updated on dependencies upgrades. + * Represents a child snapshot of a `Reference` that is being iterated over. The key will never be undefined. + */ +interface IteratedDataSnapshot extends DataSnapshot { + key: string; // key of the location of this snapshot. +} + /** * Interface representing a Firebase Realtime database data snapshot. */ @@ -120,6 +128,9 @@ export class DataSnapshot implements database.DataSnapshot { let source = this._data; if (parts.length) { for (const part of parts) { + if (source[part] === undefined) { + return null; + } source = source[part]; } } @@ -204,7 +215,7 @@ export class DataSnapshot implements database.DataSnapshot { * @return `true` if enumeration was canceled due to your callback * returning `true`. */ - forEach(action: (a: DataSnapshot) => boolean | void): boolean { + forEach(action: (a: IteratedDataSnapshot) => boolean | void): boolean { const val = this.val() || {}; if (typeof val === "object") { return Object.keys(val).some((key) => action(this.child(key)) === true); diff --git a/src/common/providers/firestore.ts b/src/common/providers/firestore.ts index 0f2e8b621..8a74b588a 100644 --- a/src/common/providers/firestore.ts +++ b/src/common/providers/firestore.ts @@ -52,9 +52,9 @@ function _getValueProto(data: any, resource: string, valueFieldName: string) { } /** @internal */ -export function createSnapshotFromProtobuf(data: Uint8Array, path: string) { +export function createSnapshotFromProtobuf(data: Uint8Array, path: string, databaseId: string) { if (!firestoreInstance) { - firestoreInstance = firestore.getFirestore(getApp()); + firestoreInstance = firestore.getFirestore(getApp(), databaseId); } try { const dataBuffer = Buffer.from(data); @@ -68,9 +68,13 @@ export function createSnapshotFromProtobuf(data: Uint8Array, path: string) { } /** @internal */ -export function createBeforeSnapshotFromProtobuf(data: Uint8Array, path: string) { +export function createBeforeSnapshotFromProtobuf( + data: Uint8Array, + path: string, + databaseId: string +) { if (!firestoreInstance) { - firestoreInstance = firestore.getFirestore(getApp()); + firestoreInstance = firestore.getFirestore(getApp(), databaseId); } try { const dataBuffer = Buffer.from(data); @@ -88,10 +92,13 @@ export function createSnapshotFromJson( data: any, source: string, createTime: string | undefined, - updateTime: string | undefined + updateTime: string | undefined, + databaseId?: string ) { if (!firestoreInstance) { - firestoreInstance = firestore.getFirestore(getApp()); + firestoreInstance = databaseId + ? firestore.getFirestore(getApp(), databaseId) + : firestore.getFirestore(getApp()); } const valueProto = _getValueProto(data, source, "value"); let timeString = createTime || updateTime; @@ -110,11 +117,15 @@ export function createBeforeSnapshotFromJson( data: any, source: string, createTime: string | undefined, - updateTime: string | undefined + updateTime: string | undefined, + databaseId?: string ) { if (!firestoreInstance) { - firestoreInstance = firestore.getFirestore(getApp()); + firestoreInstance = databaseId + ? firestore.getFirestore(getApp(), databaseId) + : firestore.getFirestore(getApp()); } + const oldValueProto = _getValueProto(data, source, "oldValue"); const oldReadTime = dateToTimestampProto(createTime || updateTime); return firestoreInstance.snapshot_(oldValueProto, oldReadTime, "json"); diff --git a/src/common/providers/identity.ts b/src/common/providers/identity.ts index 0368c08ad..e1fa3a196 100644 --- a/src/common/providers/identity.ts +++ b/src/common/providers/identity.ts @@ -69,18 +69,19 @@ const EVENT_MAPPING: Record = { }; /** - * The UserRecord passed to Cloud Functions is the same UserRecord that is returned by the Firebase Admin - * SDK. + * The `UserRecord` passed to Cloud Functions is the same + * {@link https://firebase.google.com/docs/reference/admin/node/firebase-admin.auth.userrecord | UserRecord} + * that is returned by the Firebase Admin SDK. */ export type UserRecord = auth.UserRecord; /** - * UserInfo that is part of the UserRecord + * `UserInfo` that is part of the `UserRecord`. */ export type UserInfo = auth.UserInfo; /** - * Helper class to create the user metadata in a UserRecord object + * Helper class to create the user metadata in a `UserRecord` object. */ export class UserRecordMetadata implements auth.UserMetadata { constructor(public creationTime: string, public lastSignInTime: string) {} @@ -95,9 +96,9 @@ export class UserRecordMetadata implements auth.UserMetadata { } /** - * Helper function that creates a UserRecord Class from data sent over the wire. + * Helper function that creates a `UserRecord` class from data sent over the wire. * @param wireData data sent over the wire - * @returns an instance of UserRecord with correct toJSON functions + * @returns an instance of `UserRecord` with correct toJSON functions */ export function userRecordConstructor(wireData: Record): UserRecord { // Falsey values from the wire format proto get lost when converted to JSON, this adds them back. @@ -163,7 +164,7 @@ export function userRecordConstructor(wireData: Record): UserRe } /** - * User info that is part of the AuthUserRecord + * User info that is part of the `AuthUserRecord`. */ export interface AuthUserInfo { /** @@ -243,7 +244,7 @@ export interface AuthMultiFactorSettings { } /** - * The UserRecord passed to auth blocking Cloud Functions from the identity platform. + * The `UserRecord` passed to auth blocking functions from the identity platform. */ export interface AuthUserRecord { /** @@ -340,7 +341,7 @@ export interface AuthEventContext extends EventContext { credential?: Credential; } -/** Defines the auth event for v2 blocking events */ +/** Defines the auth event for 2nd gen blocking events */ export interface AuthBlockingEvent extends AuthEventContext { data?: AuthUserRecord; } @@ -362,7 +363,12 @@ export interface BeforeSmsResponse { smsType?: SmsType; } -/** The handler response type for beforeCreate blocking events */ +/** + * The reCAPTCHA action options. + */ +export type RecaptchaActionOptions = "ALLOW" | "BLOCK"; + +/** The handler response type for `beforeCreate` blocking events */ export interface BeforeCreateResponse { displayName?: string; disabled?: boolean; @@ -372,7 +378,7 @@ export interface BeforeCreateResponse { recaptchaActionOverride?: RecaptchaActionOptions; } -/** The handler response type for beforeSignIn blocking events */ +/** The handler response type for `beforeSignIn` blocking events */ export interface BeforeSignInResponse extends BeforeCreateResponse { sessionClaims?: object; } @@ -498,7 +504,7 @@ type HandlerV2 = ( | Promise; /** - * Checks for a valid identity platform web request, otherwise throws an HttpsError + * Checks for a valid identity platform web request, otherwise throws an HttpsError. * @internal */ export function isValidRequest(req: express.Request): boolean { @@ -534,15 +540,15 @@ function unsafeDecodeAuthBlockingToken(token: string): DecodedPayload { } /** - * Helper function to parse the decoded metadata object into a UserMetaData object + * Helper function to parse the decoded metadata object into a `UserMetaData` object * @internal */ export function parseMetadata(metadata: DecodedPayloadUserRecordMetadata): AuthUserMetadata { const creationTime = metadata?.creation_time - ? new Date(metadata.creation_time * 1000).toUTCString() + ? new Date(metadata.creation_time).toUTCString() : null; const lastSignInTime = metadata?.last_sign_in_time - ? new Date(metadata.last_sign_in_time * 1000).toUTCString() + ? new Date(metadata.last_sign_in_time).toUTCString() : null; return { creationTime, @@ -551,7 +557,7 @@ export function parseMetadata(metadata: DecodedPayloadUserRecordMetadata): AuthU } /** - * Helper function to parse the decoded user info array into an AuthUserInfo array + * Helper function to parse the decoded user info array into an `AuthUserInfo` array. * @internal */ export function parseProviderData( @@ -572,7 +578,7 @@ export function parseProviderData( } /** - * Helper function to parse the date into a UTC string + * Helper function to parse the date into a UTC string. * @internal */ export function parseDate(tokensValidAfterTime?: number): string | null { @@ -665,7 +671,7 @@ export function parseAuthUserRecord( }; } -/** Helper to get the AdditionalUserInfo from the decoded jwt */ +/** Helper to get the `AdditionalUserInfo` from the decoded JWT */ function parseAdditionalUserInfo(decodedJWT: DecodedPayload): AdditionalUserInfo { let profile; let username; @@ -723,7 +729,7 @@ export function generateResponsePayload( return result; } -/** Helper to get the Credential from the decoded jwt */ +/** Helper to get the Credential from the decoded JWT */ function parseAuthCredential(decodedJWT: DecodedPayload, time: number): Credential { if ( !decodedJWT.sign_in_attributes && diff --git a/src/common/providers/tasks.ts b/src/common/providers/tasks.ts index 67ac1794a..4f2e82a78 100644 --- a/src/common/providers/tasks.ts +++ b/src/common/providers/tasks.ts @@ -98,8 +98,8 @@ export interface TaskContext { /** * The "short" name of the task, or, if no name was specified at creation, a unique * system-generated id. - * This is the my-task-id value in the complete task name, ie, task_name = - * projects/my-project-id/locations/my-location/queues/my-queue-id/tasks/my-task-id. + * This is the "my-task-id" value in the complete task name, such as "task_name = + * projects/my-project-id/locations/my-location/queues/my-queue-id/tasks/my-task-id." * Populated via the `X-CloudTasks-TaskName` header. */ id: string; diff --git a/src/params/index.ts b/src/params/index.ts index 550ab9db9..15ae9ebd7 100644 --- a/src/params/index.ts +++ b/src/params/index.ts @@ -38,13 +38,23 @@ import { InternalExpression, } from "./types"; +export { + BUCKET_PICKER, + TextInput, + SelectInput, + SelectOptions, + MultiSelectInput, + select, + multiSelect, +} from "./types"; + export { ParamOptions, Expression }; type SecretOrExpr = Param | SecretParam; export const declaredParams: SecretOrExpr[] = []; /** - * Use a helper to manage the list such that params are uniquely + * Use a helper to manage the list such that parameters are uniquely * registered once only but order is preserved. * @internal */ @@ -66,7 +76,7 @@ export function clearParams() { } /** - * A builtin param that resolves to the default RTDB database URL associated + * A built-in parameter that resolves to the default RTDB database URL associated * with the project, without prompting the deployer. Empty string if none exists. */ export const databaseURL: Param = new InternalExpression( @@ -74,7 +84,7 @@ export const databaseURL: Param = new InternalExpression( (env: NodeJS.ProcessEnv) => JSON.parse(env.FIREBASE_CONFIG)?.databaseURL || "" ); /** - * A builtin param that resolves to the Cloud project ID associated with + * A built-in parameter that resolves to the Cloud project ID associated with * the project, without prompting the deployer. */ export const projectID: Param = new InternalExpression( @@ -82,7 +92,7 @@ export const projectID: Param = new InternalExpression( (env: NodeJS.ProcessEnv) => JSON.parse(env.FIREBASE_CONFIG)?.projectId || "" ); /** - * A builtin param that resolves to the Cloud project ID, without prompting + * A built-in parameter that resolves to the Cloud project ID, without prompting * the deployer. */ export const gcloudProject: Param = new InternalExpression( @@ -90,7 +100,7 @@ export const gcloudProject: Param = new InternalExpression( (env: NodeJS.ProcessEnv) => JSON.parse(env.FIREBASE_CONFIG)?.projectId || "" ); /** - * A builtin param that resolves to the Cloud storage bucket associated + * A builtin parameter that resolves to the Cloud storage bucket associated * with the function, without prompting the deployer. Empty string if not * defined. */ @@ -101,12 +111,12 @@ export const storageBucket: Param = new InternalExpression( /** * Declares a secret param, that will persist values only in Cloud Secret Manager. - * Secrets are stored interally as bytestrings. Use ParamOptions.`as` to provide type + * Secrets are stored interally as bytestrings. Use `ParamOptions.as` to provide type * hinting during parameter resolution. * - * @param name The name of the environment variable to use to load the param. - * @param options Configuration options for the param. - * @returns A Param with a `string` return type for `.value`. + * @param name The name of the environment variable to use to load the parameter. + * @param options Configuration options for the parameter. + * @returns A parameter with a `string` return type for `.value`. */ export function defineSecret(name: string): SecretParam { const param = new SecretParam(name); @@ -115,11 +125,11 @@ export function defineSecret(name: string): SecretParam { } /** - * Declare a string param. + * Declare a string parameter. * - * @param name The name of the environment variable to use to load the param. - * @param options Configuration options for the param. - * @returns A Param with a `string` return type for `.value`. + * @param name The name of the environment variable to use to load the parameter. + * @param options Configuration options for the parameter. + * @returns A parameter with a `string` return type for `.value`. */ export function defineString(name: string, options: ParamOptions = {}): StringParam { const param = new StringParam(name, options); @@ -128,11 +138,11 @@ export function defineString(name: string, options: ParamOptions = {}): } /** - * Declare a boolean param. + * Declare a boolean parameter. * - * @param name The name of the environment variable to use to load the param. - * @param options Configuration options for the param. - * @returns A Param with a `boolean` return type for `.value`. + * @param name The name of the environment variable to use to load the parameter. + * @param options Configuration options for the parameter. + * @returns A parameter with a `boolean` return type for `.value`. */ export function defineBoolean(name: string, options: ParamOptions = {}): BooleanParam { const param = new BooleanParam(name, options); @@ -141,11 +151,11 @@ export function defineBoolean(name: string, options: ParamOptions = {}) } /** - * Declare an integer param. + * Declare an integer parameter. * - * @param name The name of the environment variable to use to load the param. - * @param options Configuration options for the param. - * @returns A Param with a `number` return type for `.value`. + * @param name The name of the environment variable to use to load the parameter. + * @param options Configuration options for the parameter. + * @returns A parameter with a `number` return type for `.value`. */ export function defineInt(name: string, options: ParamOptions = {}): IntParam { const param = new IntParam(name, options); @@ -154,11 +164,11 @@ export function defineInt(name: string, options: ParamOptions = {}): Int } /** - * Declare a float param. + * Declare a float parameter. * - * @param name The name of the environment variable to use to load the param. - * @param options Configuration options for the param. - * @returns A Param with a `number` return type for `.value`. + * @param name The name of the environment variable to use to load the parameter. + * @param options Configuration options for the parameter. + * @returns A parameter with a `number` return type for `.value`. * * @internal */ @@ -169,11 +179,11 @@ export function defineFloat(name: string, options: ParamOptions = {}): F } /** - * Declare a list param. + * Declare a list parameter. * - * @param name The name of the environment variable to use to load the param. - * @param options Configuration options for the param. - * @returns A Param with a `string[]` return type for `.value`. + * @param name The name of the environment variable to use to load the parameter. + * @param options Configuration options for the parameter. + * @returns A parameter with a `string[]` return type for `.value`. */ export function defineList(name: string, options: ParamOptions = {}): ListParam { const param = new ListParam(name, options); diff --git a/src/params/types.ts b/src/params/types.ts index 1eaf553f2..42292d767 100644 --- a/src/params/types.ts +++ b/src/params/types.ts @@ -28,7 +28,7 @@ import * as logger from "../logger"; * an Expression as the value of an option that normally accepts numbers. */ export abstract class Expression { - /** Returns the Expression's runtime value, based on the CLI's resolution of params. */ + /** Returns the expression's runtime value, based on the CLI's resolution of parameters. */ value(): T { if (process.env.FUNCTIONS_CONTROL_API === "true") { logger.warn( @@ -47,11 +47,12 @@ export abstract class Expression throw new Error("Not implemented"); } - /** Returns the Expression's representation as a braced CEL expression. */ + /** Returns the expression's representation as a braced CEL expression. */ toCEL(): string { return `{{ ${this.toString()} }}`; } + /** Returns the expression's representation as JSON. */ toJSON(): string { return this.toString(); } @@ -61,8 +62,8 @@ function valueOf(arg: T | Expres return arg instanceof Expression ? arg.runtimeValue() : arg; } /** - * Returns how an entity (either an Expression or a literal value) should be represented in CEL. - * - Expressions delegate to the .toString() method, which is used by the WireManifest + * Returns how an entity (either an `Expression` or a literal value) should be represented in CEL. + * - Expressions delegate to the `.toString()` method, which is used by the WireManifest * - Strings have to be quoted explicitly * - Arrays are represented as []-delimited, parsable JSON * - Numbers and booleans are not quoted explicitly @@ -159,7 +160,7 @@ export class CompareExpression< return `${this.lhs} ${this.cmp} ${rhsStr}`; } - /** Returns a TernaryExpression which can resolve to one of two values, based on the resolution of this comparison. */ + /** Returns a `TernaryExpression` which can resolve to one of two values, based on the resolution of this comparison. */ thenElse( ifTrue: retT | Expression, ifFalse: retT | Expression @@ -171,63 +172,120 @@ export class CompareExpression< /** @hidden */ type ParamValueType = "string" | "list" | "boolean" | "int" | "float" | "secret"; +/** Create a select input from a series of values. */ +export function select(options: T[]): SelectInput; + +/** Create a select input from a map of labels to vaues. */ +export function select(optionsWithLabels: Record): SelectInput; + +/** Create a select input from a series of values or a map of labels to values */ +export function select(options: T[] | Record): SelectInput { + let wireOpts: SelectOptions[]; + if (Array.isArray(options)) { + wireOpts = options.map((opt) => ({ value: opt })); + } else { + wireOpts = Object.entries(options).map(([label, value]) => ({ label, value })); + } + return { + select: { + options: wireOpts, + }, + }; +} + +/** Create a multi-select input from a series of values. */ +export function multiSelect(options: string[]): MultiSelectInput; + +/** Create a multi-select input from map of labels to values. */ +export function multiSelect(options: Record): MultiSelectInput; + +/** Create a multi-select input from a series of values or map of labels to values. */ +export function multiSelect(options: string[] | Record): MultiSelectInput { + let wireOpts: SelectOptions[]; + if (Array.isArray(options)) { + wireOpts = options.map((opt) => ({ value: opt })); + } else { + wireOpts = Object.entries(options).map(([label, value]) => ({ label, value })); + } + return { + multiSelect: { + options: wireOpts, + }, + }; +} + type ParamInput = - | { text: TextInput } - | { select: SelectInput } - | { multiSelect: MultiSelectInput } - | { resource: ResourceInput }; + | TextInput + | SelectInput + | (T extends string[] ? MultiSelectInput : never) + | (T extends string ? ResourceInput : never); /** - * Specifies that a Param's value should be determined by prompting the user - * to type it in interactively at deploy-time. Input that does not match the + * Specifies that a parameter's value should be determined by prompting the user + * to type it in interactively at deploy time. Input that does not match the * provided validationRegex, if present, will be retried. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars export interface TextInput { - example?: string; - /** - * A regular expression (or an escaped string to compile into a regular - * expression) which the prompted text must satisfy; the prompt will retry - * until input matching the regex is provided. - */ - validationRegex?: string | RegExp; - /** - * A custom error message to display when retrying the prompt based on input - * failing to conform to the validationRegex, - */ - validationErrorMessage?: string; + text: { + example?: string; + /** + * A regular expression (or an escaped string to compile into a regular + * expression) which the prompted text must satisfy; the prompt will retry + * until input matching the regex is provided. + */ + validationRegex?: string | RegExp; + /** + * A custom error message to display when retrying the prompt based on input + * failing to conform to the validationRegex, + */ + validationErrorMessage?: string; + }; } /** - * Specifies that a Param's value should be determined by having the user + * Specifies that a parameter's value should be determined by having the user * select from a list containing all the project's resources of a certain * type. Currently, only type:"storage.googleapis.com/Bucket" is supported. */ export interface ResourceInput { resource: { - type: string; + type: "storage.googleapis.com/Bucket"; }; } /** - * Specifies that a Param's value should be determined by having the user select - * from a list of pre-canned options interactively at deploy-time. + * Autogenerate a list of buckets in a project that a user can select from. + */ +export const BUCKET_PICKER: ResourceInput = { + resource: { + type: "storage.googleapis.com/Bucket", + }, +}; + +/** + * Specifies that a parameter's value should be determined by having the user select + * from a list of pre-canned options interactively at deploy time. */ export interface SelectInput { - options: Array>; + select: { + options: Array>; + }; } /** - * Specifies that a Param's value should be determined by having the user select - * a subset from a list of pre-canned options interactively at deploy-time. - * Will result in errors if used on Params of type other than string[]. + * Specifies that a parameter's value should be determined by having the user select + * a subset from a list of pre-canned options interactively at deploy time. + * Will result in errors if used on parameters of type other than `string[]`. */ export interface MultiSelectInput { - options: Array>; + multiSelect: { + options: Array>; + }; } /** - * One of the options provided to a SelectInput, containing a value and + * One of the options provided to a `SelectInput`, containing a value and * optionally a human-readable label to display in the selection interface. */ export interface SelectOptions { @@ -235,19 +293,19 @@ export interface SelectOptions { value: T; } -/** The wire representation of a Param when it's sent to the CLI. A superset of ParamOptions. */ +/** The wire representation of a parameter when it's sent to the CLI. A superset of `ParamOptions`. */ export type ParamSpec = { /** The name of the parameter which will be stored in .env files. Use UPPERCASE. */ name: string; /** An optional default value to be used while prompting for input. Can be a literal or another parametrized expression. */ default?: T | Expression; - /** An optional human-readable string to be used as a replacement for the Param's name when prompting. */ + /** An optional human-readable string to be used as a replacement for the parameter's name when prompting. */ label?: string; - /** An optional long-form description of the Param to be displayed while prompting. */ + /** An optional long-form description of the parameter to be displayed while prompting. */ description?: string; /** @internal */ type: ParamValueType; - /** The way in which the Firebase CLI will prompt for the value of this Param. Defaults to a TextInput. */ + /** The way in which the Firebase CLI will prompt for the value of this parameter. Defaults to a TextInput. */ input?: ParamInput; }; @@ -268,7 +326,7 @@ export type WireParamSpec = { input?: ParamInput; }; -/** Configuration options which can be used to customize the prompting behavior of a Param. */ +/** Configuration options which can be used to customize the prompting behavior of a parameter. */ export type ParamOptions = Omit< ParamSpec, "name" | "type" @@ -291,43 +349,43 @@ export abstract class Param exte throw new Error("Not implemented"); } - /** Returns a parametrized expression of Boolean type, based on comparing the value of this param to a literal or a different expression. */ + /** Returns a parametrized expression of Boolean type, based on comparing the value of this parameter to a literal or a different expression. */ cmp(cmp: "==" | "!=" | ">" | ">=" | "<" | "<=", rhs: T | Expression) { return new CompareExpression(cmp, this, rhs); } - /** Returns a parametrized expression of Boolean type, based on comparing the value of this param to a literal or a different expression. */ + /** Returns a parametrized expression of Boolean type, based on comparing the value of this parameter to a literal or a different expression. */ equals(rhs: T | Expression) { return this.cmp("==", rhs); } - /** Returns a parametrized expression of Boolean type, based on comparing the value of this param to a literal or a different expression. */ + /** Returns a parametrized expression of Boolean type, based on comparing the value of this parameter to a literal or a different expression. */ notEquals(rhs: T | Expression) { return this.cmp("!=", rhs); } - /** Returns a parametrized expression of Boolean type, based on comparing the value of this param to a literal or a different expression. */ + /** Returns a parametrized expression of Boolean type, based on comparing the value of this parameter to a literal or a different expression. */ greaterThan(rhs: T | Expression) { return this.cmp(">", rhs); } - /** Returns a parametrized expression of Boolean type, based on comparing the value of this param to a literal or a different expression. */ + /** Returns a parametrized expression of Boolean type, based on comparing the value of this parameter to a literal or a different expression. */ greaterThanOrEqualTo(rhs: T | Expression) { return this.cmp(">=", rhs); } - /** Returns a parametrized expression of Boolean type, based on comparing the value of this param to a literal or a different expression. */ + /** Returns a parametrized expression of Boolean type, based on comparing the value of this parameter to a literal or a different expression. */ lessThan(rhs: T | Expression) { return this.cmp("<", rhs); } - /** Returns a parametrized expression of Boolean type, based on comparing the value of this param to a literal or a different expression. */ + /** Returns a parametrized expression of Boolean type, based on comparing the value of this parameter to a literal or a different expression. */ lessThanOrEqualTo(rhs: T | Expression) { return this.cmp("<=", rhs); } /** - * Returns a parametrized expression of Boolean type, based on comparing the value of this param to a literal or a different expression. + * Returns a parametrized expression of Boolean type, based on comparing the value of this parameter to a literal or a different expression. * @deprecated A typo. Use lessThanOrEqualTo instead. */ lessThanorEqualTo(rhs: T | Expression) { @@ -420,7 +478,7 @@ export class StringParam extends Param { /** * A CEL expression which represents an internal Firebase variable. This class * cannot be instantiated by developers, but we provide several canned instances - * of it to make available params that will never have to be defined at + * of it to make available parameters that will never have to be defined at * deployment time, and can always be read from process.env. * @internal */ diff --git a/src/v1/cloud-functions.ts b/src/v1/cloud-functions.ts index 7909fc10d..d66539fb7 100644 --- a/src/v1/cloud-functions.ts +++ b/src/v1/cloud-functions.ts @@ -44,6 +44,7 @@ import { } from "../runtime/manifest"; import { ResetValue } from "../common/options"; import { SecretParam } from "../params/types"; +import { withInit } from "../common/onInit"; export { Change } from "../common/change"; @@ -366,6 +367,7 @@ export function makeCloudFunction({ service, triggerResource, }: MakeCloudFunctionArgs): CloudFunction { + handler = withInit(handler ?? contextOnlyHandler); const cloudFunction: any = (data: any, context: any) => { if (legacyEventType && context.eventType === legacyEventType) { /* diff --git a/src/v1/function-configuration.ts b/src/v1/function-configuration.ts index 5e74143da..90aa391fc 100644 --- a/src/v1/function-configuration.ts +++ b/src/v1/function-configuration.ts @@ -252,7 +252,8 @@ export interface RuntimeOptions { * * @remarks * Set this to true to enable the App Check replay protection feature by consuming the App Check token on callable - * request. Tokens that are found to be already consumed will have request.app.alreadyConsumed property set true. + * request. Tokens that are found to be already consumed will have the `request.app.alreadyConsumed` property set + * to true. * * * Tokens are only considered to be consumed if it is sent to the App Check service by setting this option to true. @@ -263,10 +264,10 @@ export interface RuntimeOptions { * performance and can potentially deplete your attestation providers' quotas faster. Use this feature only for * protecting low volume, security critical, or expensive operations. * - * This option does not affect the enforceAppCheck option. Setting the latter to true will cause the callable function - * to automatically respond with a 401 Unauthorized status code when request includes an invalid App Check token. - * When request includes valid but consumed App Check tokens, requests will not be automatically rejected. Instead, - * the request.app.alreadyConsumed property will be set to true and pass the execution to the handler code for making + * This option does not affect the `enforceAppCheck` option. Setting the latter to true will cause the callable function + * to automatically respond with a 401 Unauthorized status code when the request includes an invalid App Check token. + * When the request includes valid but consumed App Check tokens, requests will not be automatically rejected. Instead, + * the `request.app.alreadyConsumed` property will be set to true and pass the execution to the handler code for making * further decisions, such as requiring additional security checks or rejecting the request. */ consumeAppCheckToken?: boolean; diff --git a/src/v1/index.ts b/src/v1/index.ts index 8e75bff8d..7f3f9e10b 100644 --- a/src/v1/index.ts +++ b/src/v1/index.ts @@ -59,3 +59,5 @@ export * from "./function-configuration"; // NOTE: Equivalent to `export * as params from "../params"` but api-extractor doesn't support that syntax. import * as params from "../params"; export { params }; + +export { onInit } from "../common/onInit"; diff --git a/src/v1/providers/https.ts b/src/v1/providers/https.ts index 3c2340071..e9cd5d132 100644 --- a/src/v1/providers/https.ts +++ b/src/v1/providers/https.ts @@ -33,6 +33,8 @@ import { import { HttpsFunction, optionsToEndpoint, optionsToTrigger, Runnable } from "../cloud-functions"; import { DeploymentOptions } from "../function-configuration"; import { initV1Endpoint } from "../../runtime/manifest"; +import { withInit } from "../../common/onInit"; +import { wrapTraceContext } from "../../v2/trace"; export { Request, CallableContext, FunctionsErrorCode, HttpsError }; @@ -64,7 +66,7 @@ export function _onRequestWithOptions( ): HttpsFunction { // lets us add __endpoint without altering handler: const cloudFunction: any = (req: Request, res: express.Response) => { - return handler(req, res); + return wrapTraceContext(withInit(handler))(req, res); }; cloudFunction.__trigger = { ...optionsToTrigger(options), @@ -103,14 +105,18 @@ export function _onCallWithOptions( // onCallHandler sniffs the function length of the passed-in callback // and the user could have only tried to listen to data. Wrap their handler // in another handler to avoid accidentally triggering the v2 API - const fixedLen = (data: any, context: CallableContext) => handler(data, context); - const func: any = onCallHandler( - { - enforceAppCheck: options.enforceAppCheck, - consumeAppCheckToken: options.consumeAppCheckToken, - cors: { origin: true, methods: "POST" }, - }, - fixedLen + const fixedLen = (data: any, context: CallableContext) => { + return withInit(handler)(data, context); + }; + const func: any = wrapTraceContext( + onCallHandler( + { + enforceAppCheck: options.enforceAppCheck, + consumeAppCheckToken: options.consumeAppCheckToken, + cors: { origin: true, methods: "POST" }, + }, + fixedLen + ) ); func.__trigger = { @@ -128,7 +134,7 @@ export function _onCallWithOptions( callableTrigger: {}, }; - func.run = handler; + func.run = fixedLen; return func; } diff --git a/src/v2/core.ts b/src/v2/core.ts index fb7fc1e32..3d2e33748 100644 --- a/src/v2/core.ts +++ b/src/v2/core.ts @@ -31,6 +31,7 @@ import { ManifestEndpoint } from "../runtime/manifest"; export { Change }; export { ParamsOf } from "../common/params"; +export { onInit } from "../common/onInit"; /** @internal */ export interface TriggerAnnotation { diff --git a/src/v2/index.ts b/src/v2/index.ts index 7a1d89ef6..4a1b34263 100644 --- a/src/v2/index.ts +++ b/src/v2/index.ts @@ -68,7 +68,7 @@ export { EventHandlerOptions, } from "./options"; -export { CloudFunction, CloudEvent, ParamsOf } from "./core"; +export { CloudFunction, CloudEvent, ParamsOf, onInit } from "./core"; export { Change } from "../common/change"; // NOTE: Equivalent to `export * as params from "../params"` but api-extractor doesn't support that syntax. import * as params from "../params"; diff --git a/src/v2/providers/alerts/alerts.ts b/src/v2/providers/alerts/alerts.ts index 5dbde4f74..09e0885ca 100644 --- a/src/v2/providers/alerts/alerts.ts +++ b/src/v2/providers/alerts/alerts.ts @@ -27,6 +27,7 @@ import { Expression } from "../../../params"; import { wrapTraceContext } from "../../trace"; import * as options from "../../options"; import { SecretParam } from "../../../params/types"; +import { withInit } from "../../../common/onInit"; /** * The CloudEvent data emitted by Firebase Alerts. @@ -215,7 +216,7 @@ export function onAlertPublished( const [opts, alertType, appId] = getOptsAndAlertTypeAndApp(alertTypeOrOpts); const func = (raw: CloudEvent) => { - return wrapTraceContext(handler)(convertAlertAndApp(raw) as AlertEvent); + return wrapTraceContext(withInit(handler))(convertAlertAndApp(raw) as AlertEvent); }; func.run = handler; diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index 239233d73..d4ebdfe2e 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -32,6 +32,7 @@ import { wrapTraceContext } from "../../trace"; import { convertAlertAndApp, FirebaseAlertData, getEndpointAnnotation } from "./alerts"; import * as options from "../../options"; import { SecretParam } from "../../../params/types"; +import { withInit } from "../../../common/onInit"; /** * The internal payload object for adding a new tester device to app distribution. @@ -250,7 +251,7 @@ export function onNewTesterIosDevicePublished( const [opts, appId] = getOptsAndApp(appIdOrOptsOrHandler); const func = (raw: CloudEvent) => { - return wrapTraceContext(handler)( + return wrapTraceContext(withInit(handler))( convertAlertAndApp(raw) as AppDistributionEvent ); }; @@ -315,7 +316,7 @@ export function onInAppFeedbackPublished( const [opts, appId] = getOptsAndApp(appIdOrOptsOrHandler); const func = (raw: CloudEvent) => { - return wrapTraceContext(handler)( + return wrapTraceContext(withInit(handler))( convertAlertAndApp(raw) as AppDistributionEvent ); }; diff --git a/src/v2/providers/alerts/billing.ts b/src/v2/providers/alerts/billing.ts index 3b2af87b5..8bdb10d3d 100644 --- a/src/v2/providers/alerts/billing.ts +++ b/src/v2/providers/alerts/billing.ts @@ -29,6 +29,7 @@ import { CloudEvent, CloudFunction } from "../../core"; import { wrapTraceContext } from "../../trace"; import { convertAlertAndApp, FirebaseAlertData, getEndpointAnnotation } from "./alerts"; import * as options from "../../options"; +import { withInit } from "../../../common/onInit"; /** * The internal payload object for billing plan updates. @@ -152,7 +153,7 @@ export function onOperation( } const func = (raw: CloudEvent) => { - return wrapTraceContext(handler)(convertAlertAndApp(raw) as BillingEvent); + return wrapTraceContext(withInit(handler))(convertAlertAndApp(raw) as BillingEvent); }; func.run = handler; diff --git a/src/v2/providers/alerts/crashlytics.ts b/src/v2/providers/alerts/crashlytics.ts index 0e9cb7d8d..07aa7d892 100644 --- a/src/v2/providers/alerts/crashlytics.ts +++ b/src/v2/providers/alerts/crashlytics.ts @@ -32,6 +32,7 @@ import { wrapTraceContext } from "../../trace"; import { convertAlertAndApp, FirebaseAlertData, getEndpointAnnotation } from "./alerts"; import * as options from "../../options"; import { SecretParam } from "../../../params/types"; +import { withInit } from "../../../common/onInit"; /** Generic Crashlytics issue interface */ export interface Issue { @@ -581,7 +582,7 @@ export function onOperation( const [opts, appId] = getOptsAndApp(appIdOrOptsOrHandler); const func = (raw: CloudEvent) => { - return wrapTraceContext(handler(convertAlertAndApp(raw) as CrashlyticsEvent)); + return wrapTraceContext(withInit(handler))(convertAlertAndApp(raw) as CrashlyticsEvent); }; func.run = handler; diff --git a/src/v2/providers/alerts/performance.ts b/src/v2/providers/alerts/performance.ts index 56912d596..9ee3f7beb 100644 --- a/src/v2/providers/alerts/performance.ts +++ b/src/v2/providers/alerts/performance.ts @@ -25,8 +25,10 @@ * @packageDocumentation */ +import { withInit } from "../../../common/onInit"; import { CloudEvent, CloudFunction } from "../../core"; import { EventHandlerOptions } from "../../options"; +import { wrapTraceContext } from "../../trace"; import { convertAlertAndApp, FirebaseAlertData, getEndpointAnnotation } from "./alerts"; /** @@ -137,7 +139,7 @@ export function onThresholdAlertPublished( const event = convertAlertAndApp(raw) as PerformanceEvent; const convertedPayload = convertPayload(event.data.payload); event.data.payload = convertedPayload; - return handler(event); + return wrapTraceContext(withInit(handler(event))); }; func.run = handler; diff --git a/src/v2/providers/database.ts b/src/v2/providers/database.ts index c3318855c..50400bdcf 100644 --- a/src/v2/providers/database.ts +++ b/src/v2/providers/database.ts @@ -34,6 +34,7 @@ import { Expression } from "../../params"; import { wrapTraceContext } from "../trace"; import * as options from "../options"; import { SecretParam } from "../../params/types"; +import { withInit } from "../../common/onInit"; export { DataSnapshot }; @@ -468,7 +469,9 @@ export function onChangedOperation( const instanceUrl = getInstance(event); const params = makeParams(event, pathPattern, instancePattern) as unknown as ParamsOf; const databaseEvent = makeChangedDatabaseEvent(event, instanceUrl, params); - return wrapTraceContext(handler)(databaseEvent); + // Intentionally put init in the context of traces in case there is something + // expensive to observe. + return wrapTraceContext(withInit(handler))(databaseEvent); }; func.run = handler; @@ -496,7 +499,7 @@ export function onOperation( const params = makeParams(event, pathPattern, instancePattern) as unknown as ParamsOf; const data = eventType === deletedEventType ? event.data.data : event.data.delta; const databaseEvent = makeDatabaseEvent(event, data, instanceUrl, params); - return handler(databaseEvent); + return wrapTraceContext(withInit(handler))(databaseEvent); }; func.run = handler; diff --git a/src/v2/providers/eventarc.ts b/src/v2/providers/eventarc.ts index 6d980e2f9..3ad523b6d 100644 --- a/src/v2/providers/eventarc.ts +++ b/src/v2/providers/eventarc.ts @@ -33,6 +33,7 @@ import { wrapTraceContext } from "../trace"; import { Expression } from "../../params"; import * as options from "../options"; import { SecretParam } from "../../params/types"; +import { withInit } from "../../common/onInit"; /** Options that can be set on an Eventarc trigger. */ export interface EventarcTriggerOptions extends options.EventHandlerOptions { @@ -194,7 +195,7 @@ export function onCustomEventPublished( opts = eventTypeOrOpts; } const func = (raw: CloudEvent) => { - return wrapTraceContext(handler)(raw as CloudEvent); + return wrapTraceContext(withInit(handler))(raw as CloudEvent); }; func.run = handler; diff --git a/src/v2/providers/firestore.ts b/src/v2/providers/firestore.ts index 619e089e3..f351cc755 100644 --- a/src/v2/providers/firestore.ts +++ b/src/v2/providers/firestore.ts @@ -35,6 +35,8 @@ import { createSnapshotFromProtobuf, } from "../../common/providers/firestore"; import { wrapTraceContext } from "../trace"; +import { withInit } from "../../common/onInit"; +import { Expression } from "../../params"; export { Change }; @@ -50,6 +52,22 @@ export const updatedEventType = "google.cloud.firestore.document.v1.updated"; /** @internal */ export const deletedEventType = "google.cloud.firestore.document.v1.deleted"; +/** @internal */ +export const writtenEventWithAuthContextType = + "google.cloud.firestore.document.v1.written.withAuthContext"; + +/** @internal */ +export const createdEventWithAuthContextType = + "google.cloud.firestore.document.v1.created.withAuthContext"; + +/** @internal */ +export const updatedEventWithAuthContextType = + "google.cloud.firestore.document.v1.updated.withAuthContext"; + +/** @internal */ +export const deletedEventWithAuthContextType = + "google.cloud.firestore.document.v1.deleted.withAuthContext"; + // https://github.com/googleapis/google-cloudevents-nodejs/blob/main/cloud/firestore/v1/DocumentEventData.ts /** @internal */ export interface RawFirestoreDocument { @@ -77,12 +95,28 @@ export interface RawFirestoreEvent extends CloudEvent */ export interface FirestoreEvent> extends CloudEvent { /** The location of the Firestore instance */ @@ -102,18 +136,26 @@ export interface FirestoreEvent> extends Clou params: Params; } +export interface FirestoreAuthEvent> + extends FirestoreEvent { + /** The type of principal that triggered the event */ + authType: AuthType; + /** The unique identifier for the principal */ + authId?: string; +} + /** DocumentOptions extend EventHandlerOptions with provided document and optional database and namespace. */ export interface DocumentOptions extends EventHandlerOptions { /** The document path */ - document: Document; + document: Document | Expression; /** The Firestore database */ - database?: string; + database?: string | Expression; /** The Firestore namespace */ - namespace?: string; + namespace?: string | Expression; } /** - * Event handler which triggers when a document is created, updated, or deleted in Firestore. + * Event handler that triggers when a document is created, updated, or deleted in Firestore. * * @param document - The Firestore document path to trigger on. * @param handler - Event handler which is run every time a Firestore create, update, or delete occurs. @@ -126,7 +168,7 @@ export function onDocumentWritten( ): CloudFunction | undefined, ParamsOf>>; /** - * Event handler which triggers when a document is created, updated, or deleted in Firestore. + * Event handler that triggers when a document is created, updated, or deleted in Firestore. * * @param opts - Options that can be set on an individual event-handling function. * @param handler - Event handler which is run every time a Firestore create, update, or delete occurs. @@ -139,7 +181,7 @@ export function onDocumentWritten( ): CloudFunction | undefined, ParamsOf>>; /** - * Event handler which triggers when a document is created, updated, or deleted in Firestore. + * Event handler that triggers when a document is created, updated, or deleted in Firestore. * * @param documentOrOpts - Options or a string document path. * @param handler - Event handler which is run every time a Firestore create, update, or delete occurs. @@ -154,7 +196,51 @@ export function onDocumentWritten( } /** - * Event handler which triggers when a document is created in Firestore. + * Event handler that triggers when a document is created, updated, or deleted in Firestore. + * This trigger also provides the authentication context of the principal who triggered the event. + * + * @param document - The Firestore document path to trigger on. + * @param handler - Event handler which is run every time a Firestore create, update, or delete occurs. + */ +export function onDocumentWrittenWithAuthContext( + document: Document, + handler: ( + event: FirestoreAuthEvent | undefined, ParamsOf> + ) => any | Promise +): CloudFunction | undefined, ParamsOf>>; + +/** + * Event handler that triggers when a document is created, updated, or deleted in Firestore. + * This trigger also provides the authentication context of the principal who triggered the event. + * + * @param opts - Options that can be set on an individual event-handling function. + * @param handler - Event handler which is run every time a Firestore create, update, or delete occurs. + */ +export function onDocumentWrittenWithAuthContext( + opts: DocumentOptions, + handler: ( + event: FirestoreAuthEvent | undefined, ParamsOf> + ) => any | Promise +): CloudFunction | undefined, ParamsOf>>; + +/** + * Event handler that triggers when a document is created, updated, or deleted in Firestore. + * This trigger also provides the authentication context of the principal who triggered the event. + * + * @param opts - Options or a string document path. + * @param handler - Event handler which is run every time a Firestore create, update, or delete occurs. + */ +export function onDocumentWrittenWithAuthContext( + documentOrOpts: Document | DocumentOptions, + handler: ( + event: FirestoreAuthEvent | undefined, ParamsOf> + ) => any | Promise +): CloudFunction | undefined, ParamsOf>> { + return onChangedOperation(writtenEventWithAuthContextType, documentOrOpts, handler); +} + +/** + * Event handler that triggers when a document is created in Firestore. * * @param document - The Firestore document path to trigger on. * @param handler - Event handler which is run every time a Firestore create occurs. @@ -167,7 +253,7 @@ export function onDocumentCreated( ): CloudFunction>>; /** - * Event handler which triggers when a document is created in Firestore. + * Event handler that triggers when a document is created in Firestore. * * @param opts - Options that can be set on an individual event-handling function. * @param handler - Event handler which is run every time a Firestore create occurs. @@ -180,7 +266,7 @@ export function onDocumentCreated( ): CloudFunction>>; /** - * Event handler which triggers when a document is created in Firestore. + * Event handler that triggers when a document is created in Firestore. * * @param documentOrOpts - Options or a string document path. * @param handler - Event handler which is run every time a Firestore create occurs. @@ -195,7 +281,50 @@ export function onDocumentCreated( } /** - * Event handler which triggers when a document is updated in Firestore. + * Event handler that triggers when a document is created in Firestore. + * This trigger also provides the authentication context of the principal who triggered the event. + * + * @param document - The Firestore document path to trigger on. + * @param handler - Event handler which is run every time a Firestore create occurs. + */ +export function onDocumentCreatedWithAuthContext( + document: Document, + handler: ( + event: FirestoreAuthEvent> + ) => any | Promise +): CloudFunction>>; + +/** + * Event handler that triggers when a document is created in Firestore. + * This trigger also provides the authentication context of the principal who triggered the event. + * + * @param opts - Options that can be set on an individual event-handling function. + * @param handler - Event handler which is run every time a Firestore create occurs. + */ +export function onDocumentCreatedWithAuthContext( + opts: DocumentOptions, + handler: ( + event: FirestoreAuthEvent> + ) => any | Promise +): CloudFunction>>; + +/** + * Event handler that triggers when a document is created in Firestore. + * + * @param documentOrOpts - Options or a string document path. + * @param handler - Event handler which is run every time a Firestore create occurs. + */ +export function onDocumentCreatedWithAuthContext( + documentOrOpts: Document | DocumentOptions, + handler: ( + event: FirestoreAuthEvent> + ) => any | Promise +): CloudFunction>> { + return onOperation(createdEventWithAuthContextType, documentOrOpts, handler); +} + +/** + * Event handler that triggers when a document is updated in Firestore. * * @param document - The Firestore document path to trigger on. * @param handler - Event handler which is run every time a Firestore update occurs. @@ -207,7 +336,7 @@ export function onDocumentUpdated( ) => any | Promise ): CloudFunction | undefined, ParamsOf>>; /** - * Event handler which triggers when a document is updated in Firestore. + * Event handler that triggers when a document is updated in Firestore. * * @param opts - Options that can be set on an individual event-handling function. * @param handler - Event handler which is run every time a Firestore update occurs. @@ -220,7 +349,7 @@ export function onDocumentUpdated( ): CloudFunction | undefined, ParamsOf>>; /** - * Event handler which triggers when a document is updated in Firestore. + * Event handler that triggers when a document is updated in Firestore. * * @param documentOrOpts - Options or a string document path. * @param handler - Event handler which is run every time a Firestore update occurs. @@ -235,7 +364,52 @@ export function onDocumentUpdated( } /** - * Event handler which triggers when a document is deleted in Firestore. + * Event handler that triggers when a document is updated in Firestore. + * This trigger also provides the authentication context of the principal who triggered the event. + * + * @param document - The Firestore document path to trigger on. + * @param handler - Event handler which is run every time a Firestore update occurs. + */ +export function onDocumentUpdatedWithAuthContext( + document: Document, + handler: ( + event: FirestoreAuthEvent | undefined, ParamsOf> + ) => any | Promise +): CloudFunction | undefined, ParamsOf>>; + +/** + * Event handler that triggers when a document is updated in Firestore. + * This trigger also provides the authentication context of the principal who triggered the event. + * + * @param opts - Options that can be set on an individual event-handling function. + * @param handler - Event handler which is run every time a Firestore update occurs. + */ +export function onDocumentUpdatedWithAuthContext( + opts: DocumentOptions, + handler: ( + event: FirestoreAuthEvent | undefined, ParamsOf> + ) => any | Promise +): CloudFunction | undefined, ParamsOf>>; + +/** + * Event handler that triggers when a document is updated in Firestore. + * + * @param documentOrOpts - Options or a string document path. + * @param handler - Event handler which is run every time a Firestore update occurs. + */ +export function onDocumentUpdatedWithAuthContext( + documentOrOpts: Document | DocumentOptions, + handler: ( + event: FirestoreAuthEvent | undefined, ParamsOf> + ) => any | Promise +): CloudFunction< + FirestoreAuthEvent | undefined, ParamsOf> +> { + return onChangedOperation(updatedEventWithAuthContextType, documentOrOpts, handler); +} + +/** + * Event handler that triggers when a document is deleted in Firestore. * * @param document - The Firestore document path to trigger on. * @param handler - Event handler which is run every time a Firestore delete occurs. @@ -248,7 +422,7 @@ export function onDocumentDeleted( ): CloudFunction>>; /** - * Event handler which triggers when a document is deleted in Firestore. + * Event handler that triggers when a document is deleted in Firestore. * * @param opts - Options that can be set on an individual event-handling function. * @param handler - Event handler which is run every time a Firestore delete occurs. @@ -261,7 +435,7 @@ export function onDocumentDeleted( ): CloudFunction>>; /** - * Event handler which triggers when a document is deleted in Firestore. + * Event handler that triggers when a document is deleted in Firestore. * * @param documentOrOpts - Options or a string document path. * @param handler - Event handler which is run every time a Firestore delete occurs. @@ -275,11 +449,54 @@ export function onDocumentDeleted( return onOperation(deletedEventType, documentOrOpts, handler); } +/** + * Event handler that triggers when a document is deleted in Firestore. + * This trigger also provides the authentication context of the principal who triggered the event. + * + * @param document - The Firestore document path to trigger on. + * @param handler - Event handler which is run every time a Firestore delete occurs. + */ +export function onDocumentDeletedWithAuthContext( + document: Document, + handler: ( + event: FirestoreAuthEvent> + ) => any | Promise +): CloudFunction>>; + +/** + * Event handler that triggers when a document is deleted in Firestore. + * This trigger also provides the authentication context of the principal who triggered the event. + * + * @param opts - Options that can be set on an individual event-handling function. + * @param handler - Event handler which is run every time a Firestore delete occurs. + */ +export function onDocumentDeletedWithAuthContext( + opts: DocumentOptions, + handler: ( + event: FirestoreAuthEvent> + ) => any | Promise +): CloudFunction>>; + +/** + * Event handler that triggers when a document is deleted in Firestore. + * + * @param documentOrOpts - Options or a string document path. + * @param handler - Event handler which is run every time a Firestore delete occurs. + */ +export function onDocumentDeletedWithAuthContext( + documentOrOpts: Document | DocumentOptions, + handler: ( + event: FirestoreAuthEvent> + ) => any | Promise +): CloudFunction>> { + return onOperation(deletedEventWithAuthContextType, documentOrOpts, handler); +} + /** @internal */ export function getOpts(documentOrOpts: string | DocumentOptions) { - let document: string; - let database: string; - let namespace: string; + let document: string | Expression; + let database: string | Expression; + let namespace: string | Expression; let opts: EventHandlerOptions; if (typeof documentOrOpts === "string") { document = normalizePath(documentOrOpts); @@ -287,7 +504,10 @@ export function getOpts(documentOrOpts: string | DocumentOptions) { namespace = "(default)"; opts = {}; } else { - document = normalizePath(documentOrOpts.document); + document = + typeof documentOrOpts.document === "string" + ? normalizePath(documentOrOpts.document) + : documentOrOpts.document; database = documentOrOpts.database || "(default)"; namespace = documentOrOpts.namespace || "(default)"; opts = { ...documentOrOpts }; @@ -312,13 +532,14 @@ function getPath(event: RawFirestoreEvent): string { /** @internal */ export function createSnapshot(event: RawFirestoreEvent): QueryDocumentSnapshot { if (event.datacontenttype?.includes("application/protobuf") || Buffer.isBuffer(event.data)) { - return createSnapshotFromProtobuf(event.data as Uint8Array, getPath(event)); + return createSnapshotFromProtobuf(event.data as Uint8Array, getPath(event), event.database); } else if (event.datacontenttype?.includes("application/json")) { return createSnapshotFromJson( event.data, event.source, (event.data as RawFirestoreData).value?.createTime, - (event.data as RawFirestoreData).value?.updateTime + (event.data as RawFirestoreData).value?.updateTime, + event.database ); } else { logger.error( @@ -331,13 +552,18 @@ export function createSnapshot(event: RawFirestoreEvent): QueryDocumentSnapshot /** @internal */ export function createBeforeSnapshot(event: RawFirestoreEvent): QueryDocumentSnapshot { if (event.datacontenttype?.includes("application/protobuf") || Buffer.isBuffer(event.data)) { - return createBeforeSnapshotFromProtobuf(event.data as Uint8Array, getPath(event)); + return createBeforeSnapshotFromProtobuf( + event.data as Uint8Array, + getPath(event), + event.database + ); } else if (event.datacontenttype?.includes("application/json")) { return createBeforeSnapshotFromJson( event.data, event.source, (event.data as RawFirestoreData).oldValue?.createTime, - (event.data as RawFirestoreData).oldValue?.updateTime + (event.data as RawFirestoreData).oldValue?.updateTime, + event.database ); } else { logger.error( @@ -357,11 +583,13 @@ export function makeParams(document: string, documentPattern: PathPattern) { /** @internal */ export function makeFirestoreEvent( eventType: string, - event: RawFirestoreEvent, + event: RawFirestoreEvent | RawFirestoreAuthEvent, params: Params -): FirestoreEvent { +): + | FirestoreEvent + | FirestoreAuthEvent { const data = event.data - ? eventType === createdEventType + ? eventType === createdEventType || eventType === createdEventWithAuthContextType ? createSnapshot(event) : createBeforeSnapshot(event) : undefined; @@ -370,16 +598,31 @@ export function makeFirestoreEvent( params, data, }; + delete (firestoreEvent as any).datacontenttype; delete (firestoreEvent as any).dataschema; + + if ("authtype" in event) { + const eventWithAuth = { + ...firestoreEvent, + authType: event.authtype, + authId: event.authid, + }; + delete (eventWithAuth as any).authtype; + delete (eventWithAuth as any).authid; + return eventWithAuth; + } + return firestoreEvent; } /** @internal */ export function makeChangedFirestoreEvent( - event: RawFirestoreEvent, + event: RawFirestoreEvent | RawFirestoreAuthEvent, params: Params -): FirestoreEvent | undefined, Params> { +): + | FirestoreEvent | undefined, Params> + | FirestoreAuthEvent | undefined, Params> { const data = event.data ? Change.fromObjects(createBeforeSnapshot(event), createSnapshot(event)) : undefined; @@ -390,6 +633,18 @@ export function makeChangedFirestoreEvent( }; delete (firestoreEvent as any).datacontenttype; delete (firestoreEvent as any).dataschema; + + if ("authtype" in event) { + const eventWithAuth = { + ...firestoreEvent, + authType: event.authtype, + authId: event.authid, + }; + delete (eventWithAuth as any).authtype; + delete (eventWithAuth as any).authid; + return eventWithAuth; + } + return firestoreEvent; } @@ -397,21 +652,25 @@ export function makeChangedFirestoreEvent( export function makeEndpoint( eventType: string, opts: EventHandlerOptions, - document: PathPattern, - database: string, - namespace: string + document: string | Expression, + database: string | Expression, + namespace: string | Expression ): ManifestEndpoint { const baseOpts = optionsToEndpoint(getGlobalOptions()); const specificOpts = optionsToEndpoint(opts); - const eventFilters: Record = { + const eventFilters: Record> = { database, namespace, }; - const eventFilterPathPatterns: Record = {}; - document.hasWildcards() - ? (eventFilterPathPatterns.document = document.getValue()) - : (eventFilters.document = document.getValue()); + const eventFilterPathPatterns: Record> = {}; + const maybePattern = + typeof document === "string" ? new PathPattern(document).hasWildcards() : true; + if (maybePattern) { + eventFilterPathPatterns.document = document; + } else { + eventFilters.document = document; + } return { ...initV2Endpoint(getGlobalOptions(), opts), @@ -432,53 +691,59 @@ export function makeEndpoint( } /** @internal */ -export function onOperation( +export function onOperation< + Document extends string, + Event extends FirestoreEvent> +>( eventType: string, documentOrOpts: Document | DocumentOptions, - handler: (event: FirestoreEvent>) => any | Promise -): CloudFunction>> { + handler: (event: Event) => any | Promise +): CloudFunction { const { document, database, namespace, opts } = getOpts(documentOrOpts); - const documentPattern = new PathPattern(document); - // wrap the handler const func = (raw: CloudEvent) => { - const event = raw as RawFirestoreEvent; + const event = raw as RawFirestoreEvent | RawFirestoreAuthEvent; + const documentPattern = new PathPattern( + typeof document === "string" ? document : document.value() + ); const params = makeParams(event.document, documentPattern) as unknown as ParamsOf; const firestoreEvent = makeFirestoreEvent(eventType, event, params); - return wrapTraceContext(handler)(firestoreEvent); + return wrapTraceContext(withInit(handler))(firestoreEvent); }; func.run = handler; - func.__endpoint = makeEndpoint(eventType, opts, documentPattern, database, namespace); + func.__endpoint = makeEndpoint(eventType, opts, document, database, namespace); return func; } /** @internal */ -export function onChangedOperation( +export function onChangedOperation< + Document extends string, + Event extends FirestoreEvent, ParamsOf> +>( eventType: string, documentOrOpts: Document | DocumentOptions, - handler: ( - event: FirestoreEvent, ParamsOf> - ) => any | Promise -): CloudFunction, ParamsOf>> { + handler: (event: Event) => any | Promise +): CloudFunction { const { document, database, namespace, opts } = getOpts(documentOrOpts); - const documentPattern = new PathPattern(document); - // wrap the handler const func = (raw: CloudEvent) => { - const event = raw as RawFirestoreEvent; + const event = raw as RawFirestoreEvent | RawFirestoreAuthEvent; + const documentPattern = new PathPattern( + typeof document === "string" ? document : document.value() + ); const params = makeParams(event.document, documentPattern) as unknown as ParamsOf; const firestoreEvent = makeChangedFirestoreEvent(event, params); - return handler(firestoreEvent); + return wrapTraceContext(withInit(handler))(firestoreEvent); }; func.run = handler; - func.__endpoint = makeEndpoint(eventType, opts, documentPattern, database, namespace); + func.__endpoint = makeEndpoint(eventType, opts, document, database, namespace); return func; } diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 466160d62..311323f46 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -43,6 +43,7 @@ import { GlobalOptions, SupportedRegion } from "../options"; import { Expression } from "../../params"; import { SecretParam } from "../../params/types"; import * as options from "../options"; +import { withInit } from "../../common/onInit"; export { Request, CallableRequest, FunctionsErrorCode, HttpsError }; @@ -264,19 +265,26 @@ export function onRequest( // Respect `cors: false` to turn off cors even if debug feature is enabled. origin = opts.cors === false ? false : true; } + // Arrays cause the access-control-allow-origin header to be dynamic based + // on the origin header of the request. If there is only one element in the + // array, this is unnecessary. + if (Array.isArray(origin) && origin.length === 1) { + origin = origin[0]; + } + const middleware = cors({ origin }); const userProvidedHandler = handler; handler = (req: Request, res: express.Response): void | Promise => { return new Promise((resolve) => { res.on("finish", resolve); - cors({ origin })(req, res, () => { + middleware(req, res, () => { resolve(userProvidedHandler(req, res)); }); }); }; } - handler = wrapTraceContext(handler); + handler = wrapTraceContext(withInit(handler)); Object.defineProperty(handler, "__trigger", { get: () => { @@ -340,7 +348,8 @@ export function onRequest( export function onCall>( opts: CallableOptions, handler: (request: CallableRequest) => Return -): CallableFunction; +): CallableFunction ? Return : Promise>; + /** * Declares a callable method for clients to call using a Firebase SDK. * @param handler - A function that takes a {@link https.CallableRequest}. @@ -348,11 +357,11 @@ export function onCall>( */ export function onCall>( handler: (request: CallableRequest) => Return -): CallableFunction; +): CallableFunction ? Return : Promise>; export function onCall>( optsOrHandler: CallableOptions | ((request: CallableRequest) => Return), handler?: (request: CallableRequest) => Return -): CallableFunction { +): CallableFunction ? Return : Promise> { let opts: CallableOptions; if (arguments.length === 1) { opts = {}; @@ -361,12 +370,18 @@ export function onCall>( opts = optsOrHandler as CallableOptions; } - const origin = isDebugFeatureEnabled("enableCors") ? true : "cors" in opts ? opts.cors : true; + let origin = isDebugFeatureEnabled("enableCors") ? true : "cors" in opts ? opts.cors : true; + // Arrays cause the access-control-allow-origin header to be dynamic based + // on the origin header of the request. If there is only one element in the + // array, this is unnecessary. + if (Array.isArray(origin) && origin.length === 1) { + origin = origin[1]; + } // onCallHandler sniffs the function length to determine which API to present. // fix the length to prevent api versions from being mismatched. - const fixedLen = (req: CallableRequest) => handler(req); - const func: any = onCallHandler( + const fixedLen = (req: CallableRequest) => withInit(handler)(req); + let func: any = onCallHandler( { cors: { origin, methods: "POST" }, enforceAppCheck: opts.enforceAppCheck ?? options.getGlobalOptions().enforceAppCheck, @@ -375,6 +390,8 @@ export function onCall>( fixedLen ); + func = wrapTraceContext(func); + Object.defineProperty(func, "__trigger", { get: () => { const baseOpts = options.optionsToTriggerAnnotations(options.getGlobalOptions()); @@ -413,6 +430,6 @@ export function onCall>( callableTrigger: {}, }; - func.run = handler; + func.run = withInit(handler); return func; } diff --git a/src/v2/providers/identity.ts b/src/v2/providers/identity.ts index adb561437..753b05f0e 100644 --- a/src/v2/providers/identity.ts +++ b/src/v2/providers/identity.ts @@ -42,6 +42,7 @@ import { Expression } from "../../params"; import { initV2Endpoint } from "../../runtime/manifest"; import * as options from "../options"; import { SecretParam } from "../../params/types"; +import { withInit } from "../../common/onInit"; export { AuthUserRecord, AuthBlockingEvent, HttpsError }; @@ -376,7 +377,7 @@ export function beforeOperation( // Create our own function that just calls the provided function so we know for sure that // handler takes one argument. This is something common/providers/identity depends on. const wrappedHandler = (event: AuthBlockingEvent) => handler(event); - const func: any = wrapTraceContext(wrapHandler(eventType, wrappedHandler)); + const func: any = wrapTraceContext(withInit(wrapHandler(eventType, wrappedHandler))); const legacyEventType = `providers/cloud.auth/eventTypes/user.${eventType}`; diff --git a/src/v2/providers/pubsub.ts b/src/v2/providers/pubsub.ts index 19e368d41..0cee195ea 100644 --- a/src/v2/providers/pubsub.ts +++ b/src/v2/providers/pubsub.ts @@ -33,6 +33,7 @@ import { wrapTraceContext } from "../trace"; import { Expression } from "../../params"; import * as options from "../options"; import { SecretParam } from "../../params/types"; +import { withInit } from "../../common/onInit"; /** * Google Cloud Pub/Sub is a globally distributed message bus that automatically scales as you need it. @@ -303,7 +304,7 @@ export function onMessagePublished( subscription: string; }; messagePublishedData.message = new Message(messagePublishedData.message); - return wrapTraceContext(handler)(raw as CloudEvent>); + return wrapTraceContext(withInit(handler))(raw as CloudEvent>); }; func.run = handler; diff --git a/src/v2/providers/remoteConfig.ts b/src/v2/providers/remoteConfig.ts index f6ff45172..23707dea2 100644 --- a/src/v2/providers/remoteConfig.ts +++ b/src/v2/providers/remoteConfig.ts @@ -20,9 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +import { withInit } from "../../common/onInit"; import { initV2Endpoint, ManifestEndpoint } from "../../runtime/manifest"; import { CloudEvent, CloudFunction } from "../core"; import { EventHandlerOptions, getGlobalOptions, optionsToEndpoint } from "../options"; +import { wrapTraceContext } from "../trace"; /** @internal */ export const eventType = "google.firebase.remoteconfig.remoteConfig.v1.updated"; @@ -128,9 +130,11 @@ export function onConfigUpdated( const baseOpts = optionsToEndpoint(getGlobalOptions()); const specificOpts = optionsToEndpoint(optsOrHandler); - const func: any = (raw: CloudEvent) => { - return handler(raw as CloudEvent); - }; + const func: any = wrapTraceContext( + withInit((raw: CloudEvent) => { + return handler(raw as CloudEvent); + }) + ); func.run = handler; const ep: ManifestEndpoint = { diff --git a/src/v2/providers/scheduler.ts b/src/v2/providers/scheduler.ts index 9346c451c..1f8f33c31 100644 --- a/src/v2/providers/scheduler.ts +++ b/src/v2/providers/scheduler.ts @@ -36,6 +36,7 @@ import { wrapTraceContext } from "../trace"; import { Expression } from "../../params"; import * as logger from "../../logger"; import * as options from "../options"; +import { withInit } from "../../common/onInit"; /** @hidden */ interface SeparatedOpts { @@ -176,7 +177,7 @@ export function onSchedule( res.status(500).send(); } }; - const func: any = wrapTraceContext(httpFunc); + const func: any = wrapTraceContext(withInit(httpFunc)); func.run = handler; const globalOpts = options.getGlobalOptions(); diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts index 451a257b5..582a3db7e 100644 --- a/src/v2/providers/storage.ts +++ b/src/v2/providers/storage.ts @@ -34,6 +34,7 @@ import { wrapTraceContext } from "../trace"; import { Expression } from "../../params"; import * as options from "../options"; import { SecretParam } from "../../params/types"; +import { withInit } from "../../common/onInit"; /** * An object within Google Cloud Storage. @@ -201,7 +202,7 @@ export const metadataUpdatedEvent = "google.cloud.storage.object.v1.metadataUpda /** StorageOptions extend EventHandlerOptions with a bucket name */ export interface StorageOptions extends options.EventHandlerOptions { /** The name of the bucket containing this object. */ - bucket?: string; + bucket?: string | Expression; /** * If true, do not deploy or emulate this function. @@ -324,7 +325,7 @@ export function onObjectArchived( * @param handler - Event handler which is run every time a Google Cloud Storage archival occurs. */ export function onObjectArchived( - bucket: string, + bucket: string | Expression, handler: (event: StorageEvent) => any | Promise ): CloudFunction; @@ -352,7 +353,11 @@ export function onObjectArchived( * @param handler - Event handler which is run every time a Google Cloud Storage archival occurs. */ export function onObjectArchived( - bucketOrOptsOrHandler: string | StorageOptions | ((event: StorageEvent) => any | Promise), + bucketOrOptsOrHandler: + | string + | Expression + | StorageOptions + | ((event: StorageEvent) => any | Promise), handler?: (event: StorageEvent) => any | Promise ): CloudFunction { return onOperation(archivedEvent, bucketOrOptsOrHandler, handler); @@ -384,7 +389,7 @@ export function onObjectFinalized( * @param handler - Event handler which is run every time a Google Cloud Storage object creation occurs. */ export function onObjectFinalized( - bucket: string, + bucket: string | Expression, handler: (event: StorageEvent) => any | Promise ): CloudFunction; @@ -416,7 +421,11 @@ export function onObjectFinalized( * @param handler - Event handler which is run every time a Google Cloud Storage object creation occurs. */ export function onObjectFinalized( - bucketOrOptsOrHandler: string | StorageOptions | ((event: StorageEvent) => any | Promise), + bucketOrOptsOrHandler: + | string + | Expression + | StorageOptions + | ((event: StorageEvent) => any | Promise), handler?: (event: StorageEvent) => any | Promise ): CloudFunction { return onOperation(finalizedEvent, bucketOrOptsOrHandler, handler); @@ -450,7 +459,7 @@ export function onObjectDeleted( * @param handler - Event handler which is run every time a Google Cloud Storage object deletion occurs. */ export function onObjectDeleted( - bucket: string, + bucket: string | Expression, handler: (event: StorageEvent) => any | Promise ): CloudFunction; @@ -484,7 +493,11 @@ export function onObjectDeleted( * @param handler - Event handler which is run every time a Google Cloud Storage object deletion occurs. */ export function onObjectDeleted( - bucketOrOptsOrHandler: string | StorageOptions | ((event: StorageEvent) => any | Promise), + bucketOrOptsOrHandler: + | string + | Expression + | StorageOptions + | ((event: StorageEvent) => any | Promise), handler?: (event: StorageEvent) => any | Promise ): CloudFunction { return onOperation(deletedEvent, bucketOrOptsOrHandler, handler); @@ -509,7 +522,7 @@ export function onObjectMetadataUpdated( * @param handler - Event handler which is run every time a Google Cloud Storage object metadata update occurs. */ export function onObjectMetadataUpdated( - bucket: string, + bucket: string | Expression, handler: (event: StorageEvent) => any | Promise ): CloudFunction; @@ -533,7 +546,11 @@ export function onObjectMetadataUpdated( * @param handler - Event handler which is run every time a Google Cloud Storage object metadata update occurs. */ export function onObjectMetadataUpdated( - bucketOrOptsOrHandler: string | StorageOptions | ((event: StorageEvent) => any | Promise), + bucketOrOptsOrHandler: + | string + | Expression + | StorageOptions + | ((event: StorageEvent) => any | Promise), handler?: (event: StorageEvent) => any | Promise ): CloudFunction { return onOperation(metadataUpdatedEvent, bucketOrOptsOrHandler, handler); @@ -542,7 +559,11 @@ export function onObjectMetadataUpdated( /** @internal */ export function onOperation( eventType: string, - bucketOrOptsOrHandler: string | StorageOptions | ((event: StorageEvent) => any | Promise), + bucketOrOptsOrHandler: + | string + | Expression + | StorageOptions + | ((event: StorageEvent) => any | Promise), handler: (event: StorageEvent) => any | Promise ): CloudFunction { if (typeof bucketOrOptsOrHandler === "function") { @@ -553,7 +574,7 @@ export function onOperation( const [opts, bucket] = getOptsAndBucket(bucketOrOptsOrHandler); const func = (raw: CloudEvent) => { - return wrapTraceContext(handler)(raw as StorageEvent); + return wrapTraceContext(withInit(handler))(raw as StorageEvent); }; func.run = handler; @@ -616,11 +637,12 @@ export function onOperation( /** @internal */ export function getOptsAndBucket( - bucketOrOpts: string | StorageOptions -): [options.EventHandlerOptions, string] { - let bucket: string; + bucketOrOpts: string | Expression | StorageOptions +): [options.EventHandlerOptions, string | Expression] { + let bucket: string | Expression; let opts: options.EventHandlerOptions; - if (typeof bucketOrOpts === "string") { + // If bucket is a string or Expression + if (typeof bucketOrOpts === "string" || "value" in bucketOrOpts) { bucket = bucketOrOpts; opts = {}; } else { @@ -635,7 +657,7 @@ export function getOptsAndBucket( " by providing bucket name directly in the event handler or by setting process.env.FIREBASE_CONFIG." ); } - if (!/^[a-z\d][a-z\d\\._-]{1,230}[a-z\d]$/.test(bucket)) { + if (typeof bucket === "string" && !/^[a-z\d][a-z\d\\._-]{1,230}[a-z\d]$/.test(bucket)) { throw new Error(`Invalid bucket name ${bucket}`); } diff --git a/src/v2/providers/tasks.ts b/src/v2/providers/tasks.ts index 795939e1b..49d73bb39 100644 --- a/src/v2/providers/tasks.ts +++ b/src/v2/providers/tasks.ts @@ -40,6 +40,7 @@ import { HttpsFunction } from "./https"; import { Expression } from "../../params"; import { SecretParam } from "../../params/types"; import { initV2Endpoint, initTaskQueueTrigger } from "../../runtime/manifest"; +import { withInit } from "../../common/onInit"; export { AuthData, Request, RateLimits, RetryConfig }; @@ -210,7 +211,7 @@ export function onTaskDispatched( // onDispatchHandler sniffs the function length to determine which API to present. // fix the length to prevent api versions from being mismatched. const fixedLen = (req: Request) => handler(req); - const func: any = wrapTraceContext(onDispatchHandler(fixedLen)); + const func: any = wrapTraceContext(withInit(onDispatchHandler(fixedLen))); Object.defineProperty(func, "__trigger", { get: () => { diff --git a/src/v2/providers/testLab.ts b/src/v2/providers/testLab.ts index 0283a0280..cdf0c85f1 100644 --- a/src/v2/providers/testLab.ts +++ b/src/v2/providers/testLab.ts @@ -20,6 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +import { withInit } from "../../common/onInit"; import { initV2Endpoint, ManifestEndpoint } from "../../runtime/manifest"; import { CloudEvent, CloudFunction } from "../core"; import { EventHandlerOptions, getGlobalOptions, optionsToEndpoint } from "../options"; @@ -190,7 +191,7 @@ export function onTestMatrixCompleted( const specificOpts = optionsToEndpoint(optsOrHandler); const func: any = (raw: CloudEvent) => { - return wrapTraceContext(handler)(raw as CloudEvent); + return wrapTraceContext(withInit(handler))(raw as CloudEvent); }; func.run = handler;