@doist/todoist-api-typescript 5.8.0 → 6.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 (88) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/authentication.js +158 -0
  3. package/dist/cjs/consts/endpoints.js +74 -0
  4. package/dist/{index.js → cjs/index.js} +1 -1
  5. package/dist/cjs/package.json +1 -0
  6. package/dist/cjs/rest-client.js +124 -0
  7. package/dist/{testUtils → cjs/test-utils}/asserts.js +1 -1
  8. package/dist/{testUtils → cjs/test-utils}/mocks.js +8 -4
  9. package/dist/cjs/test-utils/msw-setup.js +27 -0
  10. package/dist/{testUtils/testDefaults.js → cjs/test-utils/test-defaults.js} +41 -52
  11. package/dist/cjs/todoist-api.js +1235 -0
  12. package/dist/{types → cjs/types}/entities.js +78 -31
  13. package/dist/cjs/types/errors.js +22 -0
  14. package/dist/cjs/types/http.js +22 -0
  15. package/dist/cjs/utils/case-conversion.js +69 -0
  16. package/dist/{utils → cjs/utils}/colors.js +3 -3
  17. package/dist/cjs/utils/fetch-with-retry.js +150 -0
  18. package/dist/{utils → cjs/utils}/index.js +4 -4
  19. package/dist/cjs/utils/multipart-upload.js +126 -0
  20. package/dist/{utils → cjs/utils}/processing-helpers.js +3 -3
  21. package/dist/{utils → cjs/utils}/sanitization.js +17 -28
  22. package/dist/{utils/urlHelpers.js → cjs/utils/url-helpers.js} +15 -15
  23. package/dist/{utils → cjs/utils}/validators.js +28 -1
  24. package/dist/esm/authentication.js +151 -0
  25. package/dist/esm/consts/endpoints.js +65 -0
  26. package/dist/esm/index.js +4 -0
  27. package/dist/esm/rest-client.js +119 -0
  28. package/dist/esm/test-utils/asserts.js +8 -0
  29. package/dist/esm/test-utils/mocks.js +10 -0
  30. package/dist/esm/test-utils/msw-setup.js +22 -0
  31. package/dist/esm/test-utils/test-defaults.js +198 -0
  32. package/dist/esm/todoist-api.js +1231 -0
  33. package/dist/esm/types/entities.js +366 -0
  34. package/dist/esm/types/errors.js +18 -0
  35. package/dist/esm/types/http.js +18 -0
  36. package/dist/esm/types/index.js +3 -0
  37. package/dist/esm/types/requests.js +1 -0
  38. package/dist/esm/types/sync.js +1 -0
  39. package/dist/esm/utils/activity-helpers.js +36 -0
  40. package/dist/esm/utils/case-conversion.js +61 -0
  41. package/dist/esm/utils/colors.js +215 -0
  42. package/dist/esm/utils/fetch-with-retry.js +147 -0
  43. package/dist/esm/utils/index.js +3 -0
  44. package/dist/esm/utils/multipart-upload.js +120 -0
  45. package/dist/esm/utils/processing-helpers.js +12 -0
  46. package/dist/esm/utils/sanitization.js +112 -0
  47. package/dist/esm/utils/url-helpers.js +68 -0
  48. package/dist/esm/utils/validators.js +97 -0
  49. package/dist/{authentication.d.ts → types/authentication.d.ts} +6 -1
  50. package/dist/{consts → types/consts}/endpoints.d.ts +11 -0
  51. package/dist/types/index.d.ts +4 -3
  52. package/dist/types/rest-client.d.ts +15 -0
  53. package/dist/types/test-utils/msw-setup.d.ts +3 -0
  54. package/dist/{TodoistApi.d.ts → types/todoist-api.d.ts} +91 -2
  55. package/dist/types/{entities.d.ts → types/entities.d.ts} +119 -0
  56. package/dist/types/types/http.d.ts +68 -0
  57. package/dist/types/types/index.d.ts +3 -0
  58. package/dist/types/{requests.d.ts → types/requests.d.ts} +137 -0
  59. package/dist/types/utils/case-conversion.d.ts +12 -0
  60. package/dist/types/utils/fetch-with-retry.d.ts +11 -0
  61. package/dist/types/utils/index.d.ts +3 -0
  62. package/dist/types/utils/multipart-upload.d.ts +50 -0
  63. package/dist/{utils → types/utils}/validators.d.ts +7 -1
  64. package/package.json +24 -8
  65. package/dist/TodoistApi.js +0 -1209
  66. package/dist/authentication.js +0 -199
  67. package/dist/consts/endpoints.js +0 -50
  68. package/dist/index.d.ts +0 -4
  69. package/dist/restClient.d.ts +0 -5
  70. package/dist/restClient.js +0 -170
  71. package/dist/types/errors.js +0 -39
  72. package/dist/types/http.d.ts +0 -1
  73. package/dist/types/http.js +0 -2
  74. package/dist/utils/index.d.ts +0 -3
  75. /package/dist/{types → cjs/types}/index.js +0 -0
  76. /package/dist/{types → cjs/types}/requests.js +0 -0
  77. /package/dist/{types → cjs/types}/sync.js +0 -0
  78. /package/dist/{utils → cjs/utils}/activity-helpers.js +0 -0
  79. /package/dist/{testUtils → types/test-utils}/asserts.d.ts +0 -0
  80. /package/dist/{testUtils → types/test-utils}/mocks.d.ts +0 -0
  81. /package/dist/{testUtils/testDefaults.d.ts → types/test-utils/test-defaults.d.ts} +0 -0
  82. /package/dist/types/{errors.d.ts → types/errors.d.ts} +0 -0
  83. /package/dist/types/{sync.d.ts → types/sync.d.ts} +0 -0
  84. /package/dist/{utils → types/utils}/activity-helpers.d.ts +0 -0
  85. /package/dist/{utils → types/utils}/colors.d.ts +0 -0
  86. /package/dist/{utils → types/utils}/processing-helpers.d.ts +0 -0
  87. /package/dist/{utils → types/utils}/sanitization.d.ts +0 -0
  88. /package/dist/{utils/urlHelpers.d.ts → types/utils/url-helpers.d.ts} +0 -0
