@firebase-bridge/firestore-admin

中文文档
0.0.10发布于 2026/08/12Apache-2.0TypeScriptESM + CJS零依赖近 30 天下载 2313

High-fidelity in-memory mock of the Firebase Firestore Admin SDK (Gapic) for unit tests and local dev.

原始使用文档
文档目录24 节
  1. What it is
  2. When to use it
  3. Why not the emulator (for this use case)
  4. Companion Packages
  5. Support
  6. Install
  7. Quick start (Jest/Vitest)
  8. A) Fast resets (preferred)
  9. B) Fresh DB per test (full isolation)
  10. Multiple databases & Firestore isolation
  11. Core concepts & API
  12. class FirestoreMock
  13. class FirestoreController
  14. class DatabaseDirect
  15. class SystemTime
  16. Structural snapshots (seed, diff, round‑trip)
  17. Stats & observability
  18. Notes on fidelity (high level)
  19. Package exports (public)
  20. Troubleshooting
  21. Versioning & compatibility
  22. Contributing
  23. License
  24. Trademarks & attribution

@firebase-bridge/firestore-admin

High‑fidelity in‑memory mock for the Firestore Admin SDK. Purpose‑built for fast, deterministic backend unit tests (no emulator boot, no deploy loop).

license: Apache-2.0 node typescript

What it is

This package lets you run a real firebase-admin Firestore instance entirely in-process against an in‑memory database with high-fidelity production Firestore semantics (CRUD, batches, transactions, queries, aggregations, vector values, listeners, etc).

  • Backend only (Node.js). No client/browser APIs.
  • Dev dependency intended for unit‑testing and rapid prototyping of backend logic.
  • To bind firebase-functions v1/v2 Firestore triggers to an in-memory Firestore database use the companion package @firebase-bridge/firestore-functions.

Important: @firebase-bridge/firestore-admin mocks the database, not the Admin SDK itself. When you use it, you’re interacting with real Firestore Admin SDK instances — the same objects you’d use in production. The mock simply patches its own Google API Client implementation under the hood, “tricking” the SDK into thinking it’s communicating with Firestore’s backend. This means your tests exercise authentic Admin SDK behavior without an emulator and without network calls.

When to use it

  • Unit and integration tests for backend code using firebase-admin/firestore
  • CI where the Firestore Emulator is slow or unavailable
  • Deterministic tests that need a controllable clock and fast resets

Why not the emulator (for this use case)

  • Zero boot time. Zero deploy loop. Zero external processes — just edit, save, and test
  • Deterministic in-memory Firestore with controllable time
  • Suited to tight test loops and CI where startup cost matters

Companion Packages

  • To bind firebase-functions (v1 & v2) Firestore triggers to an in-memory Firestore database use the companion package @firebase-bridge/firestore-functions.
  • For a high-fidelity mock invocation layer for Firebase HTTPS Cloud Functions (v1 & v2) — run real onCall / onRequest handlers locally with realistic auth, App Check, instance ID, and headers (no emulator) — use the companion package @firebase-bridge/auth-context.

Support

This project is made freely available under the Apache 2.0 License.
If you find it useful and would like to support ongoing development, you can buy me a coffee. ☕


Install

# npm
npm i -D @firebase-bridge/firestore-admin firebase-admin

# pnpm
pnpm add -D @firebase-bridge/firestore-admin firebase-admin

# yarn
yarn add -D @firebase-bridge/firestore-admin firebase-admin

Peer deps: firebase-admin • Node: 18+ recommended • TS: strict mode recommended.


Quick start (Jest/Vitest)

Two common setup styles are shown below. Prefer the fast reset approach for speed; use fresh DB per test when you need complete DB lifecycle isolation.

A) Fast resets (preferred)

Create one environment and one database for the whole suite; reset between tests:

import { Firestore } from 'firebase-admin/firestore';
import { FirestoreMock } from '@firebase-bridge/firestore-admin';

describe('Example suite (fast resets)', () => {
  const env = new FirestoreMock();
  const firestore: Firestore = env.createDatabase().firestore();

  afterEach(() => {
    env.resetAll(); // clears all databases in this env, keeps them alive
  });

  it('writes and reads', async () => {
    const ref = firestore.collection('users').doc('ada');
    await ref.set({ name: 'Ada', score: 1 });
    const snap = await ref.get();
    expect(snap.exists).toBe(true);
    expect(snap.data()).toEqual({ name: 'Ada', score: 1 });
  });
});

