import type { Enum } from '../../../../shared/src/enum.ts';
import * as v from '../../../../shared/src/valita.ts';
import type { Source } from '../../types/streams.ts';
import { type Change } from '../change-source/protocol/current/data.ts';
import type { ReplicatorMode } from '../replicator/replicator.ts';
import type { Service } from '../service.ts';
import * as ErrorType from './error-type-enum.ts';
type ErrorType = Enum<typeof ErrorType>;
/**
 * The ChangeStreamer is the component between replicators ("subscribers")
 * and a canonical upstream source of changes (e.g. a Postgres logical
 * replication slot). It facilitates multiple subscribers without incurring
 * the associated upstream expense (e.g. PG replication slots are resource
 * intensive) with a "forward-store-ack" procedure.
 *
 * * Changes from the upstream source are immediately **forwarded** to
 *   connected subscribers to minimize latency.
 *
 * * They are then **stored** in a separate DB to facilitate catchup
 *   of connecting subscribers that are behind.
 *
 * * **Acknowledgements** are sent upstream after they are successfully
 *   stored.
 *
 * Unlike Postgres replication slots, in which the progress of a static
 * subscriber is tracked in the replication slot, the ChangeStreamer
 * supports a dynamic set of subscribers (i.e.. zero-caches) that can
 * can continually change.
 *
 * However, it is not the case that the ChangeStreamer needs to support
 * arbitrarily old subscribers. Because the replica is continually
 * backed up to a global location and used to initialize new subscriber
 * tasks, an initial subscription request from a subscriber constitutes
 * a signal for how "behind" a new subscriber task can be. This is
 * reflected in the {@link SubscriberContext}, which indicates whether
 * the watermark corresponds to an "initial" watermark derived from the
 * replica at task startup.
 *
 * The ChangeStreamer uses a combination of this signal with ACK
 * responses from connected subscribers to determine the watermark up
 * to which it is safe to purge old change log entries.
 */
export interface ChangeStreamer {
    /**
     * Subscribes to changes based on the supplied subscriber `ctx`,
     * which indicates the watermark at which the subscriber is up to
     * date.
     */
    subscribe(ctx: SubscriberContext): Promise<Source<Downstream>>;
}
export declare const PROTOCOL_VERSION = 6;
export type SubscriberContext = {
    /**
     * The supported change-streamer protocol version.
     */
    protocolVersion: number;
    /**
     * Task ID. This is used to link the request with a preceding snapshot
     * reservation.
     */
    taskID: string | null;
    /**
     * Subscriber id. This is only used for debugging.
     */
    id: string;
    /**
     * The ReplicatorMode of the subscriber. 'backup' indicates that the
     * subscriber is local to the `change-streamer` in the `replication-manager`,
     * while 'serving' indicates that user-facing requests depend on the subscriber.
     */
    mode: ReplicatorMode;
    /**
     * The ChangeStreamer will return an Error if the subscriber is
     * on a different replica version (i.e. the initial snapshot associated
     * with the replication slot).
     */
    replicaVersion: string;
    /**
     * The watermark up to which the subscriber is up to date.
     * Only changes after the watermark will be streamed.
     */
    watermark: string;
    /**
     * Whether this is the first subscription request made by the task,
     * i.e. indicating that the watermark comes from a restored replica
     * backup. The ChangeStreamer uses this to determine which changes
     * are safe to purge from the Storer.
     */
    initial: boolean;
};
export type ChangeEntry = {
    change: Change;
    /**
     * Note that it is technically possible for multiple changes to have
     * the same watermark, but that of a commit is guaranteed to be final,
     * so subscribers should only store the watermark of commit changes.
     */
    watermark: string;
};
/**
 * The StatusMessage payload for now is empty, but can be extended to
 * include meta-level information in the future.
 */
