@notionhq/workers 0.5.0 → 0.6.0

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 (189) hide show
  1. package/dist/alpha/builder.d.ts +149 -0
  2. package/dist/alpha/builder.d.ts.map +1 -0
  3. package/dist/alpha/builder.js +260 -0
  4. package/dist/alpha/builder.test.d.ts +2 -0
  5. package/dist/alpha/builder.test.d.ts.map +1 -0
  6. package/dist/alpha/cli/build.d.ts +13 -0
  7. package/dist/alpha/cli/build.d.ts.map +1 -0
  8. package/dist/alpha/cli/build.js +36 -0
  9. package/dist/alpha/cli/build.test.d.ts +2 -0
  10. package/dist/alpha/cli/build.test.d.ts.map +1 -0
  11. package/dist/alpha/cli/codegen.d.ts +10 -0
  12. package/dist/alpha/cli/codegen.d.ts.map +1 -0
  13. package/dist/alpha/cli/codegen.js +41 -0
  14. package/dist/alpha/cli/codegen.test.d.ts +2 -0
  15. package/dist/alpha/cli/codegen.test.d.ts.map +1 -0
  16. package/dist/alpha/cli/discover.d.ts +35 -0
  17. package/dist/alpha/cli/discover.d.ts.map +1 -0
  18. package/dist/alpha/cli/discover.js +39 -0
  19. package/dist/alpha/cli/discover.test.d.ts +2 -0
  20. package/dist/alpha/cli/discover.test.d.ts.map +1 -0
  21. package/dist/alpha/cli/emit-manifest.d.ts +33 -0
  22. package/dist/alpha/cli/emit-manifest.d.ts.map +1 -0
  23. package/dist/alpha/cli/emit-manifest.js +266 -0
  24. package/dist/alpha/cli/emit-manifest.test.d.ts +2 -0
  25. package/dist/alpha/cli/emit-manifest.test.d.ts.map +1 -0
  26. package/dist/alpha/cli/index.d.ts +3 -0
  27. package/dist/alpha/cli/index.d.ts.map +1 -0
  28. package/dist/alpha/cli/index.js +36 -0
  29. package/dist/alpha/context.d.ts +8 -0
  30. package/dist/alpha/context.d.ts.map +1 -0
  31. package/dist/alpha/context.js +33 -0
  32. package/dist/alpha/database.d.ts +90 -0
  33. package/dist/alpha/database.d.ts.map +1 -0
  34. package/dist/alpha/database.js +9 -0
  35. package/dist/alpha/error.d.ts +41 -0
  36. package/dist/alpha/error.d.ts.map +1 -0
  37. package/dist/alpha/error.js +25 -0
  38. package/dist/alpha/icon-names.d.ts +6 -0
  39. package/dist/alpha/icon-names.d.ts.map +1 -0
  40. package/dist/alpha/icon-names.js +0 -0
  41. package/dist/alpha/json-schema.d.ts +117 -0
  42. package/dist/alpha/json-schema.d.ts.map +1 -0
  43. package/dist/alpha/json-schema.js +0 -0
  44. package/dist/alpha/manifest.d.ts +44 -0
  45. package/dist/alpha/manifest.d.ts.map +1 -0
  46. package/dist/alpha/manifest.js +0 -0
  47. package/dist/alpha/output.d.ts +7 -0
  48. package/dist/alpha/output.d.ts.map +1 -0
  49. package/dist/alpha/output.js +8 -0
  50. package/dist/alpha/pacer.d.ts +74 -0
  51. package/dist/alpha/pacer.d.ts.map +1 -0
  52. package/dist/alpha/pacer.js +66 -0
  53. package/dist/alpha/pacer.test.d.ts +2 -0
  54. package/dist/alpha/pacer.test.d.ts.map +1 -0
  55. package/dist/alpha/schedule.d.ts +6 -0
  56. package/dist/alpha/schedule.d.ts.map +1 -0
  57. package/dist/alpha/schedule.js +43 -0
  58. package/dist/alpha/schedule.test.d.ts +2 -0
  59. package/dist/alpha/schedule.test.d.ts.map +1 -0
  60. package/dist/alpha/schema-builder.d.ts +79 -0
  61. package/dist/alpha/schema-builder.d.ts.map +1 -0
  62. package/dist/alpha/schema-builder.js +135 -0
  63. package/dist/alpha/schema.d.ts +185 -0
  64. package/dist/alpha/schema.d.ts.map +1 -0
  65. package/dist/alpha/schema.js +115 -0
  66. package/dist/alpha/sync.d.ts +233 -0
  67. package/dist/alpha/sync.d.ts.map +1 -0
  68. package/dist/alpha/sync.js +70 -0
  69. package/dist/alpha/sync.test.d.ts +2 -0
  70. package/dist/alpha/sync.test.d.ts.map +1 -0
  71. package/dist/alpha/tool.d.ts +132 -0
  72. package/dist/alpha/tool.d.ts.map +1 -0
  73. package/dist/alpha/tool.js +156 -0
  74. package/dist/alpha/tool.test.d.ts +2 -0
  75. package/dist/alpha/tool.test.d.ts.map +1 -0
  76. package/dist/alpha/triggers.d.ts +365 -0
  77. package/dist/alpha/triggers.d.ts.map +1 -0
  78. package/dist/alpha/triggers.generated.d.ts +246 -0
  79. package/dist/alpha/triggers.generated.d.ts.map +1 -0
  80. package/dist/alpha/triggers.generated.js +239 -0
  81. package/dist/alpha/triggers.js +0 -0
  82. package/dist/alpha/types.d.ts +228 -0
  83. package/dist/alpha/types.d.ts.map +1 -0
  84. package/dist/alpha/types.js +0 -0
  85. package/dist/alpha/workflow.d.ts +70 -0
  86. package/dist/alpha/workflow.d.ts.map +1 -0
  87. package/dist/alpha/workflow.js +39 -0
  88. package/dist/alpha/workflow.test.d.ts +2 -0
  89. package/dist/alpha/workflow.test.d.ts.map +1 -0
  90. package/dist/builder.d.ts.map +1 -1
  91. package/dist/builder.js +2 -6
  92. package/dist/capabilities/ai_connector.d.ts.map +1 -1
  93. package/dist/capabilities/automation.d.ts.map +1 -1
  94. package/dist/capabilities/context.d.ts.map +1 -1
  95. package/dist/capabilities/custom-block.d.ts +124 -0
  96. package/dist/capabilities/custom-block.d.ts.map +1 -0
  97. package/dist/capabilities/custom-block.js +22 -0
  98. package/dist/capabilities/oauth.d.ts.map +1 -1
  99. package/dist/capabilities/output.d.ts.map +1 -1
  100. package/dist/capabilities/output.js +2 -4
  101. package/dist/capabilities/stateful-capability.d.ts.map +1 -1
  102. package/dist/capabilities/stateful-capability.js +2 -6
  103. package/dist/capabilities/sync.d.ts.map +1 -1
  104. package/dist/capabilities/sync.js +2 -8
  105. package/dist/capabilities/tool.d.ts.map +1 -1
  106. package/dist/capabilities/tool.js +6 -20
  107. package/dist/capabilities/webhook.d.ts.map +1 -1
  108. package/dist/capabilities/workflow.d.ts +2 -2
  109. package/dist/capabilities/workflow.d.ts.map +1 -1
  110. package/dist/index.d.ts +3 -2
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.js +1 -7
  113. package/dist/json-schema.d.ts.map +1 -1
  114. package/dist/schema-builder.d.ts.map +1 -1
  115. package/dist/schema.d.ts.map +1 -1
  116. package/dist/types.d.ts.map +1 -1
  117. package/dist/worker.d.ts +41 -1
  118. package/dist/worker.d.ts.map +1 -1
  119. package/dist/worker.js +51 -4
  120. package/package.json +74 -14
  121. package/src/alpha/ajv-formats.d.ts +7 -0
  122. package/src/alpha/builder.test.ts +197 -0
  123. package/src/alpha/builder.ts +338 -0
  124. package/src/alpha/cli/build.test.ts +240 -0
  125. package/src/alpha/cli/build.ts +57 -0
  126. package/src/alpha/cli/codegen.test.ts +31 -0
  127. package/src/alpha/cli/codegen.ts +51 -0
  128. package/src/alpha/cli/discover.test.ts +43 -0
  129. package/src/alpha/cli/discover.ts +81 -0
  130. package/src/alpha/cli/emit-manifest.test.ts +45 -0
  131. package/src/alpha/cli/emit-manifest.ts +409 -0
  132. package/src/alpha/cli/index.ts +41 -0
  133. package/src/alpha/context.ts +61 -0
  134. package/src/alpha/database.ts +102 -0
  135. package/src/alpha/error.ts +49 -0
  136. package/src/alpha/icon-names.ts +890 -0
  137. package/src/alpha/json-schema.ts +180 -0
  138. package/src/alpha/manifest.ts +47 -0
  139. package/src/alpha/output.ts +8 -0
  140. package/src/alpha/pacer.test.ts +81 -0
  141. package/src/alpha/pacer.ts +181 -0
  142. package/src/alpha/schedule.test.ts +31 -0
  143. package/src/alpha/schedule.ts +53 -0
  144. package/src/alpha/schema-builder.ts +193 -0
  145. package/src/alpha/schema.ts +222 -0
  146. package/src/alpha/sync.test.ts +191 -0
  147. package/src/alpha/sync.ts +340 -0
  148. package/src/alpha/tool.test.ts +116 -0
  149. package/src/alpha/tool.ts +319 -0
  150. package/src/alpha/triggers.generated.ts +489 -0
  151. package/src/alpha/triggers.ts +500 -0
  152. package/src/alpha/types.ts +284 -0
  153. package/src/alpha/workflow.test.ts +84 -0
  154. package/src/alpha/workflow.ts +131 -0
  155. package/src/builder.test.ts +28 -60
  156. package/src/builder.ts +3 -10
  157. package/src/capabilities/ai_connector.test.ts +9 -32
  158. package/src/capabilities/ai_connector.ts +5 -17
  159. package/src/capabilities/automation.test.ts +3 -12
  160. package/src/capabilities/automation.ts +3 -11
  161. package/src/capabilities/context.ts +3 -6
  162. package/src/capabilities/custom-block.ts +181 -0
  163. package/src/capabilities/oauth.test.ts +1 -0
  164. package/src/capabilities/oauth.ts +1 -3
  165. package/src/capabilities/output.ts +1 -3
  166. package/src/capabilities/stateful-capability.test.ts +1 -0
  167. package/src/capabilities/stateful-capability.ts +2 -6
  168. package/src/capabilities/sync.test.ts +15 -45
  169. package/src/capabilities/sync.ts +10 -40
  170. package/src/capabilities/tool.test.ts +106 -146
  171. package/src/capabilities/tool.ts +17 -39
  172. package/src/capabilities/webhook.ts +3 -10
  173. package/src/capabilities/workflow.test.ts +10 -31
  174. package/src/capabilities/workflow.ts +5 -12
  175. package/src/index.ts +9 -12
  176. package/src/json-schema.test.ts +2 -4
  177. package/src/json-schema.ts +7 -6
  178. package/src/pacer.test.ts +1 -3
  179. package/src/pacer_internal.ts +1 -5
  180. package/src/schema-builder.test.ts +10 -35
  181. package/src/schema-builder.ts +5 -16
  182. package/src/schema.ts +2 -10
  183. package/src/types.ts +1 -7
  184. package/src/worker.test.ts +147 -0
  185. package/src/worker.ts +67 -35
  186. package/dist/block.d.ts +0 -321
  187. package/dist/block.d.ts.map +0 -1
  188. package/src/block.ts +0 -529
  189. /package/dist/{block.js → alpha/ajv-formats.d.js} +0 -0
