import { env } from "cloudflare:workers";
import type { Doorbell } from "./job-doorbell-do";
type Scoped = { repo_id: string | null; owner_id: string | null };
function bell(key: string) {
return env.JOB_DOORBELL.get(env.JOB_DOORBELL.idFromName(key));
}
function scopeKeys(runner: Scoped): string[] {
return [
...(runner.repo_id ? [`repo:${runner.repo_id}`] : []),
...(runner.owner_id ? [`owner:${runner.owner_id}`] : []),
];
}
/** After a job becomes claimable: wakes runners registered to its repo and to its owner. */
export async function ringJobDoorbells(repoId: string, ownerId: string): Promise<void> {
await Promise.all(
[`repo:${repoId}`, `owner:${ownerId}`].map(async (key) => {
try {
await bell(key).ring();
} catch (error) {
console.error("Job doorbell ring failed", key, error);
}
}),
);
}
export type RunnerBells = { ringing: boolean; silence(): Promise<void> };
/** Whether this runner's scopes may have queued work; failures count as ringing so jobs are never stranded. */
export async function checkRunnerBells(runner: Scoped): Promise<RunnerBells> {
const keys = scopeKeys(runner);
let states: Array<{ key: string; state: Doorbell }>;
try {
states = await Promise.all(keys.map(async (key) => ({ key, state: await bell(key).check() })));
} catch (error) {
console.error("Job doorbell check failed", error);
return { ringing: true, silence: async () => {} };