@coji/durably 0.4.0 → 0.6.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 +15 -44
- package/dist/chunk-UCUP6NMJ.js +22 -0
- package/dist/chunk-UCUP6NMJ.js.map +1 -0
- package/dist/index-CHQw-b_O.d.ts +632 -0
- package/dist/index.d.ts +98 -499
- package/dist/index.js +591 -75
- package/dist/index.js.map +1 -1
- package/dist/plugins/index.d.ts +3 -8
- package/dist/plugins/index.js +3 -6
- package/dist/plugins/index.js.map +1 -1
- package/docs/llms.md +129 -17
- package/package.json +4 -4
package/docs/llms.md
CHANGED
|
@@ -10,10 +10,10 @@ Durably is a minimal workflow engine that persists step results to SQLite. If a
|
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
# Node.js with libsql (recommended)
|
|
13
|
-
|
|
13
|
+
pnpm add @coji/durably kysely zod @libsql/client @libsql/kysely-libsql
|
|
14
14
|
|
|
15
15
|
# Browser with SQLocal
|
|
16
|
-
|
|
16
|
+
pnpm add @coji/durably kysely zod sqlocal
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
## Core Concepts
|
|
@@ -61,18 +61,21 @@ const syncUsersJob = defineJob({
|
|
|
61
61
|
},
|
|
62
62
|
})
|
|
63
63
|
|
|
64
|
-
// Register
|
|
65
|
-
const syncUsers = durably.register(
|
|
64
|
+
// Register jobs with durably instance
|
|
65
|
+
const { syncUsers } = durably.register({
|
|
66
|
+
syncUsers: syncUsersJob,
|
|
67
|
+
})
|
|
66
68
|
```
|
|
67
69
|
|
|
68
70
|
### 3. Starting the Worker
|
|
69
71
|
|
|
70
72
|
```ts
|
|
71
|
-
//
|
|
72
|
-
await durably.
|
|
73
|
+
// Initialize: runs migrations and starts the worker
|
|
74
|
+
await durably.init()
|
|
73
75
|
|
|
74
|
-
//
|
|
75
|
-
durably.
|
|
76
|
+
// Or separately if needed:
|
|
77
|
+
// await durably.migrate() // Run migrations only
|
|
78
|
+
// durably.start() // Start worker only
|
|
76
79
|
```
|
|
77
80
|
|
|
78
81
|
### 4. Triggering Jobs
|
|
@@ -184,25 +187,134 @@ await durably.deleteRun(runId)
|
|
|
184
187
|
Subscribe to job execution events:
|
|
185
188
|
|
|
186
189
|
```ts
|
|
190
|
+
// Run lifecycle events
|
|
191
|
+
durably.on('run:trigger', (e) => console.log('Triggered:', e.runId))
|
|
187
192
|
durably.on('run:start', (e) => console.log('Started:', e.runId))
|
|
188
193
|
durably.on('run:complete', (e) => console.log('Done:', e.output))
|
|
189
194
|
durably.on('run:fail', (e) => console.error('Failed:', e.error))
|
|
195
|
+
durably.on('run:cancel', (e) => console.log('Cancelled:', e.runId))
|
|
196
|
+
durably.on('run:retry', (e) => console.log('Retried:', e.runId))
|
|
197
|
+
durably.on('run:progress', (e) =>
|
|
198
|
+
console.log('Progress:', e.progress.current, '/', e.progress.total),
|
|
199
|
+
)
|
|
190
200
|
|
|
201
|
+
// Step events
|
|
191
202
|
durably.on('step:start', (e) => console.log('Step:', e.stepName))
|
|
192
203
|
durably.on('step:complete', (e) => console.log('Step done:', e.stepName))
|
|
193
|
-
durably.on('step:
|
|
194
|
-
console.log('Step skipped (cached):', e.stepName),
|
|
195
|
-
)
|
|
204
|
+
durably.on('step:fail', (e) => console.error('Step failed:', e.stepName))
|
|
196
205
|
|
|
206
|
+
// Log events
|
|
197
207
|
durably.on('log:write', (e) => console.log(`[${e.level}]`, e.message))
|
|
198
208
|
```
|
|
199
209
|
|
|
210
|
+
## Advanced APIs
|
|
211
|
+
|
|
212
|
+
### getJob
|
|
213
|
+
|
|
214
|
+
Get a registered job by name:
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
const job = durably.getJob('sync-users')
|
|
218
|
+
if (job) {
|
|
219
|
+
const run = await job.trigger({ orgId: 'org_123' })
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### subscribe
|
|
224
|
+
|
|
225
|
+
Subscribe to events for a specific run as a ReadableStream:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
const stream = durably.subscribe(runId)
|
|
229
|
+
const reader = stream.getReader()
|
|
230
|
+
|
|
231
|
+
while (true) {
|
|
232
|
+
const { done, value } = await reader.read()
|
|
233
|
+
if (done) break
|
|
234
|
+
|
|
235
|
+
switch (value.type) {
|
|
236
|
+
case 'run:start':
|
|
237
|
+
console.log('Started')
|
|
238
|
+
break
|
|
239
|
+
case 'run:complete':
|
|
240
|
+
console.log('Completed:', value.output)
|
|
241
|
+
break
|
|
242
|
+
case 'run:fail':
|
|
243
|
+
console.error('Failed:', value.error)
|
|
244
|
+
break
|
|
245
|
+
case 'run:progress':
|
|
246
|
+
console.log('Progress:', value.progress)
|
|
247
|
+
break
|
|
248
|
+
case 'log:write':
|
|
249
|
+
console.log(`[${value.level}]`, value.message)
|
|
250
|
+
break
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### createDurablyHandler
|
|
256
|
+
|
|
257
|
+
Create HTTP handlers for client/server architecture using Web Standard Request/Response:
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
import { createDurablyHandler } from '@coji/durably'
|
|
261
|
+
|
|
262
|
+
const handler = createDurablyHandler(durably)
|
|
263
|
+
|
|
264
|
+
// Use the unified handle() method with automatic routing
|
|
265
|
+
app.all('/api/durably/*', async (req) => {
|
|
266
|
+
return await handler.handle(req, '/api/durably')
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
// Or use individual endpoints
|
|
270
|
+
app.post('/api/durably/trigger', (req) => handler.trigger(req))
|
|
271
|
+
app.get('/api/durably/subscribe', (req) => handler.subscribe(req))
|
|
272
|
+
app.get('/api/durably/runs', (req) => handler.runs(req))
|
|
273
|
+
app.get('/api/durably/run', (req) => handler.run(req))
|
|
274
|
+
app.get('/api/durably/steps', (req) => handler.steps(req))
|
|
275
|
+
app.get('/api/durably/runs/subscribe', (req) => handler.runsSubscribe(req))
|
|
276
|
+
app.post('/api/durably/retry', (req) => handler.retry(req))
|
|
277
|
+
app.post('/api/durably/cancel', (req) => handler.cancel(req))
|
|
278
|
+
app.delete('/api/durably/run', (req) => handler.delete(req))
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Handler Interface:**
|
|
282
|
+
|
|
283
|
+
```ts
|
|
284
|
+
interface DurablyHandler {
|
|
285
|
+
// Unified routing handler
|
|
286
|
+
handle(request: Request, basePath: string): Promise<Response>
|
|
287
|
+
|
|
288
|
+
// Individual endpoints
|
|
289
|
+
trigger(request: Request): Promise<Response> // POST /trigger
|
|
290
|
+
subscribe(request: Request): Response // GET /subscribe?runId=xxx (SSE)
|
|
291
|
+
runs(request: Request): Promise<Response> // GET /runs
|
|
292
|
+
run(request: Request): Promise<Response> // GET /run?runId=xxx
|
|
293
|
+
steps(request: Request): Promise<Response> // GET /steps?runId=xxx
|
|
294
|
+
runsSubscribe(request: Request): Response // GET /runs/subscribe (SSE)
|
|
295
|
+
retry(request: Request): Promise<Response> // POST /retry?runId=xxx
|
|
296
|
+
cancel(request: Request): Promise<Response> // POST /cancel?runId=xxx
|
|
297
|
+
delete(request: Request): Promise<Response> // DELETE /run?runId=xxx
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
interface TriggerRequest {
|
|
301
|
+
jobName: string
|
|
302
|
+
input: Record<string, unknown>
|
|
303
|
+
idempotencyKey?: string
|
|
304
|
+
concurrencyKey?: string
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
interface TriggerResponse {
|
|
308
|
+
runId: string
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
200
312
|
## Plugins
|
|
201
313
|
|
|
202
314
|
### Log Persistence
|
|
203
315
|
|
|
204
316
|
```ts
|
|
205
|
-
import { withLogPersistence } from '@coji/durably
|
|
317
|
+
import { withLogPersistence } from '@coji/durably'
|
|
206
318
|
|
|
207
319
|
durably.use(withLogPersistence())
|
|
208
320
|
```
|
|
@@ -224,18 +336,18 @@ const durably = createDurably({
|
|
|
224
336
|
})
|
|
225
337
|
|
|
226
338
|
// Same API as Node.js
|
|
227
|
-
const myJob = durably.register(
|
|
228
|
-
defineJob({
|
|
339
|
+
const { myJob } = durably.register({
|
|
340
|
+
myJob: defineJob({
|
|
229
341
|
name: 'my-job',
|
|
230
342
|
input: z.object({}),
|
|
231
343
|
run: async (step) => {
|
|
232
344
|
/* ... */
|
|
233
345
|
},
|
|
234
346
|
}),
|
|
235
|
-
)
|
|
347
|
+
})
|
|
236
348
|
|
|
237
|
-
|
|
238
|
-
durably.
|
|
349
|
+
// Initialize (same as Node.js)
|
|
350
|
+
await durably.init()
|
|
239
351
|
```
|
|
240
352
|
|
|
241
353
|
## Run Lifecycle
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coji/durably",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Step-oriented resumable batch execution for Node.js and browsers using SQLite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
},
|
|
42
42
|
"homepage": "https://github.com/coji/durably#readme",
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"kysely": "
|
|
45
|
-
"zod": "
|
|
44
|
+
"kysely": "^0.27.0",
|
|
45
|
+
"zod": "^4.0.0"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"ulidx": "^2.4.1"
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"tsup": "^8.5.1",
|
|
69
69
|
"typescript": "^5.9.3",
|
|
70
70
|
"vitest": "^4.0.16",
|
|
71
|
-
"zod": "^4.
|
|
71
|
+
"zod": "^4.3.4"
|
|
72
72
|
},
|
|
73
73
|
"scripts": {
|
|
74
74
|
"build": "tsup",
|