@visus-io/notion-sdk-ts 3.0.1 → 3.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.
Files changed (95) hide show
  1. package/README.md +7 -6
  2. package/dist/api/asyncTasks.api.d.ts +70 -0
  3. package/dist/api/asyncTasks.api.js +68 -0
  4. package/dist/api/base.api.d.ts +7 -16
  5. package/dist/api/base.api.js +6 -21
  6. package/dist/api/blocks.api.d.ts +433 -73
  7. package/dist/api/blocks.api.js +39 -0
  8. package/dist/api/comments.api.d.ts +40 -5
  9. package/dist/api/comments.api.js +43 -1
  10. package/dist/api/customEmojis.api.d.ts +36 -0
  11. package/dist/api/customEmojis.api.js +36 -0
  12. package/dist/api/dataSources.api.d.ts +70 -12
  13. package/dist/api/dataSources.api.js +29 -5
  14. package/dist/api/databases.api.d.ts +40 -6
  15. package/dist/api/databases.api.js +2 -1
  16. package/dist/api/fileUploads.api.d.ts +7 -1
  17. package/dist/api/fileUploads.api.js +16 -1
  18. package/dist/api/index.d.ts +3 -0
  19. package/dist/api/index.js +7 -1
  20. package/dist/api/pages.api.d.ts +142 -10
  21. package/dist/api/pages.api.js +64 -0
  22. package/dist/api/search.api.d.ts +10 -2
  23. package/dist/api/users.api.d.ts +1 -0
  24. package/dist/api/views.api.d.ts +284 -0
  25. package/dist/api/views.api.js +169 -0
  26. package/dist/client.d.ts +1 -1
  27. package/dist/client.js +10 -4
  28. package/dist/errors.d.ts +5 -1
  29. package/dist/errors.js +6 -0
  30. package/dist/helpers/block.helpers.d.ts +38 -1
  31. package/dist/helpers/block.helpers.js +45 -3
  32. package/dist/helpers/file.helpers.d.ts +36 -1
  33. package/dist/helpers/file.helpers.js +26 -4
  34. package/dist/helpers/filter.helpers.d.ts +6 -6
  35. package/dist/helpers/index.d.ts +3 -2
  36. package/dist/helpers/index.js +5 -1
  37. package/dist/helpers/pagination.helpers.d.ts +56 -0
  38. package/dist/helpers/pagination.helpers.js +86 -0
  39. package/dist/helpers/property.helpers.d.ts +29 -0
  40. package/dist/helpers/property.helpers.js +27 -0
  41. package/dist/helpers/webhook.helpers.d.ts +52 -0
  42. package/dist/helpers/webhook.helpers.js +81 -0
  43. package/dist/models/asyncTask.model.d.ts +59 -0
  44. package/dist/models/asyncTask.model.js +89 -0
  45. package/dist/models/block.model.js +4 -1
  46. package/dist/models/customEmoji.model.d.ts +28 -0
  47. package/dist/models/customEmoji.model.js +42 -0
  48. package/dist/models/dataSource.model.d.ts +3 -3
  49. package/dist/models/dataSource.model.js +1 -1
  50. package/dist/models/database.model.d.ts +7 -3
  51. package/dist/models/database.model.js +7 -1
  52. package/dist/models/index.d.ts +3 -0
  53. package/dist/models/index.js +7 -1
  54. package/dist/models/page.model.d.ts +2 -0
  55. package/dist/models/page.model.js +6 -0
  56. package/dist/models/view.model.d.ts +79 -0
  57. package/dist/models/view.model.js +119 -0
  58. package/dist/notion.d.ts +26 -7
  59. package/dist/notion.js +19 -3
  60. package/dist/schemas/asyncTask.schema.d.ts +42 -0
  61. package/dist/schemas/asyncTask.schema.js +83 -0
  62. package/dist/schemas/block.schema.d.ts +378 -72
  63. package/dist/schemas/block.schema.js +33 -7
  64. package/dist/schemas/comment.schema.d.ts +8 -3
  65. package/dist/schemas/customEmoji.schema.d.ts +13 -0
  66. package/dist/schemas/customEmoji.schema.js +48 -0
  67. package/dist/schemas/dataSource.schema.d.ts +68 -10
  68. package/dist/schemas/dataSource.schema.js +22 -4
  69. package/dist/schemas/database.schema.d.ts +38 -6
  70. package/dist/schemas/database.schema.js +3 -2
  71. package/dist/schemas/fileUpload.schema.d.ts +1 -1
  72. package/dist/schemas/icon.schema.d.ts +92 -0
  73. package/dist/schemas/icon.schema.js +88 -0
  74. package/dist/schemas/index.d.ts +6 -0
  75. package/dist/schemas/index.js +6 -0
  76. package/dist/schemas/meetingNotesQuery.schema.d.ts +4187 -0
  77. package/dist/schemas/meetingNotesQuery.schema.js +62 -0
  78. package/dist/schemas/page.schema.d.ts +46 -9
  79. package/dist/schemas/page.schema.js +4 -2
  80. package/dist/schemas/pageMarkdown.schema.d.ts +59 -0
  81. package/dist/schemas/pageMarkdown.schema.js +65 -0
  82. package/dist/schemas/pageProperties.schema.d.ts +30 -18
  83. package/dist/schemas/pageProperties.schema.js +2 -3
  84. package/dist/schemas/pagination.schema.d.ts +30 -3
  85. package/dist/schemas/pagination.schema.js +18 -1
  86. package/dist/schemas/parent.schema.d.ts +10 -1
  87. package/dist/schemas/parent.schema.js +9 -3
  88. package/dist/schemas/propertyObjects.schema.d.ts +12 -12
  89. package/dist/schemas/richText.schema.d.ts +8 -6
  90. package/dist/schemas/richText.schema.js +1 -1
  91. package/dist/schemas/user.schema.d.ts +2 -0
  92. package/dist/schemas/user.schema.js +2 -1
  93. package/dist/schemas/view.schema.d.ts +1077 -0
  94. package/dist/schemas/view.schema.js +115 -0
  95. package/package.json +4 -3
