@critical-path/core 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.
@@ -0,0 +1,224 @@
1
+ import type {
2
+ Project,
3
+ Task,
4
+ User,
5
+ Sprint,
6
+ Comment,
7
+ TimeEntry,
8
+ Activity,
9
+ Webhook,
10
+ TaskDependency
11
+ } from '../types/index.js';
12
+
13
+ export interface StorageAdapter {
14
+ // Projects
15
+ getProjects(): Promise<Project[]>;
16
+ getProject(id: string): Promise<Project | null>;
17
+ createProject(project: Omit<Project, 'id' | 'createdAt' | 'updatedAt'>): Promise<Project>;
18
+ updateProject(id: string, updates: Partial<Project>): Promise<Project | null>;
19
+ deleteProject(id: string): Promise<boolean>;
20
+
21
+ // Tasks
22
+ getTasks(projectId?: string): Promise<Task[]>;
23
+ getTask(id: string): Promise<Task | null>;
24
+ createTask(task: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Promise<Task>;
25
+ updateTask(id: string, updates: Partial<Task>): Promise<Task | null>;
26
+ deleteTask(id: string): Promise<boolean>;
27
+
28
+ // Sprints
29
+ getSprints(projectId: string): Promise<Sprint[]>;
30
+ createSprint(sprint: Omit<Sprint, 'id' | 'createdAt'>): Promise<Sprint>;
31
+ updateSprint(id: string, updates: Partial<Sprint>): Promise<Sprint | null>;
32
+
33
+ // Comments & Activity
34
+ getComments(taskId: string): Promise<Comment[]>;
35
+ addComment(comment: Omit<Comment, 'id' | 'createdAt' | 'updatedAt'>): Promise<Comment>;
36
+ getActivities(filter?: { projectId?: string; taskId?: string }): Promise<Activity[]>;
37
+ logActivity(activity: Omit<Activity, 'id' | 'createdAt'>): Promise<Activity>;
38
+
39
+ // Time Tracking
40
+ getTimeEntries(taskId: string): Promise<TimeEntry[]>;
41
+ logTime(entry: Omit<TimeEntry, 'id' | 'loggedAt'>): Promise<TimeEntry>;
42
+
43
+ // Dependencies
44
+ getDependencies(taskId: string): Promise<TaskDependency[]>;
45
+ addDependency(dep: Omit<TaskDependency, 'id'>): Promise<TaskDependency>;
46
+
47
+ // Webhooks
48
+ getWebhooks(): Promise<Webhook[]>;
49
+ addWebhook(webhook: Omit<Webhook, 'id' | 'createdAt'>): Promise<Webhook>;
50
+ }
51
+
52
+ export class InMemoryStore implements StorageAdapter {
53
+ private projects = new Map<string, Project>();
54
+ private tasks = new Map<string, Task>();
55
+ private sprints = new Map<string, Sprint>();
56
+ private comments = new Map<string, Comment>();
57
+ private activities: Activity[] = [];
58
+ private timeEntries = new Map<string, TimeEntry>();
59
+ private dependencies = new Map<string, TaskDependency>();
60
+ private webhooks = new Map<string, Webhook>();
61
+
62
+ async getProjects(): Promise<Project[]> {
63
+ return Array.from(this.projects.values());
64
+ }
65
+
66
+ async getProject(id: string): Promise<Project | null> {
67
+ return this.projects.get(id) || null;
68
+ }
69
+
70
+ async createProject(project: Omit<Project, 'id' | 'createdAt' | 'updatedAt'>): Promise<Project> {
71
+ const id = `proj_${Math.random().toString(36).substring(2, 9)}`;
72
+ const now = new Date().toISOString();
73
+ const newProject: Project = {
74
+ ...project,
75
+ id,
76
+ createdAt: now,
77
+ updatedAt: now
78
+ };
79
+ this.projects.set(id, newProject);
80
+ return newProject;
81
+ }
82
+
83
+ async updateProject(id: string, updates: Partial<Project>): Promise<Project | null> {
84
+ const existing = this.projects.get(id);
85
+ if (!existing) return null;
86
+ const updated: Project = {
87
+ ...existing,
88
+ ...updates,
89
+ updatedAt: new Date().toISOString()
90
+ };
91
+ this.projects.set(id, updated);
92
+ return updated;
93
+ }
94
+
95
+ async deleteProject(id: string): Promise<boolean> {
96
+ return this.projects.delete(id);
97
+ }
98
+
99
+ async getTasks(projectId?: string): Promise<Task[]> {
100
+ const all = Array.from(this.tasks.values());
101
+ if (projectId) {
102
+ return all.filter((t) => t.projectId === projectId);
103
+ }
104
+ return all;
105
+ }
106
+
107
+ async getTask(id: string): Promise<Task | null> {
108
+ return this.tasks.get(id) || null;
109
+ }
110
+
111
+ async createTask(task: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Promise<Task> {
112
+ const id = `task_${Math.random().toString(36).substring(2, 9)}`;
113
+ const now = new Date().toISOString();
114
+ const newTask: Task = {
115
+ ...task,
116
+ id,
117
+ createdAt: now,
118
+ updatedAt: now
119
+ };
120
+ this.tasks.set(id, newTask);
121
+ return newTask;
122
+ }
123
+
124
+ async updateTask(id: string, updates: Partial<Task>): Promise<Task | null> {
125
+ const existing = this.tasks.get(id);
126
+ if (!existing) return null;
127
+ const updated: Task = {
128
+ ...existing,
129
+ ...updates,
130
+ updatedAt: new Date().toISOString()
131
+ };
132
+ this.tasks.set(id, updated);
133
+ return updated;
134
+ }
135
+
136
+ async deleteTask(id: string): Promise<boolean> {
137
+ return this.tasks.delete(id);
138
+ }
139
+
140
+ async getSprints(projectId: string): Promise<Sprint[]> {
141
+ return Array.from(this.sprints.values()).filter((s) => s.projectId === projectId);
142
+ }
143
+
144
+ async createSprint(sprint: Omit<Sprint, 'id' | 'createdAt'>): Promise<Sprint> {
145
+ const id = `sprint_${Math.random().toString(36).substring(2, 9)}`;
146
+ const newSprint: Sprint = {
147
+ ...sprint,
148
+ id,
149
+ createdAt: new Date().toISOString()
150
+ };
151
+ this.sprints.set(id, newSprint);
152
+ return newSprint;
153
+ }
154
+
155
+ async updateSprint(id: string, updates: Partial<Sprint>): Promise<Sprint | null> {
156
+ const existing = this.sprints.get(id);
157
+ if (!existing) return null;
158
+ const updated: Sprint = { ...existing, ...updates };
159
+ this.sprints.set(id, updated);
160
+ return updated;
161
+ }
162
+
163
+ async getComments(taskId: string): Promise<Comment[]> {
164
+ return Array.from(this.comments.values()).filter((c) => c.taskId === taskId);
165
+ }
166
+
167
+ async addComment(comment: Omit<Comment, 'id' | 'createdAt' | 'updatedAt'>): Promise<Comment> {
168
+ const id = `cmt_${Math.random().toString(36).substring(2, 9)}`;
169
+ const now = new Date().toISOString();
170
+ const newComment: Comment = { ...comment, id, createdAt: now, updatedAt: now };
171
+ this.comments.set(id, newComment);
172
+ return newComment;
173
+ }
174
+
175
+ async getActivities(filter?: { projectId?: string; taskId?: string }): Promise<Activity[]> {
176
+ return this.activities.filter((a) => {
177
+ if (filter?.projectId && a.projectId !== filter.projectId) return false;
178
+ if (filter?.taskId && a.taskId !== filter.taskId) return false;
179
+ return true;
180
+ });
181
+ }
182
+
183
+ async logActivity(activity: Omit<Activity, 'id' | 'createdAt'>): Promise<Activity> {
184
+ const id = `act_${Math.random().toString(36).substring(2, 9)}`;
185
+ const newAct: Activity = { ...activity, id, createdAt: new Date().toISOString() };
186
+ this.activities.unshift(newAct);
187
+ return newAct;
188
+ }
189
+
190
+ async getTimeEntries(taskId: string): Promise<TimeEntry[]> {
191
+ return Array.from(this.timeEntries.values()).filter((e) => e.taskId === taskId);
192
+ }
193
+
194
+ async logTime(entry: Omit<TimeEntry, 'id' | 'loggedAt'>): Promise<TimeEntry> {
195
+ const id = `time_${Math.random().toString(36).substring(2, 9)}`;
196
+ const newEntry: TimeEntry = { ...entry, id, loggedAt: new Date().toISOString() };
197
+ this.timeEntries.set(id, newEntry);
198
+ return newEntry;
199
+ }
200
+
201
+ async getDependencies(taskId: string): Promise<TaskDependency[]> {
202
+ return Array.from(this.dependencies.values()).filter(
203
+ (d) => d.taskId === taskId || d.dependsOnTaskId === taskId
204
+ );
205
+ }
206
+
207
+ async addDependency(dep: Omit<TaskDependency, 'id'>): Promise<TaskDependency> {
208
+ const id = `dep_${Math.random().toString(36).substring(2, 9)}`;
209
+ const newDep: TaskDependency = { ...dep, id };
210
+ this.dependencies.set(id, newDep);
211
+ return newDep;
212
+ }
213
+
214
+ async getWebhooks(): Promise<Webhook[]> {
215
+ return Array.from(this.webhooks.values());
216
+ }
217
+
218
+ async addWebhook(webhook: Omit<Webhook, 'id' | 'createdAt'>): Promise<Webhook> {
219
+ const id = `wh_${Math.random().toString(36).substring(2, 9)}`;
220
+ const newWh: Webhook = { ...webhook, id, createdAt: new Date().toISOString() };
221
+ this.webhooks.set(id, newWh);
222
+ return newWh;
223
+ }
224
+ }
@@ -0,0 +1,154 @@
1
+ export type Priority = 'urgent' | 'high' | 'medium' | 'low' | 'none';
2
+
3
+ export type TaskStatus = 'backlog' | 'todo' | 'in_progress' | 'in_review' | 'done' | 'canceled';
4
+
5
+ export type Role = 'admin' | 'project_manager' | 'contributor' | 'viewer';
6
+
7
+ export interface User {
8
+ id: string;
9
+ name: string;
10
+ email: string;
11
+ avatarUrl?: string;
12
+ role: Role;
13
+ createdAt: string;
14
+ }
15
+
16
+ export interface CustomFieldDefinition {
17
+ id: string;
18
+ key: string;
19
+ label: string;
20
+ type: 'text' | 'number' | 'date' | 'boolean' | 'single_select' | 'multi_select' | 'user';
21
+ options?: string[];
22
+ required?: boolean;
23
+ defaultValue?: unknown;
24
+ }
25
+
26
+ export interface Project {
27
+ id: string;
28
+ key: string; // e.g. "CP" or "PROJ"
29
+ name: string;
30
+ description?: string;
31
+ ownerId?: string;
32
+ members?: string[]; // user IDs
33
+ customFieldDefinitions?: CustomFieldDefinition[];
34
+ createdAt: string;
35
+ updatedAt: string;
36
+ }
37
+
38
+ export interface TaskDependency {
39
+ id: string;
40
+ taskId: string;
41
+ dependsOnTaskId: string;
42
+ type: 'blocking' | 'blocked_by' | 'relates_to';
43
+ }
44
+
45
+ export interface Task {
46
+ id: string;
47
+ projectId: string;
48
+ title: string;
49
+ description?: string;
50
+ status: TaskStatus;
51
+ priority: Priority;
52
+ assigneeId?: string;
53
+ reporterId?: string;
54
+ sprintId?: string;
55
+ dueDate?: string;
56
+ estimatedHours?: number;
57
+ loggedHours?: number;
58
+ tags?: string[];
59
+ customFields?: Record<string, unknown>;
60
+ parentId?: string; // Subtask support
61
+ createdAt: string;
62
+ updatedAt: string;
63
+ }
64
+
65
+ export interface Sprint {
66
+ id: string;
67
+ projectId: string;
68
+ name: string;
69
+ goal?: string;
70
+ startDate: string;
71
+ endDate: string;
72
+ status: 'planning' | 'active' | 'completed';
73
+ createdAt: string;
74
+ }
75
+
76
+ export interface Comment {
77
+ id: string;
78
+ taskId: string;
79
+ authorId: string;
80
+ content: string;
81
+ createdAt: string;
82
+ updatedAt: string;
83
+ }
84
+
85
+ export interface TimeEntry {
86
+ id: string;
87
+ taskId: string;
88
+ userId: string;
89
+ hours: number;
90
+ description?: string;
91
+ loggedAt: string;
92
+ }
93
+
94
+ export interface Activity {
95
+ id: string;
96
+ projectId?: string;
97
+ taskId?: string;
98
+ actorId: string;
99
+ action: string;
100
+ details: Record<string, unknown>;
101
+ createdAt: string;
102
+ }
103
+
104
+ export interface Webhook {
105
+ id: string;
106
+ name: string;
107
+ url: string;
108
+ secret?: string;
109
+ events: WebhookEvent[];
110
+ active: boolean;
111
+ createdAt: string;
112
+ }
113
+
114
+ export type WebhookEvent =
115
+ | 'project.created'
116
+ | 'project.updated'
117
+ | 'task.created'
118
+ | 'task.updated'
119
+ | 'task.deleted'
120
+ | 'task.status_changed'
121
+ | 'comment.created'
122
+ | 'sprint.started'
123
+ | 'sprint.completed';
124
+
125
+ export interface PluginHooks {
126
+ beforeTaskCreate?: (task: Partial<Task>) => Promise<Partial<Task>> | Partial<Task>;
127
+ afterTaskCreate?: (task: Task) => Promise<void> | void;
128
+ beforeTaskUpdate?: (id: string, updates: Partial<Task>) => Promise<Partial<Task>> | Partial<Task>;
129
+ afterTaskUpdate?: (task: Task, previousState: Task) => Promise<void> | void;
130
+ beforeTaskDelete?: (id: string) => Promise<void> | void;
131
+ afterTaskDelete?: (id: string) => Promise<void> | void;
132
+ }
133
+
134
+ export interface CriticalPathPlugin {
135
+ id: string;
136
+ name: string;
137
+ version: string;
138
+ description?: string;
139
+ hooks?: PluginHooks;
140
+ customFieldTypes?: CustomFieldDefinition[];
141
+ init?: (engine: unknown) => Promise<void> | void;
142
+ }
143
+
144
+ export interface CriticalPathConfig {
145
+ store?: 'memory' | 'sqlite' | unknown;
146
+ plugins?: CriticalPathPlugin[];
147
+ webhooks?: Omit<Webhook, 'id' | 'createdAt'>[];
148
+ initialData?: {
149
+ projects?: Project[];
150
+ tasks?: Task[];
151
+ users?: User[];
152
+ sprints?: Sprint[];
153
+ };
154
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src"
6
+ },
7
+ "include": ["src/**/*"]
8
+ }
@@ -0,0 +1 @@
1
+ {"root":["./src/index.test.ts","./src/index.ts","./src/engine/index.ts","./src/plugins/index.ts","./src/store/index.ts","./src/types/index.ts"],"version":"5.9.3"}