@critical-path/core 0.1.2 → 0.2.1

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +43 -26
  3. package/dist/engine/index.d.ts +19 -1
  4. package/dist/engine/index.d.ts.map +1 -1
  5. package/dist/engine/index.js +129 -6
  6. package/dist/engine/index.js.map +1 -1
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.test.js +96 -1
  12. package/dist/index.test.js.map +1 -1
  13. package/dist/store/firebase.d.ts +113 -0
  14. package/dist/store/firebase.d.ts.map +1 -0
  15. package/dist/store/firebase.js +319 -0
  16. package/dist/store/firebase.js.map +1 -0
  17. package/dist/store/firebase.test.d.ts +2 -0
  18. package/dist/store/firebase.test.d.ts.map +1 -0
  19. package/dist/store/firebase.test.js +45 -0
  20. package/dist/store/firebase.test.js.map +1 -0
  21. package/dist/store/index.d.ts +36 -8
  22. package/dist/store/index.d.ts.map +1 -1
  23. package/dist/store/index.js +78 -12
  24. package/dist/store/index.js.map +1 -1
  25. package/dist/store/sqlite.d.ts +61 -0
  26. package/dist/store/sqlite.d.ts.map +1 -0
  27. package/dist/store/sqlite.js +492 -0
  28. package/dist/store/sqlite.js.map +1 -0
  29. package/dist/store/sqlite.test.d.ts +2 -0
  30. package/dist/store/sqlite.test.d.ts.map +1 -0
  31. package/dist/store/sqlite.test.js +63 -0
  32. package/dist/store/sqlite.test.js.map +1 -0
  33. package/dist/types/index.d.ts +68 -8
  34. package/dist/types/index.d.ts.map +1 -1
  35. package/dist/utils/status.d.ts +14 -0
  36. package/dist/utils/status.d.ts.map +1 -0
  37. package/dist/utils/status.js +58 -0
  38. package/dist/utils/status.js.map +1 -0
  39. package/package.json +9 -1
  40. package/src/engine/index.ts +161 -8
  41. package/src/index.test.ts +113 -1
  42. package/src/index.ts +1 -0
  43. package/src/store/firebase.test.ts +59 -0
  44. package/src/store/firebase.ts +381 -0
  45. package/src/store/index.ts +113 -19
  46. package/src/store/sqlite.test.ts +78 -0
  47. package/src/store/sqlite.ts +729 -0
  48. package/src/types/index.ts +74 -9
  49. package/src/utils/status.ts +85 -0
  50. package/tsconfig.tsbuildinfo +1 -1
@@ -1,5 +1,16 @@
1
- export type Priority = 'urgent' | 'high' | 'medium' | 'low' | 'none';
2
- export type TaskStatus = 'backlog' | 'todo' | 'in_progress' | 'in_review' | 'done' | 'canceled';
1
+ export type DefaultPriority = 'urgent' | 'high' | 'medium' | 'low' | 'none';
2
+ export type Priority = DefaultPriority | (string & {});
3
+ export type DefaultTaskStatus = 'backlog' | 'todo' | 'in_progress' | 'in_review' | 'done' | 'canceled';
4
+ export type TaskStatus = DefaultTaskStatus | (string & {});
5
+ export type CompletionState = 'done' | 'not_done';
6
+ export type ExecutionState = 'active' | 'inactive';
7
+ export interface StatusDefinition {
8
+ key: string;
9
+ label: string;
10
+ completionState: CompletionState;
11
+ executionState: ExecutionState;
12
+ isCancelled?: boolean;
13
+ }
3
14
  export type Role = 'admin' | 'project_manager' | 'contributor' | 'viewer';
