@doist/todoist-api-typescript 6.0.1 → 6.1.4

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 (34) hide show
  1. package/README.md +105 -5
  2. package/dist/cjs/authentication.js +59 -63
  3. package/dist/cjs/rest-client.js +12 -10
  4. package/dist/cjs/test-utils/mocks.js +2 -45
  5. package/dist/cjs/test-utils/msw-setup.js +74 -5
  6. package/dist/cjs/test-utils/obsidian-fetch-adapter.js +53 -0
  7. package/dist/cjs/todoist-api.js +80 -30
  8. package/dist/cjs/types/entities.js +4 -4
  9. package/dist/cjs/types/index.js +1 -0
  10. package/dist/cjs/utils/fetch-with-retry.js +37 -14
  11. package/dist/cjs/utils/multipart-upload.js +2 -1
  12. package/dist/esm/authentication.js +59 -63
  13. package/dist/esm/rest-client.js +12 -10
  14. package/dist/esm/test-utils/mocks.js +3 -10
  15. package/dist/esm/test-utils/msw-setup.js +67 -2
  16. package/dist/esm/test-utils/obsidian-fetch-adapter.js +50 -0
  17. package/dist/esm/todoist-api.js +80 -30
  18. package/dist/esm/types/entities.js +4 -4
  19. package/dist/esm/types/index.js +1 -0
  20. package/dist/esm/utils/fetch-with-retry.js +37 -14
  21. package/dist/esm/utils/multipart-upload.js +2 -1
  22. package/dist/types/authentication.d.ts +20 -0
  23. package/dist/types/rest-client.d.ts +2 -1
  24. package/dist/types/test-utils/mocks.d.ts +0 -1
  25. package/dist/types/test-utils/msw-setup.d.ts +31 -1
  26. package/dist/types/test-utils/obsidian-fetch-adapter.d.ts +29 -0
  27. package/dist/types/todoist-api.d.ts +18 -7
  28. package/dist/types/types/entities.d.ts +4 -4
  29. package/dist/types/types/http.d.ts +17 -0
  30. package/dist/types/types/index.d.ts +1 -0
  31. package/dist/types/types/sync.d.ts +5 -5
  32. package/dist/types/utils/fetch-with-retry.d.ts +2 -1
  33. package/dist/types/utils/multipart-upload.d.ts +2 -0
  34. package/package.json +4 -3
@@ -16,27 +16,6 @@ const MAX_COMMAND_COUNT = 100;
16
16
  function generatePath(...segments) {
17
17
  return segments.join('/');
18
18
  }
