@doist/todoist-sdk 8.0.0 → 8.1.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.
@@ -1,11 +1,23 @@
1
1
  "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
2
13
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PERMISSIONS = void 0;
14
+ exports.TOKEN_ENDPOINT_AUTH_METHODS = exports.PERMISSIONS = void 0;
4
15
  exports.getAuthStateParameter = getAuthStateParameter;
5
16
  exports.getAuthorizationUrl = getAuthorizationUrl;
6
17
  exports.getAuthToken = getAuthToken;
7
18
  exports.revokeToken = revokeToken;
8
19
  exports.migratePersonalToken = migratePersonalToken;
20
+ exports.registerClient = registerClient;
9
21
  const http_client_1 = require("./transport/http-client");
10
22
  const uuid_1 = require("uuid");
11
23
  const types_1 = require("./types");
@@ -19,6 +31,12 @@ exports.PERMISSIONS = [
19
31
  'project:delete',
20
32
  'backups:read',
21
33
  ];
34
+ /** Supported token endpoint authentication methods for dynamic client registration. */
35
+ exports.TOKEN_ENDPOINT_AUTH_METHODS = [
36
+ 'client_secret_post',
37
+ 'client_secret_basic',
38
+ 'none',
39
+ ];
22
40
  /**
23
41
  * Creates a Basic Authentication header value from client credentials.
24
42
  * @param clientId - The OAuth client ID
@@ -48,13 +66,17 @@ function getAuthStateParameter() {
48
66
  /**
49
67
  * Generates the authorization URL for the OAuth2 flow.
50
68
  *
69
+ * The `clientId` can be either a traditional client ID string (e.g. from
70
+ * {@link registerClient}) or an HTTPS URL pointing to a client metadata document,
71
+ * as defined in {@link https://drafts.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/ RFC draft-ietf-oauth-client-id-metadata-document}.
72
+ *
51
73
  * @example
52
74
  * ```typescript
53
- * const url = getAuthorizationUrl(
54
- * 'your-client-id',
55
- * ['data:read', 'task:add'],
56
- * state
57
- * )
75
+ * const url = getAuthorizationUrl({
76
+ * clientId: 'your-client-id',
77
+ * permissions: ['data:read', 'task:add'],
78
+ * state,
79
+ * })
58
80
  * // Redirect user to url
59
81
  * ```
60
82
  *
@@ -66,7 +88,7 @@ function getAuthorizationUrl({ clientId, permissions, state, baseUrl, }) {
66
88
  throw new Error('At least one scope value should be passed for permissions.');
67
89
  }
68
90
  const scope = permissions.join(',');
69
- return `${(0, endpoints_1.getAuthBaseUri)(baseUrl)}${endpoints_1.ENDPOINT_AUTHORIZATION}?client_id=${clientId}&scope=${scope}&state=${state}`;
91
+ return `${(0, endpoints_1.getAuthBaseUri)(baseUrl)}${endpoints_1.ENDPOINT_AUTHORIZATION}?client_id=${encodeURIComponent(clientId)}&scope=${scope}&state=${state}`;
70
92
  }
71
93
  /**
72
94
  * Exchanges an authorization code for an access token.
@@ -202,3 +224,48 @@ async function migratePersonalToken(args, options) {
202
224
  throw new types_1.TodoistRequestError('Personal token migration failed.', err.httpStatusCode, err.responseData);
203
225
  }
204
226
  }
227
+ /**
228
+ * Registers a new OAuth client via Dynamic Client Registration (RFC 7591).
229
+ *
230
+ * @example
231
+ * ```typescript
232
+ * const client = await registerClient({
233
+ * redirectUris: ['https://example.com/callback'],
234
+ * clientName: 'My App',
235
+ * scope: ['data:read_write', 'task:add'],
236
+ * })
237
+ * // Use client.clientId and client.clientSecret for OAuth flows
238
+ * ```
239
+ *
240
+ * @returns The registered client details
241
+ * @throws {@link TodoistRequestError} If the registration fails
242
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc7591 RFC 7591}
243
+ */
244
+ async function registerClient(args, options) {
245
+ var _a, _b;
246
+ const baseUrl = options === null || options === void 0 ? void 0 : options.baseUrl;
247
+ const customFetch = options === null || options === void 0 ? void 0 : options.customFetch;
248
+ try {
249
+ const response = await (0, http_client_1.request)({
250
+ httpMethod: 'POST',
251
+ baseUri: (0, endpoints_1.getAuthBaseUri)(baseUrl),
252
+ relativePath: endpoints_1.ENDPOINT_REGISTER,
253
+ apiToken: undefined,
254
+ payload: Object.assign(Object.assign({}, args), { scope: (_a = args.scope) === null || _a === void 0 ? void 0 : _a.join(' ') }),
255
+ customFetch,
256
+ });
257
+ if (!(0, http_client_1.isSuccess)(response) || !((_b = response.data) === null || _b === void 0 ? void 0 : _b.clientId)) {
258
+ throw new types_1.TodoistRequestError('Dynamic client registration failed.', response.status, response.data);
259
+ }
260
+ const _c = response.data, { clientIdIssuedAt, clientSecretExpiresAt, scope } = _c, rest = __rest(_c, ["clientIdIssuedAt", "clientSecretExpiresAt", "scope"]);
261
+ return Object.assign(Object.assign({}, rest), { scope: scope ? scope.split(' ') : undefined, clientIdIssuedAt: clientIdIssuedAt !== undefined ? new Date(clientIdIssuedAt * 1000) : undefined, clientSecretExpiresAt: clientSecretExpiresAt === undefined
262
+ ? undefined
263
+ : clientSecretExpiresAt === 0
264
+ ? null
265
+ : new Date(clientSecretExpiresAt * 1000) });
266
+ }
267
+ catch (error) {
268
+ const err = error;
269
+ throw new types_1.TodoistRequestError('Dynamic client registration failed.', err.httpStatusCode, err.responseData);
270
+ }
271
+ }
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ENDPOINT_REST_ID_MAPPINGS = exports.ENDPOINT_REST_EMAILS = exports.ENDPOINT_REST_BACKUPS_DOWNLOAD = exports.ENDPOINT_REST_BACKUPS = exports.ENDPOINT_REST_ACCESS_TOKENS_MIGRATE = exports.ENDPOINT_REST_TEMPLATES_IMPORT_FROM_ID = exports.ENDPOINT_REST_TEMPLATES_IMPORT_FROM_FILE = exports.ENDPOINT_REST_TEMPLATES_CREATE_FROM_FILE = exports.ENDPOINT_REST_TEMPLATES_URL = exports.ENDPOINT_REST_TEMPLATES_FILE = exports.ENDPOINT_REST_TASKS_COMPLETED = exports.ENDPOINT_REST_PROJECTS_MOVE_TO_PERSONAL = exports.ENDPOINT_REST_PROJECTS_MOVE_TO_WORKSPACE = exports.SECTION_UNARCHIVE = exports.SECTION_ARCHIVE = exports.ENDPOINT_REST_PROJECT_JOIN = exports.ENDPOINT_REST_PROJECT_FULL = exports.ENDPOINT_REST_PROJECTS_PERMISSIONS = exports.ENDPOINT_REST_PROJECTS_ARCHIVED_COUNT = 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_SEARCH = exports.ENDPOINT_REST_PROJECTS = exports.ENDPOINT_REST_TASK_MOVE = exports.ENDPOINT_REST_TASK_REOPEN = exports.ENDPOINT_REST_TASK_CLOSE = exports.ENDPOINT_REST_LOCATION_REMINDERS = exports.ENDPOINT_REST_REMINDERS = 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_SEARCH = exports.ENDPOINT_REST_LABELS = exports.ENDPOINT_REST_SECTIONS_SEARCH = 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.ENDPOINT_WORKSPACE_MEMBERS = 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_REST_WORKSPACES = exports.ENDPOINT_REST_FOLDERS = exports.ENDPOINT_REVOKE = exports.ENDPOINT_GET_TOKEN = exports.ENDPOINT_AUTHORIZATION = exports.ENDPOINT_SYNC = exports.ENDPOINT_SYNC_QUICK_ADD = exports.ENDPOINT_REST_MOVED_IDS = void 0;
4
+ exports.ENDPOINT_WORKSPACE_MEMBERS = 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_REST_WORKSPACES = exports.ENDPOINT_REST_FOLDERS = exports.ENDPOINT_REGISTER = exports.ENDPOINT_REVOKE = exports.ENDPOINT_GET_TOKEN = exports.ENDPOINT_AUTHORIZATION = exports.ENDPOINT_SYNC = exports.ENDPOINT_SYNC_QUICK_ADD = exports.ENDPOINT_REST_MOVED_IDS = void 0;
5
5
  exports.getSyncBaseUri = getSyncBaseUri;
