Skip to content

Commit 1b95627

Browse files
cojiclaude
andauthored
chore: release v0.15.0 (#182)
* chore: release v0.15.0 - CHANGELOG: document all changes since v0.14.0 - Version bump: both packages to 0.15.0 - Docs: WAL checkpoint in databases.md and llms.md - Docs: useJobRun lifecycle callbacks (onStart/onComplete/onFail) - Docs: maxConcurrentRuns in CLAUDE.md, concepts.md - Docs: waitForRun and coalesce in CLAUDE.md - Regenerate llms.txt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: correct changelog wording (undefined, not null) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 811f2b1 commit 1b95627

10 files changed

Lines changed: 71 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
77

88
## [Unreleased]
99

10+
## [0.15.0] - 2026-03-29
11+
12+
### Breaking Changes
13+
14+
#### @coji/durably
15+
16+
- **`trigger()` return type changed to `TriggerResult`**: Returns `{ run, disposition }` where `disposition` is `'created' | 'idempotent' | 'coalesced'`. Add `coalesce: 'skip'` option to reuse an existing pending run instead of throwing. `concurrencyKey` now enforces max 1 pending run per key (#148)
17+
18+
#### @coji/durably-react
19+
20+
- **`useRunActions()` no longer returns `isLoading` / `error`**: Use React 19 `useTransition` for per-button pending UI and `try/catch` for error handling. Peer dependency raised to React 19+ (#179)
21+
22+
### Added
23+
24+
#### @coji/durably
25+
26+
- **`waitForRun(runId, options?)`**: Wait for an existing run to reach a terminal state, with `timeout`, `onProgress`, and `onLog` callbacks. Uses event-first resolution with storage polling fallback for cross-process observation (#151, #169)
27+
- **`maxConcurrentRuns` option**: Enable parallel run processing in the worker. Defaults to `1` (sequential) (#173)
28+
- **Event classification**: Events classified as Domain or Operational with `isDomainEvent()` type guard (#169)
29+
- **Automatic WAL checkpoint**: Periodic `PRAGMA wal_checkpoint(TRUNCATE)` during idle maintenance for local SQLite backends, preventing unbounded WAL file growth. Probed at `migrate()` time; skipped for Turso, PostgreSQL, and browser environments (#181)
30+
31+
#### @coji/durably-react
32+
33+
- **`isTerminal` / `isActive` on run objects and hooks**: Derived status booleans replace manual status enumeration (#179)
34+
- **`useRuns` status array filter**: `status` option accepts `RunStatus | RunStatus[]` to filter by multiple statuses (#154)
35+
- **`createJobHooks().useRun()` lifecycle callbacks**: `onStart`, `onComplete`, and `onFail` options for per-run lifecycle handling (#155)
36+
- **`createJobHooks()` options forwarding**: All hook options forwarded transparently via `Omit` (#179)
37+
38+
### Fixed
39+
40+
#### @coji/durably
41+
42+
- **`TypedRun` / `TypedClientRun` output type**: Default `output` type no longer includes `undefined`, removing unnecessary undefined checks (#145)
43+
1044
## [0.14.0] - 2026-03-16
1145

1246
### Added

CLAUDE.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ Regenerate `llms.txt` after editing any `llms.md`. Regenerate the OG image whene
3737

3838
- **Job**: Defined via `defineJob()` and registered via `jobs` option (or `.register()`), receives a step context and payload
3939
- **Step**: Created via `step.run()`, each step's success state and return value is persisted (cleaned up on terminal state by default, see `preserveSteps`)
40-
- **Run**: A job execution instance, created via `trigger()`, always persisted as `pending` before execution
41-
- **Worker**: Polls for pending runs and executes them sequentially
40+
- **Run**: A job execution instance, created via `trigger()` (returns `TriggerResult` with `disposition`: `'created' | 'idempotent' | 'coalesced'`), always persisted as `pending` before execution. Use `coalesce: 'skip'` to reuse an existing pending run with the same `concurrencyKey`
41+
- **Worker**: Polls for pending runs and executes them (sequentially by default, or concurrently via `maxConcurrentRuns`)
42+
- **waitForRun**: `durably.waitForRun(runId, options?)` waits for a run to reach terminal state, with `timeout`, `onProgress`, `onLog` callbacks. Uses events with storage polling fallback
4243

4344
## Key Design Decisions
4445

@@ -61,6 +62,7 @@ Five tables: `durably_runs`, `durably_run_labels`, `durably_steps`, `durably_log
6162
- `leaseRenewIntervalMs`: 5000ms
6263
- `leaseMs`: 30000ms (lease duration; expired leases are reclaimed)
6364
- `preserveSteps`: false (deletes step output data when runs reach terminal state)
65+
- `maxConcurrentRuns`: 1 (concurrent runs per worker; increase for I/O-bound jobs)
6466
- `retainRuns`: undefined (no automatic cleanup; set e.g. `'30d'` to auto-delete terminal runs)
6567

6668
## Browser Constraints (by design)

packages/durably-react/docs/llms.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ function Component({ runId }: { runId: string }) {
166166
}>({
167167
api: '/api/durably',
168168
runId,
169+
onStart: () => console.log('Run started'),
170+
onComplete: () => console.log('Run completed'),
171+
onFail: () => console.log('Run failed'),
169172
})
170173

171174
return <div>Status: {status}</div>

packages/durably-react/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@coji/durably-react",
3-
"version": "0.14.0",
3+
"version": "0.15.0",
44
"description": "React bindings for Durably - step-oriented resumable batch execution",
55
"type": "module",
66
"main": "./dist/index.js",

packages/durably/docs/llms.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,10 @@ import { withLogPersistence } from '@coji/durably'
562562
durably.use(withLogPersistence())
563563
```
564564

565+
## SQLite WAL Maintenance
566+
567+
For local SQLite backends using WAL mode, Durably automatically runs periodic WAL checkpoints (`PRAGMA wal_checkpoint(TRUNCATE)`) during idle maintenance to prevent unbounded WAL file growth. This is probed at `migrate()` time and only enabled when the backend supports it — automatically skipped for Turso (remote libSQL), PostgreSQL, and browser (OPFS) backends.
568+
565569
## Browser Usage
566570

567571
```ts

packages/durably/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@coji/durably",
3-
"version": "0.14.0",
3+
"version": "0.15.0",
44
"description": "Step-oriented resumable batch execution for Node.js and browsers using SQLite",
55
"type": "module",
66
"main": "./dist/index.js",

website/api/durably-react/fullstack.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,9 @@ function Component({ runId }: { runId: string }) {
249249
}>({
250250
api: '/api/durably',
251251
runId,
252+
onStart: () => console.log('Run started'),
253+
onComplete: () => console.log('Run completed'),
254+
onFail: () => console.log('Run failed'),
252255
})
253256

254257
return <div>Status: {status}</div>
@@ -257,10 +260,13 @@ function Component({ runId }: { runId: string }) {
257260

258261
### Options
259262

260-
| Option | Type | Description |
261-
| ------- | -------- | -------------------------- |
262-
| `api` | `string` | API base path |
263-
| `runId` | `string` | The run ID to subscribe to |
263+
| Option | Type | Description |
264+
| ------------ | ------------ | ------------------------------------------------- |
265+
| `api` | `string` | API base path |
266+
| `runId` | `string` | The run ID to subscribe to |
267+
| `onStart` | `() => void` | Called when the run transitions to pending/leased |
268+
| `onComplete` | `() => void` | Called when the run completes |
269+
| `onFail` | `() => void` | Called when the run fails |
264270

265271
---
266272

website/guide/concepts.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ createDurably({
165165
dialect,
166166
leaseRenewIntervalMs: 5000, // Renew lease every 5s (default)
167167
leaseMs: 30000, // Lease duration — stale after 30s without renewal (default)
168+
maxConcurrentRuns: 1, // Concurrent runs per worker (default: 1, increase for I/O-bound jobs)
168169
})
169170
```
170171

website/guide/databases.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ const dialect = new SqliteDialect({
9797
- Good for one-off scripts and CLI tools
9898
- No remote support (use libSQL if you might need Turso later)
9999

100+
## SQLite WAL Maintenance
101+
102+
For local SQLite backends using WAL (Write-Ahead Logging) mode, Durably automatically runs `PRAGMA wal_checkpoint(TRUNCATE)` during idle maintenance to prevent unbounded WAL file growth. This is throttled to at most once per 60 seconds and only runs when the worker is idle.
103+
104+
At `migrate()` time, Durably probes whether WAL checkpointing is supported. Checkpointing is enabled only for local file-backed SQLite — it is automatically skipped for Turso (remote libSQL), PostgreSQL, and browser (OPFS) backends.
105+
100106
## PostgreSQL
101107

102108
**For multi-worker production deployments.** The recommended backend for running multiple workers concurrently, with advisory locks and `FOR UPDATE SKIP LOCKED` for strong concurrency guarantees.

website/public/llms.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,10 @@ import { withLogPersistence } from '@coji/durably'
562562
durably.use(withLogPersistence())
563563
```
564564

565+
## SQLite WAL Maintenance
566+
567+
For local SQLite backends using WAL mode, Durably automatically runs periodic WAL checkpoints (`PRAGMA wal_checkpoint(TRUNCATE)`) during idle maintenance to prevent unbounded WAL file growth. This is probed at `migrate()` time and only enabled when the backend supports it — automatically skipped for Turso (remote libSQL), PostgreSQL, and browser (OPFS) backends.
568+
565569
## Browser Usage
566570

567571
```ts
@@ -932,6 +936,9 @@ function Component({ runId }: { runId: string }) {
932936
}>({
933937
api: '/api/durably',
934938
runId,
939+
onStart: () => console.log('Run started'),
940+
onComplete: () => console.log('Run completed'),
941+
onFail: () => console.log('Run failed'),
935942
})
936943

937944
return <div>Status: {status}</div>

0 commit comments

Comments
 (0)