/** * Describes a named storage backend — a place files live. * * Declared once and shared front + back: the frontend uses it to decide * transport (HTTP proxy vs direct SDK), the backend uses the same `key` * to resolve a StorageController, and collection properties reference * a definition by its `key` via `StorageConfig.storageSource`. * * This mirrors the {@link DataSourceDefinition} pattern used for databases. * * @group Models */ /** * The default storage source key, used when a property does not specify * a `storageSource`. Shared by the frontend and backend registries so * both agree on "the default storage backend". * @group Models */ export declare const DEFAULT_STORAGE_SOURCE_KEY = "(default)"; /** * How the *frontend* reaches a storage backend. * * - `"server"` — through the Rebase backend REST API (`/api/storage`). * The backend holds the actual `StorageController` and routes by * storage-source key. This is the default and covers Local, S3, GCS, * and any other server-mediated engine. * - `"direct"` — straight from the client to the external backend via * its own SDK (e.g. Firebase Storage via `@firebase/storage`). * The Rebase backend is **not** in the upload/download path. * * @group Models */ export type StorageSourceTransport = "server" | "direct"; /** * Declarative definition of a storage source — a named place files live. * * Declared once and shared front and back: the frontend uses it to decide * transport (client HTTP proxy vs direct provider SDK), the backend uses * the same `key` to resolve a `StorageController`, and collection * properties reference a definition by its `key` via * `StorageConfig.storageSource`. * * @group Models */ export interface StorageSourceDefinition { /** * Unique identifier for this storage source. Collection properties * point at it via `StorageConfig.storageSource`. * Defaults to {@link DEFAULT_STORAGE_SOURCE_KEY}. */ key: string; /** * The engine backing this storage source (e.g. `"local"`, `"s3"`, * `"gcs"`, `"firebase"`, `"azure"`, or a custom id). */ engine: string; /** * The credential set this source signs with, when several sources share one. * * ## What it is for * * Every binding a bucket needs is read per key — `S3_BUCKET__MEDIA`, * `S3_ACCESS_KEY_ID__MEDIA`, and so on. That is right for the bucket *name*, * which is different for every source by definition, and wrong for the * credentials, which usually are not: fifteen buckets on one MinIO install * meant fifteen copies of the same endpoint, access key and secret — ninety * variables where eighteen would do, and one key rotation became fifteen * paired edits where a single missed one fails at upload time with an opaque * signing error. * * Naming an account here lets the *account-scoped* bindings fall back to * `__` when no per-key value is set. The bucket name never * falls back: it is what distinguishes one source from another. * * ## Why it does not fall back to the bare variable * * A source with no `account` reads only its own suffixed names, exactly as * before — so every project that predates this is wire-identical. The * unsuffixed `S3_ACCESS_KEY_ID` belongs to the *default* source, and letting * a named bucket inherit it would mean a typo'd key silently signs with * another source's credentials. Two forms, both explicit, opt-in. */ account?: string; /** * How the frontend reaches this storage. Defaults to `"server"`. * * When `"direct"`, the client uses a provider-specific SDK * (e.g. `@firebase/storage`) and the backend does not proxy * upload/download traffic for this source. */ transport: StorageSourceTransport; /** * Serve unqualified uploads — a storage property naming no `storageSource` * — from this source. * * Declared, never inferred. A project with named buckets and no default * used to have one chosen for it by declaration order, with a warning, and * the choice differed between development and production because the * synthesized local default is dropped in production and the promotion was * not. Where the files land is the author's decision; boot now fails * without one. */ default?: boolean; /** Human-readable label for the UI (e.g. "Firebase Storage", "S3 Media"). */ label?: string; } /** * A resolved storage source: the single source of truth that the frontend * router and backend registry both derive from. * * @group Models */ export interface ResolvedStorageSource { /** Storage source key (routing key, shared front + back). */ key: string; /** Engine backing the source. */ engine: string; /** Frontend transport. */ transport: StorageSourceTransport; /** Human-readable label. */ label?: string; } /** * The environment-variable suffix for a storage or data source key. * * `""` for the default source — so a single-bucket project keeps configuring * plain `S3_BUCKET` — and `__` for every named one, uppercased with * non-alphanumerics collapsed to underscores: `media-cdn` → `S3_BUCKET__MEDIA_CDN`. * * The rule derives the variable name from the declared key rather than * discovering keys by scanning the environment. Scanning would have to guess how * `S3_BUCKET__MEDIA_CDN` splits into a key; deriving cannot be ambiguous, and a * typo surfaces as a missing source at boot instead of a silently ignored * variable. * * It lives in this package, with no dependencies, because four things must agree * on it exactly: the CLI (validating a build), the runtime (reading its own * environment), the control plane (writing a tenant's Secret), and the docs. A * second implementation of a naming convention is a second chance to disagree. * * @group Models */ export declare function storageEnvSuffix(key: string, defaultKey?: string): string; /** * Two distinct keys that collapse onto the same variable name, or `null`. * * `media-cdn` and `media_cdn` are different source keys but the same suffix, so * without this one of them silently reads the other's configuration. Returns the * offending pair rather than throwing, so each caller can raise it in its own * idiom — a `BundleError` at boot, a build failure in the CLI, a rejected deploy * in a control plane. * * @group Models */ export declare function findStorageSuffixCollision(keys: string[], defaultKey?: string): { a: string; b: string; suffix: string; } | null;