6
6
  exports.getAuthBaseUri = getAuthBaseUri;
7
7
  exports.getWorkspaceInvitationAcceptEndpoint = getWorkspaceInvitationAcceptEndpoint;
@@ -84,6 +84,7 @@ exports.ENDPOINT_SYNC = 'sync';
84
84
  exports.ENDPOINT_AUTHORIZATION = 'authorize';
85
85
  exports.ENDPOINT_GET_TOKEN = 'access_token';
86
86
  exports.ENDPOINT_REVOKE = 'revoke';
87
+ exports.ENDPOINT_REGISTER = 'register';
87
88
  exports.ENDPOINT_REST_FOLDERS = 'folders';
88
89
  // Workspace endpoints
89
90
  exports.ENDPOINT_REST_WORKSPACES = 'workspaces';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_FOLDER = 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_LOCATION_REMINDER = exports.DEFAULT_ABSOLUTE_REMINDER = exports.DEFAULT_RELATIVE_REMINDER = exports.DEFAULT_REMINDER_ID = 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;
3
+ exports.DEFAULT_NOTE = exports.DEFAULT_COLLABORATOR_STATE = exports.DEFAULT_COLLABORATOR = exports.DEFAULT_FOLDER = 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_LOCATION_REMINDER = exports.DEFAULT_ABSOLUTE_REMINDER = exports.DEFAULT_RELATIVE_REMINDER = exports.DEFAULT_REMINDER_ID = 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.DEFAULT_WORKSPACE_PROJECT = 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;
4
4
  const url_helpers_1 = require("../utils/url-helpers");
5
5
  exports.DEFAULT_TASK_ID = '1234';
6
6
  exports.DEFAULT_TASK_CONTENT = 'This is a task';
@@ -131,6 +131,32 @@ exports.DEFAULT_PROJECT = {
131
131
  };
132
132
  exports.INVALID_PROJECT = Object.assign(Object.assign({}, exports.DEFAULT_PROJECT), { name: 123 });
133
133
  exports.PROJECT_WITH_OPTIONALS_AS_NULL = Object.assign(Object.assign({}, exports.DEFAULT_PROJECT), { parentId: null });
