@notionhq/apps 0.0.16 → 0.0.18

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.
Files changed (77) hide show
  1. package/AGENTS.md +27 -0
  2. package/README.md +86 -36
  3. package/dist/connections.d.ts +39 -11
  4. package/dist/connections.d.ts.map +1 -1
  5. package/dist/connections.js +85 -21
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +2 -0
  9. package/dist/notion-as-code/database.d.ts +89 -48
  10. package/dist/notion-as-code/database.d.ts.map +1 -1
  11. package/dist/notion-as-code/database.js +132 -45
  12. package/dist/notion-as-code/database.test.d.ts +2 -0
  13. package/dist/notion-as-code/database.test.d.ts.map +1 -0
  14. package/dist/notion-as-code/handles.d.ts +2 -0
  15. package/dist/notion-as-code/handles.d.ts.map +1 -1
  16. package/dist/notion-as-code/handles.js +2 -0
  17. package/dist/notion-as-code/index.d.ts +5 -2
  18. package/dist/notion-as-code/index.d.ts.map +1 -1
  19. package/dist/notion-as-code/intents.d.ts +36 -3
  20. package/dist/notion-as-code/intents.d.ts.map +1 -1
  21. package/dist/notion-as-code/schema.d.ts +8 -4
  22. package/dist/notion-as-code/schema.d.ts.map +1 -1
  23. package/dist/notion-as-code/view.d.ts +12 -0
  24. package/dist/notion-as-code/view.d.ts.map +1 -0
  25. package/dist/notion-as-code/view.js +13 -0
  26. package/dist/notion-as-code/views-types.test.d.ts +2 -0
  27. package/dist/notion-as-code/views-types.test.d.ts.map +1 -0
  28. package/dist/notion-as-code/views.d.ts +488 -0
  29. package/dist/notion-as-code/views.d.ts.map +1 -0
  30. package/dist/notion-as-code/views.js +0 -0
  31. package/dist/oauth.d.ts +11 -0
  32. package/dist/oauth.d.ts.map +1 -0
  33. package/dist/oauth.js +26 -0
  34. package/dist/providers.generated.d.ts +26 -86
  35. package/dist/providers.generated.d.ts.map +1 -1
  36. package/dist/providers.generated.js +54 -66
  37. package/dist/sync.d.ts +33 -15
  38. package/dist/sync.d.ts.map +1 -1
  39. package/dist/sync.js +12 -0
  40. package/dist/triggers.generated.d.ts +3 -3
  41. package/dist/triggers.generated.d.ts.map +1 -1
  42. package/dist/workflow-state.d.ts +13 -0
  43. package/dist/workflow-state.d.ts.map +1 -0
  44. package/dist/workflow-state.js +107 -0
  45. package/dist/workflow.d.ts +56 -11
  46. package/dist/workflow.d.ts.map +1 -1
  47. package/dist/workflow.js +123 -10
  48. package/docs/BUILD.md +96 -0
  49. package/docs/CONNECTIONS.md +70 -22
  50. package/package.json +1 -1
  51. package/skills/connections/SKILL.md +7 -8
  52. package/skills/notion-as-code/SKILL.md +88 -50
  53. package/skills/sync/SKILL.md +21 -18
  54. package/skills/workflow/SKILL.md +52 -3
  55. package/src/cli/build.test.ts +124 -64
  56. package/src/connections.test.ts +205 -41
  57. package/src/connections.ts +144 -38
  58. package/src/index.ts +2 -0
  59. package/src/notion-as-code/database.test.ts +661 -0
  60. package/src/notion-as-code/database.ts +346 -129
  61. package/src/notion-as-code/handles.ts +2 -0
  62. package/src/notion-as-code/index.ts +71 -1
  63. package/src/notion-as-code/intents.ts +41 -4
  64. package/src/notion-as-code/schema.ts +11 -3
  65. package/src/notion-as-code/view.ts +23 -0
  66. package/src/notion-as-code/views-types.test.ts +59 -0
  67. package/src/notion-as-code/views.ts +573 -0
  68. package/src/oauth.ts +40 -0
  69. package/src/providers.generated.ts +68 -163
  70. package/src/sync.test.ts +295 -0
  71. package/src/sync.ts +85 -21
  72. package/src/triggers.generated.ts +4 -4
  73. package/src/workflow-connections-types.test.ts +16 -10
  74. package/src/workflow-state.ts +152 -0
  75. package/src/workflow-types.test.ts +88 -16
  76. package/src/workflow.test.ts +374 -18
  77. package/src/workflow.ts +211 -23