package/src/builder.ts CHANGED
@@ -234,9 +234,7 @@ export function relation(primaryKey: string): RelationReference {
234
234
  */
235
235
  function validateDateString(dateString: string): void {
236
236
  if (!/^\d{4}-\d{2}-\d{2}$/.test(dateString)) {
237
- throw new Error(
238
- `Invalid date format: ${dateString}. Expected YYYY-MM-DD format.`,
239
- );
237
+ throw new Error(`Invalid date format: ${dateString}. Expected YYYY-MM-DD format.`);
240
238
  }
241
239
 
242
240
  const date = new Date(dateString);
@@ -290,10 +288,7 @@ export function emojiIcon(emoji: string): Icon {
290
288
  * @param icon - The name of the Notion icon (e.g., "checkmark", "pizza", "rocket")
291
289
  * @param color - The color variant (defaults to "gray")
292
290
  */
293
- export function notionIcon(
294
- icon: NoticonName,
295
- color: NoticonColor = "gray",
296
- ): Icon {
291
+ export function notionIcon(icon: NoticonName, color: NoticonColor = "gray"): Icon {
297
292
  return {
298
293
  type: "notion",
299
294
  icon,
@@ -319,9 +314,7 @@ export function imageIcon(url: string): Icon {
319
314
  */
320
315
  export function imageCover(url: string, position = 0.5): Cover {
321
316
  if (!Number.isFinite(position) || position < 0 || position > 1) {
322
- throw new Error(
323
- `Invalid cover position: ${position}. Expected a number between 0 and 1.`,
324
- );
317
+ throw new Error(`Invalid cover position: ${position}. Expected a number between 0 and 1.`);
325
318
  }
326
319
 
327
320
  return {
@@ -1,12 +1,5 @@
1
- import {
2
- afterEach,
3
- beforeEach,
4
- describe,
5
- expect,
6
- it,
7
- type Mock,
8
- vi,
9
- } from "vitest";
1
+ import { afterEach, beforeEach, describe, expect, it, type Mock, vi } from "vitest";
2
+
10
3
  import { ExecutionError } from "../error.js";
11
4
  import { getPacerState } from "../pacer_internal.js";
12
5
  import { Worker } from "../worker.js";
@@ -17,9 +10,7 @@ describe("Worker.aiConnector", () => {
17
10
  let stdoutSpy: Mock<typeof process.stdout.write>;
18
11
 
19
12
  beforeEach(() => {
20
- stdoutSpy = vi
21
- .spyOn(process.stdout, "write")
22
- .mockImplementation(() => true);
13
+ stdoutSpy = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
23
14
  });
24
15
 
25
16
  afterEach(() => {
@@ -86,12 +77,8 @@ describe("Worker.aiConnector", () => {
86
77
 
87
78
  await capability.handler();
88
79
 
89
- expect(stdoutSpy).toHaveBeenCalledWith(
90
- expect.stringContaining(`"_tag":"success"`),
91
- );
92
- expect(stdoutSpy).toHaveBeenCalledWith(
93
- expect.stringContaining(`"thread-1"`),
94
- );
80
+ expect(stdoutSpy).toHaveBeenCalledWith(expect.stringContaining(`"_tag":"success"`));
81
+ expect(stdoutSpy).toHaveBeenCalledWith(expect.stringContaining(`"thread-1"`));
95
82
  });
96
83
 
97
84
  it("passes state through from legacy runtime userContext", async () => {
@@ -106,15 +93,9 @@ describe("Worker.aiConnector", () => {
106
93
  execute,
107
94
  });
108
95
 
109
- await capability.handler(
110
- { userContext: { cursor: "cursor-1" } },
111
- { concreteOutput: true },
112
- );
96
+ await capability.handler({ userContext: { cursor: "cursor-1" } }, { concreteOutput: true });
113
97
 
114
- expect(execute).toHaveBeenCalledWith(
115
- { cursor: "cursor-1" },
116
- expect.anything(),
117
- );
98
+ expect(execute).toHaveBeenCalledWith({ cursor: "cursor-1" }, expect.anything());
118
99
  expect(stdoutSpy).not.toHaveBeenCalled();
119
100
  });
120
101
 
@@ -161,9 +142,7 @@ describe("Worker.aiConnector", () => {
161
142
  },
162
143
  },
163
144
  });
164
- expect(
165
- result.nextPacerStates.api?.lastScheduledAtMs,
166
- ).toBeGreaterThanOrEqual(before);
145
+ expect(result.nextPacerStates.api?.lastScheduledAtMs).toBeGreaterThanOrEqual(before);
167
146
  });
168
147
 
169
148
  describe("pacer local fallback", () => {
@@ -243,9 +222,7 @@ describe("Worker.aiConnector", () => {
243
222
  });
244
223
 
245
224
  await expect(capability.handler()).rejects.toThrow(ExecutionError);
246
- expect(stdoutSpy).toHaveBeenCalledWith(
247
- expect.stringContaining(`"_tag":"error"`),
248
- );
225
+ expect(stdoutSpy).toHaveBeenCalledWith(expect.stringContaining(`"_tag":"error"`));
249
226
  });
250
227
 
251
228
  it("accepts chat records at compile time", () => {
@@ -85,18 +85,14 @@ export type AiConnectorChangeUpsert<A extends AiConnectorArchetype> = {
85
85
  record: AiConnectorRecordByArchetype[A];
86
86
  };
87
87
 
88
- export type AiConnectorChange<A extends AiConnectorArchetype> =
89
- AiConnectorChangeUpsert<A>;
88
+ export type AiConnectorChange<A extends AiConnectorArchetype> = AiConnectorChangeUpsert<A>;
90
89
 
91
90
  export type AiConnectorExecutionResult<
92
91
  A extends AiConnectorArchetype,
93
92
  State = unknown,
94
93
  > = StatefulExecutionResult<AiConnectorChange<A>, State>;
95
94
 
96
- export type AiConnectorConfiguration<
97
- A extends AiConnectorArchetype,
98
- State = unknown,
99
- > = {
95
+ export type AiConnectorConfiguration<A extends AiConnectorArchetype, State = unknown> = {
100
96
  /**
101
97
  * The Notion record ID of an existing custom connector in the target workspace.
102
98
  *
@@ -113,9 +109,7 @@ export type AiConnectorConfiguration<
113
109
  ) => Promise<AiConnectorExecutionResult<A, State>>;
114
110
  };
115
111
 
116
- export type AiConnectorCapability = ReturnType<
117
- typeof createAiConnectorCapability
118
- >;
112
+ export type AiConnectorCapability = ReturnType<typeof createAiConnectorCapability>;
119
113
 
120
114
  type NormalizedAiConnectorConfiguration<A extends AiConnectorArchetype> = {
121
115
  aiConnectorId: string;
@@ -131,10 +125,7 @@ type AiConnectorHandlerResult<A extends AiConnectorArchetype, State> = {
131
125
  nextPacerStates: Record<string, PacerEntry>;
132
126
  };
133
127
 
134
- export function createAiConnectorCapability<
135
- A extends AiConnectorArchetype,
136
- State = unknown,
137
- >(
128
+ export function createAiConnectorCapability<A extends AiConnectorArchetype, State = unknown>(
138
129
  key: string,
139
130
  configuration: AiConnectorConfiguration<A, State>,
140
131
  pacerDeclarations?: readonly PacerDeclaration[],
@@ -150,10 +141,7 @@ export function createAiConnectorCapability<
150
141
  ? {}
151
142
  : { schedule: parseSchedule(configuration.schedule) }),
152
143
  } satisfies NormalizedAiConnectorConfiguration<A>,
153
- async handler(
154
- runtimeContext?: StatefulRuntimeContext<State>,
155
- options?: HandlerOptions,
156
- ) {
144
+ async handler(runtimeContext?: StatefulRuntimeContext<State>, options?: HandlerOptions) {
157
145
  const capabilityContext = createCapabilityContext("ai_connector");
158
146
  const state = runtimeContext?.state ?? runtimeContext?.userContext;
159
147
 
@@ -1,12 +1,5 @@
1
- import {
2
- afterEach,
3
- beforeEach,
4
- describe,
5
- expect,
6
- it,
7
- type Mock,
8
- vi,
9
- } from "vitest";
1
+ import { afterEach, beforeEach, describe, expect, it, type Mock, vi } from "vitest";
2
+
10
3
  import { ExecutionError } from "../error.js";
11
4
  import type { PageObjectResponse } from "./automation.js";
12
5
  import { createAutomationCapability } from "./automation.js";
@@ -15,9 +8,7 @@ describe("createAutomationCapability", () => {
15
8
  let stdoutSpy: Mock<typeof process.stdout.write>;
16
9
 
17
10
  beforeEach(() => {
18
- stdoutSpy = vi
19
- .spyOn(process.stdout, "write")
20
- .mockImplementation(() => true);
11
+ stdoutSpy = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
21
12
  });
22
13
 
23
14
  afterEach(() => {
@@ -64,10 +64,7 @@ export interface AutomationConfiguration {
64
64
  * @param context - The capability execution context (Notion client, etc.)
65
65
  * @returns A promise that resolves when the automation completes
66
66
  */
67
- execute: (
68
- event: AutomationEvent,
69
- context: CapabilityContext,
70
- ) => Promise<void> | void;
67
+ execute: (event: AutomationEvent, context: CapabilityContext) => Promise<void> | void;
71
68
  }
72
69
 
73
70
  /**
@@ -78,9 +75,7 @@ export interface AutomationHandlerResult {
78
75
  description: string;
79
76
  }
80
77
 
81
- export type AutomationCapability = ReturnType<
82
- typeof createAutomationCapability
83
- >;
78
+ export type AutomationCapability = ReturnType<typeof createAutomationCapability>;
84
79
 
85
80
  /**
86
81
  * Creates an automation capability from configuration.
@@ -89,10 +84,7 @@ export type AutomationCapability = ReturnType<
89
84
  * @param config - The automation configuration.
90
85
  * @returns The capability object.
91
86
  */
92
- export function createAutomationCapability(
93
- key: string,
94
- config: AutomationConfiguration,
95
- ) {
87
+ export function createAutomationCapability(key: string, config: AutomationConfiguration) {
96
88
  return {
97
89
  _tag: "automation" as const,
98
90
  key,
@@ -1,5 +1,6 @@
1
1
  import { Client } from "@notionhq/client";
2
2
  import type { ClientOptions } from "@notionhq/client/build/src/Client.js";
3
+
3
4
  import type { CapabilityType } from "../worker.js";
4
5
 
5
6
  export type CapabilityContext = {
@@ -32,9 +33,7 @@ function missingTokenMessage(capabilityType: CapabilityType): string {
32
33
  );
33
34
  }
34
35
 
35
- function createUnauthenticatedNotionProxy(
36
- capabilityType: CapabilityType,
37
- ): Client {
36
+ function createUnauthenticatedNotionProxy(capabilityType: CapabilityType): Client {
38
37
  return new Proxy({} as Client, {
39
38
  get(_target, prop) {
40
39
  if (prop === "then" || typeof prop === "symbol") {
@@ -45,9 +44,7 @@ function createUnauthenticatedNotionProxy(
45
44
  });
46
45
  }
47
46
 
48
- export function createCapabilityContext(
49
- capabilityType: CapabilityType,
50
- ): CapabilityContext {
47
+ export function createCapabilityContext(capabilityType: CapabilityType): CapabilityContext {
51
48
  const options: ClientOptions = {};
52
49
 
53
50
  if (process.env.NOTION_API_BASE_URL) {
@@ -0,0 +1,181 @@
1
+ // ============================================================================
2
+ // Custom Block Authoring API
3
+ // ============================================================================
4
+
5
+ /**
6
+ * Icon for a custom block data source. Mirrors the server/client
7
+ * `CustomBlockManifestIcon` (`@notionhq/shared/customViews/customBlockManifestTypes`).
8
+ */
9
+ export type CustomBlockManifestIcon =
10
+ | { type: "emoji"; emoji: string }
11
+ | { type: "external"; url: string };
12
+
13
+ /**
14
+ * Public-API-shaped property type names usable in a custom block manifest.
15
+ */
16
+ export type CustomBlockManifestPropertyType =
17
+ | "title"
18
+ | "rich_text"
19
+ | "number"
20
+ | "select"
21
+ | "multi_select"
22
+ | "status"
23
+ | "date"
24
+ | "people"
25
+ | "files"
26
+ | "checkbox"
27
+ | "url"
28
+ | "email"
29
+ | "phone_number"
30
+ | "formula"
31
+ | "relation"
32
+ | "rollup"
33
+ | "created_time"
34
+ | "created_by"
35
+ | "last_edited_time"
36
+ | "last_edited_by"
37
+ | "last_visited_time"
38
+ | "button"
39
+ | "unique_id"
40
+ | "location"
41
+ | "verification"
42
+ | "place";
43
+
44
+ /**
45
+ * Describes a single property exposed by a custom block data source.
46
+ */
47
+ export type CustomBlockManifestProperty = {
48
+ name: string;
49
+ description?: string;
50
+ type: CustomBlockManifestPropertyType;
51
+ };
52
+
53
+ /**
54
+ * Rich schema describing one of a custom block's data sources.
55
+ */
56
+ export type CustomBlockManifestDataSource = {
57
+ /** Display name for the data source. */
58
+ name: string;
59
+ /** Optional human-readable description of the data source. */
60
+ description?: string;
61
+ /** Optional icon for the data source. */
62
+ icon?: CustomBlockManifestIcon;
63
+ /** Optional property schema keyed by property key. */
64
+ properties?: Record<string, CustomBlockManifestProperty>;
65
+ };
66
+
67
+ export type CustomBlockManifest = {
68
+ version: 1;
69
+ /**
70
+ * The block's datasources schema, mapping keys you name to {@link CustomBlockManifestDataSource}
71
+ *
72
+ * @example
73
+ *
74
+ * {
75
+ * "myDataSource": {
76
+ * "name": "My Data Source",
77
+ * "description": "A description of my data source",
78
+ * "icon": "icon.png",
79
+ * "properties": {
80
+ * "property1": {
81
+ * "name": "Property 1",
82
+ * "type": "string"
83
+ * }
84
+ * }
85
+ * }
86
+ * }
87
+ */
88
+ dataSources: Record<string, CustomBlockManifestDataSource>;
89
+ };
90
+
91
+ /**
92
+ * A custom block is a front-end web app served in an iframe in the client with live data access.
93
+ *
94
+ * Available `type`s are `project` (a buildable project dir) and `static` (an already-built dir served as-is).
95
+ */
96
+ export type CustomBlockConfiguration = (
97
+ | {
98
+ /** Buildable project dir (the default). */
99
+ type?: "project";
100
+ /** Path to the project dir, relative to the worker root (e.g. `"./blocks/foo"`). */
101
+ path: string;
102
+ /** Build command run in `path`. Defaults to `npm run build`. */
103
+ command?: string;
104
+ /** Built-output dir relative to `path`. Defaults to `dist`. */
105
+ output?: string;
106
+ }
107
+ | {
108
+ /** An already-built browser-bundle dir; served as-is, no build step. */
109
+ type: "static";
110
+ /** Path to the prebuilt dir, relative to the worker root. */
111
+ path: string;
112
+ command?: never;
113
+ output?: never;
114
+ }
115
+ ) &
116
+ Partial<CustomBlockManifest>;
117
+
118
+ /**
119
+ * The build source of a custom block, mirroring the server's
120
+ * `BlockSourceDeclaration`. `project` is a buildable dir (`command` defaults to
121
+ * `npm run build`, `output` to `dist`); `static` is an already-built dir served
122
+ * as-is.
123
+ */
124
+ export type BlockSourceDeclaration =
125
+ | { type: "project"; path: string; command?: string; output?: string }
126
+ | { type: "static"; path: string };
127
+
128
+ /**
129
+ * Config payload carried in the runtime manifest for a `_tag: "custom_block"`
130
+ * capability entry. The server parses this (see `RawBlockCapabilityConfig` in
131
+ * `capabilities-service.ts`): `source` is the build entry and `manifest` is the
132
+ * author-declared {@link CustomBlockManifest} describing the block's data-source
133
+ * schema. Data-source *bindings* are instance-level and are not carried here.
134
+ */
135
+ export type CustomBlockCapabilityConfig = {
136
+ source: BlockSourceDeclaration;
137
+ manifest: CustomBlockManifest;
138
+ };
139
+
140
+ /**
141
+ * A custom-block capability entry. Unlike other capabilities it has no runtime
142
+ * handler — it is a build-time/deploy-time declaration only, carried through
143
+ * the manifest so the server can build and bind the block on deploy.
144
+ */
145
+ export type CustomBlockCapability = {
146
+ readonly _tag: "custom_block";
147
+ readonly key: string;
148
+ readonly config: CustomBlockCapabilityConfig;
149
+ };
150
+
151
+ /**
152
+ * Builds a custom-block capability entry from its authoring configuration. The
153
+ * author-declared `dataSources` schema is carried verbatim into the block
154
+ * {@link CustomBlockManifest}; no bindings are emitted.
155
+ */
156
+ export function createCustomBlockCapability(
157
+ key: string,
158
+ config: CustomBlockConfiguration,
159
+ ): CustomBlockCapability {
160
+ const source: BlockSourceDeclaration =
161
+ config.type === "static"
162
+ ? { type: "static", path: config.path }
163
+ : {
164
+ type: "project",
165
+ path: config.path,
166
+ ...(config.command !== undefined ? { command: config.command } : {}),
167
+ ...(config.output !== undefined ? { output: config.output } : {}),
168
+ };
169
+
170
+ return {
171
+ _tag: "custom_block",
172
+ key,
173
+ config: {
174
+ source,
175
+ manifest: {
176
+ version: config.version ?? 1,
177
+ dataSources: config.dataSources ?? {},
178
+ },
179
+ },
180
+ };
181
+ }
@@ -1,4 +1,5 @@
1
1
  import { afterEach, describe, expect, it } from "vitest";
2
+
2
3
  import { createOAuthCapability } from "./oauth.js";
3
4
 
4
5
  describe("oauth", () => {
@@ -84,9 +84,7 @@ export interface UserManagedOAuthConfiguration {
84
84
  /**
85
85
  * Union type representing either Notion-managed or user-managed OAuth configuration.
86
86
  */
87
- export type OAuthConfiguration =
88
- | NotionManagedOAuthConfiguration
89
- | UserManagedOAuthConfiguration;
87
+ export type OAuthConfiguration = NotionManagedOAuthConfiguration | UserManagedOAuthConfiguration;
90
88
 
91
89
  export type OAuthCapability = ReturnType<typeof createOAuthCapability>;
92
90
 
@@ -2,7 +2,5 @@
2
2
  * Writes a notion_output envelope to stdout using the shared wire format.
3
3
  */
4
4
  export function writeOutput(value: unknown): void {
5
- process.stdout.write(
6
- `\n<__notion_output__>${JSON.stringify(value)}</__notion_output__>\n`,
7
- );
5
+ process.stdout.write(`\n<__notion_output__>${JSON.stringify(value)}</__notion_output__>\n`);
8
6
  }
@@ -1,4 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
+
2
3
  import { parseSchedule } from "./stateful-capability.js";
3
4
 
4
5
  describe("stateful capability shared primitives", () => {
@@ -73,14 +73,10 @@ export function parseSchedule(schedule: Schedule): SyncSchedule {
73
73
  }
74
74
 
75
75
  if (intervalMs < MIN_INTERVAL_MS) {
76
- throw new Error(
77
- `Schedule interval must be at least 1 minute. Got: "${schedule}"`,
78
- );
76
+ throw new Error(`Schedule interval must be at least 1 minute. Got: "${schedule}"`);
79
77
  }
80
78
  if (intervalMs > MAX_INTERVAL_MS) {
81
- throw new Error(
82
- `Schedule interval must be at most 7 days. Got: "${schedule}"`,
83
- );
79
+ throw new Error(`Schedule interval must be at most 7 days. Got: "${schedule}"`);
84
80
  }
85
81
 
86
82
  return { type: "interval", intervalMs };
@@ -1,12 +1,5 @@
1
- import {
2
- afterEach,
3
- beforeEach,
4
- describe,
5
- expect,
6
- it,
7
- type Mock,
8
- vi,
9
- } from "vitest";
1
+ import { afterEach, beforeEach, describe, expect, it, type Mock, vi } from "vitest";
2
+
10
3
  import * as Builder from "../builder.js";
11
4
  import { ExecutionError, RateLimitError } from "../error.js";
12
5
  import { getPacerState } from "../pacer_internal.js";
@@ -18,9 +11,7 @@ describe("Worker.sync", () => {
18
11
  let stdoutSpy: Mock<typeof process.stdout.write>;
19
12
 
20
13
  beforeEach(() => {
21
- stdoutSpy = vi
22
- .spyOn(process.stdout, "write")
23
- .mockImplementation(() => true);
14
+ stdoutSpy = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
24
15
  });
25
16
 
26
17
  afterEach(() => {
@@ -47,10 +38,7 @@ describe("Worker.sync", () => {
47
38
  });
48
39
 
49
40
  it("can be used to build relation arrays", () => {
50
- const value = [
51
- Builder.relation("project-123"),
52
- Builder.relation("project-456"),
53
- ];
41
+ const value = [Builder.relation("project-123"), Builder.relation("project-456")];
54
42
 
55
43
  expect(value).toEqual([
56
44
  { type: "primaryKey", value: "project-123" },
@@ -81,9 +69,9 @@ describe("Worker.sync", () => {
81
69
  });
82
70
 
83
71
  it("rejects cover positions outside the supported range", () => {
84
- expect(() =>
85
- Builder.imageCover("https://example.com/cover.jpg", 1.5),
86
- ).toThrow("Expected a number between 0 and 1");
72
+ expect(() => Builder.imageCover("https://example.com/cover.jpg", 1.5)).toThrow(
73
+ "Expected a number between 0 and 1",
74
+ );
87
75
  });
88
76
  });
89
77
 
@@ -119,10 +107,7 @@ describe("Worker.sync", () => {
119
107
  key: "task-2",
120
108
  properties: {
121
109
  "Task ID": Builder.title("TASK-002"),
122
- Project: [
123
- Builder.relation("project-123"),
124
- Builder.relation("project-456"),
125
- ],
110
+ Project: [Builder.relation("project-123"), Builder.relation("project-456")],
126
111
  },
127
112
  },
128
113
  ],
@@ -307,12 +292,8 @@ describe("Worker.sync", () => {
307
292
 
308
293
  await expect(capability.handler()).rejects.toThrow(ExecutionError);
309
294
 
310
- expect(stdoutSpy).toHaveBeenCalledWith(
311
- expect.stringContaining(`"_tag":"error"`),
312
- );
313
- expect(stdoutSpy).toHaveBeenCalledWith(
314
- expect.stringContaining(`"name":"ExecutionError"`),
315
- );
295
+ expect(stdoutSpy).toHaveBeenCalledWith(expect.stringContaining(`"_tag":"error"`));
296
+ expect(stdoutSpy).toHaveBeenCalledWith(expect.stringContaining(`"name":"ExecutionError"`));
316
297
  });
317
298
 
318
299
  it("wraps RateLimitError in error envelope with _tag rate_limit", async () => {
@@ -337,12 +318,8 @@ describe("Worker.sync", () => {
337
318
 
338
319
  await expect(capability.handler()).rejects.toThrow(RateLimitError);
339
320
 
340
- expect(stdoutSpy).toHaveBeenCalledWith(
341
- expect.stringContaining(`"_tag":"rate_limit"`),
342
- );
343
- expect(stdoutSpy).toHaveBeenCalledWith(
344
- expect.stringContaining(`"name":"RateLimitError"`),
345
- );
321
+ expect(stdoutSpy).toHaveBeenCalledWith(expect.stringContaining(`"_tag":"rate_limit"`));
322
+ expect(stdoutSpy).toHaveBeenCalledWith(expect.stringContaining(`"name":"RateLimitError"`));
346
323
  });
347
324
 
348
325
  it("includes retryAfter in rate_limit error envelope when provided", async () => {
@@ -371,18 +348,14 @@ describe("Worker.sync", () => {
371
348
  .map((call) => String(call[0]))
372
349
  .find((s) => s.includes(`"_tag":"rate_limit"`)) as string;
373
350
  const parsed = JSON.parse(
374
- output.replace(
375
- /\n<__notion_output__>(.*)<\/__notion_output__>\n/,
376
- "$1",
377
- ),
351
+ output.replace(/\n<__notion_output__>(.*)<\/__notion_output__>\n/, "$1"),
378
352
  );
379
353
  expect(parsed).toEqual({
380
354
  _tag: "error",
381
355
  error: {
382
356
  _tag: "rate_limit",
383
357
  name: "RateLimitError",
384
- message:
385
- "Error during worker execution: Rate limited by external API",
358
+ message: "Error during worker execution: Rate limited by external API",
386
359
  retryAfter: 30,
387
360
  },
388
361
  });
@@ -414,10 +387,7 @@ describe("Worker.sync", () => {
414
387
  .map((call) => String(call[0]))
415
388
  .find((s) => s.includes(`"_tag":"rate_limit"`)) as string;
416
389
  const parsed = JSON.parse(
417
- output.replace(
418
- /\n<__notion_output__>(.*)<\/__notion_output__>\n/,
419
- "$1",
420
- ),
390
+ output.replace(/\n<__notion_output__>(.*)<\/__notion_output__>\n/, "$1"),
421
391
  );
422
392
  expect(parsed.error.retryAfter).toBeUndefined();
423
393
  });