4
15
  export interface User {
5
16
  id: string;
@@ -9,6 +20,15 @@ export interface User {
9
20
  role: Role;
10
21
  createdAt: string;
11
22
  }
23
+ export interface Team {
24
+ id: string;
25
+ name: string;
26
+ description?: string;
27
+ leaderId?: string;
28
+ memberIds: string[];
29
+ createdAt: string;
30
+ updatedAt: string;
31
+ }
12
32
  export interface CustomFieldDefinition {
13
33
  id: string;
14
34
  key: string;
@@ -25,16 +45,40 @@ export interface Project {
25
45
  description?: string;
26
46
  ownerId?: string;
27
47
  members?: string[];
48
+ teamIds?: string[];
49
+ statusDefinitions?: StatusDefinition[];
50
+ priorityDefinitions?: Array<{
51
+ key: string;
52
+ label: string;
53
+ level?: number;
54
+ }>;
28
55
  customFieldDefinitions?: CustomFieldDefinition[];
29
56
  createdAt: string;
30
57
  updatedAt: string;
31
58
  }
59
+ export interface TaskContainer {
60
+ id: string;
61
+ projectId: string;
62
+ name: string;
63
+ description?: string;
64
+ parentId?: string;
65
+ type?: 'epic' | 'group' | 'section' | 'folder' | string;
66
+ color?: string;
67
+ createdAt: string;
68
+ updatedAt: string;
69
+ }
32
70
  export interface TaskDependency {
33
71
  id: string;
34
72
  taskId: string;
35
73
  dependsOnTaskId: string;
36
74
  type: 'blocking' | 'blocked_by' | 'relates_to';
37
75
  }
76
+ export interface TaskDependencyGraph {
77
+ taskId: string;
78
+ upstreamTasks: Task[];
79
+ downstreamTasks: Task[];
80
+ dependencies: TaskDependency[];
81
+ }
38
82
  export interface Task {
39
83
  id: string;
40
84
  projectId: string;
@@ -44,23 +88,36 @@ export interface Task {
44
88
  priority: Priority;
45
89
  assigneeId?: string;
46
90
  reporterId?: string;
47
- sprintId?: string;
91
+ reviewerId?: string;
92
+ iterationId?: string;
93
+ teamId?: string;
94
+ containerId?: string;
95
+ plannedStartDate?: string;
96
+ actualStartDate?: string;
97
+ actualEndDate?: string;
48
98
  dueDate?: string;
49
99
  estimatedHours?: number;
50
100
  loggedHours?: number;
101
+ actualHours?: number;
102
+ billableHours?: number;
103
+ estimatedDurationMinutes?: number;
104
+ actualDurationMinutes?: number;
105
+ billableDurationMinutes?: number;
106
+ progress?: number;
51
107
  tags?: string[];
52
108
  customFields?: Record<string, unknown>;
53
109
  parentId?: string;
54
110
  createdAt: string;
55
111
  updatedAt: string;
56
112
  }
57
- export interface Sprint {
113
+ export interface Iteration {
58
114
  id: string;
59
115
  projectId: string;
60
116
  name: string;
61
117
  goal?: string;
62
- startDate: string;
63
- endDate: string;
118
+ type?: 'sprint' | 'cycle' | 'milestone' | string;
119
+ startDate?: string;
120
+ endDate?: string;
64
121
  status: 'planning' | 'active' | 'completed';
65
122
  createdAt: string;
66
123
  }
@@ -77,6 +134,7 @@ export interface TimeEntry {
77
134
  taskId: string;
78
135
  userId: string;
79
136
  hours: number;
137
+ isBillable?: boolean;
80
138
  description?: string;
81
139
  loggedAt: string;
82
140
  }
@@ -98,7 +156,7 @@ export interface Webhook {
98
156
  active: boolean;
99
157
  createdAt: string;
100
158
  }
101
- export type WebhookEvent = 'project.created' | 'project.updated' | 'task.created' | 'task.updated' | 'task.deleted' | 'task.status_changed' | 'comment.created' | 'sprint.started' | 'sprint.completed';
159
+ export type WebhookEvent = 'project.created' | 'project.updated' | 'task.created' | 'task.updated' | 'task.deleted' | 'task.status_changed' | 'comment.created' | 'iteration.started' | 'iteration.completed' | 'team.created' | 'container.created';
102
160
  export interface PluginHooks {
103
161
  beforeTaskCreate?: (task: Partial<Task>) => Promise<Partial<Task>> | Partial<Task>;
104
162
  afterTaskCreate?: (task: Task) => Promise<void> | void;
@@ -124,7 +182,9 @@ export interface CriticalPathConfig {
124
182
  projects?: Project[];
125
183
  tasks?: Task[];
126
184
  users?: User[];
127
- sprints?: Sprint[];
185
+ iterations?: Iteration[];
186
+ teams?: Team[];
187
+ containers?: TaskContainer[];
128
188
  };
129
189
  }
130
190
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { Task, StatusDefinition, CompletionState, ExecutionState } from '../types/index.js';
2
+ export declare const DEFAULT_STATUS_DEFINITIONS: Record<string, StatusDefinition>;
3
+ export interface TaskLifecycleState {
4
+ completionState: CompletionState;
5
+ executionState: ExecutionState;
6
+ isDone: boolean;
7
+ isActive: boolean;
8
+ isCancelled: boolean;
9
+ isOverdue: boolean;
10
+ isUpcoming: boolean;
11
+ }
12
+ export declare function resolveStatusDefinition(statusKey: string, customDefinitions?: StatusDefinition[]): StatusDefinition;
13
+ export declare function deriveTaskLifecycleState(task: Task, customDefinitions?: StatusDefinition[], referenceDate?: Date): TaskLifecycleState;
14
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/utils/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,IAAI,EACJ,gBAAgB,EAChB,eAAe,EACf,cAAc,EACf,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,0BAA0B,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAOvE,CAAC;AAEF,MAAM,WAAW,kBAAkB;IACjC,eAAe,EAAE,eAAe,CAAC;IACjC,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,iBAAiB,CAAC,EAAE,gBAAgB,EAAE,GACrC,gBAAgB,CAkBlB;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,IAAI,EACV,iBAAiB,CAAC,EAAE,gBAAgB,EAAE,EACtC,aAAa,GAAE,IAAiB,GAC/B,kBAAkB,CA+BpB"}
@@ -0,0 +1,58 @@
1
+ export const DEFAULT_STATUS_DEFINITIONS = {
2
+ backlog: { key: 'backlog', label: 'Backlog', completionState: 'not_done', executionState: 'inactive' },
3
+ todo: { key: 'todo', label: 'To Do', completionState: 'not_done', executionState: 'inactive' },
4
+ in_progress: { key: 'in_progress', label: 'In Progress', completionState: 'not_done', executionState: 'active' },
5
+ in_review: { key: 'in_review', label: 'In Review', completionState: 'not_done', executionState: 'active' },
6
+ done: { key: 'done', label: 'Done', completionState: 'done', executionState: 'inactive' },
7
+ canceled: { key: 'canceled', label: 'Canceled', completionState: 'done', executionState: 'inactive', isCancelled: true }
8
+ };
9
+ export function resolveStatusDefinition(statusKey, customDefinitions) {
10
+ if (customDefinitions) {
11
+ const found = customDefinitions.find((def) => def.key === statusKey);
12
+ if (found)
13
+ return found;
14
+ }
15
+ if (DEFAULT_STATUS_DEFINITIONS[statusKey]) {
16
+ return DEFAULT_STATUS_DEFINITIONS[statusKey];
17
+ }
18
+ // Fallback heuristic for unknown custom statuses:
19
+ const isDoneKey = ['done', 'completed', 'finished', 'closed', 'canceled', 'cancelled'].includes(statusKey.toLowerCase());
20
+ const isActiveKey = ['in_progress', 'active', 'working', 'in_review', 'review'].includes(statusKey.toLowerCase());
21
+ return {
22
+ key: statusKey,
23
+ label: statusKey,
24
+ completionState: isDoneKey ? 'done' : 'not_done',
25
+ executionState: isActiveKey ? 'active' : 'inactive',
26
+ isCancelled: statusKey.toLowerCase().includes('cancel')
27
+ };
28
+ }
29
+ export function deriveTaskLifecycleState(task, customDefinitions, referenceDate = new Date()) {
30
+ const def = resolveStatusDefinition(task.status, customDefinitions);
31
+ const isDone = def.completionState === 'done';
32
+ const isActive = def.executionState === 'active';
33
+ const isCancelled = Boolean(def.isCancelled);
34
+ let isOverdue = false;
35
+ if (!isDone && task.dueDate) {
36
+ const due = new Date(task.dueDate);
37
+ if (!isNaN(due.getTime()) && due < referenceDate) {
38
+ isOverdue = true;
39
+ }
40
+ }
41
+ let isUpcoming = false;
42
+ if (!isDone && !isActive && task.plannedStartDate) {
43
+ const plannedStart = new Date(task.plannedStartDate);
44
+ if (!isNaN(plannedStart.getTime()) && plannedStart > referenceDate) {
45
+ isUpcoming = true;
46
+ }
47
+ }
48
+ return {
49
+ completionState: def.completionState,
50
+ executionState: def.executionState,
51
+ isDone,
52
+ isActive,
53
+ isCancelled,
54
+ isOverdue,
55
+ isUpcoming
56
+ };
57
+ }
58
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/utils/status.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "@critical-path/core",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "Core headless domain engine, plugin architecture, and data stores for Critical Path",
5
5
  "author": "Jack James",
6
6
  "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Pixerate/critical-path.git"
10
+ },
7
11
  "type": "module",
8
12
  "main": "./dist/index.js",
9
13
  "types": "./dist/index.d.ts",
@@ -14,6 +18,7 @@
14
18
  }
15
19
  },
16
20
  "devDependencies": {
21
+ "@types/better-sqlite3": "^9.6.0",
17
22
  "@types/node": "^24.0.0",
18
23
  "typescript": "^5.7.0",
19
24
  "vitest": "^3.0.0"
@@ -21,6 +26,9 @@
21
26
  "engines": {
22
27
  "node": ">=24.0.0"
23
28
  },
29
+ "dependencies": {
30
+ "better-sqlite3": "^13.0.3"
31
+ },
24
32
  "scripts": {
25
33
  "build": "tsc --build",
26
34
  "test": "vitest run",
@@ -1,6 +1,16 @@
1
- import type { CriticalPathConfig, Project, Task, WebhookEvent } from '../types/index.js';
1
+ import type {
2
+ CriticalPathConfig,
3
+ Project,
4
+ Task,
5
+ TaskDependencyGraph,
6
+ Team,
7
+ TaskContainer,
8
+ Iteration,
9
+ WebhookEvent
10
+ } from '../types/index.js';
2
11
  import { StorageAdapter, InMemoryStore } from '../store/index.js';
3
12
  import { PluginRegistry } from '../plugins/index.js';
13
+ import { deriveTaskLifecycleState, type TaskLifecycleState } from '../utils/status.js';
4
14
 
5
15
  export class CriticalPathEngine {
6
16
  public readonly store: StorageAdapter;
@@ -30,6 +40,21 @@ export class CriticalPathEngine {
30
40
  await this.store.createProject(p);
31
41
  }
32
42
  }
43
+ if (data.teams) {
44
+ for (const tm of data.teams) {
45
+ await this.store.createTeam(tm);
46
+ }
47
+ }
48
+ if (data.containers) {
49
+ for (const c of data.containers) {
50
+ await this.store.createContainer(c);
51
+ }
52
+ }
53
+ if (data.iterations) {
54
+ for (const it of data.iterations) {
55
+ await this.store.createIteration(it);
56
+ }
57
+ }
33
58
  if (data.tasks) {
34
59
  for (const t of data.tasks) {
35
60
  await this.store.createTask(t);
@@ -37,8 +62,7 @@ export class CriticalPathEngine {
37
62
  }
38
63
  }
39
64
 
40
- // --- Domain Methods with Plugin Hooks & Audit Logging ---
41
-
65
+ // --- Projects ---
42
66
  async getProjects(): Promise<Project[]> {
43
67
  return this.store.getProjects();
44
68
  }
@@ -59,6 +83,7 @@ export class CriticalPathEngine {
59
83
  return created;
60
84
  }
61
85
 
86
+ // --- Tasks ---
62
87
  async getTasks(projectId?: string): Promise<Task[]> {
63
88
  return this.store.getTasks(projectId);
64
89
  }
@@ -68,7 +93,6 @@ export class CriticalPathEngine {
68
93
  }
69
94
 
70
95
  async createTask(taskInput: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Promise<Task> {
71
- // Run plugin beforeTaskCreate hooks
72
96
  const processedInput = await this.plugins.runBeforeTaskCreate(taskInput);
73
97
 
74
98
  const created = await this.store.createTask({
@@ -79,16 +103,27 @@ export class CriticalPathEngine {
79
103
  priority: processedInput.priority || taskInput.priority || 'medium',
80
104
  assigneeId: processedInput.assigneeId ?? taskInput.assigneeId,
81
105
  reporterId: processedInput.reporterId ?? taskInput.reporterId,
82
- sprintId: processedInput.sprintId ?? taskInput.sprintId,
106
+ reviewerId: processedInput.reviewerId ?? taskInput.reviewerId,
107
+ iterationId: processedInput.iterationId ?? taskInput.iterationId,
108
+ teamId: processedInput.teamId ?? taskInput.teamId,
109
+ containerId: processedInput.containerId ?? taskInput.containerId,
110
+ plannedStartDate: processedInput.plannedStartDate ?? taskInput.plannedStartDate,
111
+ actualStartDate: processedInput.actualStartDate ?? taskInput.actualStartDate,
112
+ actualEndDate: processedInput.actualEndDate ?? taskInput.actualEndDate,
83
113
  dueDate: processedInput.dueDate ?? taskInput.dueDate,
84
114
  estimatedHours: processedInput.estimatedHours ?? taskInput.estimatedHours,
85
115
  loggedHours: processedInput.loggedHours ?? taskInput.loggedHours ?? 0,
116
+ actualHours: processedInput.actualHours ?? taskInput.actualHours,
117
+ billableHours: processedInput.billableHours ?? taskInput.billableHours,
118
+ estimatedDurationMinutes: processedInput.estimatedDurationMinutes ?? taskInput.estimatedDurationMinutes,
119
+ actualDurationMinutes: processedInput.actualDurationMinutes ?? taskInput.actualDurationMinutes,
120
+ billableDurationMinutes: processedInput.billableDurationMinutes ?? taskInput.billableDurationMinutes,
121
+ progress: processedInput.progress ?? taskInput.progress ?? 0,
86
122
  tags: processedInput.tags ?? taskInput.tags ?? [],
87
123
  customFields: processedInput.customFields ?? taskInput.customFields ?? {},
88
124
  parentId: processedInput.parentId ?? taskInput.parentId
89
125
  });
90
126
 
91
- // Run plugin afterTaskCreate hooks
92
127
  await this.plugins.runAfterTaskCreate(created);
93
128
 
94
129
  await this.store.logActivity({
@@ -151,18 +186,136 @@ export class CriticalPathEngine {
151
186
  return deleted;
152
187
  }
153
188
 
189
+ // --- Task Dependencies & Graph ---
190
+ async getTaskDependencyGraph(taskId: string): Promise<TaskDependencyGraph> {
191
+ const dependencies = await this.store.getDependencies(taskId);
192
+ const upstreamTaskIds = new Set<string>();
193
+ const downstreamTaskIds = new Set<string>();
194
+
195
+ for (const dep of dependencies) {
196
+ if (dep.taskId === taskId) {
197
+ upstreamTaskIds.add(dep.dependsOnTaskId);
198
+ } else if (dep.dependsOnTaskId === taskId) {
199
+ downstreamTaskIds.add(dep.taskId);
200
+ }
201
+ }
202
+
203
+ const upstreamTasks = (
204
+ await Promise.all(Array.from(upstreamTaskIds).map((id) => this.store.getTask(id)))
205
+ ).filter((t): t is Task => t !== null);
206
+
207
+ const downstreamTasks = (
208
+ await Promise.all(Array.from(downstreamTaskIds).map((id) => this.store.getTask(id)))
209
+ ).filter((t): t is Task => t !== null);
210
+
211
+ return {
212
+ taskId,
213
+ upstreamTasks,
214
+ downstreamTasks,
215
+ dependencies
216
+ };
217
+ }
218
+
219
+ async getTaskLifecycleState(taskId: string): Promise<TaskLifecycleState | null> {
220
+ const task = await this.getTask(taskId);
221
+ if (!task) return null;
222
+ const project = await this.getProject(task.projectId);
223
+ return deriveTaskLifecycleState(task, project?.statusDefinitions);
224
+ }
225
+
226
+ // --- Teams ---
227
+ async getTeams(): Promise<Team[]> {
228
+ return this.store.getTeams();
229
+ }
230
+
231
+ async getTeam(id: string): Promise<Team | null> {
232
+ return this.store.getTeam(id);
233
+ }
234
+
235
+ async createTeam(team: Omit<Team, 'id' | 'createdAt' | 'updatedAt'>): Promise<Team> {
236
+ const created = await this.store.createTeam(team);
237
+ this.dispatchWebhook('team.created', { team: created });
238
+ return created;
239
+ }
240
+
241
+ async updateTeam(id: string, updates: Partial<Team>): Promise<Team | null> {
242
+ return this.store.updateTeam(id, updates);
243
+ }
244
+
245
+ async deleteTeam(id: string): Promise<boolean> {
246
+ return this.store.deleteTeam(id);
247
+ }
248
+
249
+ // --- Containers ---
250
+ async getContainers(projectId: string): Promise<TaskContainer[]> {
251
+ return this.store.getContainers(projectId);
252
+ }
253
+
254
+ async getContainer(id: string): Promise<TaskContainer | null> {
255
+ return this.store.getContainer(id);
256
+ }
257
+
258
+ async createContainer(container: Omit<TaskContainer, 'id' | 'createdAt' | 'updatedAt'>): Promise<TaskContainer> {
259
+ const created = await this.store.createContainer(container);
260
+ this.dispatchWebhook('container.created', { container: created });
261
+ return created;
262
+ }
263
+
264
+ async updateContainer(id: string, updates: Partial<TaskContainer>): Promise<TaskContainer | null> {
265
+ return this.store.updateContainer(id, updates);
266
+ }
267
+
268
+ async deleteContainer(id: string): Promise<boolean> {
269
+ return this.store.deleteContainer(id);
270
+ }
271
+
272
+ // --- Iterations ---
273
+ async getIterations(projectId: string): Promise<Iteration[]> {
274
+ return this.store.getIterations(projectId);
275
+ }
276
+
277
+ async getIteration(id: string): Promise<Iteration | null> {
278
+ return this.store.getIteration(id);
279
+ }
280
+
281
+ async createIteration(iteration: Omit<Iteration, 'id' | 'createdAt'>): Promise<Iteration> {
282
+ const created = await this.store.createIteration(iteration);
283
+ if (created.status === 'active') {
284
+ this.dispatchWebhook('iteration.started', { iteration: created });
285
+ }
286
+ return created;
287
+ }
288
+
289
+ async updateIteration(id: string, updates: Partial<Iteration>): Promise<Iteration | null> {
290
+ const existing = await this.store.getIteration(id);
291
+ if (!existing) return null;
292
+
293
+ const updated = await this.store.updateIteration(id, updates);
294
+ if (updated) {
295
+ if (existing.status !== 'active' && updated.status === 'active') {
296
+ this.dispatchWebhook('iteration.started', { iteration: updated });
297
+ } else if (existing.status !== 'completed' && updated.status === 'completed') {
298
+ this.dispatchWebhook('iteration.completed', { iteration: updated });
299
+ }
300
+ }
301
+ return updated;
302
+ }
303
+
304
+ async deleteIteration(id: string): Promise<boolean> {
305
+ return this.store.deleteIteration(id);
306
+ }
307
+
154
308
  private async dispatchWebhook(event: WebhookEvent, payload: Record<string, unknown>): Promise<void> {
155
309
  const webhooks = await this.store.getWebhooks();
156
310
  const active = webhooks.filter((w) => w.active && w.events.includes(event));
157
311
 
158
312
  for (const wh of active) {
159
- // Fire-and-forget webhook post
160
313
  fetch(wh.url, {
161
314
  method: 'POST',
162
315
  headers: { 'Content-Type': 'application/json' },
163
316
  body: JSON.stringify({ event, timestamp: new Date().toISOString(), data: payload })
164
317
  }).catch(() => {
165
- // Silent catch for webhook delivery errors in dev
318
+ // Silent catch for webhook errors in dev
166
319
  });
167
320
  }
168
321
  }
package/src/index.test.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { CriticalPathEngine } from './engine/index.js';
2
+ import { CriticalPathEngine, deriveTaskLifecycleState } from './index.js';
3
3
  import type { CriticalPathPlugin } from './types/index.js';
4
4
 
5
5
  describe('CriticalPathEngine Core Tests', () => {
@@ -60,4 +60,116 @@ describe('CriticalPathEngine Core Tests', () => {
60
60
  expect(hookExecuted).toBe(true);
61
61
  expect(task.tags).toContain('auto-tagged');
62
62
  });
63
+
64
+ it('handles teams, containers, and iterations', async () => {
65
+ const engine = new CriticalPathEngine();
66
+
67
+ const team = await engine.createTeam({
68
+ name: 'Engineering',
69
+ memberIds: ['user_1', 'user_2']
70
+ });
71
+
72
+ const proj = await engine.createProject({
73
+ key: 'ENG',
74
+ name: 'Engineering Project',
75
+ teamIds: [team.id]
76
+ });
77
+
78
+ const container = await engine.createContainer({
79
+ projectId: proj.id,
80
+ name: 'Core Platform Epic',
81
+ type: 'epic'
82
+ });
83
+
84
+ const iteration = await engine.createIteration({
85
+ projectId: proj.id,
86
+ name: 'Sprint 1',
87
+ type: 'sprint',
88
+ status: 'active'
89
+ });
90
+
91
+ const task = await engine.createTask({
92
+ projectId: proj.id,
93
+ title: 'Setup Database Schema',
94
+ status: 'in_progress',
95
+ priority: 'urgent',
96
+ reviewerId: 'user_2',
97
+ teamId: team.id,
98
+ containerId: container.id,
99
+ iterationId: iteration.id,
100
+ plannedStartDate: '2026-09-01T00:00:00.000Z',
101
+ dueDate: '2026-09-15T00:00:00.000Z',
102
+ estimatedHours: 10,
103
+ actualHours: 8,
104
+ billableHours: 8,
105
+ estimatedDurationMinutes: 600,
106
+ actualDurationMinutes: 480,
107
+ billableDurationMinutes: 480,
108
+ progress: 80
109
+ });
110
+
111
+ expect(task.reviewerId).toBe('user_2');
112
+ expect(task.teamId).toBe(team.id);
113
+ expect(task.containerId).toBe(container.id);
114
+ expect(task.iterationId).toBe(iteration.id);
115
+ expect(task.estimatedHours).toBe(10);
116
+ expect(task.actualHours).toBe(8);
117
+ expect(task.billableHours).toBe(8);
118
+ expect(task.estimatedDurationMinutes).toBe(600);
119
+ expect(task.actualDurationMinutes).toBe(480);
120
+ expect(task.billableDurationMinutes).toBe(480);
121
+ expect(task.progress).toBe(80);
122
+ });
123
+
124
+ it('evaluates status definitions and task lifecycle state', async () => {
125
+ const engine = new CriticalPathEngine();
126
+
127
+ const proj = await engine.createProject({
128
+ key: 'CUST',
129
+ name: 'Custom Workflow Project',
130
+ statusDefinitions: [
131
+ { key: 'draft', label: 'Draft', completionState: 'not_done', executionState: 'inactive' },
132
+ { key: 'active_work', label: 'Active Work', completionState: 'not_done', executionState: 'active' },
133
+ { key: 'finished', label: 'Finished', completionState: 'done', executionState: 'inactive' }
134
+ ]
135
+ });
136
+
137
+ const task = await engine.createTask({
138
+ projectId: proj.id,
139
+ title: 'Workflow Task',
140
+ status: 'active_work',
141
+ priority: 'medium',
142
+ plannedStartDate: '2026-08-01T00:00:00.000Z',
143
+ dueDate: '2026-08-10T00:00:00.000Z'
144
+ });
145
+
146
+ const state = await engine.getTaskLifecycleState(task.id);
147
+ expect(state).not.toBeNull();
148
+ expect(state?.completionState).toBe('not_done');
149
+ expect(state?.executionState).toBe('active');
150
+ expect(state?.isActive).toBe(true);
151
+ expect(state?.isDone).toBe(false);
152
+ });
153
+
154
+ it('builds task dependency graph with upstream and downstream tasks', async () => {
155
+ const engine = new CriticalPathEngine();
156
+
157
+ const proj = await engine.createProject({ key: 'DEP', name: 'Dependency Project' });
158
+
159
+ const taskA = await engine.createTask({ projectId: proj.id, title: 'Task A', status: 'todo', priority: 'medium' });
160
+ const taskB = await engine.createTask({ projectId: proj.id, title: 'Task B', status: 'todo', priority: 'medium' });
161
+
162
+ // Task B depends on Task A
163
+ await engine.store.addDependency({
164
+ taskId: taskB.id,
165
+ dependsOnTaskId: taskA.id,
166
+ type: 'blocking'
167
+ });
168
+
169
+ const graphA = await engine.getTaskDependencyGraph(taskA.id);
170
+ expect(graphA.downstreamTasks.map((t) => t.id)).toContain(taskB.id);
171
+
172
+ const graphB = await engine.getTaskDependencyGraph(taskB.id);
173
+ expect(graphB.upstreamTasks.map((t) => t.id)).toContain(taskA.id);
174
+ });
63
175
  });
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './types/index.js';
2
+ export * from './utils/status.js';
2
3
  export * from './store/index.js';
3
4
  export * from './plugins/index.js';
4
5
  export * from './engine/index.js';