@critical-path/core 0.36.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 (185) hide show
  1. package/package.json +5 -1
  2. package/CHANGELOG.md +0 -593
  3. package/dist/deliverables.test.d.ts +0 -2
  4. package/dist/deliverables.test.d.ts.map +0 -1
  5. package/dist/deliverables.test.js +0 -232
  6. package/dist/deliverables.test.js.map +0 -1
  7. package/dist/domain/calendar.test.d.ts +0 -2
  8. package/dist/domain/calendar.test.d.ts.map +0 -1
  9. package/dist/domain/calendar.test.js +0 -181
  10. package/dist/domain/calendar.test.js.map +0 -1
  11. package/dist/domain/cpm.test.d.ts +0 -2
  12. package/dist/domain/cpm.test.d.ts.map +0 -1
  13. package/dist/domain/cpm.test.js +0 -166
  14. package/dist/domain/cpm.test.js.map +0 -1
  15. package/dist/domain/ddd.test.d.ts +0 -2
  16. package/dist/domain/ddd.test.d.ts.map +0 -1
  17. package/dist/domain/ddd.test.js +0 -349
  18. package/dist/domain/ddd.test.js.map +0 -1
  19. package/dist/domain/ladder.test.d.ts +0 -2
  20. package/dist/domain/ladder.test.d.ts.map +0 -1
  21. package/dist/domain/ladder.test.js +0 -213
  22. package/dist/domain/ladder.test.js.map +0 -1
  23. package/dist/domain/metrics.test.d.ts +0 -2
  24. package/dist/domain/metrics.test.d.ts.map +0 -1
  25. package/dist/domain/metrics.test.js +0 -252
  26. package/dist/domain/metrics.test.js.map +0 -1
  27. package/dist/domain/task-duration.test.d.ts +0 -2
  28. package/dist/domain/task-duration.test.d.ts.map +0 -1
  29. package/dist/domain/task-duration.test.js +0 -307
  30. package/dist/domain/task-duration.test.js.map +0 -1
  31. package/dist/domain/workload.test.d.ts +0 -2
  32. package/dist/domain/workload.test.d.ts.map +0 -1
  33. package/dist/domain/workload.test.js +0 -261
  34. package/dist/domain/workload.test.js.map +0 -1
  35. package/dist/engine/actor.test.d.ts +0 -2
  36. package/dist/engine/actor.test.d.ts.map +0 -1
  37. package/dist/engine/actor.test.js +0 -54
  38. package/dist/engine/actor.test.js.map +0 -1
  39. package/dist/engine/authorization.test.d.ts +0 -2
  40. package/dist/engine/authorization.test.d.ts.map +0 -1
  41. package/dist/engine/authorization.test.js +0 -154
  42. package/dist/engine/authorization.test.js.map +0 -1
  43. package/dist/engine/cascade.test.d.ts +0 -2
  44. package/dist/engine/cascade.test.d.ts.map +0 -1
  45. package/dist/engine/cascade.test.js +0 -99
  46. package/dist/engine/cascade.test.js.map +0 -1
  47. package/dist/engine/config.test.d.ts +0 -2
  48. package/dist/engine/config.test.d.ts.map +0 -1
  49. package/dist/engine/config.test.js +0 -46
  50. package/dist/engine/config.test.js.map +0 -1
  51. package/dist/engine/entity-events.test.d.ts +0 -2
  52. package/dist/engine/entity-events.test.d.ts.map +0 -1
  53. package/dist/engine/entity-events.test.js +0 -61
  54. package/dist/engine/entity-events.test.js.map +0 -1
  55. package/dist/index.test.d.ts +0 -2
  56. package/dist/index.test.d.ts.map +0 -1
  57. package/dist/index.test.js +0 -429
  58. package/dist/index.test.js.map +0 -1
  59. package/dist/plugins/plugins.test.d.ts +0 -2
  60. package/dist/plugins/plugins.test.d.ts.map +0 -1
  61. package/dist/plugins/plugins.test.js +0 -137
  62. package/dist/plugins/plugins.test.js.map +0 -1
  63. package/dist/schemas/schemas.test.d.ts +0 -2
  64. package/dist/schemas/schemas.test.d.ts.map +0 -1
  65. package/dist/schemas/schemas.test.js +0 -58
  66. package/dist/schemas/schemas.test.js.map +0 -1
  67. package/dist/status.test.d.ts +0 -2
  68. package/dist/status.test.d.ts.map +0 -1
  69. package/dist/status.test.js +0 -295
  70. package/dist/status.test.js.map +0 -1
  71. package/dist/storage/storage.test.d.ts +0 -2
  72. package/dist/storage/storage.test.d.ts.map +0 -1
  73. package/dist/storage/storage.test.js +0 -236
  74. package/dist/storage/storage.test.js.map +0 -1
  75. package/dist/storage/upload-limits.test.d.ts +0 -2
  76. package/dist/storage/upload-limits.test.d.ts.map +0 -1
  77. package/dist/storage/upload-limits.test.js +0 -31
  78. package/dist/storage/upload-limits.test.js.map +0 -1
  79. package/dist/store/conformance.test.d.ts +0 -2
  80. package/dist/store/conformance.test.d.ts.map +0 -1
  81. package/dist/store/conformance.test.js +0 -15
  82. package/dist/store/conformance.test.js.map +0 -1
  83. package/dist/store/firebase.test.d.ts +0 -2
  84. package/dist/store/firebase.test.d.ts.map +0 -1
  85. package/dist/store/firebase.test.js +0 -103
  86. package/dist/store/firebase.test.js.map +0 -1
  87. package/dist/store/query.test.d.ts +0 -2
  88. package/dist/store/query.test.d.ts.map +0 -1
  89. package/dist/store/query.test.js +0 -89
  90. package/dist/store/query.test.js.map +0 -1
  91. package/dist/store/sqlite.test.d.ts +0 -2
  92. package/dist/store/sqlite.test.d.ts.map +0 -1
  93. package/dist/store/sqlite.test.js +0 -177
  94. package/dist/store/sqlite.test.js.map +0 -1
  95. package/dist/task-state.test.d.ts +0 -2
  96. package/dist/task-state.test.d.ts.map +0 -1
  97. package/dist/task-state.test.js +0 -423
  98. package/dist/task-state.test.js.map +0 -1
  99. package/dist/use-case-validation.test.d.ts +0 -2
  100. package/dist/use-case-validation.test.d.ts.map +0 -1
  101. package/dist/use-case-validation.test.js +0 -281
  102. package/dist/use-case-validation.test.js.map +0 -1
  103. package/dist/utils/fractional-index.test.d.ts +0 -2
  104. package/dist/utils/fractional-index.test.d.ts.map +0 -1
  105. package/dist/utils/fractional-index.test.js +0 -182
  106. package/dist/utils/fractional-index.test.js.map +0 -1
  107. package/dist/utils/key.test.d.ts +0 -2
  108. package/dist/utils/key.test.d.ts.map +0 -1
  109. package/dist/utils/key.test.js +0 -65
  110. package/dist/utils/key.test.js.map +0 -1
  111. package/dist/utils/mentions.test.d.ts +0 -2
  112. package/dist/utils/mentions.test.d.ts.map +0 -1
  113. package/dist/utils/mentions.test.js +0 -114
  114. package/dist/utils/mentions.test.js.map +0 -1
  115. package/dist/webhooks/webhooks.test.d.ts +0 -2
  116. package/dist/webhooks/webhooks.test.d.ts.map +0 -1
  117. package/dist/webhooks/webhooks.test.js +0 -154
  118. package/dist/webhooks/webhooks.test.js.map +0 -1
  119. package/src/deliverables.test.ts +0 -284
  120. package/src/domain/authorization.ts +0 -82
  121. package/src/domain/calendar.test.ts +0 -216
  122. package/src/domain/calendar.ts +0 -502
  123. package/src/domain/cpm.test.ts +0 -189
  124. package/src/domain/cpm.ts +0 -258
  125. package/src/domain/custom-fields.ts +0 -172
  126. package/src/domain/ddd.test.ts +0 -442
  127. package/src/domain/entities.ts +0 -798
  128. package/src/domain/errors.ts +0 -38
  129. package/src/domain/events.ts +0 -369
  130. package/src/domain/graph.ts +0 -70
  131. package/src/domain/index.ts +0 -12
  132. package/src/domain/ladder.test.ts +0 -251
  133. package/src/domain/ladder.ts +0 -474
  134. package/src/domain/metrics.test.ts +0 -288
  135. package/src/domain/metrics.ts +0 -497
  136. package/src/domain/task-duration.test.ts +0 -354
  137. package/src/domain/workload.test.ts +0 -311
  138. package/src/domain/workload.ts +0 -457
  139. package/src/engine/actor.test.ts +0 -63
  140. package/src/engine/authorization.test.ts +0 -199
  141. package/src/engine/cascade.test.ts +0 -121
  142. package/src/engine/config.test.ts +0 -51
  143. package/src/engine/entity-events.test.ts +0 -67
  144. package/src/engine/index.ts +0 -2495
  145. package/src/index.test.ts +0 -514
  146. package/src/index.ts +0 -9
  147. package/src/plugins/index.ts +0 -103
  148. package/src/plugins/plugins.test.ts +0 -161
  149. package/src/schemas/index.ts +0 -402
  150. package/src/schemas/schemas.test.ts +0 -116
  151. package/src/status.test.ts +0 -354
  152. package/src/storage/file-storage.ts +0 -200
  153. package/src/storage/firebase-storage.ts +0 -209
  154. package/src/storage/index.ts +0 -3
  155. package/src/storage/s3.ts +0 -151
  156. package/src/storage/storage.test.ts +0 -274
  157. package/src/storage/upload-limits.test.ts +0 -41
  158. package/src/store/conformance.test.ts +0 -15
  159. package/src/store/firebase.test.ts +0 -131
  160. package/src/store/firebase.ts +0 -658
  161. package/src/store/index.ts +0 -623
  162. package/src/store/query.test.ts +0 -110
  163. package/src/store/query.ts +0 -113
  164. package/src/store/sqlite.test.ts +0 -218
  165. package/src/store/sqlite.ts +0 -1480
  166. package/src/task-state.test.ts +0 -506
  167. package/src/testing/conformance.ts +0 -319
  168. package/src/testing/index.ts +0 -1
  169. package/src/types/index.ts +0 -871
  170. package/src/use-case-validation.test.ts +0 -324
  171. package/src/utils/fractional-index.test.ts +0 -228
  172. package/src/utils/fractional-index.ts +0 -254
  173. package/src/utils/index.ts +0 -7
  174. package/src/utils/key.test.ts +0 -76
  175. package/src/utils/key.ts +0 -62
  176. package/src/utils/mentions.test.ts +0 -132
  177. package/src/utils/mentions.ts +0 -131
  178. package/src/utils/status.ts +0 -419
  179. package/src/utils/workflow.ts +0 -306
  180. package/src/webhooks/dispatcher.ts +0 -190
  181. package/src/webhooks/index.ts +0 -2
  182. package/src/webhooks/signature.ts +0 -57
  183. package/src/webhooks/webhooks.test.ts +0 -201
  184. package/tsconfig.json +0 -8
  185. 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
- }