@project-ajax/sdk 0.0.62 → 0.0.63

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.
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Context provided to automation execute functions
3
+ */
4
+ export interface AutomationContext {
5
+ /**
6
+ * The ID of the page that triggered the automation (if applicable)
7
+ */
8
+ pageId?: string;
9
+ /**
10
+ * The type of automation action that was triggered
11
+ */
12
+ actionType: string;
13
+ /**
14
+ * The full page object from Notion's Public API (if triggered by a database page)
15
+ */
16
+ pageData?: PageObjectResponse;
17
+ }
18
+ /**
19
+ * Page object from Notion's Public API
20
+ * This represents a database page with all its properties.
21
+ * Properties are in Notion's Public API format.
22
+ * See: https://developers.notion.com/reference/page
23
+ */
24
+ export interface PageObjectResponse {
25
+ object: "page";
26
+ id: string;
27
+ created_time: string;
28
+ last_edited_time: string;
29
+ created_by: {
30
+ id: string;
31
+ };
32
+ last_edited_by: {
33
+ id: string;
34
+ };
35
+ cover: unknown;
36
+ icon: unknown;
37
+ parent: unknown;
38
+ archived: boolean;
39
+ properties: Record<string, unknown>;
40
+ url: string;
41
+ public_url?: string | null;
42
+ }
43
+ /**
44
+ * Configuration for an automation capability
45
+ */
46
+ export interface AutomationConfiguration {
47
+ /**
48
+ * Title of the automation - shown in the UI when selecting automations
49
+ */
50
+ title: string;
51
+ /**
52
+ * Description of what this automation does - shown in the UI
53
+ */
54
+ description: string;
55
+ /**
56
+ * The function that executes when the automation is triggered
57
+ * @param context - Context about the automation trigger, including page data if applicable
58
+ * @returns A promise that resolves when the automation completes
59
+ */
60
+ execute: (context: AutomationContext) => Promise<void> | void;
61
+ }
62
+ /**
63
+ * Result returned from automation execution
64
+ */
65
+ export interface AutomationHandlerResult {
66
+ title: string;
67
+ description: string;
68
+ }
69
+ /**
70
+ * Creates a capability definition for an automation that can be triggered
71
+ * from database automations in Notion.
72
+ *
73
+ * Example:
74
+ *
75
+ * ```ts
76
+ * automation({
77
+ * title: "Send Welcome Email",
78
+ * description: "Sends a welcome email when a new user is added",
79
+ * execute: async (context) => {
80
+ * const { pageId, pageData } = context;
81
+ *
82
+ * // Access page properties from the Public API format
83
+ * if (pageData) {
84
+ * const name = pageData.properties.Name; // Access any property
85
+ * const status = pageData.properties.Status;
86
+ * console.log(`Processing: ${name}`);
87
+ * }
88
+ *
89
+ * // Your automation logic here
90
+ * await sendEmail(pageId);
91
+ * },
92
+ * })
93
+ * ```
94
+ *
95
+ * @param config - The configuration for the automation.
96
+ * @returns A capability definition for the automation.
97
+ */
98
+ export declare function automation(config: AutomationConfiguration): {
99
+ _tag: string;
100
+ config: {
101
+ title: string;
102
+ description: string;
103
+ };
104
+ handler(context: AutomationContext): Promise<void>;
105
+ };
106
+ //# sourceMappingURL=automation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automation.d.ts","sourceRoot":"","sources":["../../src/capabilities/automation.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,iBAAiB;IACjC;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC9B;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3B,cAAc,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACvC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,OAAO,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC9D;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,uBAAuB;;;;;;qBAOjC,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;EAiBzD"}
@@ -0,0 +1,31 @@
1
+ import { ExecutionError } from "../error.js";
2
+ function automation(config) {
3
+ return {
4
+ _tag: "automation",
5
+ config: {
6
+ title: config.title,
7
+ description: config.description
8
+ },
9
+ async handler(context) {
10
+ try {
11
+ await config.execute(context);
12
+ process.stdout.write(
13
+ `
14
+ <output>${JSON.stringify({ status: "success" })}</output>
15
+ `
16
+ );
17
+ } catch (err) {
18
+ const error = new ExecutionError(err);
19
+ process.stdout.write(
20
+ `
21
+ <output>${JSON.stringify({ status: "error", error: { name: error.name, message: error.message } })}</output>
22
+ `
23
+ );
24
+ throw error;
25
+ }
26
+ }
27
+ };
28
+ }
29
+ export {
30
+ automation
31
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=automation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automation.test.d.ts","sourceRoot":"","sources":["../../src/capabilities/automation.test.ts"],"names":[],"mappings":""}
package/dist/error.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * An error that occurred during the execution of a sync.
2
+ * An error that occurred during the execution of a worker capability.
3
3
  */
4
4
  export declare class ExecutionError extends Error {
5
5
  readonly cause: unknown;
package/dist/error.js CHANGED
@@ -1,7 +1,7 @@
1
1
  class ExecutionError extends Error {
2
2
  cause;
3
3
  constructor(cause) {
4
- super(`Error during sync execution: ${cause}`);
4
+ super(`Error during worker execution: ${cause}`);
5
5
  this.name = "ExecutionError";
6
6
  this.cause = cause;
7
7
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export { emojiIcon, notionIcon } from "./builder.js";
2
+ export type { AutomationConfiguration, AutomationContext, PageObjectResponse, } from "./capabilities/automation.js";
3
+ export { automation } from "./capabilities/automation.js";
2
4
  export type { SyncConfiguration, SyncExecutionResult, SyncedObject, } from "./capabilities/sync.js";
3
5
  export { sync } from "./capabilities/sync.js";
4
6
  export { tool } from "./capabilities/tool.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACrD,YAAY,EACX,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,GACZ,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC9C,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACrD,YAAY,EACX,uBAAuB,EACvB,iBAAiB,EACjB,kBAAkB,GAClB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,YAAY,EACX,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,GACZ,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC9C,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { emojiIcon, notionIcon } from "./builder.js";
2
+ import { automation } from "./capabilities/automation.js";
2
3
  import { sync } from "./capabilities/sync.js";
3
4
  import { tool } from "./capabilities/tool.js";
4
5
  export {
6
+ automation,
5
7
  emojiIcon,
6
8
  notionIcon,
7
9
  sync,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@project-ajax/sdk",
3
- "version": "0.0.62",
3
+ "version": "0.0.63",
4
4
  "description": "An SDK for building workers for the Project Ajax platform",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -0,0 +1,148 @@
1
+ import {
2
+ afterEach,
3
+ beforeEach,
4
+ describe,
5
+ expect,
6
+ it,
7
+ type Mock,
8
+ vi,
9
+ } from "vitest";
10
+ import { ExecutionError } from "../error.js";
11
+ import {
12
+ type AutomationConfiguration,
13
+ type AutomationContext,
14
+ automation,
15
+ type PageObjectResponse,
16
+ } from "./automation.js";
17
+
18
+ describe("automation", () => {
19
+ let stdoutSpy: Mock<typeof process.stdout.write>;
20
+
21
+ beforeEach(() => {
22
+ stdoutSpy = vi
23
+ .spyOn(process.stdout, "write")
24
+ .mockImplementation(() => true);
25
+ });
26
+
27
+ afterEach(() => {
28
+ vi.restoreAllMocks();
29
+ });
30
+
31
+ it("creates automation capability with correct structure", () => {
32
+ const config: AutomationConfiguration = {
33
+ title: "Test Automation",
34
+ description: "Test automation description",
35
+ execute: () => {},
36
+ };
37
+
38
+ const result = automation(config);
39
+
40
+ expect(result._tag).toBe("automation");
41
+ expect(result.config.title).toBe("Test Automation");
42
+ expect(result.config.description).toBe("Test automation description");
43
+ expect(typeof result.handler).toBe("function");
44
+ });
45
+
46
+ it("executes automation without page data", async () => {
47
+ const executeFn = vi.fn();
48
+ const myAutomation = automation({
49
+ title: "Sync Automation",
50
+ description: "Executes synchronously",
51
+ execute: executeFn,
52
+ });
53
+
54
+ const context: AutomationContext = {
55
+ pageId: "page-123",
56
+ actionType: "test_action",
57
+ };
58
+
59
+ await myAutomation.handler(context);
60
+
61
+ expect(executeFn).toHaveBeenCalledWith(context);
62
+ expect(stdoutSpy).toHaveBeenCalledWith(
63
+ `\n<output>{"status":"success"}</output>\n`,
64
+ );
65
+ });
66
+
67
+ it("executes automation with page data", async () => {
68
+ const executeFn = vi.fn();
69
+ const myAutomation = automation({
70
+ title: "Page Automation",
71
+ description: "Processes page data",
72
+ execute: executeFn,
73
+ });
74
+
75
+ const pageData: PageObjectResponse = {
76
+ object: "page",
77
+ id: "page-789",
78
+ created_time: "2023-01-01T00:00:00.000Z",
79
+ last_edited_time: "2023-01-02T00:00:00.000Z",
80
+ created_by: { id: "user-1" },
81
+ last_edited_by: { id: "user-2" },
82
+ cover: null,
83
+ icon: null,
84
+ parent: { type: "database_id", database_id: "db-123" },
85
+ archived: false,
86
+ properties: {
87
+ Name: { id: "title", type: "title", title: [] },
88
+ Status: { id: "status", type: "status", status: { name: "Done" } },
89
+ },
90
+ url: "https://notion.so/page-789",
91
+ public_url: null,
92
+ };
93
+
94
+ const context: AutomationContext = {
95
+ pageId: "page-789",
96
+ actionType: "process_page",
97
+ pageData,
98
+ };
99
+
100
+ await myAutomation.handler(context);
101
+
102
+ expect(executeFn).toHaveBeenCalledWith(context);
103
+ expect(stdoutSpy).toHaveBeenCalledWith(
104
+ `\n<output>{"status":"success"}</output>\n`,
105
+ );
106
+ });
107
+
108
+ it("handles execution error from function", async () => {
109
+ const myAutomation = automation({
110
+ title: "Error Automation",
111
+ description: "Throws an error",
112
+ execute: () => {
113
+ throw new Error("Something went wrong");
114
+ },
115
+ });
116
+
117
+ const context: AutomationContext = {
118
+ pageId: "page-error",
119
+ actionType: "error_action",
120
+ };
121
+
122
+ await expect(myAutomation.handler(context)).rejects.toThrow(ExecutionError);
123
+
124
+ expect(stdoutSpy).toHaveBeenCalledWith(
125
+ `\n<output>{"status":"error","error":{"name":"ExecutionError","message":"Error during worker execution: Error: Something went wrong"}}</output>\n`,
126
+ );
127
+ });
128
+
129
+ it("handles execution error with non-Error object", async () => {
130
+ const myAutomation = automation({
131
+ title: "Non-Error Automation",
132
+ description: "Throws a non-Error object",
133
+ execute: () => {
134
+ throw "String error";
135
+ },
136
+ });
137
+
138
+ const context: AutomationContext = {
139
+ actionType: "string_error_action",
140
+ };
141
+
142
+ await expect(myAutomation.handler(context)).rejects.toThrow(ExecutionError);
143
+
144
+ expect(stdoutSpy).toHaveBeenCalledWith(
145
+ `\n<output>{"status":"error","error":{"name":"ExecutionError","message":"Error during worker execution: String error"}}</output>\n`,
146
+ );
147
+ });
148
+ });
@@ -0,0 +1,126 @@
1
+ import { ExecutionError } from "../error.js";
2
+
3
+ /**
4
+ * Context provided to automation execute functions
5
+ */
6
+ export interface AutomationContext {
7
+ /**
8
+ * The ID of the page that triggered the automation (if applicable)
9
+ */
10
+ pageId?: string;
11
+ /**
12
+ * The type of automation action that was triggered
13
+ */
14
+ actionType: string;
15
+ /**
16
+ * The full page object from Notion's Public API (if triggered by a database page)
17
+ */
18
+ pageData?: PageObjectResponse;
19
+ }
20
+
21
+ /**
22
+ * Page object from Notion's Public API
23
+ * This represents a database page with all its properties.
24
+ * Properties are in Notion's Public API format.
25
+ * See: https://developers.notion.com/reference/page
26
+ */
27
+ export interface PageObjectResponse {
28
+ object: "page";
29
+ id: string;
30
+ created_time: string;
31
+ last_edited_time: string;
32
+ created_by: { id: string };
33
+ last_edited_by: { id: string };
34
+ cover: unknown;
35
+ icon: unknown;
36
+ parent: unknown;
37
+ archived: boolean;
38
+ properties: Record<string, unknown>;
39
+ url: string;
40
+ public_url?: string | null;
41
+ }
42
+
43
+ /**
44
+ * Configuration for an automation capability
45
+ */
46
+ export interface AutomationConfiguration {
47
+ /**
48
+ * Title of the automation - shown in the UI when selecting automations
49
+ */
50
+ title: string;
51
+
52
+ /**
53
+ * Description of what this automation does - shown in the UI
54
+ */
55
+ description: string;
56
+
57
+ /**
58
+ * The function that executes when the automation is triggered
59
+ * @param context - Context about the automation trigger, including page data if applicable
60
+ * @returns A promise that resolves when the automation completes
61
+ */
62
+ execute: (context: AutomationContext) => Promise<void> | void;
63
+ }
64
+
65
+ /**
66
+ * Result returned from automation execution
67
+ */
68
+ export interface AutomationHandlerResult {
69
+ title: string;
70
+ description: string;
71
+ }
72
+
73
+ /**
74
+ * Creates a capability definition for an automation that can be triggered
75
+ * from database automations in Notion.
76
+ *
77
+ * Example:
78
+ *
79
+ * ```ts
80
+ * automation({
81
+ * title: "Send Welcome Email",
82
+ * description: "Sends a welcome email when a new user is added",
83
+ * execute: async (context) => {
84
+ * const { pageId, pageData } = context;
85
+ *
86
+ * // Access page properties from the Public API format
87
+ * if (pageData) {
88
+ * const name = pageData.properties.Name; // Access any property
89
+ * const status = pageData.properties.Status;
90
+ * console.log(`Processing: ${name}`);
91
+ * }
92
+ *
93
+ * // Your automation logic here
94
+ * await sendEmail(pageId);
95
+ * },
96
+ * })
97
+ * ```
98
+ *
99
+ * @param config - The configuration for the automation.
100
+ * @returns A capability definition for the automation.
101
+ */
102
+ export function automation(config: AutomationConfiguration) {
103
+ return {
104
+ _tag: "automation",
105
+ config: {
106
+ title: config.title,
107
+ description: config.description,
108
+ },
109
+ async handler(context: AutomationContext): Promise<void> {
110
+ try {
111
+ await config.execute(context);
112
+ // Write success result
113
+ process.stdout.write(
114
+ `\n<output>${JSON.stringify({ status: "success" })}</output>\n`,
115
+ );
116
+ } catch (err) {
117
+ // Convert error to ExecutionError and write it
118
+ const error = new ExecutionError(err);
119
+ process.stdout.write(
120
+ `\n<output>${JSON.stringify({ status: "error", error: { name: error.name, message: error.message } })}</output>\n`,
121
+ );
122
+ throw error;
123
+ }
124
+ },
125
+ };
126
+ }
package/src/error.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * An error that occurred during the execution of a sync.
2
+ * An error that occurred during the execution of a worker capability.
3
3
  */
4
4
  export class ExecutionError extends Error {
5
5
  readonly cause: unknown;
6
6
 
7
7
  constructor(cause: unknown) {
8
- super(`Error during sync execution: ${cause}`);
8
+ super(`Error during worker execution: ${cause}`);
9
9
  this.name = "ExecutionError";
10
10
  this.cause = cause;
11
11
  }
package/src/index.ts CHANGED
@@ -1,4 +1,10 @@
1
1
  export { emojiIcon, notionIcon } from "./builder.js";
2
+ export type {
3
+ AutomationConfiguration,
4
+ AutomationContext,
5
+ PageObjectResponse,
6
+ } from "./capabilities/automation.js";
7
+ export { automation } from "./capabilities/automation.js";
2
8
  export type {
3
9
  SyncConfiguration,
4
10
  SyncExecutionResult,