19
- /**
20
- * A client for interacting with the Todoist API v1.
21
- * This class provides methods to manage tasks, projects, sections, labels, and comments in Todoist.
22
- *
23
- * @example
24
- * ```typescript
25
- * const api = new TodoistApi('your-api-token');
26
- *
27
- * // Get all tasks
28
- * const tasks = await api.getTasks();
29
- *
30
- * // Create a new task
31
- * const newTask = await api.addTask({
32
- * content: 'My new task',
33
- * projectId: '12345'
34
- * });
35
- * ```
36
- *
37
- * For more information about the Todoist API v1, see the [official documentation](https://todoist.com/api/v1).
38
- * If you're migrating from v9, please refer to the [migration guide](https://todoist.com/api/v1/docs#tag/Migrating-from-v9).
39
- */
40
19
  export class TodoistApi {
41
20
  constructor(
42
21
  /**
@@ -44,11 +23,24 @@ export class TodoistApi {
44
23
  */
45
24
  authToken,
46
25
  /**
47
- * Optional custom API base URL. If not provided, defaults to Todoist's standard API endpoint
26
+ * Optional custom API base URL or options object
48
27
  */
49
- baseUrl) {
28
+ baseUrlOrOptions) {
50
29
  this.authToken = authToken;
51
- this.syncApiBase = getSyncBaseUri(baseUrl);
30
+ // Handle backward compatibility
31
+ if (typeof baseUrlOrOptions === 'string') {
32
+ // Legacy constructor: (authToken, baseUrl)
33
+ // eslint-disable-next-line no-console
34
+ console.warn('TodoistApi constructor with baseUrl as second parameter is deprecated. Use options object instead: new TodoistApi(token, { baseUrl, customFetch })');
35
+ this.syncApiBase = getSyncBaseUri(baseUrlOrOptions);
36
+ this.customFetch = undefined;
37
+ }
38
+ else {
39
+ // New constructor: (authToken, options)
40
+ const options = baseUrlOrOptions || {};
41
+ this.syncApiBase = getSyncBaseUri(options.baseUrl);
42
+ this.customFetch = options.customFetch;
43
+ }
52
44
  }
53
45
  /**
54
46
  * Retrieves information about the authenticated user.
@@ -61,6 +53,7 @@ export class TodoistApi {
61
53
  baseUri: this.syncApiBase,
62
54
  relativePath: ENDPOINT_REST_USER,
63
55
  apiToken: this.authToken,
56
+ customFetch: this.customFetch,
64
57
  });
65
58
  return validateCurrentUser(response.data);
66
59
  }
@@ -77,6 +70,7 @@ export class TodoistApi {
77
70
  baseUri: this.syncApiBase,
78
71
  relativePath: generatePath(ENDPOINT_REST_TASKS, id),
79
72
  apiToken: this.authToken,
73
+ customFetch: this.customFetch,
80
74
  });
81
75
  return validateTask(response.data);
82
76
  }
@@ -92,6 +86,7 @@ export class TodoistApi {
92
86
  baseUri: this.syncApiBase,
93
87
  relativePath: ENDPOINT_REST_TASKS,
94
88
  apiToken: this.authToken,
89
+ customFetch: this.customFetch,
95
90
  payload: args,
96
91
  });
97
92
  return {
@@ -111,6 +106,7 @@ export class TodoistApi {
111
106
  baseUri: this.syncApiBase,
112
107
  relativePath: ENDPOINT_REST_TASKS_FILTER,
113
108
  apiToken: this.authToken,
109
+ customFetch: this.customFetch,
114
110
  payload: args,
115
111
  });
116
112
  return {
@@ -130,6 +126,7 @@ export class TodoistApi {
130
126
  baseUri: this.syncApiBase,
131
127
  relativePath: ENDPOINT_REST_TASKS_COMPLETED_BY_COMPLETION_DATE,
132
128
  apiToken: this.authToken,
129
+ customFetch: this.customFetch,
133
130
  payload: args,
134
131
  });
135
132
  return {
@@ -149,6 +146,7 @@ export class TodoistApi {
149
146
  baseUri: this.syncApiBase,
150
147
  relativePath: ENDPOINT_REST_TASKS_COMPLETED_BY_DUE_DATE,
151
148
  apiToken: this.authToken,
149
+ customFetch: this.customFetch,
152
150
  payload: args,
153
151
  });
154
152
  return {
@@ -168,6 +166,7 @@ export class TodoistApi {
168
166
  baseUri: this.syncApiBase,
169
167
  relativePath: ENDPOINT_REST_TASKS_COMPLETED_SEARCH,
170
168
  apiToken: this.authToken,
169
+ customFetch: this.customFetch,
171
170
  payload: args,
172
171
  });
173
172
  return {
@@ -188,6 +187,7 @@ export class TodoistApi {
188
187
  baseUri: this.syncApiBase,
189
188
  relativePath: ENDPOINT_REST_TASKS,
190
189
  apiToken: this.authToken,
190
+ customFetch: this.customFetch,
191
191
  payload: args,
192
192
  requestId: requestId,
193
193
  });
@@ -205,6 +205,7 @@ export class TodoistApi {
205
205
  baseUri: this.syncApiBase,
206
206
  relativePath: ENDPOINT_SYNC_QUICK_ADD,
207
207
  apiToken: this.authToken,
208
+ customFetch: this.customFetch,
208
209
  payload: args,
209
210
  });
210
211
  return validateTask(response.data);
@@ -224,6 +225,7 @@ export class TodoistApi {
224
225
  baseUri: this.syncApiBase,
225
226
  relativePath: generatePath(ENDPOINT_REST_TASKS, id),
226
227
  apiToken: this.authToken,
228
+ customFetch: this.customFetch,
227
229
  payload: args,
228
230
  requestId: requestId,
229
231
  });
@@ -257,15 +259,16 @@ export class TodoistApi {
257
259
  baseUri: this.syncApiBase,
258
260
  relativePath: ENDPOINT_SYNC,
259
261
  apiToken: this.authToken,
262
+ customFetch: this.customFetch,
260
263
  payload: syncRequest,
261
264
  requestId: requestId,
262
265
  hasSyncCommands: true,
263
266
  });
264
- if (response.data.sync_status) {
265
- Object.entries(response.data.sync_status).forEach(([_, value]) => {
267
+ if (response.data.syncStatus) {
268
+ Object.entries(response.data.syncStatus).forEach(([_, value]) => {
266
269
  if (value === 'ok')
267
270
  return;
268
- throw new TodoistRequestError(value.error, value.http_code, value.error_extra);
271
+ throw new TodoistRequestError(value.error, value.httpCode, value.errorExtra);
269
272
  });
270
273
  }
271
274
  if (!((_a = response.data.items) === null || _a === void 0 ? void 0 : _a.length)) {
@@ -292,6 +295,7 @@ export class TodoistApi {
292
295
  baseUri: this.syncApiBase,
293
296
  relativePath: generatePath(ENDPOINT_REST_TASKS, id, ENDPOINT_REST_TASK_MOVE),
294
297
  apiToken: this.authToken,
298
+ customFetch: this.customFetch,
295
299
  payload: Object.assign(Object.assign(Object.assign({}, (args.projectId && { project_id: args.projectId })), (args.sectionId && { section_id: args.sectionId })), (args.parentId && { parent_id: args.parentId })),
296
300
  requestId: requestId,
297
301
  });
@@ -311,6 +315,7 @@ export class TodoistApi {
311
315
  baseUri: this.syncApiBase,
312
316
  relativePath: generatePath(ENDPOINT_REST_TASKS, id, ENDPOINT_REST_TASK_CLOSE),
313
317
  apiToken: this.authToken,
318
+ customFetch: this.customFetch,
314
319
  requestId: requestId,
315
320
  });
316
321
  return isSuccess(response);
@@ -329,6 +334,7 @@ export class TodoistApi {
329
334
  baseUri: this.syncApiBase,
330
335
  relativePath: generatePath(ENDPOINT_REST_TASKS, id, ENDPOINT_REST_TASK_REOPEN),
331
336
  apiToken: this.authToken,
337
+ customFetch: this.customFetch,
332
338
  requestId: requestId,
333
339
  });
334
340
  return isSuccess(response);
@@ -347,6 +353,7 @@ export class TodoistApi {
347
353
  baseUri: this.syncApiBase,
348
354
  relativePath: generatePath(ENDPOINT_REST_TASKS, id),
349
355
  apiToken: this.authToken,
356
+ customFetch: this.customFetch,
350
357
  requestId: requestId,
351
358
  });
352
359
  return isSuccess(response);
@@ -364,6 +371,7 @@ export class TodoistApi {
364
371
  baseUri: this.syncApiBase,
365
372
  relativePath: generatePath(ENDPOINT_REST_PROJECTS, id),
366
373
  apiToken: this.authToken,
374
+ customFetch: this.customFetch,
367
375
  });
368
376
  return validateProject(response.data);
369
377
  }
@@ -379,6 +387,7 @@ export class TodoistApi {
379
387
  baseUri: this.syncApiBase,
380
388
  relativePath: ENDPOINT_REST_PROJECTS,
381
389
  apiToken: this.authToken,
390
+ customFetch: this.customFetch,
382
391
  payload: args,
383
392
  });
384
393
  return {
@@ -398,6 +407,7 @@ export class TodoistApi {
398
407
  baseUri: this.syncApiBase,
399
408
  relativePath: ENDPOINT_REST_PROJECTS_ARCHIVED,
400
409
  apiToken: this.authToken,
410
+ customFetch: this.customFetch,
401
411
  payload: args,
402
412
  });
403
413
  return {
@@ -418,6 +428,7 @@ export class TodoistApi {
418
428
  baseUri: this.syncApiBase,
419
429
  relativePath: ENDPOINT_REST_PROJECTS,
420
430
  apiToken: this.authToken,
431
+ customFetch: this.customFetch,
421
432
  payload: args,
422
433
  requestId: requestId,
423
434
  });
@@ -438,6 +449,7 @@ export class TodoistApi {
438
449
  baseUri: this.syncApiBase,
439
450
  relativePath: generatePath(ENDPOINT_REST_PROJECTS, id),
440
451
  apiToken: this.authToken,
452
+ customFetch: this.customFetch,
441
453
  payload: args,
442
454
  requestId: requestId,
443
455
  });
@@ -457,6 +469,7 @@ export class TodoistApi {
457
469
  baseUri: this.syncApiBase,
458
470
  relativePath: generatePath(ENDPOINT_REST_PROJECTS, id),
459
471
  apiToken: this.authToken,
472
+ customFetch: this.customFetch,
460
473
  requestId: requestId,
461
474
  });
462
475
  return isSuccess(response);
@@ -475,6 +488,7 @@ export class TodoistApi {
475
488
  baseUri: this.syncApiBase,
476
489
  relativePath: generatePath(ENDPOINT_REST_PROJECTS, id, PROJECT_ARCHIVE),
477
490
  apiToken: this.authToken,
491
+ customFetch: this.customFetch,
478
492
  requestId: requestId,
479
493
  });
480
494
  return validateProject(response.data);
@@ -493,6 +507,7 @@ export class TodoistApi {
493
507
  baseUri: this.syncApiBase,
494
508
  relativePath: generatePath(ENDPOINT_REST_PROJECTS, id, PROJECT_UNARCHIVE),
495
509
  apiToken: this.authToken,
510
+ customFetch: this.customFetch,
496
511
  requestId: requestId,
497
512
  });
498
513
  return validateProject(response.data);
@@ -511,6 +526,7 @@ export class TodoistApi {
511
526
  baseUri: this.syncApiBase,
512
527
  relativePath: generatePath(ENDPOINT_REST_PROJECTS, projectId, ENDPOINT_REST_PROJECT_COLLABORATORS),
513
528
  apiToken: this.authToken,
529
+ customFetch: this.customFetch,
514
530
  payload: args,
515
531
  });
516
532
  return {
@@ -530,6 +546,7 @@ export class TodoistApi {
530
546
  baseUri: this.syncApiBase,
531
547
  relativePath: ENDPOINT_REST_SECTIONS,
532
548
  apiToken: this.authToken,
549
+ customFetch: this.customFetch,
533
550
  payload: args,
534
551
  });
535
552
  return {
@@ -550,6 +567,7 @@ export class TodoistApi {
550
567
  baseUri: this.syncApiBase,
551
568
  relativePath: generatePath(ENDPOINT_REST_SECTIONS, id),
552
569
  apiToken: this.authToken,
570
+ customFetch: this.customFetch,
553
571
  });
554
572
  return validateSection(response.data);
555
573
  }
@@ -566,6 +584,7 @@ export class TodoistApi {
566
584
  baseUri: this.syncApiBase,
567
585
  relativePath: ENDPOINT_REST_SECTIONS,
568
586
  apiToken: this.authToken,
587
+ customFetch: this.customFetch,
569
588
  payload: args,
570
589
  requestId: requestId,
571
590
  });
@@ -586,6 +605,7 @@ export class TodoistApi {
586
605
  baseUri: this.syncApiBase,
587
606
  relativePath: generatePath(ENDPOINT_REST_SECTIONS, id),
588
607
  apiToken: this.authToken,
608
+ customFetch: this.customFetch,
589
609
  payload: args,
590
610
  requestId: requestId,
591
611
  });
@@ -605,6 +625,7 @@ export class TodoistApi {
605
625
  baseUri: this.syncApiBase,
606
626
  relativePath: generatePath(ENDPOINT_REST_SECTIONS, id),
607
627
  apiToken: this.authToken,
628
+ customFetch: this.customFetch,
608
629
  requestId: requestId,
609
630
  });
610
631
  return isSuccess(response);
@@ -622,6 +643,7 @@ export class TodoistApi {
622
643
  baseUri: this.syncApiBase,
623
644
  relativePath: generatePath(ENDPOINT_REST_LABELS, id),
624
645
  apiToken: this.authToken,
646
+ customFetch: this.customFetch,
625
647
  });
626
648
  return validateLabel(response.data);
627
649
  }
@@ -637,6 +659,7 @@ export class TodoistApi {
637
659
  baseUri: this.syncApiBase,
638
660
  relativePath: ENDPOINT_REST_LABELS,
639
661
  apiToken: this.authToken,
662
+ customFetch: this.customFetch,
640
663
  payload: args,
641
664
  });
642
665
  return {
@@ -657,6 +680,7 @@ export class TodoistApi {
657
680
  baseUri: this.syncApiBase,
658
681
  relativePath: ENDPOINT_REST_LABELS,
659
682
  apiToken: this.authToken,
683
+ customFetch: this.customFetch,
660
684
  payload: args,
661
685
  requestId: requestId,
662
686
  });
@@ -677,6 +701,7 @@ export class TodoistApi {
677
701
  baseUri: this.syncApiBase,
678
702
  relativePath: generatePath(ENDPOINT_REST_LABELS, id),
679
703
  apiToken: this.authToken,
704
+ customFetch: this.customFetch,
680
705
  payload: args,
681
706
  requestId: requestId,
682
707
  });
@@ -696,6 +721,7 @@ export class TodoistApi {
696
721
  baseUri: this.syncApiBase,
697
722
  relativePath: generatePath(ENDPOINT_REST_LABELS, id),
698
723
  apiToken: this.authToken,
724
+ customFetch: this.customFetch,
699
725
  requestId: requestId,
700
726
  });
701
727
  return isSuccess(response);
@@ -712,6 +738,7 @@ export class TodoistApi {
712
738
  baseUri: this.syncApiBase,
713
739
  relativePath: ENDPOINT_REST_LABELS_SHARED,
714
740
  apiToken: this.authToken,
741
+ customFetch: this.customFetch,
715
742
  payload: args,
716
743
  });
717
744
  return { results, nextCursor };
@@ -728,6 +755,7 @@ export class TodoistApi {
728
755
  baseUri: this.syncApiBase,
729
756
  relativePath: ENDPOINT_REST_LABELS_SHARED_RENAME,
730
757
  apiToken: this.authToken,
758
+ customFetch: this.customFetch,
731
759
  payload: args,
732
760
  });
733
761
  return isSuccess(response);
@@ -744,6 +772,7 @@ export class TodoistApi {
744
772
  baseUri: this.syncApiBase,
745
773
  relativePath: ENDPOINT_REST_LABELS_SHARED_REMOVE,
746
774
  apiToken: this.authToken,
775
+ customFetch: this.customFetch,
747
776
  payload: args,
748
777
  });
749
778
  return isSuccess(response);
@@ -760,6 +789,7 @@ export class TodoistApi {
760
789
  baseUri: this.syncApiBase,
761
790
  relativePath: ENDPOINT_REST_COMMENTS,
762
791
  apiToken: this.authToken,
792
+ customFetch: this.customFetch,
763
793
  payload: args,
764
794
  });
765
795
  return {
@@ -780,6 +810,7 @@ export class TodoistApi {
780
810
  baseUri: this.syncApiBase,
781
811
  relativePath: generatePath(ENDPOINT_REST_COMMENTS, id),
782
812
  apiToken: this.authToken,
813
+ customFetch: this.customFetch,
783
814
  });
784
815
  return validateComment(response.data);
785
816
  }
@@ -796,6 +827,7 @@ export class TodoistApi {
796
827
  baseUri: this.syncApiBase,
797
828
  relativePath: ENDPOINT_REST_COMMENTS,
798
829
  apiToken: this.authToken,
830
+ customFetch: this.customFetch,
799
831
  payload: args,
800
832
  requestId: requestId,
801
833
  });
@@ -816,6 +848,7 @@ export class TodoistApi {
816
848
  baseUri: this.syncApiBase,
817
849
  relativePath: generatePath(ENDPOINT_REST_COMMENTS, id),
818
850
  apiToken: this.authToken,
851
+ customFetch: this.customFetch,
819
852
  payload: args,
820
853
  requestId: requestId,
821
854
  });
@@ -835,6 +868,7 @@ export class TodoistApi {
835
868
  baseUri: this.syncApiBase,
836
869
  relativePath: generatePath(ENDPOINT_REST_COMMENTS, id),
837
870
  apiToken: this.authToken,
871
+ customFetch: this.customFetch,
838
872
  requestId: requestId,
839
873
  });
840
874
  return isSuccess(response);
@@ -850,6 +884,7 @@ export class TodoistApi {
850
884
  baseUri: this.syncApiBase,
851
885
  relativePath: ENDPOINT_REST_PRODUCTIVITY,
852
886
  apiToken: this.authToken,
887
+ customFetch: this.customFetch,
853
888
  });
854
889
  return validateProductivityStats(response.data);
855
890
  }
@@ -867,6 +902,7 @@ export class TodoistApi {
867
902
  baseUri: this.syncApiBase,
868
903
  relativePath: ENDPOINT_REST_ACTIVITIES,
869
904
  apiToken: this.authToken,
905
+ customFetch: this.customFetch,
870
906
  payload: processedArgs,
871
907
  });
872
908
  // Convert legacy API object types back to modern SDK types
@@ -929,6 +965,7 @@ export class TodoistApi {
929
965
  fileName: args.fileName,
930
966
  additionalFields: additionalFields,
931
967
  requestId: requestId,
968
+ customFetch: this.customFetch,
932
969
  });
933
970
  return validateAttachment(data);
934
971
  }
@@ -952,6 +989,7 @@ export class TodoistApi {
952
989
  baseUri: this.syncApiBase,
953
990
  relativePath: ENDPOINT_REST_UPLOADS,
954
991
  apiToken: this.authToken,
992
+ customFetch: this.customFetch,
955
993
  payload: args,
956
994
  requestId: requestId,
957
995
  });
@@ -971,6 +1009,7 @@ export class TodoistApi {
971
1009
  baseUri: this.syncApiBase,
972
1010
  relativePath: ENDPOINT_WORKSPACE_INVITATIONS,
973
1011
  apiToken: this.authToken,
1012
+ customFetch: this.customFetch,
974
1013
  payload: { workspace_id: args.workspaceId },
975
1014
  requestId: requestId,
976
1015
  });
@@ -992,6 +1031,7 @@ export class TodoistApi {
992
1031
  baseUri: this.syncApiBase,
993
1032
  relativePath: ENDPOINT_WORKSPACE_INVITATIONS_ALL,
994
1033
  apiToken: this.authToken,
1034
+ customFetch: this.customFetch,
995
1035
  payload: queryParams,
996
1036
  requestId: requestId,
997
1037
  });
@@ -1010,6 +1050,7 @@ export class TodoistApi {
1010
1050
  baseUri: this.syncApiBase,
1011
1051
  relativePath: ENDPOINT_WORKSPACE_INVITATIONS_DELETE,
1012
1052
  apiToken: this.authToken,
1053
+ customFetch: this.customFetch,
1013
1054
  payload: {
1014
1055
  workspace_id: args.workspaceId,
1015
1056
  user_email: args.userEmail,
@@ -1031,6 +1072,7 @@ export class TodoistApi {
1031
1072
  baseUri: this.syncApiBase,
1032
1073
  relativePath: getWorkspaceInvitationAcceptEndpoint(args.inviteCode),
1033
1074
  apiToken: this.authToken,
1075
+ customFetch: this.customFetch,
1034
1076
  requestId: requestId,
1035
1077
  });
1036
1078
  return validateWorkspaceInvitation(response.data);
@@ -1048,6 +1090,7 @@ export class TodoistApi {
1048
1090
  baseUri: this.syncApiBase,
1049
1091
  relativePath: getWorkspaceInvitationRejectEndpoint(args.inviteCode),
1050
1092
  apiToken: this.authToken,
1093
+ customFetch: this.customFetch,
1051
1094
  requestId: requestId,
1052
1095
  });
1053
1096
  return validateWorkspaceInvitation(response.data);
@@ -1065,6 +1108,7 @@ export class TodoistApi {
1065
1108
  baseUri: this.syncApiBase,
1066
1109
  relativePath: ENDPOINT_WORKSPACE_JOIN,
1067
1110
  apiToken: this.authToken,
1111
+ customFetch: this.customFetch,
1068
1112
  payload: {
1069
1113
  invite_code: args.inviteCode,
1070
1114
  workspace_id: args.workspaceId,
@@ -1094,6 +1138,7 @@ export class TodoistApi {
1094
1138
  delete: true,
1095
1139
  },
1096
1140
  requestId: requestId,
1141
+ customFetch: this.customFetch,
1097
1142
  });
1098
1143
  return data;
1099
1144
  }
@@ -1115,6 +1160,7 @@ export class TodoistApi {
1115
1160
  fileName: args.fileName,
1116
1161
  additionalFields: additionalFields,
1117
1162
  requestId: requestId,
1163
+ customFetch: this.customFetch,
1118
1164
  });
1119
1165
  return data;
1120
1166
  }
@@ -1131,6 +1177,7 @@ export class TodoistApi {
1131
1177
  baseUri: this.syncApiBase,
1132
1178
  relativePath: ENDPOINT_WORKSPACE_PLAN_DETAILS,
1133
1179
  apiToken: this.authToken,
1180
+ customFetch: this.customFetch,
1134
1181
  payload: { workspace_id: args.workspaceId },
1135
1182
  requestId: requestId,
1136
1183
  });
@@ -1159,13 +1206,14 @@ export class TodoistApi {
1159
1206
  baseUri: this.syncApiBase,
1160
1207
  relativePath: ENDPOINT_WORKSPACE_USERS,
1161
1208
  apiToken: this.authToken,
1209
+ customFetch: this.customFetch,
1162
1210
  payload: queryParams,
1163
1211
  requestId: requestId,
1164
1212
  });
1165
1213
  return {
1166
- hasMore: response.data.has_more || false,
1167
- nextCursor: response.data.next_cursor,
1168
- workspaceUsers: validateWorkspaceUserArray(response.data.workspace_users || []),
1214
+ hasMore: response.data.hasMore || false,
1215
+ nextCursor: response.data.nextCursor,
1216
+ workspaceUsers: validateWorkspaceUserArray(response.data.workspaceUsers || []),
1169
1217
  };
1170
1218
  }
1171
1219
  /**
@@ -1189,6 +1237,7 @@ export class TodoistApi {
1189
1237
  baseUri: this.syncApiBase,
1190
1238
  relativePath: getWorkspaceActiveProjectsEndpoint(args.workspaceId),
1191
1239
  apiToken: this.authToken,
1240
+ customFetch: this.customFetch,
1192
1241
  payload: queryParams,
1193
1242
  requestId: requestId,
1194
1243
  });
@@ -1219,6 +1268,7 @@ export class TodoistApi {
1219
1268
  baseUri: this.syncApiBase,
1220
1269
  relativePath: getWorkspaceArchivedProjectsEndpoint(args.workspaceId),
1221
1270
  apiToken: this.authToken,
1271
+ customFetch: this.customFetch,
1222
1272
  payload: queryParams,
1223
1273
  requestId: requestId,
1224
1274
  });
@@ -358,9 +358,9 @@ export const WorkspacePlanDetailsSchema = z.object({
358
358
  hasBillingPortalSwitchToAnnual: z.boolean(),
359
359
  });
360
360
  export const JoinWorkspaceResultSchema = z.object({
361
- custom_sorting_applied: z.boolean(),
362
- project_sort_preference: z.string(),
361
+ customSortingApplied: z.boolean(),
362
+ projectSortPreference: z.string(),
363
363
  role: WorkspaceRoleSchema,
364
- user_id: z.string(),
365
- workspace_id: z.string(),
364
+ userId: z.string(),
365
+ workspaceId: z.string(),
366
366
  });
@@ -1,3 +1,4 @@
1
1
  export * from './entities.js';
2
2
  export * from './errors.js';
3
3
  export * from './requests.js';
4
+ export * from './http.js';
@@ -60,11 +60,26 @@ function createTimeoutSignal(timeoutMs, existingSignal) {
60
60
  });
61
61
  return controller.signal;
62
62
  }
63
+ /**
64
+ * Converts native fetch Response to CustomFetchResponse for consistency
65
+ */
66
+ function convertResponseToCustomFetch(response) {
67
+ // Clone the response so we can read it multiple times (if clone method exists)
68
+ const clonedResponse = response.clone ? response.clone() : response;
69
+ return {
70
+ ok: response.ok,
71
+ status: response.status,
72
+ statusText: response.statusText,
73
+ headers: headersToObject(response.headers),
74
+ text: () => clonedResponse.text(),
75
+ json: () => response.json(),
76
+ };
77
+ }
63
78
  /**
64
79
  * Performs a fetch request with retry logic and timeout support
65
80
  */
66
81
  export async function fetchWithRetry(args) {
67
- const { url, options = {}, retryConfig = {} } = args;
82
+ const { url, options = {}, retryConfig = {}, customFetch } = args;
68
83
  const config = Object.assign(Object.assign({}, DEFAULT_RETRY_CONFIG), retryConfig);
69
84
  const { timeout, signal: userSignal } = options, fetchOptions = __rest(options, ["timeout", "signal"]);
70
85
  let lastError;
@@ -75,22 +90,30 @@ export async function fetchWithRetry(args) {
75
90
  if (timeout && timeout > 0) {
76
91
  requestSignal = createTimeoutSignal(timeout, requestSignal);
77
92
  }
78
- const response = await fetch(url, Object.assign(Object.assign({}, fetchOptions), { signal: requestSignal }));
93
+ // Use custom fetch or native fetch
94
+ let fetchResponse;
95
+ if (customFetch) {
96
+ fetchResponse = await customFetch(url, Object.assign(Object.assign({}, fetchOptions), { signal: requestSignal, timeout }));
97
+ }
98
+ else {
99
+ const nativeResponse = await fetch(url, Object.assign(Object.assign({}, fetchOptions), { signal: requestSignal }));
100
+ fetchResponse = convertResponseToCustomFetch(nativeResponse);
101
+ }
79
102
  // Check if the response is successful
80
- if (!response.ok) {
81
- const errorMessage = `HTTP ${response.status}: ${response.statusText}`;
103
+ if (!fetchResponse.ok) {
104
+ const errorMessage = `HTTP ${fetchResponse.status}: ${fetchResponse.statusText}`;
82
105
  const error = new Error(errorMessage);
83
- error.status = response.status;
84
- error.statusText = response.statusText;
106
+ error.status = fetchResponse.status;
107
+ error.statusText = fetchResponse.statusText;
85
108
  error.response = {
86
109
  data: undefined, // Will be set below if we can parse the response
87
- status: response.status,
88
- statusText: response.statusText,
89
- headers: headersToObject(response.headers),
110
+ status: fetchResponse.status,
111
+ statusText: fetchResponse.statusText,
112
+ headers: fetchResponse.headers,
90
113
  };
91
114
  // Try to get response body for error details
92
115
  try {
93
- const responseText = await response.text();
116
+ const responseText = await fetchResponse.text();
94
117
  let responseData;
95
118
  try {
96
119
  responseData = responseText ? JSON.parse(responseText) : undefined;
@@ -107,7 +130,7 @@ export async function fetchWithRetry(args) {
107
130
  throw error;
108
131
  }
109
132
  // Parse response
110
- const responseText = await response.text();
133
+ const responseText = await fetchResponse.text();
111
134
  let data;
112
135
  try {
113
136
  data = responseText ? JSON.parse(responseText) : undefined;
@@ -118,9 +141,9 @@ export async function fetchWithRetry(args) {
118
141
  }
119
142
  return {
120
143
  data,
121
- status: response.status,
122
- statusText: response.statusText,
123
- headers: headersToObject(response.headers),
144
+ status: fetchResponse.status,
145
+ statusText: fetchResponse.statusText,
146
+ headers: fetchResponse.headers,
124
147
  };
125
148
  }
126
149
  catch (error) {
@@ -65,7 +65,7 @@ function getContentTypeFromFileName(fileName) {
65
65
  * ```
66
66
  */
67
67
  export async function uploadMultipartFile(args) {
68
- const { baseUrl, authToken, endpoint, file, fileName, additionalFields, requestId } = args;
68
+ const { baseUrl, authToken, endpoint, file, fileName, additionalFields, requestId, customFetch, } = args;
69
69
  const form = new FormData();
70
70
  // Determine file type and add to form data
71
71
  if (typeof file === 'string') {
@@ -115,6 +115,7 @@ export async function uploadMultipartFile(args) {
115
115
  headers,
116
116
  timeout: 30000, // 30 second timeout for file uploads
117
117
  },
118
+ customFetch,
118
119
  });
119
120
  return response.data;
120
121
  }
@@ -1,3 +1,11 @@
1
+ import { CustomFetch } from './types/http';
2
+ /**
3
+ * Options for authentication functions
4
+ */
5
+ export type AuthOptions = {
6
+ baseUrl?: string;
7
+ customFetch?: CustomFetch;
8
+ };
1
9
  /**
2
10
  * Permission scopes that can be requested during OAuth2 authorization.
3
11
  * @see {@link https://todoist.com/api/v1/docs#tag/Authorization}
@@ -89,7 +97,11 @@ export declare function getAuthorizationUrl({ clientId, permissions, state, base
89
97
  * @returns The access token response
90
98
  * @throws {@link TodoistRequestError} If the token exchange fails
91
99
  */
100
+ /**
101
+ * @deprecated Use options object instead: getAuthToken(args, { baseUrl, customFetch })
102
+ */
92
103
  export declare function getAuthToken(args: AuthTokenRequestArgs, baseUrl?: string): Promise<AuthTokenResponse>;
104
+ export declare function getAuthToken(args: AuthTokenRequestArgs, options?: AuthOptions): Promise<AuthTokenResponse>;
93
105
  /**
94
106
  * Revokes an access token, making it invalid for future use.
95
107
  *
@@ -106,7 +118,11 @@ export declare function getAuthToken(args: AuthTokenRequestArgs, baseUrl?: strin
106
118
  * @returns True if revocation was successful
107
119
  * @see https://todoist.com/api/v1/docs#tag/Authorization/operation/revoke_access_token_api_api_v1_access_tokens_delete
108
120
  */
121
+ /**
122
+ * @deprecated Use options object instead: revokeAuthToken(args, { baseUrl, customFetch })
123
+ */
109
124
  export declare function revokeAuthToken(args: RevokeAuthTokenRequestArgs, baseUrl?: string): Promise<boolean>;
125
+ export declare function revokeAuthToken(args: RevokeAuthTokenRequestArgs, options?: AuthOptions): Promise<boolean>;
110
126
  /**
111
127
  * Revokes a token using the RFC 7009 OAuth 2.0 Token Revocation standard.
112
128
  *
@@ -126,4 +142,8 @@ export declare function revokeAuthToken(args: RevokeAuthTokenRequestArgs, baseUr
126
142
  * @see https://datatracker.ietf.org/doc/html/rfc7009
127
143
  * @see https://todoist.com/api/v1/docs#tag/Authorization
128
144
  */
145
+ /**
146
+ * @deprecated Use options object instead: revokeToken(args, { baseUrl, customFetch })
147
+ */
129
148
  export declare function revokeToken(args: RevokeTokenRequestArgs, baseUrl?: string): Promise<boolean>;
149
+ export declare function revokeToken(args: RevokeTokenRequestArgs, options?: AuthOptions): Promise<boolean>;