@notionhq/apps 0.0.15 → 0.0.17
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/AGENTS.md +13 -0
- package/README.md +69 -2
- package/dist/connections.d.ts +39 -11
- package/dist/connections.d.ts.map +1 -1
- package/dist/connections.js +85 -21
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/notion-as-code/database.d.ts +26 -3
- package/dist/notion-as-code/database.d.ts.map +1 -1
- package/dist/notion-as-code/database.js +27 -0
- package/dist/notion-as-code/handles.d.ts +2 -0
- package/dist/notion-as-code/handles.d.ts.map +1 -1
- package/dist/notion-as-code/handles.js +2 -0
- package/dist/notion-as-code/index.d.ts +4 -1
- package/dist/notion-as-code/index.d.ts.map +1 -1
- package/dist/notion-as-code/intents.d.ts +2 -1
- package/dist/notion-as-code/intents.d.ts.map +1 -1
- package/dist/notion-as-code/page.d.ts +3 -3
- package/dist/notion-as-code/page.d.ts.map +1 -1
- package/dist/notion-as-code/page.js +2 -7
- package/dist/notion-as-code/teamspace.d.ts +3 -3
- package/dist/notion-as-code/teamspace.d.ts.map +1 -1
- package/dist/notion-as-code/teamspace.js +2 -7
- package/dist/notion-as-code/view.d.ts +12 -0
- package/dist/notion-as-code/view.d.ts.map +1 -0
- package/dist/notion-as-code/view.js +13 -0
- package/dist/notion-as-code/views-types.test.d.ts +2 -0
- package/dist/notion-as-code/views-types.test.d.ts.map +1 -0
- package/dist/notion-as-code/views.d.ts +469 -0
- package/dist/notion-as-code/views.d.ts.map +1 -0
- package/dist/notion-as-code/views.js +0 -0
- package/dist/oauth.d.ts +11 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +26 -0
- package/dist/providers.generated.d.ts +26 -86
- package/dist/providers.generated.d.ts.map +1 -1
- package/dist/providers.generated.js +54 -66
- package/dist/triggers.generated.d.ts +3 -3
- package/dist/triggers.generated.d.ts.map +1 -1
- package/dist/workflow.d.ts +8 -8
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +12 -6
- package/docs/CONNECTIONS.md +70 -22
- package/package.json +3 -1
- package/skills/connections/SKILL.md +85 -0
- package/skills/custom-blocks/SKILL.md +45 -0
- package/skills/notion-as-code/SKILL.md +195 -0
- package/skills/sync/SKILL.md +98 -0
- package/skills/workflow/SKILL.md +123 -0
- package/src/cli/build.test.ts +95 -3
- package/src/connections.test.ts +205 -41
- package/src/connections.ts +144 -38
- package/src/index.ts +1 -0
- package/src/notion-as-code/database.ts +75 -5
- package/src/notion-as-code/handles.ts +2 -0
- package/src/notion-as-code/index.ts +59 -0
- package/src/notion-as-code/intents.ts +2 -2
- package/src/notion-as-code/page.ts +3 -11
- package/src/notion-as-code/teamspace.ts +3 -11
- package/src/notion-as-code/view.ts +23 -0
- package/src/notion-as-code/views-types.test.ts +59 -0
- package/src/notion-as-code/views.ts +554 -0
- package/src/oauth.ts +40 -0
- package/src/providers.generated.ts +68 -163
- package/src/triggers.generated.ts +4 -4
- package/src/workflow-connections-types.test.ts +16 -10
- package/src/workflow-types.test.ts +88 -16
- package/src/workflow.test.ts +45 -17
- package/src/workflow.ts +31 -17
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
import type { NotionAsCodeIcon, NotionAsCodeProperty, ResourceId } from "./intents.js";
|
|
2
|
+
|
|
3
|
+
// Ported from notion-next/src/server/helpers/infraAsCode/types.ts.
|
|
4
|
+
export type ViewType = "table" | "board" | "calendar" | "list" | "gallery" | "feed" | "timeline";
|
|
5
|
+
|
|
6
|
+
export type PropertyVisibility = "show" | "hide" | "hide_if_empty";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Format for a single visible property entry in a view (table / board / list /
|
|
10
|
+
* gallery / feed / timeline).
|
|
11
|
+
*
|
|
12
|
+
* `property` must be the `resourceId` of a property in the view's data
|
|
13
|
+
* source, matching the same convention used by board `groupBy.property`,
|
|
14
|
+
* `calendarBy`, and `timelineBy`.
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
export type PropertyFormat = {
|
|
18
|
+
/** ResourceId of the property in the view's data source. */
|
|
19
|
+
property: ResourceId;
|
|
20
|
+
visible?: boolean;
|
|
21
|
+
width?: number;
|
|
22
|
+
visibility?: PropertyVisibility;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Format for a single column entry in a board view.
|
|
27
|
+
*
|
|
28
|
+
* `property` must be the `resourceId` of a property in the view's data
|
|
29
|
+
* source, matching the same convention used by `PropertyFormat.property`
|
|
30
|
+
* and board `groupBy.property`.
|
|
31
|
+
*
|
|
32
|
+
*/
|
|
33
|
+
export type GroupFormat = {
|
|
34
|
+
/** ResourceId of the property in the view's data source. */
|
|
35
|
+
property: ResourceId;
|
|
36
|
+
hidden?: boolean;
|
|
37
|
+
value?: {
|
|
38
|
+
type: string;
|
|
39
|
+
value?: string | boolean | number | null;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type GroupByFormatBase = {
|
|
44
|
+
/** ResourceId of the property in the view's data source. */
|
|
45
|
+
property: ResourceId;
|
|
46
|
+
/** Whether groups with no pages are visible. Defaults to "show". */
|
|
47
|
+
emptyGroupVisibility?: "show" | "hide";
|
|
48
|
+
statusBy?: never;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type SelectGroupByFormat = GroupByFormatBase & {
|
|
52
|
+
type: "select" | "multi_select";
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type StatusGroupByFormat = Omit<GroupByFormatBase, "statusBy"> & {
|
|
56
|
+
type: "status";
|
|
57
|
+
/** Groups status values by their canonical status group or individual option. */
|
|
58
|
+
statusBy?: "group" | "option";
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type PersonGroupByFormat = GroupByFormatBase & {
|
|
62
|
+
type: "person" | "created_by" | "last_edited_by";
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export type DateGroupByFormat = GroupByFormatBase & {
|
|
66
|
+
type: "date" | "created_time" | "last_edited_time" | "last_visited_time";
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export type TextGroupByFormat = GroupByFormatBase & {
|
|
70
|
+
type: "text" | "title" | "url" | "email" | "phone_number";
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export type NumberGroupByFormat = GroupByFormatBase & {
|
|
74
|
+
type: "number";
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export type CheckboxGroupByFormat = GroupByFormatBase & {
|
|
78
|
+
type: "checkbox";
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type RelationGroupByFormat = GroupByFormatBase & {
|
|
82
|
+
type: "relation";
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export type LocationGroupByFormat = GroupByFormatBase & {
|
|
86
|
+
type: "location";
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export type FormulaGroupByFormat = GroupByFormatBase & {
|
|
90
|
+
type: "formula";
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Board view group-by configuration.
|
|
95
|
+
*
|
|
96
|
+
* `property` must be the `resourceId` of a property in the view's data
|
|
97
|
+
* source, matching the same convention used by `PropertyFormat.property`,
|
|
98
|
+
* `CalendarViewSchema.calendarBy`, and `TimelineViewSchema.timelineBy`.
|
|
99
|
+
*
|
|
100
|
+
*/
|
|
101
|
+
export type GroupByFormat =
|
|
102
|
+
| SelectGroupByFormat
|
|
103
|
+
| StatusGroupByFormat
|
|
104
|
+
| PersonGroupByFormat
|
|
105
|
+
| DateGroupByFormat
|
|
106
|
+
| TextGroupByFormat
|
|
107
|
+
| NumberGroupByFormat
|
|
108
|
+
| CheckboxGroupByFormat
|
|
109
|
+
| RelationGroupByFormat
|
|
110
|
+
| LocationGroupByFormat
|
|
111
|
+
| FormulaGroupByFormat;
|
|
112
|
+
|
|
113
|
+
export type CoverFormat =
|
|
114
|
+
| { type: "page_cover" }
|
|
115
|
+
| { type: "page_content" }
|
|
116
|
+
| { type: "page_content_first" }
|
|
117
|
+
| { type: "property"; property: string };
|
|
118
|
+
|
|
119
|
+
export type CoverSizeFormat = "small" | "medium" | "large";
|
|
120
|
+
|
|
121
|
+
export type CoverAspectFormat = "contain" | "cover";
|
|
122
|
+
|
|
123
|
+
export type DatabaseViewSortDirection = "ascending" | "descending";
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Sort schema for ordering database view results.
|
|
127
|
+
* Sorts are applied in order — all pages are first sorted by the first sort,
|
|
128
|
+
* then ties are broken by the second sort, and so on.
|
|
129
|
+
*/
|
|
130
|
+
export type PropertyViewSortSchema = {
|
|
131
|
+
propertyId: string;
|
|
132
|
+
direction: DatabaseViewSortDirection;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
type PropertyType = NotionAsCodeProperty["type"];
|
|
136
|
+
|
|
137
|
+
export type DatePropertyTypes = "date" | "created_time" | "last_edited_time";
|
|
138
|
+
|
|
139
|
+
export type BasePropertyFilter = {
|
|
140
|
+
propertyId: string;
|
|
141
|
+
type: "property";
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export type TextPropertyFilter = {
|
|
145
|
+
propertyType: Extract<PropertyType, "text" | "title" | "url" | "email" | "phone_number">;
|
|
146
|
+
operator:
|
|
147
|
+
| "string_is"
|
|
148
|
+
| "string_is_not"
|
|
149
|
+
| "string_contains"
|
|
150
|
+
| "string_does_not_contain"
|
|
151
|
+
| "string_starts_with"
|
|
152
|
+
| "string_ends_with";
|
|
153
|
+
value: string;
|
|
154
|
+
} & BasePropertyFilter;
|
|
155
|
+
|
|
156
|
+
export type NumberPropertyFilter = {
|
|
157
|
+
propertyType: Extract<PropertyType, "number">;
|
|
158
|
+
operator:
|
|
159
|
+
| "number_equals"
|
|
160
|
+
| "number_does_not_equal"
|
|
161
|
+
| "number_greater_than"
|
|
162
|
+
| "number_less_than"
|
|
163
|
+
| "number_greater_than_or_equal_to"
|
|
164
|
+
| "number_less_than_or_equal_to";
|
|
165
|
+
value: number;
|
|
166
|
+
} & BasePropertyFilter;
|
|
167
|
+
|
|
168
|
+
export type CheckboxPropertyFilter = {
|
|
169
|
+
propertyType: Extract<PropertyType, "checkbox">;
|
|
170
|
+
operator: "checkbox_is" | "checkbox_is_not";
|
|
171
|
+
value: boolean;
|
|
172
|
+
} & BasePropertyFilter;
|
|
173
|
+
|
|
174
|
+
export type SelectPropertyFilter = {
|
|
175
|
+
propertyType: Extract<PropertyType, "select">;
|
|
176
|
+
operator: "enum_is" | "enum_is_not";
|
|
177
|
+
value: Array<string>;
|
|
178
|
+
} & BasePropertyFilter;
|
|
179
|
+
|
|
180
|
+
export type MultiSelectPropertyFilter = {
|
|
181
|
+
propertyType: Extract<PropertyType, "multi_select">;
|
|
182
|
+
operator: "enum_contains" | "enum_does_not_contain" | "enum_contains_all";
|
|
183
|
+
value: Array<string>;
|
|
184
|
+
} & BasePropertyFilter;
|
|
185
|
+
|
|
186
|
+
export type StatusPropertyFilter = {
|
|
187
|
+
propertyType: Extract<PropertyType, "status">;
|
|
188
|
+
operator: "status_is" | "status_is_not";
|
|
189
|
+
value: Array<string>;
|
|
190
|
+
} & BasePropertyFilter;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Date presets resolved relative to today, matching options such as
|
|
194
|
+
* "Today", "Tomorrow", and "One week ago".
|
|
195
|
+
*/
|
|
196
|
+
export type RelativeDatePreset =
|
|
197
|
+
| "today"
|
|
198
|
+
| "tomorrow"
|
|
199
|
+
| "yesterday"
|
|
200
|
+
| "one_week_ago"
|
|
201
|
+
| "one_week_from_now"
|
|
202
|
+
| "one_month_ago"
|
|
203
|
+
| "one_month_from_now";
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* The unit spanned by a relative "is relative to today" date range.
|
|
207
|
+
*/
|
|
208
|
+
export type RelativeDateRangeUnit = "day" | "week" | "month" | "year";
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Which date a date property filter compares against. Notion date properties
|
|
212
|
+
* can hold a range, and `end_date` reads the far end of it.
|
|
213
|
+
*/
|
|
214
|
+
export type DateFilterMode = "start_date" | "end_date";
|
|
215
|
+
|
|
216
|
+
export type ExactDatePropertyFilterValue = { type: "exact"; value: string };
|
|
217
|
+
|
|
218
|
+
export type RelativeDatePropertyFilterValue = {
|
|
219
|
+
type: "relative";
|
|
220
|
+
value: RelativeDatePreset;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export type RelativeToTodayDatePropertyFilterValue = {
|
|
224
|
+
type: "relative_to_today";
|
|
225
|
+
value: {
|
|
226
|
+
direction: "past" | "next" | "this";
|
|
227
|
+
count?: number;
|
|
228
|
+
unit: RelativeDateRangeUnit;
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Value for the `date_is_within` operator, shown as "Is between" in the UI.
|
|
234
|
+
*
|
|
235
|
+
* Bounds are inclusive `YYYY-MM-DD` dates; supplying only one leaves the range
|
|
236
|
+
* open at the other end.
|
|
237
|
+
*
|
|
238
|
+
*/
|
|
239
|
+
export type ExactRangeDatePropertyFilterValue = {
|
|
240
|
+
type: "exact_range";
|
|
241
|
+
value: { startDate: string; endDate?: string } | { startDate?: string; endDate: string };
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Value for a date property filter.
|
|
246
|
+
*
|
|
247
|
+
* - `exact` matches a calendar date in `YYYY-MM-DD` format.
|
|
248
|
+
* - `relative` matches a date preset such as today or tomorrow.
|
|
249
|
+
* - `relative_to_today` matches a range relative to today. `unit` can be
|
|
250
|
+
* `day`, `week`, `month`, or `year`. `count` defaults to `1` when omitted
|
|
251
|
+
* and is unnecessary for `direction: "this"`.
|
|
252
|
+
* - `exact_range` matches a fixed calendar range, optionally open at one end.
|
|
253
|
+
*
|
|
254
|
+
*/
|
|
255
|
+
export type DatePropertyFilterValue =
|
|
256
|
+
| ExactDatePropertyFilterValue
|
|
257
|
+
| RelativeDatePropertyFilterValue
|
|
258
|
+
| RelativeToTodayDatePropertyFilterValue
|
|
259
|
+
| ExactRangeDatePropertyFilterValue;
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Date property filter.
|
|
263
|
+
*
|
|
264
|
+
* Single-date operators accept `exact` or `relative` values.
|
|
265
|
+
* `date_is_relative_to` requires a `relative_to_today` value.
|
|
266
|
+
* `date_is_within` requires an `exact_range` value.
|
|
267
|
+
*
|
|
268
|
+
* `dateFilterMode` chooses which end of a date range the comparison reads.
|
|
269
|
+
* It defaults to `"start_date"`. `"end_date"` falls back to the start date for
|
|
270
|
+
* rows whose date is a single day rather than a range, and has no effect on
|
|
271
|
+
* `created_time` / `last_edited_time` properties, which are never ranges.
|
|
272
|
+
*
|
|
273
|
+
* @example Exact date
|
|
274
|
+
* {
|
|
275
|
+
* type: "property",
|
|
276
|
+
* propertyId: "due",
|
|
277
|
+
* propertyType: "date",
|
|
278
|
+
* operator: "date_is_on_or_after",
|
|
279
|
+
* value: { type: "exact", value: "2025-10-03" }
|
|
280
|
+
* }
|
|
281
|
+
*
|
|
282
|
+
* @example Relative date preset
|
|
283
|
+
* {
|
|
284
|
+
* type: "property",
|
|
285
|
+
* propertyId: "due",
|
|
286
|
+
* propertyType: "date",
|
|
287
|
+
* operator: "date_is",
|
|
288
|
+
* value: { type: "relative", value: "today" }
|
|
289
|
+
* }
|
|
290
|
+
*
|
|
291
|
+
* @example Next two months
|
|
292
|
+
* {
|
|
293
|
+
* type: "property",
|
|
294
|
+
* propertyId: "due",
|
|
295
|
+
* propertyType: "date",
|
|
296
|
+
* operator: "date_is_relative_to",
|
|
297
|
+
* value: { type: "relative_to_today", value: { direction: "next", count: 2, unit: "month" } }
|
|
298
|
+
* }
|
|
299
|
+
*
|
|
300
|
+
* @example Sprints that end this week
|
|
301
|
+
* {
|
|
302
|
+
* type: "property",
|
|
303
|
+
* propertyId: "sprint",
|
|
304
|
+
* propertyType: "date",
|
|
305
|
+
* operator: "date_is_relative_to",
|
|
306
|
+
* value: { type: "relative_to_today", value: { direction: "this", unit: "week" } },
|
|
307
|
+
* dateFilterMode: "end_date"
|
|
308
|
+
* }
|
|
309
|
+
*
|
|
310
|
+
* @example Fixed calendar range
|
|
311
|
+
* {
|
|
312
|
+
* type: "property",
|
|
313
|
+
* propertyId: "due",
|
|
314
|
+
* propertyType: "date",
|
|
315
|
+
* operator: "date_is_within",
|
|
316
|
+
* value: { type: "exact_range", value: { startDate: "2026-01-01", endDate: "2026-03-31" } }
|
|
317
|
+
* }
|
|
318
|
+
*
|
|
319
|
+
* @example Range left open at the end
|
|
320
|
+
* {
|
|
321
|
+
* type: "property",
|
|
322
|
+
* propertyId: "due",
|
|
323
|
+
* propertyType: "date",
|
|
324
|
+
* operator: "date_is_within",
|
|
325
|
+
* value: { type: "exact_range", value: { startDate: "2026-01-01" } }
|
|
326
|
+
* }
|
|
327
|
+
*
|
|
328
|
+
*/
|
|
329
|
+
export type DatePropertyFilter =
|
|
330
|
+
| ({
|
|
331
|
+
propertyType: DatePropertyTypes;
|
|
332
|
+
operator:
|
|
333
|
+
| "date_is"
|
|
334
|
+
| "date_is_before"
|
|
335
|
+
| "date_is_after"
|
|
336
|
+
| "date_is_on_or_before"
|
|
337
|
+
| "date_is_on_or_after";
|
|
338
|
+
value: ExactDatePropertyFilterValue | RelativeDatePropertyFilterValue;
|
|
339
|
+
dateFilterMode?: DateFilterMode;
|
|
340
|
+
} & BasePropertyFilter)
|
|
341
|
+
| ({
|
|
342
|
+
propertyType: DatePropertyTypes;
|
|
343
|
+
operator: "date_is_relative_to";
|
|
344
|
+
value: RelativeToTodayDatePropertyFilterValue;
|
|
345
|
+
dateFilterMode?: DateFilterMode;
|
|
346
|
+
} & BasePropertyFilter)
|
|
347
|
+
| ({
|
|
348
|
+
propertyType: DatePropertyTypes;
|
|
349
|
+
operator: "date_is_within";
|
|
350
|
+
value: ExactRangeDatePropertyFilterValue;
|
|
351
|
+
dateFilterMode?: DateFilterMode;
|
|
352
|
+
} & BasePropertyFilter);
|
|
353
|
+
|
|
354
|
+
export type RelationPropertyFilter = {
|
|
355
|
+
propertyType: Extract<PropertyType, "relation">;
|
|
356
|
+
operator: "relation_contains" | "relation_does_not_contain";
|
|
357
|
+
/** References related pages, or the current page when rendered as a page-layout tab. */
|
|
358
|
+
value: { type: "exact"; value: Array<ResourceId> } | { type: "relative"; value: "this_page" };
|
|
359
|
+
} & BasePropertyFilter;
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Person property filter — matches rows whose person property contains (or
|
|
363
|
+
* does not contain) the viewer (the `"me"` template variable, resolved at
|
|
364
|
+
* read time).
|
|
365
|
+
*/
|
|
366
|
+
export type PersonPropertyFilter = {
|
|
367
|
+
propertyType: Extract<PropertyType, "person" | "created_by" | "last_edited_by">;
|
|
368
|
+
operator: "person_contains" | "person_does_not_contain";
|
|
369
|
+
// TODO: Support more Person property types in the future
|
|
370
|
+
value: [{ type: "relative"; value: "me" }];
|
|
371
|
+
} & BasePropertyFilter;
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Filter schema for filtering database views by property values.
|
|
375
|
+
* Used in view definitions to specify which pages should be visible.
|
|
376
|
+
*
|
|
377
|
+
* Each filter targets a specific property by ID and applies a type-appropriate
|
|
378
|
+
* filter operator with a comparison value.
|
|
379
|
+
*
|
|
380
|
+
* @example Text property filter
|
|
381
|
+
* {
|
|
382
|
+
* propertyId: "name-prop",
|
|
383
|
+
* type: "property",
|
|
384
|
+
* propertyType: "text",
|
|
385
|
+
* operator: "string_contains",
|
|
386
|
+
* value: "Project"
|
|
387
|
+
* }
|
|
388
|
+
*/
|
|
389
|
+
export type PropertyFilterSchema =
|
|
390
|
+
| TextPropertyFilter
|
|
391
|
+
| NumberPropertyFilter
|
|
392
|
+
| CheckboxPropertyFilter
|
|
393
|
+
| SelectPropertyFilter
|
|
394
|
+
| MultiSelectPropertyFilter
|
|
395
|
+
| StatusPropertyFilter
|
|
396
|
+
| DatePropertyFilter
|
|
397
|
+
| RelationPropertyFilter
|
|
398
|
+
| PersonPropertyFilter;
|
|
399
|
+
|
|
400
|
+
export type FilterSchema = Array<PropertyFilterSchema | AdvancedFilterSchema>;
|
|
401
|
+
|
|
402
|
+
export type AdvancedFilterSchema = {
|
|
403
|
+
type: "advanced";
|
|
404
|
+
operator: "and" | "or";
|
|
405
|
+
filters: FilterSchema;
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Base view schema shared by all view types.
|
|
410
|
+
*
|
|
411
|
+
* `dataSourceResourceId` must reference a data source created by the script or
|
|
412
|
+
* supplied through existing resources.
|
|
413
|
+
*
|
|
414
|
+
* @example
|
|
415
|
+
* // When creating a database with a data source
|
|
416
|
+
* const db = await notion.database({
|
|
417
|
+
* resourceId: "my-database",
|
|
418
|
+
* dataSources: [{ resourceId: "my-datasource", name: "Main", properties: [...] }]
|
|
419
|
+
* })
|
|
420
|
+
*
|
|
421
|
+
* // Views must reference that data source
|
|
422
|
+
* await db.addView({
|
|
423
|
+
* resourceId: "my-table-view",
|
|
424
|
+
* type: "table",
|
|
425
|
+
* dataSourceResourceId: "my-datasource" // REQUIRED: matches data source above
|
|
426
|
+
* })
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* // A linked database can reference another script-created or existing data source
|
|
430
|
+
* const linkedDatabase = await notion.database({
|
|
431
|
+
* resourceId: "linked-database",
|
|
432
|
+
* parent: { type: "resourceId", resourceId: "project-page" },
|
|
433
|
+
* views: [{
|
|
434
|
+
* resourceId: "linked-table-view",
|
|
435
|
+
* type: "table",
|
|
436
|
+
* dataSourceResourceId: "my-datasource" // Created elsewhere or supplied through an existing collection
|
|
437
|
+
* }]
|
|
438
|
+
* })
|
|
439
|
+
*/
|
|
440
|
+
export type BaseViewSchema = {
|
|
441
|
+
resourceId: ResourceId;
|
|
442
|
+
name?: string;
|
|
443
|
+
/**
|
|
444
|
+
* Notion icon shown in the view tab, including tabs in database page layouts.
|
|
445
|
+
* View icons do not support emoji or uploaded files. Omit to use the default
|
|
446
|
+
* icon for the view type.
|
|
447
|
+
*/
|
|
448
|
+
icon?: Extract<NotionAsCodeIcon, { type: "notion_icon" }>;
|
|
449
|
+
type: ViewType;
|
|
450
|
+
/** Resource ID of a script-created or existing data source. */
|
|
451
|
+
dataSourceResourceId: ResourceId;
|
|
452
|
+
/**
|
|
453
|
+
* Optional resource ID of a template page in this view's data source to use as
|
|
454
|
+
* the view default template.
|
|
455
|
+
*
|
|
456
|
+
* The referenced page must be created with `template: true` and be parented to
|
|
457
|
+
* this same data source.
|
|
458
|
+
*/
|
|
459
|
+
defaultTemplate?: ResourceId;
|
|
460
|
+
/**
|
|
461
|
+
* Optional: create this view as a linked view referenced by `<database>` tags
|
|
462
|
+
* in page content markdown or use it as a tab in a database layout. It does
|
|
463
|
+
* not attach to the database block's main view tabs.
|
|
464
|
+
*/
|
|
465
|
+
ephemeral?: boolean;
|
|
466
|
+
sorts?: Array<PropertyViewSortSchema>;
|
|
467
|
+
/** Optional filter to control which pages appear in this view. */
|
|
468
|
+
filters?: FilterSchema;
|
|
469
|
+
/** Whether page icons are shown in this view. */
|
|
470
|
+
showPageIcon?: boolean;
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
export type TableViewSchema = BaseViewSchema & {
|
|
474
|
+
type: "table";
|
|
475
|
+
properties?: Array<PropertyFormat>;
|
|
476
|
+
wrap?: boolean;
|
|
477
|
+
groupBy?: GroupByFormat;
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
export type BoardViewSchema = BaseViewSchema & {
|
|
481
|
+
type: "board";
|
|
482
|
+
properties?: Array<PropertyFormat>;
|
|
483
|
+
groupBy?: GroupByFormat;
|
|
484
|
+
columns?: Array<GroupFormat>;
|
|
485
|
+
cover?: CoverFormat;
|
|
486
|
+
coverSize?: CoverSizeFormat;
|
|
487
|
+
coverAspect?: CoverAspectFormat;
|
|
488
|
+
wrap?: boolean;
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
export type CalendarViewSchema = BaseViewSchema & {
|
|
492
|
+
type: "calendar";
|
|
493
|
+
properties?: Array<PropertyFormat>;
|
|
494
|
+
/**
|
|
495
|
+
* REQUIRED: ResourceId of the date property to use for the calendar.
|
|
496
|
+
* Must match the `resourceId` of a date property in the database schema.
|
|
497
|
+
*
|
|
498
|
+
* @example
|
|
499
|
+
* calendarBy: "due-date-prop" // resourceId of a property of type "date"
|
|
500
|
+
*/
|
|
501
|
+
calendarBy: ResourceId;
|
|
502
|
+
showWeekends?: boolean;
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
export type ListViewSchema = BaseViewSchema & {
|
|
506
|
+
type: "list";
|
|
507
|
+
properties?: Array<PropertyFormat>;
|
|
508
|
+
groupBy?: GroupByFormat;
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
export type GalleryViewSchema = BaseViewSchema & {
|
|
512
|
+
type: "gallery";
|
|
513
|
+
properties?: Array<PropertyFormat>;
|
|
514
|
+
cover?: CoverFormat;
|
|
515
|
+
coverSize?: CoverSizeFormat;
|
|
516
|
+
coverAspect?: CoverAspectFormat;
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
export type FeedViewSchema = BaseViewSchema & {
|
|
520
|
+
type: "feed";
|
|
521
|
+
properties?: Array<PropertyFormat>;
|
|
522
|
+
wrap?: boolean;
|
|
523
|
+
showAuthorByline?: boolean;
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
export type TimelineViewSchema = BaseViewSchema & {
|
|
527
|
+
type: "timeline";
|
|
528
|
+
properties?: Array<PropertyFormat>;
|
|
529
|
+
tableProperties?: Array<PropertyFormat>;
|
|
530
|
+
/**
|
|
531
|
+
* REQUIRED: ResourceId of the date property to use for the timeline start.
|
|
532
|
+
* Must match the `resourceId` of a date property in the database schema.
|
|
533
|
+
*
|
|
534
|
+
* @example
|
|
535
|
+
* timelineBy: "start-date-prop" // resourceId of a property of type "date"
|
|
536
|
+
*/
|
|
537
|
+
timelineBy: ResourceId;
|
|
538
|
+
/**
|
|
539
|
+
* Optional: ResourceId of the date property to use for the timeline end
|
|
540
|
+
* (for date ranges). Must match the `resourceId` of a date property in
|
|
541
|
+
* the database schema.
|
|
542
|
+
*/
|
|
543
|
+
timelineByEnd?: ResourceId;
|
|
544
|
+
showTable?: boolean;
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
export type ViewSchema =
|
|
548
|
+
| TableViewSchema
|
|
549
|
+
| BoardViewSchema
|
|
550
|
+
| CalendarViewSchema
|
|
551
|
+
| ListViewSchema
|
|
552
|
+
| GalleryViewSchema
|
|
553
|
+
| FeedViewSchema
|
|
554
|
+
| TimelineViewSchema;
|
package/src/oauth.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** OAuth 2.0 provider configuration. The secret value is stored with the app. */
|
|
2
|
+
export type OAuthConfiguration = {
|
|
3
|
+
authorizationEndpoint: string;
|
|
4
|
+
tokenEndpoint: string;
|
|
5
|
+
clientId: string;
|
|
6
|
+
clientSecretEnv: string;
|
|
7
|
+
scope: string;
|
|
8
|
+
authorizationParams?: Record<string, string>;
|
|
9
|
+
accessTokenExpireMs?: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/** @internal */
|
|
13
|
+
export function validateOAuthConfiguration(config: OAuthConfiguration): void {
|
|
14
|
+
if (!config.clientId || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(config.clientSecretEnv))
|
|
15
|
+
throw new Error("OAuth connections require a client ID and clientSecretEnv secret name.");
|
|
16
|
+
for (const endpoint of [config.authorizationEndpoint, config.tokenEndpoint]) {
|
|
17
|
+
const url = new URL(endpoint);
|
|
18
|
+
if (url.protocol !== "https:" || url.username || url.password || url.hash)
|
|
19
|
+
throw new Error("OAuth endpoints must be HTTPS URLs without credentials or fragments.");
|
|
20
|
+
}
|
|
21
|
+
if (
|
|
22
|
+
config.accessTokenExpireMs !== undefined &&
|
|
23
|
+
(!Number.isFinite(config.accessTokenExpireMs) || config.accessTokenExpireMs <= 0)
|
|
24
|
+
)
|
|
25
|
+
throw new Error("OAuth token expiry must be positive.");
|
|
26
|
+
if (
|
|
27
|
+
Object.keys(config.authorizationParams ?? {}).some((key) =>
|
|
28
|
+
[
|
|
29
|
+
"state",
|
|
30
|
+
"redirect_uri",
|
|
31
|
+
"client_id",
|
|
32
|
+
"response_type",
|
|
33
|
+
"code_challenge",
|
|
34
|
+
"code_challenge_method",
|
|
35
|
+
"scope",
|
|
36
|
+
].includes(key),
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
throw new Error("OAuth authorization parameters cannot override protocol fields.");
|
|
40
|
+
}
|