@notionhq/workers 0.8.0 → 0.8.1
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/workflow.d.ts +30 -2
- package/dist/alpha/workflow.d.ts.map +1 -1
- package/dist/alpha/workflow.js +51 -1
- package/dist/credential.d.ts +26 -0
- package/dist/credential.d.ts.map +1 -0
- package/dist/credential.js +176 -0
- package/dist/credential.test.d.ts +2 -0
- package/dist/credential.test.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/worker.d.ts +7 -0
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +28 -1
- package/package.json +1 -1
- package/src/alpha/workflow.test.ts +124 -2
- package/src/alpha/workflow.ts +92 -3
- package/src/credential.test.ts +324 -0
- package/src/credential.ts +238 -0
- package/src/index.ts +8 -0
- package/src/worker.test.ts +153 -2
- package/src/worker.ts +43 -1
package/dist/alpha/workflow.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export type WorkflowConfiguration<TTriggers extends readonly [WorkflowTrigger, .
|
|
|
22
22
|
* Each trigger defines a specific event or condition that causes the workflow to run.
|
|
23
23
|
*/
|
|
24
24
|
triggers: TTriggers;
|
|
25
|
-
handler: (event: WorkflowEventForTriggers<TTriggers>, context:
|
|
25
|
+
handler: (event: WorkflowEventForTriggers<TTriggers>, context: WorkflowContext) => Promise<void> | void;
|
|
26
26
|
};
|
|
27
27
|
/**
|
|
28
28
|
* A workflow capability as returned by {@link createWorkflow}.
|
|
@@ -60,11 +60,39 @@ export type Workflow<TTriggers extends readonly [WorkflowTrigger, ...WorkflowTri
|
|
|
60
60
|
* title: "Send Welcome Email",
|
|
61
61
|
* description: "Sends a welcome email when a new page is added",
|
|
62
62
|
* triggers: [triggers.notionPageCreated()],
|
|
63
|
-
* handler: async (event) => {
|
|
63
|
+
* handler: async (event, context) => {
|
|
64
64
|
* console.log(event.page);
|
|
65
|
+
* await context.step("Process page", async ({ id }) => {
|
|
66
|
+
* console.log(`Running step ${id}`);
|
|
67
|
+
* return processPage(event.page);
|
|
68
|
+
* });
|
|
65
69
|
* },
|
|
66
70
|
* });
|
|
67
71
|
* ```
|
|
68
72
|
*/
|
|
69
73
|
export declare function createWorkflow<const TTriggers extends readonly [WorkflowTrigger, ...WorkflowTrigger[]]>(configuration: WorkflowConfiguration<TTriggers>): Workflow<TTriggers>;
|
|
74
|
+
/** Context passed to a workflow step. */
|
|
75
|
+
export type StepContext = {
|
|
76
|
+
/** An identifier for this invocation of the step. May be used as an idempotency key. */
|
|
77
|
+
id: string;
|
|
78
|
+
};
|
|
79
|
+
/** Context passed to a workflow handler. */
|
|
80
|
+
export type WorkflowContext = CapabilityContext & {
|
|
81
|
+
/**
|
|
82
|
+
* Run an observable unit of work within the workflow.
|
|
83
|
+
*
|
|
84
|
+
* The callback may be synchronous or asynchronous, but `step` always returns
|
|
85
|
+
* a promise. Its return value must be JSON-serializable so it can be included
|
|
86
|
+
* in the success event written to stdout.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* const page = await context.step("Fetch page", async ({ id }) => {
|
|
91
|
+
* console.log(`Running step ${id}`);
|
|
92
|
+
* return context.notion.pages.retrieve({ page_id: pageId });
|
|
93
|
+
* });
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
step: <T>(name: string, fn: (context: StepContext) => T | Promise<T>) => Promise<T>;
|
|
97
|
+
};
|
|
70
98
|
//# sourceMappingURL=workflow.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/alpha/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAItD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,MAAM,aAAa,GAAG,gBAAgB,CAAC,MAAM,gBAAgB,CAAC,CAAC;AAErE,MAAM,MAAM,uBAAuB,CAAC,CAAC,SAAS,eAAe,IAAI,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAE7F,MAAM,MAAM,wBAAwB,CAAC,CAAC,SAAS,SAAS,eAAe,EAAE,IACxE,uBAAuB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAEpC;;GAEG;AACH,MAAM,MAAM,qBAAqB,CAChC,SAAS,SAAS,SAAS,CAAC,eAAe,EAAE,GAAG,eAAe,EAAE,CAAC,IAC/D;IACH;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB,OAAO,EAAE,CACR,KAAK,EAAE,wBAAwB,CAAC,SAAS,CAAC,EAC1C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/alpha/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAItD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,MAAM,aAAa,GAAG,gBAAgB,CAAC,MAAM,gBAAgB,CAAC,CAAC;AAErE,MAAM,MAAM,uBAAuB,CAAC,CAAC,SAAS,eAAe,IAAI,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAE7F,MAAM,MAAM,wBAAwB,CAAC,CAAC,SAAS,SAAS,eAAe,EAAE,IACxE,uBAAuB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAEpC;;GAEG;AACH,MAAM,MAAM,qBAAqB,CAChC,SAAS,SAAS,SAAS,CAAC,eAAe,EAAE,GAAG,eAAe,EAAE,CAAC,IAC/D;IACH;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB,OAAO,EAAE,CACR,KAAK,EAAE,wBAAwB,CAAC,SAAS,CAAC,EAC1C,OAAO,EAAE,eAAe,KACpB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,CACnB,SAAS,SAAS,SAAS,CAAC,eAAe,EAAE,GAAG,eAAe,EAAE,CAAC,GAAG,SAAS;IAC7E,eAAe;IACf,GAAG,eAAe,EAAE;CACpB,IACE;IACH,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,SAAS,CAAC;KACpB,CAAC;IACF,OAAO,EAAE,CACR,KAAK,EAAE,wBAAwB,CAAC,SAAS,CAAC,EAC1C,OAAO,CAAC,EAAE,cAAc,KACpB,OAAO,CAAC;QAAE,MAAM,EAAE,SAAS,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC;CAChD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,cAAc,CAC7B,KAAK,CAAC,SAAS,SAAS,SAAS,CAAC,eAAe,EAAE,GAAG,eAAe,EAAE,CAAC,EACvE,aAAa,EAAE,qBAAqB,CAAC,SAAS,CAAC,GAAG,QAAQ,CAAC,SAAS,CAAC,CA0CtE;AAED,yCAAyC;AACzC,MAAM,MAAM,WAAW,GAAG;IACzB,wFAAwF;IACxF,EAAE,EAAE,MAAM,CAAC;CACX,CAAC;AAEF,4CAA4C;AAC5C,MAAM,MAAM,eAAe,GAAG,iBAAiB,GAAG;IACjD;;;;;;;;;;;;;;OAcG;IACH,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CACpF,CAAC"}
|
package/dist/alpha/workflow.js
CHANGED
|
@@ -11,7 +11,10 @@ function createWorkflow(configuration) {
|
|
|
11
11
|
},
|
|
12
12
|
async handler(event, options) {
|
|
13
13
|
try {
|
|
14
|
-
const capabilityContext =
|
|
14
|
+
const capabilityContext = {
|
|
15
|
+
...createCapabilityContext("workflow"),
|
|
16
|
+
step
|
|
17
|
+
};
|
|
15
18
|
await configuration.handler(event, capabilityContext);
|
|
16
19
|
if (options?.concreteOutput) {
|
|
17
20
|
return { status: "success" };
|
|
@@ -34,6 +37,53 @@ function createWorkflow(configuration) {
|
|
|
34
37
|
}
|
|
35
38
|
};
|
|
36
39
|
}
|
|
40
|
+
function generateUuidV7() {
|
|
41
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
42
|
+
let timestamp = Date.now();
|
|
43
|
+
for (let index = 5; index >= 0; index--) {
|
|
44
|
+
bytes[index] = timestamp % 256;
|
|
45
|
+
timestamp = Math.floor(timestamp / 256);
|
|
46
|
+
}
|
|
47
|
+
bytes[6] = bytes[6] & 15 | 112;
|
|
48
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
49
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
50
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
51
|
+
}
|
|
52
|
+
function writeStepEvent(event, value) {
|
|
53
|
+
const tag = `__notion_step_${event}__`;
|
|
54
|
+
process.stdout.write(`
|
|
55
|
+
<${tag}>${JSON.stringify(value)}</${tag}>
|
|
56
|
+
`);
|
|
57
|
+
}
|
|
58
|
+
async function step(name, fn) {
|
|
59
|
+
const context = { id: generateUuidV7() };
|
|
60
|
+
const event = { id: context.id, name };
|
|
61
|
+
writeStepEvent("started", event);
|
|
62
|
+
try {
|
|
63
|
+
const value = await fn(context);
|
|
64
|
+
writeStepEvent("success", { ...event, value });
|
|
65
|
+
writeStepEvent("completed", {
|
|
66
|
+
...event,
|
|
67
|
+
type: "success"
|
|
68
|
+
});
|
|
69
|
+
return value;
|
|
70
|
+
} catch (err) {
|
|
71
|
+
const error = new ExecutionError(err);
|
|
72
|
+
writeStepEvent("failure", {
|
|
73
|
+
...event,
|
|
74
|
+
error: {
|
|
75
|
+
name: error.name,
|
|
76
|
+
message: error.message,
|
|
77
|
+
trace: error.stack
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
writeStepEvent("completed", {
|
|
81
|
+
...event,
|
|
82
|
+
type: "failure"
|
|
83
|
+
});
|
|
84
|
+
throw err;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
37
87
|
export {
|
|
38
88
|
createWorkflow
|
|
39
89
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A symbolic marker for the declared credential value. It is safe to include
|
|
3
|
+
* in Worker source and manifests because it never contains the stored secret.
|
|
4
|
+
*/
|
|
5
|
+
export declare const CREDENTIAL_VALUE = "{{credential}}";
|
|
6
|
+
export type CredentialKey = string;
|
|
7
|
+
/** A Vercel-compatible collection of headers to add or replace. */
|
|
8
|
+
export type CredentialHeaderTransform = {
|
|
9
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Limits credential injection to a domain. This does not grant or deny general
|
|
13
|
+
* network egress; the sandbox's egress policy remains a separate concern.
|
|
14
|
+
*/
|
|
15
|
+
export type CredentialNetworkRule = {
|
|
16
|
+
readonly domain: string;
|
|
17
|
+
readonly transform: readonly CredentialHeaderTransform[];
|
|
18
|
+
};
|
|
19
|
+
export type CredentialConfiguration = {
|
|
20
|
+
/** Destinations and header templates where the credential may be injected. */
|
|
21
|
+
readonly network: readonly CredentialNetworkRule[];
|
|
22
|
+
};
|
|
23
|
+
export type CredentialDeclaration = CredentialConfiguration & {
|
|
24
|
+
readonly key: CredentialKey;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=credential.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential.d.ts","sourceRoot":"","sources":["../src/credential.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,gBAAgB,mBAAmB,CAAC;AAkBjD,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC;AAEnC,mEAAmE;AACnE,MAAM,MAAM,yBAAyB,GAAG;IACvC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACnD,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,SAAS,yBAAyB,EAAE,CAAC;CACzD,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACrC,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,uBAAuB,GAAG;IAC7D,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;CAC5B,CAAC"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
const CREDENTIAL_VALUE = "{{credential}}";
|
|
2
|
+
const MAX_CREDENTIALS = 100;
|
|
3
|
+
const MAX_CREDENTIAL_DECLARATIONS_BYTES = 256 * 1024;
|
|
4
|
+
const MAX_NETWORK_RULES_PER_CREDENTIAL = 20;
|
|
5
|
+
const MAX_TRANSFORMS_PER_NETWORK_RULE = 20;
|
|
6
|
+
const MAX_HEADERS_PER_NETWORK_RULE = 20;
|
|
7
|
+
const MAX_DOMAIN_LENGTH = 253;
|
|
8
|
+
const MAX_HEADER_NAME_LENGTH = 256;
|
|
9
|
+
const MAX_HEADER_TEMPLATE_BYTES = 8 * 1024;
|
|
10
|
+
const ALLOWED_NOTION_CREDENTIAL_KEYS = /* @__PURE__ */ new Set(["NOTION_API_TOKEN", "NOTION_API_BASE_URL"]);
|
|
11
|
+
const CREDENTIAL_DOMAIN_PATTERN = /^(?:\*\.)?(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
12
|
+
function createCredentialDeclaration(key, config) {
|
|
13
|
+
validateCredentialKey(key);
|
|
14
|
+
if (config.network.length === 0) {
|
|
15
|
+
throw new Error(`Credential "${key}" must declare at least one network rule`);
|
|
16
|
+
}
|
|
17
|
+
if (config.network.length > MAX_NETWORK_RULES_PER_CREDENTIAL) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`Credential "${key}" cannot declare more than ${MAX_NETWORK_RULES_PER_CREDENTIAL} domains`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
const domains = /* @__PURE__ */ new Set();
|
|
23
|
+
const network = config.network.map((rule) => {
|
|
24
|
+
const domain = normalizeDomain(rule.domain);
|
|
25
|
+
if (domains.has(domain)) {
|
|
26
|
+
throw new Error(`Credential "${key}" declares domain "${domain}" more than once`);
|
|
27
|
+
}
|
|
28
|
+
domains.add(domain);
|
|
29
|
+
if (rule.transform.length === 0 || rule.transform.length > MAX_TRANSFORMS_PER_NETWORK_RULE) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Credential "${key}" must declare 1-${MAX_TRANSFORMS_PER_NETWORK_RULE} transforms for domain "${domain}"`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const headerNames = /* @__PURE__ */ new Set();
|
|
35
|
+
return {
|
|
36
|
+
domain,
|
|
37
|
+
transform: rule.transform.map((transform) => {
|
|
38
|
+
const headers = normalizeHeaders({
|
|
39
|
+
credentialKey: key,
|
|
40
|
+
domain,
|
|
41
|
+
headers: transform.headers
|
|
42
|
+
});
|
|
43
|
+
for (const headerName of Object.keys(headers)) {
|
|
44
|
+
const normalizedHeaderName = headerName.toLowerCase();
|
|
45
|
+
if (headerNames.has(normalizedHeaderName)) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Credential "${key}" declares header "${headerName}" more than once for domain "${domain}"`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
headerNames.add(normalizedHeaderName);
|
|
51
|
+
if (headerNames.size > MAX_HEADERS_PER_NETWORK_RULE) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Credential "${key}" cannot inject more than ${MAX_HEADERS_PER_NETWORK_RULE} headers for domain "${domain}"`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { headers };
|
|
58
|
+
})
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
key,
|
|
63
|
+
network
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function validateCredentialDeclarationConflicts(existingDeclarations, declaration) {
|
|
67
|
+
for (const existing of existingDeclarations) {
|
|
68
|
+
for (const rule of declaration.network) {
|
|
69
|
+
for (const existingRule of existing.network) {
|
|
70
|
+
if (!credentialDomainsOverlap(rule.domain, existingRule.domain)) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const existingHeaders = new Set(
|
|
74
|
+
existingRule.transform.flatMap(
|
|
75
|
+
(transform) => Object.keys(transform.headers).map((name) => name.toLowerCase())
|
|
76
|
+
)
|
|
77
|
+
);
|
|
78
|
+
for (const transform of rule.transform) {
|
|
79
|
+
for (const headerName of Object.keys(transform.headers)) {
|
|
80
|
+
if (existingHeaders.has(headerName.toLowerCase())) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`Credential "${declaration.key}" conflicts with credential "${existing.key}" for header "${headerName}" on overlapping domains "${rule.domain}" and "${existingRule.domain}"`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function validateCredentialKey(key) {
|
|
92
|
+
if (!/^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(key)) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`Credential key "${key}" must start with a letter, contain only letters, numbers, or underscores, and be at most 64 characters`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
const normalizedKey = key.toUpperCase();
|
|
98
|
+
if (normalizedKey.startsWith("NOTION_") && !ALLOWED_NOTION_CREDENTIAL_KEYS.has(normalizedKey)) {
|
|
99
|
+
throw new Error(`Credential key "${key}" uses the reserved NOTION_ prefix`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function normalizeDomain(value) {
|
|
103
|
+
const domain = value.trim().toLowerCase().replace(/\.$/, "");
|
|
104
|
+
if (domain.length > MAX_DOMAIN_LENGTH || !CREDENTIAL_DOMAIN_PATTERN.test(domain)) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`Credential network domain "${value}" must be an exact hostname or a leading wildcard such as "*.example.com"`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return domain;
|
|
110
|
+
}
|
|
111
|
+
function credentialDomainsOverlap(first, second) {
|
|
112
|
+
if (first === second) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
const firstBase = first.startsWith("*.") ? first.slice(2) : void 0;
|
|
116
|
+
const secondBase = second.startsWith("*.") ? second.slice(2) : void 0;
|
|
117
|
+
if (firstBase === void 0 && secondBase === void 0) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
if (firstBase !== void 0 && secondBase === void 0) {
|
|
121
|
+
return second.endsWith(`.${firstBase}`);
|
|
122
|
+
}
|
|
123
|
+
if (firstBase === void 0 && secondBase !== void 0) {
|
|
124
|
+
return first.endsWith(`.${secondBase}`);
|
|
125
|
+
}
|
|
126
|
+
return firstBase !== void 0 && secondBase !== void 0 && (firstBase.endsWith(`.${secondBase}`) || secondBase.endsWith(`.${firstBase}`));
|
|
127
|
+
}
|
|
128
|
+
function normalizeHeaders(args) {
|
|
129
|
+
const entries = Object.entries(args.headers);
|
|
130
|
+
if (entries.length === 0) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
`Credential "${args.credentialKey}" has an empty header transform for domain "${args.domain}"`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
return Object.fromEntries(
|
|
136
|
+
entries.map(([name, template]) => {
|
|
137
|
+
if (name.length > MAX_HEADER_NAME_LENGTH || !/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(name)) {
|
|
138
|
+
throw new Error(`Invalid credential header name "${name}"`);
|
|
139
|
+
}
|
|
140
|
+
if (typeof template !== "string") {
|
|
141
|
+
throw new Error(`Credential header "${name}" must use a string template`);
|
|
142
|
+
}
|
|
143
|
+
validateHeaderTemplate(name, template);
|
|
144
|
+
return [name, template];
|
|
145
|
+
})
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
function validateHeaderTemplate(name, template) {
|
|
149
|
+
if (hasControlCharacters(template)) {
|
|
150
|
+
throw new Error(`Credential header "${name}" template cannot contain control characters`);
|
|
151
|
+
}
|
|
152
|
+
const markerCount = template.split(CREDENTIAL_VALUE).length - 1;
|
|
153
|
+
if (markerCount !== 1) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Credential header "${name}" template must contain CREDENTIAL_VALUE exactly once`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
if (new TextEncoder().encode(template).byteLength > MAX_HEADER_TEMPLATE_BYTES) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`Credential header "${name}" template must be at most ${MAX_HEADER_TEMPLATE_BYTES} bytes`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function hasControlCharacters(value) {
|
|
165
|
+
return Array.from(value).some((character) => {
|
|
166
|
+
const codePoint = character.codePointAt(0);
|
|
167
|
+
return codePoint !== void 0 && (codePoint <= 31 || codePoint === 127);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
export {
|
|
171
|
+
CREDENTIAL_VALUE,
|
|
172
|
+
MAX_CREDENTIALS,
|
|
173
|
+
MAX_CREDENTIAL_DECLARATIONS_BYTES,
|
|
174
|
+
createCredentialDeclaration,
|
|
175
|
+
validateCredentialDeclarationConflicts
|
|
176
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential.test.d.ts","sourceRoot":"","sources":["../src/credential.test.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export type { ToolCapability, ToolConfiguration, ToolHints } from "./capabilitie
|
|
|
9
9
|
export type { WebhookCapability, WebhookConfiguration, WebhookEvent, } from "./capabilities/webhook.js";
|
|
10
10
|
export { WebhookVerificationError } from "./capabilities/webhook.js";
|
|
11
11
|
export type { WorkflowCapability, WorkflowConfiguration, WorkflowEvent, WorkflowEventForTrigger, WorkflowEventForTriggers, } from "./capabilities/workflow.js";
|
|
12
|
+
export type { CredentialConfiguration, CredentialDeclaration, CredentialHeaderTransform, CredentialKey, CredentialNetworkRule, } from "./credential.js";
|
|
13
|
+
export { CREDENTIAL_VALUE } from "./credential.js";
|
|
12
14
|
export { RateLimitError } from "./error.js";
|
|
13
15
|
export type { AnyJSONSchema, JSONSchema } from "./json-schema.js";
|
|
14
16
|
export type { Infer, SchemaBuilder } from "./schema-builder.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,qBAAqB,EACrB,2BAA2B,EAC3B,wBAAwB,EACxB,mBAAmB,EACnB,6BAA6B,GAC7B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACnF,YAAY,EACX,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,uBAAuB,EACvB,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC1B,eAAe,EACf,uBAAuB,EACvB,wBAAwB,EACxB,4BAA4B,GAC5B,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACX,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,EACf,kBAAkB,GAClB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,YAAY,EACX,+BAA+B,EAC/B,eAAe,EACf,kBAAkB,EAClB,6BAA6B,GAC7B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACX,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,QAAQ,GACR,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC3F,YAAY,EACX,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,GACZ,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,YAAY,EACX,kBAAkB,EAClB,qBAAqB,EACrB,aAAa,EACb,uBAAuB,EACvB,wBAAwB,GACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAClE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EACX,KAAK,EACL,IAAI,EACJ,UAAU,EACV,SAAS,EACT,YAAY,EACZ,WAAW,EACX,UAAU,EACV,QAAQ,GACR,MAAM,YAAY,CAAC;AACpB,YAAY,EACX,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,cAAc,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,qBAAqB,EACrB,2BAA2B,EAC3B,wBAAwB,EACxB,mBAAmB,EACnB,6BAA6B,GAC7B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACnF,YAAY,EACX,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,uBAAuB,EACvB,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC1B,eAAe,EACf,uBAAuB,EACvB,wBAAwB,EACxB,4BAA4B,GAC5B,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACX,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,EACf,kBAAkB,GAClB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,YAAY,EACX,+BAA+B,EAC/B,eAAe,EACf,kBAAkB,EAClB,6BAA6B,GAC7B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACX,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,QAAQ,GACR,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC3F,YAAY,EACX,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,GACZ,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,YAAY,EACX,kBAAkB,EAClB,qBAAqB,EACrB,aAAa,EACb,uBAAuB,EACvB,wBAAwB,GACxB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACX,uBAAuB,EACvB,qBAAqB,EACrB,yBAAyB,EACzB,aAAa,EACb,qBAAqB,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAClE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EACX,KAAK,EACL,IAAI,EACJ,UAAU,EACV,SAAS,EACT,YAAY,EACZ,WAAW,EACX,UAAU,EACV,QAAQ,GACR,MAAM,YAAY,CAAC;AACpB,YAAY,EACX,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,cAAc,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { emojiIcon, imageCover, imageIcon, notionIcon, place } from "./builder.js";
|
|
2
2
|
import { WebhookVerificationError } from "./capabilities/webhook.js";
|
|
3
|
+
import { CREDENTIAL_VALUE } from "./credential.js";
|
|
3
4
|
import { RateLimitError } from "./error.js";
|
|
4
5
|
import { getSchema, j } from "./schema-builder.js";
|
|
5
6
|
import { Worker } from "./worker.js";
|
|
6
7
|
export {
|
|
8
|
+
CREDENTIAL_VALUE,
|
|
7
9
|
RateLimitError,
|
|
8
10
|
WebhookVerificationError,
|
|
9
11
|
Worker,
|
package/dist/worker.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { ToolCapability, ToolConfiguration } from "./capabilities/tool.js";
|
|
|
8
8
|
import type { WebhookCapability, WebhookConfiguration, WebhookEvent } from "./capabilities/webhook.js";
|
|
9
9
|
import { WebhookVerificationError } from "./capabilities/webhook.js";
|
|
10
10
|
import type { WorkflowCapability, WorkflowConfiguration, WorkflowEvent } from "./capabilities/workflow.js";
|
|
11
|
+
import type { CredentialConfiguration, CredentialDeclaration, CredentialKey } from "./credential.js";
|
|
11
12
|
import type { PacerDeclaration } from "./pacer_internal.js";
|
|
12
13
|
import type { Schema } from "./schema.js";
|
|
13
14
|
import type { WorkflowTrigger } from "./triggers.generated.js";
|
|
@@ -105,12 +106,18 @@ type CapabilityManifestEntry = Pick<Capability, "_tag" | "key" | "config">;
|
|
|
105
106
|
*/
|
|
106
107
|
export type WorkerManifest = {
|
|
107
108
|
readonly sdkVersion: string;
|
|
109
|
+
readonly credentials: readonly CredentialDeclaration[];
|
|
108
110
|
readonly databases: readonly RegisteredDatabase[];
|
|
109
111
|
readonly pacers: readonly PacerDeclaration[];
|
|
110
112
|
readonly capabilities: readonly CapabilityManifestEntry[];
|
|
111
113
|
};
|
|
112
114
|
export declare class Worker {
|
|
113
115
|
#private;
|
|
116
|
+
/**
|
|
117
|
+
* Declare a credential that is injected into matching outbound HTTPS
|
|
118
|
+
* requests without exposing its value to Worker code.
|
|
119
|
+
*/
|
|
120
|
+
credential(key: CredentialKey, config: CredentialConfiguration): void;
|
|
114
121
|
database<PK extends string, S extends Schema<PK>>(key: string, config: DatabaseConfig<PK, S>): DatabaseHandle<PK, S>;
|
|
115
122
|
/**
|
|
116
123
|
* Register a pacer for rate-limiting requests to an external API.
|
package/dist/worker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
|
package/dist/worker.js
CHANGED
|
@@ -6,6 +6,12 @@ import { createSyncCapability } from "./capabilities/sync.js";
|
|
|
6
6
|
import { createToolCapability } from "./capabilities/tool.js";
|
|
7
7
|
import { createWebhookCapability, WebhookVerificationError } from "./capabilities/webhook.js";
|
|
8
8
|
import { createWorkflowCapability } from "./capabilities/workflow.js";
|
|
9
|
+
import {
|
|
10
|
+
createCredentialDeclaration,
|
|
11
|
+
MAX_CREDENTIAL_DECLARATIONS_BYTES,
|
|
12
|
+
MAX_CREDENTIALS,
|
|
13
|
+
validateCredentialDeclarationConflicts
|
|
14
|
+
} from "./credential.js";
|
|
9
15
|
import { pacerWait } from "./pacer_internal.js";
|
|
10
16
|
import { createRequire } from "node:module";
|
|
11
17
|
const SDK_VERSION = (() => {
|
|
@@ -18,8 +24,28 @@ const SDK_VERSION = (() => {
|
|
|
18
24
|
})();
|
|
19
25
|
class Worker {
|
|
20
26
|
#capabilities = /* @__PURE__ */ new Map();
|
|
27
|
+
#credentials = /* @__PURE__ */ new Map();
|
|
21
28
|
#databases = /* @__PURE__ */ new Map();
|
|
22
29
|
#pacers = /* @__PURE__ */ new Map();
|
|
30
|
+
/**
|
|
31
|
+
* Declare a credential that is injected into matching outbound HTTPS
|
|
32
|
+
* requests without exposing its value to Worker code.
|
|
33
|
+
*/
|
|
34
|
+
credential(key, config) {
|
|
35
|
+
this.#validateUniqueKey(key);
|
|
36
|
+
if (this.#credentials.size >= MAX_CREDENTIALS) {
|
|
37
|
+
throw new Error(`Worker cannot declare more than ${MAX_CREDENTIALS} credentials`);
|
|
38
|
+
}
|
|
39
|
+
const declaration = createCredentialDeclaration(key, config);
|
|
40
|
+
validateCredentialDeclarationConflicts(this.#credentials.values(), declaration);
|
|
41
|
+
const declarations = [...this.#credentials.values(), declaration];
|
|
42
|
+
if (new TextEncoder().encode(JSON.stringify(declarations)).byteLength > MAX_CREDENTIAL_DECLARATIONS_BYTES) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`Worker credential declarations must be at most ${MAX_CREDENTIAL_DECLARATIONS_BYTES} bytes`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
this.#credentials.set(key, declaration);
|
|
48
|
+
}
|
|
23
49
|
database(key, config) {
|
|
24
50
|
this.#validateUniqueKey(key);
|
|
25
51
|
const database = { key, config };
|
|
@@ -423,6 +449,7 @@ class Worker {
|
|
|
423
449
|
get manifest() {
|
|
424
450
|
return {
|
|
425
451
|
sdkVersion: SDK_VERSION,
|
|
452
|
+
credentials: Array.from(this.#credentials.values()),
|
|
426
453
|
databases: Array.from(this.#databases.values()),
|
|
427
454
|
pacers: Array.from(this.#pacers.values()),
|
|
428
455
|
capabilities: this.capabilities
|
|
@@ -460,7 +487,7 @@ class Worker {
|
|
|
460
487
|
if (!key || typeof key !== "string") {
|
|
461
488
|
throw new Error("Capability key must be a non-empty string");
|
|
462
489
|
}
|
|
463
|
-
if (this.#capabilities.has(key) || this.#databases.has(key) || this.#pacers.has(key)) {
|
|
490
|
+
if (this.#capabilities.has(key) || this.#credentials.has(key) || this.#databases.has(key) || this.#pacers.has(key)) {
|
|
464
491
|
throw new Error(`Worker item with key "${key}" already registered`);
|
|
465
492
|
}
|
|
466
493
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi, type Mock } from "vitest";
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, expectTypeOf, it, vi, type Mock } from "vitest";
|
|
2
2
|
|
|
3
3
|
import { ExecutionError } from "./error.js";
|
|
4
4
|
import { triggers, type NotionPageCreatedEvent } from "./triggers.generated.js";
|
|
5
|
-
import { createWorkflow } from "./workflow.js";
|
|
5
|
+
import { createWorkflow, type StepContext, type WorkflowContext } from "./workflow.js";
|
|
6
6
|
|
|
7
7
|
let stdoutSpy: Mock<typeof process.stdout.write>;
|
|
8
8
|
|
|
@@ -16,6 +16,128 @@ afterEach(() => {
|
|
|
16
16
|
|
|
17
17
|
const event = { type: "notion.page.created" } as NotionPageCreatedEvent;
|
|
18
18
|
|
|
19
|
+
async function createWorkflowContext(): Promise<WorkflowContext> {
|
|
20
|
+
let context: WorkflowContext | undefined;
|
|
21
|
+
const workflow = createWorkflow({
|
|
22
|
+
title: "Test workflow",
|
|
23
|
+
description: "Captures its workflow context",
|
|
24
|
+
triggers: [triggers.notionPageCreated()],
|
|
25
|
+
handler: (_event, workflowContext) => {
|
|
26
|
+
context = workflowContext;
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
await workflow.handler(event, { concreteOutput: true });
|
|
31
|
+
|
|
32
|
+
if (!context) {
|
|
33
|
+
throw new Error("Workflow context was not created");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return context;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe("step", () => {
|
|
40
|
+
it("runs a synchronous callback and writes success events in order", async () => {
|
|
41
|
+
const { step } = await createWorkflowContext();
|
|
42
|
+
let context: StepContext | undefined;
|
|
43
|
+
const resultPromise = step("Fetch page", (stepContext) => {
|
|
44
|
+
context = stepContext;
|
|
45
|
+
return { pageId: "page-1" } as const;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expectTypeOf(resultPromise).toEqualTypeOf<Promise<{ readonly pageId: "page-1" }>>();
|
|
49
|
+
await expect(resultPromise).resolves.toEqual({ pageId: "page-1" });
|
|
50
|
+
|
|
51
|
+
expect(context).toBeDefined();
|
|
52
|
+
const id = context!.id;
|
|
53
|
+
expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
|
|
54
|
+
expect(stdoutSpy.mock.calls.map(([output]) => output)).toEqual([
|
|
55
|
+
`\n<__notion_step_started__>{"id":"${id}","name":"Fetch page"}</__notion_step_started__>\n`,
|
|
56
|
+
`\n<__notion_step_success__>{"id":"${id}","name":"Fetch page","value":{"pageId":"page-1"}}</__notion_step_success__>\n`,
|
|
57
|
+
`\n<__notion_step_completed__>{"id":"${id}","name":"Fetch page","type":"success"}</__notion_step_completed__>\n`,
|
|
58
|
+
]);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("embeds the invocation timestamp in the UUID v7", async () => {
|
|
62
|
+
const { step } = await createWorkflowContext();
|
|
63
|
+
const now = 1_721_234_567_890;
|
|
64
|
+
const nowSpy = vi.spyOn(Date, "now").mockReturnValue(now);
|
|
65
|
+
let id = "";
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
await step("Timestamped", async (context) => {
|
|
69
|
+
id = context.id;
|
|
70
|
+
});
|
|
71
|
+
} finally {
|
|
72
|
+
nowSpy.mockRestore();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
expect(Number.parseInt(id.replaceAll("-", "").slice(0, 12), 16)).toBe(now);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("uses a distinct UUID for each invocation", async () => {
|
|
79
|
+
const { step } = await createWorkflowContext();
|
|
80
|
+
const ids: string[] = [];
|
|
81
|
+
|
|
82
|
+
await step("First", ({ id }) => {
|
|
83
|
+
ids.push(id);
|
|
84
|
+
});
|
|
85
|
+
await step("Second", ({ id }) => {
|
|
86
|
+
ids.push(id);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
expect(new Set(ids).size).toBe(2);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("writes failure events in order and rethrows the original error", async () => {
|
|
93
|
+
const { step } = await createWorkflowContext();
|
|
94
|
+
const originalError = new Error("boom");
|
|
95
|
+
let id = "";
|
|
96
|
+
const fn = vi.fn((context: StepContext) => {
|
|
97
|
+
id = context.id;
|
|
98
|
+
throw originalError;
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
await expect(step("Explode", fn)).rejects.toBe(originalError);
|
|
102
|
+
|
|
103
|
+
expect(fn).toHaveBeenCalledOnce();
|
|
104
|
+
expect(stdoutSpy).toHaveBeenCalledTimes(3);
|
|
105
|
+
expect(stdoutSpy.mock.calls[0]?.[0]).toBe(
|
|
106
|
+
`\n<__notion_step_started__>{"id":"${id}","name":"Explode"}</__notion_step_started__>\n`,
|
|
107
|
+
);
|
|
108
|
+
expect(stdoutSpy.mock.calls[1]?.[0]).toEqual(
|
|
109
|
+
expect.stringContaining(
|
|
110
|
+
`<__notion_step_failure__>{"id":"${id}","name":"Explode","error":{"name":"ExecutionError","message":"Error during worker execution: Error: boom","trace":`,
|
|
111
|
+
),
|
|
112
|
+
);
|
|
113
|
+
expect(stdoutSpy.mock.calls[2]?.[0]).toBe(
|
|
114
|
+
`\n<__notion_step_completed__>{"id":"${id}","name":"Explode","type":"failure"}</__notion_step_completed__>\n`,
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("writes step events under concreteOutput", async () => {
|
|
119
|
+
const workflow = createWorkflow({
|
|
120
|
+
title: "Log Pages",
|
|
121
|
+
description: "Logs created pages",
|
|
122
|
+
triggers: [triggers.notionPageCreated()],
|
|
123
|
+
handler: async (_event, context) => {
|
|
124
|
+
await context.step("Always visible", () => "done");
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
await workflow.handler(event, { concreteOutput: true });
|
|
129
|
+
|
|
130
|
+
expect(stdoutSpy).toHaveBeenCalledTimes(3);
|
|
131
|
+
expect(stdoutSpy.mock.calls[0]?.[0]).toEqual(
|
|
132
|
+
expect.stringContaining("<__notion_step_started__>"),
|
|
133
|
+
);
|
|
134
|
+
expect(stdoutSpy.mock.calls[1]?.[0]).toEqual(
|
|
135
|
+
expect.stringContaining("<__notion_step_success__>"),
|
|
136
|
+
);
|
|
137
|
+
expect(stdoutSpy.mock.calls[2]?.[0]).toEqual(expect.stringContaining('"type":"success"'));
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
19
141
|
describe("createWorkflow", () => {
|
|
20
142
|
it("is tagged and exposes triggers in config", () => {
|
|
21
143
|
const workflow = createWorkflow({
|