B) Fresh DB per test (full isolation)

Provision a new logical DB per test and delete after each:

import { Firestore } from 'firebase-admin/firestore';
import { FirestoreMock } from '@firebase-bridge/firestore-admin';

describe('Example suite (fresh DB per test)', () => {
  const env = new FirestoreMock();
  let firestore!: Firestore;

  beforeEach(() => {
    firestore = env.createDatabase().firestore();
  });

  afterEach(() => {
    env.deleteAll(); // disposes all databases in this env
  });

  it('writes and reads', async () => {
    const ref = firestore.collection('users').doc('ada');
    await ref.set({ name: 'Ada', score: 1 });
    const snap = await ref.get();
    expect(snap.exists).toBe(true);
    expect(snap.data()).toEqual({ name: 'Ada', score: 1 });
  });
});

Multiple databases & Firestore isolation

You can host many logical databases in one environment, and you can create multiple Firestore instances attached to the same database when you need Firestore‑level isolation (e.g., independent listeners) over shared state.

const env = new FirestoreMock();

// Two separate logical databases
const dbA = env.createDatabase('proj-A', '(default)');
const dbB = env.createDatabase('proj-B', '(default)');

const fsA1 = dbA.firestore();
const fsA2 = dbA.firestore(); // isolated Firestore instances, same DB
const fsB = dbB.firestore(); // different DB entirely

Core concepts & API

This package exposes a small set of high‑leverage primitives. Names below are actual exports.

class FirestoreMock

A top-level environment that owns one or more in-memory databases and a controllable clock.

  • createDatabase(options?: FirestoreControllerOptions): FirestoreController Provision a new database using an options object. All fields are optional with defaults:

    • projectId defaults to "default-project"
    • databaseId defaults to "(default)"
    • location defaults to "nam5"
    • namespace defaults to "(default)" Use this form if you need to specify location or namespace in your tests.
  • createDatabase(projectId?: string, databaseId?: string): FirestoreController Provision a new database by explicit IDs (defaults as above).

  • getDatabase(projectId?: string, databaseId?: string): FirestoreController Access an existing database (throws if missing/deleted).

  • databaseExists(projectId?: string, databaseId?: string): boolean

  • deleteAll(): void – delete all databases in the environment.

  • resetAll(): void – reset all databases (data & stats) without deleting them.

  • systemTime: SystemTime – controllable time source for deterministic tests.

class FirestoreController

A handle to a single logical database (identified by projectId and databaseId).

  • projectId: string, databaseId: string

  • location: string – Firestore database location identifier used in CloudEvents and resource metadata. Accepts multi‑region IDs (e.g. nam5, eur3) or regional IDs (e.g. us-central1). Defaults to nam5 if omitted.

  • namespace: string – Datastore namespace. For Firestore Native mode this should remain (default). Included for fidelity when simulating Datastore‑mode events. Defaults to (default) if omitted.

  • firestore(settings?: Settings): Firestore Create a Firestore Admin SDK instance scoped to this database.

  • exists(): boolean – whether the database still exists.

  • epoch(): number – The monotonically increasing epoch version of the database (incremented with each reset()).

  • version(): number – The monotonically increasing atomic commit version of the database.

  • delete(): void – delete this database; subsequent calls (besides exists()/reset()) throw.

  • reset(): void – clear documents & stats but keep the DB alive.

  • getStats(): FirestoreMockStats – current cumulative stats snapshot.

  • watchStats(watcher: (s: FirestoreMockStats) => void): () => void – subscribe to stat changes (returns an unsubscribe).

  • watchLifecycle(watcher: (s: DatabaseLifecycleEventArg) => void): () => void – subscribe to lifecycle events (returns an unsubscribe).

  • database: DatabaseDirect – direct/low‑level access to the in‑memory DB (see below).

class DatabaseDirect

