Skip to main content
Publish pipe output to Google Cloud Pub/Sub as a BigQuery CDC-compatible change stream, with fork-aware compensation. Each row becomes a message carrying _id, _CHANGE_TYPE, and a _CHANGE_SEQUENCE_NUMBER from one gap-free, producer-wide counter, so a subscriber holding a contiguous run of sequence numbers is missing no operation the producer committed inside it. Nothing but row changes goes on the topic, so a direct BigQuery subscription needs no message filter.
@google-cloud/pubsub is an optional peer dependency. The default SQLite-backed state additionally needs better-sqlite3:

pubsubTarget

TopicRoute<Data>: MessageDraft:

Fork handling

On a reorg, the target folds every orphaned id in the rollback manifest back to the state that survives at the safe cursor, enqueues the resulting compensations (a delete, or the route’s rollbackWhenMissing result), and rewinds before the source re-streams the canonical blocks. The finalized head a consumer needs for its own confirmation policy rides the _finalized attribute on every message; it bounds retraction, not arrival, since a pipe still catching up publishes well below it. See Fork handling for how the finalization watermark is resolved and enforced across targets.

windowTopic

Builds a TopicRoute for an aggregator window stream: every re-emission of a window is an upsert on the same id (mode: 'materialized'), so BigQuery CDC replaces the previous revision instead of appending a new row.

SqlitePubsubState

The state store pubsubTarget constructs when state is given as { path }. id overrides the cursor key that would otherwise bind to the pipe’s own id once the target opens the store. To use a different backend, implement the PubsubState interface (open, getCursor, getMeta, setMeta, commit, pending, confirm, fork, stats, close) and pass the instance as state directly.

Example

Full runnable example, including windowTopic and a direct BigQuery subscription: 18.pubsub.example.ts.

Notes

  • The canonical codec encodes a bigint as a decimal string and a Date as RFC 3339; a value it cannot represent throws PubsubTargetError (E2405), and a cycle throws E2406.
  • Every error the target, windowTopic, or the SQLite state can raise is a PubsubTargetError with a stable E24xx code. See the error reference for the full list and fixes.
  • The state file is the producer’s sequencer: keep it on durable storage and run one producer per path. Losing it and restarting under the same namespace is a COLD_START_REFUSED (E2420), not something to retry past. Recovery is a fresh namespace and a consumer re-bootstrap.