import type { RunDispatchWriter, ScheduledRetryReader } from "./ports.js"; import type { PostCommitEffect, PromoteScheduledRetryOutcome } from "./types.js"; export function createEvaluateScheduledRetryGate(deps: { reader: ScheduledRetryReader }) { return (input: { runId: string; companyId: string; retryReasonOverride: string; now?: Date; }) => deps.reader.evaluateScheduledRetryGate({ ...input, now: input.now ?? new Date() }); } export function createPromoteScheduledRetry(deps: { writer: RunDispatchWriter }) { return (input: { runId: string; companyId: string; now?: Date; }): Promise => deps.writer.promoteOrCancelDueRetry({ runId: input.runId, companyId: input.companyId, now: input.now ?? new Date(), }); } const MAX_DUE_RETRIES_PER_SWEEP = 50; export function createPromoteDueScheduledRetries(deps: { reader: ScheduledRetryReader; promoteScheduledRetry: ReturnType; }) { return async function promoteDueScheduledRetries(input: { now?: Date; cutoff: Date | null }) { const now = input.now ?? new Date(); const dueRuns = ( await deps.reader.listDueRetries({ now, cutoff: input.cutoff, limit: MAX_DUE_RETRIES_PER_SWEEP }) ).slice(0, MAX_DUE_RETRIES_PER_SWEEP); const runIds: string[] = []; const postCommitEffects: PostCommitEffect[] = []; for (const dueRun of dueRuns) { const result = await deps.promoteScheduledRetry({ ...dueRun, now }); if (result.outcome !== "promoted") continue; runIds.push(dueRun.runId); postCommitEffects.push(...result.postCommitEffects); } return { promoted: runIds.length, runIds, postCommitEffects }; }; } export function createCancelStaleQueuedRun(deps: { writer: RunDispatchWriter }) { return (input: { runId: string; companyId: string; expectedStatus: "queued" | "running"; now?: Date; }) => deps.writer.cancelStaleQueuedRun({ ...input, now: input.now ?? new Date() }); } export function createDispatchResolvedInteractionIfCurrent(deps: { writer: RunDispatchWriter }) { return (input: { runId: string; companyId: string; expectedStatus: "queued" | "running"; reservationId: string; recoveryLeaseOwner?: string; enforceContinuationOwnership?: boolean; dispatch: (markDispatchStarted: () => void) => Promise; now?: Date; }) => deps.writer.dispatchResolvedInteractionIfCurrent({ ...input, now: input.now ?? new Date(), }); }