From 93c47e3aa80f83304472d1183b15395caed4e9ea Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 2 Nov 2023 06:24:52 -0700 Subject: [PATCH 01/42] Update node versions used in GitHub actions (#1479) Also bump/enable test timeouts to successfully run in node 18/20 environments. --- .github/workflows/postmerge.yaml | 2 +- .github/workflows/test.yaml | 6 +-- scripts/bin-test/run.sh | 4 +- scripts/bin-test/test.ts | 79 ++++++++++++++++++-------------- 4 files changed, 49 insertions(+), 42 deletions(-) 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/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); + }); + } + }); +}); From 2841ebdee71669a96db12bed01688cfd105150c8 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 2 Nov 2023 10:01:21 -0700 Subject: [PATCH 02/42] Fix bug where auth metadata in the auth blocking tokens are assumed to be seconds not miliseconds (#1472) Auth metadata included in the JWT sent to Auth Blocking functions may include fields `last_sign_in_time` and `creation_time`. Values of these fields are sent as _miliseconds_ since epoch. The SDK incorrectly assumes that they are _seconds_ since epoch. Unfortunately, this information is not publicly documented, but I was able to verify the fix in production. Fixes: https://github.com/firebase/firebase-functions/issues/1468 --- CHANGELOG.md | 1 + spec/common/providers/identity.spec.ts | 12 ++++++------ src/common/providers/identity.ts | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 767a26aeb..eab4161a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ - Remove HTTP server shutdown message. (#1457) - Add features to task queue functions. (#1423) - Add traces to V2 Firestore trigger logs. (#1440) +- Fix incorrectly parsed timestamps in auth blocking functions. (#1472) diff --git a/spec/common/providers/identity.spec.ts b/spec/common/providers/identity.spec.ts index 308845172..a5112b6c3 100644 --- a/spec/common/providers/identity.spec.ts +++ b/spec/common/providers/identity.spec.ts @@ -207,8 +207,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(), @@ -374,8 +374,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, @@ -632,8 +632,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/src/common/providers/identity.ts b/src/common/providers/identity.ts index 9997d7569..b90c5b549 100644 --- a/src/common/providers/identity.ts +++ b/src/common/providers/identity.ts @@ -489,10 +489,10 @@ function unsafeDecodeAuthBlockingToken(token: string): DecodedPayload { */ 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, From b897b0dd47be992e9128de8cb5b52f410e7e3f4e Mon Sep 17 00:00:00 2001 From: Liubin Jiang <56564857+Xiaoshouzi-gh@users.noreply.github.com> Date: Thu, 2 Nov 2023 08:07:49 -1000 Subject: [PATCH 03/42] supporting recaptcha verdict for auth blocking functions (#1458) Added recaptcha support in auth blocking functions beforeCreate and beforeSignIn. This allows developers to see the recaptcha scores for authentication actions and override the recaptcha actions. --- spec/common/providers/identity.spec.ts | 80 ++++++++++++++++++++++---- src/common/providers/identity.ts | 66 +++++++++++++++++---- 2 files changed, 124 insertions(+), 22 deletions(-) diff --git a/spec/common/providers/identity.spec.ts b/spec/common/providers/identity.spec.ts index a5112b6c3..cfbaca770 100644 --- a/spec/common/providers/identity.spec.ts +++ b/spec/common/providers/identity.spec.ts @@ -26,6 +26,9 @@ import * as identity from "../../../src/common/providers/identity"; const EVENT = "EVENT_TYPE"; const now = new Date(); +const TEST_NAME = "John Doe"; +const ALLOW = "ALLOW"; +const BLOCK = "BLOCK"; describe("identity", () => { describe("userRecordConstructor", () => { @@ -232,14 +235,14 @@ describe("identity", () => { describe("parseProviderData", () => { const decodedUserInfo = { provider_id: "google.com", - display_name: "John Doe", + display_name: TEST_NAME, photo_url: "https://lh3.googleusercontent.com/1234567890/photo.jpg", uid: "1234567890", email: "user@gmail.com", }; const userInfo = { providerId: "google.com", - displayName: "John Doe", + displayName: TEST_NAME, photoURL: "https://lh3.googleusercontent.com/1234567890/photo.jpg", uid: "1234567890", email: "user@gmail.com", @@ -340,12 +343,12 @@ describe("identity", () => { uid: "abcdefghijklmnopqrstuvwxyz", email: "user@gmail.com", email_verified: true, - display_name: "John Doe", + display_name: TEST_NAME, phone_number: "+11234567890", provider_data: [ { provider_id: "google.com", - display_name: "John Doe", + display_name: TEST_NAME, photo_url: "https://lh3.googleusercontent.com/1234567890/photo.jpg", email: "user@gmail.com", uid: "1234567890", @@ -366,7 +369,7 @@ describe("identity", () => { provider_id: "password", email: "user@gmail.com", uid: "user@gmail.com", - display_name: "John Doe", + display_name: TEST_NAME, }, ], password_hash: "passwordHash", @@ -407,11 +410,11 @@ describe("identity", () => { phoneNumber: "+11234567890", emailVerified: true, disabled: false, - displayName: "John Doe", + displayName: TEST_NAME, providerData: [ { providerId: "google.com", - displayName: "John Doe", + displayName: TEST_NAME, photoURL: "https://lh3.googleusercontent.com/1234567890/photo.jpg", email: "user@gmail.com", uid: "1234567890", @@ -435,7 +438,7 @@ describe("identity", () => { }, { providerId: "password", - displayName: "John Doe", + displayName: TEST_NAME, photoURL: undefined, email: "user@gmail.com", uid: "user@gmail.com", @@ -489,8 +492,9 @@ describe("identity", () => { }); describe("parseAuthEventContext", () => { + const TEST_RECAPTCHA_SCORE = 0.9; const rawUserInfo = { - name: "John Doe", + name: TEST_NAME, granted_scopes: "openid https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile", id: "123456789", @@ -516,6 +520,7 @@ describe("identity", () => { user_agent: "USER_AGENT", locale: "en", raw_user_info: JSON.stringify(rawUserInfo), + recaptcha_score: TEST_RECAPTCHA_SCORE, }; const context = { locale: "en", @@ -534,6 +539,7 @@ describe("identity", () => { profile: rawUserInfo, username: undefined, isNewUser: false, + recaptchaScore: TEST_RECAPTCHA_SCORE, }, credential: null, params: {}, @@ -563,6 +569,7 @@ describe("identity", () => { oauth_refresh_token: "REFRESH_TOKEN", oauth_token_secret: "OAUTH_TOKEN_SECRET", oauth_expires_in: 3600, + recaptcha_score: TEST_RECAPTCHA_SCORE, }; const context = { locale: "en", @@ -581,6 +588,7 @@ describe("identity", () => { profile: rawUserInfo, username: undefined, isNewUser: false, + recaptchaScore: TEST_RECAPTCHA_SCORE, }, credential: { claims: undefined, @@ -619,14 +627,14 @@ describe("identity", () => { uid: "abcdefghijklmnopqrstuvwxyz", email: "user@gmail.com", email_verified: true, - display_name: "John Doe", + display_name: TEST_NAME, phone_number: "+11234567890", provider_data: [ { provider_id: "oidc.provider", email: "user@gmail.com", uid: "user@gmail.com", - display_name: "John Doe", + display_name: TEST_NAME, }, ], photo_url: "https://lh3.googleusercontent.com/1234567890/photo.jpg", @@ -647,6 +655,7 @@ describe("identity", () => { oauth_token_secret: "OAUTH_TOKEN_SECRET", oauth_expires_in: 3600, raw_user_info: JSON.stringify(rawUserInfo), + recaptcha_score: TEST_RECAPTCHA_SCORE, }; const context = { locale: "en", @@ -665,6 +674,7 @@ describe("identity", () => { providerId: "oidc.provider", profile: rawUserInfo, isNewUser: true, + recaptchaScore: TEST_RECAPTCHA_SCORE, }, credential: { claims: undefined, @@ -762,4 +772,52 @@ describe("identity", () => { ); }); }); + + describe("generateResponsePayload", () => { + const DISPLAY_NAME_FIELD = "displayName"; + const TEST_RESPONSE = { + displayName: TEST_NAME, + recaptchaActionOverride: BLOCK, + } as identity.BeforeCreateResponse; + + const EXPECT_PAYLOAD = { + userRecord: { displayName: TEST_NAME, updateMask: DISPLAY_NAME_FIELD }, + recaptchaActionOverride: BLOCK, + }; + + const TEST_RESPONSE_RECAPTCHA_ALLOW = { + recaptchaActionOverride: ALLOW, + } as identity.BeforeCreateResponse; + + const EXPECT_PAYLOAD_RECAPTCHA_ALLOW = { + recaptchaActionOverride: ALLOW, + }; + + const TEST_RESPONSE_RECAPTCHA_UNDEFINED = { + displayName: TEST_NAME, + } as identity.BeforeSignInResponse; + + const EXPECT_PAYLOAD_UNDEFINED = { + userRecord: { displayName: TEST_NAME, updateMask: DISPLAY_NAME_FIELD }, + }; + it("should return empty object on undefined response", () => { + expect(identity.generateResponsePayload()).to.eql({}); + }); + + it("should exclude recaptchaActionOverride field from updateMask", () => { + expect(identity.generateResponsePayload(TEST_RESPONSE)).to.deep.equal(EXPECT_PAYLOAD); + }); + + it("should return recaptchaActionOverride when it is true on response", () => { + expect(identity.generateResponsePayload(TEST_RESPONSE_RECAPTCHA_ALLOW)).to.deep.equal( + EXPECT_PAYLOAD_RECAPTCHA_ALLOW + ); + }); + + it("should not return recaptchaActionOverride if undefined", () => { + const payload = identity.generateResponsePayload(TEST_RESPONSE_RECAPTCHA_UNDEFINED); + expect(payload.hasOwnProperty("recaptchaActionOverride")).to.be.false; + expect(payload).to.deep.equal(EXPECT_PAYLOAD_UNDEFINED); + }); + }); }); diff --git a/src/common/providers/identity.ts b/src/common/providers/identity.ts index b90c5b549..374e4f8bd 100644 --- a/src/common/providers/identity.ts +++ b/src/common/providers/identity.ts @@ -310,6 +310,7 @@ export interface AdditionalUserInfo { profile?: any; username?: string; isNewUser: boolean; + recaptchaScore?: number; } /** The credential component of the auth event context */ @@ -338,6 +339,11 @@ export interface AuthBlockingEvent extends AuthEventContext { data: AuthUserRecord; } +/** + * The reCAPTCHA action options. + */ +export type RecaptchaActionOptions = "ALLOW" | "BLOCK"; + /** The handler response type for beforeCreate blocking events */ export interface BeforeCreateResponse { displayName?: string; @@ -345,6 +351,7 @@ export interface BeforeCreateResponse { emailVerified?: boolean; photoURL?: string; customClaims?: object; + recaptchaActionOverride?: RecaptchaActionOptions; } /** The handler response type for beforeSignIn blocking events */ @@ -423,9 +430,26 @@ export interface DecodedPayload { oauth_refresh_token?: string; oauth_token_secret?: string; oauth_expires_in?: number; + recaptcha_score?: number; [key: string]: any; } +/** + * Internal definition to include all the fields that can be sent as + * a response from the blocking function to the backend. + * This is added mainly to have a type definition for 'generateResponsePayload' + * @internal */ +export interface ResponsePayload { + userRecord?: UserRecordResponsePayload; + recaptchaActionOverride?: RecaptchaActionOptions; +} + +/** @internal */ +export interface UserRecordResponsePayload + extends Omit { + updateMask?: string; +} + type HandlerV1 = ( user: AuthUserRecord, context: AuthEventContext @@ -640,9 +664,39 @@ function parseAdditionalUserInfo(decodedJWT: DecodedPayload): AdditionalUserInfo profile, username, isNewUser: decodedJWT.event_type === "beforeCreate" ? true : false, + recaptchaScore: decodedJWT.recaptcha_score, }; } +/** + * Helper to generate a response from the blocking function to the Firebase Auth backend. + * @internal + */ +export function generateResponsePayload( + authResponse?: BeforeCreateResponse | BeforeSignInResponse +): ResponsePayload { + if (!authResponse) { + return {}; + } + + const { recaptchaActionOverride, ...formattedAuthResponse } = authResponse; + const result = {} as ResponsePayload; + const updateMask = getUpdateMask(formattedAuthResponse); + + if (updateMask.length !== 0) { + result.userRecord = { + ...formattedAuthResponse, + updateMask, + }; + } + + if (recaptchaActionOverride !== undefined) { + result.recaptchaActionOverride = recaptchaActionOverride; + } + + return result; +} + /** Helper to get the Credential from the decoded jwt */ function parseAuthCredential(decodedJWT: DecodedPayload, time: number): Credential { if ( @@ -801,7 +855,6 @@ export function wrapHandler(eventType: AuthBlockingEventType, handler: HandlerV1 : handler.length === 2 ? await auth.getAuth(getApp())._verifyAuthBlockingToken(req.body.data.jwt) : await auth.getAuth(getApp())._verifyAuthBlockingToken(req.body.data.jwt, "run.app"); - const authUserRecord = parseAuthUserRecord(decodedPayload.user_record); const authEventContext = parseAuthEventContext(decodedPayload, projectId); @@ -818,16 +871,7 @@ export function wrapHandler(eventType: AuthBlockingEventType, handler: HandlerV1 } validateAuthResponse(eventType, authResponse); - const updateMask = getUpdateMask(authResponse); - const result = - updateMask.length === 0 - ? {} - : { - userRecord: { - ...authResponse, - updateMask, - }, - }; + const result = generateResponsePayload(authResponse); res.status(200); res.setHeader("Content-Type", "application/json"); From eef988293ce6f8003304239444d5d28bd5eaf105 Mon Sep 17 00:00:00 2001 From: Liubin Jiang <56564857+Xiaoshouzi-gh@users.noreply.github.com> Date: Thu, 2 Nov 2023 08:18:58 -1000 Subject: [PATCH 04/42] Update CHANGELOG.md (#1480) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eab4161a8..4170a8e34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,3 +2,4 @@ - Add features to task queue functions. (#1423) - Add traces to V2 Firestore trigger logs. (#1440) - Fix incorrectly parsed timestamps in auth blocking functions. (#1472) +- Add recaptcha verdict support for auth blocking functions (#1458) From c07489ddd34b76876973829ef0669fdf9a992c7a Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 2 Nov 2023 18:23:27 +0000 Subject: [PATCH 05/42] 4.5.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e4c6631e9..5080967e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-functions", - "version": "4.4.1", + "version": "4.5.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "firebase-functions", - "version": "4.4.1", + "version": "4.5.0", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index 0c2c9b835..ba8b6d9ab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "4.4.1", + "version": "4.5.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From affa64d903bd81c702f8db2b3097cba4f2a18640 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 2 Nov 2023 18:23:30 +0000 Subject: [PATCH 06/42] [firebase-release] Removed change log and reset repo after 4.5.0 release --- CHANGELOG.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4170a8e34..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +0,0 @@ -- Remove HTTP server shutdown message. (#1457) -- Add features to task queue functions. (#1423) -- Add traces to V2 Firestore trigger logs. (#1440) -- Fix incorrectly parsed timestamps in auth blocking functions. (#1472) -- Add recaptcha verdict support for auth blocking functions (#1458) From 414ff0ac3b7d25f0ac022a277de6c50922de80bf Mon Sep 17 00:00:00 2001 From: egilmorez Date: Tue, 21 Nov 2023 13:48:33 -0800 Subject: [PATCH 07/42] Refreshing 1st gen reference and making some style and format tweaks. (#1490) --- src/common/providers/identity.ts | 35 ++++++++++++++++---------------- src/common/providers/tasks.ts | 4 ++-- src/v1/function-configuration.ts | 11 +++++----- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/common/providers/identity.ts b/src/common/providers/identity.ts index 374e4f8bd..5e9551bb7 100644 --- a/src/common/providers/identity.ts +++ b/src/common/providers/identity.ts @@ -63,18 +63,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) {} @@ -89,9 +90,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. @@ -157,7 +158,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 { /** @@ -237,7 +238,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 { /** @@ -334,7 +335,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; } @@ -344,7 +345,7 @@ export interface AuthBlockingEvent extends AuthEventContext { */ export type RecaptchaActionOptions = "ALLOW" | "BLOCK"; -/** The handler response type for beforeCreate blocking events */ +/** The handler response type for `beforeCreate` blocking events */ export interface BeforeCreateResponse { displayName?: string; disabled?: boolean; @@ -354,7 +355,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; } @@ -472,7 +473,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 { @@ -508,7 +509,7 @@ 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 { @@ -525,7 +526,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( @@ -546,7 +547,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 { @@ -639,7 +640,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; @@ -697,7 +698,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/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; From a64fd48fac12cd6293885ec8402a334037470c44 Mon Sep 17 00:00:00 2001 From: blidd-google <112491344+blidd-google@users.noreply.github.com> Date: Tue, 28 Nov 2023 17:02:57 -0500 Subject: [PATCH 08/42] Wrap 2nd gen onCall functions with trace context (#1491) * wrap oncall functions with trace context --- CHANGELOG.md | 1 + src/v2/providers/https.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..35533aa31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Wrap 2nd gen onCall functions with trace context. (#1491) diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 466160d62..178ee929a 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -366,7 +366,7 @@ export function onCall>( // onCallHandler sniffs the function length to determine which API to present. // fix the length to prevent api versions from being mismatched. const fixedLen = (req: CallableRequest) => handler(req); - const func: any = onCallHandler( + let func: any = onCallHandler( { cors: { origin, methods: "POST" }, enforceAppCheck: opts.enforceAppCheck ?? options.getGlobalOptions().enforceAppCheck, @@ -375,6 +375,8 @@ export function onCall>( fixedLen ); + func = wrapTraceContext(func); + Object.defineProperty(func, "__trigger", { get: () => { const baseOpts = options.optionsToTriggerAnnotations(options.getGlobalOptions()); From 1066b531e2a1deaae14886a6c6a38df40e98e9e0 Mon Sep 17 00:00:00 2001 From: Alexander Nohe Date: Wed, 3 Jan 2024 15:01:32 -0800 Subject: [PATCH 09/42] Update the supported Admin SDK version to include v12 (#1509) * update admin sdk version * Add changelog. --------- Co-authored-by: Daniel Lee --- CHANGELOG.md | 1 + package-lock.json | 841 ++++++++++++++++++++++++---------------------- package.json | 4 +- 3 files changed, 446 insertions(+), 400 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35533aa31..7d3e9726a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ - Wrap 2nd gen onCall functions with trace context. (#1491) +- Bump peer depdencies for firebase-admin to support 12.0.0. (#1509) diff --git a/package-lock.json b/package-lock.json index 5080967e7..a9403eaf5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,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.0.0", "js-yaml": "^3.13.1", "jsdom": "^16.2.1", "jsonwebtoken": "^9.0.0", @@ -66,7 +66,7 @@ "node": ">=14.10.0" }, "peerDependencies": { - "firebase-admin": "^10.0.0 || ^11.0.0" + "firebase-admin": "^10.0.0 || ^11.0.0 || ^12.0.0" } }, "node_modules/@babel/parser": { @@ -230,6 +230,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 +259,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 +274,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 +316,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 +342,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 +405,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 +419,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 +1090,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 +1148,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 +1197,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 +1246,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 +1295,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", @@ -1840,9 +1859,9 @@ "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": { @@ -3051,12 +3070,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,15 +3185,15 @@ } }, "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.0.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-12.0.0.tgz", + "integrity": "sha512-wBrrSSsKV++/+O8E7O/C7/wL0nbG/x4Xv4yatz/+sohaZ+LsnWtYUcrd3gZutO86hLpDex7xgyrkKbgulmtVyQ==", "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", + "@firebase/database-compat": "^1.0.2", + "@firebase/database-types": "^1.0.0", + "@types/node": "^20.10.3", "jsonwebtoken": "^9.0.0", "jwks-rsa": "^3.0.1", "node-forge": "^1.3.1", @@ -3168,8 +3203,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": { @@ -3277,33 +3321,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": { @@ -3402,91 +3473,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 +3526,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": { @@ -3814,13 +3837,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 +4268,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", @@ -5103,22 +5117,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 +5311,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", @@ -5506,17 +5515,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": { @@ -5929,6 +5940,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", @@ -5967,20 +5985,20 @@ "dev": true }, "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": { @@ -6285,6 +6303,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", @@ -6337,10 +6361,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" } @@ -6862,6 +6890,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 +6919,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 +6934,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 +6976,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 +7000,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 +7049,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 +7629,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 +7687,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 +7736,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 +7784,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 +7832,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", @@ -8183,9 +8229,9 @@ "optional": 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 }, @@ -9133,12 +9179,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 +9265,32 @@ } }, "firebase-admin": { - "version": "11.8.0", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-11.8.0.tgz", - "integrity": "sha512-RxO0wWDnuqVikXExhVjnhVaaXpziKCad4D1rOX5c1WJdk1jAu9hfE4rbrFKZQZgF1okZS04kgCBIFJro7xn8NQ==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-12.0.0.tgz", + "integrity": "sha512-wBrrSSsKV++/+O8E7O/C7/wL0nbG/x4Xv4yatz/+sohaZ+LsnWtYUcrd3gZutO86hLpDex7xgyrkKbgulmtVyQ==", "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", + "@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", "jsonwebtoken": "^9.0.0", "jwks-rsa": "^3.0.1", "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": { @@ -9313,26 +9373,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" } }, @@ -9405,74 +9488,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 +9535,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" } }, @@ -9719,13 +9765,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 +10135,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", @@ -10762,9 +10799,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 +10809,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 +10825,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": { @@ -11065,14 +11095,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": { @@ -11389,6 +11421,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", @@ -11418,15 +11457,15 @@ "dev": true }, "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" }, @@ -11652,6 +11691,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", @@ -11695,9 +11740,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 ba8b6d9ab..f3e760266 100644 --- a/package.json +++ b/package.json @@ -225,7 +225,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.0.0", "js-yaml": "^3.13.1", "jsdom": "^16.2.1", "jsonwebtoken": "^9.0.0", @@ -245,7 +245,7 @@ "yargs": "^15.3.1" }, "peerDependencies": { - "firebase-admin": "^10.0.0 || ^11.0.0" + "firebase-admin": "^10.0.0 || ^11.0.0 || ^12.0.0" }, "engines": { "node": ">=14.10.0" From 7af98c95451b367f2781b192f46b35b4ab6f3281 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 3 Jan 2024 23:31:32 +0000 Subject: [PATCH 10/42] 4.6.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a9403eaf5..68fa8f131 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-functions", - "version": "4.5.0", + "version": "4.6.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "firebase-functions", - "version": "4.5.0", + "version": "4.6.0", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index f3e760266..87cc3e560 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "4.5.0", + "version": "4.6.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From b6619358958faccdae554b20b34cdb729d4a2930 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 3 Jan 2024 23:31:35 +0000 Subject: [PATCH 11/42] [firebase-release] Removed change log and reset repo after 4.6.0 release --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d3e9726a..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +0,0 @@ -- Wrap 2nd gen onCall functions with trace context. (#1491) -- Bump peer depdencies for firebase-admin to support 12.0.0. (#1509) From e6f5d8952ee1a2029ccf9419b03e00a8bac60357 Mon Sep 17 00:00:00 2001 From: blidd-google <112491344+blidd-google@users.noreply.github.com> Date: Fri, 26 Jan 2024 13:30:05 -0500 Subject: [PATCH 12/42] Fixes access on deeply nested, nonexistent property (#1432) * return null on nonexistent deeply nested property * update changelog * check specifically for undefined in val() * fix lint issue --------- Co-authored-by: Cole Rogers --- CHANGELOG.md | 1 + spec/v1/providers/database.spec.ts | 1 + src/common/providers/database.ts | 3 +++ 3 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..e97214632 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Fixes access on deeply nested, nonexistent property. (#1432) 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/src/common/providers/database.ts b/src/common/providers/database.ts index 33250f4c0..1200cc73a 100644 --- a/src/common/providers/database.ts +++ b/src/common/providers/database.ts @@ -120,6 +120,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]; } } From 9bda8f793a21fea26916a3ba382d3d8a218c6abf Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Thu, 1 Feb 2024 16:45:20 -0500 Subject: [PATCH 13/42] Add IteratedDataSnapshot interface to match with firebase admin v12 (#1517) * add IteratedDataSnapshot interface to match with firebase admin v12 * rebase upstream --- CHANGELOG.md | 1 + src/common/providers/database.ts | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e97214632..0f5f3d22f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ - Fixes access on deeply nested, nonexistent property. (#1432) +- Add IteratedDataSnapshot interface to match with firebase admin v12 (#1517). diff --git a/src/common/providers/database.ts b/src/common/providers/database.ts index 1200cc73a..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. */ @@ -207,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); From 29fc7220ccb26e997c7f71e9a0aca3c2a8b01e5e Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 7 Feb 2024 07:32:12 -0800 Subject: [PATCH 14/42] Make storage bucket parameterizeable. Create const for bucket picker (#1518) * Make storage bucket parameterizeable. Create const for bucket picker * Fix linter error * Use more specific type * Remove unused import * Export types that were part of an old spec but are not available on main package * In for a penny, in for a pound * Clarify types of select/multiSelect * Changelog --- CHANGELOG.md | 2 + src/params/index.ts | 10 ++++ src/params/types.ts | 92 +++++++++++++++++++++++++++++-------- src/v2/providers/storage.ts | 51 ++++++++++++++------ 4 files changed, 121 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f5f3d22f..bc2ff3550 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,4 @@ - Fixes access on deeply nested, nonexistent property. (#1432) - Add IteratedDataSnapshot interface to match with firebase admin v12 (#1517). +- Make bucket parameterizeable in storage functions (#1518) +- Introduce helper library for select and multi-select input (#1518) diff --git a/src/params/index.ts b/src/params/index.ts index 550ab9db9..6f19c6d4f 100644 --- a/src/params/index.ts +++ b/src/params/index.ts @@ -38,6 +38,16 @@ import { InternalExpression, } from "./types"; +export { + BUCKET_PICKER, + TextInput, + SelectInput, + SelectOptions, + MultiSelectInput, + select, + multiSelect, +} from "./types"; + export { ParamOptions, Expression }; type SecretOrExpr = Param | SecretParam; diff --git a/src/params/types.ts b/src/params/types.ts index 1eaf553f2..ca6040059 100644 --- a/src/params/types.ts +++ b/src/params/types.ts @@ -171,11 +171,53 @@ 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 @@ -184,18 +226,20 @@ type ParamInput = */ // 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; + }; } /** @@ -205,16 +249,24 @@ export interface TextInput { */ export interface ResourceInput { resource: { - type: string; + type: "storage.googleapis.com/Bucket"; }; } +export const BUCKET_PICKER: ResourceInput = { + resource: { + 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. */ export interface SelectInput { - options: Array>; + select: { + options: Array>; + }; } /** @@ -223,7 +275,9 @@ export interface SelectInput { * Will result in errors if used on Params of type other than string[]. */ export interface MultiSelectInput { - options: Array>; + multiSelect: { + options: Array>; + }; } /** diff --git a/src/v2/providers/storage.ts b/src/v2/providers/storage.ts index 451a257b5..e66f6b813 100644 --- a/src/v2/providers/storage.ts +++ b/src/v2/providers/storage.ts @@ -201,7 +201,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 +324,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 +352,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 +388,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 +420,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 +458,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 +492,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 +521,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 +545,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 +558,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") { @@ -616,11 +636,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 +656,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}`); } From b1090a527aebaf736ab942854cc2e7264658935a Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 7 Feb 2024 15:38:56 +0000 Subject: [PATCH 15/42] 4.7.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 68fa8f131..deda9f155 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-functions", - "version": "4.6.0", + "version": "4.7.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "firebase-functions", - "version": "4.6.0", + "version": "4.7.0", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index 87cc3e560..5fa70a2a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "4.6.0", + "version": "4.7.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 26cd955d174f550d5e2aedaf3a86641585510864 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 7 Feb 2024 15:39:07 +0000 Subject: [PATCH 16/42] [firebase-release] Removed change log and reset repo after 4.7.0 release --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc2ff3550..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +0,0 @@ -- Fixes access on deeply nested, nonexistent property. (#1432) -- Add IteratedDataSnapshot interface to match with firebase admin v12 (#1517). -- Make bucket parameterizeable in storage functions (#1518) -- Introduce helper library for select and multi-select input (#1518) From 5a94583e42a4263f131dcd78a84af6899a054e94 Mon Sep 17 00:00:00 2001 From: egilmorez Date: Wed, 7 Feb 2024 19:22:53 -0800 Subject: [PATCH 17/42] Revisions to doc comments related to env. config. (#1520) --- src/params/index.ts | 58 +++++++++++++++++++++---------------------- src/params/types.ts | 60 ++++++++++++++++++++++++--------------------- 2 files changed, 61 insertions(+), 57 deletions(-) diff --git a/src/params/index.ts b/src/params/index.ts index 6f19c6d4f..15ae9ebd7 100644 --- a/src/params/index.ts +++ b/src/params/index.ts @@ -54,7 +54,7 @@ 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 */ @@ -76,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( @@ -84,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( @@ -92,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( @@ -100,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. */ @@ -111,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); @@ -125,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); @@ -138,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); @@ -151,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); @@ -164,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 */ @@ -179,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 ca6040059..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 @@ -220,8 +221,8 @@ type ParamInput = | (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 @@ -243,7 +244,7 @@ export interface TextInput { } /** - * 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. */ @@ -253,6 +254,9 @@ export interface ResourceInput { }; } +/** + * 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", @@ -260,8 +264,8 @@ export const BUCKET_PICKER: ResourceInput = { }; /** - * 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. + * 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 { select: { @@ -270,9 +274,9 @@ export interface SelectInput { } /** - * 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 { multiSelect: { @@ -281,7 +285,7 @@ export interface MultiSelectInput { } /** - * 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 { @@ -289,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; }; @@ -322,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" @@ -345,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) { @@ -474,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 */ From c260c2a7d6a0010b540591ece2bfd4a5bd552132 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Thu, 7 Mar 2024 14:18:07 -0800 Subject: [PATCH 18/42] Add startup lifecycle hook (#1531) * checkpoint * Finish missing tests; remove redundant v1 tests * Fix linter error * Fix import error * Add changelog; fix exports of v2/core --- CHANGELOG.md | 1 + package.json | 5 +- spec/v1/cloud-functions.spec.ts | 29 ++++++++ spec/v1/providers/analytics.spec.ts | 4 +- spec/v1/providers/https.spec.ts | 45 +++++++++++- spec/v1/providers/remoteConfig.spec.ts | 14 ++-- spec/v2/providers/alerts/alerts.spec.ts | 19 ++++- .../providers/alerts/appDistribution.spec.ts | 21 ++++++ spec/v2/providers/alerts/billing.spec.ts | 31 ++++++++ spec/v2/providers/alerts/crashlytics.spec.ts | 11 +++ spec/v2/providers/alerts/performance.spec.ts | 18 +++++ spec/v2/providers/database.spec.ts | 69 ++++++++++++++++++ spec/v2/providers/eventarc.spec.ts | 18 +++++ spec/v2/providers/firestore.spec.ts | 73 ++++++++++++++++++- spec/v2/providers/https.spec.ts | 48 +++++++++++- spec/v2/providers/identity.spec.ts | 45 ++++++++++++ spec/v2/providers/pubsub.spec.ts | 4 +- spec/v2/providers/remoteConfig.spec.ts | 26 ++++++- spec/v2/providers/scheduler.spec.ts | 24 ++++++ spec/v2/providers/storage.spec.ts | 69 ++++++++++++++++++ spec/v2/providers/tasks.spec.ts | 22 ++++++ spec/v2/providers/testLab.spec.ts | 18 +++++ src/common/onInit.ts | 39 ++++++++++ src/v1/cloud-functions.ts | 2 + src/v1/index.ts | 2 + src/v1/providers/https.ts | 26 ++++--- src/v2/core.ts | 1 + src/v2/index.ts | 2 +- src/v2/providers/alerts/alerts.ts | 3 +- src/v2/providers/alerts/appDistribution.ts | 5 +- src/v2/providers/alerts/billing.ts | 3 +- src/v2/providers/alerts/crashlytics.ts | 3 +- src/v2/providers/alerts/performance.ts | 4 +- src/v2/providers/database.ts | 7 +- src/v2/providers/eventarc.ts | 3 +- src/v2/providers/firestore.ts | 5 +- src/v2/providers/https.ts | 14 ++-- src/v2/providers/identity.ts | 3 +- src/v2/providers/pubsub.ts | 3 +- src/v2/providers/remoteConfig.ts | 10 ++- src/v2/providers/scheduler.ts | 3 +- src/v2/providers/storage.ts | 3 +- src/v2/providers/tasks.ts | 3 +- src/v2/providers/testLab.ts | 3 +- 44 files changed, 702 insertions(+), 59 deletions(-) create mode 100644 src/common/onInit.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..129f480b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +Add onInit callback function for global variable initialization (#1531) diff --git a/package.json b/package.json index 5fa70a2a5..771cc0aea 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,9 @@ "v2": [ "lib/v2" ], + "v2/core": [ + "lib/v2/core" + ], "v2/alerts": [ "lib/v2/providers/alerts" ], @@ -250,4 +253,4 @@ "engines": { "node": ">=14.10.0" } -} +} \ No newline at end of file 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/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..7e734c671 100644 --- a/spec/v2/providers/firestore.spec.ts +++ b/spec/v2/providers/firestore.spec.ts @@ -25,6 +25,7 @@ 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"; /** static-complied protobuf */ const DocumentEventData = google.events.cloud.firestore.v1.DocumentEventData; @@ -39,9 +40,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", }; @@ -192,6 +193,23 @@ describe("firestore", () => { 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.onDocumentWritten("path", () => null)(event); + expect(hello).to.equal("world"); + }); }); describe("onDocumentCreated", () => { @@ -239,6 +257,23 @@ describe("firestore", () => { 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.onDocumentCreated("type", () => null)(event); + expect(hello).to.equal("world"); + }); }); describe("onDocumentUpdated", () => { @@ -286,6 +321,23 @@ describe("firestore", () => { 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.onDocumentUpdated("path", () => null)(event); + expect(hello).to.equal("world"); + }); }); describe("onDocumentDeleted", () => { @@ -333,6 +385,23 @@ describe("firestore", () => { 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.onDocumentDeleted("path", () => null)(event); + expect(hello).to.equal("world"); + }); }); describe("getOpts", () => { 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/v1/cloud-functions.ts b/src/v1/cloud-functions.ts index 7909fc10d..d5bd3d015 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"; @@ -403,6 +404,7 @@ export function makeCloudFunction({ context.params = context.params || _makeParams(context, triggerResource); } + handler = withInit(handler); let promise; if (labels && labels["deployment-scheduled"]) { // Scheduled function do not have meaningful data, so exclude it 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..dd3c461ff 100644 --- a/src/v2/providers/firestore.ts +++ b/src/v2/providers/firestore.ts @@ -35,6 +35,7 @@ import { createSnapshotFromProtobuf, } from "../../common/providers/firestore"; import { wrapTraceContext } from "../trace"; +import { withInit } from "../../common/onInit"; export { Change }; @@ -446,7 +447,7 @@ export function onOperation( const event = raw as RawFirestoreEvent; 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; @@ -473,7 +474,7 @@ export function onChangedOperation( const event = raw as RawFirestoreEvent; 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; diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 178ee929a..b7448eb65 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 }; @@ -276,7 +277,7 @@ export function onRequest( }; } - handler = wrapTraceContext(handler); + handler = wrapTraceContext(withInit(handler)); Object.defineProperty(handler, "__trigger", { get: () => { @@ -340,7 +341,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 +350,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 = {}; @@ -365,7 +367,7 @@ export function onCall>( // onCallHandler sniffs the function length to determine which API to present. // fix the length to prevent api versions from being mismatched. - const fixedLen = (req: CallableRequest) => handler(req); + const fixedLen = (req: CallableRequest) => withInit(handler)(req); let func: any = onCallHandler( { cors: { origin, methods: "POST" }, @@ -415,6 +417,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 3a0b1b7fc..aa93edc2f 100644 --- a/src/v2/providers/identity.ts +++ b/src/v2/providers/identity.ts @@ -40,6 +40,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 }; @@ -282,7 +283,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 e66f6b813..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. @@ -573,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; 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; From 78afcc109f6ee14a117da7f96379b054461342f1 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 8 Mar 2024 01:18:38 +0000 Subject: [PATCH 19/42] 4.8.0 --- package-lock.json | 4 ++-- package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index deda9f155..87bf870f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-functions", - "version": "4.7.0", + "version": "4.8.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "firebase-functions", - "version": "4.7.0", + "version": "4.8.0", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index 771cc0aea..cc37b3410 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "4.7.0", + "version": "4.8.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", @@ -253,4 +253,4 @@ "engines": { "node": ">=14.10.0" } -} \ No newline at end of file +} From 1ddebc2e73dba24223df80538f0774e62f0d149d Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 8 Mar 2024 01:18:42 +0000 Subject: [PATCH 20/42] [firebase-release] Removed change log and reset repo after 4.8.0 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 129f480b9..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -Add onInit callback function for global variable initialization (#1531) From 3d8d59511dabd19de2709a0bfdfe8cc662265bcd Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 13 Mar 2024 14:40:56 -0700 Subject: [PATCH 21/42] Parameterize db info for firestore events (#1538) --- spec/v2/providers/firestore.spec.ts | 111 +++++++++++++++++++++++++++- src/common/params.ts | 15 +++- src/v2/providers/firestore.ts | 52 +++++++------ 3 files changed, 152 insertions(+), 26 deletions(-) diff --git a/spec/v2/providers/firestore.spec.ts b/spec/v2/providers/firestore.spec.ts index 7e734c671..e672703a6 100644 --- a/spec/v2/providers/firestore.spec.ts +++ b/spec/v2/providers/firestore.spec.ts @@ -26,6 +26,7 @@ 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; @@ -148,6 +149,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( @@ -194,6 +209,29 @@ describe("firestore", () => { 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, @@ -258,6 +296,29 @@ describe("firestore", () => { 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, @@ -322,6 +383,29 @@ describe("firestore", () => { 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, @@ -386,6 +470,29 @@ describe("firestore", () => { 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, @@ -663,7 +770,7 @@ describe("firestore", () => { const ep = firestore.makeEndpoint( firestore.createdEventType, { region: "us-central1" }, - new PathPattern("foo/{bar}"), + "foo/{bar}", "my-db", "my-ns" ); @@ -686,7 +793,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/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/v2/providers/firestore.ts b/src/v2/providers/firestore.ts index dd3c461ff..cc78aa87f 100644 --- a/src/v2/providers/firestore.ts +++ b/src/v2/providers/firestore.ts @@ -36,6 +36,7 @@ import { } from "../../common/providers/firestore"; import { wrapTraceContext } from "../trace"; import { withInit } from "../../common/onInit"; +import { Expression } from "../../params"; export { Change }; @@ -106,11 +107,11 @@ export interface FirestoreEvent> extends Clou /** 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; } /** @@ -278,9 +279,9 @@ export function onDocumentDeleted( /** @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); @@ -288,7 +289,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 }; @@ -398,21 +402,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), @@ -440,11 +448,12 @@ export function onOperation( ): 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 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(withInit(handler))(firestoreEvent); @@ -452,7 +461,7 @@ export function onOperation( func.run = handler; - func.__endpoint = makeEndpoint(eventType, opts, documentPattern, database, namespace); + func.__endpoint = makeEndpoint(eventType, opts, document, database, namespace); return func; } @@ -467,11 +476,12 @@ export function onChangedOperation( ): CloudFunction, ParamsOf>> { 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 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 wrapTraceContext(withInit(handler))(firestoreEvent); @@ -479,7 +489,7 @@ export function onChangedOperation( func.run = handler; - func.__endpoint = makeEndpoint(eventType, opts, documentPattern, database, namespace); + func.__endpoint = makeEndpoint(eventType, opts, document, database, namespace); return func; } From 0aaedc45859119085bd8c3332c201943fb9e3462 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Tue, 19 Mar 2024 08:28:47 -0700 Subject: [PATCH 22/42] CORS origins of an array of one are made a scalar. (#1536) The access-control-allowed-origins CORS header only allows a single origin or "*" as its response. To support multiple origins, the cors middleware makes this header dynamic based on the origin header of the request when the middleware is configured with anything but a single string. To help avoid a few edge cases customers may encounter, we can unwrap an array of one element into a scalar to encourage the cors middleware to make the access-control-allowed-origin header static. As a very minor performance boost, this change also instantiates the cors middleware once and uses it on all requests rather than constructing it dynamically within a request. --- src/v2/providers/https.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index b7448eb65..54eee861e 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -265,12 +265,19 @@ 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[1]; + } + 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)); }); }); @@ -363,7 +370,13 @@ 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. From ad6cd43e53a7ef062f44c96af863a4de3fa636ee Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Tue, 19 Mar 2024 11:00:16 -0700 Subject: [PATCH 23/42] Remove infinite wrapping of withInit (#1540) * Remove infinite wrapping of withInit * Changelog --- CHANGELOG.md | 2 ++ src/v1/cloud-functions.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..6bacbe4c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,2 @@ +Fix bug where 1st gen functions eventually fail with stack too deep (#1540) +Make simple CORS options static for improved debugability (#1536) diff --git a/src/v1/cloud-functions.ts b/src/v1/cloud-functions.ts index d5bd3d015..75921ab6f 100644 --- a/src/v1/cloud-functions.ts +++ b/src/v1/cloud-functions.ts @@ -367,6 +367,7 @@ export function makeCloudFunction({ service, triggerResource, }: MakeCloudFunctionArgs): CloudFunction { + handler = withInit(handler); const cloudFunction: any = (data: any, context: any) => { if (legacyEventType && context.eventType === legacyEventType) { /* @@ -404,7 +405,6 @@ export function makeCloudFunction({ context.params = context.params || _makeParams(context, triggerResource); } - handler = withInit(handler); let promise; if (labels && labels["deployment-scheduled"]) { // Scheduled function do not have meaningful data, so exclude it From 536f8debeec4552264d8f6a7a12f10293c42aba9 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Tue, 19 Mar 2024 18:05:51 +0000 Subject: [PATCH 24/42] 4.8.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 87bf870f4..aaad08264 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-functions", - "version": "4.8.0", + "version": "4.8.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "firebase-functions", - "version": "4.8.0", + "version": "4.8.1", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index cc37b3410..d37dc16af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "4.8.0", + "version": "4.8.1", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 5cc6fdd75124909f9e504c1ca8da8b0af18ff081 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Tue, 19 Mar 2024 18:05:55 +0000 Subject: [PATCH 25/42] [firebase-release] Removed change log and reset repo after 4.8.1 release --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bacbe4c5..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +0,0 @@ -Fix bug where 1st gen functions eventually fail with stack too deep (#1540) -Make simple CORS options static for improved debugability (#1536) From f13360a695c84b42ef81aa15516c0ee649ddeb88 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 27 Mar 2024 07:58:59 -0700 Subject: [PATCH 26/42] Fix bug introduced in #1536 (#1544) * Fix bug introduced in #1536 * Changelog * formatter --- CHANGELOG.md | 1 + src/v2/providers/https.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..b8b38eeab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +Fix bug with CORS options for an array of one string (#1544) diff --git a/src/v2/providers/https.ts b/src/v2/providers/https.ts index 54eee861e..311323f46 100644 --- a/src/v2/providers/https.ts +++ b/src/v2/providers/https.ts @@ -269,7 +269,7 @@ export function onRequest( // 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]; + origin = origin[0]; } const middleware = cors({ origin }); From 23510f70b8b88632618c88cd5cc0e9fe08b6a693 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 27 Mar 2024 11:48:26 -0700 Subject: [PATCH 27/42] Remove unnecessary strict dep on node-fetch (#1543) --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index d37dc16af..9f4b65e2d 100644 --- a/package.json +++ b/package.json @@ -201,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": { From b71fea9839adfbd01875ea24baa380513008c890 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 27 Mar 2024 15:54:25 -0700 Subject: [PATCH 28/42] Fix regresssion in v1 functions with only context. (#1545) Fixes #1541 --- src/v1/cloud-functions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v1/cloud-functions.ts b/src/v1/cloud-functions.ts index 75921ab6f..d66539fb7 100644 --- a/src/v1/cloud-functions.ts +++ b/src/v1/cloud-functions.ts @@ -367,7 +367,7 @@ export function makeCloudFunction({ service, triggerResource, }: MakeCloudFunctionArgs): CloudFunction { - handler = withInit(handler); + handler = withInit(handler ?? contextOnlyHandler); const cloudFunction: any = (data: any, context: any) => { if (legacyEventType && context.eventType === legacyEventType) { /* From 010ee5233bcc89d185fbba7e2562a267f36f0cdd Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 29 Mar 2024 15:03:48 +0000 Subject: [PATCH 29/42] 4.8.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index aaad08264..6a51af0a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-functions", - "version": "4.8.1", + "version": "4.8.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "firebase-functions", - "version": "4.8.1", + "version": "4.8.2", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index 9f4b65e2d..2efbd6628 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "4.8.1", + "version": "4.8.2", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 0f1e8f3481804cf5477f915c3ee7c4033b881b81 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 29 Mar 2024 15:03:52 +0000 Subject: [PATCH 30/42] [firebase-release] Removed change log and reset repo after 4.8.2 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8b38eeab..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -Fix bug with CORS options for an array of one string (#1544) From 53f720447d2160e980750469e0d54612bd4d81cd Mon Sep 17 00:00:00 2001 From: blidd-google <112491344+blidd-google@users.noreply.github.com> Date: Thu, 4 Apr 2024 13:18:47 -0400 Subject: [PATCH 31/42] Add new 2nd gen Firestore auth context triggers (#1519) * add changelog * update docstrings * export authtype and add comment * separate out event type with auth context * Ugly type safety (#1548) * consolidate make firestore event fns and simplify typings --------- Co-authored-by: Brian Li * add oninit tests --------- Co-authored-by: Thomas Bouldin --- CHANGELOG.md | 1 + package-lock.json | 1 - spec/v2/providers/firestore.spec.ts | 294 +++++++++++++++++++++++++++ src/v2/providers/firestore.ts | 302 +++++++++++++++++++++++++--- 4 files changed, 570 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..fdbf13bda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Add new 2nd gen Firestore auth context triggers. (#1519) diff --git a/package-lock.json b/package-lock.json index 6a51af0a1..9f23b098b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,6 @@ "@types/express": "4.17.3", "cors": "^2.8.5", "express": "^4.17.1", - "node-fetch": "^2.6.7", "protobufjs": "^7.2.2" }, "bin": { diff --git a/spec/v2/providers/firestore.spec.ts b/spec/v2/providers/firestore.spec.ts index e672703a6..e5406bdb3 100644 --- a/spec/v2/providers/firestore.spec.ts +++ b/spec/v2/providers/firestore.spec.ts @@ -84,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: { @@ -511,6 +520,262 @@ describe("firestore", () => { }); }); + 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", () => { it("should handle document string", () => { const { document, database, namespace, opts } = firestore.getOpts("foo/{bar}"); @@ -720,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", () => { @@ -753,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( diff --git a/src/v2/providers/firestore.ts b/src/v2/providers/firestore.ts index cc78aa87f..1d8b823a1 100644 --- a/src/v2/providers/firestore.ts +++ b/src/v2/providers/firestore.ts @@ -52,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 { @@ -79,12 +95,28 @@ export interface RawFirestoreEvent extends CloudEvent */ export interface FirestoreEvent> extends CloudEvent { /** The location of the Firestore instance */ @@ -104,6 +136,14 @@ 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 */ @@ -115,7 +155,7 @@ export interface DocumentOptions extends Event } /** - * 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. @@ -128,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. @@ -141,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. @@ -156,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. @@ -169,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. @@ -182,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. @@ -197,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. @@ -209,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. @@ -222,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. @@ -237,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. @@ -250,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. @@ -263,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. @@ -277,6 +449,49 @@ 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 | Expression; @@ -362,11 +577,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; @@ -375,16 +592,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; @@ -395,6 +627,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; } @@ -441,16 +685,19 @@ 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); // 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() ); @@ -467,18 +714,19 @@ export function onOperation( } /** @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); // 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() ); From 482529ab3674bfdd22ba298f691c7f00f1f949b5 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 4 Apr 2024 20:06:31 +0000 Subject: [PATCH 32/42] 4.9.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9f23b098b..cfedbb2b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-functions", - "version": "4.8.2", + "version": "4.9.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "firebase-functions", - "version": "4.8.2", + "version": "4.9.0", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index 2efbd6628..29f4704a2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "4.8.2", + "version": "4.9.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From c89994f4cacfe5b3b7009e3f0a26a7a3484cbb5e Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Thu, 4 Apr 2024 20:06:35 +0000 Subject: [PATCH 33/42] [firebase-release] Removed change log and reset repo after 4.9.0 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdbf13bda..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Add new 2nd gen Firestore auth context triggers. (#1519) From 6beaaebda53345dca2712269dac9ef2ca8f8affa Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Fri, 12 Apr 2024 15:15:27 -0400 Subject: [PATCH 34/42] Add option to get named firestore instance for v2 firestore functions (#1550) * adding option to get named firestore instance for v2 firestore functions * add changelog entry --- CHANGELOG.md | 1 + src/common/providers/firestore.ts | 27 +++++++++++++++++++-------- src/v2/providers/firestore.ts | 14 ++++++++++---- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..ac93c3fe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Add option to get named firestore instance for v2 firestore functions (#1550). diff --git a/src/common/providers/firestore.ts b/src/common/providers/firestore.ts index 0f2e8b621..c4e6d7821 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(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(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(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(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/v2/providers/firestore.ts b/src/v2/providers/firestore.ts index 1d8b823a1..f351cc755 100644 --- a/src/v2/providers/firestore.ts +++ b/src/v2/providers/firestore.ts @@ -532,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( @@ -551,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( From 909215f5bfd7ee74b90fc1ae08773b1d2fab0a79 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Sat, 13 Apr 2024 08:26:34 -0400 Subject: [PATCH 35/42] bumping ts version (#1552) --- integration_test/package.json.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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__" From 29bf78fb98d60a918aab59ef21fc723c952246a7 Mon Sep 17 00:00:00 2001 From: blidd-google <112491344+blidd-google@users.noreply.github.com> Date: Thu, 25 Apr 2024 15:38:02 -0400 Subject: [PATCH 36/42] bump firebase-admin version (#1555) --- package-lock.json | 618 ++++++++++++++++++++++++++++++++++++++++++---- package.json | 4 +- 2 files changed, 571 insertions(+), 51 deletions(-) diff --git a/package-lock.json b/package-lock.json index cfedbb2b7..0050aaef4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "eslint-config-prettier": "^8.3.0", "eslint-plugin-jsdoc": "^39.2.9", "eslint-plugin-prettier": "^4.0.0", - "firebase-admin": "^12.0.0", + "firebase-admin": "^12.1.0", "js-yaml": "^3.13.1", "jsdom": "^16.2.1", "jsonwebtoken": "^9.0.0", @@ -65,7 +65,7 @@ "node": ">=14.10.0" }, "peerDependencies": { - "firebase-admin": "^10.0.0 || ^11.0.0 || ^12.0.0" + "firebase-admin": "^11.10.0 || ^12.0.0" } }, "node_modules/@babel/parser": { @@ -181,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" } @@ -1854,8 +1851,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "optional": true + ] }, "node_modules/bignumber.js": { "version": "9.1.2", @@ -1876,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", @@ -1964,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", @@ -2128,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", @@ -2346,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", @@ -2358,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", @@ -2390,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", @@ -2505,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" } @@ -2956,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", @@ -3017,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", @@ -3184,17 +3276,19 @@ } }, "node_modules/firebase-admin": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-12.0.0.tgz", - "integrity": "sha512-wBrrSSsKV++/+O8E7O/C7/wL0nbG/x4Xv4yatz/+sohaZ+LsnWtYUcrd3gZutO86hLpDex7xgyrkKbgulmtVyQ==", + "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", + "@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" }, @@ -3273,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", @@ -3404,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", @@ -3670,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", @@ -3728,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", @@ -4441,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", @@ -4486,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", @@ -4704,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", @@ -4770,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", @@ -5064,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", @@ -5334,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", @@ -5414,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", @@ -5766,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", @@ -5887,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" } @@ -5983,6 +6260,34 @@ "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": "9.0.0", "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", @@ -6025,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", @@ -6220,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", @@ -6348,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", @@ -6848,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", @@ -8224,8 +8531,7 @@ "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.2", @@ -8240,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", @@ -8323,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", @@ -8442,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", @@ -8615,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", @@ -8624,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", @@ -8646,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", @@ -8745,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" } @@ -9070,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", @@ -9130,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", @@ -9264,19 +9633,21 @@ } }, "firebase-admin": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-12.0.0.tgz", - "integrity": "sha512-wBrrSSsKV++/+O8E7O/C7/wL0nbG/x4Xv4yatz/+sohaZ+LsnWtYUcrd3gZutO86hLpDex7xgyrkKbgulmtVyQ==", + "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": "^1.2.1", + "@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" }, @@ -9335,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", @@ -9440,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", @@ -9646,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", @@ -9689,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", @@ -10276,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", @@ -10312,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", @@ -10484,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", @@ -10546,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", @@ -10764,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", @@ -10964,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", @@ -11015,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", @@ -11278,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", @@ -11383,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" } @@ -11455,6 +11948,31 @@ "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": "9.0.0", "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", @@ -11490,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", @@ -11636,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", @@ -11730,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", diff --git a/package.json b/package.json index 29f4704a2..8be876630 100644 --- a/package.json +++ b/package.json @@ -227,7 +227,7 @@ "eslint-config-prettier": "^8.3.0", "eslint-plugin-jsdoc": "^39.2.9", "eslint-plugin-prettier": "^4.0.0", - "firebase-admin": "^12.0.0", + "firebase-admin": "^12.1.0", "js-yaml": "^3.13.1", "jsdom": "^16.2.1", "jsonwebtoken": "^9.0.0", @@ -247,7 +247,7 @@ "yargs": "^15.3.1" }, "peerDependencies": { - "firebase-admin": "^10.0.0 || ^11.0.0 || ^12.0.0" + "firebase-admin": "^11.10.0 || ^12.0.0" }, "engines": { "node": ">=14.10.0" From e7c72f3dae828baf780cc2fd5cb8b50ce7dd8e56 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Wed, 1 May 2024 14:26:18 -0400 Subject: [PATCH 37/42] Add changelog entry for removing admin v10 dep (#1560) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac93c3fe2..df0ec3c23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ - Add option to get named firestore instance for v2 firestore functions (#1550). +- Remove firebase-admin v10 dependency for Firestore triggers multi-DB support (#1555). From f59cd1ac6f83f346887f3a35ca1cf6fcbef561a7 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 1 May 2024 18:39:34 +0000 Subject: [PATCH 38/42] 5.0.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0050aaef4..8ee1f7e3f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-functions", - "version": "4.9.0", + "version": "5.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "firebase-functions", - "version": "4.9.0", + "version": "5.0.0", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index 8be876630..831214e4a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "4.9.0", + "version": "5.0.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 77c16ff131b218fe82008d5c202a3d378080f576 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Wed, 1 May 2024 18:39:42 +0000 Subject: [PATCH 39/42] [firebase-release] Removed change log and reset repo after 5.0.0 release --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df0ec3c23..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +0,0 @@ -- Add option to get named firestore instance for v2 firestore functions (#1550). -- Remove firebase-admin v10 dependency for Firestore triggers multi-DB support (#1555). From 680849eef8a4db6018e8e3bfce130cc23981c1f3 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Fri, 3 May 2024 10:49:17 -0400 Subject: [PATCH 40/42] Fix: get the app when fetching named firestore (#1562) * get the app when fetching named firestore * add changelog --- CHANGELOG.md | 1 + src/common/providers/firestore.ts | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..b011bf5fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Fix App fetching for named firestore instances (#1562). diff --git a/src/common/providers/firestore.ts b/src/common/providers/firestore.ts index c4e6d7821..8a74b588a 100644 --- a/src/common/providers/firestore.ts +++ b/src/common/providers/firestore.ts @@ -54,7 +54,7 @@ function _getValueProto(data: any, resource: string, valueFieldName: string) { /** @internal */ export function createSnapshotFromProtobuf(data: Uint8Array, path: string, databaseId: string) { if (!firestoreInstance) { - firestoreInstance = firestore.getFirestore(databaseId); + firestoreInstance = firestore.getFirestore(getApp(), databaseId); } try { const dataBuffer = Buffer.from(data); @@ -74,7 +74,7 @@ export function createBeforeSnapshotFromProtobuf( databaseId: string ) { if (!firestoreInstance) { - firestoreInstance = firestore.getFirestore(databaseId); + firestoreInstance = firestore.getFirestore(getApp(), databaseId); } try { const dataBuffer = Buffer.from(data); @@ -97,7 +97,7 @@ export function createSnapshotFromJson( ) { if (!firestoreInstance) { firestoreInstance = databaseId - ? firestore.getFirestore(databaseId) + ? firestore.getFirestore(getApp(), databaseId) : firestore.getFirestore(getApp()); } const valueProto = _getValueProto(data, source, "value"); @@ -122,7 +122,7 @@ export function createBeforeSnapshotFromJson( ) { if (!firestoreInstance) { firestoreInstance = databaseId - ? firestore.getFirestore(databaseId) + ? firestore.getFirestore(getApp(), databaseId) : firestore.getFirestore(getApp()); } From 80c5a3775baca703cafc62cabd79b604a73dadd1 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 3 May 2024 15:06:49 +0000 Subject: [PATCH 41/42] 5.0.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8ee1f7e3f..d311b8583 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-functions", - "version": "5.0.0", + "version": "5.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "firebase-functions", - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", diff --git a/package.json b/package.json index 831214e4a..258be26ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "5.0.0", + "version": "5.0.1", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 9c818713db511895a33378859ab1b9f2eef99179 Mon Sep 17 00:00:00 2001 From: Google Open Source Bot Date: Fri, 3 May 2024 15:06:53 +0000 Subject: [PATCH 42/42] [firebase-release] Removed change log and reset repo after 5.0.1 release --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b011bf5fd..e69de29bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +0,0 @@ -- Fix App fetching for named firestore instances (#1562).