@doist/todoist-sdk 10.3.2 → 10.4.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.
@@ -7,6 +7,70 @@ const file_response_1 = require("../utils/file-response");
7
7
  const multipart_upload_1 = require("../utils/multipart-upload");
8
8
  const validators_1 = require("../utils/validators");
9
9
  const base_client_1 = require("./base-client");
10
+ /**
11
+ * First-party origin that serves attachment downloads behind Bearer auth.
12
+ * It redirects to the CDN; on the cross-origin hop the runtime drops the
13
+ * Authorization header, so the token never reaches the CDN.
14
+ */
15
+ const AUTHENTICATED_ATTACHMENT_HOST = 'files.todoist.com';
16
+ /**
17
+ * CDN origins that serve attachments via pre-signed URLs. These are
18
+ * third-party infrastructure and must never receive the auth token.
19
+ */
20
+ const CDN_ATTACHMENT_HOSTS = new Set(['todoist.b-cdn.net', 'd1ysz50cxb9zwl.cloudfront.net']);
21
+ const MAX_ATTACHMENT_REDIRECTS = 5;
22
+ /**
23
+ * Builds the request headers for an attachment fetch, attaching the Bearer
24
+ * token only when the URL is on the first-party authenticated host.
25
+ */
26
+ function buildAttachmentAuthHeaders(url, authToken) {
27
+ return new URL(url).hostname === AUTHENTICATED_ATTACHMENT_HOST
28
+ ? { Authorization: `Bearer ${authToken}` }
29
+ : {};
30
+ }
31
+ /**
32
+ * Fetches an attachment through a caller-supplied `customFetch` while
33
+ * guaranteeing the auth token is never sent across origins.
34
+ *
35
+ * A `customFetch` is an arbitrary implementation that may not honour the Fetch
36
+ * standard's rule of stripping `Authorization` on cross-origin redirects (older
37
+ * `node-fetch`, for example, does not). Because `files.todoist.com` redirects
38
+ * downloads to the CDN, a naive implementation could forward the token there.
39
+ * We therefore drive redirects with `redirect: 'manual'` and follow them
40
+ * ourselves, dropping the auth header whenever the origin changes.
41
+ *
42
+ * Implementations that hide the redirect behind an opaque response (status `0`,
43
+ * e.g. undici and browsers) can't be followed manually; for those we re-issue
44
+ * with normal redirect following and rely on the Fetch standard's cross-origin
45
+ * auth stripping, which those runtimes implement.
46
+ */
47
+ async function customFetchFollowingRedirects(customFetch, startUrl, authToken) {
48
+ let currentUrl = startUrl;
49
+ let headers = buildAttachmentAuthHeaders(currentUrl, authToken);
50
+ for (let redirects = 0; redirects < MAX_ATTACHMENT_REDIRECTS; redirects++) {
51
+ const response = await customFetch(currentUrl, {
52
+ method: 'GET',
53
+ headers,
54
+ redirect: 'manual',
55
+ });
56
+ const location = response.headers['location'];
57
+ const isReadableRedirect = response.status >= 300 && response.status < 400 && Boolean(location);
58
+ if (!isReadableRedirect) {
59
+ // Opaque redirect (the impl hid the Location, e.g. undici): re-issue
60
+ // with normal following and trust the runtime to strip auth cross-origin.
61
+ if (response.status === 0) {
62
+ return customFetch(currentUrl, { method: 'GET', headers });
63
+ }
64
+ return response;
65
+ }
66
+ const nextUrl = new URL(location, currentUrl);
67
+ if (nextUrl.origin !== new URL(currentUrl).origin) {
68
+ headers = {};
69
+ }
70
+ currentUrl = nextUrl.toString();
71
+ }
72
+ throw new Error('Too many redirects while fetching attachment');
73
+ }
10
74
  /**
11
75
  * Internal sub-client handling upload + attachment endpoints
12
76
  * (file upload, upload delete, attachment view).
@@ -57,22 +121,30 @@ class UploadClient extends base_client_1.BaseClient {
57
121
  }
58
122
  fileUrl = commentOrUrl.fileAttachment.fileUrl;
59
123
  }
60
- // Validate the URL belongs to Todoist to prevent leaking the auth token
124
+ // Only allow known attachment origins, and only attach the auth token to the
125
+ // first-party authenticated host. This prevents the token leaking to other
126
+ // todoist.com hosts (e.g. api.todoist.com) or to third-party CDN infrastructure.
61
127
  const urlHostname = new URL(fileUrl).hostname;
62
- if (!urlHostname.endsWith('.todoist.com')) {
63
- throw new Error('Attachment URLs must be on a todoist.com domain');
128
+ const isAuthenticatedHost = urlHostname === AUTHENTICATED_ATTACHMENT_HOST;
129
+ if (!isAuthenticatedHost && !CDN_ATTACHMENT_HOSTS.has(urlHostname)) {
130
+ throw new Error('Attachment URLs must be on a known Todoist attachment host');
64
131
  }
65
- const fetchOptions = {
66
- method: 'GET',
67
- headers: { Authorization: `Bearer ${this.authToken}` },
68
- };
69
132
  if (this.customFetch) {
70
- const response = await this.customFetch(fileUrl, fetchOptions);
133
+ // customFetch may not strip auth on cross-origin redirects, so we
134
+ // follow the files.todoist.com -> CDN hop ourselves to avoid leaking
135
+ // the token. See customFetchFollowingRedirects for details.
136
+ const response = await customFetchFollowingRedirects(this.customFetch, fileUrl, this.authToken);
71
137
  if (!response.ok) {
72
138
  throw new Error(`Failed to fetch attachment: ${response.status} ${response.statusText}`);
73
139
  }
74
140
  return (0, file_response_1.wrapAsFileResponse)(response, 'viewAttachment');
75
141
  }
142
+ // Built-in fetch (undici) strips Authorization on cross-origin redirects
143
+ // per the Fetch standard, so the files.todoist.com -> CDN hop is safe here.
144
+ const fetchOptions = {
145
+ method: 'GET',
146
+ headers: isAuthenticatedHost ? { Authorization: `Bearer ${this.authToken}` } : {},
147
+ };
76
148
  const response = await fetch(fileUrl, fetchOptions);
77
149
  if (!response.ok) {
78
150
  throw new Error(`Failed to fetch attachment: ${response.status} ${response.statusText}`);
@@ -169,6 +169,7 @@ exports.DEFAULT_SECTION = {
169
169
  updatedAt: new Date('2025-03-28T14:01:23.334885Z'),
170
170
  archivedAt: null,
171
171
  name: DEFAULT_SECTION_NAME,
172
+ description: null,
172
173
  sectionOrder: exports.DEFAULT_ORDER,
173
174
  isArchived: false,
174
175
  isDeleted: false,
@@ -862,7 +862,7 @@ class TodoistApi {
862
862
  * @example
863
863
  * ```typescript
864
864
  * await api.deleteUpload({
865
- * fileUrl: 'https://cdn.todoist.com/...'
865
+ * fileUrl: 'https://files.todoist.com/...'
866
866
  * })
867
867
  * ```
868
868
  */
