@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
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, expect, expectTypeOf, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { events } from "./events.generated.js";
|
|
4
|
+
import { database } from "./notion-as-code/database.js";
|
|
5
|
+
import { finishMetadataRecording, startMetadataRecording } from "./notion-as-code/recorder.js";
|
|
6
|
+
import type { DataSourceHandle } from "./notion-as-code/database.js";
|
|
7
|
+
|
|
8
|
+
function tasks() {
|
|
9
|
+
const recorder = startMetadataRecording();
|
|
10
|
+
const handle = database("tasks-db", {
|
|
11
|
+
dataSourceResourceId: "tasks",
|
|
12
|
+
name: "Tasks",
|
|
13
|
+
schema: {
|
|
14
|
+
Name: { resourceId: "name", type: "title" },
|
|
15
|
+
Status: { resourceId: "status", type: "select", options: [] },
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
finishMetadataRecording(recorder);
|
|
19
|
+
return handle.dataSource;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("configured Apps events", () => {
|
|
23
|
+
it("serializes property handles without including declaration objects", () => {
|
|
24
|
+
const dataSource = tasks();
|
|
25
|
+
expectTypeOf(dataSource.properties.Status.resourceId).toEqualTypeOf<"status">();
|
|
26
|
+
expectTypeOf(dataSource.properties.Status.propertyType).toEqualTypeOf<"select">();
|
|
27
|
+
expect(
|
|
28
|
+
events.notionPageUpdated({ dataSource, properties: [dataSource.properties.Status] }),
|
|
29
|
+
).toEqual({
|
|
30
|
+
type: "notion.page.updated",
|
|
31
|
+
dataSourceResourceId: "tasks",
|
|
32
|
+
propertyResourceIds: ["status"],
|
|
33
|
+
includePageContent: false,
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
it("serializes a page-created data source", () => {
|
|
37
|
+
expect(events.notionPageCreated({ dataSource: tasks() })).toEqual({
|
|
38
|
+
type: "notion.page.created",
|
|
39
|
+
dataSourceResourceId: "tasks",
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
it("supports page-content-only watches", () => {
|
|
43
|
+
expect(events.notionPageUpdated({ dataSource: tasks(), includePageContent: true })).toEqual(
|
|
44
|
+
{
|
|
45
|
+
type: "notion.page.updated",
|
|
46
|
+
dataSourceResourceId: "tasks",
|
|
47
|
+
includePageContent: true,
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
it("checks property ownership even when source types have been widened", () => {
|
|
52
|
+
const original = tasks();
|
|
53
|
+
const dataSource: DataSourceHandle = original;
|
|
54
|
+
const status = original.properties.Status;
|
|
55
|
+
if (!status) throw new Error("Missing Status property");
|
|
56
|
+
const property = { ...status, dataSourceResourceId: "other-source" };
|
|
57
|
+
expect(() => events.notionPageUpdated({ dataSource, properties: [property] })).toThrow(
|
|
58
|
+
"does not belong",
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
it("serializes weekly schedule details", () => {
|
|
62
|
+
expect(
|
|
63
|
+
events.scheduled({
|
|
64
|
+
frequency: "week",
|
|
65
|
+
interval: 1,
|
|
66
|
+
weekdays: { MO: true, FR: true },
|
|
67
|
+
start: "2026-09-23T09:00:00",
|
|
68
|
+
timeZone: "America/Los_Angeles",
|
|
69
|
+
}),
|
|
70
|
+
).toEqual({
|
|
71
|
+
type: "recurrence",
|
|
72
|
+
frequency: "week",
|
|
73
|
+
interval: 1,
|
|
74
|
+
weekdays: { MO: true, FR: true },
|
|
75
|
+
start: "2026-09-23T09:00:00",
|
|
76
|
+
timeZone: "America/Los_Angeles",
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
it("retains unconfigured events for setup in Notion", () => {
|
|
80
|
+
expect([
|
|
81
|
+
events.scheduled(),
|
|
82
|
+
events.notionPageCreated(),
|
|
83
|
+
events.notionPageUpdated(),
|
|
84
|
+
]).toEqual([
|
|
85
|
+
{ type: "recurrence" },
|
|
86
|
+
{ type: "notion.page.created" },
|
|
87
|
+
{ type: "notion.page.updated" },
|
|
88
|
+
]);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { DataSourceHandle, PropertyHandle } from "./notion-as-code/database.js";
|
|
2
|
+
import type { NotionAsCodeSchema } from "./notion-as-code/intents.js";
|
|
3
|
+
import type { RecurrenceSchedule } from "./notion-as-code/recurrence.js";
|
|
4
|
+
|
|
5
|
+
export type RecurrenceTrigger = { type: "recurrence" } & (
|
|
6
|
+
| RecurrenceSchedule
|
|
7
|
+
| { frequency?: never }
|
|
8
|
+
);
|
|
9
|
+
export type NotionPageCreatedTrigger = {
|
|
10
|
+
type: "notion.page.created";
|
|
11
|
+
dataSourceResourceId?: string;
|
|
12
|
+
};
|
|
13
|
+
export type NotionPageUpdatedTrigger = {
|
|
14
|
+
type: "notion.page.updated";
|
|
15
|
+
dataSourceResourceId?: string;
|
|
16
|
+
propertyResourceIds?: string[];
|
|
17
|
+
includePageContent?: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type WatchablePropertyType =
|
|
21
|
+
| "title"
|
|
22
|
+
| "text"
|
|
23
|
+
| "number"
|
|
24
|
+
| "select"
|
|
25
|
+
| "multi_select"
|
|
26
|
+
| "status"
|
|
27
|
+
| "date"
|
|
28
|
+
| "person"
|
|
29
|
+
| "checkbox"
|
|
30
|
+
| "url"
|
|
31
|
+
| "email"
|
|
32
|
+
| "phone_number"
|
|
33
|
+
| "relation";
|
|
34
|
+
|
|
35
|
+
type WatchableProperty<Property extends NotionAsCodeSchema[string]> = Property extends {
|
|
36
|
+
type: WatchablePropertyType;
|
|
37
|
+
}
|
|
38
|
+
? PropertyHandle<Property>
|
|
39
|
+
: never;
|
|
40
|
+
|
|
41
|
+
type SourceProperty<Schema extends NotionAsCodeSchema> = WatchableProperty<Schema[keyof Schema]>;
|
|
42
|
+
export type PageUpdatedOptions<Schema extends NotionAsCodeSchema> = {
|
|
43
|
+
dataSource: DataSourceHandle<Schema>;
|
|
44
|
+
} & (
|
|
45
|
+
| {
|
|
46
|
+
properties: readonly [
|
|
47
|
+
SourceProperty<NoInfer<Schema>>,
|
|
48
|
+
...SourceProperty<NoInfer<Schema>>[],
|
|
49
|
+
];
|
|
50
|
+
includePageContent?: boolean;
|
|
51
|
+
}
|
|
52
|
+
| { properties?: readonly SourceProperty<NoInfer<Schema>>[]; includePageContent: true }
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
/** Configure a recurrence, or omit options to set it up in Notion. */
|
|
56
|
+
export function scheduled(options?: RecurrenceSchedule): RecurrenceTrigger {
|
|
57
|
+
return { type: "recurrence", ...options };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Watch new rows in a declared data source, or configure the source in Notion. */
|
|
61
|
+
export function notionPageCreated(options?: {
|
|
62
|
+
dataSource: DataSourceHandle;
|
|
63
|
+
}): NotionPageCreatedTrigger {
|
|
64
|
+
return {
|
|
65
|
+
type: "notion.page.created",
|
|
66
|
+
...(options ? { dataSourceResourceId: options.dataSource.resourceId } : {}),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Watch typed properties and/or page content in a declared data source. */
|
|
71
|
+
export function notionPageUpdated<const Schema extends NotionAsCodeSchema>(
|
|
72
|
+
options?: PageUpdatedOptions<Schema>,
|
|
73
|
+
): NotionPageUpdatedTrigger {
|
|
74
|
+
if (!options) return { type: "notion.page.updated" };
|
|
75
|
+
const { dataSource, properties = [], includePageContent = false } = options;
|
|
76
|
+
if (properties.length === 0 && !includePageContent) {
|
|
77
|
+
throw new Error("Page-updated events must watch at least one property or page content");
|
|
78
|
+
}
|
|
79
|
+
for (const property of properties) {
|
|
80
|
+
if (
|
|
81
|
+
property.dataSourceResourceId !== dataSource.resourceId ||
|
|
82
|
+
dataSource.properties[property.name]?.resourceId !== property.resourceId
|
|
83
|
+
) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`Property "${property.name}" does not belong to data source "${dataSource.resourceId}"`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
type: "notion.page.updated",
|
|
91
|
+
dataSourceResourceId: dataSource.resourceId,
|
|
92
|
+
...(properties.length
|
|
93
|
+
? {
|
|
94
|
+
propertyResourceIds: [
|
|
95
|
+
...new Set(properties.map((property) => property.resourceId)),
|
|
96
|
+
],
|
|
97
|
+
}
|
|
98
|
+
: {}),
|
|
99
|
+
includePageContent,
|
|
100
|
+
};
|
|
101
|
+
}
|
package/src/connections.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { validateOAuthConfiguration, type OAuthConfiguration } from "./oauth.js";
|
|
2
2
|
import { connectionTriggerDefinitions } from "./connection-trigger-definitions.generated.js";
|
|
3
|
-
import type { WorkflowTrigger } from "./
|
|
3
|
+
import type { WorkflowTrigger } from "./events.generated.js";
|
|
4
4
|
import type { Client } from "@notionhq/client";
|
|
5
5
|
import {
|
|
6
6
|
connections as providerConnections,
|
|
@@ -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
|
@@ -18,7 +18,8 @@ describe("Notion-as-Code custom agent declarations", () => {
|
|
|
18
18
|
resourceId: "triage-agent",
|
|
19
19
|
name: "Triage agent",
|
|
20
20
|
webAccess: true,
|
|
21
|
-
|
|
21
|
+
browserUse: true,
|
|
22
|
+
trustedUrls: ["docs.example.com/*", "support.example.com"],
|
|
22
23
|
access: [access.edit(tasks)],
|
|
23
24
|
triggers: [
|
|
24
25
|
{
|
|
@@ -30,6 +31,11 @@ describe("Notion-as-Code custom agent declarations", () => {
|
|
|
30
31
|
timeZone: "America/Los_Angeles",
|
|
31
32
|
weekdays: { MO: true, FR: true },
|
|
32
33
|
},
|
|
34
|
+
{
|
|
35
|
+
type: "page_added",
|
|
36
|
+
resourceId: "task-added-trigger",
|
|
37
|
+
dataSourceResourceId: "tasks",
|
|
38
|
+
},
|
|
33
39
|
{
|
|
34
40
|
type: "property_updated",
|
|
35
41
|
resourceId: "task-update-trigger",
|
|
@@ -48,7 +54,8 @@ describe("Notion-as-Code custom agent declarations", () => {
|
|
|
48
54
|
resourceId: "triage-agent",
|
|
49
55
|
name: "Triage agent",
|
|
50
56
|
webAccess: true,
|
|
51
|
-
|
|
57
|
+
browserUse: true,
|
|
58
|
+
trustedUrls: { type: "list", urls: ["docs.example.com/*", "support.example.com"] },
|
|
52
59
|
access: [{ resourceId: "tasks-db", level: "edit" }],
|
|
53
60
|
triggers: [
|
|
54
61
|
{
|
|
@@ -60,9 +67,16 @@ describe("Notion-as-Code custom agent declarations", () => {
|
|
|
60
67
|
timeZone: "America/Los_Angeles",
|
|
61
68
|
weekdays: { MO: true, FR: true },
|
|
62
69
|
},
|
|
70
|
+
{
|
|
71
|
+
type: "page_added",
|
|
72
|
+
resourceId: "task-added-trigger",
|
|
73
|
+
dataSourceResourceId: "tasks",
|
|
74
|
+
waitForEditsToFinish: false,
|
|
75
|
+
},
|
|
63
76
|
{
|
|
64
77
|
type: "property_updated",
|
|
65
78
|
resourceId: "task-update-trigger",
|
|
79
|
+
waitForEditsToFinish: false,
|
|
66
80
|
dataSourceResourceId: "tasks",
|
|
67
81
|
propertyConditions: {
|
|
68
82
|
status: { type: "property_edited" },
|
|
@@ -74,6 +88,72 @@ describe("Notion-as-Code custom agent declarations", () => {
|
|
|
74
88
|
]);
|
|
75
89
|
});
|
|
76
90
|
|
|
91
|
+
it("records browser disablement and no trusted URLs", () => {
|
|
92
|
+
const recorder = startMetadataRecording();
|
|
93
|
+
notion.customAgent({
|
|
94
|
+
resourceId: "triage-agent",
|
|
95
|
+
name: "Triage agent",
|
|
96
|
+
browserUse: false,
|
|
97
|
+
trustedUrls: "none",
|
|
98
|
+
});
|
|
99
|
+
expect(finishMetadataRecording(recorder).intents).toEqual([
|
|
100
|
+
{
|
|
101
|
+
type: "custom_agent",
|
|
102
|
+
resourceId: "triage-agent",
|
|
103
|
+
name: "Triage agent",
|
|
104
|
+
browserUse: false,
|
|
105
|
+
trustedUrls: { type: "list", urls: [] },
|
|
106
|
+
},
|
|
107
|
+
]);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("records unrestricted trust and an explicit wait for edits", () => {
|
|
111
|
+
const recorder = startMetadataRecording();
|
|
112
|
+
notion.customAgent({
|
|
113
|
+
resourceId: "triage-agent",
|
|
114
|
+
name: "Triage agent",
|
|
115
|
+
trustedUrls: "all",
|
|
116
|
+
triggers: [
|
|
117
|
+
{
|
|
118
|
+
type: "page_added",
|
|
119
|
+
resourceId: "task-added-trigger",
|
|
120
|
+
dataSourceResourceId: "tasks",
|
|
121
|
+
waitForEditsToFinish: true,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
type: "property_updated",
|
|
125
|
+
resourceId: "task-update-trigger",
|
|
126
|
+
dataSourceResourceId: "tasks",
|
|
127
|
+
triggerWhenPageContentEdited: true,
|
|
128
|
+
waitForEditsToFinish: true,
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
});
|
|
132
|
+
expect(finishMetadataRecording(recorder).intents).toEqual([
|
|
133
|
+
{
|
|
134
|
+
type: "custom_agent",
|
|
135
|
+
resourceId: "triage-agent",
|
|
136
|
+
name: "Triage agent",
|
|
137
|
+
trustedUrls: { type: "all" },
|
|
138
|
+
triggers: [
|
|
139
|
+
{
|
|
140
|
+
type: "page_added",
|
|
141
|
+
resourceId: "task-added-trigger",
|
|
142
|
+
dataSourceResourceId: "tasks",
|
|
143
|
+
waitForEditsToFinish: true,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
type: "property_updated",
|
|
147
|
+
resourceId: "task-update-trigger",
|
|
148
|
+
dataSourceResourceId: "tasks",
|
|
149
|
+
triggerWhenPageContentEdited: true,
|
|
150
|
+
waitForEditsToFinish: true,
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
]);
|
|
155
|
+
});
|
|
156
|
+
|
|
77
157
|
it("rejects reserved resource IDs inside triggers", () => {
|
|
78
158
|
expect(() =>
|
|
79
159
|
notion.customAgent({
|