@visus-io/notion-sdk-ts 3.0.2 → 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.
- package/README.md +7 -6
- package/dist/api/asyncTasks.api.d.ts +70 -0
- package/dist/api/asyncTasks.api.js +68 -0
- package/dist/api/base.api.js +1 -0
- package/dist/api/blocks.api.d.ts +433 -73
- package/dist/api/blocks.api.js +39 -0
- package/dist/api/comments.api.d.ts +40 -5
- package/dist/api/comments.api.js +43 -1
- package/dist/api/customEmojis.api.d.ts +36 -0
- package/dist/api/customEmojis.api.js +36 -0
- package/dist/api/dataSources.api.d.ts +62 -11
- package/dist/api/dataSources.api.js +22 -0
- package/dist/api/databases.api.d.ts +40 -6
- package/dist/api/fileUploads.api.d.ts +1 -1
- package/dist/api/index.d.ts +3 -0
- package/dist/api/index.js +7 -1
- package/dist/api/pages.api.d.ts +142 -10
- package/dist/api/pages.api.js +64 -0
- package/dist/api/search.api.d.ts +10 -2
- package/dist/api/users.api.d.ts +1 -0
- package/dist/api/views.api.d.ts +284 -0
- package/dist/api/views.api.js +169 -0
- package/dist/client.js +3 -3
- package/dist/errors.d.ts +5 -1
- package/dist/errors.js +6 -0
- package/dist/helpers/block.helpers.d.ts +38 -1
- package/dist/helpers/block.helpers.js +45 -3
- package/dist/helpers/file.helpers.d.ts +36 -1
- package/dist/helpers/file.helpers.js +26 -4
- package/dist/helpers/filter.helpers.d.ts +6 -6
- package/dist/helpers/index.d.ts +3 -2
- package/dist/helpers/index.js +5 -1
- package/dist/helpers/pagination.helpers.d.ts +56 -0
- package/dist/helpers/pagination.helpers.js +86 -0
- package/dist/helpers/property.helpers.d.ts +29 -0
- package/dist/helpers/property.helpers.js +27 -0
- package/dist/helpers/webhook.helpers.d.ts +52 -0
- package/dist/helpers/webhook.helpers.js +81 -0
- package/dist/models/asyncTask.model.d.ts +59 -0
- package/dist/models/asyncTask.model.js +89 -0
- package/dist/models/block.model.js +4 -1
- package/dist/models/customEmoji.model.d.ts +28 -0
- package/dist/models/customEmoji.model.js +42 -0
- package/dist/models/dataSource.model.d.ts +3 -3
- package/dist/models/dataSource.model.js +1 -1
- package/dist/models/database.model.d.ts +7 -3
- package/dist/models/database.model.js +7 -1
- package/dist/models/index.d.ts +3 -0
- package/dist/models/index.js +7 -1
- package/dist/models/page.model.d.ts +2 -0
- package/dist/models/page.model.js +6 -0
- package/dist/models/view.model.d.ts +79 -0
- package/dist/models/view.model.js +119 -0
- package/dist/notion.d.ts +26 -7
- package/dist/notion.js +19 -3
- package/dist/schemas/asyncTask.schema.d.ts +42 -0
- package/dist/schemas/asyncTask.schema.js +83 -0
- package/dist/schemas/block.schema.d.ts +378 -72
- package/dist/schemas/block.schema.js +33 -7
- package/dist/schemas/comment.schema.d.ts +8 -3
- package/dist/schemas/customEmoji.schema.d.ts +13 -0
- package/dist/schemas/customEmoji.schema.js +48 -0
- package/dist/schemas/dataSource.schema.d.ts +68 -10
- package/dist/schemas/dataSource.schema.js +22 -4
- package/dist/schemas/database.schema.d.ts +38 -6
- package/dist/schemas/database.schema.js +3 -2
- package/dist/schemas/fileUpload.schema.d.ts +1 -1
- package/dist/schemas/icon.schema.d.ts +92 -0
- package/dist/schemas/icon.schema.js +88 -0
- package/dist/schemas/index.d.ts +6 -0
- package/dist/schemas/index.js +6 -0
- package/dist/schemas/meetingNotesQuery.schema.d.ts +4187 -0
- package/dist/schemas/meetingNotesQuery.schema.js +62 -0
- package/dist/schemas/page.schema.d.ts +46 -9
- package/dist/schemas/page.schema.js +4 -2
- package/dist/schemas/pageMarkdown.schema.d.ts +59 -0
- package/dist/schemas/pageMarkdown.schema.js +65 -0
- package/dist/schemas/pageProperties.schema.d.ts +30 -18
- package/dist/schemas/pageProperties.schema.js +2 -3
- package/dist/schemas/pagination.schema.d.ts +30 -3
- package/dist/schemas/pagination.schema.js +18 -1
- package/dist/schemas/parent.schema.d.ts +10 -1
- package/dist/schemas/parent.schema.js +9 -3
- package/dist/schemas/propertyObjects.schema.d.ts +12 -12
- package/dist/schemas/richText.schema.d.ts +8 -6
- package/dist/schemas/richText.schema.js +1 -1
- package/dist/schemas/user.schema.d.ts +2 -0
- package/dist/schemas/user.schema.js +2 -1
- package/dist/schemas/view.schema.d.ts +1077 -0
- package/dist/schemas/view.schema.js +115 -0
- package/package.json +2 -2
|
@@ -29,9 +29,6 @@ function resolveRichText(input) {
|
|
|
29
29
|
}
|
|
30
30
|
return input;
|
|
31
31
|
}
|
|
32
|
-
// ---------------------------------------------------------------------------
|
|
33
|
-
// Text blocks
|
|
34
|
-
// ---------------------------------------------------------------------------
|
|
35
32
|
/**
|
|
36
33
|
* Create a paragraph block.
|
|
37
34
|
*
|
|
@@ -48,6 +45,7 @@ function paragraph(text, options) {
|
|
|
48
45
|
paragraph: {
|
|
49
46
|
rich_text: resolveRichText(text),
|
|
50
47
|
color: options?.color ?? 'default',
|
|
48
|
+
...(options?.icon ? { icon: options.icon } : {}),
|
|
51
49
|
...(options?.children ? { children: options.children } : {}),
|
|
52
50
|
},
|
|
53
51
|
};
|
|
@@ -88,6 +86,18 @@ function heading3(text, options) {
|
|
|
88
86
|
},
|
|
89
87
|
};
|
|
90
88
|
}
|
|
89
|
+
function heading4(text, options) {
|
|
90
|
+
return {
|
|
91
|
+
object: 'block',
|
|
92
|
+
type: 'heading_4',
|
|
93
|
+
heading_4: {
|
|
94
|
+
rich_text: resolveRichText(text),
|
|
95
|
+
color: options?.color ?? 'default',
|
|
96
|
+
is_toggleable: options?.isToggleable ?? false,
|
|
97
|
+
...(options?.children ? { children: options.children } : {}),
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
91
101
|
function bulletedListItem(text, options) {
|
|
92
102
|
return {
|
|
93
103
|
object: 'block',
|
|
@@ -170,6 +180,14 @@ function template(text, options) {
|
|
|
170
180
|
},
|
|
171
181
|
};
|
|
172
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* @deprecated Meeting-notes blocks are server-managed -- their real shape (`title`,
|
|
185
|
+
* `status`, and child block IDs for the summary/notes/transcript) is populated by
|
|
186
|
+
* Notion, not hand-constructed by clients. This helper's `rich_text`-based output no
|
|
187
|
+
* longer matches `blockSchema`'s `meeting_notes` shape and is not a valid way to
|
|
188
|
+
* create a meeting-notes block. Kept only to avoid an abrupt removal from the helper
|
|
189
|
+
* surface; do not use for new code.
|
|
190
|
+
*/
|
|
173
191
|
function meetingNotes(text, options) {
|
|
174
192
|
return {
|
|
175
193
|
object: 'block',
|
|
@@ -348,6 +366,28 @@ function column(children) {
|
|
|
348
366
|
column: { children },
|
|
349
367
|
};
|
|
350
368
|
}
|
|
369
|
+
/**
|
|
370
|
+
* Create a tab block. Only `paragraph` blocks may be direct children of a tab block --
|
|
371
|
+
* each tab is modeled as one paragraph, whose rich text is the tab label and whose
|
|
372
|
+
* `children` hold the tab's content.
|
|
373
|
+
*
|
|
374
|
+
* @example
|
|
375
|
+
* ```ts
|
|
376
|
+
* block.tab([
|
|
377
|
+
* { label: 'Overview', children: [block.paragraph('Intro text')] },
|
|
378
|
+
* { label: 'Details', icon: icon.emoji('📋'), children: [block.paragraph('More info')] },
|
|
379
|
+
* ])
|
|
380
|
+
* ```
|
|
381
|
+
*/
|
|
382
|
+
function tab(tabs) {
|
|
383
|
+
return {
|
|
384
|
+
object: 'block',
|
|
385
|
+
type: 'tab',
|
|
386
|
+
tab: {
|
|
387
|
+
children: tabs.map((item) => paragraph(item.label, { color: item.color, icon: item.icon, children: item.children })),
|
|
388
|
+
},
|
|
389
|
+
};
|
|
390
|
+
}
|
|
351
391
|
// ---------------------------------------------------------------------------
|
|
352
392
|
// Synced blocks
|
|
353
393
|
// ---------------------------------------------------------------------------
|
|
@@ -406,6 +446,7 @@ exports.block = {
|
|
|
406
446
|
heading1,
|
|
407
447
|
heading2,
|
|
408
448
|
heading3,
|
|
449
|
+
heading4,
|
|
409
450
|
bulletedListItem,
|
|
410
451
|
numberedListItem,
|
|
411
452
|
toDo,
|
|
@@ -435,6 +476,7 @@ exports.block = {
|
|
|
435
476
|
tableRow,
|
|
436
477
|
columnList,
|
|
437
478
|
column,
|
|
479
|
+
tab,
|
|
438
480
|
// Synced
|
|
439
481
|
syncedBlock,
|
|
440
482
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { NativeIconColor } from '../schemas/icon.schema';
|
|
1
2
|
interface EmojiIcon {
|
|
2
3
|
type: 'emoji';
|
|
3
4
|
emoji: string;
|
|
@@ -14,6 +15,19 @@ interface FileUploadRef {
|
|
|
14
15
|
id: string;
|
|
15
16
|
};
|
|
16
17
|
}
|
|
18
|
+
interface NativeIconRef {
|
|
19
|
+
type: 'icon';
|
|
20
|
+
icon: {
|
|
21
|
+
name: string;
|
|
22
|
+
color?: NativeIconColor;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
interface CustomEmojiIconRef {
|
|
26
|
+
type: 'custom_emoji';
|
|
27
|
+
custom_emoji: {
|
|
28
|
+
id: string;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
17
31
|
/**
|
|
18
32
|
* Create an emoji icon object.
|
|
19
33
|
*
|
|
@@ -42,7 +56,26 @@ declare function externalIcon(url: string): ExternalRef;
|
|
|
42
56
|
*/
|
|
43
57
|
declare function fileUploadIcon(id: string): FileUploadRef;
|
|
44
58
|
/**
|
|
45
|
-
*
|
|
59
|
+
* Create a native (icon-picker) icon object.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* icon.native('star circle', 'blue')
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
declare function nativeIcon(name: string, color?: NativeIconColor): NativeIconRef;
|
|
67
|
+
/**
|
|
68
|
+
* Create a custom emoji icon object, referencing a workspace custom emoji by id.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* icon.customEmoji('emoji-id')
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
declare function customEmojiIcon(id: string): CustomEmojiIconRef;
|
|
76
|
+
/**
|
|
77
|
+
* Helpers for constructing icon objects (emoji, external URL, file upload,
|
|
78
|
+
* native icon, or custom emoji).
|
|
46
79
|
*
|
|
47
80
|
* @example
|
|
48
81
|
* ```ts
|
|
@@ -59,6 +92,8 @@ export declare const icon: {
|
|
|
59
92
|
emoji: typeof emojiIcon;
|
|
60
93
|
external: typeof externalIcon;
|
|
61
94
|
fileUpload: typeof fileUploadIcon;
|
|
95
|
+
native: typeof nativeIcon;
|
|
96
|
+
customEmoji: typeof customEmojiIcon;
|
|
62
97
|
};
|
|
63
98
|
/**
|
|
64
99
|
* Create an external cover image object.
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// ---------------------------------------------------------------------------
|
|
3
|
-
// Return types
|
|
4
|
-
// ---------------------------------------------------------------------------
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.notionFile = exports.cover = exports.icon = void 0;
|
|
7
4
|
// ---------------------------------------------------------------------------
|
|
@@ -41,7 +38,30 @@ function fileUploadIcon(id) {
|
|
|
41
38
|
return { type: 'file_upload', file_upload: { id } };
|
|
42
39
|
}
|
|
43
40
|
/**
|
|
44
|
-
*
|
|
41
|
+
* Create a native (icon-picker) icon object.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* icon.native('star circle', 'blue')
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
function nativeIcon(name, color) {
|
|
49
|
+
return { type: 'icon', icon: { name, ...(color ? { color } : {}) } };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create a custom emoji icon object, referencing a workspace custom emoji by id.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* icon.customEmoji('emoji-id')
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
function customEmojiIcon(id) {
|
|
60
|
+
return { type: 'custom_emoji', custom_emoji: { id } };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Helpers for constructing icon objects (emoji, external URL, file upload,
|
|
64
|
+
* native icon, or custom emoji).
|
|
45
65
|
*
|
|
46
66
|
* @example
|
|
47
67
|
* ```ts
|
|
@@ -58,6 +78,8 @@ exports.icon = {
|
|
|
58
78
|
emoji: emojiIcon,
|
|
59
79
|
external: externalIcon,
|
|
60
80
|
fileUpload: fileUploadIcon,
|
|
81
|
+
native: nativeIcon,
|
|
82
|
+
customEmoji: customEmojiIcon,
|
|
61
83
|
};
|
|
62
84
|
// ---------------------------------------------------------------------------
|
|
63
85
|
// Cover helpers
|
|
@@ -38,8 +38,8 @@ declare class CheckboxFilter {
|
|
|
38
38
|
declare class SelectFilter {
|
|
39
39
|
private readonly property;
|
|
40
40
|
constructor(property: string);
|
|
41
|
-
equals(value: string): FilterCondition;
|
|
42
|
-
doesNotEqual(value: string): FilterCondition;
|
|
41
|
+
equals(value: string | string[]): FilterCondition;
|
|
42
|
+
doesNotEqual(value: string | string[]): FilterCondition;
|
|
43
43
|
isEmpty(): FilterCondition;
|
|
44
44
|
isNotEmpty(): FilterCondition;
|
|
45
45
|
}
|
|
@@ -47,8 +47,8 @@ declare class SelectFilter {
|
|
|
47
47
|
declare class MultiSelectFilter {
|
|
48
48
|
private readonly property;
|
|
49
49
|
constructor(property: string);
|
|
50
|
-
contains(value: string): FilterCondition;
|
|
51
|
-
doesNotContain(value: string): FilterCondition;
|
|
50
|
+
contains(value: string | string[]): FilterCondition;
|
|
51
|
+
doesNotContain(value: string | string[]): FilterCondition;
|
|
52
52
|
isEmpty(): FilterCondition;
|
|
53
53
|
isNotEmpty(): FilterCondition;
|
|
54
54
|
}
|
|
@@ -56,8 +56,8 @@ declare class MultiSelectFilter {
|
|
|
56
56
|
declare class StatusFilter {
|
|
57
57
|
private readonly property;
|
|
58
58
|
constructor(property: string);
|
|
59
|
-
equals(value: string): FilterCondition;
|
|
60
|
-
doesNotEqual(value: string): FilterCondition;
|
|
59
|
+
equals(value: string | string[]): FilterCondition;
|
|
60
|
+
doesNotEqual(value: string | string[]): FilterCondition;
|
|
61
61
|
isEmpty(): FilterCondition;
|
|
62
62
|
isNotEmpty(): FilterCondition;
|
|
63
63
|
}
|
package/dist/helpers/index.d.ts
CHANGED
|
@@ -2,9 +2,10 @@ export { block } from './block.helpers';
|
|
|
2
2
|
export type { RichTextInput } from './block.helpers';
|
|
3
3
|
export { cover, icon, notionFile } from './file.helpers';
|
|
4
4
|
export { filter } from './filter.helpers';
|
|
5
|
-
export { paginate, paginateIterator, paginateWithMetadata } from './pagination.helpers';
|
|
6
|
-
export type { PaginatedFetchFunction } from './pagination.helpers';
|
|
5
|
+
export { collectAllDataSourceRows, iterateAllDataSourceRows, paginate, paginateIterator, paginateWithMetadata, } from './pagination.helpers';
|
|
6
|
+
export type { PaginatedFetchFunction, WindowedFetchFunction } from './pagination.helpers';
|
|
7
7
|
export { parent } from './parent.helpers';
|
|
8
8
|
export { prop } from './property.helpers';
|
|
9
9
|
export { RichTextBuilder, richText } from './richText.helpers';
|
|
10
10
|
export { sort } from './sort.helpers';
|
|
11
|
+
export { webhook } from './webhook.helpers';
|
package/dist/helpers/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sort = exports.richText = exports.RichTextBuilder = exports.prop = exports.parent = exports.paginateWithMetadata = exports.paginateIterator = exports.paginate = exports.filter = exports.notionFile = exports.icon = exports.cover = exports.block = void 0;
|
|
3
|
+
exports.webhook = exports.sort = exports.richText = exports.RichTextBuilder = exports.prop = exports.parent = exports.paginateWithMetadata = exports.paginateIterator = exports.paginate = exports.iterateAllDataSourceRows = exports.collectAllDataSourceRows = exports.filter = exports.notionFile = exports.icon = exports.cover = exports.block = void 0;
|
|
4
4
|
var block_helpers_1 = require("./block.helpers");
|
|
5
5
|
Object.defineProperty(exports, "block", { enumerable: true, get: function () { return block_helpers_1.block; } });
|
|
6
6
|
var file_helpers_1 = require("./file.helpers");
|
|
@@ -10,6 +10,8 @@ Object.defineProperty(exports, "notionFile", { enumerable: true, get: function (
|
|
|
10
10
|
var filter_helpers_1 = require("./filter.helpers");
|
|
11
11
|
Object.defineProperty(exports, "filter", { enumerable: true, get: function () { return filter_helpers_1.filter; } });
|
|
12
12
|
var pagination_helpers_1 = require("./pagination.helpers");
|
|
13
|
+
Object.defineProperty(exports, "collectAllDataSourceRows", { enumerable: true, get: function () { return pagination_helpers_1.collectAllDataSourceRows; } });
|
|
14
|
+
Object.defineProperty(exports, "iterateAllDataSourceRows", { enumerable: true, get: function () { return pagination_helpers_1.iterateAllDataSourceRows; } });
|
|
13
15
|
Object.defineProperty(exports, "paginate", { enumerable: true, get: function () { return pagination_helpers_1.paginate; } });
|
|
14
16
|
Object.defineProperty(exports, "paginateIterator", { enumerable: true, get: function () { return pagination_helpers_1.paginateIterator; } });
|
|
15
17
|
Object.defineProperty(exports, "paginateWithMetadata", { enumerable: true, get: function () { return pagination_helpers_1.paginateWithMetadata; } });
|
|
@@ -22,3 +24,5 @@ Object.defineProperty(exports, "RichTextBuilder", { enumerable: true, get: funct
|
|
|
22
24
|
Object.defineProperty(exports, "richText", { enumerable: true, get: function () { return richText_helpers_1.richText; } });
|
|
23
25
|
var sort_helpers_1 = require("./sort.helpers");
|
|
24
26
|
Object.defineProperty(exports, "sort", { enumerable: true, get: function () { return sort_helpers_1.sort; } });
|
|
27
|
+
var webhook_helpers_1 = require("./webhook.helpers");
|
|
28
|
+
Object.defineProperty(exports, "webhook", { enumerable: true, get: function () { return webhook_helpers_1.webhook; } });
|
|
@@ -149,3 +149,59 @@ export declare function paginateWithMetadata<T>(fetchPage: PaginatedFetchFunctio
|
|
|
149
149
|
pageCount: number;
|
|
150
150
|
totalCount: number;
|
|
151
151
|
}>;
|
|
152
|
+
/**
|
|
153
|
+
* Fetch function for windowed row collection. Same as {@link PaginatedFetchFunction} plus
|
|
154
|
+
* a second parameter carrying the ISO 8601 `created_time` lower bound for the *next*
|
|
155
|
+
* window, present once the previous window was capped at the query result limit
|
|
156
|
+
* (`request_status.type === 'incomplete'`). The caller merges this into their own
|
|
157
|
+
* created_time-ascending query, e.g. `filter.createdTime('Created time').onOrAfter(createdTimeCursor)`.
|
|
158
|
+
*/
|
|
159
|
+
export type WindowedFetchFunction<T extends {
|
|
160
|
+
id: string;
|
|
161
|
+
createdTime: Date;
|
|
162
|
+
}> = (cursor: string | undefined, createdTimeCursor: string | undefined) => Promise<PaginatedList<T>>;
|
|
163
|
+
/**
|
|
164
|
+
* Iterates over every row of a data source query, transparently working around the
|
|
165
|
+
* API's 10,000-result-per-query cap.
|
|
166
|
+
*
|
|
167
|
+
* Data source (and view) queries cap at 10,000 results; when capped, the response's
|
|
168
|
+
* `has_more` is still `false`, so following `next_cursor`/`has_more` alone silently
|
|
169
|
+
* truncates the result set. When a window is capped (`request_status.type ===
|
|
170
|
+
* 'incomplete'`), this starts a new query filtered to `created_time >=` the last row
|
|
171
|
+
* seen, de-duplicating by `id` across the window boundary. Requires the query to be
|
|
172
|
+
* sorted by `created_time` ascending.
|
|
173
|
+
*
|
|
174
|
+
* @param fetchWindow - Function that fetches one page, given the current cursor and
|
|
175
|
+
* (once a window has been capped) a `created_time` lower bound for the next window
|
|
176
|
+
* @yields Individual rows across all windows
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```typescript
|
|
180
|
+
* const rows = collectAllDataSourceRows((cursor, createdTimeCursor) =>
|
|
181
|
+
* notion.dataSources.query(dataSourceId, {
|
|
182
|
+
* start_cursor: cursor,
|
|
183
|
+
* sorts: [{ timestamp: 'created_time', direction: 'ascending' }],
|
|
184
|
+
* filter: createdTimeCursor
|
|
185
|
+
* ? filter.and(baseFilter, filter.createdTime('Created time').onOrAfter(createdTimeCursor))
|
|
186
|
+
* : baseFilter,
|
|
187
|
+
* }),
|
|
188
|
+
* );
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
export declare function iterateAllDataSourceRows<T extends {
|
|
192
|
+
id: string;
|
|
193
|
+
createdTime: Date;
|
|
194
|
+
}>(fetchWindow: WindowedFetchFunction<T>): AsyncGenerator<T, void, undefined>;
|
|
195
|
+
/**
|
|
196
|
+
* Collects every row of a data source query into an array, transparently working
|
|
197
|
+
* around the API's 10,000-result-per-query cap. See {@link iterateAllDataSourceRows}
|
|
198
|
+
* for the windowing behavior.
|
|
199
|
+
*
|
|
200
|
+
* @param fetchWindow - Function that fetches one page, given the current cursor and
|
|
201
|
+
* (once a window has been capped) a `created_time` lower bound for the next window
|
|
202
|
+
* @returns Array containing all rows across all windows
|
|
203
|
+
*/
|
|
204
|
+
export declare function collectAllDataSourceRows<T extends {
|
|
205
|
+
id: string;
|
|
206
|
+
createdTime: Date;
|
|
207
|
+
}>(fetchWindow: WindowedFetchFunction<T>): Promise<T[]>;
|
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.paginate = paginate;
|
|
4
4
|
exports.paginateIterator = paginateIterator;
|
|
5
5
|
exports.paginateWithMetadata = paginateWithMetadata;
|
|
6
|
+
exports.iterateAllDataSourceRows = iterateAllDataSourceRows;
|
|
7
|
+
exports.collectAllDataSourceRows = collectAllDataSourceRows;
|
|
6
8
|
/**
|
|
7
9
|
* Collects all results from a paginated endpoint by automatically following cursors.
|
|
8
10
|
*
|
|
@@ -143,3 +145,87 @@ async function paginateWithMetadata(fetchPage) {
|
|
|
143
145
|
totalCount: items.length,
|
|
144
146
|
};
|
|
145
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Iterates over every row of a data source query, transparently working around the
|
|
150
|
+
* API's 10,000-result-per-query cap.
|
|
151
|
+
*
|
|
152
|
+
* Data source (and view) queries cap at 10,000 results; when capped, the response's
|
|
153
|
+
* `has_more` is still `false`, so following `next_cursor`/`has_more` alone silently
|
|
154
|
+
* truncates the result set. When a window is capped (`request_status.type ===
|
|
155
|
+
* 'incomplete'`), this starts a new query filtered to `created_time >=` the last row
|
|
156
|
+
* seen, de-duplicating by `id` across the window boundary. Requires the query to be
|
|
157
|
+
* sorted by `created_time` ascending.
|
|
158
|
+
*
|
|
159
|
+
* @param fetchWindow - Function that fetches one page, given the current cursor and
|
|
160
|
+
* (once a window has been capped) a `created_time` lower bound for the next window
|
|
161
|
+
* @yields Individual rows across all windows
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```typescript
|
|
165
|
+
* const rows = collectAllDataSourceRows((cursor, createdTimeCursor) =>
|
|
166
|
+
* notion.dataSources.query(dataSourceId, {
|
|
167
|
+
* start_cursor: cursor,
|
|
168
|
+
* sorts: [{ timestamp: 'created_time', direction: 'ascending' }],
|
|
169
|
+
* filter: createdTimeCursor
|
|
170
|
+
* ? filter.and(baseFilter, filter.createdTime('Created time').onOrAfter(createdTimeCursor))
|
|
171
|
+
* : baseFilter,
|
|
172
|
+
* }),
|
|
173
|
+
* );
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
async function* iterateAllDataSourceRows(fetchWindow) {
|
|
177
|
+
const seenIds = new Set();
|
|
178
|
+
let cursor;
|
|
179
|
+
let createdTimeCursor;
|
|
180
|
+
let lastCreatedTime;
|
|
181
|
+
let hasMore = true;
|
|
182
|
+
while (hasMore) {
|
|
183
|
+
const response = await fetchWindow(cursor, createdTimeCursor);
|
|
184
|
+
let yieldedThisWindow = false;
|
|
185
|
+
for (const item of response.results) {
|
|
186
|
+
if (seenIds.has(item.id)) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
seenIds.add(item.id);
|
|
190
|
+
lastCreatedTime = item.createdTime;
|
|
191
|
+
yieldedThisWindow = true;
|
|
192
|
+
yield item;
|
|
193
|
+
}
|
|
194
|
+
if (response.request_status?.type === 'incomplete' && lastCreatedTime) {
|
|
195
|
+
// has_more is false here, so normal cursor pagination would silently stop short.
|
|
196
|
+
// Start a fresh window scoped to created_time >= the last row seen, de-duping by
|
|
197
|
+
// id across the boundary (created_time, not last_edited_time -- the latter shifts
|
|
198
|
+
// rows between windows).
|
|
199
|
+
if (!yieldedThisWindow) {
|
|
200
|
+
// Every row in this capped window was already seen, meaning more rows share
|
|
201
|
+
// this exact created_time than fit in one window -- the created_time >= filter
|
|
202
|
+
// can't disambiguate further, so re-querying would return the same page forever.
|
|
203
|
+
throw new Error(`More than one window's worth of rows share the same created_time (${lastCreatedTime.toISOString()}); ` +
|
|
204
|
+
'iterateAllDataSourceRows cannot make forward progress past this timestamp.');
|
|
205
|
+
}
|
|
206
|
+
cursor = undefined;
|
|
207
|
+
createdTimeCursor = lastCreatedTime.toISOString();
|
|
208
|
+
hasMore = true;
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
cursor = response.next_cursor ?? undefined;
|
|
212
|
+
hasMore = cursor !== undefined;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Collects every row of a data source query into an array, transparently working
|
|
218
|
+
* around the API's 10,000-result-per-query cap. See {@link iterateAllDataSourceRows}
|
|
219
|
+
* for the windowing behavior.
|
|
220
|
+
*
|
|
221
|
+
* @param fetchWindow - Function that fetches one page, given the current cursor and
|
|
222
|
+
* (once a window has been capped) a `created_time` lower bound for the next window
|
|
223
|
+
* @returns Array containing all rows across all windows
|
|
224
|
+
*/
|
|
225
|
+
async function collectAllDataSourceRows(fetchWindow) {
|
|
226
|
+
const all = [];
|
|
227
|
+
for await (const item of iterateAllDataSourceRows(fetchWindow)) {
|
|
228
|
+
all.push(item);
|
|
229
|
+
}
|
|
230
|
+
return all;
|
|
231
|
+
}
|
|
@@ -181,6 +181,34 @@ declare function people(userIds: string[]): {
|
|
|
181
181
|
id: string;
|
|
182
182
|
}>;
|
|
183
183
|
};
|
|
184
|
+
/** Options for the verification property's optional date. */
|
|
185
|
+
interface VerificationDateOptions {
|
|
186
|
+
start: string;
|
|
187
|
+
end?: string | null;
|
|
188
|
+
timeZone?: string | null;
|
|
189
|
+
}
|
|
190
|
+
/** The shape of a verification property value. */
|
|
191
|
+
interface VerificationValue {
|
|
192
|
+
verification: {
|
|
193
|
+
state: 'verified' | 'unverified';
|
|
194
|
+
date?: {
|
|
195
|
+
start: string;
|
|
196
|
+
end: string | null;
|
|
197
|
+
time_zone: string | null;
|
|
198
|
+
} | null;
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Create a verification property value.
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```ts
|
|
206
|
+
* prop.verification('verified')
|
|
207
|
+
* prop.verification('verified', { start: '2025-01-15' })
|
|
208
|
+
* prop.verification('unverified')
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
declare function verification(state: 'verified' | 'unverified', date?: VerificationDateOptions): VerificationValue;
|
|
184
212
|
/** A single file entry for the files' property. */
|
|
185
213
|
interface FileEntry {
|
|
186
214
|
name: string;
|
|
@@ -243,5 +271,6 @@ export declare const prop: {
|
|
|
243
271
|
relation: typeof relation;
|
|
244
272
|
people: typeof people;
|
|
245
273
|
files: typeof files;
|
|
274
|
+
verification: typeof verification;
|
|
246
275
|
};
|
|
247
276
|
export {};
|
|
@@ -210,6 +210,32 @@ function people(userIds) {
|
|
|
210
210
|
(0, validation_1.validateArrayLength)(userIds, validation_1.LIMITS.PEOPLE, 'People');
|
|
211
211
|
return { people: userIds.map((id) => ({ object: 'user', id })) };
|
|
212
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Create a verification property value.
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```ts
|
|
218
|
+
* prop.verification('verified')
|
|
219
|
+
* prop.verification('verified', { start: '2025-01-15' })
|
|
220
|
+
* prop.verification('unverified')
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
function verification(state, date) {
|
|
224
|
+
return {
|
|
225
|
+
verification: {
|
|
226
|
+
state,
|
|
227
|
+
...(date
|
|
228
|
+
? {
|
|
229
|
+
date: {
|
|
230
|
+
start: date.start,
|
|
231
|
+
end: date.end ?? null,
|
|
232
|
+
time_zone: date.timeZone ?? null,
|
|
233
|
+
},
|
|
234
|
+
}
|
|
235
|
+
: {}),
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
}
|
|
213
239
|
/**
|
|
214
240
|
* Create a files property value.
|
|
215
241
|
*
|
|
@@ -268,4 +294,5 @@ exports.prop = {
|
|
|
268
294
|
relation,
|
|
269
295
|
people,
|
|
270
296
|
files,
|
|
297
|
+
verification,
|
|
271
298
|
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sign a payload, for generating test webhook signatures.
|
|
3
|
+
*
|
|
4
|
+
* @param body - The payload to sign. A string is signed as-is; anything else is
|
|
5
|
+
* `JSON.stringify`'d first -- safe here because the caller controls the exact
|
|
6
|
+
* serialization of a payload they're constructing themselves.
|
|
7
|
+
* @param verificationToken - The webhook subscription's verification token
|
|
8
|
+
* @returns The `sha256=<hex digest>` signature
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const signature = webhook.sign({ event: 'page.updated' }, verificationToken);
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
declare function sign(body: unknown, verificationToken: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Verify an incoming webhook's `X-Notion-Signature` header via constant-time comparison.
|
|
18
|
+
*
|
|
19
|
+
* IMPORTANT: `rawBody` must be the exact raw request body Notion sent, not
|
|
20
|
+
* `JSON.stringify(parsedBody)` -- re-serializing a parsed object can produce a
|
|
21
|
+
* different byte sequence (key order, whitespace) than what was actually signed,
|
|
22
|
+
* silently breaking verification. Use your framework's raw-body access (e.g.
|
|
23
|
+
* Express's `express.raw()` / `req.rawBody`), not `req.body` after JSON middleware
|
|
24
|
+
* has already parsed it.
|
|
25
|
+
*
|
|
26
|
+
* @param rawBody - The exact raw request body string Notion sent
|
|
27
|
+
* @param signatureHeader - The value of the `X-Notion-Signature` request header
|
|
28
|
+
* @param verificationToken - The webhook subscription's verification token
|
|
29
|
+
* @returns `true` if the signature is valid; `false` for any mismatch or malformed
|
|
30
|
+
* input (never throws)
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* const isValid = webhook.verifySignature(rawBody, req.headers['x-notion-signature'], verificationToken);
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare function verifySignature(rawBody: string, signatureHeader: string | null | undefined, verificationToken: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Helpers for signing and verifying Notion webhook payloads.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* import { webhook } from '@visus-io/notion-sdk-ts';
|
|
44
|
+
*
|
|
45
|
+
* const isValid = webhook.verifySignature(rawBody, req.headers['x-notion-signature'], verificationToken);
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare const webhook: {
|
|
49
|
+
sign: typeof sign;
|
|
50
|
+
verifySignature: typeof verifySignature;
|
|
51
|
+
};
|
|
52
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.webhook = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
/**
|
|
6
|
+
* Webhook signature helpers.
|
|
7
|
+
*
|
|
8
|
+
* Notion signs webhook payloads with HMAC-SHA256, keyed by the subscription's
|
|
9
|
+
* verification token, over the raw JSON request body. The signature is sent in the
|
|
10
|
+
* `X-Notion-Signature` header as `sha256=<hex digest>`.
|
|
11
|
+
*
|
|
12
|
+
* Notion API reference:
|
|
13
|
+
* https://developers.notion.com/reference/webhooks
|
|
14
|
+
*/
|
|
15
|
+
function computeSignature(rawBody, verificationToken) {
|
|
16
|
+
return `sha256=${(0, node_crypto_1.createHmac)('sha256', verificationToken).update(rawBody).digest('hex')}`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Sign a payload, for generating test webhook signatures.
|
|
20
|
+
*
|
|
21
|
+
* @param body - The payload to sign. A string is signed as-is; anything else is
|
|
22
|
+
* `JSON.stringify`'d first -- safe here because the caller controls the exact
|
|
23
|
+
* serialization of a payload they're constructing themselves.
|
|
24
|
+
* @param verificationToken - The webhook subscription's verification token
|
|
25
|
+
* @returns The `sha256=<hex digest>` signature
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const signature = webhook.sign({ event: 'page.updated' }, verificationToken);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
function sign(body, verificationToken) {
|
|
33
|
+
return computeSignature(typeof body === 'string' ? body : JSON.stringify(body), verificationToken);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Verify an incoming webhook's `X-Notion-Signature` header via constant-time comparison.
|
|
37
|
+
*
|
|
38
|
+
* IMPORTANT: `rawBody` must be the exact raw request body Notion sent, not
|
|
39
|
+
* `JSON.stringify(parsedBody)` -- re-serializing a parsed object can produce a
|
|
40
|
+
* different byte sequence (key order, whitespace) than what was actually signed,
|
|
41
|
+
* silently breaking verification. Use your framework's raw-body access (e.g.
|
|
42
|
+
* Express's `express.raw()` / `req.rawBody`), not `req.body` after JSON middleware
|
|
43
|
+
* has already parsed it.
|
|
44
|
+
*
|
|
45
|
+
* @param rawBody - The exact raw request body string Notion sent
|
|
46
|
+
* @param signatureHeader - The value of the `X-Notion-Signature` request header
|
|
47
|
+
* @param verificationToken - The webhook subscription's verification token
|
|
48
|
+
* @returns `true` if the signature is valid; `false` for any mismatch or malformed
|
|
49
|
+
* input (never throws)
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* const isValid = webhook.verifySignature(rawBody, req.headers['x-notion-signature'], verificationToken);
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
function verifySignature(rawBody, signatureHeader, verificationToken) {
|
|
57
|
+
if (!signatureHeader) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
const expected = Buffer.from(computeSignature(rawBody, verificationToken));
|
|
61
|
+
const actual = Buffer.from(signatureHeader);
|
|
62
|
+
// timingSafeEqual throws on mismatched buffer lengths, so guard explicitly.
|
|
63
|
+
if (expected.length !== actual.length) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return (0, node_crypto_1.timingSafeEqual)(expected, actual);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Helpers for signing and verifying Notion webhook payloads.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* import { webhook } from '@visus-io/notion-sdk-ts';
|
|
74
|
+
*
|
|
75
|
+
* const isValid = webhook.verifySignature(rawBody, req.headers['x-notion-signature'], verificationToken);
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
exports.webhook = {
|
|
79
|
+
sign,
|
|
80
|
+
verifySignature,
|
|
81
|
+
};
|