A thin, synchronous façade for direct data access (seeding, inspection, structural imports/exports). It bypasses Admin SDK objects but maintains Firestore semantics.

  • Inspection & structure

    • listCollectionIds(documentPath: string): string[]
    • listDocuments(collectionPath: string, showMissing: boolean): MetaDocument[]
    • query<T>(q: DocumentQuery<T>): MetaDocumentExists<T>[]
    • toStructuralDatabase(): StructuralDatabase
    • fromStructuralDatabase(src: StructuralDatabase, merge?: MergeGranularity): NormalizedWriteResult
    • Conversion helpers: toMetaArray(), toMetaMap(), toMap(), toDocumentPaths()
  • Single‑doc ops

    • getDocument<T>(path: string): MetaDocument<T>
    • setDocument<T>(path: string, data: T): MetaDocument<T>
    • deleteDocument<T>(path: string): MetaDocument<T>
  • Batch ops (atomic)

    • batchSet<T>(...docs: DatabaseDocument<T>[]): MetaDocument<T>[]
    • batchDelete<T>(...paths: string[]): MetaDocument<T>[]
    • batchWrite<T>(writes: (DatabaseDocument<T> | string)[]): MetaDocument<T>[] ({ path, data } → set; string → delete)

Triggers: Low‑level trigger registration exists on DatabaseDirect. This can be handy in white‑box tests. For Cloud Functions parity (v1/v2 events, subjects, metadata), prefer the @firebase-bridge/firestore-functions companion package..

Low‑level trigger example (DatabaseDirect)

import { FirestoreMock } from '@firebase-bridge/firestore-admin';

const env = new FirestoreMock();
const ctl = env.createDatabase('proj', '(default)');
const direct = ctl.database;

const unsubscribe = direct.registerTrigger({
  route: 'users/{uid}/posts/{pid}',
  callback: ({ params, doc }) => {
    // Derive semantic kind from lineage
    const prev = doc.previous;
    const kind =
      !prev?.exists && doc.exists
        ? 'create'
        : prev?.exists && doc.exists
        ? 'update'
        : prev?.exists && !doc.exists
        ? 'delete'
        : 'write';

    console.log(`[${kind}] users/${params.uid}/posts/${params.pid}`, {
      exists: doc.exists,
      version: doc.version,
      updateTime: doc.updateTime.toDate().toISOString(),
    });

    // doc.data is deeply frozen; use cloneData() for a mutable copy
    const mutable = doc.cloneData();
    // ...assertions, enqueue side effects for tests, etc.
  },
});

// Perform writes via Firestore or DatabaseDirect
const fs = ctl.firestore();
await fs
  .collection('users')
  .doc('u1')
  .collection('posts')
  .doc('p1')
  .set({ title: 'hello' });

unsubscribe();

Notes:

  • Triggers fire after each atomic commit.
  • If multiple writes target the same document in a single commit, only the final state for that path is delivered.
  • For Cloud Functions fidelity (v1/v2 payload shaping, subjects, event IDs), prefer the @firebase-bridge/firestore-functions companion package.

DatabaseDirect — writes (including fromStructuralDatabase() shapes)

import {
  FirestoreMock,
  StructuralDatabase,
  MergeGranularity,
} from '@firebase-bridge/firestore-admin';

const env = new FirestoreMock();
const ctl = env.createDatabase('proj', '(default)');
const direct = ctl.database;

// 1) Single set / delete
direct.setDocument('users/alice', { name: 'Alice', score: 7 });
direct.deleteDocument('users/bob'); // no-op if it didn't exist

// 2) Batch set / delete (atomic)
direct.batchSet(
  { path: 'users/bob', data: { name: 'Bob', score: 3 } },
  { path: 'users/cara', data: { name: 'Cara', tags: ['pro'] } }
);
direct.batchDelete('users/bob', 'users/ghost');

// 3) Heterogeneous batch write (atomic)
//    - {path,data} → normalized to 'set' (merge: 'root')
//    - 'string'   → normalized to 'delete'
direct.batchWrite([{ path: 'users/alice', data: { score: 8 } }, 'users/ghost']);

// 4) Import from a structural snapshot
const snapshot: StructuralDatabase = {
  users: {
    alice: {
      data: { name: 'Alice', role: 'admin' },
      collections: {
        posts: {
          p1: { data: { title: 'Hello', likes: 1 } },
          p2: { data: { title: 'Second', likes: 0 } },
        },
      },
    },
    cara: { data: { name: 'Cara' } },
  },
  products: {
    p1: { data: { sku: 'ABC-123', price: 19.95 } },
  },
};

// Merge strategies:
// - 'root'   → replace entire doc with provided data
// - 'branch' → deep-merge maps; scalars/arrays replace
// - 'node'   → apply only explicit field paths (like update/mergeFields)

