@notionhq/apps 0.0.33 → 0.0.35
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 +15 -60
- package/dist/notion-as-code/custom-agent.d.ts.map +1 -1
- package/dist/notion-as-code/custom-agent.js +15 -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/intents.d.ts +7 -1
- package/dist/notion-as-code/intents.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/notion-as-code/SKILL.md +11 -7
- 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.test.ts +82 -2
- package/src/notion-as-code/custom-agent.ts +46 -74
- package/src/notion-as-code/database.ts +27 -0
- package/src/notion-as-code/index.ts +2 -0
- package/src/notion-as-code/intents.ts +2 -1
- package/src/notion-as-code/notion-as-code-types.test.ts +4 -1
- 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
|
@@ -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;
|
|
@@ -79,6 +33,8 @@ export type PropertyTriggerCondition = {
|
|
|
79
33
|
export type CustomAgentPageAddedTrigger = CustomAgentTriggerBase & {
|
|
80
34
|
type: "page_added";
|
|
81
35
|
dataSourceResourceId: ResourceId;
|
|
36
|
+
/** Wait for follow-up edits before running the agent. Defaults to false in Apps. */
|
|
37
|
+
waitForEditsToFinish?: boolean;
|
|
82
38
|
};
|
|
83
39
|
|
|
84
40
|
type PropertyUpdatedTriggerTarget =
|
|
@@ -95,6 +51,8 @@ export type CustomAgentPropertyUpdatedTrigger = CustomAgentTriggerBase &
|
|
|
95
51
|
PropertyUpdatedTriggerTarget & {
|
|
96
52
|
type: "property_updated";
|
|
97
53
|
dataSourceResourceId: ResourceId;
|
|
54
|
+
/** Wait for follow-up edits before running the agent. Defaults to false in Apps. */
|
|
55
|
+
waitForEditsToFinish?: boolean;
|
|
98
56
|
};
|
|
99
57
|
|
|
100
58
|
export type CustomAgentTrigger =
|
|
@@ -114,15 +72,8 @@ export type CustomAgentAccess = Extract<
|
|
|
114
72
|
{ readonly resource: { readonly resourceType: "page" | "database" } }
|
|
115
73
|
>;
|
|
116
74
|
|
|
117
|
-
/**
|
|
118
|
-
|
|
119
|
-
*
|
|
120
|
-
* URL trust is independent from web access: an agent can trust URLs without
|
|
121
|
-
* being granted web search access.
|
|
122
|
-
*/
|
|
123
|
-
export type CustomAgentTrustedUrls = {
|
|
124
|
-
type: "all";
|
|
125
|
-
};
|
|
75
|
+
/** Trust every URL, trust no URLs, or trust a nonempty list of up to 20 URL globs. */
|
|
76
|
+
export type CustomAgentTrustedUrls = "all" | "none" | [string, ...string[]];
|
|
126
77
|
|
|
127
78
|
export type CustomAgentArgs = {
|
|
128
79
|
resourceId: ResourceId;
|
|
@@ -130,6 +81,8 @@ export type CustomAgentArgs = {
|
|
|
130
81
|
icon?: Exclude<NotionAsCodeIcon, { type: "file" }>;
|
|
131
82
|
instructions?: string;
|
|
132
83
|
model?: "ambrosia-tart-high" | "opal-quince-medium" | "almond-croissant-low";
|
|
84
|
+
/** Whether the agent can use the Web Browser. Omission preserves the existing setting. */
|
|
85
|
+
browserUse?: boolean;
|
|
133
86
|
/**
|
|
134
87
|
* Whether the agent can use web search. Defaults to `false` when omitted for
|
|
135
88
|
* a new agent. When updating an existing agent, omitting this field preserves
|
|
@@ -137,9 +90,10 @@ export type CustomAgentArgs = {
|
|
|
137
90
|
*/
|
|
138
91
|
webAccess?: boolean;
|
|
139
92
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* for
|
|
93
|
+
* Set `"all"` to trust every HTTP and HTTPS URL, `"none"` to disable URL
|
|
94
|
+
* trust, or provide a nonempty list of up to 20 URL globs. Omission leaves
|
|
95
|
+
* trust disabled for a new agent and preserves it for an existing agent.
|
|
96
|
+
* URL trust is independent from web search.
|
|
143
97
|
*/
|
|
144
98
|
trustedUrls?: CustomAgentTrustedUrls;
|
|
145
99
|
/**
|
|
@@ -181,13 +135,31 @@ export function customAgent(args: CustomAgentArgs): CustomAgentHandle {
|
|
|
181
135
|
}
|
|
182
136
|
}
|
|
183
137
|
}
|
|
184
|
-
const { access: _access, ...intent } = args;
|
|
138
|
+
const { access: _access, trustedUrls, triggers, ...intent } = args;
|
|
185
139
|
recordIntent({
|
|
186
140
|
type: "custom_agent",
|
|
187
141
|
...intent,
|
|
142
|
+
...(trustedUrls === undefined ? {} : { trustedUrls: toTrustedUrlsIntent(trustedUrls) }),
|
|
143
|
+
...(triggers === undefined ? {} : { triggers: triggers.map(toTriggerIntent) }),
|
|
188
144
|
...(grants === undefined ? {} : { access: grants }),
|
|
189
145
|
} satisfies {
|
|
190
146
|
type: "custom_agent";
|
|
191
147
|
} & CustomAgentIntent);
|
|
192
148
|
return { resourceType: "customAgent", resourceId: args.resourceId };
|
|
193
149
|
}
|
|
150
|
+
|
|
151
|
+
function toTriggerIntent(trigger: CustomAgentTrigger): CustomAgentTrigger {
|
|
152
|
+
if (trigger.type === "recurrence") {
|
|
153
|
+
return trigger;
|
|
154
|
+
}
|
|
155
|
+
return { ...trigger, waitForEditsToFinish: trigger.waitForEditsToFinish ?? false };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function toTrustedUrlsIntent(
|
|
159
|
+
trustedUrls: CustomAgentTrustedUrls,
|
|
160
|
+
): NonNullable<CustomAgentIntent["trustedUrls"]> {
|
|
161
|
+
if (trustedUrls === "all") {
|
|
162
|
+
return { type: "all" };
|
|
163
|
+
}
|
|
164
|
+
return { type: "list", urls: trustedUrls === "none" ? [] : [...trustedUrls] };
|
|
165
|
+
}
|
|
@@ -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 });
|
|
@@ -347,7 +347,8 @@ export type DatabaseIntent = {
|
|
|
347
347
|
};
|
|
348
348
|
|
|
349
349
|
export type PageIntent = PageArgs & { parent: Parent };
|
|
350
|
-
export type CustomAgentIntent = Omit<CustomAgentArgs, "access"> & {
|
|
350
|
+
export type CustomAgentIntent = Omit<CustomAgentArgs, "access" | "trustedUrls"> & {
|
|
351
|
+
trustedUrls?: { type: "all" } | { type: "list"; urls: string[] };
|
|
351
352
|
access?: readonly { resourceId: ResourceId; level: ResourceAccessLevel }[];
|
|
352
353
|
};
|
|
353
354
|
|
|
@@ -37,8 +37,11 @@ it("types custom agent resource access and URL trust", () => {
|
|
|
37
37
|
expectTypeOf(
|
|
38
38
|
access.call(customAgent({ resourceId: "agent", name: "Agent" })),
|
|
39
39
|
).not.toExtend<CustomAgentAccess>();
|
|
40
|
-
expectTypeOf<
|
|
40
|
+
expectTypeOf<CustomAgentTrustedUrls>().toEqualTypeOf<"all" | "none" | [string, ...string[]]>();
|
|
41
|
+
expectTypeOf<[]>().not.toExtend<CustomAgentTrustedUrls>();
|
|
42
|
+
expectTypeOf<{ type: "all" }>().not.toExtend<CustomAgentTrustedUrls>();
|
|
41
43
|
expectTypeOf<CustomAgentArgs["webAccess"]>().toEqualTypeOf<boolean | undefined>();
|
|
44
|
+
expectTypeOf<CustomAgentArgs["browserUse"]>().toEqualTypeOf<boolean | undefined>();
|
|
42
45
|
expectTypeOf<CustomAgentArgs["trustedUrls"]>().toEqualTypeOf<
|
|
43
46
|
CustomAgentTrustedUrls | undefined
|
|
44
47
|
>();
|
|
@@ -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">();
|
|
@@ -9,19 +9,19 @@ it("checks provider keys on callback arguments and returned triggers", () => {
|
|
|
9
9
|
valid: true,
|
|
10
10
|
connections: "{ support: connections.slack(), calendar: connections.calendar() }",
|
|
11
11
|
trigger:
|
|
12
|
-
'
|
|
12
|
+
'events.slackMessage({ connectionKey: "support" }), events.calendarEventCreated({ connectionKey: "calendar" })',
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
name: "default",
|
|
16
16
|
valid: true,
|
|
17
17
|
connections: "{ slack: connections.slack() }",
|
|
18
|
-
trigger: '
|
|
18
|
+
trigger: 'events.slackMessage({ connectionKey: "slack" })',
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
name: "unbound",
|
|
22
22
|
valid: true,
|
|
23
23
|
connections: "{}",
|
|
24
|
-
trigger: "
|
|
24
|
+
trigger: "events.slackMessage(), events.notionPageCreated()",
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
name: "global_valid",
|
|
@@ -33,25 +33,25 @@ it("checks provider keys on callback arguments and returned triggers", () => {
|
|
|
33
33
|
name: "undeclared",
|
|
34
34
|
valid: false,
|
|
35
35
|
connections: "{ support: connections.slack() }",
|
|
36
|
-
trigger: '
|
|
36
|
+
trigger: 'events.slackMessage({ connectionKey: "typo" })',
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
name: "wrong_provider",
|
|
40
40
|
valid: false,
|
|
41
41
|
connections: "{ support: connections.calendar() }",
|
|
42
|
-
trigger: '
|
|
42
|
+
trigger: 'events.slackMessage({ connectionKey: "support" })',
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
name: "custom_not_default",
|
|
46
46
|
valid: false,
|
|
47
47
|
connections: "{ support: connections.slack() }",
|
|
48
|
-
trigger: '
|
|
48
|
+
trigger: 'events.slackMessage({ connectionKey: "slack" })',
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
name: "missing",
|
|
52
52
|
valid: false,
|
|
53
53
|
connections: undefined,
|
|
54
|
-
trigger: '
|
|
54
|
+
trigger: 'events.slackMessage({ connectionKey: "slack" })',
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
57
|
name: "global_invalid",
|
|
@@ -69,37 +69,37 @@ it("checks provider keys on callback arguments and returned triggers", () => {
|
|
|
69
69
|
name: "old_key_option",
|
|
70
70
|
valid: false,
|
|
71
71
|
connections: '{ support: connections.slack({ key: "support" }) }',
|
|
72
|
-
trigger: '
|
|
72
|
+
trigger: 'events.slackMessage({ connectionKey: "support" })',
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
name: "literal_default",
|
|
76
76
|
valid: true,
|
|
77
77
|
connections: '{ slack: { type: "slack" } }',
|
|
78
|
-
trigger: '
|
|
78
|
+
trigger: 'events.slackMessage({ connectionKey: "slack" })',
|
|
79
79
|
},
|
|
80
80
|
{
|
|
81
81
|
name: "saved",
|
|
82
82
|
valid: true,
|
|
83
83
|
connections: "{ support }",
|
|
84
|
-
trigger: '
|
|
84
|
+
trigger: 'events.slackMessage({ connectionKey: "support" })',
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
name: "saved_invalid",
|
|
88
88
|
valid: false,
|
|
89
89
|
connections: "{ support }",
|
|
90
|
-
trigger: '
|
|
90
|
+
trigger: 'events.slackMessage({ connectionKey: "slack" })',
|
|
91
91
|
},
|
|
92
92
|
{
|
|
93
93
|
name: "old_array",
|
|
94
94
|
valid: false,
|
|
95
95
|
connections: "[connections.slack()]",
|
|
96
|
-
trigger: "
|
|
96
|
+
trigger: "events.slackMessage()",
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
name: "two_slack",
|
|
100
100
|
valid: true,
|
|
101
101
|
connections: "{ support: connections.slack(), team: connections.slack() }",
|
|
102
|
-
trigger: '
|
|
102
|
+
trigger: 'events.slackMessage({ connectionKey: "team" })',
|
|
103
103
|
},
|
|
104
104
|
|
|
105
105
|
{
|
|
@@ -107,7 +107,7 @@ it("checks provider keys on callback arguments and returned triggers", () => {
|
|
|
107
107
|
valid: true,
|
|
108
108
|
connections:
|
|
109
109
|
"{ support: connections.slack(), team: connections.slack(), work: connections.calendar() }",
|
|
110
|
-
trigger: "
|
|
110
|
+
trigger: "events.notionPageCreated()",
|
|
111
111
|
handler:
|
|
112
112
|
'(_event, ctx) => { ctx.connections.support.findUserByEmail({ email: "a@example.com" }); ctx.connections.team.findUserByEmail({ email: "b@example.com" }); ctx.connections.work.listCalendars({}); }',
|
|
113
113
|
},
|
|
@@ -115,14 +115,14 @@ it("checks provider keys on callback arguments and returned triggers", () => {
|
|
|
115
115
|
name: "wrong_client_method",
|
|
116
116
|
valid: false,
|
|
117
117
|
connections: "{ support: connections.slack() }",
|
|
118
|
-
trigger: "
|
|
118
|
+
trigger: "events.notionPageCreated()",
|
|
119
119
|
handler: "(_event, ctx) => { ctx.connections.support.listCalendars({}); }",
|
|
120
120
|
},
|
|
121
121
|
{
|
|
122
122
|
name: "undeclared_client",
|
|
123
123
|
valid: false,
|
|
124
124
|
connections: "{ support: connections.slack() }",
|
|
125
|
-
trigger: "
|
|
125
|
+
trigger: "events.notionPageCreated()",
|
|
126
126
|
handler:
|
|
127
127
|
'(_event, ctx) => { ctx.connections.slack.findUserByEmail({ email: "a@example.com" }); }',
|
|
128
128
|
},
|
|
@@ -130,14 +130,14 @@ it("checks provider keys on callback arguments and returned triggers", () => {
|
|
|
130
130
|
name: "old_provider_call",
|
|
131
131
|
valid: false,
|
|
132
132
|
connections: "{ support: connections.slack() }",
|
|
133
|
-
trigger: "
|
|
133
|
+
trigger: "events.notionPageCreated()",
|
|
134
134
|
handler: '(_event, ctx) => { ctx.connections.support("support"); }',
|
|
135
135
|
},
|
|
136
136
|
{
|
|
137
137
|
name: "oauth_keyed_client",
|
|
138
138
|
valid: true,
|
|
139
139
|
connections: "{ github: connections.oauth(oauthConfig), support: connections.slack() }",
|
|
140
|
-
trigger: '
|
|
140
|
+
trigger: 'events.slackMessage({ connectionKey: "support" })',
|
|
141
141
|
handler:
|
|
142
142
|
"async (_event, ctx) => { const token: string = await ctx.connections.github.accessToken(); void token; }",
|
|
143
143
|
},
|
|
@@ -145,19 +145,19 @@ it("checks provider keys on callback arguments and returned triggers", () => {
|
|
|
145
145
|
name: "oauth_wrong_trigger",
|
|
146
146
|
valid: false,
|
|
147
147
|
connections: "{ github: connections.oauth(oauthConfig) }",
|
|
148
|
-
trigger: '
|
|
148
|
+
trigger: 'events.slackMessage({ connectionKey: "github" })',
|
|
149
149
|
},
|
|
150
150
|
{
|
|
151
151
|
name: "oauth_old_key_option",
|
|
152
152
|
valid: false,
|
|
153
153
|
connections: '{ github: connections.oauth({ ...oauthConfig, key: "github" }) }',
|
|
154
|
-
trigger: "
|
|
154
|
+
trigger: "events.notionPageCreated()",
|
|
155
155
|
},
|
|
156
156
|
{
|
|
157
157
|
name: "oauth_old_provider_call",
|
|
158
158
|
valid: false,
|
|
159
159
|
connections: "{ github: connections.oauth(oauthConfig) }",
|
|
160
|
-
trigger: "
|
|
160
|
+
trigger: "events.notionPageCreated()",
|
|
161
161
|
handler: '(_event, ctx) => { ctx.connections.oauth("github"); }',
|
|
162
162
|
},
|
|
163
163
|
];
|
|
@@ -165,13 +165,13 @@ it("checks provider keys on callback arguments and returned triggers", () => {
|
|
|
165
165
|
cases.map((testCase) => [
|
|
166
166
|
resolve("src", `__workflow_types_${testCase.name}.ts`),
|
|
167
167
|
`import { connections, workflow } from "./workflow.js";
|
|
168
|
-
import {
|
|
168
|
+
import { events as globalTriggers } from "./events.generated.js";
|
|
169
169
|
const support = connections.slack();
|
|
170
170
|
const oauthConfig = { authorizationEndpoint: "https://example.com/authorize", tokenEndpoint: "https://example.com/token", clientId: "client", clientSecretEnv: "CLIENT_SECRET", scope: "read" };
|
|
171
171
|
workflow({
|
|
172
172
|
name: "Type check", description: "Type check",
|
|
173
173
|
${testCase.connections === undefined ? "" : `connections: ${testCase.connections},`}
|
|
174
|
-
triggers: ({
|
|
174
|
+
triggers: ({ events }) => [${testCase.trigger}],
|
|
175
175
|
handler: ${testCase.handler ?? "() => {}"},
|
|
176
176
|
});`,
|
|
177
177
|
]),
|