@visus-io/notion-sdk-ts 3.2.0 → 3.3.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 (42) hide show
  1. package/dist/api/blocks.api.d.ts +1083 -224
  2. package/dist/api/blocks.api.js +4 -0
  3. package/dist/api/comments.api.d.ts +19 -1
  4. package/dist/api/comments.api.js +1 -1
  5. package/dist/api/dataSources.api.d.ts +36 -0
  6. package/dist/api/databases.api.d.ts +36 -0
  7. package/dist/api/databases.api.js +1 -0
  8. package/dist/api/fileUploads.api.d.ts +17 -7
  9. package/dist/api/fileUploads.api.js +30 -16
  10. package/dist/api/pages.api.d.ts +50 -0
  11. package/dist/api/pages.api.js +5 -0
  12. package/dist/client.d.ts +64 -2
  13. package/dist/client.js +150 -36
  14. package/dist/errors.d.ts +36 -3
  15. package/dist/errors.js +59 -4
  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/page.model.d.ts +20 -1
  21. package/dist/models/page.model.js +33 -2
  22. package/dist/schemas/block.schema.d.ts +1082 -224
  23. package/dist/schemas/block.schema.js +40 -25
  24. package/dist/schemas/comment.schema.d.ts +18 -0
  25. package/dist/schemas/dataSource.schema.d.ts +36 -0
  26. package/dist/schemas/database.schema.d.ts +36 -0
  27. package/dist/schemas/meetingNotesQuery.schema.d.ts +1082 -224
  28. package/dist/schemas/page.schema.d.ts +46 -0
  29. package/dist/schemas/page.schema.js +2 -1
  30. package/dist/schemas/pageMarkdown.schema.js +1 -1
  31. package/dist/schemas/pageProperties.schema.d.ts +99 -1
  32. package/dist/schemas/pageProperties.schema.js +16 -1
  33. package/dist/schemas/pagination.schema.d.ts +1 -1
  34. package/dist/schemas/propertyObjects.schema.js +2 -1
  35. package/dist/schemas/richText.schema.d.ts +36 -0
  36. package/dist/schemas/richText.schema.js +21 -0
  37. package/dist/schemas/shared.schema.d.ts +3 -7
  38. package/dist/schemas/shared.schema.js +5 -9
  39. package/dist/schemas/view.schema.d.ts +46 -0
  40. package/dist/validation.d.ts +8 -0
  41. package/dist/validation.js +16 -0
  42. package/package.json +2 -2
@@ -1,83 +1,117 @@
1
1
  "use strict";
2
- // ---------------------------------------------------------------------------
3
- // Filter value types
4
- // ---------------------------------------------------------------------------
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.filter = void 0;
4
+ const validation_1 = require("../validation");
5
+ /**
6
+ * Assert that a select-like filter value is a single string. Notion rejects an
7
+ * array for the `equals`, `does_not_equal`, `contains`, and `does_not_contain`
8
+ * operators on `select`, `status`, and `multi_select` properties.
9
+ *
10
+ * The parameter type still accepts `string[]` for backward compatibility. An
11
+ * array now fails fast with a clear error instead of a generic API 400.
12
+ *
13
+ * @throws {NotionValidationError} If `value` is an array.
14
+ */
15
+ function assertScalarFilterValue(value, label) {
16
+ if (Array.isArray(value)) {
17
+ throw new validation_1.NotionValidationError(`${label} accepts a single string, not an array. Compose an OR filter with filter.or(...) to match multiple values.`);
18
+ }
19
+ }
7
20
  // ---------------------------------------------------------------------------
8
21
  // Property filter builders
9
22
  // ---------------------------------------------------------------------------
10
23
  /** Operators common to text-like properties (title, rich_text, url, email, phone_number). */