direct.fromStructuralDatabase(snapshot, 'root' satisfies MergeGranularity);

// 5) Export to a structural snapshot (round-trippable)
const roundTrip = direct.toStructuralDatabase();

fromStructuralDatabase returns a normalized write result (server time + MetaDocument[]). All write helpers are atomic at the batch call boundary.

DatabaseDirect — stats() (example shape)

import { DatabaseStats } from '@firebase-bridge/firestore-admin';

const s: DatabaseStats = direct.stats();
console.log(s);
/*
{
  // Operation counters (cumulative until reset)
  writes: 4,
  reads: 3,
  deletes: 1,
  noopReads: 1,
  noopWrites: 0,
  noopDeletes: 1,

  // Structural counters (current view of the tree)
  documentCount: 3,
  collectionCount: 2,
  stubDocumentCount: 0,
  stubCollectionCount: 0
}
*/
  • Use ctl.getStats() for controller-scoped stats (adds DB identity).
  • To reset counters and wipe all data while keeping databases, call ctl.reset() (single DB) or env.resetAll() (all DBs). This deletes all documents and collections, flushes pending changes without invoking watcher callbacks, zeros all database stats, and resets the internal change-version nonce to 0.

DatabaseDirect — query() examples

DocumentQuery<T> provides scoping (root/document, collection ID, collection-group toggle), optional point-in-time readTime, and a predicate over existing docs.

import { Timestamp } from 'firebase-admin/firestore';
import {
  DocumentQuery,
  MetaDocumentExists,
} from '@firebase-bridge/firestore-admin';

// 1) All docs in top-level 'users'
const q1: DocumentQuery = {
  parent: '',
  allDescendants: false,
  collectionId: 'users',
  predicate: () => true,
};
const users: MetaDocumentExists[] = direct.query(q1);

// 2) Collection group query: any 'posts' anywhere under 'users/alice'
const q2: DocumentQuery<{ title: string; likes: number }> = {
  parent: 'users/alice',
  allDescendants: true,
  collectionId: 'posts',
  predicate: (m) => (m.data.likes ?? 0) >= 1,
};
const hotPosts = direct.query(q2);

// 3) As-of read (point-in-time)
const at: Timestamp = Timestamp.fromMillis(Date.now() - 1000);
const q3: DocumentQuery = {
  parent: '',
  allDescendants: true,
  predicate: () => true,
  readTime: at, // include docs with updateTime <= at
};
const asOfDocs = direct.query(q3);

Results are MetaDocumentExists<T>[] only (non-existing docs are excluded). Order as needed in your test code.

MetaDocumentExists & MetaDocumentNotExists (what you get back)

Immutable snapshots returned from direct ops, queries, and write results.

Common fields (MetaDocument<T>)

  • epoch: number – internal database epoch that increments each time the internal database is reset
  • parent: string – collection path of the doc’s parent
  • path: string – fully qualified document path
  • id: string – last segment of path
  • serverTime: Timestamp – authoritative commit/apply time for the producing op
  • updateTime: Timestamp – last update time for the doc (0 if never existed)
  • version: number – the monotonically increasing atomic commit version of the database
  • hasChanges: boolean – whether the producing op changed the doc
  • createTime?: Timestamp – when the doc was first created (undefined if never existed)
  • data?: T – deeply frozen data; use cloneData() for a mutable copy
  • previous?: MetaDocument<T> – immediate prior state for the same path (present when hasChanges === true; not a transitive chain)
  • cloneData(): T | undefined – defensive deep clone of data

Refinements

  • MetaDocumentExists<T>:

    • exists: true
    • createTime: Timestamp
    • data: T
  • MetaDocumentNotExists<T>:

    • exists: false
    • createTime?: undefined
    • data?: undefined
const m = direct.getDocument<{ name: string }>('users/alice');
if (m.exists) {
  // MetaDocumentExists<{name:string}>
  console.log(m.path, m.updateTime.toDate(), m.data.name);
  const mutable = m.cloneData();
} else {
  // MetaDocumentNotExists
  console.log('missing:', m.path);
}

Snapshots as arrays & maps (toMetaArray, toMetaMap, toMap)

// Seed
direct.batchSet(
  { path: 'users/alice', data: { name: 'Alice', score: 7 } },
  { path: 'users/cara', data: { name: 'Cara', score: 3 } }
);

