@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.
- package/LICENSE +21 -0
- package/dist/engine/index.d.ts +19 -0
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/engine/index.js +144 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +52 -0
- package/dist/index.test.js.map +1 -0
- package/dist/plugins/index.d.ts +13 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +59 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/store/index.d.ts +66 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +154 -0
- package/dist/store/index.js.map +1 -0
- package/dist/types/index.d.ts +130 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +29 -0
- package/src/engine/index.ts +169 -0
- package/src/index.test.ts +63 -0
- package/src/index.ts +4 -0
- package/src/plugins/index.ts +68 -0
- package/src/store/index.ts +224 -0
- package/src/types/index.ts +154 -0
- package/tsconfig.json +8 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -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 @@
|
|
|
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"}
|