@steadlake/run 0.2.1 → 0.3.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/README.md +231 -7
- package/dist/adapters/generic.d.ts +22 -1
- package/dist/adapters/generic.d.ts.map +1 -1
- package/dist/adapters/generic.js +27 -4
- package/dist/adapters/next.d.ts +20 -1
- package/dist/adapters/next.d.ts.map +1 -1
- package/dist/adapters/next.js +25 -4
- package/dist/adapters/utils/register.d.ts +3 -0
- package/dist/adapters/utils/register.d.ts.map +1 -0
- package/dist/adapters/{register.js → utils/register.js} +2 -2
- package/dist/adapters/worker.d.ts +31 -0
- package/dist/adapters/worker.d.ts.map +1 -0
- package/dist/adapters/worker.js +41 -0
- package/dist/client.d.ts +2 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +5 -10
- package/dist/default.d.ts +6 -0
- package/dist/default.d.ts.map +1 -0
- package/dist/default.js +13 -0
- package/dist/handler.d.ts +1 -1
- package/dist/handler.d.ts.map +1 -1
- package/dist/handler.js +1 -12
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +12 -4
- package/dist/adapters/register.d.ts +0 -3
- package/dist/adapters/register.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,15 +1,239 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @steadlake/run
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Durable task execution for your backend. Define tasks with steps, retries, scheduling, and event-driven workflows — without managing infrastructure.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
4
6
|
|
|
5
7
|
```bash
|
|
6
|
-
|
|
8
|
+
npm install @steadlake/run
|
|
7
9
|
```
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
## Setup
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
**Environment variables:**
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
STEADLAKE_API_KEY=sk_live_...
|
|
17
|
+
STEADLAKE_PROJECT_ID=proj_...
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
### 1. Define tasks
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// tasks/send-email.ts
|
|
26
|
+
import { task } from "@steadlake/run";
|
|
27
|
+
import { z } from "zod";
|
|
28
|
+
|
|
29
|
+
export const sendEmail = task({
|
|
30
|
+
id: "send-email",
|
|
31
|
+
schema: z.object({ to: z.string().email(), subject: z.string() }),
|
|
32
|
+
run: async ({ payload }) => {
|
|
33
|
+
await emailService.send(payload.to, payload.subject);
|
|
34
|
+
return { sent: true };
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 2. Mount the handler
|
|
40
|
+
|
|
41
|
+
**Next.js**
|
|
42
|
+
```ts
|
|
43
|
+
// app/api/steadlake/route.ts
|
|
44
|
+
import { createNextHandler } from "@steadlake/run/adapters/next";
|
|
45
|
+
import { sendEmail } from "@/tasks/send-email";
|
|
46
|
+
|
|
47
|
+
export const { POST } = createNextHandler({
|
|
48
|
+
baseUrl: process.env.APP_URL!,
|
|
49
|
+
tasks: [sendEmail],
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Hono / Generic**
|
|
54
|
+
```ts
|
|
55
|
+
import { createHandler } from "@steadlake/run/adapters/generic";
|
|
56
|
+
import { sendEmail } from "./tasks/send-email";
|
|
57
|
+
|
|
58
|
+
const handler = createHandler({
|
|
59
|
+
baseUrl: process.env.APP_URL!,
|
|
60
|
+
tasks: [sendEmail],
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
app.post("/api/steadlake", (c) => handler(c.req.raw));
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Cloudflare Workers**
|
|
67
|
+
```ts
|
|
68
|
+
import { createWorkerHandler } from "@steadlake/run/adapters/worker";
|
|
69
|
+
import { sendEmail } from "./tasks/send-email";
|
|
70
|
+
|
|
71
|
+
const handler = createWorkerHandler({ tasks: [sendEmail] });
|
|
72
|
+
|
|
73
|
+
export default {
|
|
74
|
+
fetch(req: Request, env: Env) {
|
|
75
|
+
if (new URL(req.url).pathname === "/api/steadlake") {
|
|
76
|
+
return handler(req, env, env.WORKER_URL);
|
|
77
|
+
}
|
|
78
|
+
return app.fetch(req, env);
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 3. Trigger
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
// anywhere in your server code
|
|
87
|
+
import { sendEmail } from "@/tasks/send-email";
|
|
88
|
+
|
|
89
|
+
await sendEmail.trigger({ to: "user@example.com", subject: "Welcome!" });
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Steps
|
|
95
|
+
|
|
96
|
+
Break long tasks into named, durable steps. Completed steps are replayed from state on retry — they never re-execute.
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
export const importUsers = task({
|
|
100
|
+
id: "import-users",
|
|
101
|
+
run: async ({ payload, step }) => {
|
|
102
|
+
const users = await step.run("fetch", () => api.getUsers(payload.source));
|
|
103
|
+
const valid = await step.run("validate", () => users.filter(isValid));
|
|
104
|
+
await step.run("insert", () => db.users.bulkInsert(valid));
|
|
105
|
+
return { imported: valid.length };
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Sleep
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
await step.sleep("wait", "3d"); // duration: ms, s, m, h, d
|
|
114
|
+
await step.sleepUntil("until", date); // Date | ISO string
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Wait for external event
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
const shipment = await step.waitForEvent<{ trackingId: string }>("shipped", {
|
|
121
|
+
event: "order.shipped",
|
|
122
|
+
timeout: "7d",
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Resume by calling `run.sendEvent("order.shipped", { trackingId: "..." })`.
|
|
127
|
+
|
|
128
|
+
### Human approval
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
const { approved } = await step.approve("review", {
|
|
132
|
+
title: "Approve $10,000 payout?",
|
|
133
|
+
timeout: "24h",
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Resume by calling `client.resolveApproval(runId, stepId, true)`.
|
|
138
|
+
|
|
139
|
+
### Emit events to other runs
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
step.emit("order.shipped", { trackingId: "TRK123" });
|
|
13
143
|
```
|
|
14
144
|
|
|
15
|
-
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Retries
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
export const chargeCustomer = task({
|
|
151
|
+
id: "charge-customer",
|
|
152
|
+
retry: {
|
|
153
|
+
maxAttempts: 5,
|
|
154
|
+
backoff: "exponential", // "exponential" | "linear" | "fixed"
|
|
155
|
+
},
|
|
156
|
+
run: async ({ payload }) => {
|
|
157
|
+
await stripe.charges.create({ amount: payload.amount });
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Throw `FatalError` to fail permanently without retrying. Throw `RetryableError` to retry explicitly.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Cron
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
export const dailyReport = task({
|
|
170
|
+
id: "daily-report",
|
|
171
|
+
cron: "0 9 * * 1-5",
|
|
172
|
+
run: async () => { ... },
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// With timezone
|
|
176
|
+
export const digest = task({
|
|
177
|
+
id: "digest",
|
|
178
|
+
cron: { pattern: "0 8 * * *", timezone: "America/New_York" },
|
|
179
|
+
run: async () => { ... },
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Queues
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
export const processVideo = task({
|
|
189
|
+
id: "process-video",
|
|
190
|
+
queue: {
|
|
191
|
+
name: "video",
|
|
192
|
+
concurrencyLimit: 3,
|
|
193
|
+
rateLimit: { max: 100, duration: "1h" },
|
|
194
|
+
},
|
|
195
|
+
run: async ({ payload }) => { ... },
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Trigger options
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
await sendEmail.trigger(payload, {
|
|
205
|
+
idempotencyKey: `email-${userId}`,
|
|
206
|
+
webhook: "https://myapp.com/webhooks/done",
|
|
207
|
+
tags: ["transactional"],
|
|
208
|
+
priority: 10,
|
|
209
|
+
scheduledAt: new Date(Date.now() + 60_000).toISOString(),
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Wait for result:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
const result = await sendEmail.triggerAndWait(payload, { timeoutMs: 30_000 });
|
|
217
|
+
console.log(result.status); // "success" | "failed"
|
|
218
|
+
console.log(result.result);
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Error types
|
|
224
|
+
|
|
225
|
+
| | HTTP | DEW behaviour |
|
|
226
|
+
|---|---|---|
|
|
227
|
+
| `FatalError` | 400 | Permanent failure, no retry |
|
|
228
|
+
| `RetryableError` | 500 | Explicit retry request |
|
|
229
|
+
| Any other error | 500 | Retry if attempts remain |
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Environment variables
|
|
234
|
+
|
|
235
|
+
| Variable | Description |
|
|
236
|
+
|---|---|
|
|
237
|
+
| `STEADLAKE_API_KEY` | Your project API key |
|
|
238
|
+
| `STEADLAKE_PROJECT_ID` | Your project ID |
|
|
239
|
+
| `STEADLAKE_API_URL` | Override platform URL (self-hosted) |
|
|
@@ -1,3 +1,24 @@
|
|
|
1
1
|
import type { Steadlake } from "../client.js";
|
|
2
|
-
|
|
2
|
+
import type { Task } from "../types.js";
|
|
3
|
+
export interface HandlerOptions {
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
tasks?: Task<unknown, unknown>[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Creates a generic fetch handler compatible with any framework that uses
|
|
9
|
+
* the standard Web API Request/Response interface (Hono, Fastify, Express, etc).
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // Hono
|
|
13
|
+
* import { createHandler } from '@steadlake/run/adapters/generic'
|
|
14
|
+
* import { sendEmail } from './tasks/send-email'
|
|
15
|
+
*
|
|
16
|
+
* const dewHandler = createHandler({
|
|
17
|
+
* baseUrl: process.env.APP_URL!,
|
|
18
|
+
* tasks: [sendEmail],
|
|
19
|
+
* })
|
|
20
|
+
*
|
|
21
|
+
* app.post('/api/steadlake', (c) => dewHandler(c.req.raw))
|
|
22
|
+
*/
|
|
23
|
+
export declare function createHandler(clientOrOptions: Steadlake | HandlerOptions, options?: HandlerOptions): (req: Request) => Promise<Response>;
|
|
3
24
|
//# sourceMappingURL=generic.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generic.d.ts","sourceRoot":"","sources":["../../src/adapters/generic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"generic.d.ts","sourceRoot":"","sources":["../../src/adapters/generic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAGxC,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC;CACjC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,eAAe,EAAE,SAAS,GAAG,cAAc,EAAE,OAAO,CAAC,EAAE,cAAc,uCAWlG"}
|
package/dist/adapters/generic.js
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { getDefaultClient } from "../default.js";
|
|
2
|
+
import { registerWithRetry } from "./utils/register.js";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a generic fetch handler compatible with any framework that uses
|
|
5
|
+
* the standard Web API Request/Response interface (Hono, Fastify, Express, etc).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // Hono
|
|
9
|
+
* import { createHandler } from '@steadlake/run/adapters/generic'
|
|
10
|
+
* import { sendEmail } from './tasks/send-email'
|
|
11
|
+
*
|
|
12
|
+
* const dewHandler = createHandler({
|
|
13
|
+
* baseUrl: process.env.APP_URL!,
|
|
14
|
+
* tasks: [sendEmail],
|
|
15
|
+
* })
|
|
16
|
+
*
|
|
17
|
+
* app.post('/api/steadlake', (c) => dewHandler(c.req.raw))
|
|
18
|
+
*/
|
|
19
|
+
export function createHandler(clientOrOptions, options) {
|
|
20
|
+
const isClient = clientOrOptions instanceof Object && "_registry" in clientOrOptions;
|
|
21
|
+
const c = isClient ? clientOrOptions : getDefaultClient();
|
|
22
|
+
const opts = isClient ? options : clientOrOptions;
|
|
23
|
+
for (const task of opts.tasks ?? []) {
|
|
24
|
+
c._registry.set(task.id, task);
|
|
25
|
+
}
|
|
26
|
+
registerWithRetry(c, opts.baseUrl);
|
|
27
|
+
return c._createHandler();
|
|
5
28
|
}
|
package/dist/adapters/next.d.ts
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import type { Steadlake } from "../client.js";
|
|
2
|
-
|
|
2
|
+
import type { Task } from "../types.js";
|
|
3
|
+
export interface NextHandlerOptions {
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
tasks?: Task<unknown, unknown>[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Creates a route handler for Next.js App Router.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // app/api/steadlake/route.ts
|
|
12
|
+
* import { createNextHandler } from '@steadlake/run/adapters/next'
|
|
13
|
+
* import { sendEmail } from './tasks/send-email'
|
|
14
|
+
* import { processOrder } from './tasks/process-order'
|
|
15
|
+
*
|
|
16
|
+
* export const { POST } = createNextHandler({
|
|
17
|
+
* baseUrl: process.env.APP_URL!,
|
|
18
|
+
* tasks: [sendEmail, processOrder],
|
|
19
|
+
* })
|
|
20
|
+
*/
|
|
21
|
+
export declare function createNextHandler(clientOrOptions: Steadlake | NextHandlerOptions, options?: NextHandlerOptions): {
|
|
3
22
|
POST: (req: Request) => Promise<Response>;
|
|
4
23
|
};
|
|
5
24
|
//# sourceMappingURL=next.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../../src/adapters/next.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../../src/adapters/next.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAGxC,MAAM,WAAW,kBAAkB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC;CACjC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,eAAe,EAAE,SAAS,GAAG,kBAAkB,EAAE,OAAO,CAAC,EAAE,kBAAkB;gBAWzF,OAAO;EAC5B"}
|
package/dist/adapters/next.js
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { getDefaultClient } from "../default.js";
|
|
2
|
+
import { registerWithRetry } from "./utils/register.js";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a route handler for Next.js App Router.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* // app/api/steadlake/route.ts
|
|
8
|
+
* import { createNextHandler } from '@steadlake/run/adapters/next'
|
|
9
|
+
* import { sendEmail } from './tasks/send-email'
|
|
10
|
+
* import { processOrder } from './tasks/process-order'
|
|
11
|
+
*
|
|
12
|
+
* export const { POST } = createNextHandler({
|
|
13
|
+
* baseUrl: process.env.APP_URL!,
|
|
14
|
+
* tasks: [sendEmail, processOrder],
|
|
15
|
+
* })
|
|
16
|
+
*/
|
|
17
|
+
export function createNextHandler(clientOrOptions, options) {
|
|
18
|
+
const isClient = clientOrOptions instanceof Object && "_registry" in clientOrOptions;
|
|
19
|
+
const c = isClient ? clientOrOptions : getDefaultClient();
|
|
20
|
+
const opts = isClient ? options : clientOrOptions;
|
|
21
|
+
for (const task of opts.tasks ?? []) {
|
|
22
|
+
c._registry.set(task.id, task);
|
|
23
|
+
}
|
|
24
|
+
registerWithRetry(c, opts.baseUrl);
|
|
25
|
+
const handle = c._createHandler();
|
|
5
26
|
return { POST: (req) => handle(req) };
|
|
6
27
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/adapters/utils/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,SAAI,iBAoBxF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export async function registerWithRetry(client, attempts = 3) {
|
|
1
|
+
export async function registerWithRetry(client, baseUrl, attempts = 3) {
|
|
2
2
|
for (let i = 1; i <= attempts; i++) {
|
|
3
3
|
try {
|
|
4
|
-
await client._autoRegister();
|
|
4
|
+
await client._autoRegister(baseUrl);
|
|
5
5
|
return;
|
|
6
6
|
}
|
|
7
7
|
catch (err) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Steadlake } from "../client.js";
|
|
2
|
+
import type { Task } from "../types.js";
|
|
3
|
+
export interface WorkerEnv {
|
|
4
|
+
STEADLAKE_API_KEY: string;
|
|
5
|
+
STEADLAKE_PROJECT_ID: string;
|
|
6
|
+
STEADLAKE_API_URL?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface WorkerHandlerOptions {
|
|
9
|
+
tasks?: Task<unknown, unknown>[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Creates a handler for Cloudflare Workers.
|
|
13
|
+
* Reads STEADLAKE_API_KEY, STEADLAKE_PROJECT_ID from worker env bindings.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* import { createWorkerHandler } from '@steadlake/run/adapters/worker'
|
|
17
|
+
* import { sendEmail } from './tasks/send-email'
|
|
18
|
+
*
|
|
19
|
+
* const dewHandler = createWorkerHandler({ tasks: [sendEmail] })
|
|
20
|
+
*
|
|
21
|
+
* export default {
|
|
22
|
+
* fetch(req: Request, env: Env) {
|
|
23
|
+
* if (new URL(req.url).pathname === '/api/steadlake') {
|
|
24
|
+
* return dewHandler(req, env, env.WORKER_URL)
|
|
25
|
+
* }
|
|
26
|
+
* return app.fetch(req, env)
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
*/
|
|
30
|
+
export declare function createWorkerHandler<TEnv extends WorkerEnv>(clientOrOptions?: Steadlake | WorkerHandlerOptions): (req: Request, env: TEnv, baseUrl: string) => Promise<Response>;
|
|
31
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/adapters/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAGxC,MAAM,WAAW,SAAS;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACpC,KAAK,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,SAAS,SAAS,EACzD,eAAe,CAAC,EAAE,SAAS,GAAG,oBAAoB,IAEpC,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,SAAS,MAAM,KAAG,OAAO,CAAC,QAAQ,CAAC,CAoB1E"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Steadlake } from "../client.js";
|
|
2
|
+
import { configure, getDefaultClient } from "../default.js";
|
|
3
|
+
import { registerWithRetry } from "./utils/register.js";
|
|
4
|
+
/**
|
|
5
|
+
* Creates a handler for Cloudflare Workers.
|
|
6
|
+
* Reads STEADLAKE_API_KEY, STEADLAKE_PROJECT_ID from worker env bindings.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { createWorkerHandler } from '@steadlake/run/adapters/worker'
|
|
10
|
+
* import { sendEmail } from './tasks/send-email'
|
|
11
|
+
*
|
|
12
|
+
* const dewHandler = createWorkerHandler({ tasks: [sendEmail] })
|
|
13
|
+
*
|
|
14
|
+
* export default {
|
|
15
|
+
* fetch(req: Request, env: Env) {
|
|
16
|
+
* if (new URL(req.url).pathname === '/api/steadlake') {
|
|
17
|
+
* return dewHandler(req, env, env.WORKER_URL)
|
|
18
|
+
* }
|
|
19
|
+
* return app.fetch(req, env)
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*/
|
|
23
|
+
export function createWorkerHandler(clientOrOptions) {
|
|
24
|
+
return async (req, env, baseUrl) => {
|
|
25
|
+
const isClient = clientOrOptions instanceof Steadlake;
|
|
26
|
+
const c = isClient ? clientOrOptions : getDefaultClient();
|
|
27
|
+
const opts = isClient ? undefined : clientOrOptions;
|
|
28
|
+
if (!isClient) {
|
|
29
|
+
configure({
|
|
30
|
+
apiKey: env.STEADLAKE_API_KEY,
|
|
31
|
+
projectId: env.STEADLAKE_PROJECT_ID,
|
|
32
|
+
apiUrl: env.STEADLAKE_API_URL,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
for (const task of opts?.tasks ?? []) {
|
|
36
|
+
c._registry.set(task.id, task);
|
|
37
|
+
}
|
|
38
|
+
await registerWithRetry(c, baseUrl);
|
|
39
|
+
return c._createHandler()(req);
|
|
40
|
+
};
|
|
41
|
+
}
|
package/dist/client.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ import type { Task, TaskConfig } from "./types.js";
|
|
|
3
3
|
export declare class Steadlake {
|
|
4
4
|
_registry: Map<string, Task<unknown, unknown>>;
|
|
5
5
|
_client: SteadlakeClient;
|
|
6
|
-
_signingSecret?: string;
|
|
7
6
|
_projectId: string;
|
|
8
7
|
_apiUrl?: string;
|
|
9
8
|
_apiKey: string;
|
|
@@ -12,15 +11,14 @@ export declare class Steadlake {
|
|
|
12
11
|
constructor(config?: {
|
|
13
12
|
apiKey?: string;
|
|
14
13
|
apiUrl?: string;
|
|
15
|
-
signingSecret?: string;
|
|
16
14
|
projectId?: string;
|
|
17
15
|
});
|
|
18
16
|
task<TPayload = unknown, TResult = unknown>(config: TaskConfig<TPayload, TResult>): Task<TPayload, TResult>;
|
|
19
17
|
sendEvent(runId: string, event: string, data?: unknown): Promise<void>;
|
|
20
18
|
resolveApproval(runId: string, stepId: string, approved: boolean, approvedBy?: string): Promise<void>;
|
|
21
19
|
_api(method: string, path: string, body?: unknown): Promise<unknown>;
|
|
22
|
-
_autoRegister(): Promise<void>;
|
|
23
|
-
init(): Promise<void>;
|
|
20
|
+
_autoRegister(baseUrl?: string): Promise<void>;
|
|
21
|
+
init(baseUrl?: string): Promise<void>;
|
|
24
22
|
_createHandler(): (req: Request) => Promise<Response>;
|
|
25
23
|
}
|
|
26
24
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,IAAI,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAG/D,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGnD,qBAAa,SAAS;IACrB,SAAS,sCAA6C;IACtD,OAAO,EAAE,eAAe,CAAC;IACzB,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,IAAI,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAG/D,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGnD,qBAAa,SAAS;IACrB,SAAS,sCAA6C;IACtD,OAAO,EAAE,eAAe,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAQ;IAC/C,qBAAqB,UAAS;gBAElB,MAAM,CAAC,EAAE;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;KACnB;IAsBD,IAAI,CAAC,QAAQ,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EACzC,MAAM,EAAE,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,GACnC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC;IAMpB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrG,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAcpE,aAAa,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4D9C,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3C,cAAc;CAGd"}
|
package/dist/client.js
CHANGED
|
@@ -4,7 +4,6 @@ import { createTask } from "./task.js";
|
|
|
4
4
|
export class Steadlake {
|
|
5
5
|
_registry = new Map();
|
|
6
6
|
_client;
|
|
7
|
-
_signingSecret;
|
|
8
7
|
_projectId;
|
|
9
8
|
_apiUrl;
|
|
10
9
|
_apiKey;
|
|
@@ -13,7 +12,6 @@ export class Steadlake {
|
|
|
13
12
|
constructor(config) {
|
|
14
13
|
const apiKey = config?.apiKey ?? process.env.STEADLAKE_API_KEY ?? "";
|
|
15
14
|
const apiUrl = config?.apiUrl ?? process.env.STEADLAKE_API_URL;
|
|
16
|
-
const signingSecret = config?.signingSecret ?? process.env.STEADLAKE_SIGNING_SECRET;
|
|
17
15
|
const projectId = config?.projectId ?? process.env.STEADLAKE_PROJECT_ID ?? "";
|
|
18
16
|
if (!apiKey) {
|
|
19
17
|
console.warn("[steadlake] No API key set — provide apiKey or set STEADLAKE_API_KEY");
|
|
@@ -21,12 +19,8 @@ export class Steadlake {
|
|
|
21
19
|
if (!projectId) {
|
|
22
20
|
console.warn("[steadlake] No project ID set — provide projectId or set STEADLAKE_PROJECT_ID");
|
|
23
21
|
}
|
|
24
|
-
if (!signingSecret) {
|
|
25
|
-
console.warn("[steadlake] No signing secret set — requests from DEW will not be verified. Set STEADLAKE_SIGNING_SECRET in production.");
|
|
26
|
-
}
|
|
27
22
|
this._apiKey = apiKey;
|
|
28
23
|
this._apiUrl = apiUrl;
|
|
29
|
-
this._signingSecret = signingSecret;
|
|
30
24
|
this._projectId = projectId;
|
|
31
25
|
this._client = new SteadlakeClient({
|
|
32
26
|
bearerAuth: apiKey,
|
|
@@ -58,7 +52,7 @@ export class Steadlake {
|
|
|
58
52
|
throw new Error(`API ${method} ${path} failed: ${res.status}`);
|
|
59
53
|
return res.json();
|
|
60
54
|
}
|
|
61
|
-
async _autoRegister() {
|
|
55
|
+
async _autoRegister(baseUrl) {
|
|
62
56
|
if (!this._projectId) {
|
|
63
57
|
console.warn("[steadlake] No projectId set — skipping auto-registration");
|
|
64
58
|
return;
|
|
@@ -103,16 +97,17 @@ export class Steadlake {
|
|
|
103
97
|
});
|
|
104
98
|
await this._client.triggers.register({
|
|
105
99
|
projectId: this._projectId,
|
|
100
|
+
...(baseUrl ? { apiUrl: baseUrl } : {}),
|
|
106
101
|
triggers,
|
|
107
102
|
});
|
|
108
103
|
this._registrationComplete = true;
|
|
109
104
|
})();
|
|
110
105
|
await this._registrationLock;
|
|
111
106
|
}
|
|
112
|
-
async init() {
|
|
113
|
-
await this._autoRegister();
|
|
107
|
+
async init(baseUrl) {
|
|
108
|
+
await this._autoRegister(baseUrl);
|
|
114
109
|
}
|
|
115
110
|
_createHandler() {
|
|
116
|
-
return createHandler(this._registry
|
|
111
|
+
return createHandler(this._registry);
|
|
117
112
|
}
|
|
118
113
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Steadlake } from "./client.js";
|
|
2
|
+
import type { TaskConfig, Task } from "./types.js";
|
|
3
|
+
export declare function getDefaultClient(): Steadlake;
|
|
4
|
+
export declare function configure(config: ConstructorParameters<typeof Steadlake>[0]): void;
|
|
5
|
+
export declare function task<TPayload = unknown, TResult = unknown>(config: TaskConfig<TPayload, TResult>): Task<TPayload, TResult>;
|
|
6
|
+
//# sourceMappingURL=default.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../src/default.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAInD,wBAAgB,gBAAgB,IAAI,SAAS,CAG5C;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,qBAAqB,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAElF;AAED,wBAAgB,IAAI,CAAC,QAAQ,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EACzD,MAAM,EAAE,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,GACnC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAEzB"}
|
package/dist/default.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Steadlake } from "./client.js";
|
|
2
|
+
let _default = null;
|
|
3
|
+
export function getDefaultClient() {
|
|
4
|
+
if (!_default)
|
|
5
|
+
_default = new Steadlake();
|
|
6
|
+
return _default;
|
|
7
|
+
}
|
|
8
|
+
export function configure(config) {
|
|
9
|
+
_default = new Steadlake(config);
|
|
10
|
+
}
|
|
11
|
+
export function task(config) {
|
|
12
|
+
return getDefaultClient().task(config);
|
|
13
|
+
}
|
package/dist/handler.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { Task } from "./types.js";
|
|
2
|
-
export declare function createHandler(registry: Map<string, Task<unknown, unknown
|
|
2
|
+
export declare function createHandler(registry: Map<string, Task<unknown, unknown>>): (req: Request) => Promise<Response>;
|
|
3
3
|
//# sourceMappingURL=handler.d.ts.map
|
package/dist/handler.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAoB,MAAM,YAAY,CAAC;AAEzD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,IAC7C,KAAK,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAkE7D"}
|
package/dist/handler.js
CHANGED
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
import { createHmac } from "node:crypto";
|
|
2
1
|
import { FatalError, RetryableError } from "./errors.js";
|
|
3
2
|
import { createStepHelpers } from "./step.js";
|
|
4
|
-
export function createHandler(registry
|
|
3
|
+
export function createHandler(registry) {
|
|
5
4
|
return async function handle(req) {
|
|
6
5
|
const body = await req.text();
|
|
7
|
-
if (signingSecret) {
|
|
8
|
-
const signature = req.headers.get("X-Steadlake-Signature");
|
|
9
|
-
const expected = `sha256=${sign(body, signingSecret)}`;
|
|
10
|
-
if (signature !== expected) {
|
|
11
|
-
return new Response("Unauthorized", { status: 401 });
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
6
|
let parsed;
|
|
15
7
|
try {
|
|
16
8
|
parsed = JSON.parse(body);
|
|
@@ -59,6 +51,3 @@ export function createHandler(registry, signingSecret) {
|
|
|
59
51
|
function isSuspension(err) {
|
|
60
52
|
return typeof err === "object" && err !== null && "__steadlake_suspend" in err;
|
|
61
53
|
}
|
|
62
|
-
function sign(body, secret) {
|
|
63
|
-
return createHmac("sha256", secret).update(body).digest("hex");
|
|
64
|
-
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { createHandler } from "./adapters/generic.js";
|
|
2
2
|
export { createNextHandler } from "./adapters/next.js";
|
|
3
3
|
export { Steadlake } from "./client.js";
|
|
4
|
+
export { task, configure, getDefaultClient } from "./default.js";
|
|
4
5
|
export { FatalError, RetryableError, SteadlakeError } from "./errors.js";
|
|
5
6
|
export type { Run, RunContext, RunResult, RunStatus, StepHelpers, SuspendReason, SuspensionSignal, Task, TaskConfig, TaskHooks, TriggerOptions, } from "./types.js";
|
|
6
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzE,YAAY,EACX,GAAG,EACH,UAAU,EACV,SAAS,EACT,SAAS,EACT,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,IAAI,EACJ,UAAU,EACV,SAAS,EACT,cAAc,GACd,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzE,YAAY,EACX,GAAG,EACH,UAAU,EACV,SAAS,EACT,SAAS,EACT,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,IAAI,EACJ,UAAU,EACV,SAAS,EACT,cAAc,GACd,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { createHandler } from "./adapters/generic.js";
|
|
2
2
|
export { createNextHandler } from "./adapters/next.js";
|
|
3
3
|
export { Steadlake } from "./client.js";
|
|
4
|
+
export { task, configure, getDefaultClient } from "./default.js";
|
|
4
5
|
export { FatalError, RetryableError, SteadlakeError } from "./errors.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steadlake/run",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Durable task execution SDK for Steadlake — define tasks with steps, retries, scheduling, and event-driven workflows.",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -13,13 +13,17 @@
|
|
|
13
13
|
"import": "./dist/index.js",
|
|
14
14
|
"types": "./dist/index.d.ts"
|
|
15
15
|
},
|
|
16
|
-
"./next": {
|
|
16
|
+
"./adapters/next": {
|
|
17
17
|
"import": "./dist/adapters/next.js",
|
|
18
18
|
"types": "./dist/adapters/next.d.ts"
|
|
19
19
|
},
|
|
20
|
-
"./
|
|
20
|
+
"./adapters/generic": {
|
|
21
21
|
"import": "./dist/adapters/generic.js",
|
|
22
22
|
"types": "./dist/adapters/generic.d.ts"
|
|
23
|
+
},
|
|
24
|
+
"./adapters/worker": {
|
|
25
|
+
"import": "./dist/adapters/worker.js",
|
|
26
|
+
"types": "./dist/adapters/worker.d.ts"
|
|
23
27
|
}
|
|
24
28
|
},
|
|
25
29
|
"publishConfig": {
|
|
@@ -29,13 +33,17 @@
|
|
|
29
33
|
"build": "tsc",
|
|
30
34
|
"dev": "tsc --watch",
|
|
31
35
|
"format": "oxfmt .",
|
|
32
|
-
"format:check": "oxfmt --check ."
|
|
36
|
+
"format:check": "oxfmt --check .",
|
|
37
|
+
"changeset": "changeset",
|
|
38
|
+
"version": "changeset version",
|
|
39
|
+
"release": "npm run build && changeset publish"
|
|
33
40
|
},
|
|
34
41
|
"dependencies": {
|
|
35
42
|
"@steadlake/core": "^0.3.2",
|
|
36
43
|
"oxfmt": "^0.44.0"
|
|
37
44
|
},
|
|
38
45
|
"devDependencies": {
|
|
46
|
+
"@changesets/cli": "^2.27.12",
|
|
39
47
|
"@types/node": "^25.9.3",
|
|
40
48
|
"typescript": "^6.0.3"
|
|
41
49
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/adapters/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,SAAI,iBAoBtE"}
|