import { R as RecordSource, D as Database, O as OrderedSourceConfig, C as ContrailConfig, a as RecordRow, S as Statement, A as AtprotoServiceAuthMethod, L as Logger, b as ResolvedContrailConfig } from './types-CmjW-xL4.js'; import { P as PublicServiceOptions, b as ChangeConsumers, S as ScheduledIngestOptions, D as DeliveryHandlers, C as CurrentBootstrapRuntimeHandlers, a as DeliveryRuntimeOptions } from './public-service-DlLFyNXn.js'; import { S as SourcePosition } from './sources-DKyp_dDd.js'; import { I as IngestDiagnostic, a as BackfillAllOptions, B as BackfillRetryOptions, b as BackfillRetryResult } from './backfill-Bmp9TtLS.js'; import { B as BackfillStatus } from './status-BRg9Abty.js'; import { Hono } from 'hono'; import { AtprotoDid, AtprotoAudience, Nsid } from '@atcute/lexicons/syntax'; import { VerifiedJwt } from '@atcute/xrpc-server/auth'; /** Prune a single actor's feed for one collection to `cap`. Bounded O(cap). */ declare function pruneActorFeed(db: Database, actor: string, collection: string, cap: number): Promise; interface FeedSweepResult { /** Rows deleted this slice. */ pruned: number; /** Actor to resume after; null once a full pass completed (wrap to start). */ nextCursor: string | null; /** True when this slice reached the end of the actor list. */ done: boolean; } /** * One bounded slice of a rolling feed-items prune. * * Pages at most `actorBudget` distinct actors (resuming after `cursor`, via the * feed_items (actor, uri) PK) and applies the per-(actor, collection) cutoff * delete for every cap in `caps`. Every issued statement is index-backed and * O(cap), so the slice's per-query CPU stays flat no matter how large * feed_items grows — the property the old global window query lacked. * * Drive it across ticks with a persisted cursor (see getFeedPruneCursor): * feed back `nextCursor` until `done`, at which point the cursor wraps to null * and the next pass starts from the beginning. Because each pass visits every * actor, this doubles as the recovery path for an already-bloated table. */ declare function sweepFeedItems(db: Database, caps: Map, cursor: string | null, actorBudget: number): Promise; /** * Prune the ENTIRE feed_items table to the per-collection `caps` by looping the * bounded {@link sweepFeedItems} until a full pass completes. * * Every statement is O(cap) and safe against D1's per-query CPU limit, but the * statement count is O(distinct actors), so keep this OFF the hot ingest path — * the cron/persistent loops issue a single bounded slice per tick instead. Use * it for one-shot recovery or admin tooling. */ declare function pruneFeedItems(db: Database, caps: Map): Promise; /** Last actor swept by the rolling feed prune; null = start of a fresh pass. */ declare function getFeedPruneCursor(db: Database): Promise; declare function saveFeedPruneCursor(db: Database, actor: string | null): Promise; interface ServingSourcePosition { position: SourcePosition; updatedAt: number; } declare function getServingSourcePosition(db: Database): Promise; declare function saveServingSourcePositionStatement(db: Database, position: SourcePosition, updatedAt?: number): Statement; declare function assertServingSourceCompatibility(db: Database, orderedSource?: OrderedSourceConfig): Promise; declare function orderedSourcePosition(orderedSource: OrderedSourceConfig, cursor: number | string): SourcePosition; declare function saveOrderedSourcePositionStatement(db: Database, orderedSource: OrderedSourceConfig, timeUs: number, updatedAt?: number): Statement; declare function getLastCursor(db: Database): Promise; /** Save a legacy timestamp cursor monotonically without allowing an old * writer to move a database that has transitioned to the v2 seq domain back * into the timestamp domain. */ declare function saveCursorStatement(db: Database, timeUs: number): Statement; declare function saveCursor(db: Database, timeUs: number, orderedSource?: OrderedSourceConfig, observations?: Iterable): Promise; interface ExistingRecordInfo { cid: string | null; record: string | null; /** When the row was last written to our DB (microseconds). Populated * whenever `lookupExistingRecords` runs, regardless of `includeRecord`. */ indexed_at: number | null; } /** * Look up existing records for a set of events, grouped by collection. * Returns a map of uri → { cid, record }. * When includeRecord is false, record will always be null (saves reading large blobs). */ declare function lookupExistingRecords(db: Database, events: { uri: string; collection: string; }[], includeRecord?: boolean, config?: ContrailConfig): Promise>; interface SortOption { recordField?: string; countType?: string; direction: "asc" | "desc"; } interface QueryOptions { collection: string; did?: string; limit?: number; cursor?: string; filters?: Record; rangeFilters?: Record; countFilters?: Record; sort?: SortOption; search?: string; source?: RecordSource; } declare function queryRecords(db: Database, config: ContrailConfig, options: QueryOptions): Promise<{ records: (RecordRow & { counts?: Record; })[]; cursor?: string; }>; interface ServiceAuthResult { principal?: VerifiedJwt; response?: Response; } interface ServiceAuthGate { readonly serviceDid: AtprotoDid; readonly audience: AtprotoAudience; protects(method: AtprotoServiceAuthMethod): boolean; authorize(request: Request, method: Nsid): Promise; } /** Parse a canonical (DID-authority) record AT-URI into its components, or null * if it isn't a valid full record URI. Backed by atcute's validator, which * also enforces the DID / NSID / record-key character classes. */ declare function parseAtUri(uri: string): { did: string; collection: string; rkey: string; } | null; declare const MAX_NOTIFY_URIS = 25; interface NotifyResult { indexed: number; deleted: number; errors?: string[]; } /** * Process notify URIs: fetch from PDS, detect changes, apply events. * Shared by both the Hono route and the Contrail.notify() method. */ declare function processNotifyUris(db: Database, config: ContrailConfig, uris: string[]): Promise; declare function registerNotifyRoute(app: Hono, db: Database, config: ContrailConfig, serviceAuth?: ServiceAuthGate | null): void; declare class JetstreamLiveHistoryExpiredError extends Error { constructor(message: string, options?: ErrorOptions); } type JetstreamLiveEvent = { kind: "commit"; did: string; seq: number; time_us: number; commit: { operation: "create" | "update"; rev: string; collection: string; rkey: string; cid: string; record: unknown; } | { operation: "delete"; rev: string; collection: string; rkey: string; }; } | { kind: "identity"; did: string; seq: number; time_us: number; identity: { did: string; handle?: string; time?: string; }; } | { kind: "account"; did: string; seq: number; time_us: number; account: { did: string; active: boolean; status?: string; time?: string; }; } | { kind: "sync"; did: string; seq: number; time_us: number; sync: { did: string; rev: string; time?: string; }; }; interface PersistentJetstreamSubscription extends AsyncIterable { /** Effective initial source coordinate, when the transport exposes one. */ cursor?: number | null; } interface PersistentIngestOptions { batchSize?: number; flushIntervalMs?: number; signal?: AbortSignal; /** Override subscription creation for testing or a custom transport. */ createSubscription?: (cursor: number | null, signal?: AbortSignal) => PersistentJetstreamSubscription; logger?: Logger; } declare function runPersistent(db: Database, config: ContrailConfig, options?: PersistentIngestOptions): Promise; /** One catch-up cycle for every configured labeler. Designed to fit inside a * Cloudflare Workers cron tick — we drain frames until the labeler has no * more buffered events for us, or `timeoutMs` is reached, then save cursor * and disconnect. Mirrors the shape of `runIngestCycle` for jetstream. */ declare function runLabelIngestCycle(db: Database, config: ContrailConfig, timeoutMs?: number): Promise; interface PersistentLabelsOptions { signal?: AbortSignal; batchSize?: number; flushIntervalMs?: number; logger?: Logger; } /** Long-lived equivalent — keeps one socket per labeler open forever, with * exponential backoff reconnect. Mirrors `runPersistent` for jetstream. */ declare function runPersistentLabels(db: Database, config: ContrailConfig, options?: PersistentLabelsOptions): Promise; interface CreateAppOptions { /** Lexicon JSON documents to expose from the deployment. */ lexicons?: object[]; /** Enable stable discovery for anonymous read-through clients. */ publicService?: PublicServiceOptions; } declare function createApp(db: Database, config: ContrailConfig, options?: CreateAppOptions): Hono; interface ContrailOptions extends ContrailConfig { db?: Database; /** Exact generated/pinned runtime Lexicon bundle used by collections with * `validate: true`. Method Lexicons in the same bundle are harmless. */ lexicons?: object[]; } declare class Contrail { readonly config: ResolvedContrailConfig; /** Durable low-level claim/hydrate/ack consumer API. */ readonly changes: ChangeConsumers; private _db?; private _ingestState; constructor(options: ContrailOptions); private getDb; /** Initialize the database schema. */ init(db?: Database): Promise; /** Refresh the SQLite query-planner statistics (bounded `PRAGMA optimize`) so * multi-predicate queries pick the selective index. No-op on Postgres. Safe * to call on a schedule; the ingest tick runs this automatically when * `config.maintenance.optimize` is enabled, so most consumers don't need to * call it directly. */ optimize(db?: Database): Promise; /** Read private aggregate ingest rejection counters. */ diagnostics(db?: Database): Promise; /** Query records from a collection. */ query(collection: string, options?: Omit, db?: Database): Promise<{ records: (RecordRow & { counts?: Record; })[]; cursor?: string; }>; /** Run one ingestion cycle: catches up records from Jetstream and — when * `config.labels` is set — labels from each configured labeler in parallel. * Scheduled record collection is independently bounded by drain time, * retained candidates, and serialized bytes. */ ingest(options?: ScheduledIngestOptions, db?: Database): Promise; /** Long-lived ingestion: streams records via Jetstream and — when * `config.labels` is set — labels via per-labeler `subscribeLabels` sockets. * Both honor the supplied `signal` and shut down cleanly together. */ runPersistent(options?: Omit, db?: Database): Promise; /** Run a persistent fair change-delivery supervisor. Run this alongside * `runPersistent()`; destination failures never stop source ingestion. */ runPersistentDeliveries(options: { env: Env; deliveries: DeliveryHandlers; bootstraps?: CurrentBootstrapRuntimeHandlers; runtime?: DeliveryRuntimeOptions & { idleMs?: number; }; }, db?: Database): Promise; /** Run *only* the labeler ingestion cycle. Escape hatch for callers who * want to run record and label ingestion in separate processes / workers. * `ingest()` already covers the typical case. */ ingestLabels(options?: { timeoutMs?: number; }, db?: Database): Promise; /** Run *only* the persistent labeler ingestion. Escape hatch counterpart * to `ingestLabels()`. `runPersistent()` covers the typical case. */ runPersistentLabels(options?: Omit, db?: Database): Promise; /** Discover users from relays. Returns discovered DIDs. */ discover(db?: Database): Promise; /** Backfill pending users' records from their PDS. */ backfill(options?: BackfillAllOptions, db?: Database): Promise; /** Retry a bounded slice of due or interrupted account backfills. Intended * for scheduled runtimes; persisted backoff prevents hammering failures. */ retryBackfill(options?: BackfillRetryOptions, db?: Database): Promise; /** Discover every DID with records in the configured collections, then * backfill their history. Logs progress via `config.logger` — supply * `onProgress` to take over output, or pass a no-op logger in the config * to silence the defaults. */ backfillAll(options?: BackfillAllOptions, db?: Database): Promise<{ discovered: number; backfilled: number; status: BackfillStatus; }>; /** Immediately fetch and index specific records from their PDS. */ notify(uris: string | string[], db?: Database): Promise; /** Build the Hono app for this Contrail instance. */ app(options?: AppOptions): Hono; /** Fetch-style handler built from `app()`. Use this from SvelteKit / Next / * Workers / Bun — anything that takes `(request) => Response`. */ handler(options?: AppOptions): (request: Request) => Promise; } /** Overrides accepted by `Contrail.app()` and `Contrail.handler()`. Mirrors * `CreateAppOptions` but lets the caller also override the DBs (falling back * to the ones given to the Contrail constructor). */ interface AppOptions extends CreateAppOptions { db?: Database; } export { type AppOptions as A, saveOrderedSourcePositionStatement as B, Contrail as C, saveServingSourcePositionStatement as D, type ExistingRecordInfo as E, type FeedSweepResult as F, sweepFeedItems as G, type JetstreamLiveEvent as J, MAX_NOTIFY_URIS as M, type NotifyResult as N, type PersistentIngestOptions as P, type QueryOptions as Q, type ServiceAuthGate as S, type ContrailOptions as a, type CreateAppOptions as b, JetstreamLiveHistoryExpiredError as c, type PersistentJetstreamSubscription as d, type PersistentLabelsOptions as e, type ServingSourcePosition as f, type SortOption as g, assertServingSourceCompatibility as h, createApp as i, getFeedPruneCursor as j, getLastCursor as k, getServingSourcePosition as l, lookupExistingRecords as m, processNotifyUris as n, orderedSourcePosition as o, parseAtUri as p, pruneActorFeed as q, pruneFeedItems as r, queryRecords as s, registerNotifyRoute as t, runLabelIngestCycle as u, runPersistent as v, runPersistentLabels as w, saveCursor as x, saveCursorStatement as y, saveFeedPruneCursor as z };