// 1) Array of existing meta documents (ordered)
const arr = direct.toMetaArray();

// 2) Map of path → MetaDocumentExists
const metaMap = direct.toMetaMap();

// 3) Map of path → plain document data (no meta)
const dataMap = direct.toMap();

class SystemTime

Deterministically control “now” as observed by writes, transforms, and snapshot timestamps.

  • now(): Date – current time per strategy.
  • system(): void – real clock.
  • constant(date: Date): void – fixed instant.
  • offset(...): start from a root time and move forward in real time (overloads support Date or UTC parts).
  • advance(msOrParts): void – jump the clock forward.
  • custom(fn: () => Date): void – fully custom time generator.

SystemTime & Timestamp.now() gotcha

Internally, this mock derives all commit/write/update times from SystemTime. However, firebase-admin’s Timestamp.now() calls into the real clock (e.g., Date.now()), which we do not monkey‑patch by default. As a result, if your application/test code directly calls Timestamp.now(), it will not reflect SystemTime unless you take additional steps.

Options:

  1. Don’t call Timestamp.now() directly in tests. Instead derive from SystemTime:
import { Timestamp } from 'firebase-admin/firestore';
const ts = Timestamp.fromDate(env.systemTime.now());
  1. Monkey‑patch Timestamp.now during tests (scoped and reversible):
import { Timestamp } from 'firebase-admin/firestore';
let restore: undefined | (() => void);

beforeAll(() => {
  const original = Timestamp.now;
  (Timestamp as any).now = () => Timestamp.fromDate(env.systemTime.now());
  restore = () => {
    (Timestamp as any).now = original;
  };
});

afterAll(() => restore?.());
  1. Use your test runner’s fake‑time utilities to align the global clock with SystemTime.
  • Jest (modern fake timers):
import { jest } from '@jest/globals';

beforeAll(() => {
  jest.useFakeTimers();
});

beforeEach(() => {
  jest.setSystemTime(env.systemTime.now());
});

afterAll(() => {
  jest.useRealTimers();
});
  • Vitest (vi.useFakeTimers() / vi.setSystemTime()), similarly.

We intentionally avoid patching Timestamp.now() automatically to keep this library side‑effect‑free with respect to peer dependencies. All internal timestamps (commit time, serverTimestamp, updateTime, writeTime) do honor SystemTime.


Structural snapshots (seed, diff, round‑trip)

The structural types let you snapshot/import database state without Firestore objects:

  • StructuralDatabase – object tree of collections → documents → nested collections.
  • StructuralCollection, StructuralCollectionGroup, StructuralDocument
import {
  StructuralDatabase,
  DatabaseDirect,
} from '@firebase-bridge/firestore-admin';

const ctl = new FirestoreMock().createDatabase('proj', '(default)');
const direct: DatabaseDirect = ctl.database;

// Export
const snapshot: StructuralDatabase = direct.toStructuralDatabase();

// Import/merge
const writes = direct.fromStructuralDatabase(
  snapshot /*, 'root' | 'collection' | 'document' */
);
expect(writes.count).toBeGreaterThan(0);

Stats & observability

Use FirestoreController.getStats() to assert fidelity and track operations:

const ctl = new FirestoreMock().createDatabase();
const fs = ctl.firestore();
await fs.collection('c').doc('d').set({ a: 1 });

const stats = ctl.getStats();
expect(stats.documents.total).toBe(1);

Subscribe to live updates during tests with watchStats() (remember to unsubscribe):

const stop = ctl.watchStats((s) => {
  // e.g., console.log('writes', s.operations.writes.total)
});
// ...
stop();

Notes on fidelity (high level)

  • Atomicity: batches/transactions are atomic; transform results follow Firestore’s ordering rules.
  • Time: updateTime, writeTime, and stored serverTimestamp follow Firestore relationships; use SystemTime to make tests deterministic.
  • Queries: filters (including or/not-in/in), ordering, cursors, limits, collection‑group, and aggregations (e.g., count()).
  • Vector values: supports FieldValue.vector() fields and nearest‑neighbor features in queries that expose them through the Admin API surface.
  • Listeners: document and query listeners behave like streaming APIs with monotonic readTime and proper change sets.
  • Partitioned queries (CollectionGroup.getPartitions() / Query.getPartitions() → GAPIC partitionQuery): currently stubbed — the mock returns an empty stream (no partitions) for compatibility with tests that call it but don’t use the results. Use the emulator/Firestore for real partitioning semantics (parallel exports/batching).