export declare const statusSchema: v.ObjectType<{
    tag: v.Type<"status">;
    lagReport: v.Optional<{
        lastTimings?: {
            sendTimeMs: number;
            commitTimeMs: number;
            receiveTimeMs: number;
        } | undefined;
        nextSendTimeMs: number;
    }>;
}, undefined>;
export type Status = v.Infer<typeof statusSchema>;
export declare const statusMessageSchema: v.TupleType<[v.Type<"status">, v.ObjectType<{
    tag: v.Type<"status">;
    lagReport: v.Optional<{
        lastTimings?: {
            sendTimeMs: number;
            commitTimeMs: number;
            receiveTimeMs: number;
        } | undefined;
        nextSendTimeMs: number;
    }>;
}, undefined>]>;
/**
 * A StatusMessage will be immediately sent on a (v2+) subscription to
 * indicate that the subscription is valid (i.e. starting at the requested
 * watermark). Invalid subscriptions will instead result in a
 * SubscriptionError as the first message.
 */
export type StatusMessage = v.Infer<typeof statusMessageSchema>;
declare const subscriptionErrorSchema: v.ObjectType<{
    type: v.Type<number>;
    message: v.Optional<string>;
}, undefined>;
export type SubscriptionError = v.Infer<typeof subscriptionErrorSchema>;
declare const errorSchema: v.TupleType<[v.Type<"error">, v.ObjectType<{
    type: v.Type<number>;
    message: v.Optional<string>;
}, undefined>]>;
export declare const downstreamSchema: v.UnionType<[v.TupleType<[v.Type<"status">, v.ObjectType<{
    tag: v.Type<"status">;
    lagReport: v.Optional<{
        lastTimings?: {
            sendTimeMs: number;
            commitTimeMs: number;
            receiveTimeMs: number;
        } | undefined;
        nextSendTimeMs: number;
    }>;
}, undefined>]>, v.UnionType<[v.TupleType<[v.Type<"begin">, v.ObjectType<{
    tag: v.Type<"begin">;
    json: v.Optional<"p" | "s">;
    skipAck: v.Optional<boolean>;
}, undefined>, v.ObjectType<{
    commitWatermark: v.Type<string>;
}, undefined>]>, v.TupleType<[v.Type<"data">, v.UnionType<[v.UnionType<[v.ObjectType<{
    tag: v.Type<"insert">;
    relation: v.Type<{
        rowKey: {
            type?: "default" | "nothing" | "full" | "index" | undefined;
            columns: string[];
        };
        keyColumns?: string[] | undefined;
        replicaIdentity?: "default" | "nothing" | "full" | "index" | undefined;
        schema: string;
        name: string;
    }>;
    new: v.Type<Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue>>;
}, undefined>, v.ObjectType<{
    tag: v.Type<"update">;
    relation: v.Type<{
        rowKey: {
            type?: "default" | "nothing" | "full" | "index" | undefined;
            columns: string[];
        };
        keyColumns?: string[] | undefined;
        replicaIdentity?: "default" | "nothing" | "full" | "index" | undefined;
        schema: string;
        name: string;
    }>;
    key: v.Type<Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue> | null>;
    new: v.Type<Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue>>;
}, undefined>, v.ObjectType<{
    tag: v.Type<"delete">;
    relation: v.Type<{
        rowKey: {
            type?: "default" | "nothing" | "full" | "index" | undefined;
            columns: string[];
        };
        keyColumns?: string[] | undefined;
        replicaIdentity?: "default" | "nothing" | "full" | "index" | undefined;
        schema: string;
        name: string;
    }>;
    key: v.Type<Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue>>;
}, undefined>, v.ObjectType<{
    tag: v.Type<"truncate">;
    relations: v.ArrayType<v.Type<{
        rowKey: {
            type?: "default" | "nothing" | "full" | "index" | undefined;
            columns: string[];
        };
        keyColumns?: string[] | undefined;
        replicaIdentity?: "default" | "nothing" | "full" | "index" | undefined;
        schema: string;
        name: string;
    }>>;
}, undefined>, v.ObjectType<{
    tag: v.Type<"backfill">;
    relation: v.ObjectType<{
        schema: v.Type<string>;
        name: v.Type<string>;
        rowKey: v.ObjectType<{
            columns: v.ArrayType<v.Type<string>>;
            type: v.Optional<"default" | "nothing" | "full" | "index">;
        }, undefined>;
    }, undefined>;
    columns: v.ArrayType<v.Type<string>>;
    watermark: v.Type<string>;
    rowValues: v.ArrayType<v.ArrayType<v.Type<import("../../../../shared/src/bigint-json.ts").JSONValue>>>;
    status: v.Optional<{
        totalBytes?: number | undefined;
        rows: number;
        totalRows: number;
    }>;
}, undefined>]>, v.UnionType<[v.ObjectType<{
    tag: v.Type<"create-table">;
    spec: v.ObjectType<Omit<{
        name: v.Type<string>;
        columns: v.Type<Record<string, {
            pgTypeClass?: "e" | "d" | "b" | "c" | "p" | "r" | "m" | undefined;
            elemPgTypeClass?: "e" | "d" | "b" | "c" | "p" | "r" | "m" | null | undefined;
            characterMaximumLength?: number | null | undefined;
            notNull?: boolean | null | undefined;
            dflt?: string | null | undefined;
            pos: number;
            dataType: string;
        }>>;
        primaryKey: v.Optional<string[]>;
    }, "schema"> & {
        schema: v.Type<string>;
    }, undefined>;
    metadata: v.Optional<{
        [x: string]: import("../../../../shared/src/bigint-json.ts").JSONValue;
        rowKey: Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue>;
    }>;
    backfill: v.Optional<Record<string, Record<string, import("../change-source/protocol/current.ts").JSONValue | undefined>>>;
}, undefined>, v.ObjectType<{
    tag: v.Type<"rename-table">;
    old: v.ObjectType<{
        schema: v.Type<string>;
        name: v.Type<string>;
    }, undefined>;
    new: v.ObjectType<{
        schema: v.Type<string>;
        name: v.Type<string>;
    }, undefined>;
}, undefined>, v.ObjectType<{
    tag: v.Type<"update-table-metadata">;
    table: v.ObjectType<{
        schema: v.Type<string>;
        name: v.Type<string>;
    }, undefined>;
    old: v.ObjectType<{
        rowKey: v.Type<Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue>>;
    }, v.Type<import("../../../../shared/src/bigint-json.ts").JSONValue>>;
    new: v.ObjectType<{
        rowKey: v.Type<Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue>>;
    }, v.Type<import("../../../../shared/src/bigint-json.ts").JSONValue>>;
}, undefined>, v.ObjectType<{
    tag: v.Type<"add-column">;
    table: v.ObjectType<{
        schema: v.Type<string>;
        name: v.Type<string>;
    }, undefined>;
    column: v.ObjectType<{
        name: v.Type<string>;
        spec: v.ObjectType<{
            pos: v.Type<number>;
            dataType: v.Type<string>;
            pgTypeClass: v.Optional<"e" | "d" | "b" | "c" | "p" | "r" | "m">;
            elemPgTypeClass: v.Optional<"e" | "d" | "b" | "c" | "p" | "r" | "m" | null>;
            characterMaximumLength: v.Optional<number | null>;
            notNull: v.Optional<boolean | null>;
            dflt: v.Optional<string | null>;
        }, undefined>;
    }, undefined>;
    tableMetadata: v.Optional<{
        [x: string]: import("../../../../shared/src/bigint-json.ts").JSONValue;
        rowKey: Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue>;
    }>;
    backfill: v.Optional<Record<string, import("../change-source/protocol/current.ts").JSONValue | undefined>>;
}, undefined>, v.ObjectType<{
    tag: v.Type<"update-column">;
    table: v.ObjectType<{
        schema: v.Type<string>;
        name: v.Type<string>;
    }, undefined>;
    old: v.ObjectType<{
        name: v.Type<string>;
        spec: v.ObjectType<{
            pos: v.Type<number>;
            dataType: v.Type<string>;
            pgTypeClass: v.Optional<"e" | "d" | "b" | "c" | "p" | "r" | "m">;
            elemPgTypeClass: v.Optional<"e" | "d" | "b" | "c" | "p" | "r" | "m" | null>;
            characterMaximumLength: v.Optional<number | null>;
            notNull: v.Optional<boolean | null>;
            dflt: v.Optional<string | null>;
        }, undefined>;
    }, undefined>;
    new: v.ObjectType<{
        name: v.Type<string>;
        spec: v.ObjectType<{
            pos: v.Type<number>;
            dataType: v.Type<string>;
            pgTypeClass: v.Optional<"e" | "d" | "b" | "c" | "p" | "r" | "m">;
            elemPgTypeClass: v.Optional<"e" | "d" | "b" | "c" | "p" | "r" | "m" | null>;
            characterMaximumLength: v.Optional<number | null>;
            notNull: v.Optional<boolean | null>;
            dflt: v.Optional<string | null>;
        }, undefined>;
    }, undefined>;
}, undefined>, v.ObjectType<{
    tag: v.Type<"drop-column">;
    table: v.ObjectType<{
        schema: v.Type<string>;
        name: v.Type<string>;
    }, undefined>;
    column: v.Type<string>;
}, undefined>, v.ObjectType<{
    tag: v.Type<"drop-table">;
    id: v.ObjectType<{
        schema: v.Type<string>;
        name: v.Type<string>;
    }, undefined>;
}, undefined>, v.ObjectType<{
    tag: v.Type<"create-index">;
    spec: v.ObjectType<Omit<{
        name: v.Type<string>;
        tableName: v.Type<string>;
        unique: v.Type<boolean>;
        columns: v.Type<Record<string, "ASC" | "DESC">>;
    }, "schema"> & {
        schema: v.Type<string>;
    }, undefined>;
}, undefined>, v.ObjectType<{
    tag: v.Type<"drop-index">;
    id: v.ObjectType<{
        schema: v.Type<string>;
        name: v.Type<string>;
    }, undefined>;
}, undefined>, v.ObjectType<{
    tag: v.Type<"backfill-completed">;
    relation: v.ObjectType<{
        schema: v.Type<string>;
        name: v.Type<string>;
        rowKey: v.ObjectType<{
            columns: v.ArrayType<v.Type<string>>;
            type: v.Optional<"default" | "nothing" | "full" | "index">;
        }, undefined>;
    }, undefined>;
    columns: v.ArrayType<v.Type<string>>;
    watermark: v.Type<string>;
    status: v.Optional<{
        totalBytes?: number | undefined;
        rows: number;
        totalRows: number;
    }>;
}, undefined>]>]>]>, v.TupleType<[v.Type<"commit">, v.ObjectType<{
    tag: v.Type<"commit">;
}, undefined>, v.ObjectType<{
    watermark: v.Type<string>;
}, undefined>]>, v.TupleType<[v.Type<"rollback">, v.ObjectType<{
    tag: v.Type<"rollback">;
}, undefined>]>]>, v.TupleType<[v.Type<"error">, v.ObjectType<{
    type: v.Type<number>;
    message: v.Optional<string>;
}, undefined>]>]>;
export type Error = v.Infer<typeof errorSchema>;
export declare function errorTypeToReadableName(val: ErrorType): "WrongReplicaVersion" | "WatermarkTooOld" | "Unknown";
/**
 * A stream of transactions, each starting with a {@link Begin} message,
 * containing one or more {@link Data} messages, and ending with a
 * {@link Commit} or {@link Rollback} message. The 'commit' tuple
 * includes a `watermark` that should be stored with the committed
 * data and used for resuming a subscription (e.g. in the
 * {@link SubscriberContext}).
 *
 * A {@link SubscriptionError} indicates an unrecoverable error that requires
 * manual intervention (e.g. configuration / operational error).
 */
export type Downstream = v.Infer<typeof downstreamSchema>;
export interface ChangeStreamerService extends ChangeStreamer, Service {
    /**
     * Notifies the change streamer of a watermark that has been backed up,
     * indicating that changes before the watermark can be purged if active
     * subscribers have progressed beyond the watermark.
     */
    scheduleCleanup(watermark: string): void;
    getChangeLogState(): Promise<{
        replicaVersion: string;
        minWatermark: string;
    }>;
}
export {};
//# sourceMappingURL=change-streamer.d.ts.map