@critical-path/core 0.35.0 → 0.36.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 (187) hide show
  1. package/README.md +11 -0
  2. package/dist/engine/index.d.ts +2 -0
  3. package/dist/engine/index.d.ts.map +1 -1
  4. package/dist/engine/index.js +25 -2
  5. package/dist/engine/index.js.map +1 -1
  6. package/dist/types/index.d.ts +11 -0
  7. package/dist/types/index.d.ts.map +1 -1
  8. package/package.json +5 -1
  9. package/CHANGELOG.md +0 -584
  10. package/dist/deliverables.test.d.ts +0 -2
  11. package/dist/deliverables.test.d.ts.map +0 -1
  12. package/dist/deliverables.test.js +0 -232
  13. package/dist/deliverables.test.js.map +0 -1
  14. package/dist/domain/calendar.test.d.ts +0 -2
  15. package/dist/domain/calendar.test.d.ts.map +0 -1
  16. package/dist/domain/calendar.test.js +0 -181
  17. package/dist/domain/calendar.test.js.map +0 -1
  18. package/dist/domain/cpm.test.d.ts +0 -2
  19. package/dist/domain/cpm.test.d.ts.map +0 -1
  20. package/dist/domain/cpm.test.js +0 -166
  21. package/dist/domain/cpm.test.js.map +0 -1
  22. package/dist/domain/ddd.test.d.ts +0 -2
  23. package/dist/domain/ddd.test.d.ts.map +0 -1
  24. package/dist/domain/ddd.test.js +0 -349
  25. package/dist/domain/ddd.test.js.map +0 -1
  26. package/dist/domain/ladder.test.d.ts +0 -2
  27. package/dist/domain/ladder.test.d.ts.map +0 -1
  28. package/dist/domain/ladder.test.js +0 -213
  29. package/dist/domain/ladder.test.js.map +0 -1
  30. package/dist/domain/metrics.test.d.ts +0 -2
  31. package/dist/domain/metrics.test.d.ts.map +0 -1
  32. package/dist/domain/metrics.test.js +0 -252
  33. package/dist/domain/metrics.test.js.map +0 -1
  34. package/dist/domain/task-duration.test.d.ts +0 -2
  35. package/dist/domain/task-duration.test.d.ts.map +0 -1
  36. package/dist/domain/task-duration.test.js +0 -307
  37. package/dist/domain/task-duration.test.js.map +0 -1
  38. package/dist/domain/workload.test.d.ts +0 -2
  39. package/dist/domain/workload.test.d.ts.map +0 -1
  40. package/dist/domain/workload.test.js +0 -261
  41. package/dist/domain/workload.test.js.map +0 -1
  42. package/dist/engine/actor.test.d.ts +0 -2
  43. package/dist/engine/actor.test.d.ts.map +0 -1
  44. package/dist/engine/actor.test.js +0 -54
  45. package/dist/engine/actor.test.js.map +0 -1
  46. package/dist/engine/authorization.test.d.ts +0 -2
  47. package/dist/engine/authorization.test.d.ts.map +0 -1
  48. package/dist/engine/authorization.test.js +0 -154
  49. package/dist/engine/authorization.test.js.map +0 -1
  50. package/dist/engine/cascade.test.d.ts +0 -2
  51. package/dist/engine/cascade.test.d.ts.map +0 -1
  52. package/dist/engine/cascade.test.js +0 -99
  53. package/dist/engine/cascade.test.js.map +0 -1
  54. package/dist/engine/config.test.d.ts +0 -2
  55. package/dist/engine/config.test.d.ts.map +0 -1
  56. package/dist/engine/config.test.js +0 -46
  57. package/dist/engine/config.test.js.map +0 -1
  58. package/dist/engine/entity-events.test.d.ts +0 -2
  59. package/dist/engine/entity-events.test.d.ts.map +0 -1
  60. package/dist/engine/entity-events.test.js +0 -61
  61. package/dist/engine/entity-events.test.js.map +0 -1
  62. package/dist/index.test.d.ts +0 -2
  63. package/dist/index.test.d.ts.map +0 -1
  64. package/dist/index.test.js +0 -429
  65. package/dist/index.test.js.map +0 -1
  66. package/dist/plugins/plugins.test.d.ts +0 -2
  67. package/dist/plugins/plugins.test.d.ts.map +0 -1
  68. package/dist/plugins/plugins.test.js +0 -137
  69. package/dist/plugins/plugins.test.js.map +0 -1
  70. package/dist/schemas/schemas.test.d.ts +0 -2
  71. package/dist/schemas/schemas.test.d.ts.map +0 -1
  72. package/dist/schemas/schemas.test.js +0 -58
  73. package/dist/schemas/schemas.test.js.map +0 -1
  74. package/dist/status.test.d.ts +0 -2
  75. package/dist/status.test.d.ts.map +0 -1
  76. package/dist/status.test.js +0 -295
  77. package/dist/status.test.js.map +0 -1
  78. package/dist/storage/storage.test.d.ts +0 -2
  79. package/dist/storage/storage.test.d.ts.map +0 -1
  80. package/dist/storage/storage.test.js +0 -236
  81. package/dist/storage/storage.test.js.map +0 -1
  82. package/dist/store/conformance.test.d.ts +0 -2
  83. package/dist/store/conformance.test.d.ts.map +0 -1
  84. package/dist/store/conformance.test.js +0 -15
  85. package/dist/store/conformance.test.js.map +0 -1
  86. package/dist/store/firebase.test.d.ts +0 -2
  87. package/dist/store/firebase.test.d.ts.map +0 -1
  88. package/dist/store/firebase.test.js +0 -103
  89. package/dist/store/firebase.test.js.map +0 -1
  90. package/dist/store/query.test.d.ts +0 -2
  91. package/dist/store/query.test.d.ts.map +0 -1
  92. package/dist/store/query.test.js +0 -89
  93. package/dist/store/query.test.js.map +0 -1
  94. package/dist/store/sqlite.test.d.ts +0 -2
  95. package/dist/store/sqlite.test.d.ts.map +0 -1
  96. package/dist/store/sqlite.test.js +0 -177
  97. package/dist/store/sqlite.test.js.map +0 -1
  98. package/dist/task-state.test.d.ts +0 -2
  99. package/dist/task-state.test.d.ts.map +0 -1
  100. package/dist/task-state.test.js +0 -423
  101. package/dist/task-state.test.js.map +0 -1
  102. package/dist/use-case-validation.test.d.ts +0 -2
  103. package/dist/use-case-validation.test.d.ts.map +0 -1
  104. package/dist/use-case-validation.test.js +0 -281
  105. package/dist/use-case-validation.test.js.map +0 -1
  106. package/dist/utils/fractional-index.test.d.ts +0 -2
  107. package/dist/utils/fractional-index.test.d.ts.map +0 -1
  108. package/dist/utils/fractional-index.test.js +0 -182
  109. package/dist/utils/fractional-index.test.js.map +0 -1
  110. package/dist/utils/key.test.d.ts +0 -2
  111. package/dist/utils/key.test.d.ts.map +0 -1
  112. package/dist/utils/key.test.js +0 -65
  113. package/dist/utils/key.test.js.map +0 -1
  114. package/dist/utils/mentions.test.d.ts +0 -2
  115. package/dist/utils/mentions.test.d.ts.map +0 -1
  116. package/dist/utils/mentions.test.js +0 -114
  117. package/dist/utils/mentions.test.js.map +0 -1
  118. package/dist/webhooks/webhooks.test.d.ts +0 -2
  119. package/dist/webhooks/webhooks.test.d.ts.map +0 -1
  120. package/dist/webhooks/webhooks.test.js +0 -154
  121. package/dist/webhooks/webhooks.test.js.map +0 -1
  122. package/src/deliverables.test.ts +0 -284
  123. package/src/domain/authorization.ts +0 -82
  124. package/src/domain/calendar.test.ts +0 -216
  125. package/src/domain/calendar.ts +0 -502
  126. package/src/domain/cpm.test.ts +0 -189
  127. package/src/domain/cpm.ts +0 -258
  128. package/src/domain/custom-fields.ts +0 -172
  129. package/src/domain/ddd.test.ts +0 -442
  130. package/src/domain/entities.ts +0 -798
  131. package/src/domain/errors.ts +0 -38
  132. package/src/domain/events.ts +0 -369
  133. package/src/domain/graph.ts +0 -70
  134. package/src/domain/index.ts +0 -12
  135. package/src/domain/ladder.test.ts +0 -251
  136. package/src/domain/ladder.ts +0 -474
  137. package/src/domain/metrics.test.ts +0 -288
  138. package/src/domain/metrics.ts +0 -497
  139. package/src/domain/task-duration.test.ts +0 -354
  140. package/src/domain/workload.test.ts +0 -311
  141. package/src/domain/workload.ts +0 -457
  142. package/src/engine/actor.test.ts +0 -63
  143. package/src/engine/authorization.test.ts +0 -199
  144. package/src/engine/cascade.test.ts +0 -121
  145. package/src/engine/config.test.ts +0 -51
  146. package/src/engine/entity-events.test.ts +0 -67
  147. package/src/engine/index.ts +0 -2472
  148. package/src/index.test.ts +0 -514
  149. package/src/index.ts +0 -9
  150. package/src/plugins/index.ts +0 -103
  151. package/src/plugins/plugins.test.ts +0 -161
  152. package/src/schemas/index.ts +0 -402
  153. package/src/schemas/schemas.test.ts +0 -116
  154. package/src/status.test.ts +0 -354
  155. package/src/storage/file-storage.ts +0 -200
  156. package/src/storage/firebase-storage.ts +0 -209
  157. package/src/storage/index.ts +0 -3
  158. package/src/storage/s3.ts +0 -151
  159. package/src/storage/storage.test.ts +0 -274
  160. package/src/store/conformance.test.ts +0 -15
  161. package/src/store/firebase.test.ts +0 -131
  162. package/src/store/firebase.ts +0 -658
  163. package/src/store/index.ts +0 -623
  164. package/src/store/query.test.ts +0 -110
  165. package/src/store/query.ts +0 -113
  166. package/src/store/sqlite.test.ts +0 -218
  167. package/src/store/sqlite.ts +0 -1480
  168. package/src/task-state.test.ts +0 -506
  169. package/src/testing/conformance.ts +0 -319
  170. package/src/testing/index.ts +0 -1
  171. package/src/types/index.ts +0 -859
  172. package/src/use-case-validation.test.ts +0 -324
  173. package/src/utils/fractional-index.test.ts +0 -228
  174. package/src/utils/fractional-index.ts +0 -254
  175. package/src/utils/index.ts +0 -7
  176. package/src/utils/key.test.ts +0 -76
  177. package/src/utils/key.ts +0 -62
  178. package/src/utils/mentions.test.ts +0 -132
  179. package/src/utils/mentions.ts +0 -131
  180. package/src/utils/status.ts +0 -419
  181. package/src/utils/workflow.ts +0 -306
  182. package/src/webhooks/dispatcher.ts +0 -190
  183. package/src/webhooks/index.ts +0 -2
  184. package/src/webhooks/signature.ts +0 -57
  185. package/src/webhooks/webhooks.test.ts +0 -201
  186. package/tsconfig.json +0 -8
  187. package/tsconfig.tsbuildinfo +0 -1
