@notionhq/apps 0.0.33 → 0.0.34
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 +59 -5
- package/dist/configured-events.d.ts +45 -0
- package/dist/configured-events.d.ts.map +1 -0
- package/dist/configured-events.js +38 -0
- package/dist/configured-events.test.d.ts +2 -0
- package/dist/configured-events.test.d.ts.map +1 -0
- package/dist/custom-block.d.ts +1 -1
- package/dist/events-types.test.d.ts +2 -0
- package/dist/events-types.test.d.ts.map +1 -0
- package/dist/{triggers.d.ts → events.d.ts} +1 -1
- package/dist/events.d.ts.map +1 -0
- package/dist/{triggers.generated.d.ts → events.generated.d.ts} +20 -38
- package/dist/events.generated.d.ts.map +1 -0
- package/dist/{triggers.generated.js → events.generated.js} +9 -35
- 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/custom-agent.d.ts +3 -48
- package/dist/notion-as-code/custom-agent.d.ts.map +1 -1
- package/dist/notion-as-code/database.d.ts +11 -0
- package/dist/notion-as-code/database.d.ts.map +1 -1
- package/dist/notion-as-code/database.js +12 -0
- package/dist/notion-as-code/index.d.ts +1 -1
- package/dist/notion-as-code/index.d.ts.map +1 -1
- package/dist/notion-as-code/recurrence.d.ts +49 -0
- package/dist/notion-as-code/recurrence.d.ts.map +1 -0
- package/dist/notion-as-code/recurrence.js +0 -0
- package/dist/workflow.d.ts +4 -4
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +2 -2
- package/package.json +7 -7
- package/skills/connections/SKILL.md +3 -3
- package/skills/workflow/SKILL.md +25 -5
- package/src/cli/build.test.ts +30 -0
- package/src/configured-events.test.ts +90 -0
- package/src/configured-events.ts +101 -0
- package/src/connections.ts +1 -1
- package/src/events-types.test.ts +110 -0
- package/src/{triggers.generated.ts → events.generated.ts} +21 -55
- package/src/index.ts +1 -0
- package/src/notion-as-code/custom-agent.ts +15 -61
- package/src/notion-as-code/database.ts +27 -0
- package/src/notion-as-code/index.ts +2 -0
- package/src/notion-as-code/recurrence.ts +58 -0
- package/src/workflow-access-types.test.ts +4 -4
- package/src/workflow-connections-types.test.ts +7 -7
- package/src/workflow-types.test.ts +23 -23
- package/src/workflow.test.ts +39 -39
- package/src/workflow.ts +7 -7
- package/dist/triggers.d.ts.map +0 -1
- package/dist/triggers.generated.d.ts.map +0 -1
- /package/dist/{triggers.js → events.js} +0 -0
- /package/src/{triggers.ts → events.ts} +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import { expect, it } from "vitest";
|
|
4
|
+
|
|
5
|
+
it("checks property references and schedule variants in user code", () => {
|
|
6
|
+
const cases = [
|
|
7
|
+
{
|
|
8
|
+
name: "property",
|
|
9
|
+
valid: true,
|
|
10
|
+
expression:
|
|
11
|
+
"events.notionPageUpdated({ dataSource: tasks.dataSource, properties: [tasks.dataSource.properties.Status] })",
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: "content",
|
|
15
|
+
valid: true,
|
|
16
|
+
expression:
|
|
17
|
+
"events.notionPageUpdated({ dataSource: tasks.dataSource, includePageContent: true })",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: "typo",
|
|
21
|
+
valid: false,
|
|
22
|
+
expression:
|
|
23
|
+
"events.notionPageUpdated({ dataSource: tasks.dataSource, properties: [tasks.dataSource.properties.Statuz] })",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "string",
|
|
27
|
+
valid: false,
|
|
28
|
+
expression:
|
|
29
|
+
'events.notionPageUpdated({ dataSource: tasks.dataSource, properties: ["Status"] })',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "other_source",
|
|
33
|
+
valid: false,
|
|
34
|
+
expression:
|
|
35
|
+
"events.notionPageUpdated({ dataSource: tasks.dataSource, properties: [other.dataSource.properties.Name] })",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "unsupported_property",
|
|
39
|
+
valid: false,
|
|
40
|
+
expression:
|
|
41
|
+
"events.notionPageUpdated({ dataSource: tasks.dataSource, properties: [tasks.dataSource.properties.Files] })",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "empty",
|
|
45
|
+
valid: false,
|
|
46
|
+
expression:
|
|
47
|
+
"events.notionPageUpdated({ dataSource: tasks.dataSource, properties: [] })",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "no_watch",
|
|
51
|
+
valid: false,
|
|
52
|
+
expression: "events.notionPageUpdated({ dataSource: tasks.dataSource })",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "weekly",
|
|
56
|
+
valid: true,
|
|
57
|
+
expression:
|
|
58
|
+
'events.scheduled({ frequency: "week", interval: 1, weekdays: { MO: true }, start: "2026-09-23", timeZone: "UTC" })',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "no_weekday",
|
|
62
|
+
valid: false,
|
|
63
|
+
expression:
|
|
64
|
+
'events.scheduled({ frequency: "week", interval: 1, weekdays: {}, start: "2026-09-23", timeZone: "UTC" })',
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
const fixtures = new Map(
|
|
68
|
+
cases.map((testCase) => [
|
|
69
|
+
resolve("src", `__events_${testCase.name}.ts`),
|
|
70
|
+
`
|
|
71
|
+
import { database, events } from "./index.js";
|
|
72
|
+
const tasks = database("tasks", { name: "Tasks", dataSourceResourceId: "tasks-source", schema: {
|
|
73
|
+
Name: { type: "title", resourceId: "name" }, Status: { type: "select", resourceId: "status", options: [] }, Files: { type: "file", resourceId: "files" }
|
|
74
|
+
} });
|
|
75
|
+
const other = database("other", { name: "Other", dataSourceResourceId: "other-source", schema: {
|
|
76
|
+
Name: { type: "title", resourceId: "other-name" }
|
|
77
|
+
} });
|
|
78
|
+
${testCase.expression};
|
|
79
|
+
`,
|
|
80
|
+
]),
|
|
81
|
+
);
|
|
82
|
+
const config = ts.readConfigFile(resolve("tsconfig.json"), ts.sys.readFile);
|
|
83
|
+
const parsed = ts.parseJsonConfigFileContent(config.config, ts.sys, process.cwd());
|
|
84
|
+
const host = ts.createCompilerHost(parsed.options);
|
|
85
|
+
const getSourceFile = host.getSourceFile.bind(host);
|
|
86
|
+
host.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
|
|
87
|
+
const fixture = fixtures.get(fileName);
|
|
88
|
+
return fixture === undefined
|
|
89
|
+
? getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile)
|
|
90
|
+
: ts.createSourceFile(fileName, fixture, languageVersion, true);
|
|
91
|
+
};
|
|
92
|
+
const program = ts.createProgram(
|
|
93
|
+
[...fixtures.keys()],
|
|
94
|
+
{ ...parsed.options, noEmit: true },
|
|
95
|
+
host,
|
|
96
|
+
);
|
|
97
|
+
const diagnostics = ts.getPreEmitDiagnostics(program);
|
|
98
|
+
expect(
|
|
99
|
+
diagnostics.filter(
|
|
100
|
+
(diagnostic) => !diagnostic.file || !fixtures.has(diagnostic.file.fileName),
|
|
101
|
+
),
|
|
102
|
+
).toEqual([]);
|
|
103
|
+
for (const testCase of cases) {
|
|
104
|
+
const errors = diagnostics.filter(
|
|
105
|
+
(diagnostic) =>
|
|
106
|
+
diagnostic.file?.fileName === resolve("src", `__events_${testCase.name}.ts`),
|
|
107
|
+
);
|
|
108
|
+
expect(errors.length === 0, testCase.name).toBe(testCase.valid);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
// Generated by scripts/generate-
|
|
1
|
+
// Generated by scripts/generate-events.ts — do not edit by hand.
|
|
2
2
|
// Regenerate with: pnpm run generate
|
|
3
3
|
|
|
4
4
|
import type { WorkflowConnectionDeclarations, WorkflowConnectionKeys } from "./connections.js";
|
|
5
|
+
import { scheduled, notionPageCreated, notionPageUpdated } from "./configured-events.js";
|
|
6
|
+
import type { NotionPageCreatedTrigger, NotionPageUpdatedTrigger, PageUpdatedOptions, RecurrenceTrigger } from "./configured-events.js";
|
|
7
|
+
import type { DataSourceHandle } from "./notion-as-code/database.js";
|
|
8
|
+
import type { NotionAsCodeSchema } from "./notion-as-code/intents.js";
|
|
9
|
+
import type { RecurrenceSchedule } from "./notion-as-code/recurrence.js";
|
|
5
10
|
|
|
6
11
|
import type {
|
|
7
12
|
CalendarEventCanceledEvent,
|
|
@@ -25,7 +30,7 @@ import type {
|
|
|
25
30
|
SlackMessageEvent,
|
|
26
31
|
SlackReactionAddedEvent,
|
|
27
32
|
WebhookEvent,
|
|
28
|
-
} from "./
|
|
33
|
+
} from "./events.js";
|
|
29
34
|
|
|
30
35
|
export type {
|
|
31
36
|
Actor,
|
|
@@ -72,7 +77,10 @@ export type {
|
|
|
72
77
|
VendorActor,
|
|
73
78
|
VendorExtensions,
|
|
74
79
|
WebhookEvent,
|
|
75
|
-
} from "./
|
|
80
|
+
} from "./events.js";
|
|
81
|
+
|
|
82
|
+
export type { NotionPageCreatedTrigger, NotionPageUpdatedTrigger, PageUpdatedOptions, RecurrenceTrigger } from "./configured-events.js";
|
|
83
|
+
export type { RecurrenceSchedule } from "./notion-as-code/recurrence.js";
|
|
76
84
|
|
|
77
85
|
/** Triggers immediately after a new message is posted in Slack (includes new threads and replies) */
|
|
78
86
|
export type SlackMessageTrigger<TKey extends string = string> = {
|
|
@@ -144,16 +152,6 @@ export type CalendarEventCanceledTrigger<TKey extends string = string> = {
|
|
|
144
152
|
connectionKey?: TKey;
|
|
145
153
|
};
|
|
146
154
|
|
|
147
|
-
/** A page has been added to a database. */
|
|
148
|
-
export type NotionPageCreatedTrigger = {
|
|
149
|
-
type: "notion.page.created";
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
/** A page was updated. */
|
|
153
|
-
export type NotionPageUpdatedTrigger = {
|
|
154
|
-
type: "notion.page.updated";
|
|
155
|
-
};
|
|
156
|
-
|
|
157
155
|
/** A page was deleted. */
|
|
158
156
|
export type NotionPageDeletedTrigger = {
|
|
159
157
|
type: "notion.page.deleted";
|
|
@@ -196,13 +194,8 @@ export type GoogleDriveOauthFilesChangedTrigger<TKey extends string = string> =
|
|
|
196
194
|
connectionKey?: TKey;
|
|
197
195
|
};
|
|
198
196
|
|
|
199
|
-
/** A recurring schedule fired. */
|
|
200
|
-
export type RecurrenceTrigger = {
|
|
201
|
-
type: "recurrence";
|
|
202
|
-
};
|
|
203
|
-
|
|
204
197
|
/** Creators for each event that can trigger a workflow. */
|
|
205
|
-
export const
|
|
198
|
+
export const events = {
|
|
206
199
|
/**
|
|
207
200
|
* Declare that a workflow can run on `slack.message` events.
|
|
208
201
|
*
|
|
@@ -323,27 +316,9 @@ export const triggers = {
|
|
|
323
316
|
};
|
|
324
317
|
},
|
|
325
318
|
|
|
326
|
-
|
|
327
|
-
* Declare that a workflow can run on `notion.page.created` events.
|
|
328
|
-
*
|
|
329
|
-
* A page has been added to a database.
|
|
330
|
-
*/
|
|
331
|
-
notionPageCreated(): NotionPageCreatedTrigger {
|
|
332
|
-
return {
|
|
333
|
-
type: "notion.page.created",
|
|
334
|
-
};
|
|
335
|
-
},
|
|
319
|
+
notionPageCreated,
|
|
336
320
|
|
|
337
|
-
|
|
338
|
-
* Declare that a workflow can run on `notion.page.updated` events.
|
|
339
|
-
*
|
|
340
|
-
* A page was updated.
|
|
341
|
-
*/
|
|
342
|
-
notionPageUpdated(): NotionPageUpdatedTrigger {
|
|
343
|
-
return {
|
|
344
|
-
type: "notion.page.updated",
|
|
345
|
-
};
|
|
346
|
-
},
|
|
321
|
+
notionPageUpdated,
|
|
347
322
|
|
|
348
323
|
/**
|
|
349
324
|
* Declare that a workflow can run on `notion.page.deleted` events.
|
|
@@ -434,20 +409,11 @@ export const triggers = {
|
|
|
434
409
|
};
|
|
435
410
|
},
|
|
436
411
|
|
|
437
|
-
|
|
438
|
-
* Declare that a workflow can run on `recurrence` events.
|
|
439
|
-
*
|
|
440
|
-
* A recurring schedule fired.
|
|
441
|
-
*/
|
|
442
|
-
scheduled(): RecurrenceTrigger {
|
|
443
|
-
return {
|
|
444
|
-
type: "recurrence",
|
|
445
|
-
};
|
|
446
|
-
},
|
|
412
|
+
scheduled,
|
|
447
413
|
} as const;
|
|
448
414
|
|
|
449
415
|
/** Trigger creators restricted to the workflow's declared provider keys. */
|
|
450
|
-
export type
|
|
416
|
+
export type WorkflowEventCreators<TConnections extends WorkflowConnectionDeclarations> = {
|
|
451
417
|
slackMessage(options?: { connectionKey: WorkflowConnectionKeys<TConnections, "slack"> }): SlackMessageTrigger<WorkflowConnectionKeys<TConnections, "slack">>;
|
|
452
418
|
slackReactionAdded(options?: { connectionKey: WorkflowConnectionKeys<TConnections, "slack"> }): SlackReactionAddedTrigger<WorkflowConnectionKeys<TConnections, "slack">>;
|
|
453
419
|
slackAppMention(options?: { connectionKey: WorkflowConnectionKeys<TConnections, "slack"> }): SlackAppMentionTrigger<WorkflowConnectionKeys<TConnections, "slack">>;
|
|
@@ -458,8 +424,8 @@ export type WorkflowTriggerCreators<TConnections extends WorkflowConnectionDecla
|
|
|
458
424
|
calendarEventCreated(options?: { connectionKey: WorkflowConnectionKeys<TConnections, "calendar"> }): CalendarEventCreatedTrigger<WorkflowConnectionKeys<TConnections, "calendar">>;
|
|
459
425
|
calendarEventUpdated(options?: { connectionKey: WorkflowConnectionKeys<TConnections, "calendar"> }): CalendarEventUpdatedTrigger<WorkflowConnectionKeys<TConnections, "calendar">>;
|
|
460
426
|
calendarEventCanceled(options?: { connectionKey: WorkflowConnectionKeys<TConnections, "calendar"> }): CalendarEventCanceledTrigger<WorkflowConnectionKeys<TConnections, "calendar">>;
|
|
461
|
-
notionPageCreated(): NotionPageCreatedTrigger;
|
|
462
|
-
notionPageUpdated(): NotionPageUpdatedTrigger;
|
|
427
|
+
notionPageCreated(options?: { dataSource: DataSourceHandle }): NotionPageCreatedTrigger;
|
|
428
|
+
notionPageUpdated<const Schema extends NotionAsCodeSchema>(options?: PageUpdatedOptions<Schema>): NotionPageUpdatedTrigger;
|
|
463
429
|
notionPageDeleted(): NotionPageDeletedTrigger;
|
|
464
430
|
notionMeetingNoteSummaryCompleted(): NotionMeetingNoteSummaryCompletedTrigger;
|
|
465
431
|
notionCommentAdded(): NotionCommentAddedTrigger;
|
|
@@ -468,7 +434,7 @@ export type WorkflowTriggerCreators<TConnections extends WorkflowConnectionDecla
|
|
|
468
434
|
notionDatabaseAgentUpdated(): NotionDatabaseAgentUpdatedTrigger;
|
|
469
435
|
webhook(): WebhookTrigger;
|
|
470
436
|
googleDriveOauthFilesChanged(options?: { connectionKey: WorkflowConnectionKeys<TConnections, "googleDriveOauth"> }): GoogleDriveOauthFilesChangedTrigger<WorkflowConnectionKeys<TConnections, "googleDriveOauth">>;
|
|
471
|
-
scheduled(): RecurrenceTrigger;
|
|
437
|
+
scheduled(options?: RecurrenceSchedule): RecurrenceTrigger;
|
|
472
438
|
};
|
|
473
439
|
|
|
474
440
|
/** Triggers whose connection keys belong to the declared provider. */
|
|
@@ -496,8 +462,8 @@ export type WorkflowTriggerForConnections<TConnections extends WorkflowConnectio
|
|
|
496
462
|
| RecurrenceTrigger;
|
|
497
463
|
|
|
498
464
|
/** @internal */
|
|
499
|
-
export function
|
|
500
|
-
return
|
|
465
|
+
export function createWorkflowEvents<TConnections extends WorkflowConnectionDeclarations>(): WorkflowEventCreators<TConnections> {
|
|
466
|
+
return events;
|
|
501
467
|
}
|
|
502
468
|
|
|
503
469
|
/** Event that can trigger a workflow. */
|
package/src/index.ts
CHANGED
|
@@ -3,67 +3,21 @@ import type { CustomAgentIntent, NotionAsCodeIcon, ResourceId } from "./intents.
|
|
|
3
3
|
import { recordIntent } from "./recorder.js";
|
|
4
4
|
import { assertUserResourceId } from "./resource.js";
|
|
5
5
|
|
|
6
|
-
type
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
weekday: RecurrenceWeekday;
|
|
22
|
-
occurrence: MonthlyWeekdayOccurrence;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export type RecurrenceScheduleEnd =
|
|
26
|
-
| { type: "date"; endsAt: string }
|
|
27
|
-
| { type: "occurrences"; count: number };
|
|
28
|
-
|
|
29
|
-
type RecurrenceScheduleBase = {
|
|
30
|
-
interval: number;
|
|
31
|
-
start: string;
|
|
32
|
-
timeZone: string;
|
|
33
|
-
end?: RecurrenceScheduleEnd;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export type HourlyRecurrenceSchedule = RecurrenceScheduleBase & {
|
|
37
|
-
frequency: "hour";
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export type DailyRecurrenceSchedule = RecurrenceScheduleBase & {
|
|
41
|
-
frequency: "day";
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export type WeeklyRecurrenceSchedule = RecurrenceScheduleBase & {
|
|
45
|
-
frequency: "week";
|
|
46
|
-
weekdays: RecurrenceWeekdays;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
export type MonthlyRecurrenceSchedule = RecurrenceScheduleBase & {
|
|
50
|
-
frequency: "month";
|
|
51
|
-
monthlyRestriction: MonthlyRecurrenceRestriction;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
export type YearlyRecurrenceSchedule = RecurrenceScheduleBase & {
|
|
55
|
-
frequency: "year";
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export type CustomAgentRecurrenceSchedule = (
|
|
59
|
-
| HourlyRecurrenceSchedule
|
|
60
|
-
| DailyRecurrenceSchedule
|
|
61
|
-
| WeeklyRecurrenceSchedule
|
|
62
|
-
| MonthlyRecurrenceSchedule
|
|
63
|
-
| YearlyRecurrenceSchedule
|
|
64
|
-
) & {
|
|
65
|
-
type: "recurrence";
|
|
66
|
-
};
|
|
6
|
+
import type { RecurrenceSchedule } from "./recurrence.js";
|
|
7
|
+
export type {
|
|
8
|
+
RecurrenceWeekdays,
|
|
9
|
+
MonthlyWeekdayOccurrence,
|
|
10
|
+
MonthlyRecurrenceRestriction,
|
|
11
|
+
RecurrenceScheduleEnd,
|
|
12
|
+
HourlyRecurrenceSchedule,
|
|
13
|
+
DailyRecurrenceSchedule,
|
|
14
|
+
WeeklyRecurrenceSchedule,
|
|
15
|
+
MonthlyRecurrenceSchedule,
|
|
16
|
+
YearlyRecurrenceSchedule,
|
|
17
|
+
RecurrenceSchedule,
|
|
18
|
+
} from "./recurrence.js";
|
|
19
|
+
|
|
20
|
+
export type CustomAgentRecurrenceSchedule = RecurrenceSchedule & { type: "recurrence" };
|
|
67
21
|
|
|
68
22
|
export type CustomAgentTriggerBase = {
|
|
69
23
|
resourceId: ResourceId;
|
|
@@ -118,6 +118,20 @@ export type ChildDatabaseArgs<
|
|
|
118
118
|
| ChildMultiSourceDatabaseArgs<DataSources>
|
|
119
119
|
| Omit<ViewOnlyDatabaseArgs, "parent">;
|
|
120
120
|
|
|
121
|
+
export type PropertyHandle<
|
|
122
|
+
Property extends NotionAsCodeSchema[string] = NotionAsCodeSchema[string],
|
|
123
|
+
> = {
|
|
124
|
+
readonly resourceType: "property";
|
|
125
|
+
readonly resourceId: Property["resourceId"];
|
|
126
|
+
readonly dataSourceResourceId: string;
|
|
127
|
+
readonly name: string;
|
|
128
|
+
readonly propertyType: Property["type"];
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export type DataSourceProperties<Schema extends NotionAsCodeSchema> = {
|
|
132
|
+
readonly [Name in keyof Schema]: PropertyHandle<Schema[Name]>;
|
|
133
|
+
};
|
|
134
|
+
|
|
121
135
|
/** A source handle retains its keyed authoring schema for sync inference. */
|
|
122
136
|
export type DataSourceHandle<Schema extends NotionAsCodeSchema = NotionAsCodeSchema> = {
|
|
123
137
|
readonly resourceType: "dataSource";
|
|
@@ -125,6 +139,7 @@ export type DataSourceHandle<Schema extends NotionAsCodeSchema = NotionAsCodeSch
|
|
|
125
139
|
readonly name: string;
|
|
126
140
|
readonly description?: string;
|
|
127
141
|
readonly schema: Schema;
|
|
142
|
+
readonly properties: DataSourceProperties<Schema>;
|
|
128
143
|
readonly database: Database<AppsSchemaForSchema<Schema>>;
|
|
129
144
|
addPage: (args: ChildPageArgs) => PageHandle;
|
|
130
145
|
};
|
|
@@ -501,6 +516,7 @@ function createDataSourceHandle(
|
|
|
501
516
|
schema: NotionAsCodeSchema,
|
|
502
517
|
databaseDescription: string | undefined,
|
|
503
518
|
): DataSourceHandle {
|
|
519
|
+
const propertyEntries: Array<[string, PropertyHandle]> = [];
|
|
504
520
|
const propertyResourceIds = new Set<string>();
|
|
505
521
|
const propertyNames = new Set<string>();
|
|
506
522
|
for (const property of dataSource.properties) {
|
|
@@ -517,6 +533,16 @@ function createDataSourceHandle(
|
|
|
517
533
|
}
|
|
518
534
|
propertyResourceIds.add(property.resourceId);
|
|
519
535
|
propertyNames.add(property.name);
|
|
536
|
+
propertyEntries.push([
|
|
537
|
+
property.name,
|
|
538
|
+
{
|
|
539
|
+
resourceType: "property",
|
|
540
|
+
resourceId: property.resourceId,
|
|
541
|
+
dataSourceResourceId: dataSource.resourceId,
|
|
542
|
+
name: property.name,
|
|
543
|
+
propertyType: property.type,
|
|
544
|
+
},
|
|
545
|
+
]);
|
|
520
546
|
}
|
|
521
547
|
const description = dataSource.description ?? databaseDescription;
|
|
522
548
|
|
|
@@ -526,6 +552,7 @@ function createDataSourceHandle(
|
|
|
526
552
|
name: dataSource.name,
|
|
527
553
|
...(description === undefined ? {} : { description }),
|
|
528
554
|
schema,
|
|
555
|
+
properties: Object.fromEntries(propertyEntries),
|
|
529
556
|
database: createAppsDatabase(dataSource.resourceId, dataSource),
|
|
530
557
|
addPage(pageArgs) {
|
|
531
558
|
return createPage(pageArgs, { type: "resourceId", resourceId: dataSource.resourceId });
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
type RecurrenceWeekday = "MO" | "TU" | "WE" | "TH" | "FR" | "SA" | "SU";
|
|
2
|
+
|
|
3
|
+
/** A nonempty selection of weekdays for a weekly recurrence. */
|
|
4
|
+
export type RecurrenceWeekdays = {
|
|
5
|
+
[Day in RecurrenceWeekday]?: true;
|
|
6
|
+
} & {
|
|
7
|
+
[Day in RecurrenceWeekday]: Required<Pick<Record<RecurrenceWeekday, true>, Day>>;
|
|
8
|
+
}[RecurrenceWeekday];
|
|
9
|
+
|
|
10
|
+
export type MonthlyWeekdayOccurrence = "1st" | "2nd" | "3rd" | "4th" | "last";
|
|
11
|
+
|
|
12
|
+
export type MonthlyRecurrenceRestriction =
|
|
13
|
+
| { type: "day_of_month"; day: number }
|
|
14
|
+
| {
|
|
15
|
+
type: "weekday_of_month";
|
|
16
|
+
weekday: RecurrenceWeekday;
|
|
17
|
+
occurrence: MonthlyWeekdayOccurrence;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type RecurrenceScheduleEnd =
|
|
21
|
+
| { type: "date"; endsAt: string }
|
|
22
|
+
| { type: "occurrences"; count: number };
|
|
23
|
+
|
|
24
|
+
type RecurrenceScheduleBase = {
|
|
25
|
+
interval: number;
|
|
26
|
+
start: string;
|
|
27
|
+
timeZone: string;
|
|
28
|
+
end?: RecurrenceScheduleEnd;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type HourlyRecurrenceSchedule = RecurrenceScheduleBase & {
|
|
32
|
+
frequency: "hour";
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type DailyRecurrenceSchedule = RecurrenceScheduleBase & {
|
|
36
|
+
frequency: "day";
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type WeeklyRecurrenceSchedule = RecurrenceScheduleBase & {
|
|
40
|
+
frequency: "week";
|
|
41
|
+
weekdays: RecurrenceWeekdays;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type MonthlyRecurrenceSchedule = RecurrenceScheduleBase & {
|
|
45
|
+
frequency: "month";
|
|
46
|
+
monthlyRestriction: MonthlyRecurrenceRestriction;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type YearlyRecurrenceSchedule = RecurrenceScheduleBase & {
|
|
50
|
+
frequency: "year";
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type RecurrenceSchedule =
|
|
54
|
+
| HourlyRecurrenceSchedule
|
|
55
|
+
| DailyRecurrenceSchedule
|
|
56
|
+
| WeeklyRecurrenceSchedule
|
|
57
|
+
| MonthlyRecurrenceSchedule
|
|
58
|
+
| YearlyRecurrenceSchedule;
|
|
@@ -3,7 +3,7 @@ import { expectTypeOf, it } from "vitest";
|
|
|
3
3
|
import { customAgent } from "./notion-as-code/custom-agent.js";
|
|
4
4
|
import { database } from "./notion-as-code/database.js";
|
|
5
5
|
import { page } from "./notion-as-code/page.js";
|
|
6
|
-
import {
|
|
6
|
+
import { events } from "./events.generated.js";
|
|
7
7
|
import { access, workflow } from "./workflow.js";
|
|
8
8
|
|
|
9
9
|
it("infers access aliases and rejects resource-specific levels", () => {
|
|
@@ -26,7 +26,7 @@ it("infers access aliases and rejects resource-specific levels", () => {
|
|
|
26
26
|
workflow({
|
|
27
27
|
name: "Access types",
|
|
28
28
|
description: "Checks access types",
|
|
29
|
-
triggers: [
|
|
29
|
+
triggers: [events.notionPageCreated()],
|
|
30
30
|
access: {
|
|
31
31
|
home: access.view(home),
|
|
32
32
|
singleDatabase: access.edit(singleDatabase),
|
|
@@ -54,7 +54,7 @@ it("infers access aliases and rejects resource-specific levels", () => {
|
|
|
54
54
|
workflow({
|
|
55
55
|
name: "Agent access types",
|
|
56
56
|
description: "Checks custom agent access types",
|
|
57
|
-
triggers: [
|
|
57
|
+
triggers: [events.notionPageCreated()],
|
|
58
58
|
// @ts-expect-error Custom agents only support the call level.
|
|
59
59
|
access: { agent: { resource: agent, level: "view" } },
|
|
60
60
|
handler: () => {},
|
|
@@ -63,7 +63,7 @@ it("infers access aliases and rejects resource-specific levels", () => {
|
|
|
63
63
|
workflow({
|
|
64
64
|
name: "Page access types",
|
|
65
65
|
description: "Checks page access types",
|
|
66
|
-
triggers: [
|
|
66
|
+
triggers: [events.notionPageCreated()],
|
|
67
67
|
// @ts-expect-error Pages do not support the custom-agent call level.
|
|
68
68
|
access: { home: { resource: home, level: "call" } },
|
|
69
69
|
handler: () => {},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expectTypeOf, it } from "vitest";
|
|
2
|
-
import {
|
|
2
|
+
import { events } from "./events.generated.js";
|
|
3
3
|
import { connections, workflow, type WorkflowConnectionDeclarations } from "./workflow.js";
|
|
4
4
|
|
|
5
5
|
it("infers only the declared provider clients", () => {
|
|
@@ -7,7 +7,7 @@ it("infers only the declared provider clients", () => {
|
|
|
7
7
|
name: "Mail",
|
|
8
8
|
description: "Read the configured mailbox",
|
|
9
9
|
connections: { inbox: connections.mail() },
|
|
10
|
-
triggers: ({
|
|
10
|
+
triggers: ({ events }) => [events.mailEmailReceived({ connectionKey: "inbox" })],
|
|
11
11
|
handler(_event, context) {
|
|
12
12
|
expectTypeOf<keyof typeof context.connections>().toEqualTypeOf<"inbox">();
|
|
13
13
|
expectTypeOf(context.connections.inbox.searchEmails).toBeFunction();
|
|
@@ -19,7 +19,7 @@ it("infers only the declared provider clients", () => {
|
|
|
19
19
|
workflow({
|
|
20
20
|
name: "Calendar",
|
|
21
21
|
description: "Calendar only",
|
|
22
|
-
triggers: [
|
|
22
|
+
triggers: [events.notionPageCreated()],
|
|
23
23
|
connections: { work: connections.calendar() },
|
|
24
24
|
handler(_event, context) {
|
|
25
25
|
expectTypeOf<keyof typeof context.connections>().toEqualTypeOf<"work">();
|
|
@@ -37,7 +37,7 @@ it("infers only the declared provider clients", () => {
|
|
|
37
37
|
workflow({
|
|
38
38
|
name: "Multiple providers",
|
|
39
39
|
description: "Preserves clients in a separately declared object",
|
|
40
|
-
triggers: [
|
|
40
|
+
triggers: [events.notionPageCreated()],
|
|
41
41
|
connections: requirements,
|
|
42
42
|
handler(_event, context) {
|
|
43
43
|
expectTypeOf<keyof typeof context.connections>().toEqualTypeOf<
|
|
@@ -51,7 +51,7 @@ it("exposes no providers for omitted or empty connections", () => {
|
|
|
51
51
|
workflow({
|
|
52
52
|
name: "No connections",
|
|
53
53
|
description: "Omitted",
|
|
54
|
-
triggers: [
|
|
54
|
+
triggers: [events.notionPageCreated()],
|
|
55
55
|
handler(_event, context) {
|
|
56
56
|
expectTypeOf<keyof typeof context.connections>().toEqualTypeOf<never>();
|
|
57
57
|
},
|
|
@@ -59,7 +59,7 @@ it("exposes no providers for omitted or empty connections", () => {
|
|
|
59
59
|
workflow({
|
|
60
60
|
name: "No connections",
|
|
61
61
|
description: "Empty",
|
|
62
|
-
triggers: [
|
|
62
|
+
triggers: [events.notionPageCreated()],
|
|
63
63
|
connections: {},
|
|
64
64
|
handler(_event, context) {
|
|
65
65
|
expectTypeOf<keyof typeof context.connections>().toEqualTypeOf<never>();
|
|
@@ -72,7 +72,7 @@ it("preserves literal declarations checked with satisfies", () => {
|
|
|
72
72
|
workflow({
|
|
73
73
|
name: "Slack",
|
|
74
74
|
description: "Slack only",
|
|
75
|
-
triggers: [
|
|
75
|
+
triggers: [events.notionPageCreated()],
|
|
76
76
|
connections: requirements,
|
|
77
77
|
handler(_event, context) {
|
|
78
78
|
expectTypeOf<keyof typeof context.connections>().toEqualTypeOf<"support">();
|