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