@@ -5,6 +5,16 @@ const schemas_1 = require("../schemas");
5
5
  const models_1 = require("../models");
6
6
  const validation_1 = require("../validation");
7
7
  const base_api_1 = require("./base.api");
8
+ /**
9
+ * Assert that `markdown` isn't combined with `properties`/`children` when creating a page.
10
+ *
11
+ * @throws {NotionValidationError}
12
+ */
13
+ function validateMarkdownExclusivity(options) {
14
+ if (options.markdown !== undefined && (options.properties || options.children)) {
15
+ throw new validation_1.NotionValidationError('markdown cannot be combined with properties or children');
16
+ }
17
+ }
8
18
  /**
9
19
  * Pages API client for working with Notion pages.
10
20
  */
@@ -42,11 +52,53 @@ class PagesAPI extends base_api_1.BaseAPI {
42
52
  * @see https://developers.notion.com/reference/post-page
43
53
  */
44
54
  async create(options) {
55
+ validateMarkdownExclusivity(options);
45
56
  if (options.children) {
46
57
  (0, validation_1.validateArrayLength)(options.children, validation_1.LIMITS.ARRAY_ELEMENTS, 'children');
47
58
  }
48
59
  return this.createResource('/pages', options);
49
60
  }
61
+ /**
62
+ * Retrieve a page's content as markdown.
63
+ *
64
+ * @param pageId - The ID of the page to retrieve
65
+ * @param options - Options for retrieving the markdown content
66
+ * @returns The page's markdown content
67
+ *
68
+ * @see https://developers.notion.com/guides/data-apis/working-with-markdown-content
69
+ */
70
+ async getMarkdown(pageId, options) {
71
+ const query = options?.include_transcript !== undefined
72
+ ? { include_transcript: String(options.include_transcript) }
73
+ : undefined;
74
+ const response = await this.client.request({
75
+ method: 'GET',
76
+ path: `/pages/${pageId}/markdown`,
77
+ query,
78
+ });
79
+ return schemas_1.pageMarkdownSchema.parse(response);
80
+ }
81
+ /**
82
+ * Update a page's content as markdown.
83
+ *
84
+ * When `allow_async: true` is set and the write is large, the API may return an
85
+ * `async_task` handle instead of completing synchronously -- poll it via
86
+ * `notion.asyncTasks.poll(task.id)`.
87
+ *
88
+ * @param pageId - The ID of the page to update
89
+ * @param options - The markdown update to apply
90
+ * @returns The updated markdown content, or an async task handle if processed asynchronously
91
+ *
92
+ * @see https://developers.notion.com/guides/data-apis/working-with-markdown-content
93
+ */
94
+ async updateMarkdown(pageId, options) {
95
+ const response = await this.client.request({
96
+ method: 'PATCH',
97
+ path: `/pages/${pageId}/markdown`,
98
+ body: options,
99
+ });
100
+ return schemas_1.markdownContentResponseSchema.parse(response);
101
+ }
50
102
  /**
51
103
  * Update a page's properties, icon, cover, or trash status.
52
104
  *
@@ -59,6 +111,18 @@ class PagesAPI extends base_api_1.BaseAPI {
59
111
  async update(pageId, options) {
60
112
  return this.updateResource(`/pages/${pageId}`, options);
61
113
  }
114
+ /**
115
+ * Move a page to a new parent page or data source.
116
+ *
117
+ * @param pageId - The ID of the page to move
118
+ * @param parent - The new parent (a page or a data source)
119
+ * @returns The moved page wrapped in a Page model
120
+ *
121
+ * @see https://developers.notion.com/reference/move-page
122
+ */
123
+ async move(pageId, parent) {
124
+ return this.createResource(`/pages/${pageId}/move`, { parent });
125
+ }
62
126
  /**
63
127
  * Move a page to trash (convenience method).
64
128
  *
@@ -8,13 +8,21 @@ import { DataSource, Page } from '../models';
8
8
  export type SearchFilterObject = 'page' | 'data_source';
9
9
  /**
10
10
  * Search filter configuration.
11
+ *
12
+ * `in_trash` can be combined with the object filter (`property`/`value`) or
13
+ * used on its own to list only trashed content.
11
14
  */
12
- export interface SearchFilter {
15
+ export type SearchFilter = {
13
16
  /** Filter by object type */
14
17
  value: SearchFilterObject;
15
18
  /** Property to filter on (always "object" for this filter type) */
16
19
  property: 'object';
17
- }
20
+ /** Whether to list only trashed pages and data sources */
21
+ in_trash?: boolean;
22
+ } | {
23
+ /** Whether to list only trashed pages and data sources */
24
+ in_trash: boolean;
25
+ };
18
26
  /**
19
27
  * Search sort configuration.
20
28
  */
@@ -16,6 +16,7 @@ export declare class UsersAPI extends BaseAPI<NotionUser, User> {
16
16
  avatar_url: import("zod").ZodOptional<import("zod").ZodUnion<readonly [import("zod").ZodURL, import("zod").ZodNull]>>;
17
17
  person: import("zod").ZodObject<{
18
18
  email: import("zod").ZodEmail;
19
+ email_verified: import("zod").ZodOptional<import("zod").ZodBoolean>;
19
20
  }, import("zod/v4/core").$strip>;
20
21
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
21
22
  object: import("zod").ZodLiteral<"user">;
@@ -0,0 +1,284 @@
1
+ import type { NotionClient } from '../client';
2
+ import { Page, View } from '../models';
3
+ import { type NotionView, type PaginatedList, type PaginationParameters, type RequestStatus, type ViewDeleteResult, type ViewType } from '../schemas';
4
+ import { BaseAPI } from './base.api';
5
+ import type { DataSourceSort } from './dataSources.api';
6
+ /**
7
+ * Options for listing views. Exactly one of `database_id`/`data_source_id` must be provided.
8
+ */
9
+ export interface ListViewsOptions extends PaginationParameters {
10
+ /** List views for this database (mutually exclusive with data_source_id) */
11
+ database_id?: string;
12
+ /** List views for this data source (mutually exclusive with database_id) */
13
+ data_source_id?: string;
14
+ }
15
+ /**
16
+ * Options for creating a view. Exactly one of `database_id`/`view_id`/`create_database`
17
+ * must be provided to select the parent context.
18
+ */
19
+ export interface CreateViewOptions {
20
+ /** The data source the view displays */
21
+ data_source_id: string;
22
+ /** The view name */
23
+ name: string;
24
+ /** The view layout type */
25
+ type: ViewType;
26
+ /** Attach the view to this existing database */
27
+ database_id?: string;
28
+ /** Clone the configuration of this existing view */
29
+ view_id?: string;
30
+ /** Create a new database to hold the view */
31
+ create_database?: true;
32
+ /** Filter configuration */
33
+ filter?: Record<string, unknown>;
34
+ /** Sort configuration */
35
+ sorts?: DataSourceSort[];
36
+ /** Quick filter configuration */
37
+ quick_filters?: Record<string, unknown>;
38
+ /** Per-layout configuration */
39
+ configuration?: Record<string, unknown>;
40
+ /** Where to place the new view relative to existing views */
41
+ position?: Record<string, unknown>;
42
+ }
43
+ /**
44
+ * Options for updating a view. All fields optional; passing `null` for
45
+ * filter/sorts/quick_filters clears the existing value.
46
+ */
47
+ export interface UpdateViewOptions {
48
+ /** Rename the view */
49
+ name?: string;
50
+ /** Update or clear (`null`) the filter configuration */
51
+ filter?: Record<string, unknown> | null;
52
+ /** Update or clear (`null`) the sort configuration */
53
+ sorts?: DataSourceSort[] | null;
54
+ /** Update or clear (`null`) the quick filter configuration */
55
+ quick_filters?: Record<string, unknown> | null;
56
+ /** Shallow-merged per-layout configuration update */
57
+ configuration?: Record<string, unknown>;
58
+ }
59
+ /**
60
+ * Options for creating a view query.
61
+ */
62
+ export interface CreateViewQueryOptions extends PaginationParameters {
63
+ /** Filter configuration */
64
+ filter?: Record<string, unknown>;
65
+ /** Sort configuration */
66
+ sorts?: DataSourceSort[];
67
+ }
68
+ /**
69
+ * Result of a view query. Non-standard pagination shape distinct from
70
+ * {@link PaginatedList} -- includes `totalCount` and an `expiresAt` after which the
71
+ * query can no longer be retrieved via {@link ViewsAPI.queries.get}.
72
+ */
73
+ export interface ViewQueryResult {
74
+ id: string;
75
+ viewId: string;
76
+ expiresAt: Date;
77
+ totalCount: number;
78
+ results: Page[];
79
+ nextCursor: string | null;
80
+ hasMore: boolean;
81
+ requestStatus?: RequestStatus;
82
+ }
83
+ /**
84
+ * Views API client for working with Notion views.
85
+ *
86
+ * Views control how a database/data source's rows are displayed (table, board,
87
+ * calendar, etc.). Requires API version 2025-09-03 or later.
88
+ */
89
+ export declare class ViewsAPI extends BaseAPI<NotionView, View> {
90
+ protected readonly client: NotionClient;
91
+ protected config: {
92
+ schema: import("zod").ZodObject<{
93
+ object: import("zod").ZodLiteral<"view">;
94
+ id: import("zod").ZodUUID;
95
+ parent: import("zod").ZodObject<{
96
+ type: import("zod").ZodLiteral<"database_id">;
97
+ database_id: import("zod").ZodUUID;
98
+ }, import("zod/v4/core").$strip>;
99
+ data_source_id: import("zod").ZodNullable<import("zod").ZodUUID>;
100
+ name: import("zod").ZodString;
101
+ type: import("zod").ZodEnum<{
102
+ map: "map";
103
+ table: "table";
104
+ list: "list";
105
+ board: "board";
106
+ calendar: "calendar";
107
+ timeline: "timeline";
108
+ gallery: "gallery";
109
+ form: "form";
110
+ chart: "chart";
111
+ dashboard: "dashboard";
112
+ }>;
113
+ filter: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>>>;
114
+ sorts: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodArray<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>>>>;
115
+ quick_filters: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>>>;
116
+ configuration: import("zod").ZodOptional<import("zod").ZodObject<{
117
+ type: import("zod").ZodEnum<{
118
+ map: "map";
119
+ table: "table";
120
+ list: "list";
121
+ board: "board";
122
+ calendar: "calendar";
123
+ timeline: "timeline";
124
+ gallery: "gallery";
125
+ form: "form";
126
+ chart: "chart";
127
+ dashboard: "dashboard";
128
+ }>;
129
+ }, import("zod/v4/core").$catchall<import("zod").ZodUnknown>>>;
130
+ created_time: import("zod").ZodUnion<readonly [import("zod").ZodISODateTime, import("zod").ZodISODate]>;
131
+ last_edited_time: import("zod").ZodUnion<readonly [import("zod").ZodISODateTime, import("zod").ZodISODate]>;
132
+ created_by: import("zod").ZodUnion<readonly [import("zod").ZodObject<{
133
+ object: import("zod").ZodLiteral<"user">;
134
+ id: import("zod").ZodUUID;
135
+ type: import("zod").ZodLiteral<"person">;
136
+ name: import("zod").ZodOptional<import("zod").ZodString>;
137
+ avatar_url: import("zod").ZodOptional<import("zod").ZodUnion<readonly [import("zod").ZodURL, import("zod").ZodNull]>>;
138
+ person: import("zod").ZodObject<{
139
+ email: import("zod").ZodEmail;
140
+ email_verified: import("zod").ZodOptional<import("zod").ZodBoolean>;
141
+ }, import("zod/v4/core").$strip>;
142
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
143
+ object: import("zod").ZodLiteral<"user">;
144
+ id: import("zod").ZodUUID;
145
+ type: import("zod").ZodLiteral<"bot">;
146
+ name: import("zod").ZodOptional<import("zod").ZodString>;
147
+ avatar_url: import("zod").ZodOptional<import("zod").ZodUnion<readonly [import("zod").ZodURL, import("zod").ZodNull]>>;
148
+ bot: import("zod").ZodObject<{
149
+ owner: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
150
+ type: import("zod").ZodLiteral<"workspace">;
151
+ workspace: import("zod").ZodLiteral<true>;
152
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
153
+ type: import("zod").ZodLiteral<"user">;
154
+ }, import("zod/v4/core").$strip>], "type">;
155
+ workspace_name: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
156
+ workspace_id: import("zod").ZodOptional<import("zod").ZodString>;
157
+ workspace_limits: import("zod").ZodOptional<import("zod").ZodObject<{
158
+ max_file_upload_size_in_bytes: import("zod").ZodInt;
159
+ }, import("zod/v4/core").$strip>>;
160
+ }, import("zod/v4/core").$strip>;
161
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
162
+ object: import("zod").ZodLiteral<"user">;
163
+ id: import("zod").ZodUUID;
164
+ }, import("zod/v4/core").$strip>]>;
165
+ last_edited_by: import("zod").ZodUnion<readonly [import("zod").ZodObject<{
166
+ object: import("zod").ZodLiteral<"user">;
167
+ id: import("zod").ZodUUID;
168
+ type: import("zod").ZodLiteral<"person">;
169
+ name: import("zod").ZodOptional<import("zod").ZodString>;
170
+ avatar_url: import("zod").ZodOptional<import("zod").ZodUnion<readonly [import("zod").ZodURL, import("zod").ZodNull]>>;
171
+ person: import("zod").ZodObject<{
172
+ email: import("zod").ZodEmail;
173
+ email_verified: import("zod").ZodOptional<import("zod").ZodBoolean>;
174
+ }, import("zod/v4/core").$strip>;
175
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
176
+ object: import("zod").ZodLiteral<"user">;
177
+ id: import("zod").ZodUUID;
178
+ type: import("zod").ZodLiteral<"bot">;
179
+ name: import("zod").ZodOptional<import("zod").ZodString>;
180
+ avatar_url: import("zod").ZodOptional<import("zod").ZodUnion<readonly [import("zod").ZodURL, import("zod").ZodNull]>>;
181
+ bot: import("zod").ZodObject<{
182
+ owner: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
183
+ type: import("zod").ZodLiteral<"workspace">;
184
+ workspace: import("zod").ZodLiteral<true>;
185
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
186
+ type: import("zod").ZodLiteral<"user">;
187
+ }, import("zod/v4/core").$strip>], "type">;
188
+ workspace_name: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
189
+ workspace_id: import("zod").ZodOptional<import("zod").ZodString>;
190
+ workspace_limits: import("zod").ZodOptional<import("zod").ZodObject<{
191
+ max_file_upload_size_in_bytes: import("zod").ZodInt;
192
+ }, import("zod/v4/core").$strip>>;
193
+ }, import("zod/v4/core").$strip>;
194
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
195
+ object: import("zod").ZodLiteral<"user">;
196
+ id: import("zod").ZodUUID;
197
+ }, import("zod/v4/core").$strip>]>;
198
+ url: import("zod").ZodURL;
199
+ dashboard_view_id: import("zod").ZodOptional<import("zod").ZodUUID>;
200
+ }, import("zod/v4/core").$strip>;
201
+ ModelClass: typeof View;
202
+ listType: "view";
203
+ };
204
+ constructor(client: NotionClient);
205
+ /**
206
+ * List the views for a database or data source.
207
+ *
208
+ * @param options - Exactly one of `database_id`/`data_source_id`, plus pagination
209
+ * @returns Paginated list of views
210
+ *
211
+ * @throws {NotionValidationError} If neither or both of `database_id`/`data_source_id` are provided
212
+ *
213
+ * @see https://developers.notion.com/guides/data-apis/working-with-views
214
+ */
215
+ list(options: ListViewsOptions): Promise<PaginatedList<View>>;
216
+ /**
217
+ * Retrieve a view by ID.
218
+ *
219
+ * @param viewId - The ID of the view to retrieve
220
+ * @returns The view wrapped in a View model
221
+ *
222
+ * @see https://developers.notion.com/guides/data-apis/working-with-views
223
+ */
224
+ retrieve(viewId: string): Promise<View>;
225
+ /**
226
+ * Create a new view.
227
+ *
228
+ * @param options - Options for creating the view
229
+ * @returns The created view wrapped in a View model
230
+ *
231
+ * @throws {NotionValidationError} If not exactly one of `database_id`/`view_id`/`create_database` is provided
232
+ *
233
+ * @see https://developers.notion.com/guides/data-apis/working-with-views
234
+ */
235
+ create(options: CreateViewOptions): Promise<View>;
236
+ /**
237
+ * Update a view's name, filter, sorts, quick filters, or configuration.
238
+ *
239
+ * @param viewId - The ID of the view to update
240
+ * @param options - Options for updating the view
241
+ * @returns The updated view wrapped in a View model
242
+ *
243
+ * @see https://developers.notion.com/guides/data-apis/working-with-views
244
+ */
245
+ update(viewId: string, options: UpdateViewOptions): Promise<View>;
246
+ /**
247
+ * Delete a view.
248
+ *
249
+ * Note: unlike other delete methods, Notion's delete-view response only includes
250
+ * `object`/`id`/`parent`/`type` (not the full view shape), so this returns the raw
251
+ * parsed result rather than a full {@link View} model.
252
+ *
253
+ * @param viewId - The ID of the view to delete
254
+ * @returns The partial view object returned by the API
255
+ *
256
+ * @see https://developers.notion.com/guides/data-apis/working-with-views
257
+ */
258
+ delete(viewId: string): Promise<ViewDeleteResult>;
259
+ /**
260
+ * Sub-resource for querying a view's rows. View queries are non-standard: they
261
+ * return a `total_count` and `expires_at`, and the query result itself expires
262
+ * roughly 15 minutes after creation.
263
+ */
264
+ readonly queries: {
265
+ /**
266
+ * Create a new query against a view.
267
+ *
268
+ * @see https://developers.notion.com/guides/data-apis/working-with-views
269
+ */
270
+ create: (viewId: string, options?: CreateViewQueryOptions) => Promise<ViewQueryResult>;
271
+ /**
272
+ * Retrieve the results of a previously created view query.
273
+ *
274
+ * @see https://developers.notion.com/guides/data-apis/working-with-views
275
+ */
276
+ get: (viewId: string, queryId: string) => Promise<ViewQueryResult>;
277
+ /**
278
+ * Delete a view query.
279
+ *
280
+ * @see https://developers.notion.com/guides/data-apis/working-with-views
281
+ */
282
+ delete: (viewId: string, queryId: string) => Promise<void>;
283
+ };
284
+ }
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ViewsAPI = void 0;
4
+ const models_1 = require("../models");
5
+ const schemas_1 = require("../schemas");
6
+ const validation_1 = require("../validation");
7
+ const base_api_1 = require("./base.api");
8
+ function toViewQueryResult(response) {
9
+ const parsed = schemas_1.viewQueryResponseSchema.parse(response);
10
+ return {
11
+ id: parsed.id,
12
+ viewId: parsed.view_id,
13
+ expiresAt: new Date(parsed.expires_at),
14
+ totalCount: parsed.total_count,
15
+ results: parsed.results.map((page) => new models_1.Page(page)),
16
+ nextCursor: parsed.next_cursor,
17
+ hasMore: parsed.has_more,
18
+ requestStatus: parsed.request_status,
19
+ };
20
+ }
21
+ /**
22
+ * Views API client for working with Notion views.
23
+ *
24
+ * Views control how a database/data source's rows are displayed (table, board,
25
+ * calendar, etc.). Requires API version 2025-09-03 or later.
26
+ */
27
+ class ViewsAPI extends base_api_1.BaseAPI {
28
+ constructor(client) {
29
+ super(client);
30
+ this.client = client;
31
+ this.config = {
32
+ schema: schemas_1.viewSchema,
33
+ ModelClass: models_1.View,
34
+ listType: 'view',
35
+ };
36
+ /**
37
+ * Sub-resource for querying a view's rows. View queries are non-standard: they
38
+ * return a `total_count` and `expires_at`, and the query result itself expires
39
+ * roughly 15 minutes after creation.
40
+ */
41
+ this.queries = {
42
+ /**
43
+ * Create a new query against a view.
44
+ *
45
+ * @see https://developers.notion.com/guides/data-apis/working-with-views
46
+ */
47
+ create: async (viewId, options) => {
48
+ const body = {};
49
+ if (options?.filter) {
50
+ body.filter = options.filter;
51
+ }
52
+ if (options?.sorts) {
53
+ body.sorts = options.sorts;
54
+ }
55
+ Object.assign(body, this.buildPaginationBody(options));
56
+ const response = await this.client.request({
57
+ method: 'POST',
58
+ path: `/views/${viewId}/queries`,
59
+ body: Object.keys(body).length > 0 ? body : undefined,
60
+ });
61
+ return toViewQueryResult(response);
62
+ },
63
+ /**
64
+ * Retrieve the results of a previously created view query.
65
+ *
66
+ * @see https://developers.notion.com/guides/data-apis/working-with-views
67
+ */
68
+ get: async (viewId, queryId) => {
69
+ const response = await this.client.request({
70
+ method: 'GET',
71
+ path: `/views/${viewId}/queries/${queryId}`,
72
+ });
73
+ return toViewQueryResult(response);
74
+ },
75
+ /**
76
+ * Delete a view query.
77
+ *
78
+ * @see https://developers.notion.com/guides/data-apis/working-with-views
79
+ */
80
+ delete: async (viewId, queryId) => {
81
+ await this.client.request({
82
+ method: 'DELETE',
83
+ path: `/views/${viewId}/queries/${queryId}`,
84
+ });
85
+ },
86
+ };
87
+ }
88
+ /**
89
+ * List the views for a database or data source.
90
+ *
91
+ * @param options - Exactly one of `database_id`/`data_source_id`, plus pagination
92
+ * @returns Paginated list of views
93
+ *
94
+ * @throws {NotionValidationError} If neither or both of `database_id`/`data_source_id` are provided
95
+ *
96
+ * @see https://developers.notion.com/guides/data-apis/working-with-views
97
+ */
98
+ async list(options) {
99
+ if (Boolean(options.database_id) === Boolean(options.data_source_id)) {
100
+ throw new validation_1.NotionValidationError('Exactly one of database_id or data_source_id must be provided');
101
+ }
102
+ const query = {
103
+ ...(options.database_id ? { database_id: options.database_id } : {}),
104
+ ...(options.data_source_id ? { data_source_id: options.data_source_id } : {}),
105
+ ...this.buildPaginationQuery(options),
106
+ };
107
+ return this.listResources('/views', query);
108
+ }
109
+ /**
110
+ * Retrieve a view by ID.
111
+ *
112
+ * @param viewId - The ID of the view to retrieve
113
+ * @returns The view wrapped in a View model
114
+ *
115
+ * @see https://developers.notion.com/guides/data-apis/working-with-views
116
+ */
117
+ async retrieve(viewId) {
118
+ return this.retrieveResource(`/views/${viewId}`);
119
+ }
120
+ /**
121
+ * Create a new view.
122
+ *
123
+ * @param options - Options for creating the view
124
+ * @returns The created view wrapped in a View model
125
+ *
126
+ * @throws {NotionValidationError} If not exactly one of `database_id`/`view_id`/`create_database` is provided
127
+ *
128
+ * @see https://developers.notion.com/guides/data-apis/working-with-views
129
+ */
130
+ async create(options) {
131
+ const selectorCount = [options.database_id, options.view_id, options.create_database].filter((value) => value !== undefined).length;
132
+ if (selectorCount !== 1) {
133
+ throw new validation_1.NotionValidationError('Exactly one of database_id, view_id, or create_database must be provided');
134
+ }
135
+ return this.createResource('/views', options);
136
+ }
137
+ /**
138
+ * Update a view's name, filter, sorts, quick filters, or configuration.
139
+ *
140
+ * @param viewId - The ID of the view to update
141
+ * @param options - Options for updating the view
142
+ * @returns The updated view wrapped in a View model
143
+ *
144
+ * @see https://developers.notion.com/guides/data-apis/working-with-views
145
+ */
146
+ async update(viewId, options) {
147
+ return this.updateResource(`/views/${viewId}`, options);
148
+ }
149
+ /**
150
+ * Delete a view.
151
+ *
152
+ * Note: unlike other delete methods, Notion's delete-view response only includes
153
+ * `object`/`id`/`parent`/`type` (not the full view shape), so this returns the raw
154
+ * parsed result rather than a full {@link View} model.
155
+ *
156
+ * @param viewId - The ID of the view to delete
157
+ * @returns The partial view object returned by the API
158
+ *
159
+ * @see https://developers.notion.com/guides/data-apis/working-with-views
160
+ */
161
+ async delete(viewId) {
162
+ const response = await this.client.request({
163
+ method: 'DELETE',
164
+ path: `/views/${viewId}`,
165
+ });
166
+ return schemas_1.viewDeleteResponseSchema.parse(response);
167
+ }
168
+ }
169
+ exports.ViewsAPI = ViewsAPI;
package/dist/client.d.ts CHANGED
@@ -26,7 +26,7 @@ export interface NotionClientOptions {
26
26
  export interface RequestOptions {
27
27
  method: 'GET' | 'POST' | 'PATCH' | 'DELETE';
28
28
  path: string;
29
- query?: Record<string, string | number | boolean | undefined>;
29
+ query?: Record<string, string | number | boolean | string[] | undefined>;
30
30
  body?: unknown;
31
31
  }
32
32
  /**
package/dist/client.js CHANGED
@@ -29,10 +29,10 @@ class NotionClient {
29
29
  return await this.makeRequest(options);
30
30
  }
31
31
  catch (error) {
32
- // Only retry on rate limit errors if retry is enabled
32
+ // Retry rate-limited requests (if enabled) and service-overload (529)
33
+ // responses, which the API recommends always retrying.
33
34
  if (error instanceof errors_1.NotionAPIError &&
34
- error.isRateLimited() &&
35
- this.retryOnRateLimit &&
35
+ ((error.isRateLimited() && this.retryOnRateLimit) || error.isServiceOverloaded()) &&
36
36
  attempt < this.maxRetries) {
37
37
  // Prefer the server-supplied Retry-After value; fall back to
38
38
  // exponential backoff when the header is absent.
@@ -110,7 +110,13 @@ class NotionClient {
110
110
  const url = new URL(`${this.baseUrl}/v1${path}`);
111
111
  if (query) {
112
112
  Object.entries(query).forEach(([key, value]) => {
113
- if (value !== undefined) {
113
+ if (value === undefined) {
114
+ return;
115
+ }
116
+ if (Array.isArray(value)) {
117
+ value.forEach((item) => url.searchParams.append(key, item));
118
+ }
119
+ else {
114
120
  url.searchParams.append(key, String(value));
115
121
  }
116
122
  });
package/dist/errors.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Notion API error codes based on official documentation.
3
3
  */
4
- export type NotionErrorCode = 'invalid_json' | 'invalid_request_url' | 'invalid_request' | 'validation_error' | 'missing_version' | 'unauthorized' | 'restricted_resource' | 'object_not_found' | 'conflict_error' | 'rate_limited' | 'internal_server_error' | 'service_unavailable' | 'database_connection_unavailable' | 'gateway_timeout';
4
+ export type NotionErrorCode = 'invalid_json' | 'invalid_request_url' | 'invalid_request' | 'validation_error' | 'missing_version' | 'unauthorized' | 'restricted_resource' | 'object_not_found' | 'conflict_error' | 'rate_limited' | 'internal_server_error' | 'service_unavailable' | 'service_overload' | 'database_connection_unavailable' | 'gateway_timeout';
5
5
  /**
6
6
  * Notion API error response structure.
7
7
  */
@@ -24,6 +24,10 @@ export declare class NotionAPIError extends Error {
24
24
  * Check if the error is a rate limit error.
25
25
  */
26
26
  isRateLimited(): boolean;
27
+ /**
28
+ * Check if the error is a service overload error (HTTP 529).
29
+ */
30
+ isServiceOverloaded(): boolean;
27
31
  /**
28
32
  * Check if the error is an authentication error.
29
33
  */
package/dist/errors.js CHANGED
@@ -23,6 +23,12 @@ class NotionAPIError extends Error {
23
23
  isRateLimited() {
24
24
  return this.code === 'rate_limited';
25
25
  }
26
+ /**
27
+ * Check if the error is a service overload error (HTTP 529).
28
+ */
29
+ isServiceOverloaded() {
30
+ return this.code === 'service_overload';
31
+ }
26
32
  /**
27
33
  * Check if the error is an authentication error.
28
34
  */