@plantagoai/firebase-core
Firebase initialization, Firestore CRUD helpers, and tenant-scoped queries. The foundational package that all other @plantagoai packages depend on.
Consumers: Foundation, MarketHub, Soho, HerbPulse
Installation
"@plantagoai/firebase-core": "file:../../shared/packages/firebase-core"
Exports
| Entry Point | Description |
|---|---|
@plantagoai/firebase-core |
Client-side: Firebase init, Firestore CRUD, tenant queries |
@plantagoai/firebase-core/admin |
Server-side: Admin SDK init, admin Firestore helpers |
Environment Detection
import { detectEnv } from "@plantagoai/firebase-core";
const env = detectEnv(); // "local" | "devnet" | "production"
Determines environment from hostname:
localhost/127.0.0.1→"local"*-devnet*→"devnet"- Everything else →
"production"
Client-Side Initialization
import { initFirebase, getDb, getFirebaseAuth, getFirebaseApp } from "@plantagoai/firebase-core";
const app = initFirebase({
config: {
apiKey: "...",
authDomain: "...",
projectId: "...",
// ...
},
useEmulators: true, // optional: connect to local emulators
emulatorHost: "localhost", // default: localhost
firestorePort: 8080, // default: 8080
authPort: 9099, // default: 9099
});
const db = getDb();
const auth = getFirebaseAuth();
InitOptions
| Field | Type | Default | Description |
|---|---|---|---|
config |
FirebaseOptions |
required | Firebase project configuration |
useEmulators |
boolean |
false |
Connect to local Firebase emulators |
emulatorHost |
string |
"localhost" |
Emulator host address |
firestorePort |
number |
8080 |
Firestore emulator port |
authPort |
number |
9099 |
Auth emulator port |
Firestore CRUD
All functions accept an optional db parameter to override the default Firestore instance.
Get Document
import { getDocById } from "@plantagoai/firebase-core";
const proposal = await getDocById<Proposal>("proposals", "abc123");
// Returns { id: "abc123", title: "...", ... } or null
List Documents
import { listDocs } from "@plantagoai/firebase-core";
import { where, orderBy } from "firebase/firestore";
const result = await listDocs<Proposal>("proposals", {
constraints: [where("status", "==", "active"), orderBy("created_at", "desc")],
pageSize: 20,
cursor: lastDoc, // from previous page
});
// result: { items: [...], lastDoc: DocumentSnapshot, hasMore: boolean }
Create Document
import { createDoc } from "@plantagoai/firebase-core";
// Auto-generated ID
const id = await createDoc("proposals", { title: "New proposal", status: "draft" });
// Specified ID
const id = await createDoc("proposals", { title: "New proposal" }, "my-doc-id");
Update Document
import { updateDoc } from "@plantagoai/firebase-core";
await updateDoc("proposals", "abc123", { status: "active", updated_at: new Date().toISOString() });
Delete Document
import { deleteDoc } from "@plantagoai/firebase-core";
await deleteDoc("proposals", "abc123");
Batch Write
Executes multiple operations atomically (up to 500 per batch, auto-chunked):
import { batchWrite } from "@plantagoai/firebase-core";
await batchWrite([
{ type: "set", collection: "votes", docId: "v1", data: { proposal_id: "p1", option_id: "opt1" } },
{ type: "update", collection: "proposals", docId: "p1", data: { total_votes: 42 } },
{ type: "delete", collection: "drafts", docId: "d1" },
]);
Tenant-Scoped Queries
All tenant functions automatically filter by tenant_id and inject it on creation.
import { tenantQuery, tenantDoc, tenantCreate } from "@plantagoai/firebase-core";
const ctx = { tenantId: "org-123", tenantField: "tenant_id" };
// List docs for this tenant
const result = await tenantQuery<Product>(ctx, "products", { pageSize: 50 });
// Get single doc (returns null if wrong tenant)
const product = await tenantDoc<Product>(ctx, "products", "prod-1");
// Create with tenant_id auto-injected
const id = await tenantCreate(ctx, "products", { name: "Widget", price: 9.99 });
TenantContext
| Field | Type | Default | Description |
|---|---|---|---|
tenantId |
string |
required | Tenant identifier |
tenantField |
string |
"tenant_id" |
Field name used for tenant scoping |
Server-Side Admin
import { initAdmin, getAdminDb, getAdminAuth, adminTenantQuery, adminBatchWrite } from "@plantagoai/firebase-core/admin";
initAdmin(); // uses GOOGLE_APPLICATION_CREDENTIALS or default
const db = getAdminDb();
const auth = getAdminAuth();
// Tenant-scoped server query
const query = adminTenantQuery("products", "org-123");
const snapshot = await query.get();
// Server batch write
await adminBatchWrite([
{ type: "set", collection: "logs", docId: "log-1", data: { action: "created" } },
{ type: "delete", collection: "temp", docId: "t1" },
]);
Types
PaginatedResult<T>
interface PaginatedResult<T> {
items: T[];
lastDoc: DocumentSnapshot | null;
hasMore: boolean;
}
BatchOp
interface BatchOp {
type: "set" | "update" | "delete";
collection: string;
docId: string;
data?: Record<string, unknown>;
}
AppEnv
type AppEnv = "local" | "devnet" | "production";