From 9173c25c7281719fc918242a01b86b920bad9881 Mon Sep 17 00:00:00 2001 From: Jacob Cable Date: Mon, 7 Sep 2026 16:07:09 +0100 Subject: [PATCH 1/3] chore(firestore-bigquery-change-tracker): release 2.1.2, run on firebase-admin 14 firebase-admin 14 removes the namespaced API. The package still read admin.apps at import and admin.firestore.Timestamp in the partition converter, so it crashed on 14 despite 2.1.1 accepting it. Every use is now the modular API, which works on 13 and 14. firebase-functions 6 peer-pins firebase-admin to 13, so a consumer on 14 kept a second nested admin copy under this package however wide its own range was. The range now accepts firebase-functions 7 as well; the package only uses its logger. --- .../firestore-bigquery-change-tracker/CHANGELOG.md | 6 ++++++ .../package-lock.json | 6 +++--- .../firestore-bigquery-change-tracker/package.json | 4 ++-- .../src/__tests__/bigquery/e2e.test.ts | 14 +++++++------- .../__tests__/bigquery/failedTransaction.test.ts | 9 ++++++--- .../initializeLatestMaterializedView.test.ts | 6 +++--- .../bigquery/materializedViews/integration.test.ts | 10 +++++----- .../shouldRecreateMaterializedView.test.ts | 10 +++++----- .../src/__tests__/bigquery/partitioning.test.ts | 14 ++++++-------- .../bigquery/partitioning/converter.test.ts | 14 ++++---------- .../src/__tests__/fixtures/changeTracker.ts | 4 ++-- .../src/bigquery/handleFailedTransactions.ts | 5 ++--- .../src/bigquery/partitioning/converter.ts | 9 +++------ .../firestore-bigquery-change-tracker/src/logs.ts | 4 ++-- 14 files changed, 56 insertions(+), 59 deletions(-) diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/CHANGELOG.md b/firestore-bigquery-export/firestore-bigquery-change-tracker/CHANGELOG.md index f9e5809490..040715142a 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/CHANGELOG.md +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.1.2 + +- Works on `firebase-admin` 14. 2.1.1 widened the range but the package still used the namespaced API (`admin.apps`, `admin.firestore.Timestamp`), which firebase-admin 14 removes, so it threw at import wherever it was resolved against admin 14. Every use is now the modular API from `firebase-admin/app` and `firebase-admin/firestore`, which works on 13 and 14 alike. +- `firebase-functions` 7 is accepted alongside 6. This package only uses its `logger`, but the firebase-functions 6 peer range for `firebase-admin` stops at 13, so on a consumer running firebase-admin 14 npm nested a second admin copy under this package however wide this package's own range was, and that nested copy has no default app. +- Consumers that pin neither `firebase-admin` nor `firebase-functions` themselves and resolve fresh now get firebase-admin 14 and firebase-functions 7, which need Node 22 and Node 18 respectively. Pin firebase-admin `^13` to stay on Node 18 or 20. + ## 2.1.1 - `firebase-admin` 14 is accepted alongside 13. With the previous `^13.2.0` range a consumer on 14 installed a second copy of the SDK under this package, and that copy never sees the consumer's `initializeApp()`, so every backup write to `backupTableId` failed with "The default Firebase app does not exist" and the rows were lost. diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/package-lock.json b/firestore-bigquery-export/firestore-bigquery-change-tracker/package-lock.json index b52fb114e5..c999b6a851 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/package-lock.json +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/package-lock.json @@ -1,18 +1,18 @@ { "name": "@firebaseextensions/firestore-bigquery-change-tracker", - "version": "2.1.1", + "version": "2.1.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@firebaseextensions/firestore-bigquery-change-tracker", - "version": "2.1.1", + "version": "2.1.2", "license": "Apache-2.0", "dependencies": { "@google-cloud/bigquery": "^7.6.0", "@google-cloud/resource-manager": "^5.1.0", "firebase-admin": "^13.2.0 || ^14.0.0", - "firebase-functions": "^6.3.2", + "firebase-functions": "^6.3.2 || ^7.0.0", "sql-formatter": "^2.3.3", "traverse": "^0.6.6" }, diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/package.json b/firestore-bigquery-export/firestore-bigquery-change-tracker/package.json index 4758ea657e..2f0d9f8b57 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/package.json +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/package.json @@ -5,7 +5,7 @@ "url": "git+https://github.com/firebase/extensions.git", "directory": "firestore-bigquery-export/firestore-bigquery-change-tracker" }, - "version": "2.1.1", + "version": "2.1.2", "description": "Core change-tracker library for Cloud Firestore Collection BigQuery Exports", "main": "./lib/index.js", "scripts": { @@ -26,7 +26,7 @@ "@google-cloud/bigquery": "^7.6.0", "@google-cloud/resource-manager": "^5.1.0", "firebase-admin": "^13.2.0 || ^14.0.0", - "firebase-functions": "^6.3.2", + "firebase-functions": "^6.3.2 || ^7.0.0", "sql-formatter": "^2.3.3", "traverse": "^0.6.6" }, diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/e2e.test.ts b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/e2e.test.ts index 6c1642411a..d3ca740ffa 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/e2e.test.ts +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/e2e.test.ts @@ -28,7 +28,7 @@ import { latestConsistentSnapshotView } from "../../bigquery/snapshot"; import { deleteTable } from "../fixtures/clearTables"; import { changeTracker, changeTrackerEvent } from "../fixtures/changeTracker"; import { getBigQueryTableData } from "../fixtures/queries"; -import { firestore } from "firebase-admin"; +import { Timestamp } from "firebase-admin/firestore"; process.env.PROJECT_ID = "dev-extensions-testing"; @@ -167,7 +167,7 @@ describe("e2e", () => { }); test("successfully partitions with a valid DateTime Timestamp", async () => { - const created = firestore.Timestamp.now(); + const created = Timestamp.now(); const event: FirestoreDocumentChangeEvent = changeTrackerEvent({ data: { created }, @@ -206,7 +206,7 @@ describe("e2e", () => { }); test("successfully partitions with a valid DateTime Timestamp Date", async () => { - const created = firestore.Timestamp.now().toDate(); + const created = Timestamp.now().toDate(); const event: FirestoreDocumentChangeEvent = changeTrackerEvent({ data: { created }, @@ -243,7 +243,7 @@ describe("e2e", () => { }); test("successfully partitions with a valid Firebase Timestamp value with a Timestamp partitioning type", async () => { - const created = firestore.Timestamp.now(); + const created = Timestamp.now(); const event: FirestoreDocumentChangeEvent = changeTrackerEvent({ data: { created }, @@ -280,7 +280,7 @@ describe("e2e", () => { }); test("successfully partitions with a valid Firebase Timestamp value with a Date partitioning type", async () => { - const created = firestore.Timestamp.now(); + const created = Timestamp.now(); const expectedDate = created.toDate().toISOString().substring(0, 10); const event: FirestoreDocumentChangeEvent = changeTrackerEvent({ @@ -316,7 +316,7 @@ describe("e2e", () => { }); test("successfully partitions with a valid Firebase Timestamp value with a DateTime partitioning type", async () => { - const created = firestore.Timestamp.now(); + const created = Timestamp.now(); const expectedDate = created.toDate().toISOString().substring(0, 22); const event: FirestoreDocumentChangeEvent = changeTrackerEvent({ @@ -354,7 +354,7 @@ describe("e2e", () => { }); test("successfully partitions with a valid Firebase Timestamp value with `timestamp` as field name and Timestamp type", async () => { - const created = firestore.Timestamp.now(); + const created = Timestamp.now(); const expectedDate = created.toDate().toISOString().substring(0, 22); const event: FirestoreDocumentChangeEvent = changeTrackerEvent({ diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/failedTransaction.test.ts b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/failedTransaction.test.ts index 0558adc946..3998e444e6 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/failedTransaction.test.ts +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/failedTransaction.test.ts @@ -14,15 +14,18 @@ * limitations under the License. */ -import * as admin from "firebase-admin"; +import { Firestore, getFirestore } from "firebase-admin/firestore"; import { ChangeTrackerConfig } from "../../bigquery/types"; import handleFailedTransactions from "../../bigquery/handleFailedTransactions"; -// admin.initializeApp(); -const db = admin.firestore(); +let db: Firestore; describe("handleFailedTransactions", () => { + beforeAll(() => { + db = getFirestore(); + }); + it("should be defined", () => { expect(handleFailedTransactions).toBeDefined(); }); diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/materializedViews/initializeLatestMaterializedView.test.ts b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/materializedViews/initializeLatestMaterializedView.test.ts index c26fb4db02..26f473d630 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/materializedViews/initializeLatestMaterializedView.test.ts +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/materializedViews/initializeLatestMaterializedView.test.ts @@ -20,7 +20,7 @@ import { TableMetadata, Table, } from "@google-cloud/bigquery"; -import { firestore } from "firebase-admin"; +import { Timestamp } from "firebase-admin/firestore"; import { RawChangelogViewSchema } from "../../../bigquery/schema"; import { initializeLatestMaterializedView } from "../../../bigquery/initializeLatestMaterializedView"; import { @@ -143,7 +143,7 @@ describe("initializeLatestMaterializedView", () => { test("does not recreate view if configuration matches", async () => { const event = changeTrackerEvent({ - data: { end_date: firestore.Timestamp.now() }, + data: { end_date: Timestamp.now() }, eventId: "testing2", }); @@ -185,7 +185,7 @@ describe("initializeLatestMaterializedView", () => { test("recreates view when switching from incremental to non-incremental", async () => { const event = changeTrackerEvent({ - data: { end_date: firestore.Timestamp.now() }, + data: { end_date: Timestamp.now() }, eventId: "testing3", }); diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/materializedViews/integration.test.ts b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/materializedViews/integration.test.ts index 8b10169d3f..6c7bd7625c 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/materializedViews/integration.test.ts +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/materializedViews/integration.test.ts @@ -34,7 +34,7 @@ import { changeTrackerEvent, } from "../../fixtures/changeTracker"; import { getBigQueryTableData } from "../../fixtures/queries"; -import { firestore } from "firebase-admin"; +import { Timestamp } from "firebase-admin/firestore"; process.env.PROJECT_ID = "dev-extensions-testing"; @@ -45,7 +45,7 @@ process.env.PROJECT_ID = "dev-extensions-testing"; // eventId = "testing", // documentId = "testing", // pathParams = { documentId: "12345" }, -// data = { end_date: firestore.Timestamp.now() }, +// data = { end_date: Timestamp.now() }, // oldData = null, // useNewSnapshotQuerySyntax = false, // }: any): FirestoreDocumentChangeEvent => { @@ -65,7 +65,7 @@ process.env.PROJECT_ID = "dev-extensions-testing"; const bq: BigQuery = new BigQuery({ projectId: process.env.PROJECT_ID }); const event: FirestoreDocumentChangeEvent = changeTrackerEvent({}); const event2: FirestoreDocumentChangeEvent = changeTrackerEvent({ - data: { end_date: firestore.Timestamp.now() }, + data: { end_date: Timestamp.now() }, eventId: "testing2", }); let randomID: string; @@ -329,7 +329,7 @@ describe("integration", () => { eventId: "testing3", documentId: "doc3", pathParams: { documentId: "doc3" }, - data: { end_date: firestore.Timestamp.now(), status: "completed" }, + data: { end_date: Timestamp.now(), status: "completed" }, oldData: null, }); @@ -340,7 +340,7 @@ describe("integration", () => { eventId: "testing4", documentId: "doc4", pathParams: { documentId: "doc4" }, - data: { end_date: firestore.Timestamp.now(), status: "pending" }, + data: { end_date: Timestamp.now(), status: "pending" }, oldData: null, }); diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/materializedViews/shouldRecreateMaterializedView.test.ts b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/materializedViews/shouldRecreateMaterializedView.test.ts index af023dac34..ff9cd96a5a 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/materializedViews/shouldRecreateMaterializedView.test.ts +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/materializedViews/shouldRecreateMaterializedView.test.ts @@ -15,7 +15,7 @@ */ import { BigQuery, Dataset, TableMetadata } from "@google-cloud/bigquery"; -import { firestore } from "firebase-admin"; +import { Timestamp } from "firebase-admin/firestore"; import { RawChangelogViewSchema } from "../../../bigquery/schema"; import { buildMaterializedViewQuery, @@ -102,7 +102,7 @@ describe("Materialized View Recreation", () => { test("should not recreate incremental materialized view when unchanged", async () => { // Create initial event const event = changeTrackerEvent({ - data: { end_date: firestore.Timestamp.now() }, + data: { end_date: Timestamp.now() }, eventId: "testing2", }); @@ -162,7 +162,7 @@ describe("Materialized View Recreation", () => { test("should not recreate non-incremental materialized view when unchanged", async () => { // Create initial event const event = changeTrackerEvent({ - data: { end_date: firestore.Timestamp.now() }, + data: { end_date: Timestamp.now() }, eventId: "testing2", }); @@ -224,7 +224,7 @@ describe("Materialized View Recreation", () => { test("should recreate materialized view when inc -> non-inc ", async () => { // Create initial event const event = changeTrackerEvent({ - data: { end_date: firestore.Timestamp.now() }, + data: { end_date: Timestamp.now() }, eventId: "testing2", }); @@ -285,7 +285,7 @@ describe("Materialized View Recreation", () => { test("should recreate materialized view when non-inc -> inc ", async () => { // Create initial event const event = changeTrackerEvent({ - data: { end_date: firestore.Timestamp.now() }, + data: { end_date: Timestamp.now() }, eventId: "testing2", }); diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/partitioning.test.ts b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/partitioning.test.ts index 4144cfff43..9d6d81bbe7 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/partitioning.test.ts +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/partitioning.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import * as admin from "firebase-admin"; +import { Timestamp } from "firebase-admin/firestore"; import { BigQuery, Dataset, Table } from "@google-cloud/bigquery"; import { ChangeType, FirestoreDocumentChangeEvent } from "../.."; @@ -236,7 +236,7 @@ describeIfBigQueryIntegration("processing partitions on a new table", () => { bqProjectId: undefined, }; - const end_date = admin.firestore.Timestamp.now(); + const end_date = Timestamp.now(); const event: FirestoreDocumentChangeEvent = { timestamp: "", @@ -382,7 +382,7 @@ describeIfBigQueryIntegration("processing partitions on a new table", () => { bqProjectId: undefined, }; - const end_date = admin.firestore.Timestamp.now(); + const end_date = Timestamp.now(); const event: FirestoreDocumentChangeEvent = { timestamp: "", @@ -415,7 +415,7 @@ describeIfBigQueryIntegration("processing partitions on a new table", () => { bqProjectId: undefined, }; - const end_date = admin.firestore.Timestamp.now(); + const end_date = Timestamp.now(); const event: FirestoreDocumentChangeEvent = { timestamp: "", @@ -449,7 +449,7 @@ describeIfBigQueryIntegration("processing partitions on a new table", () => { bqProjectId: undefined, }; - const end_date = admin.firestore.Timestamp.now(); + const end_date = Timestamp.now(); const event: FirestoreDocumentChangeEvent = { timestamp: "", @@ -777,9 +777,7 @@ describeIfBigQueryIntegration( firestoreFieldName: "endDate", }); - const oldDate = admin.firestore.Timestamp.fromDate( - new Date("2024-01-15T10:00:00Z") - ); + const oldDate = Timestamp.fromDate(new Date("2024-01-15T10:00:00Z")); const event: FirestoreDocumentChangeEvent = { timestamp: "", diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/partitioning/converter.test.ts b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/partitioning/converter.test.ts index b5cfdee037..54c679d9da 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/partitioning/converter.test.ts +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/partitioning/converter.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import * as admin from "firebase-admin"; +import { Timestamp } from "firebase-admin/firestore"; import { PartitionValueConverter } from "../../../bigquery/partitioning/converter"; describe("PartitionValueConverter", () => { @@ -22,9 +22,7 @@ describe("PartitionValueConverter", () => { const converter = new PartitionValueConverter("TIMESTAMP"); test("converts Firebase Timestamp to BigQuery timestamp string", () => { - const timestamp = admin.firestore.Timestamp.fromDate( - new Date("2024-01-15T10:30:00Z") - ); + const timestamp = Timestamp.fromDate(new Date("2024-01-15T10:30:00Z")); const result = converter.convert(timestamp); expect(result).toBeDefined(); expect(typeof result).toBe("string"); @@ -195,9 +193,7 @@ describe("PartitionValueConverter", () => { const converter = new PartitionValueConverter("DATE"); test("converts Firebase Timestamp to BigQuery date string", () => { - const timestamp = admin.firestore.Timestamp.fromDate( - new Date("2024-01-15T10:30:00Z") - ); + const timestamp = Timestamp.fromDate(new Date("2024-01-15T10:30:00Z")); const result = converter.convert(timestamp); expect(result).toBe("2024-01-15"); }); @@ -248,9 +244,7 @@ describe("PartitionValueConverter", () => { const converter = new PartitionValueConverter("DATETIME"); test("converts Firebase Timestamp to BigQuery datetime string", () => { - const timestamp = admin.firestore.Timestamp.fromDate( - new Date("2024-01-15T10:30:00Z") - ); + const timestamp = Timestamp.fromDate(new Date("2024-01-15T10:30:00Z")); const result = converter.convert(timestamp); expect(result).toBeDefined(); expect(result).toContain("2024-01-15"); diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/fixtures/changeTracker.ts b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/fixtures/changeTracker.ts index b6465dc60d..e362c4d3cc 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/fixtures/changeTracker.ts +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/fixtures/changeTracker.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { firestore } from "firebase-admin"; +import { Timestamp } from "firebase-admin/firestore"; import { ChangeType, FirestoreBigQueryEventHistoryTracker, @@ -66,7 +66,7 @@ export const changeTrackerEvent = ({ eventId = "testing", documentId = "testing", pathParams = { documentId: "12345" }, - data = { end_date: firestore.Timestamp.now() }, + data = { end_date: Timestamp.now() }, oldData = null, useNewSnapshotQuerySyntax = false, }: any): FirestoreDocumentChangeEvent => { diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/handleFailedTransactions.ts b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/handleFailedTransactions.ts index 2ad56c6a91..51e7172c35 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/handleFailedTransactions.ts +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/handleFailedTransactions.ts @@ -14,12 +14,11 @@ * limitations under the License. */ -import * as admin from "firebase-admin"; -import { initializeApp } from "firebase-admin/app"; +import { getApps, initializeApp } from "firebase-admin/app"; import { getFirestore } from "firebase-admin/firestore"; import { ChangeTrackerConfig } from "."; -if (!admin.apps.length) { +if (!getApps().length) { initializeApp(); } diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/partitioning/converter.ts b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/partitioning/converter.ts index 1186d450bf..3e6862f567 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/partitioning/converter.ts +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/bigquery/partitioning/converter.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import * as firebase from "firebase-admin"; +import { Timestamp } from "firebase-admin/firestore"; import { BigQuery } from "@google-cloud/bigquery"; export class PartitionValueConverter { @@ -34,13 +34,10 @@ export class PartitionValueConverter { convert(value: unknown): string | null { let date: Date; - if (value instanceof firebase.firestore.Timestamp) { + if (value instanceof Timestamp) { date = value.toDate(); } else if (this.isTimestampLike(value)) { - date = new firebase.firestore.Timestamp( - value._seconds, - value._nanoseconds - ).toDate(); + date = new Timestamp(value._seconds, value._nanoseconds).toDate(); } else if (value instanceof Date && !isNaN(value.getTime())) { date = value; } else if (typeof value === "string") { diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/logs.ts b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/logs.ts index 1930bdc7db..fc8213218d 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/logs.ts +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/logs.ts @@ -15,7 +15,7 @@ */ import { Table } from "@google-cloud/bigquery"; -import { firestore } from "firebase-admin"; +import type { Timestamp } from "firebase-admin/firestore"; import { logger } from "./logger"; export const arrayFieldInvalid = (fieldName: string) => { @@ -207,7 +207,7 @@ export const firestoreTimePartitioningParametersWarning = ( fieldName: string | undefined, fieldType: string | undefined, firestoreFieldName: string | undefined, - dataFirestoreField: firestore.Timestamp | string | undefined + dataFirestoreField: Timestamp | string | undefined ) => { logger.warn( "All TimePartitioning option parameters need to be available to create new custom schema field" From c58b306a377f1647bc713bfd3c39325001f0eaa1 Mon Sep 17 00:00:00 2001 From: Jacob Cable Date: Mon, 7 Sep 2026 17:29:15 +0100 Subject: [PATCH 2/3] fix(firestore-bigquery-change-tracker): mock getApps and floor firebase-functions at 7.3.0 backupSettings.test.ts mocked firebase-admin/app without getApps, so the migrated guard threw at import and all 12 tests failed. firebase-functions 7.0 through 7.2 still peer-pin firebase-admin to 13; 7.3.0 is the first release whose peer range includes 14. Accepting only ^7.3.0 makes the single-admin resolution a property of the range. --- .../firestore-bigquery-change-tracker/CHANGELOG.md | 2 +- .../firestore-bigquery-change-tracker/package-lock.json | 2 +- .../firestore-bigquery-change-tracker/package.json | 2 +- .../src/__tests__/bigquery/backupSettings.test.ts | 6 ++++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/CHANGELOG.md b/firestore-bigquery-export/firestore-bigquery-change-tracker/CHANGELOG.md index 040715142a..b7839f8f69 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/CHANGELOG.md +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/CHANGELOG.md @@ -3,7 +3,7 @@ ## 2.1.2 - Works on `firebase-admin` 14. 2.1.1 widened the range but the package still used the namespaced API (`admin.apps`, `admin.firestore.Timestamp`), which firebase-admin 14 removes, so it threw at import wherever it was resolved against admin 14. Every use is now the modular API from `firebase-admin/app` and `firebase-admin/firestore`, which works on 13 and 14 alike. -- `firebase-functions` 7 is accepted alongside 6. This package only uses its `logger`, but the firebase-functions 6 peer range for `firebase-admin` stops at 13, so on a consumer running firebase-admin 14 npm nested a second admin copy under this package however wide this package's own range was, and that nested copy has no default app. +- `firebase-functions` 7.3 and later is accepted alongside 6. This package only uses its `logger`, but the firebase-functions 6 peer range for `firebase-admin` stops at 13, as does the range of firebase-functions 7.0 through 7.2, so on a consumer running firebase-admin 14 npm nested a second admin copy under this package however wide this package's own range was, and that nested copy has no default app. - Consumers that pin neither `firebase-admin` nor `firebase-functions` themselves and resolve fresh now get firebase-admin 14 and firebase-functions 7, which need Node 22 and Node 18 respectively. Pin firebase-admin `^13` to stay on Node 18 or 20. ## 2.1.1 diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/package-lock.json b/firestore-bigquery-export/firestore-bigquery-change-tracker/package-lock.json index c999b6a851..91c2dda31a 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/package-lock.json +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/package-lock.json @@ -12,7 +12,7 @@ "@google-cloud/bigquery": "^7.6.0", "@google-cloud/resource-manager": "^5.1.0", "firebase-admin": "^13.2.0 || ^14.0.0", - "firebase-functions": "^6.3.2 || ^7.0.0", + "firebase-functions": "^6.3.2 || ^7.3.0", "sql-formatter": "^2.3.3", "traverse": "^0.6.6" }, diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/package.json b/firestore-bigquery-export/firestore-bigquery-change-tracker/package.json index 2f0d9f8b57..af4631fcb2 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/package.json +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/package.json @@ -26,7 +26,7 @@ "@google-cloud/bigquery": "^7.6.0", "@google-cloud/resource-manager": "^5.1.0", "firebase-admin": "^13.2.0 || ^14.0.0", - "firebase-functions": "^6.3.2 || ^7.0.0", + "firebase-functions": "^6.3.2 || ^7.3.0", "sql-formatter": "^2.3.3", "traverse": "^0.6.6" }, diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/backupSettings.test.ts b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/backupSettings.test.ts index 562b84f525..f2057ae822 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/backupSettings.test.ts +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/src/__tests__/bigquery/backupSettings.test.ts @@ -23,8 +23,10 @@ const settings = jest.fn(); const batch = jest.fn(() => ({ set, commit })); const collection = jest.fn(() => ({ doc: (id: string) => ({ id }) })); -jest.mock("firebase-admin", () => ({ apps: [{}] })); -jest.mock("firebase-admin/app", () => ({ initializeApp: jest.fn() })); +jest.mock("firebase-admin/app", () => ({ + getApps: jest.fn(() => [{}]), + initializeApp: jest.fn(), +})); jest.mock("firebase-admin/firestore", () => ({ // A fresh object per call, deliberately: the guard must key on the database id // rather than on instance identity. From c23913a5366ebc5b4f773c250601310813430872 Mon Sep 17 00:00:00 2001 From: Jacob Cable Date: Mon, 7 Sep 2026 17:30:31 +0100 Subject: [PATCH 3/3] chore(firestore-bigquery-change-tracker): release as 2.2.0 with an engines floor Widening the runtime dependency ranges moves the versions a fresh, unpinned resolve picks, so this ships as a minor rather than a patch. engines.node >=18 states the floor firebase-admin 13 already imposed. --- .../firestore-bigquery-change-tracker/CHANGELOG.md | 3 ++- .../firestore-bigquery-change-tracker/package-lock.json | 4 ++-- .../firestore-bigquery-change-tracker/package.json | 5 ++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/CHANGELOG.md b/firestore-bigquery-export/firestore-bigquery-change-tracker/CHANGELOG.md index b7839f8f69..a4c97b0bc8 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/CHANGELOG.md +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/CHANGELOG.md @@ -1,9 +1,10 @@ # Changelog -## 2.1.2 +## 2.2.0 - Works on `firebase-admin` 14. 2.1.1 widened the range but the package still used the namespaced API (`admin.apps`, `admin.firestore.Timestamp`), which firebase-admin 14 removes, so it threw at import wherever it was resolved against admin 14. Every use is now the modular API from `firebase-admin/app` and `firebase-admin/firestore`, which works on 13 and 14 alike. - `firebase-functions` 7.3 and later is accepted alongside 6. This package only uses its `logger`, but the firebase-functions 6 peer range for `firebase-admin` stops at 13, as does the range of firebase-functions 7.0 through 7.2, so on a consumer running firebase-admin 14 npm nested a second admin copy under this package however wide this package's own range was, and that nested copy has no default app. +- Declares `engines.node >=18`, the floor firebase-admin 13 already imposed, so an unsupported Node is reported at install rather than at runtime. - Consumers that pin neither `firebase-admin` nor `firebase-functions` themselves and resolve fresh now get firebase-admin 14 and firebase-functions 7, which need Node 22 and Node 18 respectively. Pin firebase-admin `^13` to stay on Node 18 or 20. ## 2.1.1 diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/package-lock.json b/firestore-bigquery-export/firestore-bigquery-change-tracker/package-lock.json index 91c2dda31a..999ee96349 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/package-lock.json +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/package-lock.json @@ -1,12 +1,12 @@ { "name": "@firebaseextensions/firestore-bigquery-change-tracker", - "version": "2.1.2", + "version": "2.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@firebaseextensions/firestore-bigquery-change-tracker", - "version": "2.1.2", + "version": "2.2.0", "license": "Apache-2.0", "dependencies": { "@google-cloud/bigquery": "^7.6.0", diff --git a/firestore-bigquery-export/firestore-bigquery-change-tracker/package.json b/firestore-bigquery-export/firestore-bigquery-change-tracker/package.json index af4631fcb2..0ebc3ea45b 100644 --- a/firestore-bigquery-export/firestore-bigquery-change-tracker/package.json +++ b/firestore-bigquery-export/firestore-bigquery-change-tracker/package.json @@ -5,7 +5,10 @@ "url": "git+https://github.com/firebase/extensions.git", "directory": "firestore-bigquery-export/firestore-bigquery-change-tracker" }, - "version": "2.1.2", + "version": "2.2.0", + "engines": { + "node": ">=18" + }, "description": "Core change-tracker library for Cloud Firestore Collection BigQuery Exports", "main": "./lib/index.js", "scripts": {