import { Row } from "../storage.mjs"; import { IcebergS3Config } from "./schema.mjs"; import { Sink, SinkCloseResult, SliceOverwriteWriter } from "../sink.mjs"; /** Connection details for the Iceberg REST catalog the writer targets. */ interface OverwriteWriterCatalogConfig { /** * Iceberg REST catalog endpoint, e.g. `http://localhost:8181` (POC) or the * R2 Data Catalog endpoint (prod). */ catalogUri: string; /** Catalog namespace the 5 fact tables live under, e.g. `gsc`. */ namespace: string; /** Warehouse identifier the catalog resolves table locations under. */ warehouse: string; /** S3-compatible object store backing the catalog. */ s3: IcebergS3Config; /** Bearer token for a token-authed REST catalog (R2 Data Catalog). */ catalogToken?: string; } /** * The JSON job wire-format shared with `scripts/iceberg-writer.py`. * `op` is always `overwrite` here. */ interface OverwriteJob { op: 'overwrite'; catalogUri: string; namespace: string; warehouse: string; s3: IcebergS3Config; table: string; spec: unknown; siteId: string; searchType: string; date: string; /** Data columns only — the script injects `site_id` / `search_type`. */ rows: readonly Row[]; } /** * Per-site delete job — removes EVERY row for one `siteId` across a whole * table (all dates / search types). Recovery op for clearing a corrupt site * (e.g. a cross-run append double) from a shared per-team shard before a clean * re-backfill, without touching sibling sites. Carries no rows. `siteId` is a * `number` for an INT-encoded shard, a `string` for STRING encoding. */ interface DeleteJob { op: 'delete'; catalogUri: string; namespace: string; warehouse: string; s3: IcebergS3Config; /** Bearer token for a token-authed REST catalog (R2 Data Catalog). */ catalogToken?: string; table: string; siteId: string | number; } /** Any job the shared PyIceberg backend can execute. */ type PyIcebergJob = OverwriteJob | DeleteJob; /** Result wire-format from the PyIceberg backend. */ interface OverwriteJobResult { /** Rows written (emit/overwrite) or removed (delete). */ rowCount?: number; error?: string; } /** * A transport that executes one PyIceberg job and returns its result. * `subprocessBackend` and `httpBackend` are the two shipped implementations; * tests inject a fake. The param is the `OverwriteJob | DeleteJob` union, so a * backend value is still assignable where the narrower `OverwriteBackend` is * expected (a wider-param function accepts the narrower call). */ type OverwriteBackend = (job: PyIcebergJob) => Promise; interface IcebergOverwriteWriterOptions { catalog: OverwriteWriterCatalogConfig; /** The transport that runs the PyIceberg job. */ backend: OverwriteBackend; } /** * The trailing-window Iceberg overwrite writer. Conforms to the frozen * `SliceOverwriteWriter` contract; `overwriteSlice` builds one PyIceberg * overwrite job per call and runs it through the configured backend. */ interface IcebergOverwriteWriter extends SliceOverwriteWriter { /** Release any backend resources. Idempotent. */ close: () => Promise; } /** * Build a trailing-window Iceberg overwrite writer over the given backend. * Pure — all I/O is the backend's; the writer only shapes the job. */ declare function createIcebergOverwriteWriter(opts: IcebergOverwriteWriterOptions): IcebergOverwriteWriter; /** Options for the PyIceberg subprocess backend. */ interface SubprocessBackendOptions { /** Python interpreter. Defaults to `$GSCDUMP_ICEBERG_PYTHON` then `python3`. */ python?: string; /** * Path to the PyIceberg writer script. Defaults to * `/scripts/iceberg-writer.py`. */ writerScript?: string; } /** * Backend that runs the PyIceberg overwrite by spawning the shared writer * script (`scripts/iceberg-writer.py`). Node-only — used by local integration * tests and a Node-side scheduled job box. The script reads the job JSON on * stdin and writes `{rowCount}` / `{error}` on stdout. */ declare function subprocessBackend(opts?: SubprocessBackendOptions): OverwriteBackend; /** Options for the PyIceberg HTTP backend (the Cloudflare Container in prod). */ interface HttpBackendOptions { /** * URL of the PyIceberg overwrite service — a POST endpoint accepting the * `OverwriteJob` JSON and returning `OverwriteJobResult`. */ endpoint: string; /** Optional bearer token for the service. */ token?: string; /** Injectable fetch — defaults to the global. */ fetch?: typeof fetch; } /** * Backend that POSTs the overwrite job to a PyIceberg HTTP service — the * Cloudflare Container running `iceberg-writer.py` behind an HTTP shim. This * is the prod transport: the request-path Worker holds no Python and never * does an Iceberg metadata commit itself. */ declare function httpBackend(opts: HttpBackendOptions): OverwriteBackend; /** * Adapt an `IcebergOverwriteWriter` into a full `Sink` whose `emit` is also a * partition-overwrite — convenient for tests so a single Iceberg writer backs * both append and revision paths against the local stack. Prod keeps `emit` * on the Pipeline; only `overwriteSlice` delegates here. */ declare function overwriteWriterAsSink(writer: IcebergOverwriteWriter): Sink & SliceOverwriteWriter; /** Per-table outcome of a {@link deleteSiteFromShard} run. */ interface DeleteSiteResult { table: string; /** Rows removed, when the backend reported a count. */ rowCount?: number; /** Present when this table's delete failed; the sweep continues regardless. */ error?: string; } /** * Remove every row for one `siteId` from a per-team Iceberg shard, across the * given tables. Recovery op: clear a corrupt site (e.g. a cross-run append * double) before a clean re-backfill, leaving sibling sites in the shared shard * untouched. `site_id` is an identity partition column, so PyIceberg prunes * whole data files (no row-level delete files). * * Runs SEQUENTIALLY (one commit per table at a time) so the per-table catalog * commit-rate ceiling is never tripped. A table failure is captured in its * result row, not thrown — the sweep always attempts every table so a partial * failure is visible and re-runnable (delete is idempotent: re-deleting an * already-empty site is a no-op). */ declare function deleteSiteFromShard(args: { catalog: OverwriteWriterCatalogConfig; backend: OverwriteBackend; siteId: string | number; tables: readonly string[]; }): Promise; export { DeleteJob, DeleteSiteResult, HttpBackendOptions, IcebergOverwriteWriter, IcebergOverwriteWriterOptions, OverwriteBackend, OverwriteJob, OverwriteJobResult, OverwriteWriterCatalogConfig, PyIcebergJob, SubprocessBackendOptions, createIcebergOverwriteWriter, deleteSiteFromShard, httpBackend, overwriteWriterAsSink, subprocessBackend };