@@ -876,9 +876,14 @@ class TodoistApi {
876
876
  * or a direct file URL string. Returns the raw Response object so the caller
877
877
  * can read the body in the appropriate format (.arrayBuffer(), .text(), etc.).
878
878
  *
879
+ * Only known Todoist attachment hosts are allowed: the authenticated
880
+ * `files.todoist.com` origin (which receives the Bearer token) and the
881
+ * attachment CDN origins (fetched without the token). Other URLs are rejected.
882
+ *
879
883
  * @param commentOrUrl - A Comment object with a file attachment, or a file URL string.
880
884
  * @returns The raw fetch Response for the file content.
881
- * @throws Error if a Comment is provided without a file attachment or file URL.
885
+ * @throws Error if a Comment is provided without a file attachment or file URL,
886
+ * or if the URL is not on a known Todoist attachment host.
882
887
  *
883
888
  * @example
884
889
  * ```typescript
@@ -16,6 +16,7 @@ exports.SectionBaseSchema = zod_1.z.object({
16
16
  updatedAt: zod_1.z.coerce.date(),
17
17
  archivedAt: zod_1.z.coerce.date().nullable(),
18
18
  name: zod_1.z.string(),
19
+ description: zod_1.z.string().nullable(),
19
20
  sectionOrder: zod_1.z.number().int(),
20
21
  isArchived: zod_1.z.boolean(),
21
22
  isDeleted: zod_1.z.boolean(),
@@ -32,14 +32,19 @@ exports.SECTION_WEBHOOK_EVENTS = [
32
32
  * payloads that deliver it as `null` or omit the key entirely — tombstones
33
33
  * and never-updated sections come through without an `updated_at` value.
34
34
  * The result is normalised to `Date | null` so consumers don't have to
35
- * handle three shapes. Surfaces the computed `url` so the exposed shape
36
- * matches {@link Section}.
35
+ * handle three shapes. `description` is similarly tolerated (older webhook
36
+ * payloads may omit it) and normalised to `string | null`. Surfaces the
37
+ * computed `url` so the exposed shape matches {@link Section}.
37
38
  */
38
39
  exports.WebhookSectionSchema = types_1.SectionBaseSchema.extend({
39
40
  updatedAt: zod_1.z.coerce
40
41
  .date()
41
42
  .nullish()
42
43
  .transform((value) => value !== null && value !== void 0 ? value : null),
44
+ description: zod_1.z
45
+ .string()
46
+ .nullish()
47
+ .transform((value) => value !== null && value !== void 0 ? value : null),
43
48
  }).transform((data) => (Object.assign(Object.assign({}, data), { url: (0, url_helpers_1.getSectionUrl)(data.id, data.name) })));
44
49
  exports.SectionAddedPayloadSchema = zod_1.z.object(Object.assign(Object.assign({}, envelope_1.BaseWebhookEnvelopeShape), { eventName: zod_1.z.literal(exports.SectionWebhookEventEnum.Added), eventData: exports.WebhookSectionSchema }));
45
50
  exports.SectionUpdatedPayloadSchema = zod_1.z.object(Object.assign(Object.assign({}, envelope_1.BaseWebhookEnvelopeShape), { eventName: zod_1.z.literal(exports.SectionWebhookEventEnum.Updated), eventData: exports.WebhookSectionSchema }));
@@ -4,6 +4,70 @@ import { wrapAsFileResponse } from '../utils/file-response.js';
4
4
  import { uploadMultipartFile } from '../utils/multipart-upload.js';
5
5
  import { validateAttachment } from '../utils/validators.js';
6
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
+ }
7
71
  /**
8
72
  * Internal sub-client handling upload + attachment endpoints
9
73
  * (file upload, upload delete, attachment view).
@@ -54,22 +118,30 @@ export class UploadClient extends BaseClient {
54
118
  }
55
119
  fileUrl = commentOrUrl.fileAttachment.fileUrl;
56
120
  }
57
- // Validate the URL belongs to Todoist to prevent leaking the auth token
121
+ // Only allow known attachment origins, and only attach the auth token to the
122
+ // first-party authenticated host. This prevents the token leaking to other
123
+ // todoist.com hosts (e.g. api.todoist.com) or to third-party CDN infrastructure.
58
124
  const urlHostname = new URL(fileUrl).hostname;
59
- if (!urlHostname.endsWith('.todoist.com')) {
60
- throw new Error('Attachment URLs must be on a todoist.com domain');
125
+ const isAuthenticatedHost = urlHostname === AUTHENTICATED_ATTACHMENT_HOST;
126
+ if (!isAuthenticatedHost && !CDN_ATTACHMENT_HOSTS.has(urlHostname)) {
127
+ throw new Error('Attachment URLs must be on a known Todoist attachment host');
61
128
  }
62
- const fetchOptions = {
63
- method: 'GET',
64
- headers: { Authorization: `Bearer ${this.authToken}` },
65
- };
66
129
  if (this.customFetch) {
67
- const response = await this.customFetch(fileUrl, fetchOptions);
130
+ // customFetch may not strip auth on cross-origin redirects, so we
131
+ // follow the files.todoist.com -> CDN hop ourselves to avoid leaking
132
+ // the token. See customFetchFollowingRedirects for details.
133
+ const response = await customFetchFollowingRedirects(this.customFetch, fileUrl, this.authToken);
68
134
  if (!response.ok) {
69
135
  throw new Error(`Failed to fetch attachment: ${response.status} ${response.statusText}`);
70
136
  }
71
137
  return wrapAsFileResponse(response, 'viewAttachment');
72
138
  }
139
+ // Built-in fetch (undici) strips Authorization on cross-origin redirects
140
+ // per the Fetch standard, so the files.todoist.com -> CDN hop is safe here.
141
+ const fetchOptions = {
142
+ method: 'GET',
143
+ headers: isAuthenticatedHost ? { Authorization: `Bearer ${this.authToken}` } : {},
144
+ };
73
145
  const response = await fetch(fileUrl, fetchOptions);
74
146
  if (!response.ok) {
75
147
  throw new Error(`Failed to fetch attachment: ${response.status} ${response.statusText}`);
@@ -165,6 +165,7 @@ export const DEFAULT_SECTION = {
165
165
  updatedAt: new Date('2025-03-28T14:01:23.334885Z'),
166
166
  archivedAt: null,
167
167
  name: DEFAULT_SECTION_NAME,
168
+ description: null,
168
169
  sectionOrder: DEFAULT_ORDER,
169
170
  isArchived: false,
170
171
  isDeleted: false,
@@ -859,7 +859,7 @@ export class TodoistApi {
859
859
  * @example
860
860
  * ```typescript
861
861
  * await api.deleteUpload({
862
- * fileUrl: 'https://cdn.todoist.com/...'
862
+ * fileUrl: 'https://files.todoist.com/...'
863
863
  * })
864
864
  * ```
865
865
  */
@@ -873,9 +873,14 @@ export class TodoistApi {
873
873
  * or a direct file URL string. Returns the raw Response object so the caller
874
874
  * can read the body in the appropriate format (.arrayBuffer(), .text(), etc.).
875
875
  *
876
+ * Only known Todoist attachment hosts are allowed: the authenticated
877
+ * `files.todoist.com` origin (which receives the Bearer token) and the
878
+ * attachment CDN origins (fetched without the token). Other URLs are rejected.
879
+ *
876
880
  * @param commentOrUrl - A Comment object with a file attachment, or a file URL string.
877
881
  * @returns The raw fetch Response for the file content.
878
- * @throws Error if a Comment is provided without a file attachment or file URL.
882
+ * @throws Error if a Comment is provided without a file attachment or file URL,
883
+ * or if the URL is not on a known Todoist attachment host.
879
884
  *
880
885
  * @example
881
886
  * ```typescript
@@ -13,6 +13,7 @@ export const SectionBaseSchema = z.object({
13
13
  updatedAt: z.coerce.date(),
14
14
  archivedAt: z.coerce.date().nullable(),
15
15
  name: z.string(),
16
+ description: z.string().nullable(),
16
17
  sectionOrder: z.number().int(),
17
18
  isArchived: z.boolean(),
18
19
  isDeleted: z.boolean(),
@@ -29,14 +29,19 @@ export const SECTION_WEBHOOK_EVENTS = [
29
29
  * payloads that deliver it as `null` or omit the key entirely — tombstones
30
30
  * and never-updated sections come through without an `updated_at` value.
31
31
  * The result is normalised to `Date | null` so consumers don't have to
32
- * handle three shapes. Surfaces the computed `url` so the exposed shape
33
- * matches {@link Section}.
32
+ * handle three shapes. `description` is similarly tolerated (older webhook
33
+ * payloads may omit it) and normalised to `string | null`. Surfaces the
34
+ * computed `url` so the exposed shape matches {@link Section}.
34
35
  */
35
36
  export const WebhookSectionSchema = SectionBaseSchema.extend({
36
37
  updatedAt: z.coerce
37
38
  .date()
38
39
  .nullish()
39
40
  .transform((value) => value !== null && value !== void 0 ? value : null),
41
+ description: z
42
+ .string()
43
+ .nullish()
44
+ .transform((value) => value !== null && value !== void 0 ? value : null),
40
45
  }).transform((data) => (Object.assign(Object.assign({}, data), { url: getSectionUrl(data.id, data.name) })));
41
46
  export const SectionAddedPayloadSchema = z.object(Object.assign(Object.assign({}, BaseWebhookEnvelopeShape), { eventName: z.literal(SectionWebhookEventEnum.Added), eventData: WebhookSectionSchema }));
42
47
  export const SectionUpdatedPayloadSchema = z.object(Object.assign(Object.assign({}, BaseWebhookEnvelopeShape), { eventName: z.literal(SectionWebhookEventEnum.Updated), eventData: WebhookSectionSchema }));
@@ -90,6 +90,7 @@ export declare const INVALID_SECTION: {
90
90
  updatedAt: Date;
91
91
  archivedAt: Date | null;
92
92
  name: string;
93
+ description: string | null;
93
94
  sectionOrder: number;
94
95
  isArchived: boolean;
95
96
  isDeleted: boolean;
@@ -722,7 +722,7 @@ export declare class TodoistApi {
722
722
  * @example
723
723
  * ```typescript
724
724
  * await api.deleteUpload({
725
- * fileUrl: 'https://cdn.todoist.com/...'
725
+ * fileUrl: 'https://files.todoist.com/...'
726
726
  * })
727
727
  * ```
728
728
  */
@@ -734,9 +734,14 @@ export declare class TodoistApi {
734
734
  * or a direct file URL string. Returns the raw Response object so the caller
735
735
  * can read the body in the appropriate format (.arrayBuffer(), .text(), etc.).
736
736
  *
737
+ * Only known Todoist attachment hosts are allowed: the authenticated
738
+ * `files.todoist.com` origin (which receives the Bearer token) and the
739
+ * attachment CDN origins (fetched without the token). Other URLs are rejected.
740
+ *
737
741
  * @param commentOrUrl - A Comment object with a file attachment, or a file URL string.
738
742
  * @returns The raw fetch Response for the file content.
739
- * @throws Error if a Comment is provided without a file attachment or file URL.
743
+ * @throws Error if a Comment is provided without a file attachment or file URL,
744
+ * or if the URL is not on a known Todoist attachment host.
740
745
  *
741
746
  * @example
742
747
  * ```typescript
@@ -60,6 +60,8 @@ export type AddProjectArgs = {
60
60
  isFavorite?: boolean;
61
61
  viewStyle?: ProjectViewStyle;
62
62
  workspaceId?: string;
63
+ /** Optional description for the project (Markdown). */
64
+ description?: string;
63
65
  };
64
66
  /**
65
67
  * Arguments for updating a project.
@@ -70,6 +72,11 @@ export type UpdateProjectArgs = {
70
72
  color?: ColorKey;
71
73
  isFavorite?: boolean;
72
74
  viewStyle?: ProjectViewStyle;
75
+ /**
76
+ * Updated project description (Markdown). Pass an empty string to clear it.
77
+ * Omit this field to keep it unchanged.
78
+ */
79
+ description?: string;
73
80
  /**
74
81
  * Folder to move the project into. Only supported for workspace projects.
75
82
  * Pass `null` to clear the value. Omit this field to keep it unchanged.
@@ -1,3 +1,4 @@
1
+ import type { RequireAtLeastOne } from 'type-fest';
1
2
  import type { SearchArgs } from '../common.js';
2
3
  import type { Section } from './types.js';
3
4
  /**
@@ -31,11 +32,18 @@ export type AddSectionArgs = {
31
32
  name: string;
32
33
  projectId: string;
33
34
  order?: number | null;
35
+ /** Optional description for the section (Markdown). */
36
+ description?: string;
34
37
  };
35
38
  /**
36
- * Arguments for updating a section.
39
+ * Arguments for updating a section. At least one field must be provided.
37
40
  * @see https://developer.todoist.com/api/v1/#tag/Sections/operation/update_section_api_v1_sections__section_id__post
38
41
  */
39
- export type UpdateSectionArgs = {
42
+ export type UpdateSectionArgs = RequireAtLeastOne<{
40
43
  name: string;
41
- };
44
+ /**
45
+ * Updated section description (Markdown). Pass `null` to clear it. Omit the
46
+ * field to keep it unchanged.
47
+ */
48
+ description: string | null;
49
+ }>;
@@ -12,6 +12,7 @@ export declare const SectionBaseSchema: z.ZodObject<{
12
12
  updatedAt: z.ZodCoercedDate<unknown>;
13
13
  archivedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
14
14
  name: z.ZodString;
15
+ description: z.ZodNullable<z.ZodString>;
15
16
  sectionOrder: z.ZodNumber;
16
17
  isArchived: z.ZodBoolean;
17
18
  isDeleted: z.ZodBoolean;
@@ -25,6 +26,7 @@ export declare const SectionSchema: z.ZodPipe<z.ZodObject<{
25
26
  updatedAt: z.ZodCoercedDate<unknown>;
26
27
  archivedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
27
28
  name: z.ZodString;
29
+ description: z.ZodNullable<z.ZodString>;
28
30
  sectionOrder: z.ZodNumber;
29
31
  isArchived: z.ZodBoolean;
30
32
  isDeleted: z.ZodBoolean;
@@ -38,6 +40,7 @@ export declare const SectionSchema: z.ZodPipe<z.ZodObject<{
38
40
  updatedAt: Date;
39
41
  archivedAt: Date | null;
40
42
  name: string;
43
+ description: string | null;
41
44
  sectionOrder: number;
42
45
  isArchived: boolean;
43
46
  isDeleted: boolean;
@@ -50,6 +53,7 @@ export declare const SectionSchema: z.ZodPipe<z.ZodObject<{
50
53
  updatedAt: Date;
51
54
  archivedAt: Date | null;
52
55
  name: string;
56
+ description: string | null;
53
57
  sectionOrder: number;
54
58
  isArchived: boolean;
55
59
  isDeleted: boolean;
@@ -194,6 +194,7 @@ export declare const SyncResponseSchema: z.ZodObject<{
194
194
  updatedAt: z.ZodCoercedDate<unknown>;
195
195
  archivedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
196
196
  name: z.ZodString;
197
+ description: z.ZodNullable<z.ZodString>;
197
198
  sectionOrder: z.ZodNumber;
198
199
  isArchived: z.ZodBoolean;
199
200
  isDeleted: z.ZodBoolean;
@@ -207,6 +208,7 @@ export declare const SyncResponseSchema: z.ZodObject<{
207
208
  updatedAt: Date;
208
209
  archivedAt: Date | null;
209
210
  name: string;
211
+ description: string | null;
210
212
  sectionOrder: number;
211
213
  isArchived: boolean;
212
214
  isDeleted: boolean;
@@ -219,6 +221,7 @@ export declare const SyncResponseSchema: z.ZodObject<{
219
221
  updatedAt: Date;
220
222
  archivedAt: Date | null;
221
223
  name: string;
224
+ description: string | null;
222
225
  sectionOrder: number;
223
226
  isArchived: boolean;
224
227
  isDeleted: boolean;
@@ -20,8 +20,9 @@ export declare const SECTION_WEBHOOK_EVENTS: readonly ["section:added", "section
20
20
  * payloads that deliver it as `null` or omit the key entirely — tombstones
21
21
  * and never-updated sections come through without an `updated_at` value.
22
22
  * The result is normalised to `Date | null` so consumers don't have to
23
- * handle three shapes. Surfaces the computed `url` so the exposed shape
24
- * matches {@link Section}.
23
+ * handle three shapes. `description` is similarly tolerated (older webhook
24
+ * payloads may omit it) and normalised to `string | null`. Surfaces the
25
+ * computed `url` so the exposed shape matches {@link Section}.
25
26
  */
26
27
  export declare const WebhookSectionSchema: z.ZodPipe<z.ZodObject<{
27
28
  id: z.ZodString;
@@ -35,6 +36,7 @@ export declare const WebhookSectionSchema: z.ZodPipe<z.ZodObject<{
35
36
  isDeleted: z.ZodBoolean;
36
37
  isCollapsed: z.ZodBoolean;
37
38
  updatedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>, z.ZodTransform<Date | null, Date | null | undefined>>;
39
+ description: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
38
40
  }, z.core.$strip>, z.ZodTransform<{
39
41
  url: string;
40
42
  id: string;
@@ -48,6 +50,7 @@ export declare const WebhookSectionSchema: z.ZodPipe<z.ZodObject<{
48
50
  isDeleted: boolean;
49
51
  isCollapsed: boolean;
50
52
  updatedAt: Date | null;
53
+ description: string | null;
51
54
  }, {
52
55
  id: string;
53
56
  userId: string;
@@ -60,6 +63,7 @@ export declare const WebhookSectionSchema: z.ZodPipe<z.ZodObject<{
60
63
  isDeleted: boolean;
61
64
  isCollapsed: boolean;
62
65
  updatedAt: Date | null;
66
+ description: string | null;
63
67
  }>>;
64
68
  /** A section delivered in a webhook payload. */
65
69
  export type WebhookSection = z.infer<typeof WebhookSectionSchema>;
@@ -77,6 +81,7 @@ export declare const SectionAddedPayloadSchema: z.ZodObject<{
77
81
  isDeleted: z.ZodBoolean;
78
82
  isCollapsed: z.ZodBoolean;
79
83
  updatedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>, z.ZodTransform<Date | null, Date | null | undefined>>;
84
+ description: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
80
85
  }, z.core.$strip>, z.ZodTransform<{
81
86
  url: string;
82
87
  id: string;
@@ -90,6 +95,7 @@ export declare const SectionAddedPayloadSchema: z.ZodObject<{
90
95
  isDeleted: boolean;
91
96
  isCollapsed: boolean;
92
97
  updatedAt: Date | null;
98
+ description: string | null;
93
99
  }, {
94
100
  id: string;
95
101
  userId: string;
@@ -102,6 +108,7 @@ export declare const SectionAddedPayloadSchema: z.ZodObject<{
102
108
  isDeleted: boolean;
103
109
  isCollapsed: boolean;
104
110
  updatedAt: Date | null;
111
+ description: string | null;
105
112
  }>>;
106
113
  version: z.ZodEnum<{
107
114
  1: "1";
@@ -131,6 +138,7 @@ export declare const SectionUpdatedPayloadSchema: z.ZodObject<{
131
138
  isDeleted: z.ZodBoolean;
132
139
  isCollapsed: z.ZodBoolean;
133
140
  updatedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>, z.ZodTransform<Date | null, Date | null | undefined>>;
141
+ description: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
134
142
  }, z.core.$strip>, z.ZodTransform<{
135
143
  url: string;
136
144
  id: string;
@@ -144,6 +152,7 @@ export declare const SectionUpdatedPayloadSchema: z.ZodObject<{
144
152
  isDeleted: boolean;
145
153
  isCollapsed: boolean;
146
154
  updatedAt: Date | null;
155
+ description: string | null;
147
156
  }, {
148
157
  id: string;
149
158
  userId: string;
@@ -156,6 +165,7 @@ export declare const SectionUpdatedPayloadSchema: z.ZodObject<{
156
165
  isDeleted: boolean;
157
166
  isCollapsed: boolean;
158
167
  updatedAt: Date | null;
168
+ description: string | null;
159
169
  }>>;
160
170
  version: z.ZodEnum<{
161
171
  1: "1";
@@ -185,6 +195,7 @@ export declare const SectionDeletedPayloadSchema: z.ZodObject<{
185
195
  isDeleted: z.ZodBoolean;
186
196
  isCollapsed: z.ZodBoolean;
187
197
  updatedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>, z.ZodTransform<Date | null, Date | null | undefined>>;
198
+ description: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
188
199
  }, z.core.$strip>, z.ZodTransform<{
189
200
  url: string;
190
201
  id: string;
@@ -198,6 +209,7 @@ export declare const SectionDeletedPayloadSchema: z.ZodObject<{
198
209
  isDeleted: boolean;
199
210
  isCollapsed: boolean;
200
211
  updatedAt: Date | null;
212
+ description: string | null;
201
213
  }, {
202
214
  id: string;
203
215
  userId: string;
@@ -210,6 +222,7 @@ export declare const SectionDeletedPayloadSchema: z.ZodObject<{
210
222
  isDeleted: boolean;
211
223
  isCollapsed: boolean;
212
224
  updatedAt: Date | null;
225
+ description: string | null;
213
226
  }>>;
214
227
  version: z.ZodEnum<{
215
228
  1: "1";
@@ -239,6 +252,7 @@ export declare const SectionArchivedPayloadSchema: z.ZodObject<{
239
252
  isDeleted: z.ZodBoolean;
240
253
  isCollapsed: z.ZodBoolean;
241
254
  updatedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>, z.ZodTransform<Date | null, Date | null | undefined>>;
255
+ description: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
242
256
  }, z.core.$strip>, z.ZodTransform<{
243
257
  url: string;
244
258
  id: string;
@@ -252,6 +266,7 @@ export declare const SectionArchivedPayloadSchema: z.ZodObject<{
252
266
  isDeleted: boolean;
253
267
  isCollapsed: boolean;
254
268
  updatedAt: Date | null;
269
+ description: string | null;
255
270
  }, {
256
271
  id: string;
257
272
  userId: string;
@@ -264,6 +279,7 @@ export declare const SectionArchivedPayloadSchema: z.ZodObject<{
264
279
  isDeleted: boolean;
265
280
  isCollapsed: boolean;
266
281
  updatedAt: Date | null;
282
+ description: string | null;
267
283
  }>>;
268
284
  version: z.ZodEnum<{
269
285
  1: "1";
@@ -293,6 +309,7 @@ export declare const SectionUnarchivedPayloadSchema: z.ZodObject<{
293
309
  isDeleted: z.ZodBoolean;
294
310
  isCollapsed: z.ZodBoolean;
295
311
  updatedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>, z.ZodTransform<Date | null, Date | null | undefined>>;
312
+ description: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
296
313
  }, z.core.$strip>, z.ZodTransform<{
297
314
  url: string;
298
315
  id: string;
@@ -306,6 +323,7 @@ export declare const SectionUnarchivedPayloadSchema: z.ZodObject<{
306
323
  isDeleted: boolean;
307
324
  isCollapsed: boolean;
308
325
  updatedAt: Date | null;
326
+ description: string | null;
309
327
  }, {
310
328
  id: string;
311
329
  userId: string;
@@ -318,6 +336,7 @@ export declare const SectionUnarchivedPayloadSchema: z.ZodObject<{
318
336
  isDeleted: boolean;
319
337
  isCollapsed: boolean;
320
338
  updatedAt: Date | null;
339
+ description: string | null;
321
340
  }>>;
322
341
  version: z.ZodEnum<{
323
342
  1: "1";
@@ -2189,6 +2189,7 @@ export declare const WebhookPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
2189
2189
  isDeleted: z.ZodBoolean;
2190
2190
  isCollapsed: z.ZodBoolean;
2191
2191
  updatedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>, z.ZodTransform<Date | null, Date | null | undefined>>;
2192
+ description: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
2192
2193
  }, z.core.$strip>, z.ZodTransform<{
2193
2194
  url: string;
2194
2195
  id: string;
@@ -2202,6 +2203,7 @@ export declare const WebhookPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
2202
2203
  isDeleted: boolean;
2203
2204
  isCollapsed: boolean;
2204
2205
  updatedAt: Date | null;
2206
+ description: string | null;
2205
2207
  }, {
2206
2208
  id: string;
2207
2209
  userId: string;
@@ -2214,6 +2216,7 @@ export declare const WebhookPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
2214
2216
  isDeleted: boolean;
2215
2217
  isCollapsed: boolean;
2216
2218
  updatedAt: Date | null;
2219
+ description: string | null;
2217
2220
  }>>;
2218
2221
  version: z.ZodEnum<{
2219
2222
  1: "1";
@@ -2241,6 +2244,7 @@ export declare const WebhookPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
2241
2244
  isDeleted: z.ZodBoolean;
2242
2245
  isCollapsed: z.ZodBoolean;
2243
2246
  updatedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>, z.ZodTransform<Date | null, Date | null | undefined>>;
2247
+ description: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
2244
2248
  }, z.core.$strip>, z.ZodTransform<{
2245
2249
  url: string;
2246
2250
  id: string;
@@ -2254,6 +2258,7 @@ export declare const WebhookPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
2254
2258
  isDeleted: boolean;
2255
2259
  isCollapsed: boolean;
2256
2260
  updatedAt: Date | null;
2261
+ description: string | null;
2257
2262
  }, {
2258
2263
  id: string;
2259
2264
  userId: string;
@@ -2266,6 +2271,7 @@ export declare const WebhookPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
2266
2271
  isDeleted: boolean;
2267
2272
  isCollapsed: boolean;
2268
2273
  updatedAt: Date | null;
2274
+ description: string | null;
2269
2275
  }>>;
2270
2276
  version: z.ZodEnum<{
2271
2277
  1: "1";
@@ -2293,6 +2299,7 @@ export declare const WebhookPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
2293
2299
  isDeleted: z.ZodBoolean;
2294
2300
  isCollapsed: z.ZodBoolean;
2295
2301
  updatedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>, z.ZodTransform<Date | null, Date | null | undefined>>;
2302
+ description: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
2296
2303
  }, z.core.$strip>, z.ZodTransform<{
2297
2304
  url: string;
2298
2305
  id: string;
@@ -2306,6 +2313,7 @@ export declare const WebhookPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
2306
2313
  isDeleted: boolean;
2307
2314
  isCollapsed: boolean;
2308
2315
  updatedAt: Date | null;
2316
+ description: string | null;
2309
2317
  }, {
2310
2318
  id: string;
2311
2319
  userId: string;
@@ -2318,6 +2326,7 @@ export declare const WebhookPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
2318
2326
  isDeleted: boolean;
2319
2327
  isCollapsed: boolean;
2320
2328
  updatedAt: Date | null;
2329
+ description: string | null;
2321
2330
  }>>;
2322
2331
  version: z.ZodEnum<{
2323
2332
  1: "1";
@@ -2345,6 +2354,7 @@ export declare const WebhookPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
2345
2354
  isDeleted: z.ZodBoolean;
2346
2355
  isCollapsed: z.ZodBoolean;
2347
2356
  updatedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>, z.ZodTransform<Date | null, Date | null | undefined>>;
2357
+ description: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
2348
2358
  }, z.core.$strip>, z.ZodTransform<{
2349
2359
  url: string;
2350
2360
  id: string;
@@ -2358,6 +2368,7 @@ export declare const WebhookPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
2358
2368
  isDeleted: boolean;
2359
2369
  isCollapsed: boolean;
2360
2370
  updatedAt: Date | null;
2371
+ description: string | null;
2361
2372
  }, {
2362
2373
  id: string;
2363
2374
  userId: string;
@@ -2370,6 +2381,7 @@ export declare const WebhookPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
2370
2381
  isDeleted: boolean;
2371
2382
  isCollapsed: boolean;
2372
2383
  updatedAt: Date | null;
2384
+ description: string | null;
2373
2385
  }>>;
2374
2386
  version: z.ZodEnum<{
2375
2387
  1: "1";
@@ -2397,6 +2409,7 @@ export declare const WebhookPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
2397
2409
  isDeleted: z.ZodBoolean;
2398
2410
  isCollapsed: z.ZodBoolean;
2399
2411
  updatedAt: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>, z.ZodTransform<Date | null, Date | null | undefined>>;
2412
+ description: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
2400
2413
  }, z.core.$strip>, z.ZodTransform<{
2401
2414
  url: string;
2402
2415
  id: string;
@@ -2410,6 +2423,7 @@ export declare const WebhookPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
2410
2423
  isDeleted: boolean;
2411
2424
  isCollapsed: boolean;
2412
2425
  updatedAt: Date | null;
2426
+ description: string | null;
2413
2427
  }, {
2414
2428
  id: string;
2415
2429
  userId: string;
@@ -2422,6 +2436,7 @@ export declare const WebhookPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
2422
2436
  isDeleted: boolean;
2423
2437
  isCollapsed: boolean;
2424
2438
  updatedAt: Date | null;
2439
+ description: string | null;
2425
2440
  }>>;
2426
2441
  version: z.ZodEnum<{
2427
2442
  1: "1";
@@ -248,6 +248,7 @@ export declare const validateSection: (input: unknown) => {
248
248
  updatedAt: Date;
249
249
  archivedAt: Date | null;
250
250
  name: string;
251
+ description: string | null;
251
252
  sectionOrder: number;
252
253
  isArchived: boolean;
253
254
  isDeleted: boolean;
@@ -261,6 +262,7 @@ export declare const validateSection: (input: unknown) => {
261
262
  updatedAt: Date;
262
263
  archivedAt: Date | null;
263
264
  name: string;
265
+ description: string | null;
264
266
  sectionOrder: number;
265
267
  isArchived: boolean;
266
268
  isDeleted: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doist/todoist-sdk",
3
- "version": "10.3.2",
3
+ "version": "10.4.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",