@visus-io/notion-sdk-ts 3.1.1 → 3.2.1

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 (50) hide show
  1. package/README.md +3 -3
  2. package/dist/api/blocks.api.d.ts +1083 -224
  3. package/dist/api/blocks.api.js +4 -0
  4. package/dist/api/comments.api.d.ts +19 -1
  5. package/dist/api/comments.api.js +1 -1
  6. package/dist/api/dataSources.api.d.ts +41 -0
  7. package/dist/api/databases.api.d.ts +52 -5
  8. package/dist/api/databases.api.js +5 -3
  9. package/dist/api/fileUploads.api.d.ts +17 -7
  10. package/dist/api/fileUploads.api.js +30 -16
  11. package/dist/api/pages.api.d.ts +49 -0
  12. package/dist/client.d.ts +33 -2
  13. package/dist/client.js +88 -21
  14. package/dist/errors.d.ts +7 -0
  15. package/dist/errors.js +7 -0
  16. package/dist/helpers/filter.helpers.d.ts +11 -4
  17. package/dist/helpers/filter.helpers.js +73 -30
  18. package/dist/helpers/pagination.helpers.d.ts +7 -2
  19. package/dist/helpers/pagination.helpers.js +26 -5
  20. package/dist/models/dataSource.model.d.ts +6 -1
  21. package/dist/models/dataSource.model.js +7 -0
  22. package/dist/models/database.model.d.ts +5 -1
  23. package/dist/models/database.model.js +6 -0
  24. package/dist/models/page.model.d.ts +20 -1
  25. package/dist/models/page.model.js +33 -2
  26. package/dist/models/user.model.d.ts +5 -0
  27. package/dist/models/user.model.js +10 -0
  28. package/dist/schemas/block.schema.d.ts +1082 -224
  29. package/dist/schemas/block.schema.js +40 -25
  30. package/dist/schemas/comment.schema.d.ts +18 -0
  31. package/dist/schemas/dataSource.schema.d.ts +41 -0
  32. package/dist/schemas/dataSource.schema.js +3 -0
  33. package/dist/schemas/database.schema.d.ts +54 -0
  34. package/dist/schemas/database.schema.js +11 -1
  35. package/dist/schemas/meetingNotesQuery.schema.d.ts +1082 -224
  36. package/dist/schemas/page.schema.d.ts +49 -0
  37. package/dist/schemas/page.schema.js +2 -1
  38. package/dist/schemas/pageMarkdown.schema.js +1 -1
  39. package/dist/schemas/pageProperties.schema.d.ts +105 -1
  40. package/dist/schemas/pageProperties.schema.js +17 -1
  41. package/dist/schemas/pagination.schema.d.ts +1 -1
  42. package/dist/schemas/propertyObjects.schema.js +2 -1
  43. package/dist/schemas/richText.schema.d.ts +36 -0
  44. package/dist/schemas/richText.schema.js +21 -0
  45. package/dist/schemas/shared.schema.d.ts +3 -7
  46. package/dist/schemas/shared.schema.js +5 -9
  47. package/dist/schemas/view.schema.d.ts +49 -0
  48. package/dist/validation.d.ts +8 -0
  49. package/dist/validation.js +16 -0
  50. package/package.json +16 -15