@@ -1,623 +0,0 @@
1
- export * from './sqlite.js';
2
- export * from './firebase.js';
3
-
4
- import type {
5
- Project,
6
- Task,
7
- Iteration,
8
- Team,
9
- TaskContainer,
10
- Comment,
11
- Attachment,
12
- TimeEntry,
13
- Activity,
14
- Webhook,
15
- TaskDependency,
16
- Workflow,
17
- Deliverable,
18
- CreateDeliverableInput
19
- } from '../types/index.js';
20
- import { generateProjectKey } from '../utils/key.js';
21
- import {
22
- matchesActivityQuery,
23
- matchesTaskQuery,
24
- paginate,
25
- type ActivityQuery,
26
- type Page,
27
- type TaskQuery
28
- } from './query.js';
29
-
30
- export * from './query.js';
31
-
32
- export interface ProjectRepository {
33
- getProjects(): Promise<Project[]>;
34
- getProject(id: string): Promise<Project | null>;
35
- createProject(project: Omit<Project, 'id' | 'createdAt' | 'updatedAt'>): Promise<Project>;
36
- updateProject(id: string, updates: Partial<Project>): Promise<Project | null>;
37
- deleteProject(id: string): Promise<boolean>;
38
- }
39
-
40
- export interface WorkflowRepository {
41
- getWorkflows(): Promise<Workflow[]>;
42
- getWorkflow(id: string): Promise<Workflow | null>;
43
- createWorkflow(workflow: Omit<Workflow, 'id' | 'createdAt' | 'updatedAt'>): Promise<Workflow>;
44
- updateWorkflow(id: string, updates: Partial<Workflow>): Promise<Workflow | null>;
45
- deleteWorkflow(id: string): Promise<boolean>;
46
- }
47
-
48
- export interface TaskRepository {
49
- getTasks(projectId?: string): Promise<Task[]>;
50
- /** Filtered, paginated tasks ordered by `(createdAt, id)` ascending. */
51
- queryTasks(query: TaskQuery): Promise<Page<Task>>;
52
- getTask(id: string): Promise<Task | null>;
53
- createTask(task: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Promise<Task>;
54
- updateTask(id: string, updates: Partial<Task>): Promise<Task | null>;
55
- deleteTask(id: string): Promise<boolean>;
56
- }
57
-
58
- export interface TeamRepository {
59
- getTeams(): Promise<Team[]>;
60
- getTeam(id: string): Promise<Team | null>;
61
- createTeam(team: Omit<Team, 'id' | 'createdAt' | 'updatedAt'>): Promise<Team>;
62
- updateTeam(id: string, updates: Partial<Team>): Promise<Team | null>;
63
- deleteTeam(id: string): Promise<boolean>;
64
- }
65
-
66
- export interface ContainerRepository {
67
- getContainers(projectId: string): Promise<TaskContainer[]>;
68
- getContainer(id: string): Promise<TaskContainer | null>;
69
- createContainer(container: Omit<TaskContainer, 'id' | 'createdAt' | 'updatedAt'>): Promise<TaskContainer>;
70
- updateContainer(id: string, updates: Partial<TaskContainer>): Promise<TaskContainer | null>;
71
- deleteContainer(id: string): Promise<boolean>;
72
- }
73
-
74
- export interface IterationRepository {
75
- getIterations(projectId: string): Promise<Iteration[]>;
76
- getIteration(id: string): Promise<Iteration | null>;
77
- createIteration(iteration: Omit<Iteration, 'id' | 'createdAt'>): Promise<Iteration>;
78
- updateIteration(id: string, updates: Partial<Iteration>): Promise<Iteration | null>;
79
- deleteIteration(id: string): Promise<boolean>;
80
- }
81
-
82
- export interface CommentRepository {
83
- getComments(taskId: string): Promise<Comment[]>;
84
- getComment(id: string): Promise<Comment | null>;
85
- addComment(comment: Omit<Comment, 'id' | 'createdAt' | 'updatedAt'>): Promise<Comment>;
86
- updateComment(id: string, updates: Partial<Comment>): Promise<Comment | null>;
87
- deleteComment(id: string): Promise<boolean>;
88
- addReaction?(commentId: string, reaction: { emoji: string; userId: string }): Promise<Comment | null>;
89
- removeReaction?(commentId: string, reaction: { emoji: string; userId: string }): Promise<Comment | null>;
90
- }
91
-
92
- export interface AttachmentRepository {
93
- getAttachments(filter?: { taskId?: string; projectId?: string; commentId?: string }): Promise<Attachment[]>;
94
- getAttachment(id: string): Promise<Attachment | null>;
95
- createAttachment(attachment: Omit<Attachment, 'id' | 'createdAt' | 'updatedAt'>): Promise<Attachment>;
96
- deleteAttachment(id: string): Promise<boolean>;
97
- }
98
-
99
- export interface ActivityRepository {
100
- getActivities(filter?: { projectId?: string; taskId?: string }): Promise<Activity[]>;
101
- /** Paginated activity feed ordered by `(createdAt, id)` descending (newest first). */
102
- queryActivities(query: ActivityQuery): Promise<Page<Activity>>;
103
- logActivity(activity: Omit<Activity, 'id' | 'createdAt'>): Promise<Activity>;
104
- }
105
-
106
- export interface TimeEntryRepository {
107
- getTimeEntries(taskId: string): Promise<TimeEntry[]>;
108
- logTime(entry: Omit<TimeEntry, 'id' | 'loggedAt'> & { loggedAt?: string }): Promise<TimeEntry>;
109
- deleteTimeEntry(id: string): Promise<boolean>;
110
- }
111
-
112
- export interface DependencyRepository {
113
- /** Dependencies where `taskId` is either the dependent or the upstream task. */
114
- getDependencies(taskId: string): Promise<TaskDependency[]>;
115
- getDependency(id: string): Promise<TaskDependency | null>;
116
- addDependency(dep: Omit<TaskDependency, 'id'>): Promise<TaskDependency>;
117
- removeDependency(id: string): Promise<boolean>;
118
- }
119
-
120
- export interface WebhookRepository {
121
- getWebhooks(): Promise<Webhook[]>;
122
- getWebhook(id: string): Promise<Webhook | null>;
123
- addWebhook(webhook: Omit<Webhook, 'id' | 'createdAt'>): Promise<Webhook>;
124
- updateWebhook(id: string, updates: Partial<Omit<Webhook, 'id' | 'createdAt'>>): Promise<Webhook | null>;
125
- deleteWebhook(id: string): Promise<boolean>;
126
- }
127
-
128
- export interface DeliverableRepository {
129
- getDeliverables(projectId: string): Promise<Deliverable[]>;
130
- getDeliverable(id: string): Promise<Deliverable | null>;
131
- createDeliverable(deliverable: CreateDeliverableInput): Promise<Deliverable>;
132
- updateDeliverable(id: string, updates: Partial<Deliverable>): Promise<Deliverable | null>;
133
- deleteDeliverable(id: string): Promise<boolean>;
134
- }
135
-
136
- export interface StorageAdapter
137
- extends ProjectRepository,
138
- WorkflowRepository,
139
- TaskRepository,
140
- TeamRepository,
141
- ContainerRepository,
142
- DeliverableRepository,
143
- IterationRepository,
144
- CommentRepository,
145
- AttachmentRepository,
146
- ActivityRepository,
147
- TimeEntryRepository,
148
- DependencyRepository,
149
- WebhookRepository {}
150
-
151
- /**
152
- * Map-backed store for development and tests. Reads and writes are deep-copied, so it behaves
153
- * like a database: returned records can be mutated freely without affecting stored state.
154
- */
155
- export class InMemoryStore implements StorageAdapter {
156
- private projects = new Map<string, Project>();
157
- private workflows = new Map<string, Workflow>();
158
- private tasks = new Map<string, Task>();
159
- private teams = new Map<string, Team>();
160
- private containers = new Map<string, TaskContainer>();
161
- private deliverables = new Map<string, Deliverable>();
162
- private iterations = new Map<string, Iteration>();
163
- private comments = new Map<string, Comment>();
164
- private attachments = new Map<string, Attachment>();
165
- private activities: Activity[] = [];
166
- private timeEntries = new Map<string, TimeEntry>();
167
- private dependencies = new Map<string, TaskDependency>();
168
- private webhooks = new Map<string, Webhook>();
169
-
170
- constructor() {
171
- // Hand out copies: callers mutating a returned record (or an input they keep using) must not
172
- // change what is stored, matching the database-backed adapters. Internal calls between
173
- // methods use the unwrapped instance, so each call clones only at the boundary.
174
- return new Proxy(this, {
175
- get(target, property, receiver) {
176
- const value = Reflect.get(target, property, receiver);
177
- if (typeof value !== 'function' || property === 'constructor') return value;
178
- return async (...args: unknown[]) => structuredClone(await value.apply(target, structuredClone(args)));
179
- }
180
- });
181
- }
182
-
183
- async getProjects(): Promise<Project[]> {
184
- return Array.from(this.projects.values());
185
- }
186
-
187
- async getProject(id: string): Promise<Project | null> {
188
- return this.projects.get(id) || null;
189
- }
190
-
191
- async createProject(project: Omit<Project, 'id' | 'createdAt' | 'updatedAt'>): Promise<Project> {
192
- const id = `proj_${crypto.randomUUID()}`;
193
- const now = new Date().toISOString();
194
- const key = project.key || generateProjectKey(project.name);
195
- const newProject: Project = {
196
- ...project,
197
- key,
198
- id,
199
- createdAt: now,
200
- updatedAt: now
201
- };
202
- this.projects.set(id, newProject);
203
- return newProject;
204
- }
205
-
206
- async updateProject(id: string, updates: Partial<Project>): Promise<Project | null> {
207
- const existing = this.projects.get(id);
208
- if (!existing) return null;
209
- const updated: Project = {
210
- ...existing,
211
- ...updates,
212
- updatedAt: new Date().toISOString()
213
- };
214
- this.projects.set(id, updated);
215
- return updated;
216
- }
217
-
218
- async deleteProject(id: string): Promise<boolean> {
219
- return this.projects.delete(id);
220
- }
221
-
222
- // Workflows
223
- async getWorkflows(): Promise<Workflow[]> {
224
- return Array.from(this.workflows.values());
225
- }
226
-
227
- async getWorkflow(id: string): Promise<Workflow | null> {
228
- return this.workflows.get(id) || null;
229
- }
230
-
231
- async createWorkflow(workflow: Omit<Workflow, 'id' | 'createdAt' | 'updatedAt'>): Promise<Workflow> {
232
- const id = `wf_${crypto.randomUUID()}`;
233
- const now = new Date().toISOString();
234
- const newWorkflow: Workflow = {
235
- ...workflow,
236
- id,
237
- createdAt: now,
238
- updatedAt: now
239
- };
240
- this.workflows.set(id, newWorkflow);
241
- return newWorkflow;
242
- }
243
-
244
- async updateWorkflow(id: string, updates: Partial<Workflow>): Promise<Workflow | null> {
245
- const existing = this.workflows.get(id);
246
- if (!existing) return null;
247
- const updated: Workflow = {
248
- ...existing,
249
- ...updates,
250
- updatedAt: new Date().toISOString()
251
- };
252
- this.workflows.set(id, updated);
253
- return updated;
254
- }
255
-
256
- async deleteWorkflow(id: string): Promise<boolean> {
257
- return this.workflows.delete(id);
258
- }
259
-
260
- async getTasks(projectId?: string): Promise<Task[]> {
261
- const all = Array.from(this.tasks.values());
262
- if (projectId) {
263
- return all.filter((t) => t.projectId === projectId);
264
- }
265
- return all;
266
- }
267
-
268
- async queryTasks(query: TaskQuery): Promise<Page<Task>> {
269
- const tasks = Array.from(this.tasks.values()).filter((t) => matchesTaskQuery(t, query));
270
- return paginate(tasks, (t) => t.createdAt, 'asc', query.limit, query.cursor);
271
- }
272
-
273
- async getTask(id: string): Promise<Task | null> {
274
- return this.tasks.get(id) || null;
275
- }
276
-
277
- async createTask(task: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Promise<Task> {
278
- const id = `task_${crypto.randomUUID()}`;
279
- const now = new Date().toISOString();
280
- const newTask: Task = {
281
- ...task,
282
- id,
283
- createdAt: now,
284
- updatedAt: now
285
- };
286
- this.tasks.set(id, newTask);
287
- return newTask;
288
- }
289
-
290
- async updateTask(id: string, updates: Partial<Task>): Promise<Task | null> {
291
- const existing = this.tasks.get(id);
292
- if (!existing) return null;
293
- const updated: Task = {
294
- ...existing,
295
- ...updates,
296
- updatedAt: new Date().toISOString()
297
- };
298
- this.tasks.set(id, updated);
299
- return updated;
300
- }
301
-
302
- async deleteTask(id: string): Promise<boolean> {
303
- return this.tasks.delete(id);
304
- }
305
-
306
- // Teams
307
- async getTeams(): Promise<Team[]> {
308
- return Array.from(this.teams.values());
309
- }
310
-
311
- async getTeam(id: string): Promise<Team | null> {
312
- return this.teams.get(id) || null;
313
- }
314
-
315
- async createTeam(team: Omit<Team, 'id' | 'createdAt' | 'updatedAt'>): Promise<Team> {
316
- const id = `team_${crypto.randomUUID()}`;
317
- const now = new Date().toISOString();
318
- const newTeam: Team = { ...team, id, createdAt: now, updatedAt: now };
319
- this.teams.set(id, newTeam);
320
- return newTeam;
321
- }
322
-
323
- async updateTeam(id: string, updates: Partial<Team>): Promise<Team | null> {
324
- const existing = this.teams.get(id);
325
- if (!existing) return null;
326
- const updated: Team = { ...existing, ...updates, updatedAt: new Date().toISOString() };
327
- this.teams.set(id, updated);
328
- return updated;
329
- }
330
-
331
- async deleteTeam(id: string): Promise<boolean> {
332
- return this.teams.delete(id);
333
- }
334
-
335
- // Containers
336
- async getContainers(projectId: string): Promise<TaskContainer[]> {
337
- return Array.from(this.containers.values()).filter((c) => c.projectId === projectId);
338
- }
339
-
340
- async getContainer(id: string): Promise<TaskContainer | null> {
341
- return this.containers.get(id) || null;
342
- }
343
-
344
- async createContainer(container: Omit<TaskContainer, 'id' | 'createdAt' | 'updatedAt'>): Promise<TaskContainer> {
345
- const id = `cnt_${crypto.randomUUID()}`;
346
- const now = new Date().toISOString();
347
- const newContainer: TaskContainer = { ...container, id, createdAt: now, updatedAt: now };
348
- this.containers.set(id, newContainer);
349
- return newContainer;
350
- }
351
-
352
- async updateContainer(id: string, updates: Partial<TaskContainer>): Promise<TaskContainer | null> {
353
- const existing = this.containers.get(id);
354
- if (!existing) return null;
355
- const updated: TaskContainer = { ...existing, ...updates, updatedAt: new Date().toISOString() };
356
- this.containers.set(id, updated);
357
- return updated;
358
- }
359
-
360
- async deleteContainer(id: string): Promise<boolean> {
361
- return this.containers.delete(id);
362
- }
363
-
364
- // Deliverables
365
- async getDeliverables(projectId: string): Promise<Deliverable[]> {
366
- return Array.from(this.deliverables.values()).filter((d) => d.projectId === projectId);
367
- }
368
-
369
- async getDeliverable(id: string): Promise<Deliverable | null> {
370
- return this.deliverables.get(id) || null;
371
- }
372
-
373
- async createDeliverable(deliverable: CreateDeliverableInput): Promise<Deliverable> {
374
- const id = (deliverable as any).id || `deliv_${crypto.randomUUID()}`;
375
- const now = new Date().toISOString();
376
- const newDeliverable: Deliverable = {
377
- ...deliverable,
378
- id,
379
- status: deliverable.status || 'planned',
380
- outputUrls: deliverable.outputUrls ? [...deliverable.outputUrls] : [],
381
- customFields: deliverable.customFields ? { ...deliverable.customFields } : {},
382
- createdAt: now,
383
- updatedAt: now
384
- };
385
- this.deliverables.set(id, newDeliverable);
386
- return newDeliverable;
387
- }
388
-
389
- async updateDeliverable(id: string, updates: Partial<Deliverable>): Promise<Deliverable | null> {
390
- const existing = this.deliverables.get(id);
391
- if (!existing) return null;
392
- const updated: Deliverable = {
393
- ...existing,
394
- ...updates,
395
- updatedAt: new Date().toISOString()
396
- };
397
- this.deliverables.set(id, updated);
398
- return updated;
399
- }
400
-
401
- async deleteDeliverable(id: string): Promise<boolean> {
402
- return this.deliverables.delete(id);
403
- }
404
-
405
- // Iterations
406
- async getIterations(projectId: string): Promise<Iteration[]> {
407
- return Array.from(this.iterations.values()).filter((s) => s.projectId === projectId);
408
- }
409
-
410
- async getIteration(id: string): Promise<Iteration | null> {
411
- return this.iterations.get(id) || null;
412
- }
413
-
414
- async createIteration(iteration: Omit<Iteration, 'id' | 'createdAt'>): Promise<Iteration> {
415
- const id = `iter_${crypto.randomUUID()}`;
416
- const newIteration: Iteration = {
417
- ...iteration,
418
- id,
419
- createdAt: new Date().toISOString()
420
- };
421
- this.iterations.set(id, newIteration);
422
- return newIteration;
423
- }
424
-
425
- async updateIteration(id: string, updates: Partial<Iteration>): Promise<Iteration | null> {
426
- const existing = this.iterations.get(id);
427
- if (!existing) return null;
428
- const updated: Iteration = { ...existing, ...updates };
429
- this.iterations.set(id, updated);
430
- return updated;
431
- }
432
-
433
- async deleteIteration(id: string): Promise<boolean> {
434
- return this.iterations.delete(id);
435
- }
436
-
437
- // Comments
438
- async getComments(taskId: string): Promise<Comment[]> {
439
- return Array.from(this.comments.values()).filter((c) => c.taskId === taskId);
440
- }
441
-
442
- async getComment(id: string): Promise<Comment | null> {
443
- return this.comments.get(id) || null;
444
- }
445
-
446
- async addComment(comment: Omit<Comment, 'id' | 'createdAt' | 'updatedAt'>): Promise<Comment> {
447
- const id = `cmt_${crypto.randomUUID()}`;
448
- const now = new Date().toISOString();
449
- const newComment: Comment = { ...comment, id, createdAt: now, updatedAt: now };
450
- this.comments.set(id, newComment);
451
- return newComment;
452
- }
453
-
454
- async updateComment(id: string, updates: Partial<Comment>): Promise<Comment | null> {
455
- const existing = this.comments.get(id);
456
- if (!existing) return null;
457
- const updated: Comment = {
458
- ...existing,
459
- ...updates,
460
- updatedAt: new Date().toISOString()
461
- };
462
- this.comments.set(id, updated);
463
- return updated;
464
- }
465
-
466
- async deleteComment(id: string): Promise<boolean> {
467
- return this.comments.delete(id);
468
- }
469
-
470
- async addReaction(commentId: string, reaction: { emoji: string; userId: string }): Promise<Comment | null> {
471
- const existing = this.comments.get(commentId);
472
- if (!existing) return null;
473
- const reactions = existing.reactions ? [...existing.reactions] : [];
474
- const alreadyExists = reactions.some((r) => r.emoji === reaction.emoji && r.userId === reaction.userId);
475
- if (!alreadyExists) {
476
- reactions.push({
477
- emoji: reaction.emoji,
478
- userId: reaction.userId,
479
- createdAt: new Date().toISOString()
480
- });
481
- }
482
- const updated: Comment = {
483
- ...existing,
484
- reactions,
485
- updatedAt: new Date().toISOString()
486
- };
487
- this.comments.set(commentId, updated);
488
- return updated;
489
- }
490
-
491
- async removeReaction(commentId: string, reaction: { emoji: string; userId: string }): Promise<Comment | null> {
492
- const existing = this.comments.get(commentId);
493
- if (!existing) return null;
494
- const reactions = (existing.reactions || []).filter(
495
- (r) => !(r.emoji === reaction.emoji && r.userId === reaction.userId)
496
- );
497
- const updated: Comment = {
498
- ...existing,
499
- reactions,
500
- updatedAt: new Date().toISOString()
501
- };
502
- this.comments.set(commentId, updated);
503
- return updated;
504
- }
505
-
506
- // Attachments
507
- async getAttachments(filter?: { taskId?: string; projectId?: string; commentId?: string }): Promise<Attachment[]> {
508
- return Array.from(this.attachments.values()).filter((a) => {
509
- if (filter?.taskId && a.taskId !== filter.taskId) return false;
510
- if (filter?.projectId && a.projectId !== filter.projectId) return false;
511
- if (filter?.commentId && a.commentId !== filter.commentId) return false;
512
- return true;
513
- });
514
- }
515
-
516
- async getAttachment(id: string): Promise<Attachment | null> {
517
- return this.attachments.get(id) || null;
518
- }
519
-
520
- async createAttachment(attachment: Omit<Attachment, 'id' | 'createdAt' | 'updatedAt'>): Promise<Attachment> {
521
- const id = `att_${crypto.randomUUID()}`;
522
- const now = new Date().toISOString();
523
- const newAttachment: Attachment = {
524
- ...attachment,
525
- id,
526
- createdAt: now,
527
- updatedAt: now
528
- };
529
- this.attachments.set(id, newAttachment);
530
- return newAttachment;
531
- }
532
-
533
- async deleteAttachment(id: string): Promise<boolean> {
534
- return this.attachments.delete(id);
535
- }
536
-
537
- // Activities
538
- async getActivities(filter?: { projectId?: string; taskId?: string }): Promise<Activity[]> {
539
- return this.activities.filter((a) => {
540
- if (filter?.projectId && a.projectId !== filter.projectId) return false;
541
- if (filter?.taskId && a.taskId !== filter.taskId) return false;
542
- return true;
543
- });
544
- }
545
-
546
- async queryActivities(query: ActivityQuery): Promise<Page<Activity>> {
547
- const activities = this.activities.filter((a) => matchesActivityQuery(a, query));
548
- return paginate(activities, (a) => a.createdAt, 'desc', query.limit, query.cursor);
549
- }
550
-
551
- async logActivity(activity: Omit<Activity, 'id' | 'createdAt'>): Promise<Activity> {
552
- const id = `act_${crypto.randomUUID()}`;
553
- const newAct: Activity = { ...activity, id, createdAt: new Date().toISOString() };
554
- this.activities.unshift(newAct);
555
- return newAct;
556
- }
557
-
558
- // Time Entries
559
- async getTimeEntries(taskId: string): Promise<TimeEntry[]> {
560
- return Array.from(this.timeEntries.values()).filter((e) => e.taskId === taskId);
561
- }
562
-
563
- async logTime(entry: Omit<TimeEntry, 'id' | 'loggedAt'> & { loggedAt?: string }): Promise<TimeEntry> {
564
- const id = `time_${crypto.randomUUID()}`;
565
- const newEntry: TimeEntry = { ...entry, id, loggedAt: entry.loggedAt || new Date().toISOString() };
566
- this.timeEntries.set(id, newEntry);
567
- return newEntry;
568
- }
569
-
570
- async deleteTimeEntry(id: string): Promise<boolean> {
571
- return this.timeEntries.delete(id);
572
- }
573
-
574
- // Dependencies
575
- async getDependencies(taskId: string): Promise<TaskDependency[]> {
576
- return Array.from(this.dependencies.values()).filter(
577
- (d) => d.taskId === taskId || d.dependsOnTaskId === taskId
578
- );
579
- }
580
-
581
- async getDependency(id: string): Promise<TaskDependency | null> {
582
- return this.dependencies.get(id) ?? null;
583
- }
584
-
585
- async addDependency(dep: Omit<TaskDependency, 'id'>): Promise<TaskDependency> {
586
- const id = `dep_${crypto.randomUUID()}`;
587
- const newDep: TaskDependency = { ...dep, id };
588
- this.dependencies.set(id, newDep);
589
- return newDep;
590
- }
591
-
592
- async removeDependency(id: string): Promise<boolean> {
593
- return this.dependencies.delete(id);
594
- }
595
-
596
- // Webhooks
597
- async getWebhooks(): Promise<Webhook[]> {
598
- return Array.from(this.webhooks.values());
599
- }
600
-
601
- async getWebhook(id: string): Promise<Webhook | null> {
602
- return this.webhooks.get(id) ?? null;
603
- }
604
-
605
- async addWebhook(webhook: Omit<Webhook, 'id' | 'createdAt'>): Promise<Webhook> {
606
- const id = `wh_${crypto.randomUUID()}`;
607
- const newWh: Webhook = { ...webhook, id, createdAt: new Date().toISOString() };
608
- this.webhooks.set(id, newWh);
609
- return newWh;
610
- }
611
-
612
- async updateWebhook(id: string, updates: Partial<Omit<Webhook, 'id' | 'createdAt'>>): Promise<Webhook | null> {
613
- const existing = this.webhooks.get(id);
614
- if (!existing) return null;
615
- const updated: Webhook = { ...existing, ...updates, id, createdAt: existing.createdAt };
616
- this.webhooks.set(id, updated);
617
- return updated;
618
- }
619
-
620
- async deleteWebhook(id: string): Promise<boolean> {
621
- return this.webhooks.delete(id);
622
- }
623
- }