@fbdo/smart-agentic-calendar 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +195 -0
- package/dist/analytics/allocation.d.ts +9 -0
- package/dist/analytics/allocation.d.ts.map +1 -0
- package/dist/analytics/allocation.js +27 -0
- package/dist/analytics/allocation.js.map +1 -0
- package/dist/analytics/analytics-engine.d.ts +17 -0
- package/dist/analytics/analytics-engine.d.ts.map +1 -0
- package/dist/analytics/analytics-engine.js +33 -0
- package/dist/analytics/analytics-engine.js.map +1 -0
- package/dist/analytics/estimation.d.ts +9 -0
- package/dist/analytics/estimation.d.ts.map +1 -0
- package/dist/analytics/estimation.js +74 -0
- package/dist/analytics/estimation.js.map +1 -0
- package/dist/analytics/health.d.ts +14 -0
- package/dist/analytics/health.d.ts.map +1 -0
- package/dist/analytics/health.js +146 -0
- package/dist/analytics/health.js.map +1 -0
- package/dist/analytics/period.d.ts +9 -0
- package/dist/analytics/period.d.ts.map +1 -0
- package/dist/analytics/period.js +41 -0
- package/dist/analytics/period.js.map +1 -0
- package/dist/analytics/productivity.d.ts +9 -0
- package/dist/analytics/productivity.d.ts.map +1 -0
- package/dist/analytics/productivity.js +34 -0
- package/dist/analytics/productivity.js.map +1 -0
- package/dist/common/constants.d.ts +13 -0
- package/dist/common/constants.d.ts.map +1 -0
- package/dist/common/constants.js +19 -0
- package/dist/common/constants.js.map +1 -0
- package/dist/common/id.d.ts +2 -0
- package/dist/common/id.d.ts.map +1 -0
- package/dist/common/id.js +5 -0
- package/dist/common/id.js.map +1 -0
- package/dist/common/time.d.ts +11 -0
- package/dist/common/time.d.ts.map +1 -0
- package/dist/common/time.js +67 -0
- package/dist/common/time.js.map +1 -0
- package/dist/engine/conflict-detector.d.ts +22 -0
- package/dist/engine/conflict-detector.d.ts.map +1 -0
- package/dist/engine/conflict-detector.js +194 -0
- package/dist/engine/conflict-detector.js.map +1 -0
- package/dist/engine/dependency-resolver.d.ts +8 -0
- package/dist/engine/dependency-resolver.d.ts.map +1 -0
- package/dist/engine/dependency-resolver.js +160 -0
- package/dist/engine/dependency-resolver.js.map +1 -0
- package/dist/engine/recurrence-manager.d.ts +24 -0
- package/dist/engine/recurrence-manager.d.ts.map +1 -0
- package/dist/engine/recurrence-manager.js +140 -0
- package/dist/engine/recurrence-manager.js.map +1 -0
- package/dist/engine/replan-coordinator.d.ts +24 -0
- package/dist/engine/replan-coordinator.d.ts.map +1 -0
- package/dist/engine/replan-coordinator.js +107 -0
- package/dist/engine/replan-coordinator.js.map +1 -0
- package/dist/engine/scheduler.d.ts +65 -0
- package/dist/engine/scheduler.d.ts.map +1 -0
- package/dist/engine/scheduler.js +368 -0
- package/dist/engine/scheduler.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +25 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +421 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/analytics-tools.d.ts +45 -0
- package/dist/mcp/tools/analytics-tools.d.ts.map +1 -0
- package/dist/mcp/tools/analytics-tools.js +27 -0
- package/dist/mcp/tools/analytics-tools.js.map +1 -0
- package/dist/mcp/tools/config-tools.d.ts +55 -0
- package/dist/mcp/tools/config-tools.d.ts.map +1 -0
- package/dist/mcp/tools/config-tools.js +47 -0
- package/dist/mcp/tools/config-tools.js.map +1 -0
- package/dist/mcp/tools/event-tools.d.ts +50 -0
- package/dist/mcp/tools/event-tools.d.ts.map +1 -0
- package/dist/mcp/tools/event-tools.js +48 -0
- package/dist/mcp/tools/event-tools.js.map +1 -0
- package/dist/mcp/tools/schedule-tools.d.ts +80 -0
- package/dist/mcp/tools/schedule-tools.d.ts.map +1 -0
- package/dist/mcp/tools/schedule-tools.js +103 -0
- package/dist/mcp/tools/schedule-tools.js.map +1 -0
- package/dist/mcp/tools/task-tools.d.ts +106 -0
- package/dist/mcp/tools/task-tools.d.ts.map +1 -0
- package/dist/mcp/tools/task-tools.js +161 -0
- package/dist/mcp/tools/task-tools.js.map +1 -0
- package/dist/mcp/validators.d.ts +231 -0
- package/dist/mcp/validators.d.ts.map +1 -0
- package/dist/mcp/validators.js +405 -0
- package/dist/mcp/validators.js.map +1 -0
- package/dist/models/analytics.d.ts +55 -0
- package/dist/models/analytics.d.ts.map +1 -0
- package/dist/models/analytics.js +2 -0
- package/dist/models/analytics.js.map +1 -0
- package/dist/models/config.d.ts +32 -0
- package/dist/models/config.d.ts.map +1 -0
- package/dist/models/config.js +2 -0
- package/dist/models/config.js.map +1 -0
- package/dist/models/conflict.d.ts +21 -0
- package/dist/models/conflict.d.ts.map +1 -0
- package/dist/models/conflict.js +2 -0
- package/dist/models/conflict.js.map +1 -0
- package/dist/models/dependency.d.ts +5 -0
- package/dist/models/dependency.d.ts.map +1 -0
- package/dist/models/dependency.js +2 -0
- package/dist/models/dependency.js.map +1 -0
- package/dist/models/errors.d.ts +19 -0
- package/dist/models/errors.d.ts.map +1 -0
- package/dist/models/errors.js +29 -0
- package/dist/models/errors.js.map +1 -0
- package/dist/models/event.d.ts +11 -0
- package/dist/models/event.d.ts.map +1 -0
- package/dist/models/event.js +2 -0
- package/dist/models/event.js.map +1 -0
- package/dist/models/recurrence.d.ts +22 -0
- package/dist/models/recurrence.d.ts.map +1 -0
- package/dist/models/recurrence.js +2 -0
- package/dist/models/recurrence.js.map +1 -0
- package/dist/models/schedule.d.ts +16 -0
- package/dist/models/schedule.d.ts.map +1 -0
- package/dist/models/schedule.js +2 -0
- package/dist/models/schedule.js.map +1 -0
- package/dist/models/task.d.ts +21 -0
- package/dist/models/task.d.ts.map +1 -0
- package/dist/models/task.js +9 -0
- package/dist/models/task.js.map +1 -0
- package/dist/storage/analytics-repository.d.ts +17 -0
- package/dist/storage/analytics-repository.d.ts.map +1 -0
- package/dist/storage/analytics-repository.js +99 -0
- package/dist/storage/analytics-repository.js.map +1 -0
- package/dist/storage/config-repository.d.ts +20 -0
- package/dist/storage/config-repository.d.ts.map +1 -0
- package/dist/storage/config-repository.js +145 -0
- package/dist/storage/config-repository.js.map +1 -0
- package/dist/storage/database.d.ts +6 -0
- package/dist/storage/database.d.ts.map +1 -0
- package/dist/storage/database.js +160 -0
- package/dist/storage/database.js.map +1 -0
- package/dist/storage/event-repository.d.ts +16 -0
- package/dist/storage/event-repository.d.ts.map +1 -0
- package/dist/storage/event-repository.js +120 -0
- package/dist/storage/event-repository.js.map +1 -0
- package/dist/storage/recurrence-repository.d.ts +18 -0
- package/dist/storage/recurrence-repository.d.ts.map +1 -0
- package/dist/storage/recurrence-repository.js +99 -0
- package/dist/storage/recurrence-repository.js.map +1 -0
- package/dist/storage/schedule-repository.d.ts +13 -0
- package/dist/storage/schedule-repository.d.ts.map +1 -0
- package/dist/storage/schedule-repository.js +53 -0
- package/dist/storage/schedule-repository.js.map +1 -0
- package/dist/storage/task-repository.d.ts +29 -0
- package/dist/storage/task-repository.d.ts.map +1 -0
- package/dist/storage/task-repository.js +235 -0
- package/dist/storage/task-repository.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"productivity.d.ts","sourceRoot":"","sources":["../../src/analytics/productivity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsB;gBAExC,aAAa,EAAE,mBAAmB;IAI9C,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,IAAI,GAAG,iBAAiB;CAiCjE"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { resolvePeriod } from "./period.js";
|
|
2
|
+
export class ProductivityCalculator {
|
|
3
|
+
analyticsRepo;
|
|
4
|
+
constructor(analyticsRepo) {
|
|
5
|
+
this.analyticsRepo = analyticsRepo;
|
|
6
|
+
}
|
|
7
|
+
compute(period, referenceDate) {
|
|
8
|
+
const range = resolvePeriod(period, referenceDate);
|
|
9
|
+
const completedTasks = this.analyticsRepo.getCompletedTasks(range.start, range.end);
|
|
10
|
+
const allOverdueTasks = this.analyticsRepo.getOverdueTasks(range.end);
|
|
11
|
+
const cancelledTasks = this.analyticsRepo.getCancelledTasks(range.start, range.end);
|
|
12
|
+
// Filter overdue tasks to those with deadline within the period
|
|
13
|
+
const overdueTasks = allOverdueTasks.filter((t) => t.deadline !== null && t.deadline >= range.start);
|
|
14
|
+
const tasksCompleted = completedTasks.length;
|
|
15
|
+
const tasksOverdue = overdueTasks.length;
|
|
16
|
+
const tasksCancelled = cancelledTasks.length;
|
|
17
|
+
const resolvedCount = tasksCompleted + tasksOverdue + tasksCancelled;
|
|
18
|
+
const completionRate = resolvedCount === 0 ? 0 : round2((tasksCompleted / resolvedCount) * 100);
|
|
19
|
+
const onTimeCount = completedTasks.filter((t) => t.wasOnTime).length;
|
|
20
|
+
const onTimeRate = completedTasks.length === 0 ? 0 : round2((onTimeCount / completedTasks.length) * 100);
|
|
21
|
+
return {
|
|
22
|
+
period,
|
|
23
|
+
tasksCompleted,
|
|
24
|
+
tasksOverdue,
|
|
25
|
+
tasksCancelled,
|
|
26
|
+
completionRate,
|
|
27
|
+
onTimeRate,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function round2(value) {
|
|
32
|
+
return Math.round(value * 100) / 100;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=productivity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"productivity.js","sourceRoot":"","sources":["../../src/analytics/productivity.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,OAAO,sBAAsB;IAChB,aAAa,CAAsB;IAEpD,YAAY,aAAkC;QAC5C,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACrC,CAAC;IAED,OAAO,CAAC,MAAc,EAAE,aAAoB;QAC1C,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QAEnD,MAAM,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QACpF,MAAM,eAAe,GAAG,IAAI,CAAC,aAAa,CAAC,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACtE,MAAM,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QAEpF,gEAAgE;QAChE,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CACzC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,CAAC,QAAQ,IAAI,KAAK,CAAC,KAAK,CACxD,CAAC;QAEF,MAAM,cAAc,GAAG,cAAc,CAAC,MAAM,CAAC;QAC7C,MAAM,YAAY,GAAG,YAAY,CAAC,MAAM,CAAC;QACzC,MAAM,cAAc,GAAG,cAAc,CAAC,MAAM,CAAC;QAE7C,MAAM,aAAa,GAAG,cAAc,GAAG,YAAY,GAAG,cAAc,CAAC;QAErE,MAAM,cAAc,GAAG,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,cAAc,GAAG,aAAa,CAAC,GAAG,GAAG,CAAC,CAAC;QAEhG,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC;QACrE,MAAM,UAAU,GACd,cAAc,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC;QAExF,OAAO;YACL,MAAM;YACN,cAAc;YACd,YAAY;YACZ,cAAc;YACd,cAAc;YACd,UAAU;SACX,CAAC;IACJ,CAAC;CACF;AAED,SAAS,MAAM,CAAC,KAAa;IAC3B,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const DEFAULT_BUFFER_TIME_MINUTES = 15;
|
|
2
|
+
export declare const DEFAULT_PRIORITY: "P3";
|
|
3
|
+
export declare const DEFAULT_DURATION_MINUTES = 60;
|
|
4
|
+
export declare const DEFAULT_SCHEDULING_HORIZON_WEEKS = 4;
|
|
5
|
+
export declare const DEFAULT_MINIMUM_BLOCK_MINUTES = 30;
|
|
6
|
+
export declare const DEFAULT_FOCUS_TIME_MINIMUM_BLOCK_MINUTES = 60;
|
|
7
|
+
export declare const VALID_PRIORITIES: readonly ["P1", "P2", "P3", "P4"];
|
|
8
|
+
export declare const VALID_STATUSES: readonly ["pending", "scheduled", "completed", "cancelled", "at_risk"];
|
|
9
|
+
export declare const VALID_PERIODS: readonly ["day", "week", "month"];
|
|
10
|
+
export declare const MAX_SCHEDULING_HORIZON_WEEKS = 12;
|
|
11
|
+
export declare const MIN_MINIMUM_BLOCK_MINUTES = 15;
|
|
12
|
+
export declare const MAX_MINIMUM_BLOCK_MINUTES = 120;
|
|
13
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/common/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAC9C,eAAO,MAAM,gBAAgB,EAAG,IAAa,CAAC;AAC9C,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAC3C,eAAO,MAAM,gCAAgC,IAAI,CAAC;AAClD,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAChD,eAAO,MAAM,wCAAwC,KAAK,CAAC;AAE3D,eAAO,MAAM,gBAAgB,mCAAoC,CAAC;AAClE,eAAO,MAAM,cAAc,wEAMjB,CAAC;AACX,eAAO,MAAM,aAAa,mCAAoC,CAAC;AAE/D,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAC/C,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAC5C,eAAO,MAAM,yBAAyB,MAAM,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const DEFAULT_BUFFER_TIME_MINUTES = 15;
|
|
2
|
+
export const DEFAULT_PRIORITY = "P3";
|
|
3
|
+
export const DEFAULT_DURATION_MINUTES = 60;
|
|
4
|
+
export const DEFAULT_SCHEDULING_HORIZON_WEEKS = 4;
|
|
5
|
+
export const DEFAULT_MINIMUM_BLOCK_MINUTES = 30;
|
|
6
|
+
export const DEFAULT_FOCUS_TIME_MINIMUM_BLOCK_MINUTES = 60;
|
|
7
|
+
export const VALID_PRIORITIES = ["P1", "P2", "P3", "P4"];
|
|
8
|
+
export const VALID_STATUSES = [
|
|
9
|
+
"pending",
|
|
10
|
+
"scheduled",
|
|
11
|
+
"completed",
|
|
12
|
+
"cancelled",
|
|
13
|
+
"at_risk",
|
|
14
|
+
];
|
|
15
|
+
export const VALID_PERIODS = ["day", "week", "month"];
|
|
16
|
+
export const MAX_SCHEDULING_HORIZON_WEEKS = 12;
|
|
17
|
+
export const MIN_MINIMUM_BLOCK_MINUTES = 15;
|
|
18
|
+
export const MAX_MINIMUM_BLOCK_MINUTES = 120;
|
|
19
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/common/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAC9C,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAa,CAAC;AAC9C,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAC3C,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,CAAC;AAClD,MAAM,CAAC,MAAM,6BAA6B,GAAG,EAAE,CAAC;AAChD,MAAM,CAAC,MAAM,wCAAwC,GAAG,EAAE,CAAC;AAE3D,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAC;AAClE,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,SAAS;IACT,WAAW;IACX,WAAW;IACX,WAAW;IACX,SAAS;CACD,CAAC;AACX,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC;AAE/D,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,CAAC;AAC/C,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,CAAC;AAC5C,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/common/id.ts"],"names":[],"mappings":"AAEA,wBAAgB,UAAU,IAAI,MAAM,CAEnC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id.js","sourceRoot":"","sources":["../../src/common/id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,UAAU,UAAU;IACxB,OAAO,UAAU,EAAE,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function toUTC(date: Date): string;
|
|
2
|
+
export declare function parseUTC(isoString: string): Date;
|
|
3
|
+
export declare function isValidISO8601(str: string): boolean;
|
|
4
|
+
export declare function isValidTimeHHMM(str: string): boolean;
|
|
5
|
+
export declare function isValidDateYYYYMMDD(str: string): boolean;
|
|
6
|
+
export declare function nowUTC(): string;
|
|
7
|
+
export declare function startOfDay(isoString: string): string;
|
|
8
|
+
export declare function endOfDay(isoString: string): string;
|
|
9
|
+
export declare function addMinutes(isoString: string, minutes: number): string;
|
|
10
|
+
export declare function diffMinutes(start: string, end: string): number;
|
|
11
|
+
//# sourceMappingURL=time.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../src/common/time.ts"],"names":[],"mappings":"AAAA,wBAAgB,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAExC;AAED,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAShD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAanD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAIpD;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQxD;AAED,wBAAgB,MAAM,IAAI,MAAM,CAE/B;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAIpD;AAED,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIrE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAI9D"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export function toUTC(date) {
|
|
2
|
+
return date.toISOString();
|
|
3
|
+
}
|
|
4
|
+
export function parseUTC(isoString) {
|
|
5
|
+
if (!isoString) {
|
|
6
|
+
throw new Error("Invalid ISO 8601 string: empty");
|
|
7
|
+
}
|
|
8
|
+
const date = new Date(isoString);
|
|
9
|
+
if (isNaN(date.getTime())) {
|
|
10
|
+
throw new Error(`Invalid ISO 8601 string: ${isoString}`);
|
|
11
|
+
}
|
|
12
|
+
return date;
|
|
13
|
+
}
|
|
14
|
+
export function isValidISO8601(str) {
|
|
15
|
+
if (!str)
|
|
16
|
+
return false;
|
|
17
|
+
const date = new Date(str);
|
|
18
|
+
if (isNaN(date.getTime()))
|
|
19
|
+
return false;
|
|
20
|
+
// Verify it round-trips reasonably (rejects things like month 13)
|
|
21
|
+
const isoRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
|
|
22
|
+
if (!isoRegex.test(str))
|
|
23
|
+
return false;
|
|
24
|
+
// Verify the parsed date components match the input
|
|
25
|
+
const [datePart] = str.split("T");
|
|
26
|
+
const [year, month, day] = datePart.split("-").map(Number);
|
|
27
|
+
return (date.getUTCFullYear() === year && date.getUTCMonth() + 1 === month && date.getUTCDate() === day);
|
|
28
|
+
}
|
|
29
|
+
export function isValidTimeHHMM(str) {
|
|
30
|
+
if (!/^\d{2}:\d{2}$/.test(str))
|
|
31
|
+
return false;
|
|
32
|
+
const [hours, minutes] = str.split(":").map(Number);
|
|
33
|
+
return hours >= 0 && hours <= 23 && minutes >= 0 && minutes <= 59;
|
|
34
|
+
}
|
|
35
|
+
export function isValidDateYYYYMMDD(str) {
|
|
36
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(str))
|
|
37
|
+
return false;
|
|
38
|
+
const [year, month, day] = str.split("-").map(Number);
|
|
39
|
+
if (month < 1 || month > 12 || day < 1 || day > 31)
|
|
40
|
+
return false;
|
|
41
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
|
42
|
+
return (date.getUTCFullYear() === year && date.getUTCMonth() + 1 === month && date.getUTCDate() === day);
|
|
43
|
+
}
|
|
44
|
+
export function nowUTC() {
|
|
45
|
+
return new Date().toISOString();
|
|
46
|
+
}
|
|
47
|
+
export function startOfDay(isoString) {
|
|
48
|
+
const date = parseUTC(isoString);
|
|
49
|
+
date.setUTCHours(0, 0, 0, 0);
|
|
50
|
+
return date.toISOString();
|
|
51
|
+
}
|
|
52
|
+
export function endOfDay(isoString) {
|
|
53
|
+
const date = parseUTC(isoString);
|
|
54
|
+
date.setUTCHours(23, 59, 59, 999);
|
|
55
|
+
return date.toISOString();
|
|
56
|
+
}
|
|
57
|
+
export function addMinutes(isoString, minutes) {
|
|
58
|
+
const date = parseUTC(isoString);
|
|
59
|
+
date.setTime(date.getTime() + minutes * 60_000);
|
|
60
|
+
return date.toISOString();
|
|
61
|
+
}
|
|
62
|
+
export function diffMinutes(start, end) {
|
|
63
|
+
const startDate = parseUTC(start);
|
|
64
|
+
const endDate = parseUTC(end);
|
|
65
|
+
return (endDate.getTime() - startDate.getTime()) / 60_000;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.js","sourceRoot":"","sources":["../../src/common/time.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,KAAK,CAAC,IAAU;IAC9B,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,SAAiB;IACxC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;IACjC,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,4BAA4B,SAAS,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IACvB,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,kEAAkE;IAClE,MAAM,QAAQ,GAAG,gDAAgD,CAAC;IAClE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,oDAAoD;IACpD,MAAM,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3D,OAAO,CACL,IAAI,CAAC,cAAc,EAAE,KAAK,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,UAAU,EAAE,KAAK,GAAG,CAChG,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpD,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,IAAI,OAAO,IAAI,CAAC,IAAI,OAAO,IAAI,EAAE,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACnD,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACtD,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,EAAE;QAAE,OAAO,KAAK,CAAC;IACjE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IACtD,OAAO,CACL,IAAI,CAAC,cAAc,EAAE,KAAK,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,UAAU,EAAE,KAAK,GAAG,CAChG,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,MAAM;IACpB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC1C,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IACjC,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7B,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,SAAiB;IACxC,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IACjC,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;IAClC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,SAAiB,EAAE,OAAe;IAC3D,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IACjC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC;IAChD,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,GAAW;IACpD,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC9B,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Task, TaskPriority } from "../models/task.js";
|
|
2
|
+
import type { TimeBlock } from "../models/schedule.js";
|
|
3
|
+
import type { Availability } from "../models/config.js";
|
|
4
|
+
import type { Conflict, DeprioritizationSuggestion } from "../models/conflict.js";
|
|
5
|
+
import type { DependencyEdge } from "../models/dependency.js";
|
|
6
|
+
interface CompetingTask {
|
|
7
|
+
taskId: string;
|
|
8
|
+
priority: TaskPriority;
|
|
9
|
+
deadline: string | null;
|
|
10
|
+
scheduledMinutes: number;
|
|
11
|
+
}
|
|
12
|
+
export declare class ConflictDetector {
|
|
13
|
+
detectConflicts(tasks: Task[], timeBlocks: TimeBlock[], _availability: Availability, dependencies: DependencyEdge[], now: Date): Conflict[];
|
|
14
|
+
suggestDeprioritizations(atRiskTask: Task, competingTasks: CompetingTask[], requiredMinutes: number): DeprioritizationSuggestion[];
|
|
15
|
+
findCompetingTasks(atRiskTask: Task, timeBlocks: TimeBlock[], allTasks: Task[]): CompetingTask[];
|
|
16
|
+
private getScheduledMinutes;
|
|
17
|
+
private computeAvailableMinutesBeforeDeadline;
|
|
18
|
+
private computeDependencyChainDuration;
|
|
19
|
+
private getDependencyChainIds;
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=conflict-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conflict-detector.d.ts","sourceRoot":"","sources":["../../src/engine/conflict-detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAU9D,UAAU,aAAa;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,YAAY,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,gBAAgB;IAC3B,eAAe,CACb,KAAK,EAAE,IAAI,EAAE,EACb,UAAU,EAAE,SAAS,EAAE,EACvB,aAAa,EAAE,YAAY,EAC3B,YAAY,EAAE,cAAc,EAAE,EAC9B,GAAG,EAAE,IAAI,GACR,QAAQ,EAAE;IA2Eb,wBAAwB,CACtB,UAAU,EAAE,IAAI,EAChB,cAAc,EAAE,aAAa,EAAE,EAC/B,eAAe,EAAE,MAAM,GACtB,0BAA0B,EAAE;IAmC/B,kBAAkB,CAAC,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,aAAa,EAAE;IAqChG,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,qCAAqC;IAO7C,OAAO,CAAC,8BAA8B;IAqCtC,OAAO,CAAC,qBAAqB;CA8B9B"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { diffMinutes } from "../common/time.js";
|
|
2
|
+
const PRIORITY_RANK = {
|
|
3
|
+
P1: 1,
|
|
4
|
+
P2: 2,
|
|
5
|
+
P3: 3,
|
|
6
|
+
P4: 4,
|
|
7
|
+
};
|
|
8
|
+
export class ConflictDetector {
|
|
9
|
+
detectConflicts(tasks, timeBlocks, _availability, dependencies, now) {
|
|
10
|
+
const conflicts = [];
|
|
11
|
+
for (const task of tasks) {
|
|
12
|
+
if (task.status === "completed" || task.status === "cancelled") {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
// Check 1: Overdue
|
|
16
|
+
if (task.deadline && new Date(task.deadline) < now) {
|
|
17
|
+
const scheduledMinutes = this.getScheduledMinutes(task.id, timeBlocks);
|
|
18
|
+
conflicts.push({
|
|
19
|
+
taskId: task.id,
|
|
20
|
+
reason: "overdue",
|
|
21
|
+
deadline: task.deadline,
|
|
22
|
+
requiredMinutes: task.duration,
|
|
23
|
+
availableMinutes: scheduledMinutes,
|
|
24
|
+
competingTaskIds: [],
|
|
25
|
+
suggestions: [],
|
|
26
|
+
});
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
// Tasks with no deadline can't have deadline-based conflicts
|
|
30
|
+
if (!task.deadline) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
// Check 2: Dependency chain infeasibility
|
|
34
|
+
const chainDuration = this.computeDependencyChainDuration(task, tasks, dependencies);
|
|
35
|
+
if (chainDuration > 0) {
|
|
36
|
+
const hoursAvailable = this.computeAvailableMinutesBeforeDeadline(now, new Date(task.deadline));
|
|
37
|
+
if (chainDuration > hoursAvailable) {
|
|
38
|
+
const chainIds = this.getDependencyChainIds(task, tasks, dependencies);
|
|
39
|
+
conflicts.push({
|
|
40
|
+
taskId: task.id,
|
|
41
|
+
reason: "dependency_chain",
|
|
42
|
+
deadline: task.deadline,
|
|
43
|
+
requiredMinutes: chainDuration,
|
|
44
|
+
availableMinutes: hoursAvailable,
|
|
45
|
+
competingTaskIds: chainIds,
|
|
46
|
+
suggestions: [],
|
|
47
|
+
});
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Check 3: Insufficient time
|
|
52
|
+
const scheduledMinutes = this.getScheduledMinutes(task.id, timeBlocks);
|
|
53
|
+
if (scheduledMinutes < task.duration) {
|
|
54
|
+
const competing = this.findCompetingTasks(task, timeBlocks, tasks);
|
|
55
|
+
const requiredMinutes = task.duration - scheduledMinutes;
|
|
56
|
+
const suggestions = this.suggestDeprioritizations(task, competing, requiredMinutes);
|
|
57
|
+
conflicts.push({
|
|
58
|
+
taskId: task.id,
|
|
59
|
+
reason: "insufficient_time",
|
|
60
|
+
deadline: task.deadline,
|
|
61
|
+
requiredMinutes,
|
|
62
|
+
availableMinutes: this.computeAvailableMinutesBeforeDeadline(now, new Date(task.deadline)),
|
|
63
|
+
competingTaskIds: competing.map((c) => c.taskId),
|
|
64
|
+
suggestions,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return conflicts;
|
|
69
|
+
}
|
|
70
|
+
suggestDeprioritizations(atRiskTask, competingTasks, requiredMinutes) {
|
|
71
|
+
// Filter: never suggest deprioritizing a task with a nearer deadline
|
|
72
|
+
const filtered = competingTasks.filter((t) => {
|
|
73
|
+
if (!t.deadline || !atRiskTask.deadline)
|
|
74
|
+
return true;
|
|
75
|
+
return new Date(t.deadline) > new Date(atRiskTask.deadline);
|
|
76
|
+
});
|
|
77
|
+
// Sort: lowest priority first (P4 before P3), then furthest deadline first
|
|
78
|
+
const sorted = [...filtered].sort((a, b) => {
|
|
79
|
+
const priorityDiff = (PRIORITY_RANK[b.priority] ?? 99) - (PRIORITY_RANK[a.priority] ?? 99);
|
|
80
|
+
if (priorityDiff !== 0)
|
|
81
|
+
return priorityDiff;
|
|
82
|
+
const aDeadline = a.deadline ? new Date(a.deadline).getTime() : Number.MAX_SAFE_INTEGER;
|
|
83
|
+
const bDeadline = b.deadline ? new Date(b.deadline).getTime() : Number.MAX_SAFE_INTEGER;
|
|
84
|
+
return bDeadline - aDeadline;
|
|
85
|
+
});
|
|
86
|
+
const suggestions = [];
|
|
87
|
+
let freedMinutes = 0;
|
|
88
|
+
for (const task of sorted) {
|
|
89
|
+
suggestions.push({
|
|
90
|
+
taskId: task.taskId,
|
|
91
|
+
currentPriority: task.priority,
|
|
92
|
+
freedMinutes: task.scheduledMinutes,
|
|
93
|
+
});
|
|
94
|
+
freedMinutes += task.scheduledMinutes;
|
|
95
|
+
if (freedMinutes >= requiredMinutes) {
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return suggestions;
|
|
100
|
+
}
|
|
101
|
+
findCompetingTasks(atRiskTask, timeBlocks, allTasks) {
|
|
102
|
+
if (!atRiskTask.deadline)
|
|
103
|
+
return [];
|
|
104
|
+
const deadline = new Date(atRiskTask.deadline);
|
|
105
|
+
const taskMap = new Map();
|
|
106
|
+
for (const task of allTasks) {
|
|
107
|
+
taskMap.set(task.id, task);
|
|
108
|
+
}
|
|
109
|
+
// Find blocks that occupy time before the at-risk task's deadline
|
|
110
|
+
const competingBlocks = timeBlocks.filter((b) => b.taskId !== atRiskTask.id && new Date(b.endTime) <= deadline);
|
|
111
|
+
// Group by taskId and sum scheduled time
|
|
112
|
+
const minutesByTask = new Map();
|
|
113
|
+
for (const block of competingBlocks) {
|
|
114
|
+
const minutes = diffMinutes(block.startTime, block.endTime);
|
|
115
|
+
minutesByTask.set(block.taskId, (minutesByTask.get(block.taskId) ?? 0) + minutes);
|
|
116
|
+
}
|
|
117
|
+
const result = [];
|
|
118
|
+
for (const [taskId, scheduledMinutes] of minutesByTask) {
|
|
119
|
+
const task = taskMap.get(taskId);
|
|
120
|
+
if (task) {
|
|
121
|
+
result.push({
|
|
122
|
+
taskId,
|
|
123
|
+
priority: task.priority,
|
|
124
|
+
deadline: task.deadline,
|
|
125
|
+
scheduledMinutes,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
getScheduledMinutes(taskId, timeBlocks) {
|
|
132
|
+
return timeBlocks
|
|
133
|
+
.filter((b) => b.taskId === taskId)
|
|
134
|
+
.reduce((sum, b) => sum + diffMinutes(b.startTime, b.endTime), 0);
|
|
135
|
+
}
|
|
136
|
+
computeAvailableMinutesBeforeDeadline(now, deadline) {
|
|
137
|
+
// Rough estimate: total minutes between now and deadline
|
|
138
|
+
// A more accurate calculation would factor in availability windows,
|
|
139
|
+
// but for conflict reporting this provides a useful upper bound
|
|
140
|
+
return Math.max(0, (deadline.getTime() - now.getTime()) / 60_000);
|
|
141
|
+
}
|
|
142
|
+
computeDependencyChainDuration(task, allTasks, dependencies) {
|
|
143
|
+
const taskMap = new Map();
|
|
144
|
+
for (const t of allTasks) {
|
|
145
|
+
taskMap.set(t.id, t);
|
|
146
|
+
}
|
|
147
|
+
// Walk backwards through dependencies, summing durations
|
|
148
|
+
const visited = new Set();
|
|
149
|
+
let totalDuration = 0;
|
|
150
|
+
const walk = (taskId) => {
|
|
151
|
+
if (visited.has(taskId))
|
|
152
|
+
return;
|
|
153
|
+
visited.add(taskId);
|
|
154
|
+
const deps = dependencies.filter((d) => d.taskId === taskId);
|
|
155
|
+
for (const dep of deps) {
|
|
156
|
+
const depTask = taskMap.get(dep.dependsOnId);
|
|
157
|
+
if (depTask && depTask.status !== "completed") {
|
|
158
|
+
totalDuration += depTask.duration;
|
|
159
|
+
walk(dep.dependsOnId);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
walk(task.id);
|
|
164
|
+
// Include the task itself
|
|
165
|
+
if (totalDuration > 0) {
|
|
166
|
+
totalDuration += task.duration;
|
|
167
|
+
}
|
|
168
|
+
return totalDuration;
|
|
169
|
+
}
|
|
170
|
+
getDependencyChainIds(task, allTasks, dependencies) {
|
|
171
|
+
const taskMap = new Map();
|
|
172
|
+
for (const t of allTasks) {
|
|
173
|
+
taskMap.set(t.id, t);
|
|
174
|
+
}
|
|
175
|
+
const visited = new Set();
|
|
176
|
+
const chainIds = [];
|
|
177
|
+
const walk = (taskId) => {
|
|
178
|
+
if (visited.has(taskId))
|
|
179
|
+
return;
|
|
180
|
+
visited.add(taskId);
|
|
181
|
+
const deps = dependencies.filter((d) => d.taskId === taskId);
|
|
182
|
+
for (const dep of deps) {
|
|
183
|
+
const depTask = taskMap.get(dep.dependsOnId);
|
|
184
|
+
if (depTask && depTask.status !== "completed") {
|
|
185
|
+
chainIds.push(dep.dependsOnId);
|
|
186
|
+
walk(dep.dependsOnId);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
walk(task.id);
|
|
191
|
+
return chainIds;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=conflict-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conflict-detector.js","sourceRoot":"","sources":["../../src/engine/conflict-detector.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,aAAa,GAA2B;IAC5C,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,CAAC;CACN,CAAC;AASF,MAAM,OAAO,gBAAgB;IAC3B,eAAe,CACb,KAAa,EACb,UAAuB,EACvB,aAA2B,EAC3B,YAA8B,EAC9B,GAAS;QAET,MAAM,SAAS,GAAe,EAAE,CAAC;QAEjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,MAAM,KAAK,WAAW,IAAI,IAAI,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;gBAC/D,SAAS;YACX,CAAC;YAED,mBAAmB;YACnB,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,EAAE,CAAC;gBACnD,MAAM,gBAAgB,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;gBACvE,SAAS,CAAC,IAAI,CAAC;oBACb,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,MAAM,EAAE,SAAS;oBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,eAAe,EAAE,IAAI,CAAC,QAAQ;oBAC9B,gBAAgB,EAAE,gBAAgB;oBAClC,gBAAgB,EAAE,EAAE;oBACpB,WAAW,EAAE,EAAE;iBAChB,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,6DAA6D;YAC7D,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACnB,SAAS;YACX,CAAC;YAED,0CAA0C;YAC1C,MAAM,aAAa,GAAG,IAAI,CAAC,8BAA8B,CAAC,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;YACrF,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;gBACtB,MAAM,cAAc,GAAG,IAAI,CAAC,qCAAqC,CAC/D,GAAG,EACH,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CACxB,CAAC;gBACF,IAAI,aAAa,GAAG,cAAc,EAAE,CAAC;oBACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,qBAAqB,CAAC,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;oBACvE,SAAS,CAAC,IAAI,CAAC;wBACb,MAAM,EAAE,IAAI,CAAC,EAAE;wBACf,MAAM,EAAE,kBAAkB;wBAC1B,QAAQ,EAAE,IAAI,CAAC,QAAQ;wBACvB,eAAe,EAAE,aAAa;wBAC9B,gBAAgB,EAAE,cAAc;wBAChC,gBAAgB,EAAE,QAAQ;wBAC1B,WAAW,EAAE,EAAE;qBAChB,CAAC,CAAC;oBACH,SAAS;gBACX,CAAC;YACH,CAAC;YAED,6BAA6B;YAC7B,MAAM,gBAAgB,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YACvE,IAAI,gBAAgB,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACrC,MAAM,SAAS,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;gBACnE,MAAM,eAAe,GAAG,IAAI,CAAC,QAAQ,GAAG,gBAAgB,CAAC;gBACzD,MAAM,WAAW,GAAG,IAAI,CAAC,wBAAwB,CAAC,IAAI,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;gBAEpF,SAAS,CAAC,IAAI,CAAC;oBACb,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,MAAM,EAAE,mBAAmB;oBAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,eAAe;oBACf,gBAAgB,EAAE,IAAI,CAAC,qCAAqC,CAC1D,GAAG,EACH,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CACxB;oBACD,gBAAgB,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;oBAChD,WAAW;iBACZ,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,wBAAwB,CACtB,UAAgB,EAChB,cAA+B,EAC/B,eAAuB;QAEvB,qEAAqE;QACrE,MAAM,QAAQ,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YAC3C,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,UAAU,CAAC,QAAQ;gBAAE,OAAO,IAAI,CAAC;YACrD,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;QAEH,2EAA2E;QAC3E,MAAM,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACzC,MAAM,YAAY,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;YAC3F,IAAI,YAAY,KAAK,CAAC;gBAAE,OAAO,YAAY,CAAC;YAE5C,MAAM,SAAS,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC;YACxF,MAAM,SAAS,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC;YACxF,OAAO,SAAS,GAAG,SAAS,CAAC;QAC/B,CAAC,CAAC,CAAC;QAEH,MAAM,WAAW,GAAiC,EAAE,CAAC;QACrD,IAAI,YAAY,GAAG,CAAC,CAAC;QAErB,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;YAC1B,WAAW,CAAC,IAAI,CAAC;gBACf,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,eAAe,EAAE,IAAI,CAAC,QAAQ;gBAC9B,YAAY,EAAE,IAAI,CAAC,gBAAgB;aACpC,CAAC,CAAC;YACH,YAAY,IAAI,IAAI,CAAC,gBAAgB,CAAC;YACtC,IAAI,YAAY,IAAI,eAAe,EAAE,CAAC;gBACpC,MAAM;YACR,CAAC;QACH,CAAC;QAED,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,kBAAkB,CAAC,UAAgB,EAAE,UAAuB,EAAE,QAAgB;QAC5E,IAAI,CAAC,UAAU,CAAC,QAAQ;YAAE,OAAO,EAAE,CAAC;QAEpC,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAgB,CAAC;QACxC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC7B,CAAC;QAED,kEAAkE;QAClE,MAAM,eAAe,GAAG,UAAU,CAAC,MAAM,CACvC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,QAAQ,CACrE,CAAC;QAEF,yCAAyC;QACzC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;QAChD,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YAC5D,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC;QACpF,CAAC;QAED,MAAM,MAAM,GAAoB,EAAE,CAAC;QACnC,KAAK,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,IAAI,aAAa,EAAE,CAAC;YACvD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACjC,IAAI,IAAI,EAAE,CAAC;gBACT,MAAM,CAAC,IAAI,CAAC;oBACV,MAAM;oBACN,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,gBAAgB;iBACjB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,mBAAmB,CAAC,MAAc,EAAE,UAAuB;QACjE,OAAO,UAAU;aACd,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC;aAClC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IACtE,CAAC;IAEO,qCAAqC,CAAC,GAAS,EAAE,QAAc;QACrE,yDAAyD;QACzD,oEAAoE;QACpE,gEAAgE;QAChE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC;IACpE,CAAC;IAEO,8BAA8B,CACpC,IAAU,EACV,QAAgB,EAChB,YAA8B;QAE9B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAgB,CAAC;QACxC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACvB,CAAC;QAED,yDAAyD;QACzD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,IAAI,aAAa,GAAG,CAAC,CAAC;QAEtB,MAAM,IAAI,GAAG,CAAC,MAAc,EAAQ,EAAE;YACpC,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;gBAAE,OAAO;YAChC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAEpB,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;YAC7D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;gBAC7C,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;oBAC9C,aAAa,IAAI,OAAO,CAAC,QAAQ,CAAC;oBAClC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;gBACxB,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAEF,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACd,0BAA0B;QAC1B,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;YACtB,aAAa,IAAI,IAAI,CAAC,QAAQ,CAAC;QACjC,CAAC;QAED,OAAO,aAAa,CAAC;IACvB,CAAC;IAEO,qBAAqB,CAC3B,IAAU,EACV,QAAgB,EAChB,YAA8B;QAE9B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAgB,CAAC;QACxC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACvB,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,MAAM,QAAQ,GAAa,EAAE,CAAC;QAE9B,MAAM,IAAI,GAAG,CAAC,MAAc,EAAQ,EAAE;YACpC,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;gBAAE,OAAO;YAChC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAEpB,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;YAC7D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;gBAC7C,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;oBAC9C,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;oBAC/B,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;gBACxB,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAEF,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACd,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Task } from "../models/task.js";
|
|
2
|
+
import type { DependencyEdge } from "../models/dependency.js";
|
|
3
|
+
export declare class DependencyResolver {
|
|
4
|
+
validateNoCycles(taskId: string, dependsOnId: string, existingDeps: DependencyEdge[]): boolean;
|
|
5
|
+
topologicalSort(tasks: Task[], dependencies: DependencyEdge[]): Task[];
|
|
6
|
+
getBlockedTasks(tasks: Task[], dependencies: DependencyEdge[]): Task[];
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=dependency-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependency-resolver.d.ts","sourceRoot":"","sources":["../../src/engine/dependency-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AA2E9D,qBAAa,kBAAkB;IAC7B,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,cAAc,EAAE,GAAG,OAAO;IAuD9F,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,cAAc,EAAE,GAAG,IAAI,EAAE;IAoCtE,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,cAAc,EAAE,GAAG,IAAI,EAAE;CAqBvE"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { CircularDependencyError, ValidationError } from "../models/errors.js";
|
|
2
|
+
const PRIORITY_RANK = {
|
|
3
|
+
P1: 1,
|
|
4
|
+
P2: 2,
|
|
5
|
+
P3: 3,
|
|
6
|
+
P4: 4,
|
|
7
|
+
};
|
|
8
|
+
function buildAdjacencyMap(edges) {
|
|
9
|
+
const adj = new Map();
|
|
10
|
+
for (const edge of edges) {
|
|
11
|
+
const deps = adj.get(edge.dependsOnId) ?? [];
|
|
12
|
+
deps.push(edge.taskId);
|
|
13
|
+
adj.set(edge.dependsOnId, deps);
|
|
14
|
+
}
|
|
15
|
+
return adj;
|
|
16
|
+
}
|
|
17
|
+
function taskSortKey(task) {
|
|
18
|
+
const priority = PRIORITY_RANK[task.priority] ?? 99;
|
|
19
|
+
const deadline = task.deadline ? new Date(task.deadline).getTime() : Number.MAX_SAFE_INTEGER;
|
|
20
|
+
return [priority, deadline, task.duration];
|
|
21
|
+
}
|
|
22
|
+
function compareTasks(a, b) {
|
|
23
|
+
const [ap, ad, adur] = taskSortKey(a);
|
|
24
|
+
const [bp, bd, bdur] = taskSortKey(b);
|
|
25
|
+
return ap - bp || ad - bd || adur - bdur;
|
|
26
|
+
}
|
|
27
|
+
function buildInDegreeAndQueue(tasks, dependencies, taskMap) {
|
|
28
|
+
const inDegree = new Map();
|
|
29
|
+
for (const task of tasks) {
|
|
30
|
+
inDegree.set(task.id, 0);
|
|
31
|
+
}
|
|
32
|
+
for (const edge of dependencies) {
|
|
33
|
+
if (taskMap.has(edge.taskId)) {
|
|
34
|
+
inDegree.set(edge.taskId, (inDegree.get(edge.taskId) ?? 0) + 1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const queue = [];
|
|
38
|
+
for (const task of tasks) {
|
|
39
|
+
if ((inDegree.get(task.id) ?? 0) === 0) {
|
|
40
|
+
queue.push(task);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
queue.sort(compareTasks);
|
|
44
|
+
return { inDegree, queue };
|
|
45
|
+
}
|
|
46
|
+
function insertSorted(queue, task) {
|
|
47
|
+
const key = taskSortKey(task);
|
|
48
|
+
let insertIdx = queue.length;
|
|
49
|
+
for (let i = 0; i < queue.length; i++) {
|
|
50
|
+
const qKey = taskSortKey(queue[i]);
|
|
51
|
+
if (key[0] < qKey[0] ||
|
|
52
|
+
(key[0] === qKey[0] && key[1] < qKey[1]) ||
|
|
53
|
+
(key[0] === qKey[0] && key[1] === qKey[1] && key[2] < qKey[2])) {
|
|
54
|
+
insertIdx = i;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
queue.splice(insertIdx, 0, task);
|
|
59
|
+
}
|
|
60
|
+
export class DependencyResolver {
|
|
61
|
+
validateNoCycles(taskId, dependsOnId, existingDeps) {
|
|
62
|
+
if (taskId === dependsOnId) {
|
|
63
|
+
throw new ValidationError("a task cannot depend on itself");
|
|
64
|
+
}
|
|
65
|
+
// Check: can we reach taskId starting from dependsOnId via existing edges?
|
|
66
|
+
// Edges go: dependsOnId → taskId (dependsOn direction)
|
|
67
|
+
// We need to traverse: from dependsOnId, follow "who depends on this node" edges
|
|
68
|
+
// If we can reach taskId, then adding taskId→dependsOnId creates a cycle.
|
|
69
|
+
//
|
|
70
|
+
// Actually, rethink: edges represent "taskId depends on dependsOnId".
|
|
71
|
+
// To check if adding (taskId depends on dependsOnId) creates a cycle,
|
|
72
|
+
// we need to check: can we reach dependsOnId from taskId via existing dependency chains?
|
|
73
|
+
// i.e., does taskId have a path TO dependsOnId through existing edges?
|
|
74
|
+
// An edge { taskId: X, dependsOnId: Y } means X depends on Y, so X comes after Y.
|
|
75
|
+
// Following "depends on" from taskId: taskId → its dependencies → their dependencies → ...
|
|
76
|
+
// If we find dependsOnId in that chain, adding dependsOnId as another dependency of taskId
|
|
77
|
+
// doesn't create a cycle (it's just adding a redundant path).
|
|
78
|
+
//
|
|
79
|
+
// The cycle occurs if dependsOnId can reach taskId via "depends on" edges.
|
|
80
|
+
// i.e., dependsOnId depends on ... depends on taskId. Then adding taskId depends on dependsOnId
|
|
81
|
+
// closes the loop.
|
|
82
|
+
//
|
|
83
|
+
// Build reverse lookup: for each node, what does it depend on?
|
|
84
|
+
const dependsOnMap = new Map();
|
|
85
|
+
for (const edge of existingDeps) {
|
|
86
|
+
const deps = dependsOnMap.get(edge.taskId) ?? [];
|
|
87
|
+
deps.push(edge.dependsOnId);
|
|
88
|
+
dependsOnMap.set(edge.taskId, deps);
|
|
89
|
+
}
|
|
90
|
+
// DFS from dependsOnId, following "dependsOn" edges, looking for taskId
|
|
91
|
+
const visited = new Set();
|
|
92
|
+
const stack = [dependsOnId];
|
|
93
|
+
const path = [];
|
|
94
|
+
while (stack.length > 0) {
|
|
95
|
+
const current = stack.pop();
|
|
96
|
+
if (current === taskId) {
|
|
97
|
+
throw new CircularDependencyError([taskId, ...path, current]);
|
|
98
|
+
}
|
|
99
|
+
if (visited.has(current)) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
visited.add(current);
|
|
103
|
+
path.push(current);
|
|
104
|
+
const neighbors = dependsOnMap.get(current) ?? [];
|
|
105
|
+
for (const neighbor of neighbors) {
|
|
106
|
+
stack.push(neighbor);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
topologicalSort(tasks, dependencies) {
|
|
112
|
+
if (tasks.length === 0)
|
|
113
|
+
return [];
|
|
114
|
+
const taskMap = new Map();
|
|
115
|
+
for (const task of tasks) {
|
|
116
|
+
taskMap.set(task.id, task);
|
|
117
|
+
}
|
|
118
|
+
const adj = buildAdjacencyMap(dependencies);
|
|
119
|
+
const { inDegree, queue } = buildInDegreeAndQueue(tasks, dependencies, taskMap);
|
|
120
|
+
const result = [];
|
|
121
|
+
while (queue.length > 0) {
|
|
122
|
+
const node = queue.shift();
|
|
123
|
+
result.push(node);
|
|
124
|
+
for (const depId of adj.get(node.id) ?? []) {
|
|
125
|
+
const depTask = taskMap.get(depId);
|
|
126
|
+
if (!depTask)
|
|
127
|
+
continue;
|
|
128
|
+
const newDegree = (inDegree.get(depId) ?? 1) - 1;
|
|
129
|
+
inDegree.set(depId, newDegree);
|
|
130
|
+
if (newDegree === 0) {
|
|
131
|
+
insertSorted(queue, depTask);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (result.length < tasks.length) {
|
|
136
|
+
const remainingIds = tasks.filter((t) => !result.includes(t)).map((t) => t.id);
|
|
137
|
+
throw new CircularDependencyError(remainingIds);
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
getBlockedTasks(tasks, dependencies) {
|
|
142
|
+
const taskMap = new Map();
|
|
143
|
+
for (const task of tasks) {
|
|
144
|
+
taskMap.set(task.id, task);
|
|
145
|
+
}
|
|
146
|
+
const blocked = [];
|
|
147
|
+
for (const task of tasks) {
|
|
148
|
+
const deps = dependencies.filter((d) => d.taskId === task.id);
|
|
149
|
+
for (const dep of deps) {
|
|
150
|
+
const depTask = taskMap.get(dep.dependsOnId);
|
|
151
|
+
if (depTask && depTask.status !== "completed") {
|
|
152
|
+
blocked.push(task);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return blocked;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=dependency-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependency-resolver.js","sourceRoot":"","sources":["../../src/engine/dependency-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { RecurrenceRepository } from "../storage/recurrence-repository.js";
|
|
2
|
+
import type { TaskRepository } from "../storage/task-repository.js";
|
|
3
|
+
import type { RecurrenceTemplate, RecurrenceInstance } from "../models/recurrence.js";
|
|
4
|
+
import type { Task } from "../models/task.js";
|
|
5
|
+
type TaskFields = Omit<Task, "id" | "createdAt" | "updatedAt" | "status" | "actualDuration">;
|
|
6
|
+
export declare class RecurrenceManager {
|
|
7
|
+
private readonly recurrenceRepo;
|
|
8
|
+
private readonly taskRepo;
|
|
9
|
+
private currentHorizonEnd;
|
|
10
|
+
constructor(recurrenceRepo: RecurrenceRepository, taskRepo: TaskRepository);
|
|
11
|
+
createRecurringTask(taskData: TaskFields, rruleString: string, now: Date, horizonEnd: Date): {
|
|
12
|
+
template: RecurrenceTemplate;
|
|
13
|
+
instances: RecurrenceInstance[];
|
|
14
|
+
};
|
|
15
|
+
generateInstances(templateId: string, now: Date, horizonEnd: Date): RecurrenceInstance[];
|
|
16
|
+
expandHorizon(newHorizonEnd: Date): RecurrenceInstance[];
|
|
17
|
+
skipInstance(templateId: string, date: string): void;
|
|
18
|
+
modifyInstance(templateId: string, date: string, updates: Partial<Task>): void;
|
|
19
|
+
deleteTemplate(templateId: string, now: Date): void;
|
|
20
|
+
private generateInstancesForTemplate;
|
|
21
|
+
private parseRule;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=recurrence-manager.d.ts.map
|