import { DurableObject } from "cloudflare:workers";
import { processAutoMerges } from "./merge-automation";
import { processQueue } from "./merge-queue";
const TARGET_KEY = "target";
const DIRTY_KEY = "dirty";
const RETRY_ON_ERROR_MS = 60_000;
type Target = { repoId: string; baseRef: string };
/**
* One instance per (repository, base branch). Alarms are delivered one at a time, so every auto-merge
* and merge-queue landing into a branch is serialized here; D1 remains the source of truth.
*/
export class MergeQueue extends DurableObject<Env> {
async kick(repoId: string, baseRef: string): Promise<void> {
await this.ctx.storage.put({ [TARGET_KEY]: { repoId, baseRef }, [DIRTY_KEY]: true });
await this.ctx.storage.setAlarm(Date.now());
}
override async alarm(): Promise<void> {
const target = await this.ctx.storage.get<Target>(TARGET_KEY);
if (!target) return;
await this.ctx.storage.put(DIRTY_KEY, false);
try {
await processAutoMerges(target.repoId, target.baseRef);
const delay = await processQueue(target.repoId, target.baseRef);
const rekicked = (await this.ctx.storage.get<boolean>(DIRTY_KEY)) === true;
if (rekicked) await this.ctx.storage.setAlarm(Date.now());
else if (delay !== null) await this.ctx.storage.setAlarm(Date.now() + delay);
} catch (error) {
console.error("Merge queue pass failed", error);
await this.ctx.storage.setAlarm(Date.now() + RETRY_ON_ERROR_MS);
}
}
}