import { type D1, queryDb } from "./db"; const BATCH_SIZE = 90; export async function detectCycle(db: D1, taskId: string, dependsOn: string[]): Promise { if (dependsOn.includes(taskId)) return true; for (const depId of dependsOn) { const result = await queryDb( db, ` WITH RECURSIVE dep_chain(tid) AS ( SELECT $1::text UNION SELECT td.depends_on FROM dep_chain dc JOIN task_dependencies td ON td.task_id = dc.tid ) SELECT 1 FROM dep_chain WHERE tid = $2 LIMIT 1 `, [depId, taskId], ); if (result.rows.length > 0) return true; } return false; } export async function computeBlocked(db: D1, taskIds: string[]): Promise> { if (taskIds.length === 0) return new Set(); const blocked = new Set(); for (let i = 0; i < taskIds.length; i += BATCH_SIZE) { const chunk = taskIds.slice(i, i + BATCH_SIZE); const placeholders = chunk.map((_, idx) => `$${idx + 1}`).join(","); const result = await queryDb<{ task_id: string }>( db, ` SELECT DISTINCT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE td.task_id IN (${placeholders}) AND dep.status NOT IN ('done', 'cancelled') `, chunk, ); for (const r of result.rows) blocked.add(r.task_id); } return blocked; } export async function getDependencies(db: D1, taskId: string): Promise { const result = await queryDb<{ depends_on: string }>(db, "SELECT depends_on FROM task_dependencies WHERE task_id = $1", [taskId]); return result.rows.map((r) => r.depends_on); } export async function setDependencies(db: D1, taskId: string, deps: string[]): Promise { await queryDb(db, "DELETE FROM task_dependencies WHERE task_id = $1", [taskId]); for (const depId of deps) { await queryDb(db, "INSERT INTO task_dependencies (task_id, depends_on) VALUES ($1, $2)", [taskId, depId]); } }