@notionhq/apps 0.0.6 → 0.0.9
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 +24 -1
- package/dist/context.js +2 -2
- package/dist/workflow.d.ts +14 -0
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +18 -1
- package/package.json +1 -1
- package/src/context.ts +6 -6
- package/src/workflow.test.ts +25 -1
- package/src/workflow.ts +32 -0
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Apps SDK
|
|
2
2
|
|
|
3
|
+
[](https://github.com/makenotion/apps-sdk/actions/workflows/ci.yml?query=branch%3Amain)
|
|
4
|
+
[](https://github.com/makenotion/apps-sdk/actions/workflows/publish.yml?query=branch%3Amain)
|
|
5
|
+
|
|
3
6
|
## Development
|
|
4
7
|
|
|
5
8
|
1. Install [mise](https://mise.jdx.dev/getting-started.html)
|
|
@@ -11,9 +14,29 @@ The SDK requires Node 26 or newer. It supports workflows and database syncs.
|
|
|
11
14
|
|
|
12
15
|
```ts
|
|
13
16
|
import { triggers } from "@notionhq/apps/triggers";
|
|
14
|
-
import { createWorkflow } from "@notionhq/apps/workflow";
|
|
17
|
+
import { connections, createWorkflow } from "@notionhq/apps/workflow";
|
|
15
18
|
```
|
|
16
19
|
|
|
20
|
+
Add a connection requirement to a workflow when it needs Calendar:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
export default createWorkflow({
|
|
24
|
+
name: "Schedule follow-up",
|
|
25
|
+
description: "Schedules a follow-up after a page is created",
|
|
26
|
+
triggers: [triggers.notionPageCreated()],
|
|
27
|
+
connections: [connections.calendar()],
|
|
28
|
+
handler: async () => {},
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Declaring the same connection more than once causes the app build to fail.
|
|
33
|
+
|
|
34
|
+
## Notion API access
|
|
35
|
+
|
|
36
|
+
Use `context.notion` for Notion API calls. Deployed apps receive Notion API
|
|
37
|
+
credentials automatically, so do not configure or push `NOTION_API_TOKEN`.
|
|
38
|
+
Local execution needs the token in `.env` before making a Notion API request.
|
|
39
|
+
|
|
17
40
|
## Database syncs
|
|
18
41
|
|
|
19
42
|
Apps declare a database schema, and users attach a real Notion database to it. Apps SDK
|
package/dist/context.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Client } from "@notionhq/client";
|
|
2
|
-
const
|
|
2
|
+
const LOCAL_FIX_STEPS = "\n\nFor local execution:\n1. Create a personal access token at https://app.notion.com/developers/tokens\n2. Add NOTION_API_TOKEN=<your-token> to your .env file\n\nDeployed apps receive Notion API credentials automatically. Do not push NOTION_API_TOKEN.";
|
|
3
3
|
function missingTokenMessage() {
|
|
4
|
-
return "NOTION_API_TOKEN is not set. context.notion requires an API token
|
|
4
|
+
return "NOTION_API_TOKEN is not set. context.notion requires an API token when an app capability runs locally." + LOCAL_FIX_STEPS;
|
|
5
5
|
}
|
|
6
6
|
function createUnauthenticatedNotionProxy() {
|
|
7
7
|
return new Proxy({}, {
|
package/dist/workflow.d.ts
CHANGED
|
@@ -8,6 +8,15 @@ type HandlerOptions = {
|
|
|
8
8
|
export type WorkflowEvent = WorkflowEventMap[keyof WorkflowEventMap];
|
|
9
9
|
export type WorkflowEventForTrigger<T extends WorkflowTrigger> = WorkflowEventMap[T["type"]];
|
|
10
10
|
export type WorkflowEventForTriggers<T extends readonly WorkflowTrigger[]> = WorkflowEventForTrigger<T[number]>;
|
|
11
|
+
/** A connection that a workflow needs before it can run. */
|
|
12
|
+
export type WorkflowConnection = {
|
|
13
|
+
type: "calendar";
|
|
14
|
+
};
|
|
15
|
+
/** Connection requirements supported by workflow capabilities. */
|
|
16
|
+
export declare const connections: {
|
|
17
|
+
/** Require an authorized Calendar connection. */
|
|
18
|
+
calendar(): WorkflowConnection;
|
|
19
|
+
};
|
|
11
20
|
/**
|
|
12
21
|
* Configuration passed to {@link createWorkflow}.
|
|
13
22
|
*/
|
|
@@ -26,6 +35,10 @@ export type WorkflowConfiguration<TTriggers extends readonly [WorkflowTrigger, .
|
|
|
26
35
|
* Each trigger defines a specific event or condition that causes the workflow to run.
|
|
27
36
|
*/
|
|
28
37
|
triggers: TTriggers;
|
|
38
|
+
/**
|
|
39
|
+
* Connections that must be set up before this workflow can run.
|
|
40
|
+
*/
|
|
41
|
+
connections?: readonly WorkflowConnection[];
|
|
29
42
|
handler: (event: WorkflowEventForTriggers<TTriggers>, context: WorkflowContext) => Promise<void> | void;
|
|
30
43
|
};
|
|
31
44
|
/**
|
|
@@ -43,6 +56,7 @@ export type Workflow<TTriggers extends readonly [WorkflowTrigger, ...WorkflowTri
|
|
|
43
56
|
name: string;
|
|
44
57
|
description: string;
|
|
45
58
|
triggers: TTriggers;
|
|
59
|
+
connections?: readonly WorkflowConnection[];
|
|
46
60
|
};
|
|
47
61
|
handler: (event: WorkflowEventForTriggers<TTriggers>, options?: HandlerOptions) => Promise<{
|
|
48
62
|
status: "success";
|
package/dist/workflow.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../src/workflow.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAKtD,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAEjF,KAAK,cAAc,GAAG;IACrB,yEAAyE;IACzE,cAAc,CAAC,EAAE,IAAI,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,gBAAgB,CAAC,MAAM,gBAAgB,CAAC,CAAC;AAErE,MAAM,MAAM,uBAAuB,CAAC,CAAC,SAAS,eAAe,IAAI,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAE7F,MAAM,MAAM,wBAAwB,CAAC,CAAC,SAAS,SAAS,eAAe,EAAE,IACxE,uBAAuB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAEpC;;GAEG;AACH,MAAM,MAAM,qBAAqB,CAChC,SAAS,SAAS,SAAS,CAAC,eAAe,EAAE,GAAG,eAAe,EAAE,CAAC,IAC/D;IACH;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB,OAAO,EAAE,CACR,KAAK,EAAE,wBAAwB,CAAC,SAAS,CAAC,EAC1C,OAAO,EAAE,eAAe,KACpB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,CACnB,SAAS,SAAS,SAAS,CAAC,eAAe,EAAE,GAAG,eAAe,EAAE,CAAC,GAAG,SAAS;IAC7E,eAAe;IACf,GAAG,eAAe,EAAE;CACpB,IACE;IACH,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../src/workflow.ts"],"names":[],"mappings":"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"}
|
package/dist/workflow.js
CHANGED
|
@@ -6,13 +6,29 @@ import { ExecutionError } from "./error.js";
|
|
|
6
6
|
import { writeOutput } from "./output.js";
|
|
7
7
|
import { resolveRuntimeInput } from "./runtime-input.js";
|
|
8
8
|
import { readRunMetadata } from "./runtime-metadata.js";
|
|
9
|
+
const connections = {
|
|
10
|
+
/** Require an authorized Calendar connection. */
|
|
11
|
+
calendar() {
|
|
12
|
+
return { type: "calendar" };
|
|
13
|
+
}
|
|
14
|
+
};
|
|
9
15
|
function createWorkflow(configuration) {
|
|
16
|
+
if (configuration.connections !== void 0) {
|
|
17
|
+
const connectionTypes = /* @__PURE__ */ new Set();
|
|
18
|
+
for (const connection of configuration.connections) {
|
|
19
|
+
if (connectionTypes.has(connection.type)) {
|
|
20
|
+
throw new Error(`Duplicate workflow connection: ${connection.type}`);
|
|
21
|
+
}
|
|
22
|
+
connectionTypes.add(connection.type);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
10
25
|
return {
|
|
11
26
|
_tag: "workflow",
|
|
12
27
|
config: {
|
|
13
28
|
name: configuration.name,
|
|
14
29
|
description: configuration.description,
|
|
15
|
-
triggers: configuration.triggers
|
|
30
|
+
triggers: configuration.triggers,
|
|
31
|
+
...configuration.connections === void 0 ? {} : { connections: configuration.connections }
|
|
16
32
|
},
|
|
17
33
|
async handler(event, options) {
|
|
18
34
|
try {
|
|
@@ -208,5 +224,6 @@ function isNodeError(error) {
|
|
|
208
224
|
return error instanceof Error && "code" in error;
|
|
209
225
|
}
|
|
210
226
|
export {
|
|
227
|
+
connections,
|
|
211
228
|
createWorkflow
|
|
212
229
|
};
|
package/package.json
CHANGED
package/src/context.ts
CHANGED
|
@@ -6,17 +6,17 @@ export type CapabilityContext = {
|
|
|
6
6
|
notion: Client;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
const
|
|
10
|
-
"\n\
|
|
9
|
+
const LOCAL_FIX_STEPS =
|
|
10
|
+
"\n\nFor local execution:\n" +
|
|
11
11
|
"1. Create a personal access token at https://app.notion.com/developers/tokens\n" +
|
|
12
|
-
"2. Add NOTION_API_TOKEN=<your-token> to your .env file\n" +
|
|
13
|
-
"
|
|
12
|
+
"2. Add NOTION_API_TOKEN=<your-token> to your .env file\n\n" +
|
|
13
|
+
"Deployed apps receive Notion API credentials automatically. Do not push NOTION_API_TOKEN.";
|
|
14
14
|
|
|
15
15
|
function missingTokenMessage(): string {
|
|
16
16
|
return (
|
|
17
17
|
"NOTION_API_TOKEN is not set. " +
|
|
18
|
-
"context.notion requires an API token
|
|
19
|
-
|
|
18
|
+
"context.notion requires an API token when an app capability runs locally." +
|
|
19
|
+
LOCAL_FIX_STEPS
|
|
20
20
|
);
|
|
21
21
|
}
|
|
22
22
|
|
package/src/workflow.test.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { afterEach, beforeEach, describe, expect, expectTypeOf, it, vi, type Moc
|
|
|
8
8
|
import { ExecutionError } from "./error.js";
|
|
9
9
|
import type { RunMetadata } from "./runtime-metadata.js";
|
|
10
10
|
import { triggers, type NotionPageCreatedEvent } from "./triggers.generated.js";
|
|
11
|
-
import { createWorkflow, type StepContext, type WorkflowContext } from "./workflow.js";
|
|
11
|
+
import { connections, createWorkflow, type StepContext, type WorkflowContext } from "./workflow.js";
|
|
12
12
|
|
|
13
13
|
let stdoutSpy: Mock<typeof process.stdout.write>;
|
|
14
14
|
let checkpointDirectory: string | undefined;
|
|
@@ -738,6 +738,30 @@ describe("createWorkflow", () => {
|
|
|
738
738
|
});
|
|
739
739
|
});
|
|
740
740
|
|
|
741
|
+
it("declares a Calendar connection requirement", () => {
|
|
742
|
+
const workflow = createWorkflow({
|
|
743
|
+
name: "Schedule Follow-up",
|
|
744
|
+
description: "Schedules a follow-up after a page is created",
|
|
745
|
+
triggers: [triggers.notionPageCreated()],
|
|
746
|
+
connections: [connections.calendar()],
|
|
747
|
+
handler: () => {},
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
expect(workflow.config.connections).toEqual([{ type: "calendar" }]);
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
it("rejects duplicate connection requirements", () => {
|
|
754
|
+
expect(() =>
|
|
755
|
+
createWorkflow({
|
|
756
|
+
name: "Schedule Follow-up",
|
|
757
|
+
description: "Schedules a follow-up after a page is created",
|
|
758
|
+
triggers: [triggers.notionPageCreated()],
|
|
759
|
+
connections: [connections.calendar(), connections.calendar()],
|
|
760
|
+
handler: () => {},
|
|
761
|
+
}),
|
|
762
|
+
).toThrowError("Duplicate workflow connection: calendar");
|
|
763
|
+
});
|
|
764
|
+
|
|
741
765
|
it("writes a success envelope after the handler runs", async () => {
|
|
742
766
|
const handler = vi.fn();
|
|
743
767
|
const workflow = createWorkflow({
|
package/src/workflow.ts
CHANGED
|
@@ -22,6 +22,19 @@ export type WorkflowEventForTrigger<T extends WorkflowTrigger> = WorkflowEventMa
|
|
|
22
22
|
export type WorkflowEventForTriggers<T extends readonly WorkflowTrigger[]> =
|
|
23
23
|
WorkflowEventForTrigger<T[number]>;
|
|
24
24
|
|
|
25
|
+
/** A connection that a workflow needs before it can run. */
|
|
26
|
+
export type WorkflowConnection = {
|
|
27
|
+
type: "calendar";
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Connection requirements supported by workflow capabilities. */
|
|
31
|
+
export const connections = {
|
|
32
|
+
/** Require an authorized Calendar connection. */
|
|
33
|
+
calendar(): WorkflowConnection {
|
|
34
|
+
return { type: "calendar" };
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
25
38
|
/**
|
|
26
39
|
* Configuration passed to {@link createWorkflow}.
|
|
27
40
|
*/
|
|
@@ -45,6 +58,11 @@ export type WorkflowConfiguration<
|
|
|
45
58
|
*/
|
|
46
59
|
triggers: TTriggers;
|
|
47
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Connections that must be set up before this workflow can run.
|
|
63
|
+
*/
|
|
64
|
+
connections?: readonly WorkflowConnection[];
|
|
65
|
+
|
|
48
66
|
handler: (
|
|
49
67
|
event: WorkflowEventForTriggers<TTriggers>,
|
|
50
68
|
context: WorkflowContext,
|
|
@@ -68,6 +86,7 @@ export type Workflow<
|
|
|
68
86
|
name: string;
|
|
69
87
|
description: string;
|
|
70
88
|
triggers: TTriggers;
|
|
89
|
+
connections?: readonly WorkflowConnection[];
|
|
71
90
|
};
|
|
72
91
|
handler: (
|
|
73
92
|
event: WorkflowEventForTriggers<TTriggers>,
|
|
@@ -104,12 +123,25 @@ export type Workflow<
|
|
|
104
123
|
export function createWorkflow<
|
|
105
124
|
const TTriggers extends readonly [WorkflowTrigger, ...WorkflowTrigger[]],
|
|
106
125
|
>(configuration: WorkflowConfiguration<TTriggers>): Workflow<TTriggers> {
|
|
126
|
+
if (configuration.connections !== undefined) {
|
|
127
|
+
const connectionTypes = new Set<string>();
|
|
128
|
+
for (const connection of configuration.connections) {
|
|
129
|
+
if (connectionTypes.has(connection.type)) {
|
|
130
|
+
throw new Error(`Duplicate workflow connection: ${connection.type}`);
|
|
131
|
+
}
|
|
132
|
+
connectionTypes.add(connection.type);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
107
136
|
return {
|
|
108
137
|
_tag: "workflow",
|
|
109
138
|
config: {
|
|
110
139
|
name: configuration.name,
|
|
111
140
|
description: configuration.description,
|
|
112
141
|
triggers: configuration.triggers,
|
|
142
|
+
...(configuration.connections === undefined
|
|
143
|
+
? {}
|
|
144
|
+
: { connections: configuration.connections }),
|
|
113
145
|
},
|
|
114
146
|
async handler(
|
|
115
147
|
event: WorkflowEventForTriggers<TTriggers>,
|