@critical-path/client 0.14.9 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts CHANGED
@@ -1,4 +1,29 @@
1
1
  import type {
2
+ CreateWorkflowBody,
3
+ UpdateWorkflowBody,
4
+ CreateProjectBody,
5
+ UpdateProjectBody,
6
+ CreateTaskBody,
7
+ UpdateTaskBody,
8
+ CreateDependencyBody,
9
+ CreateTeamBody,
10
+ UpdateTeamBody,
11
+ CreateContainerBody,
12
+ UpdateContainerBody,
13
+ CreateDeliverableBody,
14
+ UpdateDeliverableBody,
15
+ CreateIterationBody,
16
+ UpdateIterationBody,
17
+ CreateCommentBody,
18
+ UpdateCommentBody,
19
+ CommentReactionBody,
20
+ CreateAttachmentBody,
21
+ UploadAttachmentBody,
22
+ PresignAttachmentBody,
23
+ LogTimeBody
24
+ } from '@critical-path/core/schemas';
25
+ import type {
26
+ TaskDependency,
2
27
  Project,
3
28
  Task,
4
29
  TaskTodoItem,
@@ -98,6 +123,15 @@ export interface ClientOptions {
98
123
  fetch?: typeof fetch;
99
124
  }
100
125
 
126
+ function toBase64(data: Uint8Array | ArrayBuffer): string {
127
+ const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
128
+ let binary = '';
129
+ for (let i = 0; i < bytes.length; i += 0x8000) {
130
+ binary += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
131
+ }
132
+ return btoa(binary);
133
+ }
134
+
101
135
  class NotFoundResponseError extends Error {
102
136
  constructor(message: string) {
103
137
  super(message);
@@ -166,7 +200,7 @@ export class CriticalPathClient {
166
200
  return res.workflow;
167
201
  }
168
202
 
169
- async createWorkflow(data: Omit<Workflow, 'id' | 'createdAt' | 'updatedAt'>): Promise<Workflow> {
203
+ async createWorkflow(data: CreateWorkflowBody): Promise<Workflow> {
170
204
  const res = await this.request<{ workflow: Workflow }>('/workflows', {
171
205
  method: 'POST',
172
206
  body: JSON.stringify(data)
@@ -174,7 +208,7 @@ export class CriticalPathClient {
174
208
  return res.workflow;
175
209
  }
176
210
 
177
- async updateWorkflow(id: string, updates: Partial<Workflow>): Promise<Workflow> {
211
+ async updateWorkflow(id: string, updates: UpdateWorkflowBody): Promise<Workflow> {
178
212
  const res = await this.request<{ workflow: Workflow }>(`/workflows/${id}`, {
179
213
  method: 'PATCH',
180
214
  body: JSON.stringify(updates)
@@ -197,7 +231,7 @@ export class CriticalPathClient {
197
231
  return res.project;
198
232
  }
199
233
 
200
- async createProject(data: Omit<Project, 'id' | 'createdAt' | 'updatedAt'>): Promise<Project> {
234
+ async createProject(data: CreateProjectBody): Promise<Project> {
201
235
  const res = await this.request<{ project: Project }>('/projects', {
202
236
  method: 'POST',
203
237
  body: JSON.stringify(data)
@@ -205,6 +239,19 @@ export class CriticalPathClient {
205
239
  return res.project;
206
240
  }
207
241
 
242
+ async updateProject(id: string, updates: UpdateProjectBody): Promise<Project> {
243
+ const res = await this.request<{ project: Project }>(`/projects/${encodeURIComponent(id)}`, {
244
+ method: 'PATCH',
245
+ body: JSON.stringify(updates)
246
+ });
247
+ return res.project;
248
+ }
249
+
250
+ /** Deletes a project and its tasks. Resolves to `false` if it does not exist. */
251
+ async deleteProject(id: string): Promise<boolean> {
252
+ return this.deleteRequest(`/projects/${encodeURIComponent(id)}`);
253
+ }
254
+
208
255
  async calculateCriticalPath(projectId: string): Promise<CriticalPathAnalysis> {
209
256
  const res = await this.request<{ analysis: CriticalPathAnalysis }>(
210
257
  `/projects/${encodeURIComponent(projectId)}/critical-path`
@@ -285,7 +332,7 @@ export class CriticalPathClient {
285
332
  return res.progressHistory;
286
333
  }
287
334
 
288
- async createTask(data: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Promise<Task> {
335
+ async createTask(data: CreateTaskBody): Promise<Task> {
289
336
  const res = await this.request<{ task: Task }>('/tasks', {
290
337
  method: 'POST',
291
338
  body: JSON.stringify(data)
@@ -293,15 +340,7 @@ export class CriticalPathClient {
293
340
  return res.task;
294
341
  }
295
342
 
296
- async updateTask(
297
- id: string,
298
- updates: Partial<Task> & {
299
- actorId?: string;
300
- actorName?: string;
301
- actorType?: string;
302
- actor?: { userId: string; username?: string; actorType?: string };
303
- }
304
- ): Promise<Task> {
343
+ async updateTask(id: string, updates: UpdateTaskBody): Promise<Task> {
305
344
  const res = await this.request<{ task: Task }>(`/tasks/${id}`, {
306
345
  method: 'PATCH',
307
346
  body: JSON.stringify(updates)
@@ -340,8 +379,7 @@ export class CriticalPathClient {
340
379
 
341
380
  async addTodo(
342
381
  taskId: string,
343
- title: string,
344
- options?: { actorId?: string; actorName?: string; actorType?: string }
382
+ title: string
345
383
  ): Promise<TaskTodoItem> {
346
384
  const task = await this.getTask(taskId);
347
385
  const item: TaskTodoItem = {
@@ -351,15 +389,14 @@ export class CriticalPathClient {
351
389
  createdAt: new Date().toISOString()
352
390
  };
353
391
  const todos = [...(task.todos || []), item];
354
- await this.updateTask(taskId, { todos, ...options });
392
+ await this.updateTask(taskId, { todos });
355
393
  return item;
356
394
  }
357
395
 
358
396
  async toggleTodo(
359
397
  taskId: string,
360
398
  todoIdOrTitle: string,
361
- completed?: boolean,
362
- options?: { actorId?: string; actorName?: string; actorType?: string }
399
+ completed?: boolean
363
400
  ): Promise<Task> {
364
401
  const task = await this.getTask(taskId);
365
402
  if (!task.todos || task.todos.length === 0) {
@@ -373,7 +410,16 @@ export class CriticalPathClient {
373
410
  }
374
411
  item.completed = completed !== undefined ? completed : !item.completed;
375
412
  item.completedAt = item.completed ? new Date().toISOString() : undefined;
376
- return this.updateTask(taskId, { todos: task.todos, ...options });
413
+ return this.updateTask(taskId, { todos: task.todos });
414
+ }
415
+
416
+ /** Declares that `taskId` depends on another task. Rejects with a 409 error on cycles. */
417
+ async addDependency(taskId: string, data: CreateDependencyBody): Promise<TaskDependency> {
418
+ const res = await this.request<{ dependency: TaskDependency }>(`/tasks/${encodeURIComponent(taskId)}/dependencies`, {
419
+ method: 'POST',
420
+ body: JSON.stringify(data)
421
+ });
422
+ return res.dependency;
377
423
  }
378
424
 
379
425
  async deleteTask(id: string): Promise<boolean> {
@@ -411,7 +457,7 @@ export class CriticalPathClient {
411
457
  return res.team;
412
458
  }
413
459
 
414
- async createTeam(data: Omit<Team, 'id' | 'createdAt' | 'updatedAt'>): Promise<Team> {
460
+ async createTeam(data: CreateTeamBody): Promise<Team> {
415
461
  const res = await this.request<{ team: Team }>('/teams', {
416
462
  method: 'POST',
417
463
  body: JSON.stringify(data)
@@ -419,7 +465,7 @@ export class CriticalPathClient {
419
465
  return res.team;
420
466
  }
421
467
 
422
- async updateTeam(id: string, updates: Partial<Team>): Promise<Team> {
468
+ async updateTeam(id: string, updates: UpdateTeamBody): Promise<Team> {
423
469
  const res = await this.request<{ team: Team }>(`/teams/${id}`, {
424
470
  method: 'PATCH',
425
471
  body: JSON.stringify(updates)
@@ -437,7 +483,7 @@ export class CriticalPathClient {
437
483
  return res.containers;
438
484
  }
439
485
 
440
- async createContainer(data: Omit<TaskContainer, 'id' | 'createdAt' | 'updatedAt'>): Promise<TaskContainer> {
486
+ async createContainer(data: CreateContainerBody): Promise<TaskContainer> {
441
487
  const res = await this.request<{ container: TaskContainer }>('/containers', {
442
488
  method: 'POST',
443
489
  body: JSON.stringify(data)
@@ -445,7 +491,7 @@ export class CriticalPathClient {
445
491
  return res.container;
446
492
  }
447
493
 
448
- async updateContainer(id: string, updates: Partial<TaskContainer>): Promise<TaskContainer> {
494
+ async updateContainer(id: string, updates: UpdateContainerBody): Promise<TaskContainer> {
449
495
  const res = await this.request<{ container: TaskContainer }>(`/containers/${id}`, {
450
496
  method: 'PATCH',
451
497
  body: JSON.stringify(updates)
@@ -473,7 +519,7 @@ export class CriticalPathClient {
473
519
  return res.summary;
474
520
  }
475
521
 
476
- async createDeliverable(data: CreateDeliverableInput): Promise<Deliverable> {
522
+ async createDeliverable(data: CreateDeliverableBody): Promise<Deliverable> {
477
523
  const res = await this.request<{ deliverable: Deliverable }>('/deliverables', {
478
524
  method: 'POST',
479
525
  body: JSON.stringify(data)
@@ -481,7 +527,7 @@ export class CriticalPathClient {
481
527
  return res.deliverable;
482
528
  }
483
529
 
484
- async updateDeliverable(id: string, updates: Partial<Deliverable>): Promise<Deliverable> {
530
+ async updateDeliverable(id: string, updates: UpdateDeliverableBody): Promise<Deliverable> {
485
531
  const res = await this.request<{ deliverable: Deliverable }>(`/deliverables/${id}`, {
486
532
  method: 'PATCH',
487
533
  body: JSON.stringify(updates)
@@ -499,7 +545,7 @@ export class CriticalPathClient {
499
545
  return res.iterations;
500
546
  }
501
547
 
502
- async createIteration(data: Omit<Iteration, 'id' | 'createdAt'>): Promise<Iteration> {
548
+ async createIteration(data: CreateIterationBody): Promise<Iteration> {
503
549
  const res = await this.request<{ iteration: Iteration }>('/iterations', {
504
550
  method: 'POST',
505
551
  body: JSON.stringify(data)
@@ -507,7 +553,7 @@ export class CriticalPathClient {
507
553
  return res.iteration;
508
554
  }
509
555
 
510
- async updateIteration(id: string, updates: Partial<Iteration>): Promise<Iteration> {
556
+ async updateIteration(id: string, updates: UpdateIterationBody): Promise<Iteration> {
511
557
  const res = await this.request<{ iteration: Iteration }>(`/iterations/${id}`, {
512
558
  method: 'PATCH',
513
559
  body: JSON.stringify(updates)
@@ -540,7 +586,7 @@ export class CriticalPathClient {
540
586
  return res.comment;
541
587
  }
542
588
 
543
- async addComment(data: Omit<Comment, 'id' | 'createdAt' | 'updatedAt'>): Promise<Comment> {
589
+ async addComment(data: CreateCommentBody): Promise<Comment> {
544
590
  const res = await this.request<{ comment: Comment }>('/comments', {
545
591
  method: 'POST',
546
592
  body: JSON.stringify(data)
@@ -548,7 +594,7 @@ export class CriticalPathClient {
548
594
  return res.comment;
549
595
  }
550
596
 
551
- async updateComment(id: string, updates: Partial<Comment>): Promise<Comment> {
597
+ async updateComment(id: string, updates: UpdateCommentBody): Promise<Comment> {
552
598
  const res = await this.request<{ comment: Comment }>(`/comments/${encodeURIComponent(id)}`, {
553
599
  method: 'PATCH',
554
600
  body: JSON.stringify(updates)
@@ -560,7 +606,7 @@ export class CriticalPathClient {
560
606
  return this.deleteRequest(`/comments/${encodeURIComponent(id)}`);
561
607
  }
562
608
 
563
- async addCommentReaction(commentId: string, reaction: { emoji: string; userId: string }): Promise<Comment> {
609
+ async addCommentReaction(commentId: string, reaction: CommentReactionBody): Promise<Comment> {
564
610
  const res = await this.request<{ comment: Comment }>(`/comments/${encodeURIComponent(commentId)}/reactions`, {
565
611
  method: 'POST',
566
612
  body: JSON.stringify(reaction)
@@ -568,11 +614,12 @@ export class CriticalPathClient {
568
614
  return res.comment;
569
615
  }
570
616
 
571
- async removeCommentReaction(commentId: string, reaction: { emoji: string; userId: string }): Promise<Comment> {
572
- const res = await this.request<{ comment: Comment }>(`/comments/${encodeURIComponent(commentId)}/reactions`, {
573
- method: 'DELETE',
574
- body: JSON.stringify(reaction)
575
- });
617
+ async removeCommentReaction(commentId: string, reaction: CommentReactionBody): Promise<Comment> {
618
+ const query = new URLSearchParams({ emoji: reaction.emoji });
619
+ const res = await this.request<{ comment: Comment }>(
620
+ `/comments/${encodeURIComponent(commentId)}/reactions?${query}`,
621
+ { method: 'DELETE' }
622
+ );
576
623
  return res.comment;
577
624
  }
578
625
 
@@ -592,7 +639,7 @@ export class CriticalPathClient {
592
639
  return res.attachment;
593
640
  }
594
641
 
595
- async createAttachment(data: Omit<Attachment, 'id' | 'createdAt' | 'updatedAt'>): Promise<Attachment> {
642
+ async createAttachment(data: CreateAttachmentBody): Promise<Attachment> {
596
643
  const res = await this.request<{ attachment: Attachment }>('/attachments', {
597
644
  method: 'POST',
598
645
  body: JSON.stringify(data)
@@ -600,21 +647,19 @@ export class CriticalPathClient {
600
647
  return res.attachment;
601
648
  }
602
649
 
603
- async uploadAttachmentFile(data: {
604
- filename: string;
605
- data: string | Uint8Array | ArrayBuffer;
606
- mimeType?: string;
607
- pathPrefix?: string;
608
- taskId?: string;
609
- projectId?: string;
610
- commentId?: string;
611
- uploaderId: string;
612
- uploaderType?: 'user' | 'agent' | 'system';
613
- metadata?: Record<string, unknown>;
614
- }): Promise<Attachment> {
650
+ /**
651
+ * Uploads a file through the server's FileStorageAdapter. Binary data is sent base64-encoded.
652
+ */
653
+ async uploadAttachmentFile(
654
+ data: Omit<UploadAttachmentBody, 'data'> & { data: string | Uint8Array | ArrayBuffer }
655
+ ): Promise<Attachment> {
656
+ const body: UploadAttachmentBody =
657
+ typeof data.data === 'string'
658
+ ? { ...data, data: data.data }
659
+ : { ...data, data: toBase64(data.data), encoding: 'base64' };
615
660
  const res = await this.request<{ attachment: Attachment }>('/attachments/upload', {
616
661
  method: 'POST',
617
- body: JSON.stringify(data)
662
+ body: JSON.stringify(body)
618
663
  });
619
664
  return res.attachment;
620
665
  }
@@ -623,7 +668,7 @@ export class CriticalPathClient {
623
668
  return this.deleteRequest(`/attachments/${encodeURIComponent(id)}`);
624
669
  }
625
670
 
626
- async getPresignedAttachmentUploadUrl(options: PresignedUrlOptions): Promise<PresignedUploadResult> {
671
+ async getPresignedAttachmentUploadUrl(options: PresignAttachmentBody): Promise<PresignedUploadResult> {
627
672
  const res = await this.request<{ presigned: PresignedUploadResult }>('/attachments/presign', {
628
673
  method: 'POST',
629
674
  body: JSON.stringify(options)
@@ -637,7 +682,7 @@ export class CriticalPathClient {
637
682
  return res.timeEntries;
638
683
  }
639
684
 
640
- async logTime(data: Omit<TimeEntry, 'id' | 'loggedAt'>): Promise<TimeEntry> {
685
+ async logTime(data: LogTimeBody): Promise<TimeEntry> {
641
686
  const res = await this.request<{ timeEntry: TimeEntry }>('/time-entries', {
642
687
  method: 'POST',
643
688
  body: JSON.stringify(data)
@@ -1 +1 @@
1
- {"root":["./src/cli.test.ts","./src/index.test.ts","./src/index.ts","./src/bin/cli.ts"],"version":"5.9.3"}
1
+ {"root":["./src/cli.test.ts","./src/contract.test.ts","./src/index.test.ts","./src/index.ts","./src/bin/cli.ts"],"version":"5.9.3"}