@notionhq/workers 0.0.85 → 0.1.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/builder.d.ts +9 -4
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +23 -18
- package/dist/builder.test.d.ts +2 -0
- package/dist/builder.test.d.ts.map +1 -0
- package/dist/capabilities/automation.js +2 -2
- package/dist/capabilities/sync.d.ts.map +1 -1
- package/dist/capabilities/sync.js +22 -6
- package/dist/capabilities/tool.d.ts +13 -6
- package/dist/capabilities/tool.d.ts.map +1 -1
- package/dist/capabilities/tool.js +28 -18
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/schema-builder.d.ts +79 -0
- package/dist/schema-builder.d.ts.map +1 -0
- package/dist/schema-builder.js +135 -0
- package/dist/schema-builder.test.d.ts +2 -0
- package/dist/schema-builder.test.d.ts.map +1 -0
- package/dist/worker.d.ts +3 -8
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +3 -8
- package/package.json +5 -1
- package/src/builder.test.ts +229 -0
- package/src/builder.ts +38 -25
- package/src/capabilities/automation.test.ts +8 -4
- package/src/capabilities/automation.ts +2 -2
- package/src/capabilities/sync.test.ts +70 -1
- package/src/capabilities/sync.ts +18 -6
- package/src/capabilities/tool.test.ts +32 -167
- package/src/capabilities/tool.ts +33 -26
- package/src/index.ts +2 -0
- package/src/schema-builder.test.ts +677 -0
- package/src/schema-builder.ts +203 -0
- package/src/worker.ts +3 -8
package/dist/worker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,EACf,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,EACX,+BAA+B,EAC/B,eAAe,EACf,kBAAkB,EAClB,6BAA6B,EAC7B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhF,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5D,YAAY,EACX,uBAAuB,EACvB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,+BAA+B,EAC/B,6BAA6B,EAC7B,iBAAiB,EACjB,iBAAiB,GACjB,CAAC;AAMF,KAAK,UAAU,GACZ,cAAc,GAEd,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,GACxB,oBAAoB,GACpB,eAAe,CAAC;AAMnB,qBAAa,MAAM;;IAGlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8CG;IACH,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,EAC9D,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,iBAAiB,CAAC,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,GACvC,cAAc;IAOjB
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,EACf,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,EACX,+BAA+B,EAC/B,eAAe,EACf,kBAAkB,EAClB,6BAA6B,EAC7B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhF,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5D,YAAY,EACX,uBAAuB,EACvB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,+BAA+B,EAC/B,6BAA6B,EAC7B,iBAAiB,EACjB,iBAAiB,GACjB,CAAC;AAMF,KAAK,UAAU,GACZ,cAAc,GAEd,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,GACxB,oBAAoB,GACpB,eAAe,CAAC;AAMnB,qBAAa,MAAM;;IAGlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8CG;IACH,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,EAC9D,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,iBAAiB,CAAC,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,GACvC,cAAc;IAOjB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,IAAI,CAAC,CAAC,SAAS,SAAS,EAAE,CAAC,SAAS,SAAS,GAAG,SAAS,EACxD,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,GAC7B,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC;IAQvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,UAAU,CACT,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,uBAAuB,GAC7B,oBAAoB;IAOvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwCG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,eAAe;IAO/D;;OAEG;IACH,IAAI,YAAY,IAAI,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC,EAAE,CAMhE;IAED;;;;;;;OAOG;IACG,GAAG,CACR,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,cAAmB,GAC1B,OAAO,CAAC,OAAO,CAAC;CAyBnB"}
|
package/dist/worker.js
CHANGED
|
@@ -63,23 +63,18 @@ class Worker {
|
|
|
63
63
|
* Example:
|
|
64
64
|
*
|
|
65
65
|
* ```ts
|
|
66
|
+
* import { j } from "@notionhq/workers/schema-builder";
|
|
67
|
+
*
|
|
66
68
|
* worker.tool<{ name: string }, string>("sayHello", {
|
|
67
69
|
* title: "Say Hello",
|
|
68
70
|
* description: "Say hello to the user",
|
|
69
|
-
* schema: {
|
|
70
|
-
* type: "object",
|
|
71
|
-
* properties: {
|
|
72
|
-
* name: { type: "string" },
|
|
73
|
-
* },
|
|
74
|
-
* required: ["name"],
|
|
75
|
-
* },
|
|
71
|
+
* schema: j.object({ name: j.string() }),
|
|
76
72
|
* execute: ({ name }, { notion }) => {
|
|
77
73
|
* return `Hello, ${name}!`;
|
|
78
74
|
* },
|
|
79
75
|
* })
|
|
80
76
|
* ```
|
|
81
77
|
*
|
|
82
|
-
*
|
|
83
78
|
* @param key - The unique key for this capability.
|
|
84
79
|
* @param config - The tool configuration.
|
|
85
80
|
* @returns The capability object.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@notionhq/workers",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "An SDK for building workers for the Notion Workers platform",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
"types": "./dist/error.d.ts",
|
|
26
26
|
"default": "./dist/error.js"
|
|
27
27
|
},
|
|
28
|
+
"./schema-builder": {
|
|
29
|
+
"types": "./dist/schema-builder.d.ts",
|
|
30
|
+
"default": "./dist/schema-builder.js"
|
|
31
|
+
},
|
|
28
32
|
"./types": {
|
|
29
33
|
"types": "./dist/types.d.ts",
|
|
30
34
|
"default": "./dist/types.js"
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import * as Builder from "./builder.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Helper to extract the DateValue from a date token.
|
|
6
|
+
* Date tokens have the shape [["\u2023", [["d", dateValue]]]].
|
|
7
|
+
*/
|
|
8
|
+
// biome-ignore lint/suspicious/noExplicitAny: test helper
|
|
9
|
+
function extractDateValue(token: unknown): any {
|
|
10
|
+
// biome-ignore lint/suspicious/noExplicitAny: test helper
|
|
11
|
+
const arr = token as any;
|
|
12
|
+
return arr[0][1][0][1];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe("Builder.date", () => {
|
|
16
|
+
it("creates a date token from YYYY-MM-DD", () => {
|
|
17
|
+
expect(extractDateValue(Builder.date("2024-01-15"))).toEqual({
|
|
18
|
+
type: "date",
|
|
19
|
+
start_date: "2024-01-15",
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("rejects non-YYYY-MM-DD strings", () => {
|
|
24
|
+
expect(() => Builder.date("01-15-2024")).toThrow("Expected YYYY-MM-DD");
|
|
25
|
+
expect(() => Builder.date("2024/01/15")).toThrow("Expected YYYY-MM-DD");
|
|
26
|
+
expect(() => Builder.date("2024-1-5")).toThrow("Expected YYYY-MM-DD");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("rejects invalid calendar dates", () => {
|
|
30
|
+
expect(() => Builder.date("2024-13-01")).toThrow();
|
|
31
|
+
// Note: "2024-02-30" is silently coerced by JS Date (rolls to March 1st),
|
|
32
|
+
// so the current validator does not catch it.
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("Builder.dateTime", () => {
|
|
37
|
+
it("parses minimal ISO 8601 (YYYY-MM-DDTHH:mm)", () => {
|
|
38
|
+
expect(extractDateValue(Builder.dateTime("2024-01-15T10:30"))).toEqual({
|
|
39
|
+
type: "datetime",
|
|
40
|
+
start_date: "2024-01-15",
|
|
41
|
+
start_time: "10:30",
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("parses ISO 8601 with seconds", () => {
|
|
46
|
+
expect(extractDateValue(Builder.dateTime("2024-01-15T10:30:45"))).toEqual({
|
|
47
|
+
type: "datetime",
|
|
48
|
+
start_date: "2024-01-15",
|
|
49
|
+
start_time: "10:30",
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("parses ISO 8601 with Z suffix", () => {
|
|
54
|
+
expect(extractDateValue(Builder.dateTime("2024-01-15T10:30:00Z"))).toEqual({
|
|
55
|
+
type: "datetime",
|
|
56
|
+
start_date: "2024-01-15",
|
|
57
|
+
start_time: "10:30",
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("parses ISO 8601 with positive timezone offset", () => {
|
|
62
|
+
expect(
|
|
63
|
+
extractDateValue(Builder.dateTime("2024-01-15T10:30:00+05:30")),
|
|
64
|
+
).toEqual({
|
|
65
|
+
type: "datetime",
|
|
66
|
+
start_date: "2024-01-15",
|
|
67
|
+
start_time: "10:30",
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("parses ISO 8601 with negative timezone offset", () => {
|
|
72
|
+
expect(
|
|
73
|
+
extractDateValue(Builder.dateTime("2024-01-15T10:30:00-08:00")),
|
|
74
|
+
).toEqual({
|
|
75
|
+
type: "datetime",
|
|
76
|
+
start_date: "2024-01-15",
|
|
77
|
+
start_time: "10:30",
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("parses ISO 8601 with milliseconds", () => {
|
|
82
|
+
expect(
|
|
83
|
+
extractDateValue(Builder.dateTime("2024-01-15T10:30:00.000Z")),
|
|
84
|
+
).toEqual({
|
|
85
|
+
type: "datetime",
|
|
86
|
+
start_date: "2024-01-15",
|
|
87
|
+
start_time: "10:30",
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("includes timeZone when provided", () => {
|
|
92
|
+
expect(
|
|
93
|
+
extractDateValue(
|
|
94
|
+
Builder.dateTime("2024-01-15T10:30", "America/New_York"),
|
|
95
|
+
),
|
|
96
|
+
).toEqual({
|
|
97
|
+
type: "datetime",
|
|
98
|
+
start_date: "2024-01-15",
|
|
99
|
+
start_time: "10:30",
|
|
100
|
+
time_zone: "America/New_York",
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("handles midnight", () => {
|
|
105
|
+
expect(extractDateValue(Builder.dateTime("2024-01-15T00:00"))).toEqual({
|
|
106
|
+
type: "datetime",
|
|
107
|
+
start_date: "2024-01-15",
|
|
108
|
+
start_time: "00:00",
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("handles end of day", () => {
|
|
113
|
+
expect(extractDateValue(Builder.dateTime("2024-01-15T23:59"))).toEqual({
|
|
114
|
+
type: "datetime",
|
|
115
|
+
start_date: "2024-01-15",
|
|
116
|
+
start_time: "23:59",
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("rejects date-only strings", () => {
|
|
121
|
+
expect(() => Builder.dateTime("2024-01-15")).toThrow(
|
|
122
|
+
"Invalid ISO 8601 datetime",
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("rejects plain text", () => {
|
|
127
|
+
expect(() => Builder.dateTime("not a date")).toThrow(
|
|
128
|
+
"Invalid ISO 8601 datetime",
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("rejects empty string", () => {
|
|
133
|
+
expect(() => Builder.dateTime("")).toThrow("Invalid ISO 8601 datetime");
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("rejects date with space instead of T", () => {
|
|
137
|
+
expect(() => Builder.dateTime("2024-01-15 10:30")).toThrow(
|
|
138
|
+
"Invalid ISO 8601 datetime",
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("Builder.dateRange", () => {
|
|
144
|
+
it("creates a date range token", () => {
|
|
145
|
+
expect(
|
|
146
|
+
extractDateValue(Builder.dateRange("2024-01-15", "2024-01-20")),
|
|
147
|
+
).toEqual({
|
|
148
|
+
type: "daterange",
|
|
149
|
+
start_date: "2024-01-15",
|
|
150
|
+
end_date: "2024-01-20",
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("rejects invalid start date", () => {
|
|
155
|
+
expect(() => Builder.dateRange("bad", "2024-01-20")).toThrow(
|
|
156
|
+
"Expected YYYY-MM-DD",
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("rejects invalid end date", () => {
|
|
161
|
+
expect(() => Builder.dateRange("2024-01-15", "bad")).toThrow(
|
|
162
|
+
"Expected YYYY-MM-DD",
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe("Builder.dateTimeRange", () => {
|
|
168
|
+
it("creates a datetime range token from ISO strings", () => {
|
|
169
|
+
expect(
|
|
170
|
+
extractDateValue(
|
|
171
|
+
Builder.dateTimeRange("2024-01-15T10:30", "2024-01-16T14:00"),
|
|
172
|
+
),
|
|
173
|
+
).toEqual({
|
|
174
|
+
type: "datetimerange",
|
|
175
|
+
start_date: "2024-01-15",
|
|
176
|
+
start_time: "10:30",
|
|
177
|
+
end_date: "2024-01-16",
|
|
178
|
+
end_time: "14:00",
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("parses mixed ISO formats", () => {
|
|
183
|
+
expect(
|
|
184
|
+
extractDateValue(
|
|
185
|
+
Builder.dateTimeRange(
|
|
186
|
+
"2024-01-15T10:30:00Z",
|
|
187
|
+
"2024-01-16T14:00:00.000+05:00",
|
|
188
|
+
),
|
|
189
|
+
),
|
|
190
|
+
).toEqual({
|
|
191
|
+
type: "datetimerange",
|
|
192
|
+
start_date: "2024-01-15",
|
|
193
|
+
start_time: "10:30",
|
|
194
|
+
end_date: "2024-01-16",
|
|
195
|
+
end_time: "14:00",
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("includes timeZone when provided", () => {
|
|
200
|
+
expect(
|
|
201
|
+
extractDateValue(
|
|
202
|
+
Builder.dateTimeRange(
|
|
203
|
+
"2024-01-15T10:30",
|
|
204
|
+
"2024-01-16T14:00",
|
|
205
|
+
"Europe/London",
|
|
206
|
+
),
|
|
207
|
+
),
|
|
208
|
+
).toEqual({
|
|
209
|
+
type: "datetimerange",
|
|
210
|
+
start_date: "2024-01-15",
|
|
211
|
+
start_time: "10:30",
|
|
212
|
+
end_date: "2024-01-16",
|
|
213
|
+
end_time: "14:00",
|
|
214
|
+
time_zone: "Europe/London",
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("rejects invalid start datetime", () => {
|
|
219
|
+
expect(() =>
|
|
220
|
+
Builder.dateTimeRange("2024-01-15", "2024-01-16T14:00"),
|
|
221
|
+
).toThrow("Invalid ISO 8601 datetime");
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("rejects invalid end datetime", () => {
|
|
225
|
+
expect(() =>
|
|
226
|
+
Builder.dateTimeRange("2024-01-15T10:30", "2024-01-16"),
|
|
227
|
+
).toThrow("Invalid ISO 8601 datetime");
|
|
228
|
+
});
|
|
229
|
+
});
|
package/src/builder.ts
CHANGED
|
@@ -92,13 +92,17 @@ export function date(dateString: string): TextValue {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
|
-
* Creates a datetime value from
|
|
95
|
+
* Creates a datetime value from an ISO 8601 datetime string.
|
|
96
|
+
* @param isoString - An ISO 8601 datetime string (e.g., "2024-01-15T10:30", "2024-01-15T10:30:00Z")
|
|
97
|
+
* @param timeZone - Optional IANA timezone name (e.g., "America/New_York")
|
|
96
98
|
*/
|
|
97
|
-
export function dateTime(
|
|
99
|
+
export function dateTime(isoString: string, timeZone?: string): TextValue {
|
|
100
|
+
const { date, time } = parseISODateTime(isoString);
|
|
101
|
+
|
|
98
102
|
const dateValue: DateValue = {
|
|
99
103
|
type: "datetime",
|
|
100
|
-
start_date:
|
|
101
|
-
start_time:
|
|
104
|
+
start_date: date,
|
|
105
|
+
start_time: time,
|
|
102
106
|
};
|
|
103
107
|
|
|
104
108
|
if (timeZone) {
|
|
@@ -125,19 +129,25 @@ export function dateRange(startDate: string, endDate: string): TextValue {
|
|
|
125
129
|
}
|
|
126
130
|
|
|
127
131
|
/**
|
|
128
|
-
* Creates a datetime range value from
|
|
132
|
+
* Creates a datetime range value from ISO 8601 datetime strings.
|
|
133
|
+
* @param startDateTime - An ISO 8601 datetime string for the range start
|
|
134
|
+
* @param endDateTime - An ISO 8601 datetime string for the range end
|
|
135
|
+
* @param timeZone - Optional IANA timezone name (e.g., "America/New_York")
|
|
129
136
|
*/
|
|
130
137
|
export function dateTimeRange(
|
|
131
|
-
|
|
132
|
-
|
|
138
|
+
startDateTime: string,
|
|
139
|
+
endDateTime: string,
|
|
133
140
|
timeZone?: string,
|
|
134
141
|
): TextValue {
|
|
142
|
+
const start = parseISODateTime(startDateTime);
|
|
143
|
+
const end = parseISODateTime(endDateTime);
|
|
144
|
+
|
|
135
145
|
const dateValue: DateValue = {
|
|
136
146
|
type: "datetimerange",
|
|
137
|
-
start_date:
|
|
138
|
-
start_time:
|
|
139
|
-
end_date:
|
|
140
|
-
end_time:
|
|
147
|
+
start_date: start.date,
|
|
148
|
+
start_time: start.time,
|
|
149
|
+
end_date: end.date,
|
|
150
|
+
end_time: end.time,
|
|
141
151
|
};
|
|
142
152
|
|
|
143
153
|
if (timeZone) {
|
|
@@ -242,22 +252,25 @@ function createDateToken(dateValue: DateValue): TextValue {
|
|
|
242
252
|
}
|
|
243
253
|
|
|
244
254
|
/**
|
|
245
|
-
*
|
|
255
|
+
* Parses an ISO 8601 datetime string into date and time components.
|
|
256
|
+
* Accepts formats like "2024-01-15T10:30", "2024-01-15T10:30:00Z",
|
|
257
|
+
* "2024-01-15T10:30:00.000+05:00", etc.
|
|
246
258
|
*/
|
|
247
|
-
function
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
259
|
+
function parseISODateTime(isoString: string): { date: string; time: string } {
|
|
260
|
+
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(isoString)) {
|
|
261
|
+
throw new Error(
|
|
262
|
+
`Invalid ISO 8601 datetime: ${isoString}. Expected format like 2024-01-15T10:30 or 2024-01-15T10:30:00Z`,
|
|
263
|
+
);
|
|
264
|
+
}
|
|
253
265
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
266
|
+
const date = isoString.slice(0, 10);
|
|
267
|
+
const time = isoString.slice(11, 16);
|
|
268
|
+
|
|
269
|
+
if (Number.isNaN(new Date(isoString).getTime())) {
|
|
270
|
+
throw new Error(`Invalid datetime: ${isoString}`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return { date, time };
|
|
261
274
|
}
|
|
262
275
|
|
|
263
276
|
/**
|
|
@@ -59,7 +59,7 @@ describe("createAutomationCapability", () => {
|
|
|
59
59
|
}),
|
|
60
60
|
);
|
|
61
61
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
62
|
-
`\n<output>{"status":"success"}</output>\n`,
|
|
62
|
+
`\n<output>{"_tag":"success","value":{"status":"success"}}</output>\n`,
|
|
63
63
|
);
|
|
64
64
|
});
|
|
65
65
|
|
|
@@ -105,7 +105,7 @@ describe("createAutomationCapability", () => {
|
|
|
105
105
|
}),
|
|
106
106
|
);
|
|
107
107
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
108
|
-
`\n<output>{"status":"success"}</output>\n`,
|
|
108
|
+
`\n<output>{"_tag":"success","value":{"status":"success"}}</output>\n`,
|
|
109
109
|
);
|
|
110
110
|
});
|
|
111
111
|
|
|
@@ -126,7 +126,9 @@ describe("createAutomationCapability", () => {
|
|
|
126
126
|
await expect(capability.handler(event)).rejects.toThrow(ExecutionError);
|
|
127
127
|
|
|
128
128
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
129
|
-
|
|
129
|
+
expect.stringContaining(
|
|
130
|
+
`"_tag":"error","error":{"name":"ExecutionError","message":"Error during worker execution: Error: Something went wrong"`,
|
|
131
|
+
),
|
|
130
132
|
);
|
|
131
133
|
});
|
|
132
134
|
|
|
@@ -146,7 +148,9 @@ describe("createAutomationCapability", () => {
|
|
|
146
148
|
await expect(capability.handler(event)).rejects.toThrow(ExecutionError);
|
|
147
149
|
|
|
148
150
|
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
149
|
-
|
|
151
|
+
expect.stringContaining(
|
|
152
|
+
`"_tag":"error","error":{"name":"ExecutionError","message":"Error during worker execution: String error"`,
|
|
153
|
+
),
|
|
150
154
|
);
|
|
151
155
|
});
|
|
152
156
|
});
|
|
@@ -111,7 +111,7 @@ export function createAutomationCapability(
|
|
|
111
111
|
return { status: "success" };
|
|
112
112
|
} else {
|
|
113
113
|
process.stdout.write(
|
|
114
|
-
`\n<output>${JSON.stringify({ status: "success" })}</output>\n`,
|
|
114
|
+
`\n<output>${JSON.stringify({ _tag: "success", value: { status: "success" } })}</output>\n`,
|
|
115
115
|
);
|
|
116
116
|
}
|
|
117
117
|
} catch (err) {
|
|
@@ -119,7 +119,7 @@ export function createAutomationCapability(
|
|
|
119
119
|
|
|
120
120
|
if (!options?.concreteOutput) {
|
|
121
121
|
process.stdout.write(
|
|
122
|
-
`\n<output>${JSON.stringify({
|
|
122
|
+
`\n<output>${JSON.stringify({ _tag: "error", error: { name: error.name, message: error.message, trace: error.stack } })}</output>\n`,
|
|
123
123
|
);
|
|
124
124
|
}
|
|
125
125
|
|
|
@@ -1,9 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
afterEach,
|
|
3
|
+
beforeEach,
|
|
4
|
+
describe,
|
|
5
|
+
expect,
|
|
6
|
+
it,
|
|
7
|
+
type Mock,
|
|
8
|
+
vi,
|
|
9
|
+
} from "vitest";
|
|
2
10
|
import * as Builder from "../builder.js";
|
|
11
|
+
import { ExecutionError } from "../error.js";
|
|
3
12
|
import * as Schema from "../schema.js";
|
|
4
13
|
import { createSyncCapability } from "./sync.js";
|
|
5
14
|
|
|
6
15
|
describe("Worker.sync", () => {
|
|
16
|
+
let stdoutSpy: Mock<typeof process.stdout.write>;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
stdoutSpy = vi
|
|
20
|
+
.spyOn(process.stdout, "write")
|
|
21
|
+
.mockImplementation(() => true);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
vi.restoreAllMocks();
|
|
26
|
+
});
|
|
27
|
+
|
|
7
28
|
describe("Schema.relation", () => {
|
|
8
29
|
it("creates a relation property configuration", () => {
|
|
9
30
|
const relationProp = Schema.relation("projectsSync");
|
|
@@ -101,4 +122,52 @@ describe("Worker.sync", () => {
|
|
|
101
122
|
expect(capability.config.primaryKeyProperty).toBe("Task ID");
|
|
102
123
|
});
|
|
103
124
|
});
|
|
125
|
+
|
|
126
|
+
describe("handler output envelope", () => {
|
|
127
|
+
it("wraps successful result in success envelope", async () => {
|
|
128
|
+
const capability = createSyncCapability("testSync", {
|
|
129
|
+
primaryKeyProperty: "ID",
|
|
130
|
+
schema: {
|
|
131
|
+
defaultName: "Test",
|
|
132
|
+
properties: {
|
|
133
|
+
ID: Schema.title(),
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
execute: async () => ({
|
|
137
|
+
changes: [],
|
|
138
|
+
hasMore: false,
|
|
139
|
+
}),
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
await capability.handler();
|
|
143
|
+
|
|
144
|
+
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
145
|
+
`\n<output>${JSON.stringify({ _tag: "success", value: { changes: [], hasMore: false, nextUserContext: undefined } })}</output>\n`,
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("wraps error in error envelope", async () => {
|
|
150
|
+
const capability = createSyncCapability("errorSync", {
|
|
151
|
+
primaryKeyProperty: "ID",
|
|
152
|
+
schema: {
|
|
153
|
+
defaultName: "Test",
|
|
154
|
+
properties: {
|
|
155
|
+
ID: Schema.title(),
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
execute: async () => {
|
|
159
|
+
throw new Error("sync failed");
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
await expect(capability.handler()).rejects.toThrow(ExecutionError);
|
|
164
|
+
|
|
165
|
+
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
166
|
+
expect.stringContaining(`"_tag":"error"`),
|
|
167
|
+
);
|
|
168
|
+
expect(stdoutSpy).toHaveBeenCalledWith(
|
|
169
|
+
expect.stringContaining(`"name":"ExecutionError"`),
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
104
173
|
});
|
package/src/capabilities/sync.ts
CHANGED
|
@@ -227,11 +227,21 @@ export function createSyncCapability<
|
|
|
227
227
|
) {
|
|
228
228
|
const capabilityContext = createCapabilityContext();
|
|
229
229
|
const state = runtimeContext?.state ?? runtimeContext?.userContext;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
.
|
|
233
|
-
|
|
234
|
-
|
|
230
|
+
let executionResult: SyncExecutionResult<PK, Context>;
|
|
231
|
+
try {
|
|
232
|
+
executionResult = await syncConfiguration.execute(
|
|
233
|
+
state,
|
|
234
|
+
capabilityContext,
|
|
235
|
+
);
|
|
236
|
+
} catch (err) {
|
|
237
|
+
const error = new ExecutionError(err);
|
|
238
|
+
if (!options?.concreteOutput) {
|
|
239
|
+
process.stdout.write(
|
|
240
|
+
`\n<output>${JSON.stringify({ _tag: "error", error: { name: error.name, message: error.message } })}</output>\n`,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
235
245
|
|
|
236
246
|
const result = {
|
|
237
247
|
changes: executionResult.changes,
|
|
@@ -242,7 +252,9 @@ export function createSyncCapability<
|
|
|
242
252
|
if (options?.concreteOutput) {
|
|
243
253
|
return result;
|
|
244
254
|
} else {
|
|
245
|
-
process.stdout.write(
|
|
255
|
+
process.stdout.write(
|
|
256
|
+
`\n<output>${JSON.stringify({ _tag: "success", value: result })}</output>\n`,
|
|
257
|
+
);
|
|
246
258
|
}
|
|
247
259
|
|
|
248
260
|
return result;
|