package/README.md CHANGED
@@ -51,7 +51,7 @@ Example scratch.ts file:
51
51
 
52
52
  ```
53
53
  /* eslint-disable no-console */
54
- import { TodoistApi } from './TodoistApi'
54
+ import { TodoistApi } from './todoist-api'
55
55
 
56
56
  const token = 'YOURTOKEN'
57
57
  const api = new TodoistApi(token)
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAuthStateParameter = getAuthStateParameter;
4
+ exports.getAuthorizationUrl = getAuthorizationUrl;
5
+ exports.getAuthToken = getAuthToken;
6
+ exports.revokeAuthToken = revokeAuthToken;
7
+ exports.revokeToken = revokeToken;
8
+ const rest_client_1 = require("./rest-client");
9
+ const uuid_1 = require("uuid");
10
+ const types_1 = require("./types");
11
+ const endpoints_1 = require("./consts/endpoints");
12
+ /**
13
+ * Creates a Basic Authentication header value from client credentials.
14
+ * @param clientId - The OAuth client ID
15
+ * @param clientSecret - The OAuth client secret
16
+ * @returns The Basic Auth header value (without the 'Basic ' prefix)
17
+ */
18
+ function createBasicAuthHeader(clientId, clientSecret) {
19
+ const credentials = `${clientId}:${clientSecret}`;
20
+ return Buffer.from(credentials).toString('base64');
21
+ }
22
+ /**
23
+ * Generates a random state parameter for OAuth2 authorization.
24
+ * The state parameter helps prevent CSRF attacks.
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const state = getAuthStateParameter()
29
+ * // Store state in session
30
+ * const authUrl = getAuthorizationUrl(clientId, ['data:read'], state)
31
+ * ```
32
+ *
33
+ * @returns A random UUID v4 string
34
+ */
35
+ function getAuthStateParameter() {
36
+ return (0, uuid_1.v4)();
37
+ }
38
+ /**
39
+ * Generates the authorization URL for the OAuth2 flow.
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * const url = getAuthorizationUrl(
44
+ * 'your-client-id',
45
+ * ['data:read', 'task:add'],
46
+ * state
47
+ * )
48
+ * // Redirect user to url
49
+ * ```
50
+ *
51
+ * @returns The full authorization URL to redirect users to
52
+ * @see https://todoist.com/api/v1/docs#tag/Authorization/OAuth
53
+ */
54
+ function getAuthorizationUrl({ clientId, permissions, state, baseUrl, }) {
55
+ if (!(permissions === null || permissions === void 0 ? void 0 : permissions.length)) {
56
+ throw new Error('At least one scope value should be passed for permissions.');
57
+ }
58
+ const scope = permissions.join(',');
59
+ return `${(0, endpoints_1.getAuthBaseUri)(baseUrl)}${endpoints_1.ENDPOINT_AUTHORIZATION}?client_id=${clientId}&scope=${scope}&state=${state}`;
60
+ }
61
+ /**
62
+ * Exchanges an authorization code for an access token.
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const { accessToken } = await getAuthToken({
67
+ * clientId: 'your-client-id',
68
+ * clientSecret: 'your-client-secret',
69
+ * code: authCode
70
+ * })
71
+ * ```
72
+ *
73
+ * @returns The access token response
74
+ * @throws {@link TodoistRequestError} If the token exchange fails
75
+ */
76
+ async function getAuthToken(args, baseUrl) {
77
+ var _a;
78
+ const response = await (0, rest_client_1.request)({
79
+ httpMethod: 'POST',
80
+ baseUri: (0, endpoints_1.getAuthBaseUri)(baseUrl),
81
+ relativePath: endpoints_1.ENDPOINT_GET_TOKEN,
82
+ apiToken: undefined,
83
+ payload: args,
84
+ });
85
+ if (response.status !== 200 || !((_a = response.data) === null || _a === void 0 ? void 0 : _a.accessToken)) {
86
+ throw new types_1.TodoistRequestError('Authentication token exchange failed.', response.status, response.data);
87
+ }
88
+ return response.data;
89
+ }
90
+ /**
91
+ * Revokes an access token, making it invalid for future use.
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * await revokeAuthToken({
96
+ * clientId: 'your-client-id',
97
+ * clientSecret: 'your-client-secret',
98
+ * accessToken: token
99
+ * })
100
+ * ```
101
+ *
102
+ * @deprecated Use {@link revokeToken} instead. This function uses a legacy endpoint that will be removed in a future version. The new function uses the RFC 7009 compliant endpoint.
103
+ * @returns True if revocation was successful
104
+ * @see https://todoist.com/api/v1/docs#tag/Authorization/operation/revoke_access_token_api_api_v1_access_tokens_delete
105
+ */
106
+ async function revokeAuthToken(args, baseUrl) {
107
+ const response = await (0, rest_client_1.request)({
108
+ httpMethod: 'POST',
109
+ baseUri: (0, endpoints_1.getSyncBaseUri)(baseUrl),
110
+ relativePath: endpoints_1.ENDPOINT_REVOKE_TOKEN,
111
+ apiToken: undefined,
112
+ payload: args,
113
+ });
114
+ return (0, rest_client_1.isSuccess)(response);
115
+ }
116
+ /**
117
+ * Revokes a token using the RFC 7009 OAuth 2.0 Token Revocation standard.
118
+ *
119
+ * This function uses HTTP Basic Authentication with client credentials and follows
120
+ * the RFC 7009 specification for token revocation.
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * await revokeToken({
125
+ * clientId: 'your-client-id',
126
+ * clientSecret: 'your-client-secret',
127
+ * token: 'access-token-to-revoke'
128
+ * })
129
+ * ```
130
+ *
131
+ * @returns True if revocation was successful
132
+ * @see https://datatracker.ietf.org/doc/html/rfc7009
133
+ * @see https://todoist.com/api/v1/docs#tag/Authorization
134
+ */
135
+ async function revokeToken(args, baseUrl) {
136
+ const { clientId, clientSecret, token } = args;
137
+ // Create Basic Auth header as per RFC 7009
138
+ const basicAuth = createBasicAuthHeader(clientId, clientSecret);
139
+ const customHeaders = {
140
+ Authorization: `Basic ${basicAuth}`,
141
+ };
142
+ // Request body only contains the token and optional token_type_hint
143
+ const requestBody = {
144
+ token,
145
+ token_type_hint: 'access_token',
146
+ };
147
+ const response = await (0, rest_client_1.request)({
148
+ httpMethod: 'POST',
149
+ baseUri: (0, endpoints_1.getSyncBaseUri)(baseUrl),
150
+ relativePath: endpoints_1.ENDPOINT_REVOKE,
151
+ apiToken: undefined,
152
+ payload: requestBody,
153
+ requestId: undefined,
154
+ hasSyncCommands: false,
155
+ customHeaders: customHeaders,
156
+ });
157
+ return (0, rest_client_1.isSuccess)(response);
158
+ }
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ENDPOINT_WORKSPACE_USERS = exports.ENDPOINT_WORKSPACE_PLAN_DETAILS = exports.ENDPOINT_WORKSPACE_LOGO = exports.ENDPOINT_WORKSPACE_JOIN = exports.ENDPOINT_WORKSPACE_INVITATIONS_DELETE = exports.ENDPOINT_WORKSPACE_INVITATIONS_ALL = exports.ENDPOINT_WORKSPACE_INVITATIONS = exports.ENDPOINT_REVOKE = exports.ENDPOINT_REVOKE_TOKEN = exports.ENDPOINT_GET_TOKEN = exports.ENDPOINT_AUTHORIZATION = exports.ENDPOINT_SYNC = exports.ENDPOINT_SYNC_QUICK_ADD = exports.PROJECT_UNARCHIVE = exports.PROJECT_ARCHIVE = exports.ENDPOINT_REST_UPLOADS = exports.ENDPOINT_REST_ACTIVITIES = exports.ENDPOINT_REST_PRODUCTIVITY = exports.ENDPOINT_REST_USER = exports.ENDPOINT_REST_PROJECT_COLLABORATORS = exports.ENDPOINT_REST_PROJECTS_ARCHIVED = exports.ENDPOINT_REST_PROJECTS = exports.ENDPOINT_REST_TASK_MOVE = exports.ENDPOINT_REST_TASK_REOPEN = exports.ENDPOINT_REST_TASK_CLOSE = exports.ENDPOINT_REST_COMMENTS = exports.ENDPOINT_REST_LABELS_SHARED_REMOVE = exports.ENDPOINT_REST_LABELS_SHARED_RENAME = exports.ENDPOINT_REST_LABELS_SHARED = exports.ENDPOINT_REST_LABELS = exports.ENDPOINT_REST_SECTIONS = exports.ENDPOINT_REST_TASKS_COMPLETED_SEARCH = exports.ENDPOINT_REST_TASKS_COMPLETED_BY_DUE_DATE = exports.ENDPOINT_REST_TASKS_COMPLETED_BY_COMPLETION_DATE = exports.ENDPOINT_REST_TASKS_FILTER = exports.ENDPOINT_REST_TASKS = exports.API_BASE_URI = exports.API_VERSION = exports.TODOIST_WEB_URI = void 0;
4
+ exports.getSyncBaseUri = getSyncBaseUri;
5
+ exports.getAuthBaseUri = getAuthBaseUri;
6
+ exports.getWorkspaceInvitationAcceptEndpoint = getWorkspaceInvitationAcceptEndpoint;
7
+ exports.getWorkspaceInvitationRejectEndpoint = getWorkspaceInvitationRejectEndpoint;
8
+ exports.getWorkspaceActiveProjectsEndpoint = getWorkspaceActiveProjectsEndpoint;
9
+ exports.getWorkspaceArchivedProjectsEndpoint = getWorkspaceArchivedProjectsEndpoint;
10
+ const BASE_URI = 'https://api.todoist.com';
11
+ const TODOIST_URI = 'https://todoist.com';
12
+ exports.TODOIST_WEB_URI = 'https://app.todoist.com/app';
13
+ // The API version is not configurable, to ensure
14
+ // compatibility between the API and the client.
15
+ exports.API_VERSION = 'v1';
16
+ exports.API_BASE_URI = `/api/${exports.API_VERSION}/`;
17
+ const API_AUTHORIZATION_BASE_URI = '/oauth/';
18
+ function getSyncBaseUri(domainBase = BASE_URI) {
19
+ return new URL(exports.API_BASE_URI, domainBase).toString();
20
+ }
21
+ function getAuthBaseUri(domainBase = TODOIST_URI) {
22
+ return new URL(API_AUTHORIZATION_BASE_URI, domainBase).toString();
23
+ }
24
+ exports.ENDPOINT_REST_TASKS = 'tasks';
25
+ exports.ENDPOINT_REST_TASKS_FILTER = exports.ENDPOINT_REST_TASKS + '/filter';
26
+ exports.ENDPOINT_REST_TASKS_COMPLETED_BY_COMPLETION_DATE = exports.ENDPOINT_REST_TASKS + '/completed/by_completion_date';
27
+ exports.ENDPOINT_REST_TASKS_COMPLETED_BY_DUE_DATE = exports.ENDPOINT_REST_TASKS + '/completed/by_due_date';
28
+ exports.ENDPOINT_REST_TASKS_COMPLETED_SEARCH = 'completed/search';
29
+ exports.ENDPOINT_REST_SECTIONS = 'sections';
30
+ exports.ENDPOINT_REST_LABELS = 'labels';
31
+ exports.ENDPOINT_REST_LABELS_SHARED = exports.ENDPOINT_REST_LABELS + '/shared';
32
+ exports.ENDPOINT_REST_LABELS_SHARED_RENAME = exports.ENDPOINT_REST_LABELS_SHARED + '/rename';
33
+ exports.ENDPOINT_REST_LABELS_SHARED_REMOVE = exports.ENDPOINT_REST_LABELS_SHARED + '/remove';
34
+ exports.ENDPOINT_REST_COMMENTS = 'comments';
35
+ exports.ENDPOINT_REST_TASK_CLOSE = 'close';
36
+ exports.ENDPOINT_REST_TASK_REOPEN = 'reopen';
37
+ exports.ENDPOINT_REST_TASK_MOVE = 'move';
38
+ exports.ENDPOINT_REST_PROJECTS = 'projects';
39
+ exports.ENDPOINT_REST_PROJECTS_ARCHIVED = exports.ENDPOINT_REST_PROJECTS + '/archived';
40
+ exports.ENDPOINT_REST_PROJECT_COLLABORATORS = 'collaborators';
41
+ exports.ENDPOINT_REST_USER = 'user';
42
+ exports.ENDPOINT_REST_PRODUCTIVITY = exports.ENDPOINT_REST_TASKS + '/completed/stats';
43
+ exports.ENDPOINT_REST_ACTIVITIES = 'activities';
44
+ exports.ENDPOINT_REST_UPLOADS = 'uploads';
45
+ exports.PROJECT_ARCHIVE = 'archive';
46
+ exports.PROJECT_UNARCHIVE = 'unarchive';
47
+ exports.ENDPOINT_SYNC_QUICK_ADD = exports.ENDPOINT_REST_TASKS + '/quick';
48
+ exports.ENDPOINT_SYNC = 'sync';
49
+ exports.ENDPOINT_AUTHORIZATION = 'authorize';
50
+ exports.ENDPOINT_GET_TOKEN = 'access_token';
51
+ exports.ENDPOINT_REVOKE_TOKEN = 'access_tokens/revoke';
52
+ exports.ENDPOINT_REVOKE = 'revoke';
53
+ // Workspace endpoints
54
+ exports.ENDPOINT_WORKSPACE_INVITATIONS = 'workspaces/invitations';
55
+ exports.ENDPOINT_WORKSPACE_INVITATIONS_ALL = 'workspaces/invitations/all';
56
+ exports.ENDPOINT_WORKSPACE_INVITATIONS_DELETE = 'workspaces/invitations/delete';
57
+ exports.ENDPOINT_WORKSPACE_JOIN = 'workspaces/join';
58
+ exports.ENDPOINT_WORKSPACE_LOGO = 'workspaces/logo';
59
+ exports.ENDPOINT_WORKSPACE_PLAN_DETAILS = 'workspaces/plan_details';
60
+ exports.ENDPOINT_WORKSPACE_USERS = 'workspaces/users';
61
+ // Workspace invitation actions (require invite_code parameter)
62
+ function getWorkspaceInvitationAcceptEndpoint(inviteCode) {
63
+ return `workspaces/invitations/${inviteCode}/accept`;
64
+ }
65
+ function getWorkspaceInvitationRejectEndpoint(inviteCode) {
66
+ return `workspaces/invitations/${inviteCode}/reject`;
67
+ }
68
+ // Workspace projects (require workspace_id parameter)
69
+ function getWorkspaceActiveProjectsEndpoint(workspaceId) {
70
+ return `workspaces/${workspaceId}/projects/active`;
71
+ }
72
+ function getWorkspaceArchivedProjectsEndpoint(workspaceId) {
73
+ return `workspaces/${workspaceId}/projects/archived`;
74
+ }
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./TodoistApi"), exports);
17
+ __exportStar(require("./todoist-api"), exports);
18
18
  __exportStar(require("./authentication"), exports);
