import type { RawSqlRunner } from "../../security/rls-enforcement"; /** * Trigger-based Change Data Capture (CDC). * * The preferred CDC source is the write-ahead log (logical replication), which * — like Supabase Realtime — sees *every* commit regardless of how it was made. * When logical replication is unavailable (managed Postgres without * `wal_level=logical`, no replication privilege, no `REPLICA IDENTITY`), this * trigger-based fallback provides the same guarantee at the row level: * * AFTER INSERT/UPDATE/DELETE trigger → pg_notify('rebase_cdc', payload) * * A single dedicated LISTEN client per backend instance consumes the channel * (see {@link CdcListener}) and feeds the change into the existing * `RealtimeService.notifyUpdate` pipeline, so subscribers see the change no * matter what wrote it — psql, a cron in another service, raw Drizzle/SQL, or * the Studio SQL editor. * * Provisioning runs from the framework's own bootstrap as the owner (server) * context, alongside the RLS role provisioning. It is idempotent. */ /** Postgres NOTIFY channel carrying database-level change events. */ export declare const CDC_CHANNEL = "rebase_cdc"; /** Schema-qualified name of the generic trigger function. */ export declare const CDC_TRIGGER_FUNCTION = "rebase.rebase_cdc_notify"; /** Name of the per-table trigger (unqualified — triggers are namespaced by table). */ export declare const CDC_TRIGGER_NAME = "rebase_cdc_trigger"; /** * SQL that (re)creates the generic CDC trigger function. Safe to run repeatedly: * `CREATE OR REPLACE` updates in place without dropping dependent triggers. * * The function emits `{ schema, table, op, row }`. The `row` is the full changed * tuple (NEW for insert/update, OLD for delete) so the consumer can route it to * a collection and extract the primary key. It is *not* trusted for delivery: * the consumer marks the row invalidated and each subscriber re-reads it under * its own RLS context, so a subscriber never receives a row it cannot read. */ export declare function buildCdcFunctionSql(): string; /** * SQL that (re)attaches the CDC trigger to a single table. `DROP ... IF EXISTS` * before `CREATE` keeps it idempotent and picks up any function signature change. */ export declare function buildCdcTriggerSql(schema: string, table: string): string; export interface CdcTableRef { schema: string; table: string; } export interface ProvisionResult { /** Tables the trigger was successfully attached to. */ installed: CdcTableRef[]; /** Tables that could not be provisioned (e.g. not yet migrated), with the error. */ skipped: Array; } /** * Idempotently install the CDC trigger function and per-table triggers. * * Runs as the owner (server) connection at bootstrap. A table that does not yet * exist in the database (schema drift) is skipped with a warning rather than * aborting the whole install, so one un-migrated collection cannot disable CDC * for the rest. */ export declare function provisionTriggerCdc(run: RawSqlRunner, tables: CdcTableRef[]): Promise;