mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
chore: relocate backend into apps/api ahead of monorepo merge
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
"use strict";
|
||||
|
||||
/***********************************************************************************************************************************************************************
|
||||
* File Name: taskPrerequisites.util.js
|
||||
* Type of Program: Utility
|
||||
* Description: Cycle detection for the explicit Task ↔ Task prerequisite graph
|
||||
* (task_prerequisites junction table). A task's prerequisite set is
|
||||
* only meaningful if the graph stays a DAG — this checks whether
|
||||
* persisting a proposed edge set would introduce a cycle.
|
||||
*
|
||||
* Author: Kenneth Obsequio (@lash0000)
|
||||
* Date Created: Jul. 17, 2026
|
||||
***********************************************************************************************************************************************************************/
|
||||
|
||||
const { TaskPrerequisite } = require("../../models/task/task.mdl");
|
||||
|
||||
/**
|
||||
* Would setting `taskId`'s prerequisites to `newPrereqIds` introduce a cycle?
|
||||
*
|
||||
* Loads every existing task_prerequisites edge for the task list, overlays the
|
||||
* proposed edges for `taskId` (replacing whatever it currently points to), then
|
||||
* DFS from `taskId` looking for a path back to itself.
|
||||
*/
|
||||
async function wouldCreateCycle(taskId, newPrereqIds, taskListId, transaction) {
|
||||
const { Task } = require("../../models/task/task.mdl");
|
||||
|
||||
const siblingTasks = await Task.findAll({
|
||||
where: { task_list_id: taskListId },
|
||||
attributes: ["task_id"],
|
||||
transaction,
|
||||
});
|
||||
const siblingIds = siblingTasks.map((t) => t.task_id);
|
||||
|
||||
const existingEdges = await TaskPrerequisite.findAll({
|
||||
where: { task_id: siblingIds },
|
||||
attributes: ["task_id", "prerequisite_task_id"],
|
||||
transaction,
|
||||
});
|
||||
|
||||
// adjacency: task_id -> Set(prerequisite_task_id) ("depends on")
|
||||
const adjacency = new Map();
|
||||
for (const { task_id, prerequisite_task_id } of existingEdges) {
|
||||
if (task_id === taskId) continue; // overlay taskId's edges with the proposed set below
|
||||
if (!adjacency.has(task_id)) adjacency.set(task_id, new Set());
|
||||
adjacency.get(task_id).add(prerequisite_task_id);
|
||||
}
|
||||
adjacency.set(taskId, new Set(newPrereqIds));
|
||||
|
||||
// DFS from taskId looking for a path back to taskId
|
||||
const visited = new Set();
|
||||
const stack = [...adjacency.get(taskId)];
|
||||
while (stack.length) {
|
||||
const current = stack.pop();
|
||||
if (current === taskId) return true;
|
||||
if (visited.has(current)) continue;
|
||||
visited.add(current);
|
||||
for (const next of adjacency.get(current) ?? []) stack.push(next);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports = { wouldCreateCycle };
|
||||
Reference in New Issue
Block a user