import { AuthAdapter, BackendBootstrapper, BootstrappedAuth, DatabaseAdapter, DataDriver, DataSourceDefinition, CollectionCallbacks, CollectionConfig, HealthCheckResult, HistoryConfig, RealtimeProvider, SecurityRule } from "@rebasepro/types"; import { BackendCollectionRegistry } from "./collections/BackendCollectionRegistry"; import { DriverRegistry } from "./services/driver-registry"; import { Server } from "http"; import { Hono } from "hono"; import { HonoEnv } from "./api/types"; import { BackendStorageConfig, StorageController, StorageRegistry } from "./storage"; import { EmailConfig } from "./email"; import type { OAuthProvider } from "./auth/interfaces"; import type { AuthHooks } from "./auth/auth-hooks"; export interface RebaseAuthConfig { /** * The collection that represents auth users. * * When provided, this collection's underlying database table is used * for all auth operations (login, registration, password reset, etc.). * * Import the built-in default: * ```ts * import { defaultUsersCollection } from "@rebasepro/common"; * auth: { collection: defaultUsersCollection, jwtSecret: "..." } * ``` * * Or pass your own collection with the required auth fields * (email, passwordHash, displayName, etc.). */ collection?: CollectionConfig; jwtSecret?: string; accessExpiresIn?: string; refreshExpiresIn?: string; requireAuth?: boolean; allowRegistration?: boolean; /** * Opt-in: expose `POST /auth/find-user` so an authenticated user can resolve * an email address to a minimal public profile (`uid`, `displayName`, * `photoURL` only). This powers invite-by-email flows without a custom * admin server function. Off by default because it enables user enumeration * by any signed-in user. Available on the client as `auth.findUserByEmail`. */ allowUserLookup?: boolean; /** * A static secret key for server-to-server / script authentication. * * When a request includes `Authorization: Bearer `, it is * granted admin-level access without JWT verification. This is the * Rebase equivalent of a Service Account key. * * Generate with: `node -e "logger.info(require('crypto').randomBytes(48).toString('base64'))"` * * Set via `REBASE_SERVICE_KEY` in your `.env`. * Must be at least 32 characters. */ serviceKey?: string; email?: EmailConfig; google?: { clientId: string; clientSecret?: string; }; linkedin?: { clientId: string; clientSecret: string; }; github?: { clientId: string; clientSecret: string; }; microsoft?: { clientId: string; clientSecret: string; tenantId?: string; }; apple?: { clientId: string; teamId: string; keyId: string; privateKey: string; }; facebook?: { clientId: string; clientSecret: string; }; twitter?: { clientId: string; clientSecret: string; }; discord?: { clientId: string; clientSecret: string; }; gitlab?: { clientId: string; clientSecret: string; baseUrl?: string; }; bitbucket?: { clientId: string; clientSecret: string; }; slack?: { clientId: string; clientSecret: string; }; spotify?: { clientId: string; clientSecret: string; }; defaultRole?: string; /** * Canonical array of OAuth providers. * * This is the primary extension point for **all** OAuth integrations. * Each entry is an `OAuthProvider` constructed via one of * the `create*Provider` factories exported from `@rebasepro/server-core` * (e.g. `createGoogleProvider`, `createGitHubProvider`). * * The named convenience fields above (`google`, `github`, etc.) are * automatically resolved into this array at startup. You can mix both * approaches; named fields and explicit entries are merged (named * fields are appended after explicit entries). * * @example * ```ts * import { createGoogleProvider } from "@rebasepro/server-core"; * * auth: { * providers: [ * createGoogleProvider({ clientId: "…", clientSecret: "…" }), * ], * } * ``` */ providers?: OAuthProvider[]; /** * Override specific parts of the built-in auth implementation. * * Each override replaces one piece of the default behavior while * keeping everything else intact. Unset overrides fall through * to the built-in defaults (scrypt passwords, standard validation, etc.). * * @example bcrypt passwords with a custom hash * ```ts * import bcrypt from "bcrypt"; * * hooks: { * hashPassword: (pw) => bcrypt.hash(pw, 12), * verifyPassword: (pw, hash) => bcrypt.compare(pw, hash), * } * ``` */ hooks?: AuthHooks; /** * Enable magic link (passwordless email) authentication. * Requires email to be configured. */ magicLink?: boolean; /** * Opt-in httpOnly cookie mode for refresh tokens. * * When set, the refresh token is delivered as an `httpOnly`, `Secure`, * `SameSite` cookie instead of in the JSON response body. This * prevents XSS from stealing the long-lived refresh token. * * The access token remains in the JSON body so the client can use it * in `Authorization: Bearer` headers for API calls. * * **Requires** `credentials: "include"` on client-side fetch calls to * auth endpoints, and CORS must allow credentials (no `origin: "*"`). */ cookieAuth?: import("./auth").CookieAuthConfig; } export interface RebaseBackendConfig { collections?: CollectionConfig[]; collectionsDir?: string; server: Server; app: Hono; basePath?: string; /** * Declared data sources, shared with the frontend ``. * * Used to resolve each collection's engine (capabilities) and transport. * Collections on a `direct`/`custom` transport are client-only: the backend * still owns their schema/registry but does **not** generate server data * routes for them. Server-mediated sources (the default) need no entry. */ dataSources?: DataSourceDefinition[]; /** * Database bootstrappers. */ bootstrappers?: BackendBootstrapper[]; /** * Database adapter. * * When set, this takes precedence over `bootstrappers`. * * @example * ```ts * import { createPostgresAdapter } from "@rebasepro/server-postgresql"; * database: createPostgresAdapter({ connection: db, schema }), * ``` */ database?: DatabaseAdapter; logging?: { level?: "error" | "warn" | "info" | "debug"; }; /** * Authentication configuration. * * Accepts **either**: * - `RebaseAuthConfig` — built-in configuration * - `AuthAdapter` — pluggable adapter for external auth (Clerk, Auth0, etc.) * * When a plain config object is provided, the built-in adapter is created * automatically from the bootstrapper's `initializeAuth()` result. */ auth?: RebaseAuthConfig | AuthAdapter; /** * Storage configuration. Accepts: * * - A `BackendStorageConfig` object (`{ type: 'local' | 's3' | 'gcs', ... }`) * - A `StorageController` instance (for custom providers like Azure, etc.) * - A `Record` of either, for multi-backend setups */ storage?: BackendStorageConfig | StorageController | Record; /** * Declared storage sources. Drives the client-side StorageSourceRegistry * and the transport distinction (server vs direct). * * Server-backed sources are auto-derived from the `storage` map — you * only need explicit entries for "direct" transport sources (e.g. * external storage) that the backend does not proxy. */ storageSources?: import("@rebasepro/types").StorageSourceDefinition[]; /** * Entity history / audit-log configuration. * * - `true` — enable history with default settings * - `{ retention?: number }` — enable with optional retention period (days) */ history?: HistoryConfig; /** * Default security rules applied to any collection that does not define * its own `securityRules`. Opt-in — if not set, collections without * explicit rules remain unrestricted (beyond `requireAuth`). * * @example * ```ts * defaultSecurityRules: [ * { operation: "select", access: "public" }, * { operations: ["insert", "update", "delete"], roles: ["admin"] } * ] * ``` */ defaultSecurityRules?: SecurityRule[]; enableSwagger?: boolean; functionsDir?: string; cronsDir?: string; /** * Enable/disable database persistence for cron job execution logs. * When set to false, cron jobs will run but logs will not be persisted to the database. * Default: true. */ cronPersistence?: boolean; /** * Maximum request body size in bytes for API routes (default: 10MB). * Set to 0 to disable the global limit entirely. * * Note: Storage upload routes use their own limit from the storage config's * `maxFileSize` property (default: 50MB), which takes precedence over this. */ maxBodySize?: number; /** * CSRF protection configuration. **Opt-in** — disabled by default. * * BaaS APIs are consumed by mobile apps, SPAs on different domains, * and CLI tools, so CSRF is intentionally not enabled unless you * explicitly configure it with allowed origins. * * @example * ```ts * csrf: { origin: ["https://myapp.com", "https://admin.myapp.com"] } * ``` */ csrf?: { /** Allowed origins for CSRF validation. */ origin: string | string[] | ((origin: string) => boolean); }; /** * Global lifecycle callbacks applied to every collection. * * Same type as per-collection `callbacks` — fires on **every** data path * (REST API, WebSocket / realtime, server-side `rebase.data`). * * Execution order: global callbacks → collection callbacks → property callbacks. * * @example * ```ts * callbacks: { * afterRead({ row, collection }) { * console.log(`Read ${collection.slug}/${row.id}`); * return row; * } * } * ``` */ callbacks?: CollectionCallbacks; } /** * Type guard to detect whether the `auth` config is an `AuthAdapter` * (has a `verifyRequest` method) vs a plain `RebaseAuthConfig` (plain object). */ export declare function isAuthAdapter(auth: RebaseAuthConfig | AuthAdapter): auth is AuthAdapter; /** * Type guard to detect whether `database` is a `DatabaseAdapter`. */ export declare function isDatabaseAdapter(db: unknown): db is DatabaseAdapter; export interface RebaseBackendInstance { driverRegistry: DriverRegistry; driver: DataDriver; realtimeServices: Record; realtimeService: RealtimeProvider; auth?: BootstrappedAuth; history?: { historyService: import("./history/history-routes").HistoryService; }; storageRegistry?: StorageRegistry; storageController?: StorageController; collectionRegistry: BackendCollectionRegistry; cronScheduler?: import("./cron").CronScheduler; /** * Deep health check that verifies database connectivity. * Returns latency and component status. */ healthCheck(): Promise; /** * Graceful shutdown helper for the BaaS instance. * Stops the cron scheduler and closes the HTTP server, allowing * in-flight requests to drain within the given timeout. * * @param timeoutMs - Maximum time (ms) to wait for drain before force-exit (default: 15000). * Pass 0 to skip the force-exit timer (useful in tests). */ shutdown(timeoutMs?: number): Promise; } export declare function initializeRebaseBackend(config: RebaseBackendConfig): Promise;