134
+ exports.DEFAULT_WORKSPACE_PROJECT = {
135
+ id: exports.DEFAULT_PROJECT_ID,
136
+ name: exports.DEFAULT_PROJECT_NAME,
137
+ color: DEFAULT_ENTITY_COLOR,
138
+ childOrder: exports.DEFAULT_ORDER,
139
+ isFavorite: false,
140
+ isShared: true,
141
+ viewStyle: exports.DEFAULT_PROJECT_VIEW_STYLE,
142
+ canAssignTasks: DEFAULT_CAN_ASSIGN_TASKS,
143
+ isArchived: DEFAULT_IS_ARCHIVED,
144
+ isDeleted: DEFAULT_IS_DELETED,
145
+ isFrozen: DEFAULT_IS_FROZEN,
146
+ createdAt: DEFAULT_DATE,
147
+ updatedAt: DEFAULT_DATE,
148
+ defaultOrder: exports.DEFAULT_ORDER,
149
+ description: '',
150
+ isCollapsed: DEFAULT_IS_COLLAPSED,
151
+ url: DEFAULT_PROJECT_URL,
152
+ workspaceId: '100',
153
+ folderId: null,
154
+ collaboratorRoleDefault: 'READ_WRITE',
155
+ isInviteOnly: false,
156
+ isLinkSharingEnabled: true,
157
+ role: 'ADMIN',
158
+ status: 'ACTIVE',
159
+ };
134
160
  exports.DEFAULT_SECTION = {
135
161
  id: DEFAULT_SECTION_ID,
136
162
  userId: DEFAULT_USER_ID,
@@ -238,3 +264,27 @@ exports.DEFAULT_FOLDER = {
238
264
  childOrder: 3,
239
265
  isDeleted: false,
240
266
  };
267
+ exports.DEFAULT_COLLABORATOR = {
268
+ id: DEFAULT_USER_ID,
269
+ email: DEFAULT_USER_EMAIL,
270
+ fullName: DEFAULT_USER_NAME,
271
+ timezone: 'UTC',
272
+ imageId: null,
273
+ };
274
+ exports.DEFAULT_COLLABORATOR_STATE = {
275
+ userId: DEFAULT_USER_ID,
276
+ projectId: exports.DEFAULT_PROJECT_ID,
277
+ state: 'active',
278
+ isDeleted: false,
279
+ };
280
+ exports.DEFAULT_NOTE = {
281
+ id: DEFAULT_COMMENT_ID,
282
+ projectId: exports.DEFAULT_PROJECT_ID,
283
+ content: DEFAULT_COMMENT_CONTENT,
284
+ postedAt: DEFAULT_DATE,
285
+ fileAttachment: exports.DEFAULT_ATTACHMENT,
286
+ postedUid: DEFAULT_USER_ID,
287
+ uidsToNotify: null,
288
+ reactions: DEFAULT_COMMENT_REACTIONS,
289
+ isDeleted: false,
290
+ };
@@ -12,6 +12,7 @@ var __rest = (this && this.__rest) || function (s, e) {
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.TodoistApi = void 0;
15
+ const projects_1 = require("./types/projects");
15
16
  const reminders_1 = require("./types/reminders");
16
17
  const http_client_1 = require("./transport/http-client");
17
18
  const endpoints_1 = require("./consts/endpoints");
@@ -771,15 +772,18 @@ class TodoistApi {
771
772
  };
772
773
  }
773
774
  /**
774
- * Joins a shared project by its ID.
775
+ * Joins a workspace project.
775
776
  *
776
- * @param id - The unique identifier of the project to join.
777
+ * Only used for workspaces — this endpoint is used to join a workspace project
778
+ * by a workspace user.
779
+ *
780
+ * @param id - The unique identifier of the workspace project to join.
777
781
  * @param requestId - Optional custom identifier for the request.
778
- * @returns A promise that resolves to the joined project.
782
+ * @returns A promise that resolves to the full project data after joining.
779
783
  */
780
784
  async joinProject(id, requestId) {
781
785
  zod_1.z.string().parse(id);
782
- const response = await (0, http_client_1.request)({
786
+ const { data } = await (0, http_client_1.request)({
783
787
  httpMethod: 'POST',
784
788
  baseUri: this.syncApiBase,
785
789
  relativePath: generatePath(endpoints_1.ENDPOINT_REST_PROJECTS, id, endpoints_1.ENDPOINT_REST_PROJECT_JOIN),
@@ -787,7 +791,16 @@ class TodoistApi {
787
791
  customFetch: this.customFetch,
788
792
  requestId: requestId,
789
793
  });
790
- return (0, validators_1.validateProject)(response.data);
794
+ return {
795
+ project: projects_1.WorkspaceProjectSchema.parse(data.project),
796
+ tasks: (0, validators_1.validateTaskArray)(data.tasks),
797
+ sections: (0, validators_1.validateSectionArray)(data.sections),
798
+ comments: (0, validators_1.validateNoteArray)(data.comments),
799
+ collaborators: (0, validators_1.validateCollaboratorArray)(data.collaborators),
800
+ collaboratorStates: (0, validators_1.validateCollaboratorStateArray)(data.collaboratorStates),
801
+ folder: data.folder ? (0, validators_1.validateFolder)(data.folder) : null,
802
+ subprojects: data.subprojects.map((p) => projects_1.WorkspaceProjectSchema.parse(p)),
803
+ };
791
804
  }
792
805
  /**
793
806
  * Retrieves a list of collaborators for a specific project.
@@ -1,7 +1,18 @@
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
+ };
1
12
  import { request, isSuccess } from './transport/http-client.js';
2
13
  import { v4 as uuid } from 'uuid';
3
14
  import { TodoistRequestError } from './types/index.js';
4
- import { getAuthBaseUri, getSyncBaseUri, ENDPOINT_AUTHORIZATION, ENDPOINT_GET_TOKEN, ENDPOINT_REVOKE, ENDPOINT_REST_ACCESS_TOKENS_MIGRATE, } from './consts/endpoints.js';
15
+ import { getAuthBaseUri, getSyncBaseUri, ENDPOINT_AUTHORIZATION, ENDPOINT_GET_TOKEN, ENDPOINT_REVOKE, ENDPOINT_REGISTER, ENDPOINT_REST_ACCESS_TOKENS_MIGRATE, } from './consts/endpoints.js';
5
16
  /** Available OAuth2 permission scopes. */
6
17
  export const PERMISSIONS = [
7
18
  'task:add',
@@ -11,6 +22,12 @@ export const PERMISSIONS = [
11
22
  'project:delete',
12
23
  'backups:read',
13
24
  ];
25
+ /** Supported token endpoint authentication methods for dynamic client registration. */
26
+ export const TOKEN_ENDPOINT_AUTH_METHODS = [
27
+ 'client_secret_post',
28
+ 'client_secret_basic',
29
+ 'none',
30
+ ];
14
31
  /**
15
32
  * Creates a Basic Authentication header value from client credentials.
16
33
  * @param clientId - The OAuth client ID
@@ -40,13 +57,17 @@ export function getAuthStateParameter() {
40
57
  /**
41
58
  * Generates the authorization URL for the OAuth2 flow.
42
59
  *
60
+ * The `clientId` can be either a traditional client ID string (e.g. from
61
+ * {@link registerClient}) or an HTTPS URL pointing to a client metadata document,
62
+ * as defined in {@link https://drafts.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/ RFC draft-ietf-oauth-client-id-metadata-document}.
63
+ *
43
64
  * @example
44
65
  * ```typescript
45
- * const url = getAuthorizationUrl(
46
- * 'your-client-id',
47
- * ['data:read', 'task:add'],
48
- * state
49
- * )
66
+ * const url = getAuthorizationUrl({
67
+ * clientId: 'your-client-id',
68
+ * permissions: ['data:read', 'task:add'],
69
+ * state,
70
+ * })
50
71
  * // Redirect user to url
51
72
  * ```
52
73
  *
@@ -58,7 +79,7 @@ export function getAuthorizationUrl({ clientId, permissions, state, baseUrl, })
58
79
  throw new Error('At least one scope value should be passed for permissions.');
59
80
  }
60
81
  const scope = permissions.join(',');
61
- return `${getAuthBaseUri(baseUrl)}${ENDPOINT_AUTHORIZATION}?client_id=${clientId}&scope=${scope}&state=${state}`;
82
+ return `${getAuthBaseUri(baseUrl)}${ENDPOINT_AUTHORIZATION}?client_id=${encodeURIComponent(clientId)}&scope=${scope}&state=${state}`;
62
83
  }
63
84
  /**
64
85
  * Exchanges an authorization code for an access token.
@@ -194,3 +215,48 @@ export async function migratePersonalToken(args, options) {
194
215
  throw new TodoistRequestError('Personal token migration failed.', err.httpStatusCode, err.responseData);
195
216
  }
196
217
  }
218
+ /**
219
+ * Registers a new OAuth client via Dynamic Client Registration (RFC 7591).
220
+ *
221
+ * @example
222
+ * ```typescript
223
+ * const client = await registerClient({
224
+ * redirectUris: ['https://example.com/callback'],
225
+ * clientName: 'My App',
226
+ * scope: ['data:read_write', 'task:add'],
227
+ * })
228
+ * // Use client.clientId and client.clientSecret for OAuth flows
229
+ * ```
230
+ *
231
+ * @returns The registered client details
232
+ * @throws {@link TodoistRequestError} If the registration fails
233
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc7591 RFC 7591}
234
+ */
235
+ export async function registerClient(args, options) {
236
+ var _a, _b;
237
+ const baseUrl = options === null || options === void 0 ? void 0 : options.baseUrl;
238
+ const customFetch = options === null || options === void 0 ? void 0 : options.customFetch;
239
+ try {
240
+ const response = await request({
241
+ httpMethod: 'POST',
242
+ baseUri: getAuthBaseUri(baseUrl),
243
+ relativePath: ENDPOINT_REGISTER,
244
+ apiToken: undefined,
245
+ payload: Object.assign(Object.assign({}, args), { scope: (_a = args.scope) === null || _a === void 0 ? void 0 : _a.join(' ') }),
246
+ customFetch,
247
+ });
248
+ if (!isSuccess(response) || !((_b = response.data) === null || _b === void 0 ? void 0 : _b.clientId)) {
249
+ throw new TodoistRequestError('Dynamic client registration failed.', response.status, response.data);
250
+ }
251
+ const _c = response.data, { clientIdIssuedAt, clientSecretExpiresAt, scope } = _c, rest = __rest(_c, ["clientIdIssuedAt", "clientSecretExpiresAt", "scope"]);
252
+ return Object.assign(Object.assign({}, rest), { scope: scope ? scope.split(' ') : undefined, clientIdIssuedAt: clientIdIssuedAt !== undefined ? new Date(clientIdIssuedAt * 1000) : undefined, clientSecretExpiresAt: clientSecretExpiresAt === undefined
253
+ ? undefined
254
+ : clientSecretExpiresAt === 0
255
+ ? null
256
+ : new Date(clientSecretExpiresAt * 1000) });
257
+ }
258
+ catch (error) {
259
+ const err = error;
260
+ throw new TodoistRequestError('Dynamic client registration failed.', err.httpStatusCode, err.responseData);
261
+ }
262
+ }
@@ -65,6 +65,7 @@ export const ENDPOINT_SYNC = 'sync';
65
65
  export const ENDPOINT_AUTHORIZATION = 'authorize';
66
66
  export const ENDPOINT_GET_TOKEN = 'access_token';
67
67
  export const ENDPOINT_REVOKE = 'revoke';
68
+ export const ENDPOINT_REGISTER = 'register';
68
69
  export const ENDPOINT_REST_FOLDERS = 'folders';
69
70
  // Workspace endpoints
70
71
  export const ENDPOINT_REST_WORKSPACES = 'workspaces';
@@ -128,6 +128,32 @@ export const DEFAULT_PROJECT = {
128
128
  };
129
129
  export const INVALID_PROJECT = Object.assign(Object.assign({}, DEFAULT_PROJECT), { name: 123 });
130
130
  export const PROJECT_WITH_OPTIONALS_AS_NULL = Object.assign(Object.assign({}, DEFAULT_PROJECT), { parentId: null });
131
+ export const DEFAULT_WORKSPACE_PROJECT = {
132
+ id: DEFAULT_PROJECT_ID,
133
+ name: DEFAULT_PROJECT_NAME,
134
+ color: DEFAULT_ENTITY_COLOR,
135
+ childOrder: DEFAULT_ORDER,
136
+ isFavorite: false,
137
+ isShared: true,
138
+ viewStyle: DEFAULT_PROJECT_VIEW_STYLE,
139
+ canAssignTasks: DEFAULT_CAN_ASSIGN_TASKS,
140
+ isArchived: DEFAULT_IS_ARCHIVED,
141
+ isDeleted: DEFAULT_IS_DELETED,
142
+ isFrozen: DEFAULT_IS_FROZEN,
143
+ createdAt: DEFAULT_DATE,
144
+ updatedAt: DEFAULT_DATE,
145
+ defaultOrder: DEFAULT_ORDER,
146
+ description: '',
147
+ isCollapsed: DEFAULT_IS_COLLAPSED,
148
+ url: DEFAULT_PROJECT_URL,
149
+ workspaceId: '100',
150
+ folderId: null,
151
+ collaboratorRoleDefault: 'READ_WRITE',
152
+ isInviteOnly: false,
153
+ isLinkSharingEnabled: true,
154
+ role: 'ADMIN',
155
+ status: 'ACTIVE',
156
+ };
131
157
  export const DEFAULT_SECTION = {
132
158
  id: DEFAULT_SECTION_ID,
133
159
  userId: DEFAULT_USER_ID,
@@ -235,3 +261,27 @@ export const DEFAULT_FOLDER = {
235
261
  childOrder: 3,
236
262
  isDeleted: false,
237
263
  };
264
+ export const DEFAULT_COLLABORATOR = {
265
+ id: DEFAULT_USER_ID,
266
+ email: DEFAULT_USER_EMAIL,
267
+ fullName: DEFAULT_USER_NAME,
268
+ timezone: 'UTC',
269
+ imageId: null,
270
+ };
271
+ export const DEFAULT_COLLABORATOR_STATE = {
272
+ userId: DEFAULT_USER_ID,
273
+ projectId: DEFAULT_PROJECT_ID,
274
+ state: 'active',
275
+ isDeleted: false,
276
+ };
277
+ export const DEFAULT_NOTE = {
278
+ id: DEFAULT_COMMENT_ID,
279
+ projectId: DEFAULT_PROJECT_ID,
280
+ content: DEFAULT_COMMENT_CONTENT,
281
+ postedAt: DEFAULT_DATE,
282
+ fileAttachment: DEFAULT_ATTACHMENT,
283
+ postedUid: DEFAULT_USER_ID,
284
+ uidsToNotify: null,
285
+ reactions: DEFAULT_COMMENT_REACTIONS,
286
+ isDeleted: false,
287
+ };
@@ -9,10 +9,11 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
+ import { WorkspaceProjectSchema, } from './types/projects/index.js';
12
13
  import { UpdateReminderArgsSchema, UpdateLocationReminderArgsSchema, ReminderIdSchema, } from './types/reminders/index.js';
13
14
  import { request, isSuccess } from './transport/http-client.js';
14
15
  import { getSyncBaseUri, ENDPOINT_REST_TASKS, ENDPOINT_REST_TASKS_FILTER, ENDPOINT_REST_TASKS_COMPLETED_BY_COMPLETION_DATE, ENDPOINT_REST_TASKS_COMPLETED_BY_DUE_DATE, ENDPOINT_REST_TASKS_COMPLETED_SEARCH, ENDPOINT_REST_TASKS_COMPLETED, ENDPOINT_REST_TEMPLATES_FILE, ENDPOINT_REST_TEMPLATES_URL, ENDPOINT_REST_TEMPLATES_CREATE_FROM_FILE, ENDPOINT_REST_TEMPLATES_IMPORT_FROM_FILE, ENDPOINT_REST_TEMPLATES_IMPORT_FROM_ID, ENDPOINT_REST_PROJECTS, ENDPOINT_REST_PROJECTS_SEARCH, ENDPOINT_SYNC_QUICK_ADD, ENDPOINT_REST_TASK_CLOSE, ENDPOINT_REST_TASK_REOPEN, ENDPOINT_REST_TASK_MOVE, ENDPOINT_REST_LABELS, ENDPOINT_REST_LABELS_SEARCH, ENDPOINT_REST_PROJECT_COLLABORATORS, ENDPOINT_REST_SECTIONS, ENDPOINT_REST_SECTIONS_SEARCH, ENDPOINT_REST_COMMENTS, ENDPOINT_REST_LOCATION_REMINDERS, ENDPOINT_REST_REMINDERS, ENDPOINT_REST_LABELS_SHARED, ENDPOINT_REST_LABELS_SHARED_RENAME, ENDPOINT_REST_LABELS_SHARED_REMOVE, ENDPOINT_SYNC, PROJECT_ARCHIVE, PROJECT_UNARCHIVE, ENDPOINT_REST_PROJECTS_MOVE_TO_WORKSPACE, ENDPOINT_REST_PROJECTS_MOVE_TO_PERSONAL, ENDPOINT_REST_PROJECTS_ARCHIVED, ENDPOINT_REST_PROJECTS_ARCHIVED_COUNT, ENDPOINT_REST_PROJECTS_PERMISSIONS, ENDPOINT_REST_PROJECT_FULL, ENDPOINT_REST_PROJECT_JOIN, SECTION_ARCHIVE, SECTION_UNARCHIVE, ENDPOINT_REST_USER, ENDPOINT_REST_PRODUCTIVITY, ENDPOINT_REST_ACTIVITIES, ENDPOINT_REST_UPLOADS, getProjectInsightsActivityStatsEndpoint, getProjectInsightsHealthEndpoint, getProjectInsightsHealthContextEndpoint, getProjectInsightsProgressEndpoint, getProjectInsightsHealthAnalyzeEndpoint, getWorkspaceInsightsEndpoint, ENDPOINT_REST_BACKUPS, ENDPOINT_REST_BACKUPS_DOWNLOAD, ENDPOINT_REST_EMAILS, ENDPOINT_REST_ID_MAPPINGS, ENDPOINT_REST_MOVED_IDS, ENDPOINT_REST_WORKSPACES, ENDPOINT_WORKSPACE_MEMBERS, getWorkspaceUserTasksEndpoint, getWorkspaceInviteUsersEndpoint, getWorkspaceUserEndpoint, ENDPOINT_WORKSPACE_INVITATIONS, ENDPOINT_WORKSPACE_INVITATIONS_ALL, ENDPOINT_WORKSPACE_INVITATIONS_DELETE, getWorkspaceInvitationAcceptEndpoint, getWorkspaceInvitationRejectEndpoint, ENDPOINT_WORKSPACE_JOIN, ENDPOINT_WORKSPACE_LOGO, ENDPOINT_WORKSPACE_PLAN_DETAILS, ENDPOINT_WORKSPACE_USERS, getWorkspaceActiveProjectsEndpoint, getWorkspaceArchivedProjectsEndpoint, ENDPOINT_REST_FOLDERS, } from './consts/endpoints.js';
15
- import { validateAttachment, validateComment, validateCommentArray, validateCurrentUser, validateLabel, validateLabelArray, validateProject, validateProjectArray, validateSection, validateSectionArray, validateTask, validateTaskArray, validateUserArray, validateProductivityStats, validateReminder, validateReminderArray, validateLocationReminderArray, validateActivityEventArray, validateWorkspaceUserArray, validateWorkspaceInvitation, validateWorkspaceInvitationArray, validateWorkspacePlanDetails, validateJoinWorkspaceResult, validateWorkspace, validateWorkspaceArray, validateMemberActivityInfoArray, validateWorkspaceUserTaskArray, validateProjectActivityStats, validateProjectHealth, validateProjectHealthContext, validateProjectProgress, validateWorkspaceInsights, validateBackupArray, validateIdMappingArray, validateMovedIdArray, validateFolder, validateFolderArray, } from './utils/validators.js';
16
+ import { validateAttachment, validateComment, validateCommentArray, validateCurrentUser, validateLabel, validateLabelArray, validateProject, validateProjectArray, validateSection, validateSectionArray, validateTask, validateTaskArray, validateUserArray, validateProductivityStats, validateReminder, validateReminderArray, validateLocationReminderArray, validateActivityEventArray, validateWorkspaceUserArray, validateWorkspaceInvitation, validateWorkspaceInvitationArray, validateWorkspacePlanDetails, validateJoinWorkspaceResult, validateWorkspace, validateWorkspaceArray, validateMemberActivityInfoArray, validateWorkspaceUserTaskArray, validateProjectActivityStats, validateProjectHealth, validateProjectHealthContext, validateProjectProgress, validateWorkspaceInsights, validateBackupArray, validateIdMappingArray, validateMovedIdArray, validateFolder, validateFolderArray, validateCollaboratorArray, validateCollaboratorStateArray, validateNoteArray, } from './utils/validators.js';
16
17
  import { formatDateToYYYYMMDD } from './utils/url-helpers.js';
17
18
  import { uploadMultipartFile } from './utils/multipart-upload.js';
18
19
  import { camelCaseKeys } from './utils/case-conversion.js';
@@ -768,15 +769,18 @@ export class TodoistApi {
768
769
  };
769
770
  }
770
771
  /**
771
- * Joins a shared project by its ID.
772
+ * Joins a workspace project.
772
773
  *
773
- * @param id - The unique identifier of the project to join.
774
+ * Only used for workspaces — this endpoint is used to join a workspace project
775
+ * by a workspace user.
776
+ *
777
+ * @param id - The unique identifier of the workspace project to join.
774
778
  * @param requestId - Optional custom identifier for the request.
775
- * @returns A promise that resolves to the joined project.
779
+ * @returns A promise that resolves to the full project data after joining.
776
780
  */
777
781
  async joinProject(id, requestId) {
778
782
  z.string().parse(id);
779
- const response = await request({
783
+ const { data } = await request({
780
784
  httpMethod: 'POST',
781
785
  baseUri: this.syncApiBase,
782
786
  relativePath: generatePath(ENDPOINT_REST_PROJECTS, id, ENDPOINT_REST_PROJECT_JOIN),
@@ -784,7 +788,16 @@ export class TodoistApi {
784
788
  customFetch: this.customFetch,
785
789
  requestId: requestId,
786
790
  });
787
- return validateProject(response.data);
791
+ return {
792
+ project: WorkspaceProjectSchema.parse(data.project),
793
+ tasks: validateTaskArray(data.tasks),
794
+ sections: validateSectionArray(data.sections),
795
+ comments: validateNoteArray(data.comments),
796
+ collaborators: validateCollaboratorArray(data.collaborators),
797
+ collaboratorStates: validateCollaboratorStateArray(data.collaboratorStates),
798
+ folder: data.folder ? validateFolder(data.folder) : null,
799
+ subprojects: data.subprojects.map((p) => WorkspaceProjectSchema.parse(p)),
800
+ };
788
801
  }
789
802
  /**
790
803
  * Retrieves a list of collaborators for a specific project.
@@ -13,6 +13,51 @@ export declare const PERMISSIONS: readonly ["task:add", "data:read", "data:read_
13
13
  * @see {@link https://developer.todoist.com/api/v1/#tag/Authorization}
14
14
  */
15
15
  export type Permission = (typeof PERMISSIONS)[number];
16
+ /** Supported token endpoint authentication methods for dynamic client registration. */
17
+ export declare const TOKEN_ENDPOINT_AUTH_METHODS: readonly ["client_secret_post", "client_secret_basic", "none"];
18
+ /**
19
+ * Authentication method used at the token endpoint.
20
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc7591#section-2 RFC 7591 Section 2}
21
+ */
22
+ export type TokenEndpointAuthMethod = (typeof TOKEN_ENDPOINT_AUTH_METHODS)[number];
23
+ /**
24
+ * Parameters for registering a new OAuth client via Dynamic Client Registration.
25
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc7591 RFC 7591}
26
+ */
27
+ export type ClientRegistrationRequest = {
28
+ redirectUris: string[];
29
+ clientName?: string;
30
+ clientUri?: string;
31
+ logoUri?: string;
32
+ scope?: readonly Permission[];
33
+ grantTypes?: string[];
34
+ responseTypes?: string[];
35
+ tokenEndpointAuthMethod?: TokenEndpointAuthMethod;
36
+ };
37
+ type RawClientRegistrationResponse = {
38
+ clientId: string;
39
+ clientSecret?: string;
40
+ clientName: string;
41
+ redirectUris: string[];
42
+ scope?: string;
43
+ grantTypes: string[];
44
+ responseTypes: string[];
45
+ tokenEndpointAuthMethod: string;
46
+ clientIdIssuedAt?: number;
47
+ clientSecretExpiresAt?: number;
48
+ clientUri?: string;
49
+ logoUri?: string;
50
+ };
51
+ /**
52
+ * Response from a successful dynamic client registration.
53
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.1 RFC 7591 Section 3.2.1}
54
+ */
55
+ export type ClientRegistrationResponse = Omit<RawClientRegistrationResponse, 'clientIdIssuedAt' | 'clientSecretExpiresAt' | 'scope'> & {
56
+ scope?: Permission[];
57
+ clientIdIssuedAt?: Date;
58
+ /** `null` indicates the client secret never expires. Absent when no secret is issued. */
59
+ clientSecretExpiresAt?: Date | null;
60
+ };
16
61
  /**
17
62
  * Parameters required to exchange an authorization code for an access token.
18
63
  * @see https://developer.todoist.com/api/v1/#tag/Authorization/OAuth
@@ -80,13 +125,17 @@ export declare function getAuthStateParameter(): string;
80
125
  /**
81
126
  * Generates the authorization URL for the OAuth2 flow.
82
127
  *
128
+ * The `clientId` can be either a traditional client ID string (e.g. from
129
+ * {@link registerClient}) or an HTTPS URL pointing to a client metadata document,
130
+ * as defined in {@link https://drafts.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/ RFC draft-ietf-oauth-client-id-metadata-document}.
131
+ *
83
132
  * @example
84
133
  * ```typescript
85
- * const url = getAuthorizationUrl(
86
- * 'your-client-id',
87
- * ['data:read', 'task:add'],
88
- * state
89
- * )
134
+ * const url = getAuthorizationUrl({
135
+ * clientId: 'your-client-id',
136
+ * permissions: ['data:read', 'task:add'],
137
+ * state,
138
+ * })
90
139
  * // Redirect user to url
91
140
  * ```
92
141
  *
@@ -156,3 +205,22 @@ export declare function revokeToken(args: RevokeTokenRequestArgs, options?: Auth
156
205
  * @throws {@link TodoistRequestError} If the migration fails
157
206
  */
158
207
  export declare function migratePersonalToken(args: MigratePersonalTokenArgs, options?: AuthOptions): Promise<MigratePersonalTokenResponse>;
208
+ /**
209
+ * Registers a new OAuth client via Dynamic Client Registration (RFC 7591).
210
+ *
211
+ * @example
212
+ * ```typescript
213
+ * const client = await registerClient({
214
+ * redirectUris: ['https://example.com/callback'],
215
+ * clientName: 'My App',
216
+ * scope: ['data:read_write', 'task:add'],
217
+ * })
218
+ * // Use client.clientId and client.clientSecret for OAuth flows
219
+ * ```
220
+ *
221
+ * @returns The registered client details
222
+ * @throws {@link TodoistRequestError} If the registration fails
223
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc7591 RFC 7591}
224
+ */
225
+ export declare function registerClient(args: ClientRegistrationRequest, options?: AuthOptions): Promise<ClientRegistrationResponse>;
226
+ export {};
@@ -56,6 +56,7 @@ export declare const ENDPOINT_SYNC = "sync";
56
56
  export declare const ENDPOINT_AUTHORIZATION = "authorize";
57
57
  export declare const ENDPOINT_GET_TOKEN = "access_token";
58
58
  export declare const ENDPOINT_REVOKE = "revoke";
59
+ export declare const ENDPOINT_REGISTER = "register";
59
60
  export declare const ENDPOINT_REST_FOLDERS = "folders";
60
61
  export declare const ENDPOINT_REST_WORKSPACES = "workspaces";
61
62
  export declare const ENDPOINT_WORKSPACE_INVITATIONS = "workspaces/invitations";
@@ -1,4 +1,6 @@
1
- import { Label, Section, Task, User, Attachment, Duration, Deadline, RawComment, PersonalProject, Reminder, Folder } from '../types/index.js';
1
+ import { Label, Section, Task, User, Attachment, Duration, Deadline, RawComment, PersonalProject, WorkspaceProject, Reminder, Folder } from '../types/index.js';
2
+ import type { Collaborator, CollaboratorState } from '../types/sync/resources/collaborators.js';
3
+ import type { Note } from '../types/sync/resources/notes.js';
2
4
  export declare const DEFAULT_TASK_ID = "1234";
3
5
  export declare const DEFAULT_TASK_CONTENT = "This is a task";
4
6
  export declare const DEFAULT_TASK_DESCRIPTION = "A description";
@@ -77,6 +79,7 @@ export declare const INVALID_PROJECT: {
77
79
  inboxProject: boolean;
78
80
  };
79
81
  export declare const PROJECT_WITH_OPTIONALS_AS_NULL: PersonalProject;
82
+ export declare const DEFAULT_WORKSPACE_PROJECT: WorkspaceProject;
80
83
  export declare const DEFAULT_SECTION: Section;
81
84
  export declare const INVALID_SECTION: {
82
85
  projectId: undefined;
@@ -259,3 +262,6 @@ export declare const COMMENT_WITH_OPTIONALS_AS_NULL_PROJECT: {
259
262
  projectId?: string | undefined;
260
263
  };
261
264
  export declare const DEFAULT_FOLDER: Folder;
265
+ export declare const DEFAULT_COLLABORATOR: Collaborator;
266
+ export declare const DEFAULT_COLLABORATOR_STATE: CollaboratorState;
267
+ export declare const DEFAULT_NOTE: Note;
@@ -1,5 +1,5 @@
1
1
  import { Task, AddTaskArgs, GetTasksArgs, GetTasksByFilterArgs, UpdateTaskArgs, QuickAddTaskArgs, type MoveTaskArgs, GetCompletedTasksByCompletionDateArgs, GetCompletedTasksByDueDateArgs, GetCompletedTasksResponse, SearchCompletedTasksArgs, GetTasksResponse, GetAllCompletedTasksArgs, GetAllCompletedTasksResponse } from './types/tasks/index.js';
2
- import { PersonalProject, WorkspaceProject, AddProjectArgs, UpdateProjectArgs, GetProjectsArgs, SearchProjectsArgs, GetProjectCollaboratorsArgs, GetProjectsResponse, GetProjectCollaboratorsResponse, GetArchivedProjectsArgs, GetArchivedProjectsResponse, MoveProjectToWorkspaceArgs, MoveProjectToPersonalArgs, GetArchivedProjectsCountArgs, GetArchivedProjectsCountResponse, GetProjectPermissionsResponse, GetFullProjectArgs, GetFullProjectResponse } from './types/projects/index.js';
2
+ import { PersonalProject, WorkspaceProject, AddProjectArgs, UpdateProjectArgs, GetProjectsArgs, SearchProjectsArgs, GetProjectCollaboratorsArgs, GetProjectsResponse, GetProjectCollaboratorsResponse, GetArchivedProjectsArgs, GetArchivedProjectsResponse, MoveProjectToWorkspaceArgs, MoveProjectToPersonalArgs, GetArchivedProjectsCountArgs, GetArchivedProjectsCountResponse, GetProjectPermissionsResponse, GetFullProjectArgs, GetFullProjectResponse, JoinProjectResponse } from './types/projects/index.js';
3
3
  import { Section, AddSectionArgs, UpdateSectionArgs, GetSectionsArgs, SearchSectionsArgs, GetSectionsResponse } from './types/sections/index.js';
4
4
  import { Label, AddLabelArgs, UpdateLabelArgs, GetLabelsArgs, SearchLabelsArgs, GetLabelsResponse, GetSharedLabelsArgs, GetSharedLabelsResponse, RenameSharedLabelArgs, RemoveSharedLabelArgs } from './types/labels/index.js';
5
5
  import { Attachment, Comment, AddCommentArgs, UpdateCommentArgs, GetProjectCommentsArgs, GetTaskCommentsArgs, GetCommentsResponse } from './types/comments/index.js';
@@ -318,13 +318,16 @@ export declare class TodoistApi {
318
318
  */
319
319
  getFullProject(id: string, args?: GetFullProjectArgs): Promise<GetFullProjectResponse>;
320
320
  /**
321
- * Joins a shared project by its ID.
321
+ * Joins a workspace project.
322
322
  *
323
- * @param id - The unique identifier of the project to join.
323
+ * Only used for workspaces — this endpoint is used to join a workspace project
324
+ * by a workspace user.
325
+ *
326
+ * @param id - The unique identifier of the workspace project to join.
324
327
  * @param requestId - Optional custom identifier for the request.
325
- * @returns A promise that resolves to the joined project.
328
+ * @returns A promise that resolves to the full project data after joining.
326
329
  */
327
- joinProject(id: string, requestId?: string): Promise<PersonalProject | WorkspaceProject>;
330
+ joinProject(id: string, requestId?: string): Promise<JoinProjectResponse>;
328
331
  /**
329
332
  * Retrieves a list of collaborators for a specific project.
330
333
  *
@@ -2,6 +2,9 @@ import type { ColorKey } from '../../utils/colors.js';
2
2
  import type { SearchArgs } from '../common.js';
3
3
  import type { Comment } from '../comments/types.js';
4
4
  import type { Section } from '../sections/types.js';
5
+ import type { Collaborator, CollaboratorState } from '../sync/resources/collaborators.js';
6
+ import type { Folder } from '../sync/resources/folders.js';
7
+ import type { Note } from '../sync/resources/notes.js';
5
8
  import type { Task } from '../tasks/types.js';
6
9
  import type { User } from '../users/types.js';
7
10
  import type { CollaboratorRole, WorkspaceRole } from '../workspaces/types.js';
@@ -167,3 +170,21 @@ export type GetFullProjectResponse = {
167
170
  collaborators: User[];
168
171
  notes: Comment[];
169
172
  };
173
+ /**
174
+ * Response from joining a workspace project.
175
+ *
176
+ * Only used for workspaces — this endpoint is used to join a workspace project
177
+ * by a workspace user.
178
+ *
179
+ * @see https://developer.todoist.com/api/v1/#tag/Projects/operation/join_project_api_v1_projects__project_id__join_post
180
+ */
181
+ export type JoinProjectResponse = {
182
+ project: WorkspaceProject;
183
+ tasks: Task[];
184
+ sections: Section[];
185
+ comments: Note[];
186
+ collaborators: Collaborator[];
187
+ collaboratorStates: CollaboratorState[];
188
+ folder: Folder | null;
189
+ subprojects: WorkspaceProject[];
190
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doist/todoist-sdk",
3
- "version": "8.0.0",
3
+ "version": "8.1.0",
4
4
  "description": "The official TypeScript SDK for the Todoist REST API.",
5
5
  "author": "Doist developers",
6
6
  "repository": "https://github.com/Doist/todoist-sdk-typescript",