@todoist/sdk 0.0.1 → 18.0.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 (335) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +293 -29
  3. package/dist/esm/authentication.js +307 -0
  4. package/dist/esm/clients/activity-client.js +60 -0
  5. package/dist/esm/clients/app-client.js +366 -0
  6. package/dist/esm/clients/backup-client.js +47 -0
  7. package/dist/esm/clients/base-client.js +29 -0
  8. package/dist/esm/clients/billing-client.js +160 -0
  9. package/dist/esm/clients/comment-client.js +92 -0
  10. package/dist/esm/clients/email-client.js +41 -0
  11. package/dist/esm/clients/filter-client.js +26 -0
  12. package/dist/esm/clients/folder-client.js +80 -0
  13. package/dist/esm/clients/id-mapping-client.js +37 -0
  14. package/dist/esm/clients/insights-client.js +89 -0
  15. package/dist/esm/clients/label-client.js +131 -0
  16. package/dist/esm/clients/productivity-client.js +24 -0
  17. package/dist/esm/clients/project-client.js +284 -0
  18. package/dist/esm/clients/reminder-client.js +204 -0
  19. package/dist/esm/clients/section-client.js +121 -0
  20. package/dist/esm/clients/task-client.js +264 -0
  21. package/dist/esm/clients/template-client.js +89 -0
  22. package/dist/esm/clients/ui-extension-client.js +110 -0
  23. package/dist/esm/clients/upload-client.js +153 -0
  24. package/dist/esm/clients/view-options-client.js +21 -0
  25. package/dist/esm/clients/workspace-client.js +384 -0
  26. package/dist/esm/consts/endpoints.js +193 -0
  27. package/dist/esm/index.js +5 -0
  28. package/dist/esm/test-utils/asserts.js +8 -0
  29. package/dist/esm/test-utils/msw-setup.js +88 -0
  30. package/dist/esm/test-utils/obsidian-fetch-adapter.js +51 -0
  31. package/dist/esm/test-utils/test-defaults.js +367 -0
  32. package/dist/esm/test-utils/transport-seam.js +15 -0
  33. package/dist/esm/test-utils/webhook-fixtures.js +26 -0
  34. package/dist/esm/todoist-api.js +1757 -0
  35. package/dist/esm/transport/fetch-with-retry.js +216 -0
  36. package/dist/esm/transport/http-client.js +140 -0
  37. package/dist/esm/transport/http-dispatcher.js +158 -0
  38. package/dist/esm/transport/sync-request.js +106 -0
  39. package/dist/esm/types/activity/index.js +2 -0
  40. package/dist/esm/types/activity/requests.js +1 -0
  41. package/dist/esm/types/activity/types.js +45 -0
  42. package/dist/esm/types/apps/index.js +2 -0
  43. package/dist/esm/types/apps/requests.js +1 -0
  44. package/dist/esm/types/apps/types.js +249 -0
  45. package/dist/esm/types/backups/index.js +2 -0
  46. package/dist/esm/types/backups/requests.js +1 -0
  47. package/dist/esm/types/backups/types.js +5 -0
  48. package/dist/esm/types/billing/index.js +2 -0
  49. package/dist/esm/types/billing/requests.js +1 -0
  50. package/dist/esm/types/billing/types.js +89 -0
  51. package/dist/esm/types/comments/index.js +2 -0
  52. package/dist/esm/types/comments/requests.js +1 -0
  53. package/dist/esm/types/comments/types.js +57 -0
  54. package/dist/esm/types/common.js +7 -0
  55. package/dist/esm/types/emails/index.js +1 -0
  56. package/dist/esm/types/emails/requests.js +2 -0
  57. package/dist/esm/types/errors.js +41 -0
  58. package/dist/esm/types/folders/index.js +1 -0
  59. package/dist/esm/types/folders/requests.js +1 -0
  60. package/dist/esm/types/http.js +20 -0
  61. package/dist/esm/types/id-mappings/index.js +2 -0
  62. package/dist/esm/types/id-mappings/requests.js +17 -0
  63. package/dist/esm/types/id-mappings/types.js +9 -0
  64. package/dist/esm/types/index.js +24 -0
  65. package/dist/esm/types/insights/index.js +2 -0
  66. package/dist/esm/types/insights/requests.js +1 -0
  67. package/dist/esm/types/insights/types.js +77 -0
  68. package/dist/esm/types/labels/index.js +2 -0
  69. package/dist/esm/types/labels/requests.js +1 -0
  70. package/dist/esm/types/labels/types.js +8 -0
  71. package/dist/esm/types/productivity/index.js +1 -0
  72. package/dist/esm/types/productivity/types.js +65 -0
  73. package/dist/esm/types/projects/index.js +2 -0
  74. package/dist/esm/types/projects/requests.js +1 -0
  75. package/dist/esm/types/projects/types.js +65 -0
  76. package/dist/esm/types/reminders/index.js +1 -0
  77. package/dist/esm/types/reminders/requests.js +92 -0
  78. package/dist/esm/types/sections/index.js +2 -0
  79. package/dist/esm/types/sections/requests.js +1 -0
  80. package/dist/esm/types/sections/types.js +23 -0
  81. package/dist/esm/types/sync/commands/calendars.js +1 -0
  82. package/dist/esm/types/sync/commands/filters.js +1 -0
  83. package/dist/esm/types/sync/commands/folders.js +1 -0
  84. package/dist/esm/types/sync/commands/index.js +18 -0
  85. package/dist/esm/types/sync/commands/labels.js +2 -0
  86. package/dist/esm/types/sync/commands/notes.js +1 -0
  87. package/dist/esm/types/sync/commands/notifications.js +1 -0
  88. package/dist/esm/types/sync/commands/others.js +1 -0
  89. package/dist/esm/types/sync/commands/project-view-options.js +1 -0
  90. package/dist/esm/types/sync/commands/projects.js +1 -0
  91. package/dist/esm/types/sync/commands/reminders.js +1 -0
  92. package/dist/esm/types/sync/commands/sections.js +1 -0
  93. package/dist/esm/types/sync/commands/shared.js +16 -0
  94. package/dist/esm/types/sync/commands/sharing.js +1 -0
  95. package/dist/esm/types/sync/commands/tasks.js +1 -0
  96. package/dist/esm/types/sync/commands/view-options.js +1 -0
  97. package/dist/esm/types/sync/commands/workspace-filters.js +1 -0
  98. package/dist/esm/types/sync/commands/workspaces.js +1 -0
  99. package/dist/esm/types/sync/index.js +6 -0
  100. package/dist/esm/types/sync/request.js +1 -0
  101. package/dist/esm/types/sync/resource-types.js +36 -0
  102. package/dist/esm/types/sync/resources/calendars.js +24 -0
  103. package/dist/esm/types/sync/resources/collaborators.js +21 -0
  104. package/dist/esm/types/sync/resources/completed-info.js +21 -0
  105. package/dist/esm/types/sync/resources/filters.js +12 -0
  106. package/dist/esm/types/sync/resources/folders.js +10 -0
  107. package/dist/esm/types/sync/resources/index.js +18 -0
  108. package/dist/esm/types/sync/resources/labels.js +7 -0
  109. package/dist/esm/types/sync/resources/live-notifications.js +35 -0
  110. package/dist/esm/types/sync/resources/notes.js +18 -0
  111. package/dist/esm/types/sync/resources/reminders.js +58 -0
  112. package/dist/esm/types/sync/resources/suggestions.js +33 -0
  113. package/dist/esm/types/sync/resources/tooltips.js +5 -0
  114. package/dist/esm/types/sync/resources/user-plan-limits.js +21 -0
  115. package/dist/esm/types/sync/resources/user-settings.js +41 -0
  116. package/dist/esm/types/sync/resources/user-status.js +22 -0
  117. package/dist/esm/types/sync/resources/user.js +109 -0
  118. package/dist/esm/types/sync/resources/view-options.js +128 -0
  119. package/dist/esm/types/sync/resources/workspace-filters.js +17 -0
  120. package/dist/esm/types/sync/resources/workspaces.js +69 -0
  121. package/dist/esm/types/sync/response.js +57 -0
  122. package/dist/esm/types/sync/user-preferences.js +68 -0
  123. package/dist/esm/types/tasks/index.js +2 -0
  124. package/dist/esm/types/tasks/requests.js +1 -0
  125. package/dist/esm/types/tasks/types.js +56 -0
  126. package/dist/esm/types/templates/index.js +1 -0
  127. package/dist/esm/types/templates/requests.js +1 -0
  128. package/dist/esm/types/ui-extensions/index.js +2 -0
  129. package/dist/esm/types/ui-extensions/requests.js +1 -0
  130. package/dist/esm/types/ui-extensions/types.js +74 -0
  131. package/dist/esm/types/uploads/index.js +1 -0
  132. package/dist/esm/types/uploads/requests.js +1 -0
  133. package/dist/esm/types/users/index.js +1 -0
  134. package/dist/esm/types/users/types.js +49 -0
  135. package/dist/esm/types/webhooks/comments.js +93 -0
  136. package/dist/esm/types/webhooks/envelope.js +23 -0
  137. package/dist/esm/types/webhooks/filters.js +27 -0
  138. package/dist/esm/types/webhooks/index.js +9 -0
  139. package/dist/esm/types/webhooks/labels.js +51 -0
  140. package/dist/esm/types/webhooks/projects.js +33 -0
  141. package/dist/esm/types/webhooks/reminders.js +32 -0
  142. package/dist/esm/types/webhooks/sections.js +50 -0
  143. package/dist/esm/types/webhooks/tasks.js +65 -0
  144. package/dist/esm/types/webhooks/types.js +52 -0
  145. package/dist/esm/types/workspaces/index.js +2 -0
  146. package/dist/esm/types/workspaces/requests.js +1 -0
  147. package/dist/esm/types/workspaces/types.js +151 -0
  148. package/dist/esm/utils/activity-helpers.js +53 -0
  149. package/dist/esm/utils/case-conversion.js +84 -0
  150. package/dist/esm/utils/colors.js +145 -0
  151. package/dist/esm/utils/file-response.js +29 -0
  152. package/dist/esm/utils/filter-queries.js +231 -0
  153. package/dist/esm/utils/filter-query-definitions.js +2789 -0
  154. package/dist/esm/utils/headers.js +42 -0
  155. package/dist/esm/utils/index.js +11 -0
  156. package/dist/esm/utils/multipart-upload.js +267 -0
  157. package/dist/esm/utils/person-names.js +61 -0
  158. package/dist/esm/utils/processing-helpers.js +12 -0
  159. package/dist/esm/utils/project-helpers.js +29 -0
  160. package/dist/esm/utils/request-helpers.js +43 -0
  161. package/dist/esm/utils/sanitization.js +131 -0
  162. package/dist/esm/utils/sync-helpers.js +19 -0
  163. package/dist/esm/utils/task-sorting.js +280 -0
  164. package/dist/esm/utils/uncompletable-helpers.js +54 -0
  165. package/dist/esm/utils/url-helpers.js +196 -0
  166. package/dist/esm/utils/validators.js +102 -0
  167. package/dist/esm/utils/view-options.js +15 -0
  168. package/dist/esm/utils/webhook-parser.js +56 -0
  169. package/dist/types/authentication.d.ts +262 -0
  170. package/dist/types/clients/activity-client.d.ts +12 -0
  171. package/dist/types/clients/app-client.d.ts +37 -0
  172. package/dist/types/clients/backup-client.d.ts +14 -0
  173. package/dist/types/clients/base-client.d.ts +23 -0
  174. package/dist/types/clients/billing-client.d.ts +22 -0
  175. package/dist/types/clients/comment-client.d.ts +15 -0
  176. package/dist/types/clients/email-client.d.ts +13 -0
  177. package/dist/types/clients/filter-client.d.ts +10 -0
  178. package/dist/types/clients/folder-client.d.ts +16 -0
  179. package/dist/types/clients/id-mapping-client.d.ts +13 -0
  180. package/dist/types/clients/insights-client.d.ts +18 -0
  181. package/dist/types/clients/label-client.d.ts +19 -0
  182. package/dist/types/clients/productivity-client.d.ts +12 -0
  183. package/dist/types/clients/project-client.d.ts +22 -0
  184. package/dist/types/clients/reminder-client.d.ts +25 -0
  185. package/dist/types/clients/section-client.d.ts +18 -0
  186. package/dist/types/clients/task-client.d.ts +25 -0
  187. package/dist/types/clients/template-client.d.ts +17 -0
  188. package/dist/types/clients/ui-extension-client.d.ts +18 -0
  189. package/dist/types/clients/upload-client.d.ts +17 -0
  190. package/dist/types/clients/view-options-client.d.ts +8 -0
  191. package/dist/types/clients/workspace-client.d.ts +43 -0
  192. package/dist/types/consts/endpoints.d.ts +120 -0
  193. package/dist/types/index.d.ts +6 -0
  194. package/dist/types/test-utils/asserts.d.ts +1 -0
  195. package/dist/types/test-utils/msw-setup.d.ts +33 -0
  196. package/dist/types/test-utils/obsidian-fetch-adapter.d.ts +29 -0
  197. package/dist/types/test-utils/test-defaults.d.ts +286 -0
  198. package/dist/types/test-utils/transport-seam.d.ts +1 -0
  199. package/dist/types/test-utils/webhook-fixtures.d.ts +13 -0
  200. package/dist/types/todoist-api.d.ts +1437 -0
  201. package/dist/types/transport/fetch-with-retry.d.ts +12 -0
  202. package/dist/types/transport/http-client.d.ts +25 -0
  203. package/dist/types/transport/http-dispatcher.d.ts +65 -0
  204. package/dist/types/transport/sync-request.d.ts +31 -0
  205. package/dist/types/types/activity/index.d.ts +2 -0
  206. package/dist/types/types/activity/requests.d.ts +85 -0
  207. package/dist/types/types/activity/types.d.ts +56 -0
  208. package/dist/types/types/apps/index.d.ts +2 -0
  209. package/dist/types/types/apps/requests.d.ts +89 -0
  210. package/dist/types/types/apps/types.d.ts +418 -0
  211. package/dist/types/types/backups/index.d.ts +2 -0
  212. package/dist/types/types/backups/requests.d.ts +16 -0
  213. package/dist/types/types/backups/types.d.ts +7 -0
  214. package/dist/types/types/billing/index.d.ts +2 -0
  215. package/dist/types/types/billing/requests.d.ts +62 -0
  216. package/dist/types/types/billing/types.d.ts +207 -0
  217. package/dist/types/types/comments/index.d.ts +2 -0
  218. package/dist/types/types/comments/requests.d.ts +64 -0
  219. package/dist/types/types/comments/types.d.ts +141 -0
  220. package/dist/types/types/common.d.ts +13 -0
  221. package/dist/types/types/emails/index.d.ts +1 -0
  222. package/dist/types/types/emails/requests.d.ts +30 -0
  223. package/dist/types/types/errors.d.ts +29 -0
  224. package/dist/types/types/folders/index.d.ts +1 -0
  225. package/dist/types/types/folders/requests.d.ts +20 -0
  226. package/dist/types/types/http.d.ts +104 -0
  227. package/dist/types/types/id-mappings/index.d.ts +2 -0
  228. package/dist/types/types/id-mappings/requests.d.ts +28 -0
  229. package/dist/types/types/id-mappings/types.d.ts +13 -0
  230. package/dist/types/types/index.d.ts +24 -0
  231. package/dist/types/types/insights/index.d.ts +2 -0
  232. package/dist/types/types/insights/requests.d.ts +26 -0
  233. package/dist/types/types/insights/types.d.ts +167 -0
  234. package/dist/types/types/labels/index.d.ts +2 -0
  235. package/dist/types/types/labels/requests.d.ts +75 -0
  236. package/dist/types/types/labels/types.d.ts +13 -0
  237. package/dist/types/types/productivity/index.d.ts +1 -0
  238. package/dist/types/types/productivity/types.d.ts +115 -0
  239. package/dist/types/types/projects/index.d.ts +2 -0
  240. package/dist/types/types/projects/requests.d.ts +206 -0
  241. package/dist/types/types/projects/types.d.ts +286 -0
  242. package/dist/types/types/reminders/index.d.ts +1 -0
  243. package/dist/types/types/reminders/requests.d.ts +205 -0
  244. package/dist/types/types/sections/index.d.ts +2 -0
  245. package/dist/types/types/sections/requests.d.ts +49 -0
  246. package/dist/types/types/sections/types.d.ts +70 -0
  247. package/dist/types/types/sync/commands/calendars.d.ts +14 -0
  248. package/dist/types/types/sync/commands/filters.d.ts +24 -0
  249. package/dist/types/types/sync/commands/folders.d.ts +20 -0
  250. package/dist/types/types/sync/commands/index.d.ts +167 -0
  251. package/dist/types/types/sync/commands/labels.d.ts +32 -0
  252. package/dist/types/types/sync/commands/notes.d.ts +27 -0
  253. package/dist/types/types/sync/commands/notifications.d.ts +10 -0
  254. package/dist/types/types/sync/commands/others.d.ts +61 -0
  255. package/dist/types/types/sync/commands/project-view-options.d.ts +13 -0
  256. package/dist/types/types/sync/commands/projects.d.ts +96 -0
  257. package/dist/types/types/sync/commands/reminders.d.ts +51 -0
  258. package/dist/types/types/sync/commands/sections.d.ts +36 -0
  259. package/dist/types/types/sync/commands/shared.d.ts +51 -0
  260. package/dist/types/types/sync/commands/sharing.d.ts +16 -0
  261. package/dist/types/types/sync/commands/tasks.d.ts +78 -0
  262. package/dist/types/types/sync/commands/view-options.d.ts +18 -0
  263. package/dist/types/types/sync/commands/workspace-filters.d.ts +25 -0
  264. package/dist/types/types/sync/commands/workspaces.d.ts +82 -0
  265. package/dist/types/types/sync/index.d.ts +6 -0
  266. package/dist/types/types/sync/request.d.ts +7 -0
  267. package/dist/types/types/sync/resource-types.d.ts +5 -0
  268. package/dist/types/types/sync/resources/calendars.d.ts +38 -0
  269. package/dist/types/types/sync/resources/collaborators.d.ts +45 -0
  270. package/dist/types/types/sync/resources/completed-info.d.ts +33 -0
  271. package/dist/types/types/sync/resources/filters.d.ts +13 -0
  272. package/dist/types/types/sync/resources/folders.d.ts +11 -0
  273. package/dist/types/types/sync/resources/index.d.ts +18 -0
  274. package/dist/types/types/sync/resources/labels.d.ts +12 -0
  275. package/dist/types/types/sync/resources/live-notifications.d.ts +32 -0
  276. package/dist/types/types/sync/resources/notes.d.ts +34 -0
  277. package/dist/types/types/sync/resources/reminders.d.ts +213 -0
  278. package/dist/types/types/sync/resources/suggestions.d.ts +96 -0
  279. package/dist/types/types/sync/resources/tooltips.d.ts +6 -0
  280. package/dist/types/types/sync/resources/user-plan-limits.d.ts +47 -0
  281. package/dist/types/types/sync/resources/user-settings.d.ts +40 -0
  282. package/dist/types/types/sync/resources/user-status.d.ts +46 -0
  283. package/dist/types/types/sync/resources/user.d.ts +151 -0
  284. package/dist/types/types/sync/resources/view-options.d.ts +157 -0
  285. package/dist/types/types/sync/resources/workspace-filters.d.ts +18 -0
  286. package/dist/types/types/sync/resources/workspaces.d.ts +142 -0
  287. package/dist/types/types/sync/response.d.ts +930 -0
  288. package/dist/types/types/sync/user-preferences.d.ts +39 -0
  289. package/dist/types/types/tasks/index.d.ts +2 -0
  290. package/dist/types/types/tasks/requests.d.ts +197 -0
  291. package/dist/types/types/tasks/types.d.ts +162 -0
  292. package/dist/types/types/templates/index.d.ts +1 -0
  293. package/dist/types/types/templates/requests.d.ts +92 -0
  294. package/dist/types/types/ui-extensions/index.d.ts +2 -0
  295. package/dist/types/types/ui-extensions/requests.d.ts +108 -0
  296. package/dist/types/types/ui-extensions/types.d.ts +185 -0
  297. package/dist/types/types/uploads/index.d.ts +1 -0
  298. package/dist/types/types/uploads/requests.d.ts +32 -0
  299. package/dist/types/types/users/index.d.ts +1 -0
  300. package/dist/types/types/users/types.d.ts +67 -0
  301. package/dist/types/types/webhooks/comments.d.ts +1600 -0
  302. package/dist/types/types/webhooks/envelope.d.ts +34 -0
  303. package/dist/types/types/webhooks/filters.d.ts +99 -0
  304. package/dist/types/types/webhooks/index.d.ts +9 -0
  305. package/dist/types/types/webhooks/labels.d.ts +160 -0
  306. package/dist/types/types/webhooks/projects.d.ts +365 -0
  307. package/dist/types/types/webhooks/reminders.d.ts +82 -0
  308. package/dist/types/types/webhooks/sections.d.ts +372 -0
  309. package/dist/types/types/webhooks/tasks.d.ts +981 -0
  310. package/dist/types/types/webhooks/types.d.ts +2606 -0
  311. package/dist/types/types/workspaces/index.d.ts +2 -0
  312. package/dist/types/types/workspaces/requests.d.ts +260 -0
  313. package/dist/types/types/workspaces/types.d.ts +266 -0
  314. package/dist/types/utils/activity-helpers.d.ts +28 -0
  315. package/dist/types/utils/case-conversion.d.ts +20 -0
  316. package/dist/types/utils/colors.d.ts +201 -0
  317. package/dist/types/utils/file-response.d.ts +15 -0
  318. package/dist/types/utils/filter-queries.d.ts +42 -0
  319. package/dist/types/utils/filter-query-definitions.d.ts +27 -0
  320. package/dist/types/utils/headers.d.ts +20 -0
  321. package/dist/types/utils/index.d.ts +15 -0
  322. package/dist/types/utils/multipart-upload.d.ts +58 -0
  323. package/dist/types/utils/person-names.d.ts +52 -0
  324. package/dist/types/utils/processing-helpers.d.ts +1 -0
  325. package/dist/types/utils/project-helpers.d.ts +21 -0
  326. package/dist/types/utils/request-helpers.d.ts +33 -0
  327. package/dist/types/utils/sanitization.d.ts +52 -0
  328. package/dist/types/utils/sync-helpers.d.ts +17 -0
  329. package/dist/types/utils/task-sorting.d.ts +46 -0
  330. package/dist/types/utils/uncompletable-helpers.d.ts +30 -0
  331. package/dist/types/utils/url-helpers.d.ts +120 -0
  332. package/dist/types/utils/validators.d.ts +1917 -0
  333. package/dist/types/utils/view-options.d.ts +11 -0
  334. package/dist/types/utils/webhook-parser.d.ts +48 -0
  335. package/package.json +87 -8