@@ -1 +1 @@
1
- {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../src/workflow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../src/workflow.ts"],"names":[],"mappings":"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"}
package/dist/workflow.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createWorkflowConnections,
3
- validateConnectionRequirements,
3
+ normalizeConnectionRequirements,
4
4
  validateTriggerConnections
5
5
  } from "./connections.js";
6
6
  import { createHash } from "node:crypto";
@@ -12,29 +12,39 @@ import { writeOutput } from "./output.js";
12
12
  import { resolveRuntimeInput } from "./runtime-input.js";
13
13
  import { readRunMetadata } from "./runtime-metadata.js";
14
14
  import { createWorkflowTriggers } from "./triggers.generated.js";
15
- import { connections } from "./connections.js";
15
+ import { createWorkflowStepState } from "./workflow-state.js";
16
+ const MAX_WORKFLOW_WAIT_MS = 7 * 24 * 60 * 60 * 1e3;
17
+ import {
18
+ connections
19
+ } from "./connections.js";
16
20
  function workflow(configuration) {
21
+ const requirements = normalizeConnectionRequirements(configuration.connections ?? {});
22
+ const connectionDeclarations = configuration.connections === void 0 ? void 0 : structuredClone(configuration.connections);
17
23
  const triggers = typeof configuration.triggers === "function" ? configuration.triggers({ triggers: createWorkflowTriggers() }) : configuration.triggers;
18
- validateConnectionRequirements(configuration.connections ?? []);
19
- validateTriggerConnections(triggers, configuration.connections ?? []);
24
+ validateTriggerConnections(triggers, requirements);
20
25
  return {
21
26
  _tag: "workflow",
22
27
  config: {
23
28
  name: configuration.name,
24
29
  description: configuration.description,
25
30
  triggers,
26
- ...configuration.connections === void 0 ? {} : { connections: configuration.connections }
31
+ ...configuration.connections === void 0 ? {} : { connections: requirements }
27
32
  },
28
33
  async handler(event, options) {
29
34
  try {
30
35
  event = await resolveRuntimeInput(event);
31
36
  const runMetadata = readRunMetadata();
32
37
  const baseContext = createCapabilityContext();
38
+ const step = createStep(runMetadata);
33
39
  const capabilityContext = {
34
40
  ...baseContext,
35
- connections: createWorkflowConnections(baseContext.notion),
41
+ connections: createWorkflowConnections(
42
+ baseContext.notion,
43
+ connectionDeclarations
44
+ ),
36
45
  ...runMetadata,
37
- step: createStep(runMetadata)
46
+ step,
47
+ wait: createWait(step)
38
48
  };
39
49
  await configuration.handler(event, capabilityContext);
40
50
  if (options?.concreteOutput) {
@@ -42,6 +52,16 @@ function workflow(configuration) {
42
52
  }
43
53
  writeOutput({ _tag: "success", value: { status: "success" } });
44
54
  } catch (err) {
55
+ if (err instanceof WorkflowWaitInterrupt) {
56
+ if (options?.concreteOutput) {
57
+ return err.result;
58
+ }
59
+ writeOutput({
60
+ _tag: "wait",
61
+ wait: err.result.wait
62
+ });
63
+ return;
64
+ }
45
65
  const error = new ExecutionError(err);
46
66
  if (!options?.concreteOutput) {
47
67
  writeOutput({
@@ -75,7 +95,8 @@ function writeStepEvent(event, value, startedAt, emittedAt = performance.now())
75
95
  `
76
96
  );
77
97
  }
78
- function createStep({ runGroupId }) {
98
+ function createStep(metadata) {
99
+ const { runGroupId } = metadata;
79
100
  const stepNameByKey = /* @__PURE__ */ new Map();
80
101
  async function step(name, optionsOrFn, maybeFn) {
81
102
  const options = typeof optionsOrFn === "function" ? void 0 : optionsOrFn;
@@ -92,7 +113,9 @@ function createStep({ runGroupId }) {
92
113
  );
93
114
  }
94
115
  stepNameByKey.set(key, name);
95
- const context = { id: `${runGroupId}:${key}` };
116
+ const id = `${runGroupId}:${key}`;
117
+ const stepState = createWorkflowStepState(metadata, id);
118
+ const context = { id, state: stepState.state };
96
119
  const event = { id: context.id, name, key };
97
120
  const startedAt = performance.now();
98
121
  writeStepEvent("started", event, startedAt, startedAt);
@@ -118,7 +141,7 @@ function createStep({ runGroupId }) {
118
141
  );
119
142
  return checkpoint.value;
120
143
  }
121
- const value = normalizeWorkflowStepResult(await fn(context));
144
+ const value = normalizeWorkflowStepResult(await stepState.run(() => fn(context)));
122
145
  writeStepEvent("success", { ...event, value }, startedAt);
123
146
  writeStepEvent(
124
147
  "completed",
@@ -156,6 +179,96 @@ function createStep({ runGroupId }) {
156
179
  }
157
180
  return step;
158
181
  }
182
+ class WorkflowWaitInterrupt extends Error {
183
+ constructor(result) {
184
+ super(`Workflow is waiting until ${new Date(result.wait.resumeAtMs).toISOString()}`);
185
+ this.result = result;
186
+ this.name = "WorkflowWaitInterrupt";
187
+ }
188
+ result;
189
+ }
190
+ function createWait(step) {
191
+ return {
192
+ async until(name, options) {
193
+ const keyInput = options.key ?? name;
194
+ const keySegments = [
195
+ "wait",
196
+ "until",
197
+ ...typeof keyInput === "string" ? [keyInput] : keyInput
198
+ ];
199
+ const resumeAtMs = await step(
200
+ name,
201
+ { key: keySegments },
202
+ () => resolveWaitUntilMs(options)
203
+ );
204
+ if (Date.now() >= resumeAtMs) {
205
+ return;
206
+ }
207
+ throw new WorkflowWaitInterrupt({
208
+ status: "waiting",
209
+ wait: {
210
+ type: "until",
211
+ name,
212
+ resumeAtMs
213
+ }
214
+ });
215
+ }
216
+ };
217
+ }
218
+ function resolveWaitUntilMs(options) {
219
+ const now = Date.now();
220
+ if ("at" in options) {
221
+ const resumeAtMs = options.at.getTime();
222
+ if (!Number.isFinite(resumeAtMs)) {
223
+ throw new Error("Workflow wait date must be valid.");
224
+ }
225
+ assertWaitFitsCheckpointRetention(resumeAtMs - now);
226
+ return resumeAtMs;
227
+ }
228
+ const durationMs = workflowWaitDurationMs(options.after);
229
+ assertWaitFitsCheckpointRetention(durationMs);
230
+ return now + durationMs;
231
+ }
232
+ function workflowWaitDurationMs(duration) {
233
+ const entries = Object.entries(duration);
234
+ if (entries.length === 0) {
235
+ throw new Error("Workflow wait duration must specify at least one unit.");
236
+ }
237
+ let durationMs = 0;
238
+ for (const [unit, value] of entries) {
239
+ if (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
240
+ throw new Error("Workflow wait duration values must be positive finite integers.");
241
+ }
242
+ durationMs += value * waitDurationUnitMultiplier(unit);
243
+ if (!Number.isSafeInteger(durationMs)) {
244
+ throw new Error("Workflow wait duration is too large.");
245
+ }
246
+ }
247
+ return durationMs;
248
+ }
249
+ function assertWaitFitsCheckpointRetention(durationMs) {
250
+ if (durationMs >= MAX_WORKFLOW_WAIT_MS) {
251
+ throw new Error("Workflow waits must be shorter than 7 days.");
252
+ }
253
+ }
254
+ function waitDurationUnitMultiplier(unit) {
255
+ switch (unit) {
256
+ case "milliseconds":
257
+ return 1;
258
+ case "seconds":
259
+ return 1e3;
260
+ case "minutes":
261
+ return 6e4;
262
+ case "hours":
263
+ return 36e5;
264
+ case "days":
265
+ return 864e5;
266
+ case "weeks":
267
+ return 6048e5;
268
+ default:
269
+ throw new Error(`Unsupported workflow wait duration unit: ${unit}`);
270
+ }
271
+ }
159
272
  function createWorkflowStepKey(segments) {
160
273
  if (segments.length === 0) {
161
274
  throw new Error("Workflow step keys must contain at least one segment.");
package/docs/BUILD.md CHANGED
@@ -20,6 +20,7 @@ corresponding capability. The filename becomes its key:
20
20
  ```text
21
21
  my-app/
22
22
  ├── src/
23
+ │ ├── notion.ts
23
24
  │ ├── workflows/
24
25
  │ │ └── onPageCreated.ts
25
26
  │ └── lib/
@@ -32,6 +33,10 @@ my-app/
32
33
 
33
34
  Files elsewhere under `src/` are ordinary modules and enter the build only when imported
34
35
  by a capability. Capability discovery does not recurse into subdirectories.
36
+ Notion-as-Code declarations may be inline or in an imported helper. Use a local
37
+ module such as `src/workflows/myWorkflow/lib/notion.ts` for resources owned by one
38
+ capability, and `src/notion.ts` for app-wide declarations or resources without a
39
+ natural capability owner.
35
40
 
36
41
  ## Pipeline
37
42
 
@@ -47,6 +52,97 @@ Capability modules must therefore be importable without secrets or network acces
47
52
  Read required environment variables and make requests inside handlers or workflow
48
53
  steps, not at module scope.
49
54
 
55
+ ## Declaring Notion-as-Code databases
56
+
57
+ Database declarations use explicit resource IDs and keyed schemas. For a single
58
+ data source, supply the database ID as the first argument and the distinct data
59
+ source ID in `dataSourceResourceId`:
60
+
61
+ ```ts
62
+ import { notion } from "@notionhq/apps/notion-as-code";
63
+
64
+ const tasks = notion.database("tasks-db", {
65
+ dataSourceResourceId: "tasks-source",
66
+ name: "Tasks",
67
+ schema: {
68
+ Name: { type: "title", resourceId: "tasks-name" },
69
+ Effort: { type: "text", resourceId: "tasks-effort" },
70
+ },
71
+ });
72
+ ```
73
+
74
+ Here `name` is the single-source shorthand: it supplies both the database and
75
+ data-source display names. The source handle is `tasks.dataSource`; pass it to
76
+ `sync({ dataSource, ... })` from `@notionhq/apps`.
77
+
78
+ For multiple sources, use `datasources` instead of the top-level `schema`:
79
+
80
+ ````ts
81
+ const work = notion.database("work-db", {
82
+ name: "Work",
83
+ datasources: {
84
+ Tasks: {
85
+ resourceId: "work-tasks-source",
86
+ schema: {
87
+ Name: { type: "title", resourceId: "work-tasks-name" },
88
+ Effort: { type: "text", resourceId: "work-tasks-effort" },
89
+ },
90
+ },
91
+ Projects: {
92
+ resourceId: "work-projects-source",
93
+ schema: {
94
+ Name: { type: "title", resourceId: "work-projects-name" },
95
+ },
96
+ },
97
+ },
98
+ });
99
+
100
+ The keys in `datasources` are the source names, so the handles are indexed by
101
+ those names: `work.datasources.Tasks` and `work.datasources.Projects`. Nested
102
+ data-source configs do not accept `name`. The top-level database `name` remains
103
+ supported and names the database; in a single-source declaration it also
104
+ supplies the source display name. Property configs do not accept `name`; each
105
+ schema key is always the property's name. Typed property access uses
106
+ `work.datasources.Tasks.schema.Effort`; sync primary keys and row properties use
107
+ the string/object key `"Effort"`.
108
+
109
+ Every database, source, and property ID remains explicit and must be unique
110
+ across the provisioning declarations. No IDs are generated from keys or names.
111
+ Preserve explicit IDs when changing authoring keys or display names.
112
+
113
+ Page and teamspace handles accept the same forms through
114
+ `page.addDatabase("database-id", args)` and `teamspace.addDatabase("database-id", args)`.
115
+ Top-level declarations also accept
116
+ `parent: { type: "resourceId", resourceId: "parent-id" }`; omitting it retains the
117
+ private Apps workspace parent. A database must declare at least one data source
118
+ or at least one view. A linked-only declaration may omit `datasources`, or use
119
+ `datasources: {}`, only with a nonempty `views` list. The empty forms `{}`,
120
+ `{ datasources: {} }`, and `{ datasources: {}, views: [] }` are rejected; widened
121
+ or dynamic maps and arrays are checked at runtime. The single-source `schema` and
122
+ multi-source `datasources` forms cannot be combined.
123
+
124
+ Here is a linked-only database whose typed table view references an explicit
125
+ data source resource ID:
126
+
127
+ ```ts
128
+ const linked = notion.database("work-linked-db", {
129
+ views: [
130
+ {
131
+ resourceId: "work-issues-table",
132
+ type: "table",
133
+ dataSourceResourceId: "issues-source",
134
+ properties: [{ property: "issue-title", visible: true }],
135
+ },
136
+ ],
137
+ });
138
+ ````
139
+
140
+ The build converts these declarations to the existing serialized database
141
+ intents: `dataSources` and `properties` remain arrays with exact resource IDs
142
+ and names from the authoring keys. Authoring-only `schema`, `datasources`, and
143
+ database-level `dataSourceResourceId` fields do not leak into the serialized
144
+ database intent. The provisioning JSON envelope and server API are unchanged.
145
+
50
146
  ## Provisioning artifact and deployment
51
147
 
52
148
  The optional provisioning artifact uses `$schema: "notion:apps-provisioning:v1"`,
@@ -11,15 +11,15 @@ export default workflow({
11
11
  name: "List work calendars",
12
12
  description: "List calendars available through the work connection",
13
13
  triggers: [triggers.notionPageCreated()],
14
- connections: [connections.calendar({ key: "work" })],
14
+ connections: { work: connections.calendar() },
15
15
  handler: async (_event, context) => {
16
- const calendars = await context.connections.calendar("work").listCalendars({});
16
+ const calendars = await context.connections.work.listCalendars({});
17
17
  console.log(calendars.accounts);
18
18
  },
19
19
  });
20
20
  ```
21
21
 
22
- An omitted key defaults to the provider type, so `connections.calendar()` is accessed through `context.connections.calendar()`. Keys must be unique, begin with a letter, and contain at most 128 letters, numbers, underscores, or hyphens. A workflow can declare up to 100 requirements.
22
+ The object property name is the connection key. Provider factories do not take a key option. Keys must begin with a letter and contain at most 128 letters, numbers, underscores, or hyphens; `constructor` and `prototype` are reserved. A workflow can declare up to 100 connections.
23
23
 
24
24
  ## Trigger a workflow from a connection
25
25
 
@@ -29,25 +29,25 @@ Use a trigger callback to check connection keys against the workflow’s declare
29
29
  export default workflow({
30
30
  name: "Support messages",
31
31
  description: "Run when a message arrives in the configured support channel",
32
- connections: [connections.slack({ key: "support" })],
32
+ connections: { support: connections.slack() },
33
33
  triggers: ({ triggers }) => [triggers.slackMessage({ connectionKey: "support" })],
34
34
  handler: async (event, context) => {
35
35
  console.log(event);
36
- const user = await context.connections
37
- .slack("support")
38
- .findUserByEmail({ email: "person@example.com" });
36
+ const user = await context.connections.support.findUserByEmail({
37
+ email: "person@example.com",
38
+ });
39
39
  console.log(user);
40
40
  },
41
41
  });
42
42
  ```
43
43
 
44
- The callback’s `triggers.slackMessage` accepts only Slack keys declared in `connections`. In this example, `"typo"` is a type error, and a Calendar connection named `"support"` would not satisfy a Slack trigger. Keys remain literal when you save a declaration in a variable, so the same check works with `const support = connections.slack({ key: "support" })`. Omitting the declaration key defaults to the provider name.
44
+ The callback’s `triggers.slackMessage` accepts only Slack keys declared in `connections`. In this example, `"typo"` is a type error, and a Calendar connection named `"support"` would not satisfy a Slack trigger. Keys come from the object properties, so saving `const slack = connections.slack()` and declaring `{ support: slack }` still restricts the trigger to `"support"`.
45
45
 
46
46
  The callback runs once when `workflow` is called. Its result is serialized as the ordinary trigger array, and the handler’s event type is inferred from those triggers. Returning a keyed trigger from an imported helper is checked too. Existing static trigger arrays remain supported and validate connection keys at runtime; use the callback for compile-time checking. Unbound triggers, including existing calls without `connectionKey`, keep their existing behavior.
47
47
 
48
48
  Deployment creates a disabled trigger attached to that connection. Configure the account, channel or calendar, and enable the trigger through workflow setup before publishing. Redeploying preserves its configuration. The server checks the binding at publication and execution, so a trigger on another connection cannot invoke this declaration.
49
49
 
50
- Calendar, Slack, Google Drive OAuth, and Discord currently have generated connection trigger helpers. Providers without registered public trigger events do not gain helpers merely by supporting actions. Existing helper calls without `connectionKey` keep their existing behavior. To bind a default connection such as `connections.calendar()`, use `triggers.calendarEventCreated({ connectionKey: "calendar" })`.
50
+ Calendar, Slack, Google Drive OAuth, and Discord currently have generated connection trigger helpers. Providers without registered public trigger events do not gain helpers merely by supporting actions. Existing helper calls without `connectionKey` keep their existing behavior. For `connections: { calendar: connections.calendar() }`, use `triggers.calendarEventCreated({ connectionKey: "calendar" })`.
51
51
 
52
52
  Removing a declaration retains the configured trigger, but it can no longer invoke the capability unless another declaration allows it. Renaming a key creates a new connection and disabled trigger. Two keys can declare the same event type independently; repeating the same type and key is rejected.
53
53
 
@@ -56,8 +56,8 @@ Removing a declaration retains the configured trigger, but it can no longer invo
56
56
  Use the typed provider client to invoke a method:
57
57
 
58
58
  ```ts
59
- const calendars = await context.connections.calendar().listCalendars({});
60
- const user = await context.connections.slack("support").findUserByEmail({
59
+ const calendars = await context.connections.work.listCalendars({});
60
+ const user = await context.connections.support.findUserByEmail({
61
61
  email: "person@example.com",
62
62
  });
63
63
  console.log(user?.displayName);
@@ -65,21 +65,32 @@ console.log(user?.displayName);
65
65
 
66
66
  Method inputs and results come from Tool Core's workflow projections. The SDK generates provider clients from those contracts, including any wire input or output transformations. Adding another registered provider generates its client from that provider's Tool Core methods.
67
67
 
68
- The handler context exposes only providers declared in that workflow's
69
- `connections` list. A Calendar-only declaration exposes `context.connections.calendar`
70
- but not `context.connections.slack`; omitting connections exposes no provider
71
- clients. Named keys still select a configured binding at runtime.
68
+ The handler context exposes only the declared keys, each with its provider's client type. With `{ work: connections.calendar(), support: connections.slack() }`, `context.connections.work` is a Calendar client and `context.connections.support` is a Slack client. Missing keys and methods from the wrong provider are type errors. Omitting connections exposes no clients.
72
69
 
73
- For a separate declaration array, let TypeScript infer its type or use
74
- `satisfies readonly WorkflowConnection[]`. An explicit broad
75
- `WorkflowConnection[]` annotation erases provider information and prevents this
76
- narrowing. Reusable helpers can accept `WorkflowContext<typeof requirements>`.
70
+ Two names can use the same provider:
71
+
72
+ ```ts
73
+ connections: {
74
+ support: connections.slack(),
75
+ internal: connections.slack(),
76
+ }
77
+ ```
78
+
79
+ `context.connections.support` and `context.connections.internal` use separately configured workflow bindings. Reusing a declaration across workflows does not share credentials.
80
+
81
+ For a separate declarations object, let TypeScript infer its type or use `satisfies WorkflowConnectionDeclarations`. A broad `WorkflowConnectionDeclarations` annotation loses the exact keys and providers. Reusable helpers can accept `WorkflowContext<typeof declarations>`.
82
+
83
+ ### Migrating from connection arrays
84
+
85
+ Replace `connections: [connections.calendar({ key: "work" })]` with `connections: { work: connections.calendar() }`, then replace `context.connections.calendar("work")` with `context.connections.work`. For an old declaration with no explicit key, use the provider name as the object property to preserve its existing binding. Arrays and factory key options are no longer supported.
86
+
87
+ The SDK serializes the object to the existing manifest array of `{ key, type }` requirements. Keeping the same key and provider preserves the server binding.
77
88
 
78
89
  ## Supported providers and setup
79
90
 
80
91
  The registry currently generates 73 methods across 12 providers: Calendar, Slack, Google Drive OAuth, Discord, Cursor, Box, Confluence, Gmail, Google Calendar, Google Drive, Outlook, and Salesforce. Only eligible Tool Core methods are generated; this does not expose every operation in those services.
81
92
 
82
- Each connection must be authenticated and granted access through the workflow’s setup flow before use. The server handles provider-specific authentication and checks that setup is complete. App code only declares a provider and optional binding key; declaring a connection never grants permissions.
93
+ Each connection must be authenticated and granted access through the workflow’s setup flow before use. The server handles provider-specific authentication and checks that setup is complete. App code declares a provider under a binding key; declaring a connection never grants permissions.
83
94
 
84
95
  Providers outside the supported list are not currently available as workflow connections. Unsupported declarations receive an unavailable-provider error.
85
96
 
@@ -106,7 +117,7 @@ notion tool-core codegen-script-types --connections --path ../apps-sdk/src --che
106
117
  3. It reads the exact Tool Core definition stored on each workflow effect projection, so providers with multiple tool versions use the contract selected by their module.
107
118
  4. The existing script-type emitter renders the method's wire input and result types. It honors input field mappings and declared output projections, so the types describe the workflow endpoint contract.
108
119
  5. It also reads each provider module’s trigger definitions and emits `connection-trigger-definitions.generated.ts`. The SDK trigger generator intersects these with the public `TriggerEventMap` to generate supported `connectionKey` options, descriptions, event types, and provider-scoped trigger creators. Internal events are not exposed.
109
- 6. It writes `<provider>.generated.ts` with types and thin client methods, plus `providers.generated.ts` with the declaration helpers and provider factories. The runtime transport stays in `connections.ts`.
120
+ 6. It writes `<provider>.generated.ts` with types and thin client methods, plus `providers.generated.ts` with keyless declaration helpers, the provider-to-client type map, and the client factory. The runtime transport stays in `connections.ts`.
110
121
 
111
122
  ### Updating the SDK after a Tool Core change
112
123
 
@@ -125,6 +136,43 @@ This does not require app developers to import server code or install Tool Core
125
136
 
126
137
  ## Runtime and rollout
127
138
 
128
- The runtime automatically connects each declared key to the account configured during workflow setup. For example, `context.connections.calendar("work")` uses the connection configured as `work`. App code does not manage connection IDs or credentials.
139
+ The runtime automatically connects each declared key to the account configured during workflow setup. For example, `context.connections.work` uses the connection configured as `work`. App code does not manage connection IDs or credentials.
129
140
 
130
141
  Provider method calls currently require the server's local/development environment and `public_api_runtime_sdk_tools` and `workers_call_function` gates. A personal access token alone does not enable these endpoints. Developer portal connection setup UI wiring is a separate follow-up. Renaming a requirement key creates a new binding; removing a requirement does not revoke or delete the server's existing configured module.
142
+
143
+ ## Generic OAuth
144
+
145
+ Use `connections.oauth()` for an OAuth 2.0 provider without a generated provider client:
146
+
147
+ ```ts
148
+ export default workflow({
149
+ name: "Read GitHub repositories",
150
+ description: "Read repositories using this workflow's GitHub authorization",
151
+ triggers: [triggers.scheduled()],
152
+ connections: {
153
+ github: connections.oauth({
154
+ authorizationEndpoint: "https://github.com/login/oauth/authorize",
155
+ tokenEndpoint: "https://github.com/login/oauth/access_token",
156
+ clientId: "your-oauth-app-client-id",
157
+ clientSecretEnv: "GITHUB_CLIENT_SECRET",
158
+ scope: "repo",
159
+ }),
160
+ },
161
+ handler: async (_event, context) => {
162
+ const token = await context.connections.github.accessToken();
163
+ const response = await fetch("https://api.github.com/user/repos", {
164
+ headers: { Authorization: `Bearer ${token}` },
165
+ });
166
+ if (!response.ok) throw new Error(`GitHub returned ${response.status}`);
167
+ console.log(await response.json());
168
+ },
169
+ });
170
+ ```
171
+
172
+ Store the client secret in the app's Workers secrets under the name supplied by `clientSecretEnv`. The declaration contains the secret's name, not its value. Optional `authorizationParams` supports provider options such as `access_type: "offline"`; it cannot override state, callback, client ID, scope, or PKCE fields. Optional `accessTokenExpireMs` supplies a positive default expiry for providers that omit it.
173
+
174
+ Each workflow instance requires separate authorization, even when instances share the same app and declaration. The SDK reads only the access token bound to the current run and does not fall back to app-level `worker.oauth` tokens. The server refreshes tokens before execution; `accessToken()` does not make a refresh request during a long-running handler.
175
+
176
+ This SDK change requires the matching server OAuth implementation before deployment. In the workflow settings, find the declared connection in **Access**, alongside other provider connections. Choose **Connect** beside the declared key, authorize with the provider, and close the authorization window to refresh status. **Reconnect** replaces authorization for that workflow instance; If the client secret is missing, setup shows the secret name to configure. Setup requires full access to the app and workflow editor access. Store the client secret before connecting, and register the app OAuth callback URL with the provider. The SDK does not configure the provider’s OAuth app for you.
177
+
178
+ Generic OAuth provides authentication for your own API calls. It does not generate provider methods or provider triggers. Unlike the generated Tool Core clients, this helper is maintained directly in the SDK.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notionhq/apps",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "An SDK for building workflow apps for Notion",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -9,15 +9,15 @@ user-invocable: false
9
9
  Import `{ workflow }` from `@notionhq/apps` and
10
10
  `connections` from `@notionhq/apps/workflow`. Connections are not root exports.
11
11
  Declare requirements on the workflow, then use the corresponding typed client
12
- inside an awaited durable step. Access a named connection through its provider
13
- client, such as `context.connections.slack("support")`.
12
+ inside an awaited durable step. Access a named connection directly through its key, such as
13
+ `context.connections.support`.
14
14
 
15
15
  Read the installed provider declarations before choosing methods and inputs.
16
16
  The SDK handles transport, credentials, and runtime bindings. Do not construct
17
17
  raw tools API envelopes or call internal endpoints. Check the installed version
18
18
  supports the typed methods; resolve version mismatches before using them.
19
- Provider clients are inferred from the declared requirements. Keys default to
20
- the provider name; custom keys distinguish multiple connections to one provider.
19
+ Provider clients are inferred from the declared requirements. Object property names supply the keys; factories do not accept a key option.
20
+ Separate keys distinguish multiple connections to one provider.
21
21
  Keep keys unique and ensure connection-trigger keys match a declared provider.
22
22
 
23
23
  A declaration requests setup; it grants no access. Deploy and configure the
@@ -49,7 +49,7 @@ import { connections } from "@notionhq/apps/workflow";
49
49
  export default workflow({
50
50
  name: "Watch support messages",
51
51
  description: "Runs when a message arrives through the support connection.",
52
- connections: [connections.slack({ key: "support" })],
52
+ connections: { support: connections.slack() },
53
53
  triggers: ({ triggers }) => [triggers.slackMessage({ connectionKey: "support" })],
54
54
  handler: async (_event, context) => {
55
55
  await context.step("Record trigger", () => {
@@ -62,8 +62,7 @@ export default workflow({
62
62
  Use the callback's `triggers` argument to get connection-key checking.
63
63
  `connectionKey` refers to a declared key for that trigger's provider, not an
64
64
  external account ID or a durable step key. Here, a typo or a key belonging to
65
- a different provider's connection is a type error. Without a custom key,
66
- `connections.slack()` declares the key `"slack"`.
65
+ a different provider's connection is a type error. For example, `{ slack: connections.slack() }` declares the key `"slack"`.
67
66
 
68
67
  The SDK also validates explicit bindings when constructing the workflow,
69
68
  including array-form triggers: the key must exist and its provider must match.
@@ -73,7 +72,7 @@ manifest. Event types still come from the selected triggers; narrow
73
72
  `event.type` before using provider-specific fields in mixed-trigger workflows.
74
73
 
75
74
  Unkeyed provider triggers remain supported for compatibility. Omitting
76
- `connectionKey` does not explicitly bind a trigger to the provider's default
75
+ `connectionKey` does not explicitly bind a trigger to a declared
77
76
  key; supply it when the workflow should listen through a particular connection.
78
77
  SDK validation does not establish server availability or complete connection
79
78
  setup.