@@ -71,6 +71,7 @@ class BlocksAPI extends base_api_1.BaseAPI {
71
71
  *
72
72
  * @param options - Filter, sort, and limit options
73
73
  * @returns Matching meeting-notes blocks
74
+ * @throws {NotionValidationError} If `limit` is outside the range 1 to 50.
74
75
  *
75
76
  * @see https://developers.notion.com/reference/query-meeting-notes
76
77
  */
@@ -78,6 +79,9 @@ class BlocksAPI extends base_api_1.BaseAPI {
78
79
  if (options?.sort) {
79
80
  (0, validation_1.validateArrayLength)(options.sort, validation_1.LIMITS.ARRAY_ELEMENTS, 'sort');
80
81
  }
82
+ if (options?.limit !== undefined) {
83
+ (0, validation_1.validateNumberRange)(options.limit, 1, 50, 'limit');
84
+ }
81
85
  const body = {};
82
86
  if (options?.filter) {
83
87
  body.filter = options.filter;
@@ -162,10 +162,23 @@ export declare class CommentsAPI extends BaseAPI<NotionComment, Comment> {
162
162
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
163
163
  type: import("zod").ZodLiteral<"mention">;
164
164
  mention: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
165
+ type: import("zod").ZodLiteral<"custom_emoji">;
166
+ custom_emoji: import("zod").ZodObject<{
167
+ id: import("zod").ZodUUID;
168
+ name: import("zod").ZodString;
169
+ url: import("zod").ZodURL;
170
+ }, import("zod/v4/core").$strip>;
171
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
165
172
  type: import("zod").ZodLiteral<"database">;
166
173
  database: import("zod").ZodObject<{
167
174
  id: import("zod").ZodUUID;
168
175
  }, import("zod/v4/core").$strip>;
176
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
177
+ type: import("zod").ZodLiteral<"data_source">;
178
+ data_source: import("zod").ZodObject<{
179
+ id: import("zod").ZodUUID;
180
+ database_id: import("zod").ZodOptional<import("zod").ZodUUID>;
181
+ }, import("zod/v4/core").$strip>;
169
182
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
170
183
  type: import("zod").ZodLiteral<"date">;
171
184
  date: import("zod").ZodObject<{
@@ -173,6 +186,11 @@ export declare class CommentsAPI extends BaseAPI<NotionComment, Comment> {
173
186
  end: import("zod").ZodNullable<import("zod").ZodUnion<readonly [import("zod").ZodISODateTime, import("zod").ZodISODate]>>;
174
187
  time_zone: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
175
188
  }, import("zod/v4/core").$strip>;
189
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
190
+ type: import("zod").ZodLiteral<"link_mention">;
191
+ link_mention: import("zod").ZodObject<{
192
+ href: import("zod").ZodString;
193
+ }, import("zod/v4/core").$loose>;
176
194
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
177
195
  type: import("zod").ZodLiteral<"link_preview">;
178
196
  link_preview: import("zod").ZodObject<{
@@ -329,7 +347,7 @@ export declare class CommentsAPI extends BaseAPI<NotionComment, Comment> {
329
347
  * @param params - Pagination parameters
330
348
  * @returns Paginated list of comments
331
349
  *
332
- * @see https://developers.notion.com/reference/retrieve-a-comment
350
+ * @see https://developers.notion.com/reference/retrieve-a-comment-list
333
351
  */
334
352
  list(parentId: string, params?: PaginationParameters): Promise<PaginatedList<Comment>>;
335
353
  /**
@@ -39,7 +39,7 @@ class CommentsAPI extends base_api_1.BaseAPI {
39
39
  * @param params - Pagination parameters
40
40
  * @returns Paginated list of comments
41
41
  *
42
- * @see https://developers.notion.com/reference/retrieve-a-comment
42
+ * @see https://developers.notion.com/reference/retrieve-a-comment-list
43
43
  */
44
44
  async list(parentId, params) {
45
45
  const query = {
@@ -541,10 +541,23 @@ export declare class DataSourcesAPI extends BaseAPI<NotionDataSource, DataSource
541
541
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
542
542
  type: import("zod").ZodLiteral<"mention">;
543
543
  mention: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
544
+ type: import("zod").ZodLiteral<"custom_emoji">;
545
+ custom_emoji: import("zod").ZodObject<{
546
+ id: import("zod").ZodUUID;
547
+ name: import("zod").ZodString;
548
+ url: import("zod").ZodURL;
549
+ }, import("zod/v4/core").$strip>;
550
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
544
551
  type: import("zod").ZodLiteral<"database">;
545
552
  database: import("zod").ZodObject<{
546
553
  id: import("zod").ZodUUID;
547
554
  }, import("zod/v4/core").$strip>;
555
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
556
+ type: import("zod").ZodLiteral<"data_source">;
557
+ data_source: import("zod").ZodObject<{
558
+ id: import("zod").ZodUUID;
559
+ database_id: import("zod").ZodOptional<import("zod").ZodUUID>;
560
+ }, import("zod/v4/core").$strip>;
548
561
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
549
562
  type: import("zod").ZodLiteral<"date">;
550
563
  date: import("zod").ZodObject<{
@@ -552,6 +565,11 @@ export declare class DataSourcesAPI extends BaseAPI<NotionDataSource, DataSource
552
565
  end: import("zod").ZodNullable<import("zod").ZodUnion<readonly [import("zod").ZodISODateTime, import("zod").ZodISODate]>>;
553
566
  time_zone: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
554
567
  }, import("zod/v4/core").$strip>;
568
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
569
+ type: import("zod").ZodLiteral<"link_mention">;
570
+ link_mention: import("zod").ZodObject<{
571
+ href: import("zod").ZodString;
572
+ }, import("zod/v4/core").$loose>;
555
573
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
556
574
  type: import("zod").ZodLiteral<"link_preview">;
557
575
  link_preview: import("zod").ZodObject<{
@@ -717,10 +735,23 @@ export declare class DataSourcesAPI extends BaseAPI<NotionDataSource, DataSource
717
735
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
718
736
  type: import("zod").ZodLiteral<"mention">;
719
737
  mention: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
738
+ type: import("zod").ZodLiteral<"custom_emoji">;
739
+ custom_emoji: import("zod").ZodObject<{
740
+ id: import("zod").ZodUUID;
741
+ name: import("zod").ZodString;
742
+ url: import("zod").ZodURL;
743
+ }, import("zod/v4/core").$strip>;
744
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
720
745
  type: import("zod").ZodLiteral<"database">;
721
746
  database: import("zod").ZodObject<{
722
747
  id: import("zod").ZodUUID;
723
748
  }, import("zod/v4/core").$strip>;
749
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
750
+ type: import("zod").ZodLiteral<"data_source">;
751
+ data_source: import("zod").ZodObject<{
752
+ id: import("zod").ZodUUID;
753
+ database_id: import("zod").ZodOptional<import("zod").ZodUUID>;
754
+ }, import("zod/v4/core").$strip>;
724
755
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
725
756
  type: import("zod").ZodLiteral<"date">;
726
757
  date: import("zod").ZodObject<{
@@ -728,6 +759,11 @@ export declare class DataSourcesAPI extends BaseAPI<NotionDataSource, DataSource
728
759
  end: import("zod").ZodNullable<import("zod").ZodUnion<readonly [import("zod").ZodISODateTime, import("zod").ZodISODate]>>;
729
760
  time_zone: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
730
761
  }, import("zod/v4/core").$strip>;
762
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
763
+ type: import("zod").ZodLiteral<"link_mention">;
764
+ link_mention: import("zod").ZodObject<{
765
+ href: import("zod").ZodString;
766
+ }, import("zod/v4/core").$loose>;
731
767
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
732
768
  type: import("zod").ZodLiteral<"link_preview">;
733
769
  link_preview: import("zod").ZodObject<{
@@ -917,6 +953,11 @@ export declare class DataSourcesAPI extends BaseAPI<NotionDataSource, DataSource
917
953
  public_url: import("zod").ZodUnion<readonly [import("zod").ZodURL, import("zod").ZodNull]>;
918
954
  is_inline: import("zod").ZodBoolean;
919
955
  in_trash: import("zod").ZodBoolean;
956
+ database_type: import("zod").ZodOptional<import("zod").ZodEnum<{
957
+ tasks: "tasks";
958
+ projects: "projects";
959
+ skills: "skills";
960
+ }>>;
920
961
  }, import("zod/v4/core").$strip>;
921
962
  ModelClass: typeof DataSource;
922
963
  listType: "data_source";
@@ -1,5 +1,5 @@
1
1
  import type { NotionClient } from '../client';
2
- import { type NotionDatabase, type PaginatedList, type PaginationParameters } from '../schemas';
2
+ import { type DatabaseType, type NotionDatabase, type PaginatedList, type PaginationParameters } from '../schemas';
3
3
  import { Database, Page } from '../models';
4
4
  import { BaseAPI } from './base.api';
5
5
  /**
@@ -52,7 +52,9 @@ export type CreateDatabaseParent = {
52
52
  };
53
53
  /**
54
54
  * Initial data source configuration for creating a database.
55
- * As of API version 2025-09-03, databases are created with an initial data source.
55
+ * Provide this to create a database with a custom properties schema. To create a database
56
+ * from one of Notion's canonical schemas instead, use `database_type` on
57
+ * {@link CreateDatabaseOptions}.
56
58
  */
57
59
  export interface InitialDataSource {
58
60
  /** Data source properties schema */
@@ -62,14 +64,17 @@ export interface InitialDataSource {
62
64
  }
63
65
  /**
64
66
  * Options for creating a database.
65
- * As of API version 2025-09-03, databases require an `initial_data_source` object.
66
- * This object holds the properties schema. It replaces top-level properties.
67
+ * Provide exactly one of `database_type` or `initial_data_source`. `database_type` builds
68
+ * the database from one of Notion's canonical schemas. `initial_data_source` holds a custom
69
+ * properties schema. It replaces top-level properties.
67
70
  */
68
71
  export interface CreateDatabaseOptions {
69
72
  /** The parent object (page or workspace) */
70
73
  parent: CreateDatabaseParent;
74
+ /** Build the database from a canonical Notion schema instead of a custom one */
75
+ database_type?: DatabaseType;
71
76
  /** Initial data source configuration (contains properties schema) */
72
- initial_data_source: InitialDataSource;
77
+ initial_data_source?: InitialDataSource;
73
78
  /** Database title as rich text array */
74
79
  title?: unknown[];
75
80
  /** Database icon (emoji, file, or external) */
@@ -224,10 +229,23 @@ export declare class DatabasesAPI extends BaseAPI<NotionDatabase, Database> {
224
229
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
225
230
  type: import("zod").ZodLiteral<"mention">;
226
231
  mention: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
232
+ type: import("zod").ZodLiteral<"custom_emoji">;
233
+ custom_emoji: import("zod").ZodObject<{
234
+ id: import("zod").ZodUUID;
235
+ name: import("zod").ZodString;
236
+ url: import("zod").ZodURL;
237
+ }, import("zod/v4/core").$strip>;
238
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
227
239
  type: import("zod").ZodLiteral<"database">;
228
240
  database: import("zod").ZodObject<{
229
241
  id: import("zod").ZodUUID;
230
242
  }, import("zod/v4/core").$strip>;
243
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
244
+ type: import("zod").ZodLiteral<"data_source">;
245
+ data_source: import("zod").ZodObject<{
246
+ id: import("zod").ZodUUID;
247
+ database_id: import("zod").ZodOptional<import("zod").ZodUUID>;
248
+ }, import("zod/v4/core").$strip>;
231
249
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
232
250
  type: import("zod").ZodLiteral<"date">;
233
251
  date: import("zod").ZodObject<{
@@ -235,6 +253,11 @@ export declare class DatabasesAPI extends BaseAPI<NotionDatabase, Database> {
235
253
  end: import("zod").ZodNullable<import("zod").ZodUnion<readonly [import("zod").ZodISODateTime, import("zod").ZodISODate]>>;
236
254
  time_zone: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
237
255
  }, import("zod/v4/core").$strip>;
256
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
257
+ type: import("zod").ZodLiteral<"link_mention">;
258
+ link_mention: import("zod").ZodObject<{
259
+ href: import("zod").ZodString;
260
+ }, import("zod/v4/core").$loose>;
238
261
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
239
262
  type: import("zod").ZodLiteral<"link_preview">;
240
263
  link_preview: import("zod").ZodObject<{
@@ -400,10 +423,23 @@ export declare class DatabasesAPI extends BaseAPI<NotionDatabase, Database> {
400
423
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
401
424
  type: import("zod").ZodLiteral<"mention">;
402
425
  mention: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
426
+ type: import("zod").ZodLiteral<"custom_emoji">;
427
+ custom_emoji: import("zod").ZodObject<{
428
+ id: import("zod").ZodUUID;
429
+ name: import("zod").ZodString;
430
+ url: import("zod").ZodURL;
431
+ }, import("zod/v4/core").$strip>;
432
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
403
433
  type: import("zod").ZodLiteral<"database">;
404
434
  database: import("zod").ZodObject<{
405
435
  id: import("zod").ZodUUID;
406
436
  }, import("zod/v4/core").$strip>;
437
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
438
+ type: import("zod").ZodLiteral<"data_source">;
439
+ data_source: import("zod").ZodObject<{
440
+ id: import("zod").ZodUUID;
441
+ database_id: import("zod").ZodOptional<import("zod").ZodUUID>;
442
+ }, import("zod/v4/core").$strip>;
407
443
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
408
444
  type: import("zod").ZodLiteral<"date">;
409
445
  date: import("zod").ZodObject<{
@@ -411,6 +447,11 @@ export declare class DatabasesAPI extends BaseAPI<NotionDatabase, Database> {
411
447
  end: import("zod").ZodNullable<import("zod").ZodUnion<readonly [import("zod").ZodISODateTime, import("zod").ZodISODate]>>;
412
448
  time_zone: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
413
449
  }, import("zod/v4/core").$strip>;
450
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
451
+ type: import("zod").ZodLiteral<"link_mention">;
452
+ link_mention: import("zod").ZodObject<{
453
+ href: import("zod").ZodString;
454
+ }, import("zod/v4/core").$loose>;
414
455
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
415
456
  type: import("zod").ZodLiteral<"link_preview">;
416
457
  link_preview: import("zod").ZodObject<{
@@ -621,6 +662,11 @@ export declare class DatabasesAPI extends BaseAPI<NotionDatabase, Database> {
621
662
  is_inline: import("zod").ZodBoolean;
622
663
  is_locked: import("zod").ZodOptional<import("zod").ZodBoolean>;
623
664
  public_url: import("zod").ZodNullable<import("zod").ZodURL>;
665
+ database_type: import("zod").ZodOptional<import("zod").ZodEnum<{
666
+ tasks: "tasks";
667
+ projects: "projects";
668
+ skills: "skills";
669
+ }>>;
624
670
  }, import("zod/v4/core").$strip>;
625
671
  ModelClass: typeof Database;
626
672
  listType: "database";
@@ -652,6 +698,7 @@ export declare class DatabasesAPI extends BaseAPI<NotionDatabase, Database> {
652
698
  *
653
699
  * @param options - Options for creating the database
654
700
  * @returns The created database wrapped in a Database model
701
+ * @throws {NotionValidationError} If not exactly one of `database_type`/`initial_data_source` is provided
655
702
  *
656
703
  * @see https://developers.notion.com/reference/create-a-database
657
704
  */
@@ -67,6 +67,7 @@ class DatabasesAPI extends base_api_1.BaseAPI {
67
67
  next_cursor: parsed.next_cursor,
68
68
  has_more: parsed.has_more,
69
69
  type: 'page',
70
+ request_status: parsed.request_status,
70
71
  };
71
72
  }
72
73
  /**
@@ -74,19 +75,20 @@ class DatabasesAPI extends base_api_1.BaseAPI {
74
75
  *
75
76
  * @param options - Options for creating the database
76
77
  * @returns The created database wrapped in a Database model
78
+ * @throws {NotionValidationError} If not exactly one of `database_type`/`initial_data_source` is provided
77
79
  *
78
80
  * @see https://developers.notion.com/reference/create-a-database
79
81
  */
80
82
  async create(options) {
83
+ if (Boolean(options.database_type) === Boolean(options.initial_data_source)) {
84
+ throw new validation_1.NotionValidationError('Exactly one of database_type or initial_data_source must be provided');
85
+ }
81
86
  if (options.title) {
82
87
  (0, validation_1.validateArrayLength)(options.title, validation_1.LIMITS.ARRAY_ELEMENTS, 'title');
83
88
  }
84
89
  if (options.initial_data_source?.title) {
85
90
  (0, validation_1.validateArrayLength)(options.initial_data_source.title, validation_1.LIMITS.ARRAY_ELEMENTS, 'initial_data_source.title');
86
91
  }
87
- if (options.initial_data_source.title) {
88
- (0, validation_1.validateArrayLength)(options.initial_data_source.title, validation_1.LIMITS.ARRAY_ELEMENTS, 'initial_data_source.title');
89
- }
90
92
  return this.createResource('/databases', options);
91
93
  }
92
94
  /**
@@ -14,9 +14,12 @@ export interface InitiateFileUploadOptions {
14
14
  content_length: number;
15
15
  }
16
16
  /**
17
- * File data for uploading (can be Buffer, ArrayBuffer, Blob, or ReadableStream).
17
+ * File data for uploading. Accepts a `Buffer`, `Uint8Array`, `ArrayBuffer`, or
18
+ * `Blob`. The upload endpoint uses `multipart/form-data`, so the SDK cannot
19
+ * stream a `ReadableStream` without buffering it in full. Read a stream into a
20
+ * `Buffer` yourself before you call `upload()`.
18
21
  */
19
- export type FileData = Buffer | ArrayBuffer | Blob | ReadableStream;
22
+ export type FileData = Buffer | Uint8Array | ArrayBuffer | Blob;
20
23
  /**
21
24
  * FileUploads API client for uploading files to Notion.
22
25
  *
@@ -56,17 +59,24 @@ export declare class FileUploadsAPI extends BaseAPI<NotionFileUpload, FileUpload
56
59
  */
57
60
  initiate(options: InitiateFileUploadOptions): Promise<FileUpload>;
58
61
  /**
59
- * Upload file data to the upload URL.
60
- * This method sends a PUT request directly to the upload URL. It does not go
61
- * through the Notion API.
62
+ * Upload file data to the upload URL from initiate(). Sends `multipart/form-data`
63
+ * through the configured `NotionClient`.
62
64
  *
63
65
  * @param uploadUrl - The upload URL from initiate()
64
66
  * @param fileData - The file data to upload
65
67
  * @param contentType - The MIME type of the file
68
+ * @param partNumber - The 1-based part number, for a multi-part upload of a file
69
+ * larger than 20 MB. Pass a positive integer.
70
+ * @throws {NotionValidationError} If `partNumber` is not a positive integer.
71
+ * @throws {NotionAPIError} If the upload endpoint returns an error response.
66
72
  *
67
- * @see https://developers.notion.com/reference/upload-a-file
73
+ * @see https://developers.notion.com/reference/upload-file
68
74
  */
69
- upload(uploadUrl: string, fileData: FileData, contentType: string): Promise<void>;
75
+ upload(uploadUrl: string, fileData: FileData, contentType: string, partNumber?: number): Promise<void>;
76
+ /**
77
+ * Convert file data into a `Blob` for the multipart request body.
78
+ */
79
+ private static toBlob;
70
80
  /**
71
81
  * Complete the file upload after uploading to the URL.
72
82
  *
@@ -4,6 +4,7 @@ exports.FileUploadsAPI = void 0;
4
4
  const schemas_1 = require("../schemas");
5
5
  const models_1 = require("../models");
6
6
  const base_model_1 = require("../models/base.model");
7
+ const validation_1 = require("../validation");
7
8
  const base_api_1 = require("./base.api");
8
9
  /**
9
10
  * FileUploads API client for uploading files to Notion.
@@ -37,27 +38,38 @@ class FileUploadsAPI extends base_api_1.BaseAPI {
37
38
  return new models_1.FileUpload(parsed, base_model_1.TRUSTED);
38
39
  }
39
40
  /**
40
- * Upload file data to the upload URL.
41
- * This method sends a PUT request directly to the upload URL. It does not go
42
- * through the Notion API.
41
+ * Upload file data to the upload URL from initiate(). Sends `multipart/form-data`
42
+ * through the configured `NotionClient`.
43
43
  *
44
44
  * @param uploadUrl - The upload URL from initiate()
45
45
  * @param fileData - The file data to upload
46
46
  * @param contentType - The MIME type of the file
47
+ * @param partNumber - The 1-based part number, for a multi-part upload of a file
48
+ * larger than 20 MB. Pass a positive integer.
49
+ * @throws {NotionValidationError} If `partNumber` is not a positive integer.
50
+ * @throws {NotionAPIError} If the upload endpoint returns an error response.
47
51
  *
48
- * @see https://developers.notion.com/reference/upload-a-file
52
+ * @see https://developers.notion.com/reference/upload-file
49
53
  */
50
- async upload(uploadUrl, fileData, contentType) {
51
- const response = await fetch(uploadUrl, {
52
- method: 'PUT',
53
- headers: {
54
- 'Content-Type': contentType,
55
- },
56
- body: fileData,
57
- });
58
- if (!response.ok) {
59
- throw new Error(`File upload failed: ${response.status} ${response.statusText}`);
54
+ async upload(uploadUrl, fileData, contentType, partNumber) {
55
+ const form = new FormData();
56
+ form.append('file', FileUploadsAPI.toBlob(fileData, contentType));
57
+ if (partNumber !== undefined) {
58
+ if (!Number.isInteger(partNumber) || partNumber < 1) {
59
+ throw new validation_1.NotionValidationError(`partNumber must be a positive integer (got ${partNumber})`);
60
+ }
61
+ form.append('part_number', String(partNumber));
60
62
  }
63
+ await this.client.sendFileUpload(uploadUrl, form);
64
+ }
65
+ /**
66
+ * Convert file data into a `Blob` for the multipart request body.
67
+ */
68
+ static toBlob(fileData, contentType) {
69
+ if (fileData instanceof Blob) {
70
+ return fileData;
71
+ }
72
+ return new Blob([new Uint8Array(fileData)], { type: contentType });
61
73
  }
62
74
  /**
63
75
  * Complete the file upload after uploading to the URL.
@@ -101,11 +113,13 @@ class FileUploadsAPI extends base_api_1.BaseAPI {
101
113
  * @returns The completed file upload object
102
114
  */
103
115
  async uploadFile(filename, fileData, contentType) {
104
- // Get content length
105
116
  let contentLength;
106
117
  if (fileData instanceof Buffer) {
107
118
  contentLength = fileData.length;
108
119
  }
120
+ else if (fileData instanceof Uint8Array) {
121
+ contentLength = fileData.byteLength;
122
+ }
109
123
  else if (fileData instanceof ArrayBuffer) {
110
124
  contentLength = fileData.byteLength;
111
125
  }
@@ -113,7 +127,7 @@ class FileUploadsAPI extends base_api_1.BaseAPI {
113
127
  contentLength = fileData.size;
114
128
  }
115
129
  else {
116
- throw new TypeError('Cannot determine content length for ReadableStream. Use initiate/upload/complete separately.');
130
+ throw new TypeError('Unsupported file data type. Pass a Buffer, Uint8Array, ArrayBuffer, or Blob.');
117
131
  }
118
132
  // Step 1: Initiate upload
119
133
  const fileUpload = await this.initiate({
@@ -380,6 +380,9 @@ export declare class PagesAPI extends BaseAPI<NotionPage, Page> {
380
380
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
381
381
  type: import("zod").ZodLiteral<"string">;
382
382
  string: import("zod").ZodNullable<import("zod").ZodString>;
383
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
384
+ type: import("zod").ZodLiteral<"unsupported">;
385
+ unsupported: import("zod").ZodObject<{}, import("zod/v4/core").$strip>;
383
386
  }, import("zod/v4/core").$strip>], "type">;
384
387
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
385
388
  id: import("zod").ZodString;
@@ -493,6 +496,16 @@ export declare class PagesAPI extends BaseAPI<NotionPage, Page> {
493
496
  id: import("zod").ZodString;
494
497
  type: import("zod").ZodLiteral<"phone_number">;
495
498
  phone_number: import("zod").ZodNullable<import("zod").ZodString>;
499
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
500
+ id: import("zod").ZodString;
501
+ type: import("zod").ZodLiteral<"place">;
502
+ place: import("zod").ZodNullable<import("zod").ZodObject<{
503
+ name: import("zod").ZodOptional<import("zod").ZodString>;
504
+ address: import("zod").ZodOptional<import("zod").ZodString>;
505
+ latitude: import("zod").ZodOptional<import("zod").ZodNumber>;
506
+ longitude: import("zod").ZodOptional<import("zod").ZodNumber>;
507
+ google_place_id: import("zod").ZodOptional<import("zod").ZodString>;
508
+ }, import("zod/v4/core").$loose>>;
496
509
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
497
510
  id: import("zod").ZodString;
498
511
  type: import("zod").ZodLiteral<"relation">;
@@ -544,10 +557,23 @@ export declare class PagesAPI extends BaseAPI<NotionPage, Page> {
544
557
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
545
558
  type: import("zod").ZodLiteral<"mention">;
546
559
  mention: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
560
+ type: import("zod").ZodLiteral<"custom_emoji">;
561
+ custom_emoji: import("zod").ZodObject<{
562
+ id: import("zod").ZodUUID;
563
+ name: import("zod").ZodString;
564
+ url: import("zod").ZodURL;
565
+ }, import("zod/v4/core").$strip>;
566
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
547
567
  type: import("zod").ZodLiteral<"database">;
548
568
  database: import("zod").ZodObject<{
549
569
  id: import("zod").ZodUUID;
550
570
  }, import("zod/v4/core").$strip>;
571
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
572
+ type: import("zod").ZodLiteral<"data_source">;
573
+ data_source: import("zod").ZodObject<{
574
+ id: import("zod").ZodUUID;
575
+ database_id: import("zod").ZodOptional<import("zod").ZodUUID>;
576
+ }, import("zod/v4/core").$strip>;
551
577
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
552
578
  type: import("zod").ZodLiteral<"date">;
553
579
  date: import("zod").ZodObject<{
@@ -555,6 +581,11 @@ export declare class PagesAPI extends BaseAPI<NotionPage, Page> {
555
581
  end: import("zod").ZodNullable<import("zod").ZodUnion<readonly [import("zod").ZodISODateTime, import("zod").ZodISODate]>>;
556
582
  time_zone: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
557
583
  }, import("zod/v4/core").$strip>;
584
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
585
+ type: import("zod").ZodLiteral<"link_mention">;
586
+ link_mention: import("zod").ZodObject<{
587
+ href: import("zod").ZodString;
588
+ }, import("zod/v4/core").$loose>;
558
589
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
559
590
  type: import("zod").ZodLiteral<"link_preview">;
560
591
  link_preview: import("zod").ZodObject<{
@@ -824,10 +855,23 @@ export declare class PagesAPI extends BaseAPI<NotionPage, Page> {
824
855
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
825
856
  type: import("zod").ZodLiteral<"mention">;
826
857
  mention: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
858
+ type: import("zod").ZodLiteral<"custom_emoji">;
859
+ custom_emoji: import("zod").ZodObject<{
860
+ id: import("zod").ZodUUID;
861
+ name: import("zod").ZodString;
862
+ url: import("zod").ZodURL;
863
+ }, import("zod/v4/core").$strip>;
864
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
827
865
  type: import("zod").ZodLiteral<"database">;
828
866
  database: import("zod").ZodObject<{
829
867
  id: import("zod").ZodUUID;
830
868
  }, import("zod/v4/core").$strip>;
869
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
870
+ type: import("zod").ZodLiteral<"data_source">;
871
+ data_source: import("zod").ZodObject<{
872
+ id: import("zod").ZodUUID;
873
+ database_id: import("zod").ZodOptional<import("zod").ZodUUID>;
874
+ }, import("zod/v4/core").$strip>;
831
875
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
832
876
  type: import("zod").ZodLiteral<"date">;
833
877
  date: import("zod").ZodObject<{
@@ -835,6 +879,11 @@ export declare class PagesAPI extends BaseAPI<NotionPage, Page> {
835
879
  end: import("zod").ZodNullable<import("zod").ZodUnion<readonly [import("zod").ZodISODateTime, import("zod").ZodISODate]>>;
836
880
  time_zone: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
837
881
  }, import("zod/v4/core").$strip>;
882
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
883
+ type: import("zod").ZodLiteral<"link_mention">;
884
+ link_mention: import("zod").ZodObject<{
885
+ href: import("zod").ZodString;
886
+ }, import("zod/v4/core").$loose>;
838
887
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
839
888
  type: import("zod").ZodLiteral<"link_preview">;
840
889
  link_preview: import("zod").ZodObject<{
package/dist/client.d.ts CHANGED
@@ -49,10 +49,40 @@ export declare class NotionClient {
49
49
  * Retry automatically on rate-limited and overloaded responses.
50
50
  */
51
51
  request<T>(options: RequestOptions): Promise<T>;
52
+ /**
53
+ * Send a `multipart/form-data` `POST` to a Notion file-upload URL. Reuses the
54
+ * configured `fetch` and the request timeout. Does not retry. Does not set
55
+ * `Content-Type`, so `fetch` adds the multipart boundary.
56
+ *
57
+ * @param uploadUrl - The absolute `upload_url` from `fileUploads.initiate()`.
58
+ * @param form - A `FormData` body with the file bytes under the `file` key.
59
+ * @throws {NotionValidationError} If `uploadUrl` is not a valid `https` URL.
60
+ * @throws {NotionAPIError} If the endpoint returns an error response.
61
+ * @throws {NotionRequestTimeoutError} If the request exceeds the timeout.
62
+ * @throws {NotionNetworkError} If a network problem blocks the request.
63
+ */
64
+ sendFileUpload(uploadUrl: string, form: FormData): Promise<void>;
52
65
  /**
53
66
  * Send one HTTP request to the Notion API.
54
67
  */
55
68
  private makeRequest;
69
+ /**
70
+ * Call the configured `fetch` with an abort timeout of `timeoutMs`.
71
+ * Clear the timer as soon as the response headers arrive or the request fails.
72
+ */
73
+ private fetchWithTimeout;
74
+ /**
75
+ * Map a transport-layer failure to the matching SDK error and throw it.
76
+ * Rethrow a `NotionAPIError` unchanged. Map an `AbortError` to
77
+ * `NotionRequestTimeoutError`. Map any other `Error` to `NotionNetworkError`.
78
+ */
79
+ private mapTransportError;
80
+ /**
81
+ * Assert that `url` is a valid `https` URL. The SDK sends the API token in the
82
+ * `Authorization` header of a file upload. Reject a plaintext `http` URL so the
83
+ * token never travels unencrypted.
84
+ */
85
+ private assertHttpsUrl;
56
86
  /**
57
87
  * Calculate a fallback retry delay with exponential backoff.
58
88
  * Use this delay when the response has no `Retry-After` header.
@@ -68,8 +98,9 @@ export declare class NotionClient {
68
98
  */
69
99
  private buildUrl;
70
100
  /**
71
- * Parse the `Retry-After` response header into milliseconds.
72
- * Return `undefined` if the header is missing or not a valid non-negative number.
101
+ * Parse the `Retry-After` response header into milliseconds, clamped to
102
+ * {@link MAX_RETRY_DELAY_MS}. Return `undefined` if the header is missing or
103
+ * not a valid non-negative number.
73
104
  */
74
105
  private parseRetryAfterHeader;
75
106
  /**