If you find behavior that diverges from the real Admin SDK or emulator, please open an issue with a minimal repro — fidelity is the project’s #1 priority.


Package exports (public)

From this package you can import:

  • FirestoreMock, FirestoreController
  • DatabaseDirect and structural types: StructuralDatabase, StructuralCollection, StructuralCollectionGroup, StructuralDocument
  • Time control: SystemTime
  • Useful types for assertions: MetaDocument, MetaDocumentExists, MetaDocumentNotExists, MergeGranularity, Trigger, TriggerEventArg, FirestoreMockStats

Cloud Functions: for registering/using triggers in tests, depend on the @firebase-bridge/firestore-functions companion package.


Troubleshooting

  • My Firestore calls don’t hit the mock Ensure you’re using firestore = env.createDatabase(...).firestore() from a FirestoreController created by this environment.

  • I need fresh state between tests Prefer env.resetAll() (fast) over env.deleteAll() (disposes DBs completely). You can also reset a single DB via controller.reset().

  • Time‑sensitive assertions are flaky Pin or advance time via env.systemTime.

  • Tests hang or fail to exit cleanly after running
    Ensure all in-memory databases are explicitly disposed of once tests finish.
    Add an afterEach() or afterAll() hook to call one of the following, depending on scope:

    • env.deleteAll() — deletes all databases in the FirestoreMock environment
    • controller.delete() — deletes a single database via its FirestoreController

    This guarantees that background resources (timers, intervals, listeners, etc.) are released and allows your test runner to shut down cleanly.


Versioning & compatibility

  • Peer dependency: firebase-admin (see your package’s peerDependencies for the supported range).
  • Node.js ≥ 18. TypeScript projects (ESM or CJS) are supported via the Admin SDK.

Contributing

Thanks for your interest! This project is in minimal-maintainer mode.

  • Issues first. Please open an issue with a clear repro or failing test. Unsolicited feature PRs may be closed.
  • PRs limited to: bug fixes with tests, small docs improvements, or build/release hygiene. New features require an accepted proposal in an issue first.
  • Tests are required. Changes must include high-fidelity tests that show alignment (or documented divergence) with the Firebase Emulator.
  • Review cadence. I review in batches and may be slow. There’s no support SLA.
  • Scope guardrails. The goal is fidelity to Firestore/Admin SDK semantics; out-of-scope features will be declined.

If that works for you, awesome—bugfixes and docs tweaks are especially welcome.


License

Apache-2.0 © 2026 Bryce Marshall


Trademarks & attribution

This project is not affiliated with, associated with, or endorsed by Google LLC. “Firebase” and “Firestore” are trademarks of Google LLC. Names are used solely to identify compatibility and do not imply endorsement.

版本与发布记录

@firebase-bridge/firestore-admin 共发布 10 个版本,首次发布于 2025/09/29;过去 12 个月发布了 2 个版本。

版本发布日期距今
0.0.10latest2026/08/123 个月前
0.0.92025/11/2811 个月前
0.0.82025/10/201.1 年前
0.0.72025/10/181.1 年前
0.0.62025/10/181.1 年前
0.0.52025/10/151.1 年前

关于 @firebase-bridge/firestore-admin 的常见问题

@firebase-bridge/firestore-admin 是什么?

High-fidelity in-memory mock of the Firebase Firestore Admin SDK (Gapic) for unit tests and local dev.

如何安装 @firebase-bridge/firestore-admin?

在项目目录运行 npm install @firebase-bridge/firestore-admin,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:firebase-admin(^13.4.0)、google-gax(>=5 <6)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@firebase-bridge/firestore-admin 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@firebase-bridge/firestore-admin 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@firebase-bridge/firestore-admin 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@firebase-bridge/firestore-admin 可以商用吗?

@firebase-bridge/firestore-admin 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@firebase-bridge/firestore-admin 的下载量有多少?

根据 npm 官方下载统计,@firebase-bridge/firestore-admin 在 2026/10/15 – 2026/11/13 这 30 天内被下载 2,313 次,日均约 77 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@firebase-bridge/firestore-admin 还在维护吗?

最新版本 0.0.10 发布于 2026/08/12(3 个月前);至今共发布 10 个版本,其中过去 12 个月发布了 2 个;首次发布于 2025/09/29。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。