@@ -0,0 +1,264 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { v4 as uuidv4 } from 'uuid';
13
+ import { ENDPOINT_REST_TASKS, ENDPOINT_REST_TASKS_COMPLETED, ENDPOINT_REST_TASKS_COMPLETED_BY_COMPLETION_DATE, ENDPOINT_REST_TASKS_COMPLETED_BY_DUE_DATE, ENDPOINT_REST_TASKS_COMPLETED_SEARCH, ENDPOINT_REST_TASKS_FILTER, ENDPOINT_REST_TASK_CLOSE, ENDPOINT_REST_TASK_MOVE, ENDPOINT_REST_TASK_REOPEN, ENDPOINT_SYNC_QUICK_ADD, } from '../consts/endpoints.js';
14
+ import { isSuccess, request } from '../transport/http-client.js';
15
+ import { performSyncRequest } from '../transport/sync-request.js';
16
+ import { TodoistRequestError } from '../types/index.js';
17
+ import { generatePath, spreadIfDefined } from '../utils/request-helpers.js';
18
+ import { processTaskContent } from '../utils/uncompletable-helpers.js';
19
+ import { IdSchema, validateTask, validateTaskArray } from '../utils/validators.js';
20
+ import { BaseClient } from './base-client.js';
21
+ const MAX_COMMAND_COUNT = 100;
22
+ /**
23
+ * Internal sub-client handling all task-domain endpoints.
24
+ *
25
+ * Instantiated by `TodoistApi`; every public task method on `TodoistApi`
26
+ * delegates here. See `todoist-api.ts` for the user-facing JSDoc.
27
+ */
28
+ export class TaskClient extends BaseClient {
29
+ async getTask(id) {
30
+ IdSchema.parse(id);
31
+ const response = await request({
32
+ httpMethod: 'GET',
33
+ baseUri: this.syncApiBase,
34
+ relativePath: generatePath(ENDPOINT_REST_TASKS, id),
35
+ apiToken: this.authToken,
36
+ customFetch: this.customFetch,
37
+ customHeaders: this.customHeaders,
38
+ });
39
+ return validateTask(response.data);
40
+ }
41
+ async getTasks(args = {}) {
42
+ const { data: { results, nextCursor }, } = await request({
43
+ httpMethod: 'GET',
44
+ baseUri: this.syncApiBase,
45
+ relativePath: ENDPOINT_REST_TASKS,
46
+ apiToken: this.authToken,
47
+ customFetch: this.customFetch,
48
+ customHeaders: this.customHeaders,
49
+ payload: Object.assign(Object.assign({}, args), spreadIfDefined(args.ids, (ids) => ({ ids: ids.join(',') }))),
50
+ });
51
+ return {
52
+ results: validateTaskArray(results),
53
+ nextCursor,
54
+ };
55
+ }
56
+ async getTasksByFilter(args) {
57
+ const { data: { results, nextCursor }, } = await request({
58
+ httpMethod: 'GET',
59
+ baseUri: this.syncApiBase,
60
+ relativePath: ENDPOINT_REST_TASKS_FILTER,
61
+ apiToken: this.authToken,
62
+ customFetch: this.customFetch,
63
+ customHeaders: this.customHeaders,
64
+ payload: args,
65
+ });
66
+ return {
67
+ results: validateTaskArray(results),
68
+ nextCursor,
69
+ };
70
+ }
71
+ async getCompletedTasksByCompletionDate(args) {
72
+ const { data: { items, nextCursor }, } = await request({
73
+ httpMethod: 'GET',
74
+ baseUri: this.syncApiBase,
75
+ relativePath: ENDPOINT_REST_TASKS_COMPLETED_BY_COMPLETION_DATE,
76
+ apiToken: this.authToken,
77
+ customFetch: this.customFetch,
78
+ customHeaders: this.customHeaders,
79
+ payload: args,
80
+ });
81
+ return {
82
+ items: validateTaskArray(items),
83
+ nextCursor,
84
+ };
85
+ }
86
+ async getCompletedTasksByDueDate(args) {
87
+ const { data: { items, nextCursor }, } = await request({
88
+ httpMethod: 'GET',
89
+ baseUri: this.syncApiBase,
90
+ relativePath: ENDPOINT_REST_TASKS_COMPLETED_BY_DUE_DATE,
91
+ apiToken: this.authToken,
92
+ customFetch: this.customFetch,
93
+ customHeaders: this.customHeaders,
94
+ payload: args,
95
+ });
96
+ return {
97
+ items: validateTaskArray(items),
98
+ nextCursor,
99
+ };
100
+ }
101
+ async searchCompletedTasks(args) {
102
+ const { data: { items, nextCursor }, } = await request({
103
+ httpMethod: 'GET',
104
+ baseUri: this.syncApiBase,
105
+ relativePath: ENDPOINT_REST_TASKS_COMPLETED_SEARCH,
106
+ apiToken: this.authToken,
107
+ customFetch: this.customFetch,
108
+ customHeaders: this.customHeaders,
109
+ payload: args,
110
+ });
111
+ return {
112
+ items: validateTaskArray(items),
113
+ nextCursor,
114
+ };
115
+ }
116
+ async getAllCompletedTasks(args = {}) {
117
+ const { since, until } = args, rest = __rest(args, ["since", "until"]);
118
+ const { data } = await request({
119
+ httpMethod: 'GET',
120
+ baseUri: this.syncApiBase,
121
+ relativePath: ENDPOINT_REST_TASKS_COMPLETED,
122
+ apiToken: this.authToken,
123
+ customFetch: this.customFetch,
124
+ customHeaders: this.customHeaders,
125
+ payload: Object.assign(Object.assign(Object.assign({}, rest), (since ? { since: since.toISOString() } : {})), (until ? { until: until.toISOString() } : {})),
126
+ });
127
+ return {
128
+ projects: data.projects,
129
+ sections: data.sections,
130
+ items: validateTaskArray(data.items),
131
+ };
132
+ }
133
+ async addTask(args, requestId) {
134
+ // Process content based on isUncompletable flag
135
+ const processedArgs = Object.assign(Object.assign({}, args), { content: processTaskContent(args.content, args.isUncompletable) });
136
+ const response = await request({
137
+ httpMethod: 'POST',
138
+ baseUri: this.syncApiBase,
139
+ relativePath: ENDPOINT_REST_TASKS,
140
+ apiToken: this.authToken,
141
+ customFetch: this.customFetch,
142
+ customHeaders: this.customHeaders,
143
+ payload: processedArgs,
144
+ requestId: requestId,
145
+ });
146
+ return validateTask(response.data);
147
+ }
148
+ async quickAddTask(args) {
149
+ // Process text based on isUncompletable flag
150
+ const processedArgs = Object.assign(Object.assign({}, args), { text: processTaskContent(args.text, args.isUncompletable) });
151
+ const response = await request({
152
+ httpMethod: 'POST',
153
+ baseUri: this.syncApiBase,
154
+ relativePath: ENDPOINT_SYNC_QUICK_ADD,
155
+ apiToken: this.authToken,
156
+ customFetch: this.customFetch,
157
+ customHeaders: this.customHeaders,
158
+ payload: processedArgs,
159
+ });
160
+ return validateTask(response.data);
161
+ }
162
+ async updateTask(id, args, requestId) {
163
+ IdSchema.parse(id);
164
+ // Translate SDK alias for due-date clearing to Todoist's accepted payload value.
165
+ const normalizedArgs = args.dueString === null ? Object.assign(Object.assign({}, args), { dueString: 'no date' }) : args;
166
+ // Process content if both content and isUncompletable are provided
167
+ const processedArgs = normalizedArgs.content && normalizedArgs.isUncompletable !== undefined
168
+ ? Object.assign(Object.assign({}, normalizedArgs), { content: processTaskContent(normalizedArgs.content, normalizedArgs.isUncompletable) }) : normalizedArgs;
169
+ // Remap `order` → `childOrder` so snakeCaseKeys() produces `child_order`
170
+ const { order } = processedArgs, argsWithoutOrder = __rest(processedArgs, ["order"]);
171
+ const remappedArgs = order !== undefined ? Object.assign(Object.assign({}, argsWithoutOrder), { childOrder: order }) : argsWithoutOrder;
172
+ const response = await request({
173
+ httpMethod: 'POST',
174
+ baseUri: this.syncApiBase,
175
+ relativePath: generatePath(ENDPOINT_REST_TASKS, id),
176
+ apiToken: this.authToken,
177
+ customFetch: this.customFetch,
178
+ customHeaders: this.customHeaders,
179
+ payload: remappedArgs,
180
+ requestId: requestId,
181
+ });
182
+ return validateTask(response.data);
183
+ }
184
+ async moveTasks(ids, args, requestId) {
185
+ var _a;
186
+ if (ids.length > MAX_COMMAND_COUNT) {
187
+ throw new TodoistRequestError(`Maximum number of items is ${MAX_COMMAND_COUNT}`, 400);
188
+ }
189
+ const commands = ids.map((id) => ({
190
+ type: 'item_move',
191
+ uuid: uuidv4(),
192
+ args: Object.assign(Object.assign(Object.assign({ id }, spreadIfDefined(args.projectId, (v) => ({ projectId: v }))), spreadIfDefined(args.sectionId, (v) => ({ sectionId: v }))), spreadIfDefined(args.parentId, (v) => ({ parentId: v }))),
193
+ }));
194
+ const syncRequest = {
195
+ commands,
196
+ resourceTypes: ['items'],
197
+ };
198
+ const syncResponse = await performSyncRequest(this.syncContext, syncRequest, {
199
+ requestId,
200
+ hasSyncCommands: true,
201
+ });
202
+ if (!((_a = syncResponse.items) === null || _a === void 0 ? void 0 : _a.length)) {
203
+ throw new TodoistRequestError('Tasks not found', 404);
204
+ }
205
+ const syncTasks = syncResponse.items.filter((task) => ids.includes(task.id));
206
+ if (!syncTasks.length) {
207
+ throw new TodoistRequestError('Tasks not found', 404);
208
+ }
209
+ return validateTaskArray(syncTasks);
210
+ }
211
+ async moveTask(id, args, requestId) {
212
+ IdSchema.parse(id);
213
+ const response = await request({
214
+ httpMethod: 'POST',
215
+ baseUri: this.syncApiBase,
216
+ relativePath: generatePath(ENDPOINT_REST_TASKS, id, ENDPOINT_REST_TASK_MOVE),
217
+ apiToken: this.authToken,
218
+ customFetch: this.customFetch,
219
+ customHeaders: this.customHeaders,
220
+ payload: Object.assign(Object.assign(Object.assign({}, spreadIfDefined(args.projectId, (v) => ({ project_id: v }))), spreadIfDefined(args.sectionId, (v) => ({ section_id: v }))), spreadIfDefined(args.parentId, (v) => ({ parent_id: v }))),
221
+ requestId: requestId,
222
+ });
223
+ return validateTask(response.data);
224
+ }
225
+ async closeTask(id, requestId) {
226
+ IdSchema.parse(id);
227
+ const response = await request({
228
+ httpMethod: 'POST',
229
+ baseUri: this.syncApiBase,
230
+ relativePath: generatePath(ENDPOINT_REST_TASKS, id, ENDPOINT_REST_TASK_CLOSE),
231
+ apiToken: this.authToken,
232
+ customFetch: this.customFetch,
233
+ customHeaders: this.customHeaders,
234
+ requestId: requestId,
235
+ });
236
+ return isSuccess(response);
237
+ }
238
+ async reopenTask(id, requestId) {
239
+ IdSchema.parse(id);
240
+ const response = await request({
241
+ httpMethod: 'POST',
242
+ baseUri: this.syncApiBase,
243
+ relativePath: generatePath(ENDPOINT_REST_TASKS, id, ENDPOINT_REST_TASK_REOPEN),
244
+ apiToken: this.authToken,
245
+ customFetch: this.customFetch,
246
+ customHeaders: this.customHeaders,
247
+ requestId: requestId,
248
+ });
249
+ return isSuccess(response);
250
+ }
251
+ async deleteTask(id, requestId) {
252
+ IdSchema.parse(id);
253
+ const response = await request({
254
+ httpMethod: 'DELETE',
255
+ baseUri: this.syncApiBase,
256
+ relativePath: generatePath(ENDPOINT_REST_TASKS, id),
257
+ apiToken: this.authToken,
258
+ customFetch: this.customFetch,
259
+ customHeaders: this.customHeaders,
260
+ requestId: requestId,
261
+ });
262
+ return isSuccess(response);
263
+ }
264
+ }
@@ -0,0 +1,89 @@
1
+ import { ENDPOINT_REST_TEMPLATES_CREATE_FROM_FILE, ENDPOINT_REST_TEMPLATES_FILE, ENDPOINT_REST_TEMPLATES_IMPORT_FROM_FILE, ENDPOINT_REST_TEMPLATES_IMPORT_FROM_ID, ENDPOINT_REST_TEMPLATES_URL, } from '../consts/endpoints.js';
2
+ import { request } from '../transport/http-client.js';
3
+ import { uploadMultipartFile } from '../utils/multipart-upload.js';
4
+ import { validateCommentArray, validateProjectArray, validateSectionArray, validateTaskArray, } from '../utils/validators.js';
5
+ import { BaseClient } from './base-client.js';
6
+ /**
7
+ * Internal sub-client handling all template-domain endpoints.
8
+ *
9
+ * Instantiated by `TodoistApi`; every public template method on
10
+ * `TodoistApi` delegates here. See `todoist-api.ts` for user-facing
11
+ * JSDoc.
12
+ */
13
+ export class TemplateClient extends BaseClient {
14
+ async exportTemplateAsFile(args) {
15
+ const response = await request({
16
+ httpMethod: 'GET',
17
+ baseUri: this.syncApiBase,
18
+ relativePath: ENDPOINT_REST_TEMPLATES_FILE,
19
+ apiToken: this.authToken,
20
+ customFetch: this.customFetch,
21
+ customHeaders: this.customHeaders,
22
+ payload: args,
23
+ });
24
+ return response.data;
25
+ }
26
+ async exportTemplateAsUrl(args) {
27
+ const { data } = await request({
28
+ httpMethod: 'GET',
29
+ baseUri: this.syncApiBase,
30
+ relativePath: ENDPOINT_REST_TEMPLATES_URL,
31
+ apiToken: this.authToken,
32
+ customFetch: this.customFetch,
33
+ customHeaders: this.customHeaders,
34
+ payload: args,
35
+ });
36
+ return data;
37
+ }
38
+ async createProjectFromTemplate(args, requestId) {
39
+ const { file, fileName, name, workspaceId } = args;
40
+ const additionalFields = { name };
41
+ if (workspaceId !== undefined && workspaceId !== null) {
42
+ additionalFields.workspace_id = workspaceId;
43
+ }
44
+ const data = await uploadMultipartFile({
45
+ baseUrl: this.syncApiBase,
46
+ authToken: this.authToken,
47
+ endpoint: ENDPOINT_REST_TEMPLATES_CREATE_FROM_FILE,
48
+ file,
49
+ fileName,
50
+ additionalFields,
51
+ customFetch: this.customFetch,
52
+ customHeaders: this.customHeaders,
53
+ requestId,
54
+ });
55
+ return this.validateTemplateResponse(data);
56
+ }
57
+ async importTemplateIntoProject(args, requestId) {
58
+ const { file, fileName, projectId } = args;
59
+ const data = await uploadMultipartFile({
60
+ baseUrl: this.syncApiBase,
61
+ authToken: this.authToken,
62
+ endpoint: ENDPOINT_REST_TEMPLATES_IMPORT_FROM_FILE,
63
+ file,
64
+ fileName,
65
+ additionalFields: { project_id: projectId },
66
+ customFetch: this.customFetch,
67
+ customHeaders: this.customHeaders,
68
+ requestId,
69
+ });
70
+ return this.validateTemplateResponse(data);
71
+ }
72
+ async importTemplateFromId(args, requestId) {
73
+ const { data } = await request({
74
+ httpMethod: 'POST',
75
+ baseUri: this.syncApiBase,
76
+ relativePath: ENDPOINT_REST_TEMPLATES_IMPORT_FROM_ID,
77
+ apiToken: this.authToken,
78
+ customFetch: this.customFetch,
79
+ customHeaders: this.customHeaders,
80
+ payload: args,
81
+ requestId: requestId,
82
+ });
83
+ return this.validateTemplateResponse(data);
84
+ }
85
+ validateTemplateResponse(data) {
86
+ var _a, _b, _c, _d;
87
+ return Object.assign(Object.assign({}, data), { projects: validateProjectArray((_a = data.projects) !== null && _a !== void 0 ? _a : []), sections: validateSectionArray((_b = data.sections) !== null && _b !== void 0 ? _b : []), tasks: validateTaskArray((_c = data.tasks) !== null && _c !== void 0 ? _c : []), comments: validateCommentArray((_d = data.comments) !== null && _d !== void 0 ? _d : []) });
88
+ }
89
+ }
@@ -0,0 +1,110 @@
1
+ import { ENDPOINT_REST_APPS_UI_EXTENSIONS, ENDPOINT_REST_APPS_UI_EXTENSIONS_INSTALLED, getUiExtensionEndpoint, getUiExtensionIconEndpoint, getUiExtensionsByIntegrationEndpoint, } from '../consts/endpoints.js';
2
+ import { isSuccess, request } from '../transport/http-client.js';
3
+ import { uploadMultipartFile } from '../utils/multipart-upload.js';
4
+ import { IdSchema, validateUiExtension, validateUiExtensionArray } from '../utils/validators.js';
5
+ import { BaseClient } from './base-client.js';
6
+ /**
7
+ * Internal sub-client handling all UI-extension endpoints for developer
8
+ * applications. All endpoints require the `dev:app_console` OAuth scope.
9
+ *
10
+ * Instantiated by `TodoistApi`; every public UI-extension method on
11
+ * `TodoistApi` delegates here. See `todoist-api.ts` for user-facing JSDoc.
12
+ */
13
+ export class UiExtensionClient extends BaseClient {
14
+ async getUiExtension(uiExtensionId, requestId) {
15
+ IdSchema.parse(uiExtensionId);
16
+ const response = await request({
17
+ httpMethod: 'GET',
18
+ baseUri: this.apiRootBase,
19
+ relativePath: getUiExtensionEndpoint(uiExtensionId),
20
+ apiToken: this.authToken,
21
+ customFetch: this.customFetch,
22
+ customHeaders: this.customHeaders,
23
+ requestId: requestId,
24
+ });
25
+ return validateUiExtension(response.data);
26
+ }
27
+ async getInstalledUiExtensions(requestId) {
28
+ const response = await request({
29
+ httpMethod: 'GET',
30
+ baseUri: this.apiRootBase,
31
+ relativePath: ENDPOINT_REST_APPS_UI_EXTENSIONS_INSTALLED,
32
+ apiToken: this.authToken,
33
+ customFetch: this.customFetch,
34
+ customHeaders: this.customHeaders,
35
+ requestId: requestId,
36
+ });
37
+ return validateUiExtensionArray(response.data);
38
+ }
39
+ async getUiExtensionsForApp(appId, requestId) {
40
+ IdSchema.parse(appId);
41
+ const response = await request({
42
+ httpMethod: 'GET',
43
+ baseUri: this.apiRootBase,
44
+ relativePath: getUiExtensionsByIntegrationEndpoint(appId),
45
+ apiToken: this.authToken,
46
+ customFetch: this.customFetch,
47
+ customHeaders: this.customHeaders,
48
+ requestId: requestId,
49
+ });
50
+ return validateUiExtensionArray(response.data);
51
+ }
52
+ async addUiExtension(args, requestId) {
53
+ const response = await request({
54
+ httpMethod: 'POST',
55
+ baseUri: this.apiRootBase,
56
+ relativePath: ENDPOINT_REST_APPS_UI_EXTENSIONS,
57
+ apiToken: this.authToken,
58
+ customFetch: this.customFetch,
59
+ customHeaders: this.customHeaders,
60
+ payload: args,
61
+ requestId: requestId,
62
+ });
63
+ return validateUiExtension(response.data);
64
+ }
65
+ async updateUiExtension(uiExtensionId, args, requestId) {
66
+ IdSchema.parse(uiExtensionId);
67
+ const response = await request({
68
+ httpMethod: 'PATCH',
69
+ baseUri: this.apiRootBase,
70
+ relativePath: getUiExtensionEndpoint(uiExtensionId),
71
+ apiToken: this.authToken,
72
+ customFetch: this.customFetch,
73
+ customHeaders: this.customHeaders,
74
+ payload: args,
75
+ requestId: requestId,
76
+ });
77
+ return validateUiExtension(response.data);
78
+ }
79
+ async deleteUiExtension(uiExtensionId, requestId) {
80
+ IdSchema.parse(uiExtensionId);
81
+ const response = await request({
82
+ httpMethod: 'DELETE',
83
+ baseUri: this.apiRootBase,
84
+ relativePath: getUiExtensionEndpoint(uiExtensionId),
85
+ apiToken: this.authToken,
86
+ customFetch: this.customFetch,
87
+ customHeaders: this.customHeaders,
88
+ requestId: requestId,
89
+ });
90
+ return isSuccess(response);
91
+ }
92
+ async uploadUiExtensionIcon(args, requestId) {
93
+ var _a;
94
+ if (Buffer.isBuffer(args.file) && args.file.length === 0) {
95
+ throw new Error('Cannot upload empty image file');
96
+ }
97
+ const data = await uploadMultipartFile({
98
+ baseUrl: this.apiRootBase,
99
+ authToken: this.authToken,
100
+ endpoint: getUiExtensionIconEndpoint(args.uiExtensionId),
101
+ file: args.file,
102
+ fileName: args.fileName,
103
+ additionalFields: { file_name: (_a = args.fileName) !== null && _a !== void 0 ? _a : '' },
104
+ requestId: requestId,
105
+ customFetch: this.customFetch,
106
+ customHeaders: this.customHeaders,
107
+ });
108
+ return validateUiExtension(data);
109
+ }
110
+ }
@@ -0,0 +1,153 @@
1
+ import { ENDPOINT_REST_UPLOADS } from '../consts/endpoints.js';
2
+ import { isSuccess, request } from '../transport/http-client.js';
3
+ import { wrapAsFileResponse } from '../utils/file-response.js';
4
+ import { uploadMultipartFile } from '../utils/multipart-upload.js';
5
+ import { validateAttachment } from '../utils/validators.js';
6
+ import { BaseClient } from './base-client.js';
7
+ /**
8
+ * First-party origin that serves attachment downloads behind Bearer auth.
9
+ * It redirects to the CDN; on the cross-origin hop the runtime drops the
10
+ * Authorization header, so the token never reaches the CDN.
11
+ */
12
+ const AUTHENTICATED_ATTACHMENT_HOST = 'files.todoist.com';
13
+ /**
14
+ * CDN origins that serve attachments via pre-signed URLs. These are
15
+ * third-party infrastructure and must never receive the auth token.
16
+ */
17
+ const CDN_ATTACHMENT_HOSTS = new Set(['todoist.b-cdn.net', 'd1ysz50cxb9zwl.cloudfront.net']);
18
+ const MAX_ATTACHMENT_REDIRECTS = 5;
19
+ /**
20
+ * Builds the request headers for an attachment fetch, attaching the Bearer
21
+ * token only when the URL is on the first-party authenticated host.
22
+ */
23
+ function buildAttachmentAuthHeaders(url, authToken) {
24
+ return new URL(url).hostname === AUTHENTICATED_ATTACHMENT_HOST
25
+ ? { Authorization: `Bearer ${authToken}` }
26
+ : {};
27
+ }
28
+ /**
29
+ * Fetches an attachment through a caller-supplied `customFetch` while
30
+ * guaranteeing the auth token is never sent across origins.
31
+ *
32
+ * A `customFetch` is an arbitrary implementation that may not honour the Fetch
33
+ * standard's rule of stripping `Authorization` on cross-origin redirects (older
34
+ * `node-fetch`, for example, does not). Because `files.todoist.com` redirects
35
+ * downloads to the CDN, a naive implementation could forward the token there.
36
+ * We therefore drive redirects with `redirect: 'manual'` and follow them
37
+ * ourselves, dropping the auth header whenever the origin changes.
38
+ *
39
+ * Implementations that hide the redirect behind an opaque response (status `0`,
40
+ * e.g. undici and browsers) can't be followed manually; for those we re-issue
41
+ * with normal redirect following and rely on the Fetch standard's cross-origin
42
+ * auth stripping, which those runtimes implement.
43
+ */
44
+ async function customFetchFollowingRedirects(customFetch, startUrl, authToken) {
45
+ let currentUrl = startUrl;
46
+ let headers = buildAttachmentAuthHeaders(currentUrl, authToken);
47
+ for (let redirects = 0; redirects < MAX_ATTACHMENT_REDIRECTS; redirects++) {
48
+ const response = await customFetch(currentUrl, {
49
+ method: 'GET',
50
+ headers,
51
+ redirect: 'manual',
52
+ });
53
+ const location = response.headers['location'];
54
+ const isReadableRedirect = response.status >= 300 && response.status < 400 && Boolean(location);
55
+ if (!isReadableRedirect) {
56
+ // Opaque redirect (the impl hid the Location, e.g. undici): re-issue
57
+ // with normal following and trust the runtime to strip auth cross-origin.
58
+ if (response.status === 0) {
59
+ return customFetch(currentUrl, { method: 'GET', headers });
60
+ }
61
+ return response;
62
+ }
63
+ const nextUrl = new URL(location, currentUrl);
64
+ if (nextUrl.origin !== new URL(currentUrl).origin) {
65
+ headers = {};
66
+ }
67
+ currentUrl = nextUrl.toString();
68
+ }
69
+ throw new Error('Too many redirects while fetching attachment');
70
+ }
71
+ /**
72
+ * Internal sub-client handling upload + attachment endpoints
73
+ * (file upload, upload delete, attachment view).
74
+ *
75
+ * Instantiated by `TodoistApi`; every public upload method on
76
+ * `TodoistApi` delegates here. See `todoist-api.ts` for user-facing
77
+ * JSDoc.
78
+ */
79
+ export class UploadClient extends BaseClient {
80
+ async uploadFile(args, requestId) {
81
+ const additionalFields = {};
82
+ if (args.projectId) {
83
+ additionalFields.project_id = args.projectId;
84
+ }
85
+ const data = await uploadMultipartFile({
86
+ baseUrl: this.syncApiBase,
87
+ authToken: this.authToken,
88
+ endpoint: ENDPOINT_REST_UPLOADS,
89
+ file: args.file,
90
+ fileName: args.fileName,
91
+ additionalFields: additionalFields,
92
+ requestId: requestId,
93
+ customFetch: this.customFetch,
94
+ customHeaders: this.customHeaders,
95
+ });
96
+ return validateAttachment(data);
97
+ }
98
+ async deleteUpload(args, requestId) {
99
+ const response = await request({
100
+ httpMethod: 'DELETE',
101
+ baseUri: this.syncApiBase,
102
+ relativePath: ENDPOINT_REST_UPLOADS,
103
+ apiToken: this.authToken,
104
+ customFetch: this.customFetch,
105
+ customHeaders: this.customHeaders,
106
+ payload: args,
107
+ requestId: requestId,
108
+ });
109
+ return isSuccess(response);
110
+ }
111
+ async viewAttachment(commentOrUrl) {
112
+ var _a;
113
+ let fileUrl;
114
+ if (typeof commentOrUrl === 'string') {
115
+ fileUrl = commentOrUrl;
116
+ }
117
+ else {
118
+ if (!((_a = commentOrUrl.fileAttachment) === null || _a === void 0 ? void 0 : _a.fileUrl)) {
119
+ throw new Error('Comment does not have a file attachment');
120
+ }
121
+ fileUrl = commentOrUrl.fileAttachment.fileUrl;
122
+ }
123
+ // Only allow known attachment origins, and only attach the auth token to the
124
+ // first-party authenticated host. This prevents the token leaking to other
125
+ // todoist.com hosts (e.g. api.todoist.com) or to third-party CDN infrastructure.
126
+ const urlHostname = new URL(fileUrl).hostname;
127
+ const isAuthenticatedHost = urlHostname === AUTHENTICATED_ATTACHMENT_HOST;
128
+ if (!isAuthenticatedHost && !CDN_ATTACHMENT_HOSTS.has(urlHostname)) {
129
+ throw new Error('Attachment URLs must be on a known Todoist attachment host');
130
+ }
131
+ if (this.customFetch) {
132
+ // customFetch may not strip auth on cross-origin redirects, so we
133
+ // follow the files.todoist.com -> CDN hop ourselves to avoid leaking
134
+ // the token. See customFetchFollowingRedirects for details.
135
+ const response = await customFetchFollowingRedirects(this.customFetch, fileUrl, this.authToken);
136
+ if (!response.ok) {
137
+ throw new Error(`Failed to fetch attachment: ${response.status} ${response.statusText}`);
138
+ }
139
+ return wrapAsFileResponse(response, 'viewAttachment');
140
+ }
141
+ // Built-in fetch (undici) strips Authorization on cross-origin redirects
142
+ // per the Fetch standard, so the files.todoist.com -> CDN hop is safe here.
143
+ const fetchOptions = {
144
+ method: 'GET',
145
+ headers: isAuthenticatedHost ? { Authorization: `Bearer ${this.authToken}` } : {},
146
+ };
147
+ const response = await fetch(fileUrl, fetchOptions);
148
+ if (!response.ok) {
149
+ throw new Error(`Failed to fetch attachment: ${response.status} ${response.statusText}`);
150
+ }
151
+ return wrapAsFileResponse(response, 'viewAttachment');
152
+ }
153
+ }
@@ -0,0 +1,21 @@
1
+ import { performSyncRequest } from '../transport/sync-request.js';
2
+ import { createCommand } from '../utils/sync-helpers.js';
3
+ import { isActiveViewOption } from '../utils/view-options.js';
4
+ import { BaseClient } from './base-client.js';
5
+ /** Internal sub-client for saved view-option reads and writes. */
6
+ export class ViewOptionsClient extends BaseClient {
7
+ async getViewOptions() {
8
+ var _a;
9
+ const response = await performSyncRequest(this.syncContext, {
10
+ resourceTypes: ['view_options'],
11
+ syncToken: '*',
12
+ });
13
+ return ((_a = response.viewOptions) !== null && _a !== void 0 ? _a : []).filter(isActiveViewOption);
14
+ }
15
+ async setViewOptions(args, requestId) {
16
+ await performSyncRequest(this.syncContext, { commands: [createCommand('view_options_set', args)] }, { requestId, hasSyncCommands: true });
17
+ }
18
+ async deleteViewOptions(args, requestId) {
19
+ await performSyncRequest(this.syncContext, { commands: [createCommand('view_options_delete', args)] }, { requestId, hasSyncCommands: true });
20
+ }
21
+ }