@visus-io/notion-sdk-ts 3.2.0 → 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.
- package/dist/api/blocks.api.d.ts +1083 -224
- package/dist/api/blocks.api.js +4 -0
- package/dist/api/comments.api.d.ts +19 -1
- package/dist/api/comments.api.js +1 -1
- package/dist/api/dataSources.api.d.ts +36 -0
- package/dist/api/databases.api.d.ts +36 -0
- package/dist/api/databases.api.js +1 -0
- package/dist/api/fileUploads.api.d.ts +17 -7
- package/dist/api/fileUploads.api.js +30 -16
- package/dist/api/pages.api.d.ts +46 -0
- package/dist/client.d.ts +33 -2
- package/dist/client.js +88 -21
- package/dist/helpers/filter.helpers.d.ts +11 -4
- package/dist/helpers/filter.helpers.js +73 -30
- package/dist/helpers/pagination.helpers.d.ts +7 -2
- package/dist/helpers/pagination.helpers.js +26 -5
- package/dist/models/page.model.d.ts +20 -1
- package/dist/models/page.model.js +33 -2
- package/dist/schemas/block.schema.d.ts +1082 -224
- package/dist/schemas/block.schema.js +40 -25
- package/dist/schemas/comment.schema.d.ts +18 -0
- package/dist/schemas/dataSource.schema.d.ts +36 -0
- package/dist/schemas/database.schema.d.ts +36 -0
- package/dist/schemas/meetingNotesQuery.schema.d.ts +1082 -224
- package/dist/schemas/page.schema.d.ts +46 -0
- package/dist/schemas/page.schema.js +2 -1
- package/dist/schemas/pageMarkdown.schema.js +1 -1
- package/dist/schemas/pageProperties.schema.d.ts +99 -1
- package/dist/schemas/pageProperties.schema.js +16 -1
- package/dist/schemas/pagination.schema.d.ts +1 -1
- package/dist/schemas/propertyObjects.schema.js +2 -1
- package/dist/schemas/richText.schema.d.ts +36 -0
- package/dist/schemas/richText.schema.js +21 -0
- package/dist/schemas/shared.schema.d.ts +3 -7
- package/dist/schemas/shared.schema.js +5 -9
- package/dist/schemas/view.schema.d.ts +46 -0
- package/dist/validation.d.ts +8 -0
- package/dist/validation.js +16 -0
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.NotionClient = exports.NOTION_VERSION = void 0;
|
|
4
4
|
const errors_1 = require("./errors");
|
|
5
|
+
const validation_1 = require("./validation");
|
|
5
6
|
/**
|
|
6
7
|
* The Notion API version this SDK uses.
|
|
7
8
|
* All schemas, request bodies, helpers, and models depend on this version.
|
|
@@ -9,6 +10,8 @@ const errors_1 = require("./errors");
|
|
|
9
10
|
* @category Client & Core
|
|
10
11
|
*/
|
|
11
12
|
exports.NOTION_VERSION = '2026-03-11';
|
|
13
|
+
/** Upper bound for a single retry wait, in milliseconds. */
|
|
14
|
+
const MAX_RETRY_DELAY_MS = 60_000;
|
|
12
15
|
/**
|
|
13
16
|
* Base HTTP client for Notion API requests.
|
|
14
17
|
*/
|
|
@@ -36,10 +39,10 @@ class NotionClient {
|
|
|
36
39
|
return await this.makeRequest(options);
|
|
37
40
|
}
|
|
38
41
|
catch (error) {
|
|
39
|
-
// Retry
|
|
40
|
-
// responses, which the API recommends always retrying.
|
|
42
|
+
// Retry per isRetryable(); retryOnRateLimit:false suppresses only rate_limited.
|
|
41
43
|
if (error instanceof errors_1.NotionAPIError &&
|
|
42
|
-
|
|
44
|
+
error.isRetryable() &&
|
|
45
|
+
!(error.isRateLimited() && !this.retryOnRateLimit) &&
|
|
43
46
|
attempt < this.maxRetries) {
|
|
44
47
|
// Prefer the server-supplied Retry-After value; fall back to
|
|
45
48
|
// exponential backoff when the header is absent.
|
|
@@ -55,21 +58,46 @@ class NotionClient {
|
|
|
55
58
|
// If we exhausted all retries, throw the last error
|
|
56
59
|
throw lastError ?? new Error('Request failed after all retries');
|
|
57
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Send a `multipart/form-data` `POST` to a Notion file-upload URL. Reuses the
|
|
63
|
+
* configured `fetch` and the request timeout. Does not retry. Does not set
|
|
64
|
+
* `Content-Type`, so `fetch` adds the multipart boundary.
|
|
65
|
+
*
|
|
66
|
+
* @param uploadUrl - The absolute `upload_url` from `fileUploads.initiate()`.
|
|
67
|
+
* @param form - A `FormData` body with the file bytes under the `file` key.
|
|
68
|
+
* @throws {NotionValidationError} If `uploadUrl` is not a valid `https` URL.
|
|
69
|
+
* @throws {NotionAPIError} If the endpoint returns an error response.
|
|
70
|
+
* @throws {NotionRequestTimeoutError} If the request exceeds the timeout.
|
|
71
|
+
* @throws {NotionNetworkError} If a network problem blocks the request.
|
|
72
|
+
*/
|
|
73
|
+
async sendFileUpload(uploadUrl, form) {
|
|
74
|
+
this.assertHttpsUrl(uploadUrl);
|
|
75
|
+
const { 'Content-Type': _contentType, ...headers } = this.requestHeaders;
|
|
76
|
+
try {
|
|
77
|
+
const response = await this.fetchWithTimeout(uploadUrl, {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
headers,
|
|
80
|
+
body: form,
|
|
81
|
+
});
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
await this.handleErrorResponse(response);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
this.mapTransportError(error);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
58
90
|
/**
|
|
59
91
|
* Send one HTTP request to the Notion API.
|
|
60
92
|
*/
|
|
61
93
|
async makeRequest(options) {
|
|
62
94
|
const url = this.buildUrl(options.path, options.query);
|
|
63
|
-
const controller = new AbortController();
|
|
64
|
-
const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
65
95
|
try {
|
|
66
|
-
const response = await this.
|
|
96
|
+
const response = await this.fetchWithTimeout(url, {
|
|
67
97
|
method: options.method,
|
|
68
98
|
headers: this.requestHeaders,
|
|
69
99
|
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
70
|
-
signal: controller.signal,
|
|
71
100
|
});
|
|
72
|
-
clearTimeout(timeoutId);
|
|
73
101
|
if (!response.ok) {
|
|
74
102
|
await this.handleErrorResponse(response);
|
|
75
103
|
}
|
|
@@ -81,18 +109,56 @@ class NotionClient {
|
|
|
81
109
|
return data;
|
|
82
110
|
}
|
|
83
111
|
catch (error) {
|
|
112
|
+
this.mapTransportError(error);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Call the configured `fetch` with an abort timeout of `timeoutMs`.
|
|
117
|
+
* Clear the timer as soon as the response headers arrive or the request fails.
|
|
118
|
+
*/
|
|
119
|
+
async fetchWithTimeout(url, init) {
|
|
120
|
+
const controller = new AbortController();
|
|
121
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
122
|
+
try {
|
|
123
|
+
return await this.fetchImpl(url, { ...init, signal: controller.signal });
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
84
126
|
clearTimeout(timeoutId);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Map a transport-layer failure to the matching SDK error and throw it.
|
|
131
|
+
* Rethrow a `NotionAPIError` unchanged. Map an `AbortError` to
|
|
132
|
+
* `NotionRequestTimeoutError`. Map any other `Error` to `NotionNetworkError`.
|
|
133
|
+
*/
|
|
134
|
+
mapTransportError(error) {
|
|
135
|
+
if (error instanceof errors_1.NotionAPIError) {
|
|
94
136
|
throw error;
|
|
95
137
|
}
|
|
138
|
+
if (error instanceof Error) {
|
|
139
|
+
if (error.name === 'AbortError') {
|
|
140
|
+
throw new errors_1.NotionRequestTimeoutError(`Request timed out after ${this.timeoutMs}ms`);
|
|
141
|
+
}
|
|
142
|
+
throw new errors_1.NotionNetworkError('Network request failed', error);
|
|
143
|
+
}
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Assert that `url` is a valid `https` URL. The SDK sends the API token in the
|
|
148
|
+
* `Authorization` header of a file upload. Reject a plaintext `http` URL so the
|
|
149
|
+
* token never travels unencrypted.
|
|
150
|
+
*/
|
|
151
|
+
assertHttpsUrl(url) {
|
|
152
|
+
let protocol;
|
|
153
|
+
try {
|
|
154
|
+
protocol = new URL(url).protocol;
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
throw new validation_1.NotionValidationError(`Invalid file upload URL: ${url}`);
|
|
158
|
+
}
|
|
159
|
+
if (protocol !== 'https:') {
|
|
160
|
+
throw new validation_1.NotionValidationError(`File upload URL must use https, got "${protocol}"`);
|
|
161
|
+
}
|
|
96
162
|
}
|
|
97
163
|
/**
|
|
98
164
|
* Calculate a fallback retry delay with exponential backoff.
|
|
@@ -101,7 +167,7 @@ class NotionClient {
|
|
|
101
167
|
*/
|
|
102
168
|
getRetryAfter(attempt) {
|
|
103
169
|
const backoffMs = Math.pow(2, attempt) * 1000;
|
|
104
|
-
return Math.min(backoffMs,
|
|
170
|
+
return Math.min(backoffMs, MAX_RETRY_DELAY_MS);
|
|
105
171
|
}
|
|
106
172
|
/**
|
|
107
173
|
* Pause for the given duration, in milliseconds.
|
|
@@ -130,8 +196,9 @@ class NotionClient {
|
|
|
130
196
|
return url.toString();
|
|
131
197
|
}
|
|
132
198
|
/**
|
|
133
|
-
* Parse the `Retry-After` response header into milliseconds
|
|
134
|
-
* Return `undefined` if the header is missing or
|
|
199
|
+
* Parse the `Retry-After` response header into milliseconds, clamped to
|
|
200
|
+
* {@link MAX_RETRY_DELAY_MS}. Return `undefined` if the header is missing or
|
|
201
|
+
* not a valid non-negative number.
|
|
135
202
|
*/
|
|
136
203
|
parseRetryAfterHeader(response) {
|
|
137
204
|
const header = response.headers.get('Retry-After');
|
|
@@ -142,7 +209,7 @@ class NotionClient {
|
|
|
142
209
|
if (!Number.isFinite(seconds) || seconds < 0) {
|
|
143
210
|
return undefined;
|
|
144
211
|
}
|
|
145
|
-
return Math.ceil(seconds) * 1000;
|
|
212
|
+
return Math.min(Math.ceil(seconds) * 1000, MAX_RETRY_DELAY_MS);
|
|
146
213
|
}
|
|
147
214
|
/**
|
|
148
215
|
* Handle an error response from the API.
|
|
@@ -4,7 +4,9 @@ type FilterCondition = Record<string, unknown>;
|
|
|
4
4
|
declare class TextFilter {
|
|
5
5
|
private readonly property;
|
|
6
6
|
private readonly propertyType;
|
|
7
|
-
|
|
7
|
+
private readonly isFormula;
|
|
8
|
+
constructor(property: string, propertyType: string, isFormula?: boolean);
|
|
9
|
+
private build;
|
|
8
10
|
equals(value: string): FilterCondition;
|
|
9
11
|
doesNotEqual(value: string): FilterCondition;
|
|
10
12
|
contains(value: string): FilterCondition;
|
|
@@ -17,7 +19,9 @@ declare class TextFilter {
|
|
|
17
19
|
/** Operators for number properties. */
|
|
18
20
|
declare class NumberFilter {
|
|
19
21
|
private readonly property;
|
|
20
|
-
|
|
22
|
+
private readonly isFormula;
|
|
23
|
+
constructor(property: string, isFormula?: boolean);
|
|
24
|
+
private build;
|
|
21
25
|
equals(value: number): FilterCondition;
|
|
22
26
|
doesNotEqual(value: number): FilterCondition;
|
|
23
27
|
greaterThan(value: number): FilterCondition;
|
|
@@ -30,7 +34,9 @@ declare class NumberFilter {
|
|
|
30
34
|
/** Operators for checkbox properties. */
|
|
31
35
|
declare class CheckboxFilter {
|
|
32
36
|
private readonly property;
|
|
33
|
-
|
|
37
|
+
private readonly isFormula;
|
|
38
|
+
constructor(property: string, isFormula?: boolean);
|
|
39
|
+
private build;
|
|
34
40
|
equals(value: boolean): FilterCondition;
|
|
35
41
|
doesNotEqual(value: boolean): FilterCondition;
|
|
36
42
|
}
|
|
@@ -65,7 +71,8 @@ declare class StatusFilter {
|
|
|
65
71
|
declare class DateFilter {
|
|
66
72
|
private readonly key;
|
|
67
73
|
private readonly isTimestamp;
|
|
68
|
-
|
|
74
|
+
private readonly isFormula;
|
|
75
|
+
constructor(key: string, isTimestamp: boolean, isFormula?: boolean);
|
|
69
76
|
private wrap;
|
|
70
77
|
equals(value: string): FilterCondition;
|
|
71
78
|
before(value: string): FilterCondition;
|
|
@@ -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
|
|
37
|
+
return this.build({ equals: value });
|
|
18
38
|
}
|
|
19
39
|
doesNotEqual(value) {
|
|
20
|
-
return
|
|
40
|
+
return this.build({ does_not_equal: value });
|
|
21
41
|
}
|
|
22
42
|
contains(value) {
|
|
23
|
-
return
|
|
43
|
+
return this.build({ contains: value });
|
|
24
44
|
}
|
|
25
45
|
doesNotContain(value) {
|
|
26
|
-
return
|
|
46
|
+
return this.build({ does_not_contain: value });
|
|
27
47
|
}
|
|
28
48
|
startsWith(value) {
|
|
29
|
-
return
|
|
49
|
+
return this.build({ starts_with: value });
|
|
30
50
|
}
|
|
31
51
|
endsWith(value) {
|
|
32
|
-
return
|
|
52
|
+
return this.build({ ends_with: value });
|
|
33
53
|
}
|
|
34
54
|
isEmpty() {
|
|
35
|
-
return
|
|
55
|
+
return this.build({ is_empty: true });
|
|
36
56
|
}
|
|
37
57
|
isNotEmpty() {
|
|
38
|
-
return
|
|
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
|
|
74
|
+
return this.build({ equals: value });
|
|
48
75
|
}
|
|
49
76
|
doesNotEqual(value) {
|
|
50
|
-
return
|
|
77
|
+
return this.build({ does_not_equal: value });
|
|
51
78
|
}
|
|
52
79
|
greaterThan(value) {
|
|
53
|
-
return
|
|
80
|
+
return this.build({ greater_than: value });
|
|
54
81
|
}
|
|
55
82
|
greaterThanOrEqualTo(value) {
|
|
56
|
-
return
|
|
83
|
+
return this.build({ greater_than_or_equal_to: value });
|
|
57
84
|
}
|
|
58
85
|
lessThan(value) {
|
|
59
|
-
return
|
|
86
|
+
return this.build({ less_than: value });
|
|
60
87
|
}
|
|
61
88
|
lessThanOrEqualTo(value) {
|
|
62
|
-
return
|
|
89
|
+
return this.build({ less_than_or_equal_to: value });
|
|
63
90
|
}
|
|
64
91
|
isEmpty() {
|
|
65
|
-
return
|
|
92
|
+
return this.build({ is_empty: true });
|
|
66
93
|
}
|
|
67
94
|
isNotEmpty() {
|
|
68
|
-
return
|
|
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
|
|
111
|
+
return this.build({ equals: value });
|
|
78
112
|
}
|
|
79
113
|
doesNotEqual(value) {
|
|
80
|
-
return
|
|
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
|
-
|
|
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
|
|
47
|
-
* into one array. Use this function when you need all results
|
|
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
|
|
12
|
-
* into one array. Use this function when you need all results
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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;
|