11
24
  class TextFilter {
12
- constructor(property, propertyType) {
25
+ constructor(property, propertyType, isFormula = false) {
13
26
  this.property = property;
14
27
  this.propertyType = propertyType;
28
+ this.isFormula = isFormula;
29
+ }
30
+ build(condition) {
31
+ if (this.isFormula) {
32
+ return { property: this.property, formula: { string: condition } };
33
+ }
34
+ return { property: this.property, [this.propertyType]: condition };
15
35
  }
16
36
  equals(value) {
17
- return { property: this.property, [this.propertyType]: { equals: value } };
37
+ return this.build({ equals: value });
18
38
  }
19
39
  doesNotEqual(value) {
20
- return { property: this.property, [this.propertyType]: { does_not_equal: value } };
40
+ return this.build({ does_not_equal: value });
21
41
  }
22
42
  contains(value) {
23
- return { property: this.property, [this.propertyType]: { contains: value } };
43
+ return this.build({ contains: value });
24
44
  }
25
45
  doesNotContain(value) {
26
- return { property: this.property, [this.propertyType]: { does_not_contain: value } };
46
+ return this.build({ does_not_contain: value });
27
47
  }
28
48
  startsWith(value) {
29
- return { property: this.property, [this.propertyType]: { starts_with: value } };
49
+ return this.build({ starts_with: value });
30
50
  }
31
51
  endsWith(value) {
32
- return { property: this.property, [this.propertyType]: { ends_with: value } };
52
+ return this.build({ ends_with: value });
33
53
  }
34
54
  isEmpty() {
35
- return { property: this.property, [this.propertyType]: { is_empty: true } };
55
+ return this.build({ is_empty: true });
36
56
  }
37
57
  isNotEmpty() {
38
- return { property: this.property, [this.propertyType]: { is_not_empty: true } };
58
+ return this.build({ is_not_empty: true });
39
59
  }
40
60
  }
41
61
  /** Operators for number properties. */
42
62
  class NumberFilter {
43
- constructor(property) {
63
+ constructor(property, isFormula = false) {
44
64
  this.property = property;
65
+ this.isFormula = isFormula;
66
+ }
67
+ build(condition) {
68
+ if (this.isFormula) {
69
+ return { property: this.property, formula: { number: condition } };
70
+ }
71
+ return { property: this.property, number: condition };
45
72
  }
46
73
  equals(value) {
47
- return { property: this.property, number: { equals: value } };
74
+ return this.build({ equals: value });
48
75
  }
49
76
  doesNotEqual(value) {
50
- return { property: this.property, number: { does_not_equal: value } };
77
+ return this.build({ does_not_equal: value });
51
78
  }
52
79
  greaterThan(value) {
53
- return { property: this.property, number: { greater_than: value } };
80
+ return this.build({ greater_than: value });
54
81
  }
55
82
  greaterThanOrEqualTo(value) {
56
- return { property: this.property, number: { greater_than_or_equal_to: value } };
83
+ return this.build({ greater_than_or_equal_to: value });
57
84
  }
58
85
  lessThan(value) {
59
- return { property: this.property, number: { less_than: value } };
86
+ return this.build({ less_than: value });
60
87
  }
61
88
  lessThanOrEqualTo(value) {
62
- return { property: this.property, number: { less_than_or_equal_to: value } };
89
+ return this.build({ less_than_or_equal_to: value });
63
90
  }
64
91
  isEmpty() {
65
- return { property: this.property, number: { is_empty: true } };
92
+ return this.build({ is_empty: true });
66
93
  }
67
94
  isNotEmpty() {
68
- return { property: this.property, number: { is_not_empty: true } };
95
+ return this.build({ is_not_empty: true });
69
96
  }
70
97
  }
71
98
  /** Operators for checkbox properties. */
72
99
  class CheckboxFilter {
73
- constructor(property) {
100
+ constructor(property, isFormula = false) {
74
101
  this.property = property;
102
+ this.isFormula = isFormula;
103
+ }
104
+ build(condition) {
105
+ if (this.isFormula) {
106
+ return { property: this.property, formula: { checkbox: condition } };
107
+ }
108
+ return { property: this.property, checkbox: condition };
75
109
  }
76
110
  equals(value) {
77
- return { property: this.property, checkbox: { equals: value } };
111
+ return this.build({ equals: value });
78
112
  }
79
113
  doesNotEqual(value) {
80
- return { property: this.property, checkbox: { does_not_equal: value } };
114
+ return this.build({ does_not_equal: value });
81
115
  }
82
116
  }
83
117
  /** Operators for select properties. */
@@ -86,9 +120,11 @@ class SelectFilter {
86
120
  this.property = property;
87
121
  }
88
122
  equals(value) {
123
+ assertScalarFilterValue(value, 'select().equals');
89
124
  return { property: this.property, select: { equals: value } };
90
125
  }
91
126
  doesNotEqual(value) {
127
+ assertScalarFilterValue(value, 'select().doesNotEqual');
92
128
  return { property: this.property, select: { does_not_equal: value } };
93
129
  }
94
130
  isEmpty() {
@@ -104,9 +140,11 @@ class MultiSelectFilter {
104
140
  this.property = property;
105
141
  }
106
142
  contains(value) {
143
+ assertScalarFilterValue(value, 'multiSelect().contains');
107
144
  return { property: this.property, multi_select: { contains: value } };
108
145
  }
109
146
  doesNotContain(value) {
147
+ assertScalarFilterValue(value, 'multiSelect().doesNotContain');
110
148
  return { property: this.property, multi_select: { does_not_contain: value } };
111
149
  }
112
150
  isEmpty() {
@@ -122,9 +160,11 @@ class StatusFilter {
122
160
  this.property = property;
123
161
  }
124
162
  equals(value) {
163
+ assertScalarFilterValue(value, 'status().equals');
125
164
  return { property: this.property, status: { equals: value } };
126
165
  }
127
166
  doesNotEqual(value) {
167
+ assertScalarFilterValue(value, 'status().doesNotEqual');
128
168
  return { property: this.property, status: { does_not_equal: value } };
129
169
  }
130
170
  isEmpty() {
@@ -136,11 +176,15 @@ class StatusFilter {
136
176
  }
137
177
  /** Operators for date properties and timestamp filters. */
138
178
  class DateFilter {
139
- constructor(key, isTimestamp) {
179
+ constructor(key, isTimestamp, isFormula = false) {
140
180
  this.key = key;
141
181
  this.isTimestamp = isTimestamp;
182
+ this.isFormula = isFormula;
142
183
  }
143
184
  wrap(condition) {
185
+ if (this.isFormula) {
186
+ return { property: this.key, formula: { date: condition } };
187
+ }
144
188
  if (this.isTimestamp) {
145
189
  return { timestamp: this.key, [this.key]: condition };
146
190
  }
@@ -240,17 +284,16 @@ class FormulaFilter {
240
284
  this.property = property;
241
285
  }
242
286
  text() {
243
- return new TextFilter(this.property, 'formula');
287
+ return new TextFilter(this.property, 'formula', true);
244
288
  }
245
289
  number() {
246
- // Return a NumberFilter-like but under "formula" key
247
- return new NumberFilter(this.property);
290
+ return new NumberFilter(this.property, true);
248
291
  }
249
292
  checkbox() {
250
- return new CheckboxFilter(this.property);
293
+ return new CheckboxFilter(this.property, true);
251
294
  }
252
295
  date() {
253
- return new DateFilter(this.property, false);
296
+ return new DateFilter(this.property, false, true);
254
297
  }
255
298
  }
256
299
  // ---------------------------------------------------------------------------
@@ -43,8 +43,13 @@ export type PaginatedFetchFunction<T> = (cursor?: string) => Promise<PaginatedLi
43
43
  /**
44
44
  * Collects all results from a paginated endpoint by automatically following cursors.
45
45
  *
46
- * This function fetches pages until `has_more` is `false`. It collects all results
47
- * into one array. Use this function when you need all results at once.
46
+ * This function fetches pages until `has_more` is `false` or `next_cursor` is `null`.
47
+ * It collects all results into one array. Use this function when you need all results
48
+ * at once.
49
+ *
50
+ * This helper does not work around the 10,000-result cap on data source, view, and
51
+ * meeting-notes queries. When a query hits that cap, the result set is truncated and
52
+ * this helper writes a warning. Use {@link collectAllDataSourceRows} for those queries.
48
53
  *
49
54
  * @param fetchPage - Function that fetches a single page of results
50
55
  * @returns Array containing all results from all pages
@@ -5,11 +5,32 @@ exports.paginateIterator = paginateIterator;
5
5
  exports.paginateWithMetadata = paginateWithMetadata;
6
6
  exports.iterateAllDataSourceRows = iterateAllDataSourceRows;
7
7
  exports.collectAllDataSourceRows = collectAllDataSourceRows;
8
+ /**
9
+ * Return the cursor for the next page, or `undefined` when iteration must stop
10
+ * (`has_more` is `false`, or `next_cursor` is `null`). Warn when the result is a
11
+ * truncated query (`request_status.type === 'incomplete'`).
12
+ */
13
+ function nextCursor(response) {
14
+ if (response.request_status?.type === 'incomplete') {
15
+ console.warn('Notion returned a truncated query result (the 10,000-result cap was reached). ' +
16
+ 'This paginate helper stops here and the result set is incomplete. ' +
17
+ 'Use collectAllDataSourceRows() or iterateAllDataSourceRows() to read every row.');
18
+ }
19
+ if (!response.has_more || response.next_cursor === null) {
20
+ return undefined;
21
+ }
22
+ return response.next_cursor ?? undefined;
23
+ }
8
24
  /**
9
25
  * Collects all results from a paginated endpoint by automatically following cursors.
10
26
  *
11
- * This function fetches pages until `has_more` is `false`. It collects all results
12
- * into one array. Use this function when you need all results at once.
27
+ * This function fetches pages until `has_more` is `false` or `next_cursor` is `null`.
28
+ * It collects all results into one array. Use this function when you need all results
29
+ * at once.
30
+ *
31
+ * This helper does not work around the 10,000-result cap on data source, view, and
32
+ * meeting-notes queries. When a query hits that cap, the result set is truncated and
33
+ * this helper writes a warning. Use {@link collectAllDataSourceRows} for those queries.
13
34
  *
14
35
  * @param fetchPage - Function that fetches a single page of results
15
36
  * @returns Array containing all results from all pages
@@ -58,7 +79,7 @@ async function paginate(fetchPage) {
58
79
  do {
59
80
  const response = await fetchPage(cursor);
60
81
  all.push(...response.results);
61
- cursor = response.next_cursor ?? undefined;
82
+ cursor = nextCursor(response);
62
83
  } while (cursor);
63
84
  return all;
64
85
  }
@@ -112,7 +133,7 @@ async function* paginateIterator(fetchPage) {
112
133
  for (const item of response.results) {
113
134
  yield item;
114
135
  }
115
- cursor = response.next_cursor ?? undefined;
136
+ cursor = nextCursor(response);
116
137
  } while (cursor);
117
138
  }
118
139
  /**
@@ -142,7 +163,7 @@ async function paginateWithMetadata(fetchPage) {
142
163
  do {
143
164
  const response = await fetchPage(cursor);
144
165
  items.push(...response.results);
145
- cursor = response.next_cursor ?? undefined;
166
+ cursor = nextCursor(response);
146
167
  pageCount++;
147
168
  } while (cursor);
148
169
  return {
@@ -28,11 +28,30 @@ export declare class Page extends BaseModel<NotionPage> {
28
28
  */
29
29
  getTitle(): string | null;
30
30
  /**
31
- * Check if the page is a child of a database.
31
+ * Check if the page is a row in a database.
32
+ *
33
+ * On API version 2025-09-03 and later, a database row has a `data_source_id`
34
+ * parent. Older responses use a `database_id` parent. This method returns `true`
35
+ * for both.
32
36
  */
33
37
  isInDatabase(): boolean;
38
+ /**
39
+ * Check if the page is a row in a data source.
40
+ */
41
+ isInDataSource(): boolean;
34
42
  /**
35
43
  * Check if the page is a child of another page.
36
44
  */
37
45
  isSubpage(): boolean;
46
+ /**
47
+ * Get the parent data source ID.
48
+ * Return `null` when the parent is not a data source.
49
+ */
50
+ getParentDataSourceId(): string | null;
51
+ /**
52
+ * Get the parent database ID.
53
+ * A `data_source_id` parent also carries its database ID. Return `null` when the
54
+ * parent is neither a database nor a data source.
55
+ */
56
+ getParentDatabaseId(): string | null;
38
57
  }
@@ -62,10 +62,20 @@ class Page extends base_model_1.BaseModel {
62
62
  return null;
63
63
  }
64
64
  /**
65
- * Check if the page is a child of a database.
65
+ * Check if the page is a row in a database.
66
+ *
67
+ * On API version 2025-09-03 and later, a database row has a `data_source_id`
68
+ * parent. Older responses use a `database_id` parent. This method returns `true`
69
+ * for both.
66
70
  */
67
71
  isInDatabase() {
68
- return this.data.parent.type === 'database_id';
72
+ return this.data.parent.type === 'data_source_id' || this.data.parent.type === 'database_id';
73
+ }
74
+ /**
75
+ * Check if the page is a row in a data source.
76
+ */
77
+ isInDataSource() {
78
+ return this.data.parent.type === 'data_source_id';
69
79
  }
70
80
  /**
71
81
  * Check if the page is a child of another page.
@@ -73,5 +83,26 @@ class Page extends base_model_1.BaseModel {
73
83
  isSubpage() {
74
84
  return this.data.parent.type === 'page_id';
75
85
  }
86
+ /**
87
+ * Get the parent data source ID.
88
+ * Return `null` when the parent is not a data source.
89
+ */
90
+ getParentDataSourceId() {
91
+ return this.data.parent.type === 'data_source_id' ? this.data.parent.data_source_id : null;
92
+ }
93
+ /**
94
+ * Get the parent database ID.
95
+ * A `data_source_id` parent also carries its database ID. Return `null` when the
96
+ * parent is neither a database nor a data source.
97
+ */
98
+ getParentDatabaseId() {
99
+ if (this.data.parent.type === 'database_id') {
100
+ return this.data.parent.database_id;
101
+ }
102
+ if (this.data.parent.type === 'data_source_id') {
103
+ return this.data.parent.database_id;
104
+ }
105
+ return null;
106
+ }
76
107
  }
77
108
  exports.Page = Page;