19
19
  __exportStar(require("./types"), exports);
20
20
  __exportStar(require("./utils"), exports);
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.paramsSerializer = paramsSerializer;
4
+ exports.isSuccess = isSuccess;
5
+ exports.request = request;
6
+ const errors_1 = require("./types/errors");
7
+ const http_1 = require("./types/http");
8
+ const uuid_1 = require("uuid");
9
+ const endpoints_1 = require("./consts/endpoints");
10
+ const case_conversion_1 = require("./utils/case-conversion");
11
+ const fetch_with_retry_1 = require("./utils/fetch-with-retry");
12
+ function paramsSerializer(params) {
13
+ const qs = new URLSearchParams();
14
+ Object.keys(params).forEach((key) => {
15
+ const value = params[key];
16
+ if (value != null) {
17
+ if (Array.isArray(value)) {
18
+ qs.append(key, value.join(','));
19
+ }
20
+ else {
21
+ qs.append(key, String(value));
22
+ }
23
+ }
24
+ });
25
+ return qs.toString();
26
+ }
27
+ const defaultHeaders = {
28
+ 'Content-Type': 'application/json',
29
+ };
30
+ function getAuthHeader(apiKey) {
31
+ return `Bearer ${apiKey}`;
32
+ }
33
+ function getRetryDelay(retryCount) {
34
+ return retryCount === 1 ? 0 : 500;
35
+ }
36
+ function getTodoistRequestError(args) {
37
+ const { error, originalStack } = args;
38
+ const requestError = new errors_1.TodoistRequestError(error.message);
39
+ requestError.stack = originalStack ? originalStack.stack : error.stack;
40
+ if ((0, http_1.isHttpError)(error)) {
41
+ requestError.httpStatusCode = error.status;
42
+ requestError.responseData = error.data;
43
+ }
44
+ return requestError;
45
+ }
46
+ function getRequestConfiguration(args) {
47
+ const { baseURL, apiToken, requestId, customHeaders } = args;
48
+ const authHeader = apiToken ? { Authorization: getAuthHeader(apiToken) } : undefined;
49
+ const requestIdHeader = requestId ? { 'X-Request-Id': requestId } : undefined;
50
+ const headers = Object.assign(Object.assign(Object.assign(Object.assign({}, defaultHeaders), authHeader), requestIdHeader), customHeaders);
51
+ return { baseURL, headers };
52
+ }
53
+ function getHttpClientConfig(args) {
54
+ const { baseURL, apiToken, requestId, customHeaders } = args;
55
+ const configuration = getRequestConfiguration({ baseURL, apiToken, requestId, customHeaders });
56
+ return Object.assign(Object.assign({}, configuration), { timeout: 30000, retry: {
57
+ retries: 3,
58
+ retryCondition: http_1.isNetworkError,
59
+ retryDelay: getRetryDelay,
60
+ } });
61
+ }
62
+ function isSuccess(response) {
63
+ return response.status >= 200 && response.status < 300;
64
+ }
65
+ async function request(args) {
66
+ const { httpMethod, baseUri, relativePath, apiToken, payload, requestId: initialRequestId, hasSyncCommands, customHeaders, } = args;
67
+ // Capture original stack for better error reporting
68
+ const originalStack = new Error();
69
+ try {
70
+ let requestId = initialRequestId;
71
+ // Sync api don't allow a request id in the CORS
72
+ if (httpMethod === 'POST' && !requestId && !baseUri.includes(endpoints_1.API_BASE_URI)) {
73
+ requestId = (0, uuid_1.v4)();
74
+ }
75
+ const config = getHttpClientConfig({ baseURL: baseUri, apiToken, requestId, customHeaders });
76
+ const url = `${baseUri}${relativePath}`;
77
+ const fetchOptions = {
78
+ method: httpMethod,
79
+ headers: config.headers,
80
+ timeout: config.timeout,
81
+ };
82
+ let finalUrl = url;
83
+ switch (httpMethod) {
84
+ case 'GET':
85
+ // For GET requests, add query parameters to URL
86
+ if (payload) {
87
+ const queryString = paramsSerializer(payload);
88
+ if (queryString) {
89
+ const separator = url.includes('?') ? '&' : '?';
90
+ finalUrl = `${url}${separator}${queryString}`;
91
+ }
92
+ }
93
+ break;
94
+ case 'POST':
95
+ case 'PUT': {
96
+ // Convert payload from camelCase to snake_case
97
+ const convertedPayload = payload ? (0, case_conversion_1.snakeCaseKeys)(payload) : payload;
98
+ const body = hasSyncCommands
99
+ ? JSON.stringify(convertedPayload)
100
+ : JSON.stringify(convertedPayload);
101
+ fetchOptions.body = body;
102
+ break;
103
+ }
104
+ case 'DELETE':
105
+ // DELETE requests don't have a body
106
+ break;
107
+ }
108
+ // Make the request
109
+ const response = await (0, fetch_with_retry_1.fetchWithRetry)({
110
+ url: finalUrl,
111
+ options: fetchOptions,
112
+ retryConfig: config.retry,
113
+ });
114
+ // Convert snake_case response to camelCase
115
+ const convertedData = (0, case_conversion_1.camelCaseKeys)(response.data);
116
+ return Object.assign(Object.assign({}, response), { data: convertedData });
117
+ }
118
+ catch (error) {
119
+ if (!(error instanceof Error)) {
120
+ throw new Error('An unknown error occurred during the request');
121
+ }
122
+ throw getTodoistRequestError({ error, originalStack });
123
+ }
124
+ }
@@ -7,5 +7,5 @@ function assertInstance(value, type) {
7
7
  if (value instanceof type) {
8
8
  return;
9
9
  }
10
- throw new TypeError("Unexpected type ".concat(typeof value));
10
+ throw new TypeError(`Unexpected type ${typeof value}`);
11
11
  }
