@notionhq/workers 0.4.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.
- package/dist/alpha/builder.d.ts +149 -0
- package/dist/alpha/builder.d.ts.map +1 -0
- package/dist/alpha/builder.js +260 -0
- package/dist/alpha/builder.test.d.ts +2 -0
- package/dist/alpha/builder.test.d.ts.map +1 -0
- package/dist/alpha/cli/build.d.ts +13 -0
- package/dist/alpha/cli/build.d.ts.map +1 -0
- package/dist/alpha/cli/build.js +36 -0
- package/dist/alpha/cli/build.test.d.ts +2 -0
- package/dist/alpha/cli/build.test.d.ts.map +1 -0
- package/dist/alpha/cli/codegen.d.ts +10 -0
- package/dist/alpha/cli/codegen.d.ts.map +1 -0
- package/dist/alpha/cli/codegen.js +41 -0
- package/dist/alpha/cli/codegen.test.d.ts +2 -0
- package/dist/alpha/cli/codegen.test.d.ts.map +1 -0
- package/dist/alpha/cli/discover.d.ts +35 -0
- package/dist/alpha/cli/discover.d.ts.map +1 -0
- package/dist/alpha/cli/discover.js +39 -0
- package/dist/alpha/cli/discover.test.d.ts +2 -0
- package/dist/alpha/cli/discover.test.d.ts.map +1 -0
- package/dist/alpha/cli/emit-manifest.d.ts +33 -0
- package/dist/alpha/cli/emit-manifest.d.ts.map +1 -0
- package/dist/alpha/cli/emit-manifest.js +266 -0
- package/dist/alpha/cli/emit-manifest.test.d.ts +2 -0
- package/dist/alpha/cli/emit-manifest.test.d.ts.map +1 -0
- package/dist/alpha/cli/index.d.ts +3 -0
- package/dist/alpha/cli/index.d.ts.map +1 -0
- package/dist/alpha/cli/index.js +36 -0
- package/dist/alpha/context.d.ts +8 -0
- package/dist/alpha/context.d.ts.map +1 -0
- package/dist/alpha/context.js +33 -0
- package/dist/alpha/database.d.ts +90 -0
- package/dist/alpha/database.d.ts.map +1 -0
- package/dist/alpha/database.js +9 -0
- package/dist/alpha/error.d.ts +41 -0
- package/dist/alpha/error.d.ts.map +1 -0
- package/dist/alpha/error.js +25 -0
- package/dist/alpha/icon-names.d.ts +6 -0
- package/dist/alpha/icon-names.d.ts.map +1 -0
- package/dist/alpha/icon-names.js +0 -0
- package/dist/alpha/json-schema.d.ts +117 -0
- package/dist/alpha/json-schema.d.ts.map +1 -0
- package/dist/alpha/json-schema.js +0 -0
- package/dist/alpha/manifest.d.ts +44 -0
- package/dist/alpha/manifest.d.ts.map +1 -0
- package/dist/alpha/manifest.js +0 -0
- package/dist/alpha/output.d.ts +7 -0
- package/dist/alpha/output.d.ts.map +1 -0
- package/dist/alpha/output.js +8 -0
- package/dist/alpha/pacer.d.ts +74 -0
- package/dist/alpha/pacer.d.ts.map +1 -0
- package/dist/alpha/pacer.js +66 -0
- package/dist/alpha/pacer.test.d.ts +2 -0
- package/dist/alpha/pacer.test.d.ts.map +1 -0
- package/dist/alpha/schedule.d.ts +6 -0
- package/dist/alpha/schedule.d.ts.map +1 -0
- package/dist/alpha/schedule.js +43 -0
- package/dist/alpha/schedule.test.d.ts +2 -0
- package/dist/alpha/schedule.test.d.ts.map +1 -0
- package/dist/alpha/schema-builder.d.ts +79 -0
- package/dist/alpha/schema-builder.d.ts.map +1 -0
- package/dist/alpha/schema-builder.js +135 -0
- package/dist/alpha/schema.d.ts +185 -0
- package/dist/alpha/schema.d.ts.map +1 -0
- package/dist/alpha/schema.js +115 -0
- package/dist/alpha/sync.d.ts +233 -0
- package/dist/alpha/sync.d.ts.map +1 -0
- package/dist/alpha/sync.js +70 -0
- package/dist/alpha/sync.test.d.ts +2 -0
- package/dist/alpha/sync.test.d.ts.map +1 -0
- package/dist/alpha/tool.d.ts +132 -0
- package/dist/alpha/tool.d.ts.map +1 -0
- package/dist/alpha/tool.js +156 -0
- package/dist/alpha/tool.test.d.ts +2 -0
- package/dist/alpha/tool.test.d.ts.map +1 -0
- package/dist/alpha/triggers.d.ts +365 -0
- package/dist/alpha/triggers.d.ts.map +1 -0
- package/dist/alpha/triggers.generated.d.ts +246 -0
- package/dist/alpha/triggers.generated.d.ts.map +1 -0
- package/dist/alpha/triggers.generated.js +239 -0
- package/dist/alpha/triggers.js +0 -0
- package/dist/alpha/types.d.ts +228 -0
- package/dist/alpha/types.d.ts.map +1 -0
- package/dist/alpha/types.js +0 -0
- package/dist/alpha/workflow.d.ts +70 -0
- package/dist/alpha/workflow.d.ts.map +1 -0
- package/dist/alpha/workflow.js +39 -0
- package/dist/alpha/workflow.test.d.ts +2 -0
- package/dist/alpha/workflow.test.d.ts.map +1 -0
- package/dist/builder.d.ts +7 -1
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +12 -3
- package/dist/capabilities/ai_connector.d.ts.map +1 -1
- package/dist/capabilities/automation.d.ts.map +1 -1
- package/dist/capabilities/context.d.ts.map +1 -1
- package/dist/capabilities/context.js +1 -1
- package/dist/capabilities/custom-block.d.ts +124 -0
- package/dist/capabilities/custom-block.d.ts.map +1 -0
- package/dist/capabilities/custom-block.js +22 -0
- package/dist/capabilities/oauth.d.ts.map +1 -1
- package/dist/capabilities/output.d.ts.map +1 -1
- package/dist/capabilities/output.js +2 -4
- package/dist/capabilities/stateful-capability.d.ts.map +1 -1
- package/dist/capabilities/stateful-capability.js +3 -7
- package/dist/capabilities/sync.d.ts +11 -1
- package/dist/capabilities/sync.d.ts.map +1 -1
- package/dist/capabilities/sync.js +2 -8
- package/dist/capabilities/tool.d.ts.map +1 -1
- package/dist/capabilities/tool.js +6 -20
- package/dist/capabilities/webhook.d.ts.map +1 -1
- package/dist/capabilities/workflow.d.ts +50 -0
- package/dist/capabilities/workflow.d.ts.map +1 -0
- package/dist/capabilities/workflow.js +40 -0
- package/dist/capabilities/workflow.test.d.ts +2 -0
- package/dist/capabilities/workflow.test.d.ts.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/json-schema.d.ts.map +1 -1
- package/dist/schema-builder.d.ts.map +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/triggers.d.ts +365 -0
- package/dist/triggers.d.ts.map +1 -0
- package/dist/triggers.generated.d.ts +246 -0
- package/dist/triggers.generated.d.ts.map +1 -0
- package/dist/triggers.generated.js +239 -0
- package/dist/triggers.js +0 -0
- package/dist/types.d.ts +18 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/worker.d.ts +72 -2
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +85 -4
- package/package.json +81 -14
- package/src/alpha/ajv-formats.d.ts +7 -0
- package/src/alpha/builder.test.ts +197 -0
- package/src/alpha/builder.ts +338 -0
- package/src/alpha/cli/build.test.ts +240 -0
- package/src/alpha/cli/build.ts +57 -0
- package/src/alpha/cli/codegen.test.ts +31 -0
- package/src/alpha/cli/codegen.ts +51 -0
- package/src/alpha/cli/discover.test.ts +43 -0
- package/src/alpha/cli/discover.ts +81 -0
- package/src/alpha/cli/emit-manifest.test.ts +45 -0
- package/src/alpha/cli/emit-manifest.ts +409 -0
- package/src/alpha/cli/index.ts +41 -0
- package/src/alpha/context.ts +61 -0
- package/src/alpha/database.ts +102 -0
- package/src/alpha/error.ts +49 -0
- package/src/alpha/icon-names.ts +890 -0
- package/src/alpha/json-schema.ts +180 -0
- package/src/alpha/manifest.ts +47 -0
- package/src/alpha/output.ts +8 -0
- package/src/alpha/pacer.test.ts +81 -0
- package/src/alpha/pacer.ts +181 -0
- package/src/alpha/schedule.test.ts +31 -0
- package/src/alpha/schedule.ts +53 -0
- package/src/alpha/schema-builder.ts +193 -0
- package/src/alpha/schema.ts +222 -0
- package/src/alpha/sync.test.ts +191 -0
- package/src/alpha/sync.ts +340 -0
- package/src/alpha/tool.test.ts +116 -0
- package/src/alpha/tool.ts +319 -0
- package/src/alpha/triggers.generated.ts +489 -0
- package/src/alpha/triggers.ts +500 -0
- package/src/alpha/types.ts +284 -0
- package/src/alpha/workflow.test.ts +84 -0
- package/src/alpha/workflow.ts +131 -0
- package/src/builder.test.ts +28 -60
- package/src/builder.ts +20 -7
- package/src/capabilities/ai_connector.test.ts +9 -32
- package/src/capabilities/ai_connector.ts +5 -17
- package/src/capabilities/automation.test.ts +3 -12
- package/src/capabilities/automation.ts +3 -11
- package/src/capabilities/context.ts +5 -8
- package/src/capabilities/custom-block.ts +181 -0
- package/src/capabilities/oauth.test.ts +1 -0
- package/src/capabilities/oauth.ts +1 -3
- package/src/capabilities/output.ts +1 -3
- package/src/capabilities/stateful-capability.test.ts +1 -0
- package/src/capabilities/stateful-capability.ts +3 -7
- package/src/capabilities/sync.test.ts +76 -42
- package/src/capabilities/sync.ts +16 -40
- package/src/capabilities/tool.test.ts +106 -146
- package/src/capabilities/tool.ts +17 -39
- package/src/capabilities/webhook.ts +3 -10
- package/src/capabilities/workflow.test.ts +163 -0
- package/src/capabilities/workflow.ts +112 -0
- package/src/index.ts +18 -6
- package/src/json-schema.test.ts +2 -4
- package/src/json-schema.ts +7 -6
- package/src/pacer.test.ts +1 -3
- package/src/pacer_internal.ts +1 -5
- package/src/schema-builder.test.ts +10 -35
- package/src/schema-builder.ts +6 -16
- package/src/schema.ts +2 -10
- package/src/triggers.generated.ts +489 -0
- package/src/triggers.ts +504 -0
- package/src/types.ts +21 -7
- package/src/worker.test.ts +147 -0
- package/src/worker.ts +155 -32
- package/dist/block.d.ts +0 -321
- package/dist/block.d.ts.map +0 -1
- package/src/block.ts +0 -529
- /package/dist/{block.js → alpha/ajv-formats.d.js} +0 -0
package/src/capabilities/tool.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Ajv } from "ajv";
|
|
2
2
|
import addFormats from "ajv-formats";
|
|
3
|
+
|
|
3
4
|
import type { AnyJSONSchema } from "../json-schema.js";
|
|
4
5
|
import type { SchemaBuilder } from "../schema-builder.js";
|
|
5
6
|
import { getSchema } from "../schema-builder.js";
|
|
@@ -15,10 +16,7 @@ import { writeOutput } from "./output.js";
|
|
|
15
16
|
* Throws a descriptive error at tool registration time (not request time)
|
|
16
17
|
* so the developer sees it immediately.
|
|
17
18
|
*/
|
|
18
|
-
function validateRequiredProperties(
|
|
19
|
-
schema: AnyJSONSchema,
|
|
20
|
-
path: string = "",
|
|
21
|
-
): void {
|
|
19
|
+
function validateRequiredProperties(schema: AnyJSONSchema, path: string = ""): void {
|
|
22
20
|
if ("type" in schema && schema.type === "object" && "properties" in schema) {
|
|
23
21
|
const propKeys = Object.keys(schema.properties).sort();
|
|
24
22
|
const required = [...(schema.required ?? [])].sort();
|
|
@@ -43,20 +41,14 @@ function validateRequiredProperties(
|
|
|
43
41
|
// Recurse into $defs
|
|
44
42
|
if ("$defs" in schema && schema.$defs) {
|
|
45
43
|
for (const [key, defSchema] of Object.entries(schema.$defs)) {
|
|
46
|
-
validateRequiredProperties(
|
|
47
|
-
defSchema,
|
|
48
|
-
path ? `${path}.$defs.${key}` : `$defs.${key}`,
|
|
49
|
-
);
|
|
44
|
+
validateRequiredProperties(defSchema, path ? `${path}.$defs.${key}` : `$defs.${key}`);
|
|
50
45
|
}
|
|
51
46
|
}
|
|
52
47
|
}
|
|
53
48
|
|
|
54
49
|
// Recurse into array items
|
|
55
50
|
if ("type" in schema && schema.type === "array" && "items" in schema) {
|
|
56
|
-
validateRequiredProperties(
|
|
57
|
-
schema.items as AnyJSONSchema,
|
|
58
|
-
path ? `${path}.items` : "items",
|
|
59
|
-
);
|
|
51
|
+
validateRequiredProperties(schema.items as AnyJSONSchema, path ? `${path}.items` : "items");
|
|
60
52
|
}
|
|
61
53
|
|
|
62
54
|
// Recurse into anyOf branches
|
|
@@ -90,10 +82,7 @@ export interface ToolHints {
|
|
|
90
82
|
readOnlyHint?: boolean;
|
|
91
83
|
}
|
|
92
84
|
|
|
93
|
-
export interface ToolConfiguration<
|
|
94
|
-
I extends JSONValue,
|
|
95
|
-
O extends JSONValue = JSONValue,
|
|
96
|
-
> {
|
|
85
|
+
export interface ToolConfiguration<I extends JSONValue, O extends JSONValue = JSONValue> {
|
|
97
86
|
title: string;
|
|
98
87
|
description: string;
|
|
99
88
|
schema: SchemaBuilder<I>;
|
|
@@ -156,10 +145,9 @@ export class ToolExecutionError extends Error {
|
|
|
156
145
|
}
|
|
157
146
|
}
|
|
158
147
|
|
|
159
|
-
export type ToolCapability<
|
|
160
|
-
I
|
|
161
|
-
|
|
162
|
-
> = ReturnType<typeof createToolCapability<I, O>>;
|
|
148
|
+
export type ToolCapability<I extends JSONValue, O extends JSONValue = JSONValue> = ReturnType<
|
|
149
|
+
typeof createToolCapability<I, O>
|
|
150
|
+
>;
|
|
163
151
|
|
|
164
152
|
/**
|
|
165
153
|
* Creates a capability definition for a tool to be used by an agent.
|
|
@@ -167,16 +155,14 @@ export type ToolCapability<
|
|
|
167
155
|
* @param config - The configuration for the tool.
|
|
168
156
|
* @returns A capability definition for the tool.
|
|
169
157
|
*/
|
|
170
|
-
export function createToolCapability<
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
158
|
+
export function createToolCapability<I extends JSONValue, O extends JSONValue = JSONValue>(
|
|
159
|
+
key: string,
|
|
160
|
+
config: ToolConfiguration<I, O>,
|
|
161
|
+
) {
|
|
174
162
|
const inputSchema = getSchema(config.schema);
|
|
175
163
|
validateRequiredProperties(inputSchema as AnyJSONSchema);
|
|
176
164
|
|
|
177
|
-
const outputSchema = config.outputSchema
|
|
178
|
-
? getSchema(config.outputSchema)
|
|
179
|
-
: undefined;
|
|
165
|
+
const outputSchema = config.outputSchema ? getSchema(config.outputSchema) : undefined;
|
|
180
166
|
|
|
181
167
|
if (outputSchema) {
|
|
182
168
|
validateRequiredProperties(outputSchema as AnyJSONSchema);
|
|
@@ -215,14 +201,10 @@ export function createToolCapability<
|
|
|
215
201
|
if (!validateInput(input)) {
|
|
216
202
|
// ajv resets the "errors" property on each validation call.
|
|
217
203
|
if (validateInput.errors == null) {
|
|
218
|
-
throw new Error(
|
|
219
|
-
"Unexpected: No validation errors after failed validation",
|
|
220
|
-
);
|
|
204
|
+
throw new Error("Unexpected: No validation errors after failed validation");
|
|
221
205
|
}
|
|
222
206
|
|
|
223
|
-
const error = new InvalidToolInputError(
|
|
224
|
-
JSON.stringify(validateInput.errors, null, 2),
|
|
225
|
-
);
|
|
207
|
+
const error = new InvalidToolInputError(JSON.stringify(validateInput.errors, null, 2));
|
|
226
208
|
|
|
227
209
|
if (options?.concreteOutput) {
|
|
228
210
|
throw error;
|
|
@@ -242,9 +224,7 @@ export function createToolCapability<
|
|
|
242
224
|
const capabilityContext = createCapabilityContext("tool");
|
|
243
225
|
const result = await config.execute(input, capabilityContext);
|
|
244
226
|
if (validateOutput && !validateOutput(result)) {
|
|
245
|
-
const error = new InvalidToolOutputError(
|
|
246
|
-
JSON.stringify(validateOutput.errors, null, 2),
|
|
247
|
-
);
|
|
227
|
+
const error = new InvalidToolOutputError(JSON.stringify(validateOutput.errors, null, 2));
|
|
248
228
|
|
|
249
229
|
if (options?.concreteOutput) {
|
|
250
230
|
throw error;
|
|
@@ -275,9 +255,7 @@ export function createToolCapability<
|
|
|
275
255
|
value: result,
|
|
276
256
|
};
|
|
277
257
|
} catch (err) {
|
|
278
|
-
const error = new ToolExecutionError(
|
|
279
|
-
err instanceof Error ? err.message : String(err),
|
|
280
|
-
);
|
|
258
|
+
const error = new ToolExecutionError(err instanceof Error ? err.message : String(err));
|
|
281
259
|
|
|
282
260
|
if (options?.concreteOutput) {
|
|
283
261
|
throw error;
|
|
@@ -94,18 +94,12 @@ export interface WebhookConfiguration {
|
|
|
94
94
|
* @param context - The capability execution context (Notion client, etc.)
|
|
95
95
|
* @returns A promise that resolves when the webhook processing completes
|
|
96
96
|
*/
|
|
97
|
-
execute: (
|
|
98
|
-
events: WebhookEvent[],
|
|
99
|
-
context: CapabilityContext,
|
|
100
|
-
) => Promise<void> | void;
|
|
97
|
+
execute: (events: WebhookEvent[], context: CapabilityContext) => Promise<void> | void;
|
|
101
98
|
}
|
|
102
99
|
|
|
103
100
|
export type WebhookCapability = ReturnType<typeof createWebhookCapability>;
|
|
104
101
|
|
|
105
|
-
export function createWebhookCapability(
|
|
106
|
-
key: string,
|
|
107
|
-
config: WebhookConfiguration,
|
|
108
|
-
) {
|
|
102
|
+
export function createWebhookCapability(key: string, config: WebhookConfiguration) {
|
|
109
103
|
return {
|
|
110
104
|
_tag: "webhook" as const,
|
|
111
105
|
key,
|
|
@@ -127,8 +121,7 @@ export function createWebhookCapability(
|
|
|
127
121
|
writeOutput({ _tag: "success", value: { status: "success" } });
|
|
128
122
|
}
|
|
129
123
|
} catch (err) {
|
|
130
|
-
const error =
|
|
131
|
-
err instanceof ExecutionError ? err : new ExecutionError(err);
|
|
124
|
+
const error = err instanceof ExecutionError ? err : new ExecutionError(err);
|
|
132
125
|
|
|
133
126
|
if (!options?.concreteOutput) {
|
|
134
127
|
writeOutput({
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, expectTypeOf, it, type Mock, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { ExecutionError } from "../error.js";
|
|
4
|
+
import { type CapabilityContext, Worker, type WorkflowConfiguration } from "../index.js";
|
|
5
|
+
import {
|
|
6
|
+
type NotionPageCreatedEvent,
|
|
7
|
+
type NotionPageCreatedTrigger,
|
|
8
|
+
triggers,
|
|
9
|
+
} from "../triggers.generated.js";
|
|
10
|
+
import { createWorkflowCapability } from "./workflow.js";
|
|
11
|
+
|
|
12
|
+
const pageCreatedEvent: NotionPageCreatedEvent = {
|
|
13
|
+
type: "notion.page.created",
|
|
14
|
+
vendor: "notion",
|
|
15
|
+
message: "Page created",
|
|
16
|
+
actor: {
|
|
17
|
+
id: "user-1",
|
|
18
|
+
displayName: "Ada Lovelace",
|
|
19
|
+
email: "ada@example.com",
|
|
20
|
+
vendorActor: {
|
|
21
|
+
vendor: "notion",
|
|
22
|
+
id: "user-1",
|
|
23
|
+
displayName: "Ada Lovelace",
|
|
24
|
+
handle: null,
|
|
25
|
+
type: "user",
|
|
26
|
+
url: null,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
url: "https://www.notion.so/page-1",
|
|
30
|
+
timestamp: "2026-06-26T12:00:00.000Z",
|
|
31
|
+
page: {
|
|
32
|
+
id: "page-1",
|
|
33
|
+
Name: "Welcome",
|
|
34
|
+
Status: "New",
|
|
35
|
+
},
|
|
36
|
+
content: "# Welcome\n\nHello, world!",
|
|
37
|
+
edits: [
|
|
38
|
+
{
|
|
39
|
+
before: null,
|
|
40
|
+
after: {
|
|
41
|
+
id: "page-1",
|
|
42
|
+
Name: "Welcome",
|
|
43
|
+
Status: "New",
|
|
44
|
+
},
|
|
45
|
+
afterContent: "# Welcome\n\nHello, world!",
|
|
46
|
+
contentChanged: true,
|
|
47
|
+
timestamp: 1_782_477_600_000,
|
|
48
|
+
editedBy: null,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
describe("createWorkflowCapability", () => {
|
|
54
|
+
let stdoutSpy: Mock<typeof process.stdout.write>;
|
|
55
|
+
|
|
56
|
+
beforeEach(() => {
|
|
57
|
+
stdoutSpy = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
afterEach(() => {
|
|
61
|
+
vi.restoreAllMocks();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("declares a page-created trigger without a database", () => {
|
|
65
|
+
expect(triggers.notionPageCreated()).toEqual({
|
|
66
|
+
type: "notion.page.created",
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("requires trigger declarations in workflow configuration", () => {
|
|
71
|
+
type PageAddedConfiguration = WorkflowConfiguration<readonly [NotionPageCreatedTrigger]>;
|
|
72
|
+
type ConfigurationWithoutTriggers = Omit<PageAddedConfiguration, "triggers">;
|
|
73
|
+
|
|
74
|
+
expectTypeOf<ConfigurationWithoutTriggers>().not.toMatchTypeOf<PageAddedConfiguration>();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("infers the canonical page-created event and capability context", () => {
|
|
78
|
+
createWorkflowCapability("pageAdded", {
|
|
79
|
+
title: "Page Added",
|
|
80
|
+
description: "Runs when a page is added",
|
|
81
|
+
triggers: [triggers.notionPageCreated()],
|
|
82
|
+
execute: (event, context) => {
|
|
83
|
+
expectTypeOf(event).toEqualTypeOf<NotionPageCreatedEvent>();
|
|
84
|
+
expectTypeOf(event.type).toEqualTypeOf<"notion.page.created">();
|
|
85
|
+
expectTypeOf(context).toEqualTypeOf<CapabilityContext>();
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("includes triggers in capability config and the worker manifest", () => {
|
|
91
|
+
const worker = new Worker();
|
|
92
|
+
worker.workflow("pageAdded", {
|
|
93
|
+
title: "Page Added",
|
|
94
|
+
description: "Runs when a page is added",
|
|
95
|
+
triggers: [triggers.notionPageCreated()],
|
|
96
|
+
execute: () => {},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(worker.manifest.capabilities).toEqual([
|
|
100
|
+
{
|
|
101
|
+
_tag: "workflow",
|
|
102
|
+
key: "pageAdded",
|
|
103
|
+
config: {
|
|
104
|
+
title: "Page Added",
|
|
105
|
+
description: "Runs when a page is added",
|
|
106
|
+
triggers: [{ type: "notion.page.created" }],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
]);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("executes with the public event and capability context", async () => {
|
|
113
|
+
const execute = vi.fn();
|
|
114
|
+
const capability = createWorkflowCapability("pageAdded", {
|
|
115
|
+
title: "Page Added",
|
|
116
|
+
description: "Runs when a page is added",
|
|
117
|
+
triggers: [triggers.notionPageCreated()],
|
|
118
|
+
execute,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
await expect(capability.handler(pageCreatedEvent, { concreteOutput: true })).resolves.toEqual({
|
|
122
|
+
status: "success",
|
|
123
|
+
});
|
|
124
|
+
expect(execute).toHaveBeenCalledWith(
|
|
125
|
+
pageCreatedEvent,
|
|
126
|
+
expect.objectContaining({ notion: expect.any(Object) }),
|
|
127
|
+
);
|
|
128
|
+
expect(stdoutSpy).not.toHaveBeenCalled();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("writes the success envelope by default", async () => {
|
|
132
|
+
const capability = createWorkflowCapability("pageAdded", {
|
|
133
|
+
title: "Page Added",
|
|
134
|
+
description: "Runs when a page is added",
|
|
135
|
+
triggers: [triggers.notionPageCreated()],
|
|
136
|
+
execute: () => {},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
await capability.handler(pageCreatedEvent);
|
|
140
|
+
|
|
141
|
+
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
142
|
+
'\n<__notion_output__>{"_tag":"success","value":{"status":"success"}}</__notion_output__>\n',
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("writes and throws an execution error", async () => {
|
|
147
|
+
const capability = createWorkflowCapability("pageAdded", {
|
|
148
|
+
title: "Page Added",
|
|
149
|
+
description: "Runs when a page is added",
|
|
150
|
+
triggers: [triggers.notionPageCreated()],
|
|
151
|
+
execute: () => {
|
|
152
|
+
throw new Error("Something went wrong");
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
await expect(capability.handler(pageCreatedEvent)).rejects.toThrow(ExecutionError);
|
|
157
|
+
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
158
|
+
expect.stringContaining(
|
|
159
|
+
'"_tag":"error","error":{"name":"ExecutionError","message":"Error during worker execution: Error: Something went wrong"',
|
|
160
|
+
),
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { ExecutionError } from "../error.js";
|
|
2
|
+
import type { WorkflowEventMap, WorkflowTrigger } from "../triggers.generated.js";
|
|
3
|
+
import type { HandlerOptions } from "../types.js";
|
|
4
|
+
import type { CapabilityContext } from "./context.js";
|
|
5
|
+
import { createCapabilityContext } from "./context.js";
|
|
6
|
+
import { writeOutput } from "./output.js";
|
|
7
|
+
|
|
8
|
+
export type WorkflowEvent = WorkflowEventMap[keyof WorkflowEventMap];
|
|
9
|
+
|
|
10
|
+
export type WorkflowEventForTrigger<T extends WorkflowTrigger> = WorkflowEventMap[T["type"]];
|
|
11
|
+
|
|
12
|
+
export type WorkflowEventForTriggers<T extends readonly WorkflowTrigger[]> =
|
|
13
|
+
WorkflowEventForTrigger<T[number]>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Configuration for a workflow capability
|
|
17
|
+
*/
|
|
18
|
+
export type WorkflowConfiguration<
|
|
19
|
+
TTriggers extends readonly [WorkflowTrigger, ...WorkflowTrigger[]],
|
|
20
|
+
> = {
|
|
21
|
+
/**
|
|
22
|
+
* A human-readable title for the workflow, shown in the UI when viewing workflows
|
|
23
|
+
*/
|
|
24
|
+
title: string;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A human-readable description of what the workflow does, shown in the UI when viewing workflows
|
|
28
|
+
*/
|
|
29
|
+
description: string;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* An array of triggers that can invoke this workflow.
|
|
33
|
+
*
|
|
34
|
+
* Each trigger defines a specific event or condition that causes the workflow to run.
|
|
35
|
+
*/
|
|
36
|
+
triggers: TTriggers;
|
|
37
|
+
|
|
38
|
+
execute: (
|
|
39
|
+
event: WorkflowEventForTriggers<TTriggers>,
|
|
40
|
+
context: CapabilityContext,
|
|
41
|
+
) => Promise<void> | void;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type WorkflowCapability<
|
|
45
|
+
TTriggers extends readonly [WorkflowTrigger, ...WorkflowTrigger[]] = readonly [
|
|
46
|
+
WorkflowTrigger,
|
|
47
|
+
...WorkflowTrigger[],
|
|
48
|
+
],
|
|
49
|
+
> = {
|
|
50
|
+
_tag: "workflow";
|
|
51
|
+
key: string;
|
|
52
|
+
config: {
|
|
53
|
+
title: string;
|
|
54
|
+
description: string;
|
|
55
|
+
triggers: TTriggers;
|
|
56
|
+
};
|
|
57
|
+
handler: (
|
|
58
|
+
event: WorkflowEventForTriggers<TTriggers>,
|
|
59
|
+
options?: HandlerOptions,
|
|
60
|
+
) => Promise<{ status: "success" } | undefined>;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Creates a workflow capability from configuration.
|
|
65
|
+
*
|
|
66
|
+
* @param key - The unique name for this capability.
|
|
67
|
+
* @param config - The workflow configuration.
|
|
68
|
+
* @returns The capability object.
|
|
69
|
+
*/
|
|
70
|
+
export function createWorkflowCapability<
|
|
71
|
+
const TTriggers extends readonly [WorkflowTrigger, ...WorkflowTrigger[]],
|
|
72
|
+
>(key: string, config: WorkflowConfiguration<TTriggers>): WorkflowCapability<TTriggers> {
|
|
73
|
+
return {
|
|
74
|
+
_tag: "workflow",
|
|
75
|
+
key,
|
|
76
|
+
config: {
|
|
77
|
+
title: config.title,
|
|
78
|
+
description: config.description,
|
|
79
|
+
triggers: config.triggers,
|
|
80
|
+
},
|
|
81
|
+
async handler(
|
|
82
|
+
event: WorkflowEventForTriggers<TTriggers>,
|
|
83
|
+
options?: HandlerOptions,
|
|
84
|
+
): Promise<{ status: "success" } | undefined> {
|
|
85
|
+
try {
|
|
86
|
+
const capabilityContext = createCapabilityContext("workflow");
|
|
87
|
+
await config.execute(event, capabilityContext);
|
|
88
|
+
|
|
89
|
+
if (options?.concreteOutput) {
|
|
90
|
+
return { status: "success" };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
writeOutput({ _tag: "success", value: { status: "success" } });
|
|
94
|
+
} catch (err) {
|
|
95
|
+
const error = new ExecutionError(err);
|
|
96
|
+
|
|
97
|
+
if (!options?.concreteOutput) {
|
|
98
|
+
writeOutput({
|
|
99
|
+
_tag: "error",
|
|
100
|
+
error: {
|
|
101
|
+
name: error.name,
|
|
102
|
+
message: error.message,
|
|
103
|
+
trace: error.stack,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export type {
|
|
2
|
+
CustomBlockCapability,
|
|
3
|
+
CustomBlockCapabilityConfig,
|
|
4
|
+
CustomBlockConfiguration,
|
|
5
|
+
CustomBlockManifest,
|
|
6
|
+
CustomBlockManifestDataSource,
|
|
7
|
+
} from "./capabilities/custom-block.js";
|
|
8
|
+
export { emojiIcon, imageCover, imageIcon, notionIcon, place } from "./builder.js";
|
|
2
9
|
export type {
|
|
3
10
|
AiConnectorArchetype,
|
|
4
11
|
AiConnectorCapability,
|
|
@@ -37,23 +44,28 @@ export type {
|
|
|
37
44
|
SyncExecutionResult,
|
|
38
45
|
SyncMode,
|
|
39
46
|
} from "./capabilities/sync.js";
|
|
40
|
-
export type {
|
|
41
|
-
ToolCapability,
|
|
42
|
-
ToolConfiguration,
|
|
43
|
-
ToolHints,
|
|
44
|
-
} from "./capabilities/tool.js";
|
|
47
|
+
export type { ToolCapability, ToolConfiguration, ToolHints } from "./capabilities/tool.js";
|
|
45
48
|
export type {
|
|
46
49
|
WebhookCapability,
|
|
47
50
|
WebhookConfiguration,
|
|
48
51
|
WebhookEvent,
|
|
49
52
|
} from "./capabilities/webhook.js";
|
|
50
53
|
export { WebhookVerificationError } from "./capabilities/webhook.js";
|
|
54
|
+
export type {
|
|
55
|
+
WorkflowCapability,
|
|
56
|
+
WorkflowConfiguration,
|
|
57
|
+
WorkflowEvent,
|
|
58
|
+
WorkflowEventForTrigger,
|
|
59
|
+
WorkflowEventForTriggers,
|
|
60
|
+
} from "./capabilities/workflow.js";
|
|
51
61
|
export { RateLimitError } from "./error.js";
|
|
52
62
|
export type { AnyJSONSchema, JSONSchema } from "./json-schema.js";
|
|
53
63
|
export type { Infer, SchemaBuilder } from "./schema-builder.js";
|
|
54
64
|
export { getSchema, j } from "./schema-builder.js";
|
|
55
65
|
export type {
|
|
66
|
+
Cover,
|
|
56
67
|
Icon,
|
|
68
|
+
ImageCover,
|
|
57
69
|
ImageIcon,
|
|
58
70
|
NoticonColor,
|
|
59
71
|
NoticonName,
|
package/src/json-schema.test.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* This ensures tsc sees the error on the line immediately after the directive.
|
|
13
13
|
*/
|
|
14
14
|
import { describe, expect, it } from "vitest";
|
|
15
|
+
|
|
15
16
|
import type {
|
|
16
17
|
AnyJSONSchema,
|
|
17
18
|
JSONSchema,
|
|
@@ -671,10 +672,7 @@ describe("JSONSchema — complex schemas", () => {
|
|
|
671
672
|
|
|
672
673
|
describe("JSONSchema — structural type assertions", () => {
|
|
673
674
|
it("JSONSchemaObjectDef requires additionalProperties to be false", () => {
|
|
674
|
-
type _Proof = Assert<
|
|
675
|
-
JSONSchemaObjectDef<{ x: number }>["additionalProperties"],
|
|
676
|
-
false
|
|
677
|
-
>;
|
|
675
|
+
type _Proof = Assert<JSONSchemaObjectDef<{ x: number }>["additionalProperties"], false>;
|
|
678
676
|
});
|
|
679
677
|
|
|
680
678
|
it("JSONSchemaArrayDef minItems is restricted to 0 | 1", () => {
|
package/src/json-schema.ts
CHANGED
|
@@ -45,16 +45,18 @@ interface ForbiddenCompositionKeywords {
|
|
|
45
45
|
|
|
46
46
|
// ---- Individual schema definitions ----
|
|
47
47
|
|
|
48
|
-
export interface JSONSchemaStringDef<
|
|
49
|
-
extends
|
|
48
|
+
export interface JSONSchemaStringDef<
|
|
49
|
+
T extends string = string,
|
|
50
|
+
> extends ForbiddenCompositionKeywords {
|
|
50
51
|
type: "string";
|
|
51
52
|
description?: string;
|
|
52
53
|
format?: JSONSchemaStringFormat;
|
|
53
54
|
enum?: readonly T[];
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
export interface JSONSchemaNumberDef<
|
|
57
|
-
extends
|
|
57
|
+
export interface JSONSchemaNumberDef<
|
|
58
|
+
T extends number = number,
|
|
59
|
+
> extends ForbiddenCompositionKeywords {
|
|
58
60
|
type: "number" | "integer";
|
|
59
61
|
description?: string;
|
|
60
62
|
enum?: readonly T[];
|
|
@@ -70,8 +72,7 @@ export interface JSONSchemaNullDef extends ForbiddenCompositionKeywords {
|
|
|
70
72
|
description?: string;
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
export interface JSONSchemaArrayDef<E = unknown>
|
|
74
|
-
extends ForbiddenCompositionKeywords {
|
|
75
|
+
export interface JSONSchemaArrayDef<E = unknown> extends ForbiddenCompositionKeywords {
|
|
75
76
|
type: "array";
|
|
76
77
|
description?: string;
|
|
77
78
|
items: JSONSchema<E>;
|
package/src/pacer.test.ts
CHANGED
|
@@ -83,9 +83,7 @@ describe("pacerWait", () => {
|
|
|
83
83
|
it("throws when pacer key not found in runtime context", async () => {
|
|
84
84
|
setPacerState({ pacers: {} });
|
|
85
85
|
|
|
86
|
-
await expect(pacerWait("unknown")).rejects.toThrow(
|
|
87
|
-
'Pacer "unknown" not found',
|
|
88
|
-
);
|
|
86
|
+
await expect(pacerWait("unknown")).rejects.toThrow('Pacer "unknown" not found');
|
|
89
87
|
});
|
|
90
88
|
|
|
91
89
|
it("preserves allowedRequests and intervalMs across calls", async () => {
|
package/src/pacer_internal.ts
CHANGED
|
@@ -39,11 +39,7 @@ export function initPacerState(
|
|
|
39
39
|
declarations?: readonly PacerDeclaration[],
|
|
40
40
|
): void {
|
|
41
41
|
const pacers = runtimePacers ?? {};
|
|
42
|
-
if (
|
|
43
|
-
Object.keys(pacers).length === 0 &&
|
|
44
|
-
declarations &&
|
|
45
|
-
declarations.length > 0
|
|
46
|
-
) {
|
|
42
|
+
if (Object.keys(pacers).length === 0 && declarations && declarations.length > 0) {
|
|
47
43
|
const localPacers: Record<string, PacerEntry> = {};
|
|
48
44
|
for (const decl of declarations) {
|
|
49
45
|
localPacers[decl.key] = {
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
2
3
|
import type { JSONSchema } from "./json-schema.js";
|
|
3
|
-
import {
|
|
4
|
-
getSchema,
|
|
5
|
-
type Infer,
|
|
6
|
-
j,
|
|
7
|
-
type SchemaBuilder,
|
|
8
|
-
} from "./schema-builder.js";
|
|
4
|
+
import { getSchema, type Infer, j, type SchemaBuilder } from "./schema-builder.js";
|
|
9
5
|
|
|
10
6
|
// ---------------------------------------------------------------------------
|
|
11
7
|
// 1. Primitives
|
|
@@ -188,9 +184,7 @@ describe("json-schema-builder — array", () => {
|
|
|
188
184
|
});
|
|
189
185
|
|
|
190
186
|
it("array of objects", () => {
|
|
191
|
-
const schema = getSchema(
|
|
192
|
-
j.array(j.object({ name: j.string(), age: j.number() })),
|
|
193
|
-
);
|
|
187
|
+
const schema = getSchema(j.array(j.object({ name: j.string(), age: j.number() })));
|
|
194
188
|
expect(schema).toEqual({
|
|
195
189
|
type: "array",
|
|
196
190
|
items: {
|
|
@@ -351,9 +345,7 @@ describe("json-schema-builder — anyOf", () => {
|
|
|
351
345
|
});
|
|
352
346
|
|
|
353
347
|
it("anyOf with description", () => {
|
|
354
|
-
expect(
|
|
355
|
-
getSchema(j.anyOf(j.string(), j.number()).describe("string or number")),
|
|
356
|
-
).toEqual({
|
|
348
|
+
expect(getSchema(j.anyOf(j.string(), j.number()).describe("string or number"))).toEqual({
|
|
357
349
|
anyOf: [{ type: "string" }, { type: "number" }],
|
|
358
350
|
description: "string or number",
|
|
359
351
|
});
|
|
@@ -463,12 +455,8 @@ describe("json-schema-builder — discriminated unions", () => {
|
|
|
463
455
|
|
|
464
456
|
it("discriminated union with descriptions on individual variants", () => {
|
|
465
457
|
const schema = j.anyOf(
|
|
466
|
-
j
|
|
467
|
-
|
|
468
|
-
.describe("A text node"),
|
|
469
|
-
j
|
|
470
|
-
.object({ type: j.enum("image"), url: j.string() })
|
|
471
|
-
.describe("An image node"),
|
|
458
|
+
j.object({ type: j.enum("text"), value: j.string() }).describe("A text node"),
|
|
459
|
+
j.object({ type: j.enum("image"), url: j.string() }).describe("An image node"),
|
|
472
460
|
);
|
|
473
461
|
|
|
474
462
|
const raw = getSchema(schema) as unknown as {
|
|
@@ -497,9 +485,7 @@ describe("json-schema-builder — discriminated unions", () => {
|
|
|
497
485
|
expect(raw.properties.content.anyOf).toHaveLength(2);
|
|
498
486
|
|
|
499
487
|
// The content field is in required
|
|
500
|
-
expect(
|
|
501
|
-
(getSchema(schema) as unknown as Record<string, unknown>).required,
|
|
502
|
-
).toContain("content");
|
|
488
|
+
expect((getSchema(schema) as unknown as Record<string, unknown>).required).toContain("content");
|
|
503
489
|
});
|
|
504
490
|
|
|
505
491
|
it("nullable discriminated union", () => {
|
|
@@ -614,15 +600,7 @@ describe("json-schema-builder — integration", () => {
|
|
|
614
600
|
},
|
|
615
601
|
createdAt: { type: "string", format: "date-time" },
|
|
616
602
|
},
|
|
617
|
-
required: [
|
|
618
|
-
"foo",
|
|
619
|
-
"bar",
|
|
620
|
-
"count",
|
|
621
|
-
"tags",
|
|
622
|
-
"address",
|
|
623
|
-
"nickname",
|
|
624
|
-
"createdAt",
|
|
625
|
-
],
|
|
603
|
+
required: ["foo", "bar", "count", "tags", "address", "nickname", "createdAt"],
|
|
626
604
|
additionalProperties: false,
|
|
627
605
|
});
|
|
628
606
|
});
|
|
@@ -634,9 +612,7 @@ describe("json-schema-builder — integration", () => {
|
|
|
634
612
|
|
|
635
613
|
describe("json-schema-builder — constraint enforcement", () => {
|
|
636
614
|
it("object always has required matching properties keys", () => {
|
|
637
|
-
const schema = getSchema(
|
|
638
|
-
j.object({ x: j.number(), y: j.string(), z: j.boolean() }),
|
|
639
|
-
);
|
|
615
|
+
const schema = getSchema(j.object({ x: j.number(), y: j.string(), z: j.boolean() }));
|
|
640
616
|
const raw = schema as unknown as Record<string, unknown>;
|
|
641
617
|
expect(raw.required).toEqual(["x", "y", "z"]);
|
|
642
618
|
expect(raw.additionalProperties).toBe(false);
|
|
@@ -669,8 +645,7 @@ describe("json-schema-builder — constraint enforcement", () => {
|
|
|
669
645
|
}),
|
|
670
646
|
);
|
|
671
647
|
const raw = schema as unknown as Record<string, unknown>;
|
|
672
|
-
const outer = (raw as { properties: { outer: Record<string, unknown> } })
|
|
673
|
-
.properties.outer;
|
|
648
|
+
const outer = (raw as { properties: { outer: Record<string, unknown> } }).properties.outer;
|
|
674
649
|
expect(outer.required).toEqual(["inner"]);
|
|
675
650
|
expect(outer.additionalProperties).toBe(false);
|
|
676
651
|
});
|