import type * as Webhooks from '../../internal/Webhooks.js'
import type * as Db from '../Db.js'
import type * as db_Schema from '../Schema.js'
/** Columns of the `webhook_queue_completions` table, derived from `Schema.WebhookQueueCompletion`. */
export type Table = db_Schema.WebhookQueueCompletion
/** A terminal delivery-obligation outcome. */
export type TerminalStatus = db_Schema.WebhookQueueCompletion['status']
/**
* Reads the unexpired completion for a reference, if any. Rows are written by
* the queue-event terminal transition and only ever read for dedupe.
*/
export async function get(
db: Db.Db,
reference: Webhooks.QueueReference,
now: string,
): Promise
{
return (
(await db.kysely
.selectFrom('webhook_queue_completions')
.selectAll()
.where('eventId', '=', reference.eventId)
.where('subscriptionId', '=', reference.subscriptionId)
.where('expiresAt', '>', now)
.executeTakeFirst()) ?? null
)
}
// Per-statement delete bound: prune runs every minute, so a backlog drains in
// a few ticks instead of one unbounded DELETE churning the origin.
const pruneBatchLimit = 50_000
/** Deletes expired completions, bounded per call; expiry is filtered at query time, so pruning is space-only. */
export async function pruneExpired(db: Db.Db, now: string): Promise {
const result = await db.kysely
.deleteFrom('webhook_queue_completions')
// Repeated on the outer delete: the row recheck under concurrency re-runs
// only this predicate, so a completion renewed mid-statement survives.
.where('expiresAt', '<=', now)
.where(({ eb, refTuple, selectFrom }) =>
eb(
refTuple('subscriptionId', 'eventId'),
'in',
selectFrom('webhook_queue_completions')
.select(['subscriptionId', 'eventId'])
.where('expiresAt', '<=', now)
.limit(pruneBatchLimit)
.$asTuple('subscriptionId', 'eventId'),
),
)
.executeTakeFirst()
return Number(result.numDeletedRows)
}