@@ -34,9 +34,13 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.setupRestClientMock = setupRestClientMock;
37
- var restClient = __importStar(require("../restClient"));
38
- function setupRestClientMock(responseData, status) {
39
- if (status === void 0) { status = 200; }
40
- var response = { status: status, data: responseData };
37
+ const restClient = __importStar(require("../rest-client"));
38
+ function setupRestClientMock(responseData, status = 200) {
39
+ const response = {
40
+ status,
41
+ statusText: status === 200 ? 'OK' : 'Error',
42
+ headers: {},
43
+ data: responseData,
44
+ };
41
45
  return jest.spyOn(restClient, 'request').mockResolvedValue(response);
42
46
  }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpResponse = exports.http = exports.server = exports.handlers = void 0;
4
+ const node_1 = require("msw/node");
5
+ // Default handlers for common API responses
6
+ exports.handlers = [
7
+ // Default handlers can be added here for common endpoints
8
+ // Individual test files will add their own specific handlers
9
+ ];
10
+ // Create MSW server instance
11
+ exports.server = (0, node_1.setupServer)(...exports.handlers);
12
+ // Setup MSW for tests
13
+ beforeAll(() => {
14
+ exports.server.listen({
15
+ onUnhandledRequest: 'warn', // Log warnings for unhandled requests during development
16
+ });
17
+ });
18
+ afterEach(() => {
19
+ exports.server.resetHandlers(); // Reset handlers between tests
20
+ });
21
+ afterAll(() => {
22
+ exports.server.close(); // Clean up after all tests
23
+ });
24
+ // Export MSW utilities for use in tests
25
+ var msw_1 = require("msw");
26
+ Object.defineProperty(exports, "http", { enumerable: true, get: function () { return msw_1.http; } });
27
+ Object.defineProperty(exports, "HttpResponse", { enumerable: true, get: function () { return msw_1.HttpResponse; } });
@@ -1,18 +1,7 @@
1
1
  "use strict";
2
- var __assign = (this && this.__assign) || function () {
3
- __assign = Object.assign || function(t) {
4
- for (var s, i = 1, n = arguments.length; i < n; i++) {
5
- s = arguments[i];
6
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
- t[p] = s[p];
8
- }
9
- return t;
10
- };
11
- return __assign.apply(this, arguments);
12
- };
13
2
  Object.defineProperty(exports, "__esModule", { value: true });
14
3
  exports.COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT = exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT = exports.COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL = exports.RAW_COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL = exports.COMMENT_WITH_OPTIONALS_AS_NULL_TASK = exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_TASK = exports.INVALID_COMMENT = exports.DEFAULT_COMMENT = exports.DEFAULT_RAW_COMMENT = exports.INVALID_ATTACHMENT = exports.DEFAULT_ATTACHMENT = exports.INVALID_USER = exports.DEFAULT_USER = exports.INVALID_LABEL = exports.DEFAULT_LABEL = exports.INVALID_SECTION = exports.DEFAULT_SECTION = exports.PROJECT_WITH_OPTIONALS_AS_NULL = exports.INVALID_PROJECT = exports.DEFAULT_PROJECT = exports.TASK_WITH_OPTIONALS_AS_NULL = exports.INVALID_TASK = exports.DEFAULT_TASK = exports.DEFAULT_DEADLINE = exports.DEFAULT_DURATION = exports.DEFAULT_DUE_DATE = exports.INVALID_ENTITY_ID = exports.DEFAULT_REQUEST_ID = exports.DEFAULT_AUTH_TOKEN = exports.DEFAULT_PROJECT_VIEW_STYLE = exports.DEFAULT_PROJECT_NAME = exports.DEFAULT_PROJECT_ID = exports.DEFAULT_ORDER = exports.DEFAULT_TASK_PRIORITY = exports.DEFAULT_TASK_DESCRIPTION = exports.DEFAULT_TASK_CONTENT = exports.DEFAULT_TASK_ID = void 0;
15
- var urlHelpers_1 = require("../utils/urlHelpers");
4
+ const url_helpers_1 = require("../utils/url-helpers");
16
5
  exports.DEFAULT_TASK_ID = '1234';
17
6
  exports.DEFAULT_TASK_CONTENT = 'This is a task';
18
7
  exports.DEFAULT_TASK_DESCRIPTION = 'A description';
@@ -21,32 +10,32 @@ exports.DEFAULT_ORDER = 3;
21
10
  exports.DEFAULT_PROJECT_ID = '123';
22
11
  exports.DEFAULT_PROJECT_NAME = 'This is a project';
23
12
  exports.DEFAULT_PROJECT_VIEW_STYLE = 'list';
24
- var DEFAULT_LABEL_ID = '456';
25
- var DEFAULT_LABEL_NAME = 'This is a label';
26
- var DEFAULT_SECTION_ID = '456';
27
- var DEFAULT_SECTION_NAME = 'This is a section';
28
- var DEFAULT_PARENT_ID = '5678';
29
- var DEFAULT_ASSIGNEE = '1234';
30
- var DEFAULT_CREATOR = '1234';
31
- var DEFAULT_DATE = '2020-09-08T12:00:00Z';
32
- var DEFAULT_ENTITY_COLOR = 'berry_red';
33
- var DEFAULT_LABELS = ['personal', 'work', 'hobby'];
34
- var DEFAULT_USER_ID = '5';
35
- var DEFAULT_USER_NAME = 'A User';
36
- var DEFAULT_USER_EMAIL = 'atestuser@doist.com';
37
- var DEFAULT_COMMENT_ID = '4';
38
- var DEFAULT_COMMENT_CONTENT = 'A comment';
39
- var DEFAULT_COMMENT_REACTIONS = { '👍': ['1234', '5678'] };
40
- var DEFAULT_NOTE_COUNT = 0;
41
- var DEFAULT_CAN_ASSIGN_TASKS = true;
42
- var DEFAULT_IS_ARCHIVED = false;
43
- var DEFAULT_IS_DELETED = false;
44
- var DEFAULT_IS_FROZEN = false;
45
- var DEFAULT_IS_COLLAPSED = false;
13
+ const DEFAULT_LABEL_ID = '456';
14
+ const DEFAULT_LABEL_NAME = 'This is a label';
15
+ const DEFAULT_SECTION_ID = '456';
16
+ const DEFAULT_SECTION_NAME = 'This is a section';
17
+ const DEFAULT_PARENT_ID = '5678';
18
+ const DEFAULT_ASSIGNEE = '1234';
19
+ const DEFAULT_CREATOR = '1234';
20
+ const DEFAULT_DATE = '2020-09-08T12:00:00Z';
21
+ const DEFAULT_ENTITY_COLOR = 'berry_red';
22
+ const DEFAULT_LABELS = ['personal', 'work', 'hobby'];
23
+ const DEFAULT_USER_ID = '5';
24
+ const DEFAULT_USER_NAME = 'A User';
25
+ const DEFAULT_USER_EMAIL = 'atestuser@doist.com';
26
+ const DEFAULT_COMMENT_ID = '4';
27
+ const DEFAULT_COMMENT_CONTENT = 'A comment';
28
+ const DEFAULT_COMMENT_REACTIONS = { '👍': ['1234', '5678'] };
29
+ const DEFAULT_NOTE_COUNT = 0;
30
+ const DEFAULT_CAN_ASSIGN_TASKS = true;
31
+ const DEFAULT_IS_ARCHIVED = false;
32
+ const DEFAULT_IS_DELETED = false;
33
+ const DEFAULT_IS_FROZEN = false;
34
+ const DEFAULT_IS_COLLAPSED = false;
46
35
  // URL constants using the helper functions
47
- var DEFAULT_TASK_URL = (0, urlHelpers_1.getTaskUrl)(exports.DEFAULT_TASK_ID, exports.DEFAULT_TASK_CONTENT);
48
- var DEFAULT_PROJECT_URL = (0, urlHelpers_1.getProjectUrl)(exports.DEFAULT_PROJECT_ID, exports.DEFAULT_PROJECT_NAME);
49
- var DEFAULT_SECTION_URL = (0, urlHelpers_1.getSectionUrl)(DEFAULT_SECTION_ID, DEFAULT_SECTION_NAME);
36
+ const DEFAULT_TASK_URL = (0, url_helpers_1.getTaskUrl)(exports.DEFAULT_TASK_ID, exports.DEFAULT_TASK_CONTENT);
37
+ const DEFAULT_PROJECT_URL = (0, url_helpers_1.getProjectUrl)(exports.DEFAULT_PROJECT_ID, exports.DEFAULT_PROJECT_NAME);
38
+ const DEFAULT_SECTION_URL = (0, url_helpers_1.getSectionUrl)(DEFAULT_SECTION_ID, DEFAULT_SECTION_NAME);
50
39
  exports.DEFAULT_AUTH_TOKEN = 'AToken';
51
40
  exports.DEFAULT_REQUEST_ID = 'ARequestID';
52
41
  exports.INVALID_ENTITY_ID = 1234;
@@ -92,7 +81,7 @@ exports.DEFAULT_TASK = {
92
81
  isCollapsed: DEFAULT_IS_COLLAPSED,
93
82
  url: DEFAULT_TASK_URL,
94
83
  };
95
- exports.INVALID_TASK = __assign(__assign({}, exports.DEFAULT_TASK), { due: '2020-01-31' });
84
+ exports.INVALID_TASK = Object.assign(Object.assign({}, exports.DEFAULT_TASK), { due: '2020-01-31' });
96
85
  exports.TASK_WITH_OPTIONALS_AS_NULL = {
97
86
  userId: DEFAULT_CREATOR,
98
87
  id: exports.DEFAULT_TASK_ID,
@@ -141,8 +130,8 @@ exports.DEFAULT_PROJECT = {
141
130
  isCollapsed: DEFAULT_IS_COLLAPSED,
142
131
  url: DEFAULT_PROJECT_URL,
143
132
  };
144
- exports.INVALID_PROJECT = __assign(__assign({}, exports.DEFAULT_PROJECT), { name: 123 });
145
- exports.PROJECT_WITH_OPTIONALS_AS_NULL = __assign(__assign({}, exports.DEFAULT_PROJECT), { parentId: null });
133
+ exports.INVALID_PROJECT = Object.assign(Object.assign({}, exports.DEFAULT_PROJECT), { name: 123 });
134
+ exports.PROJECT_WITH_OPTIONALS_AS_NULL = Object.assign(Object.assign({}, exports.DEFAULT_PROJECT), { parentId: null });
146
135
  exports.DEFAULT_SECTION = {
147
136
  id: DEFAULT_SECTION_ID,
148
137
  userId: DEFAULT_USER_ID,
@@ -157,7 +146,7 @@ exports.DEFAULT_SECTION = {
157
146
  isCollapsed: false,
158
147
  url: DEFAULT_SECTION_URL,
159
148
  };
160
- exports.INVALID_SECTION = __assign(__assign({}, exports.DEFAULT_SECTION), { projectId: undefined });
149
+ exports.INVALID_SECTION = Object.assign(Object.assign({}, exports.DEFAULT_SECTION), { projectId: undefined });
161
150
  exports.DEFAULT_LABEL = {
162
151
  id: DEFAULT_LABEL_ID,
163
152
  name: DEFAULT_LABEL_NAME,
@@ -165,20 +154,20 @@ exports.DEFAULT_LABEL = {
165
154
  order: exports.DEFAULT_ORDER,
166
155
  isFavorite: false,
167
156
  };
168
- exports.INVALID_LABEL = __assign(__assign({}, exports.DEFAULT_LABEL), { isFavorite: 'true' });
157
+ exports.INVALID_LABEL = Object.assign(Object.assign({}, exports.DEFAULT_LABEL), { isFavorite: 'true' });
169
158
  exports.DEFAULT_USER = {
170
159
  id: DEFAULT_USER_ID,
171
160
  name: DEFAULT_USER_NAME,
172
161
  email: DEFAULT_USER_EMAIL,
173
162
  };
174
- exports.INVALID_USER = __assign(__assign({}, exports.DEFAULT_USER), { email: undefined });
163
+ exports.INVALID_USER = Object.assign(Object.assign({}, exports.DEFAULT_USER), { email: undefined });
175
164
  exports.DEFAULT_ATTACHMENT = {
176
165
  resourceType: 'file',
177
166
  fileType: 'image/png',
178
167
  fileUrl: 'https://someurl.com/image.jpg',
179
168
  uploadState: 'completed',
180
169
  };
181
- exports.INVALID_ATTACHMENT = __assign(__assign({}, exports.DEFAULT_ATTACHMENT), { uploadState: 'something random' });
170
+ exports.INVALID_ATTACHMENT = Object.assign(Object.assign({}, exports.DEFAULT_ATTACHMENT), { uploadState: 'something random' });
182
171
  exports.DEFAULT_RAW_COMMENT = {
183
172
  id: DEFAULT_COMMENT_ID,
184
173
  postedUid: DEFAULT_USER_ID,
@@ -190,11 +179,11 @@ exports.DEFAULT_RAW_COMMENT = {
190
179
  reactions: DEFAULT_COMMENT_REACTIONS,
191
180
  itemId: exports.DEFAULT_TASK_ID,
192
181
  };
193
- exports.DEFAULT_COMMENT = __assign(__assign({}, exports.DEFAULT_RAW_COMMENT), { taskId: exports.DEFAULT_RAW_COMMENT.itemId, itemId: undefined });
194
- exports.INVALID_COMMENT = __assign(__assign({}, exports.DEFAULT_RAW_COMMENT), { isDeleted: 'true' });
195
- exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_TASK = __assign(__assign({}, exports.DEFAULT_RAW_COMMENT), { fileAttachment: null, uidsToNotify: null, reactions: null });
196
- exports.COMMENT_WITH_OPTIONALS_AS_NULL_TASK = __assign(__assign({}, exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_TASK), { taskId: exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_TASK.itemId, itemId: undefined });
197
- exports.RAW_COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL = __assign(__assign({}, exports.DEFAULT_RAW_COMMENT), { fileAttachment: {
182
+ exports.DEFAULT_COMMENT = Object.assign(Object.assign({}, exports.DEFAULT_RAW_COMMENT), { taskId: exports.DEFAULT_RAW_COMMENT.itemId, itemId: undefined });
183
+ exports.INVALID_COMMENT = Object.assign(Object.assign({}, exports.DEFAULT_RAW_COMMENT), { isDeleted: 'true' });
184
+ exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_TASK = Object.assign(Object.assign({}, exports.DEFAULT_RAW_COMMENT), { fileAttachment: null, uidsToNotify: null, reactions: null });
185
+ exports.COMMENT_WITH_OPTIONALS_AS_NULL_TASK = Object.assign(Object.assign({}, exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_TASK), { taskId: exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_TASK.itemId, itemId: undefined });
186
+ exports.RAW_COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL = Object.assign(Object.assign({}, exports.DEFAULT_RAW_COMMENT), { fileAttachment: {
198
187
  resourceType: 'file',
199
188
  fileName: null,
200
189
  fileSize: null,
@@ -207,6 +196,6 @@ exports.RAW_COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL = __assign(__assign({
207
196
  url: null,
208
197
  title: null,
209
198
  } });
210
- exports.COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL = __assign(__assign({}, exports.RAW_COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL), { taskId: exports.RAW_COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL.itemId, itemId: undefined });
211
- exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT = __assign(__assign({}, exports.DEFAULT_RAW_COMMENT), { itemId: undefined, projectId: exports.DEFAULT_PROJECT_ID });
212
- exports.COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT = __assign(__assign({}, exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT), { taskId: undefined });
199
+ exports.COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL = Object.assign(Object.assign({}, exports.RAW_COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL), { taskId: exports.RAW_COMMENT_WITH_ATTACHMENT_WITH_OPTIONALS_AS_NULL.itemId, itemId: undefined });
200
+ exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT = Object.assign(Object.assign({}, exports.DEFAULT_RAW_COMMENT), { itemId: undefined, projectId: exports.DEFAULT_PROJECT_ID });
201
+ exports.COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT = Object.assign(Object.assign({}, exports.RAW_COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT), { taskId: undefined });