Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

## [0.14.0] - 2026-03-16

### Added

#### @coji/durably

- **Export error classes**: `DurablyError`, `NotFoundError`, `ValidationError`, `ConflictError` now exported for programmatic error handling (#129)

### Fixed

#### @coji/durably

- **Prevent completed step from being written to cancelled run** (#128)
- **Return proper HTTP status codes and stop coercing input** (#129)
- **Separate maintenance from processOne to fix idle contract**: `processOne()` is now pure claim-execute-return. Lease normalization and auto-purge run only during idle cycles via worker `onIdle` callback (#134)
- **Catch rejected promises from async event listeners**: Async listener rejections are now forwarded to `onError` instead of being silently dropped. `withLogPersistence()` fixed to use explicit fire-and-forget (#137)

### Performance

#### @coji/durably

- **Skip write mutex for PostgreSQL backend**: PostgreSQL doesn't need the serialization mutex used for SQLite (#130)
- **Denormalize step_count and remove labels JSON fallback**: Avoids subqueries and JSON parsing in hot paths (#132)

### Internal

- Extract `claim-postgres.ts`, `claim-sqlite.ts`, `transformers.ts` from `storage.ts` for better separation of concerns (#133)
- Extract `toError()` helper in `errors.ts` to deduplicate error coercion pattern (#137)

### Documentation

- Document synchronous event listener behavior with best practices (#135)
- Add `run:lease-renewed` event to Events API reference
- Add error classes to Quick Reference and LLM documentation
- PostgreSQL example, database comparison guide, expanded LLM docs (#114-#120)

## [0.13.0] - 2026-03-16

### Breaking Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/durably-react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@coji/durably-react",
"version": "0.13.0",
"version": "0.14.0",
"description": "React bindings for Durably - step-oriented resumable batch execution",
"type": "module",
"main": "./dist/index.js",
Expand Down
17 changes: 17 additions & 0 deletions packages/durably/docs/llms.md
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,23 @@ interface RunFilter<
}
```

## Error Classes

Durably exports typed error classes for programmatic error handling:

```ts
import {
DurablyError, // Base class with statusCode (extends Error)
NotFoundError, // 404 — resource not found
ValidationError, // 400 — invalid input or request
ConflictError, // 409 — operation conflicts with current state
CancelledError, // Run was cancelled during execution
LeaseLostError, // Worker lost lease ownership
} from '@coji/durably'
```

`DurablyError` subclasses (`NotFoundError`, `ValidationError`, `ConflictError`) carry a `statusCode` property and are used by the HTTP handler to return appropriate responses.

## License

MIT
2 changes: 1 addition & 1 deletion packages/durably/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@coji/durably",
"version": "0.13.0",
"version": "0.14.0",
"description": "Step-oriented resumable batch execution for Node.js and browsers using SQLite",
"type": "module",
"main": "./dist/index.js",
Expand Down
22 changes: 21 additions & 1 deletion website/api/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,25 @@ durably.on('run:leased', (event) => {
})
```

#### `run:lease-renewed`

Fired when a run's lease is renewed during execution.

```ts
durably.on('run:lease-renewed', (event) => {
// event: {
// type: 'run:lease-renewed',
// runId: string,
// jobName: string,
// leaseOwner: string,
// leaseExpiresAt: string,
// labels: Record<string, string>,
// timestamp: string,
// sequence: number
// }
})
```

#### `run:complete`

Fired when a run completes successfully.
Expand Down Expand Up @@ -295,7 +314,7 @@ durably.on('run:complete', (e) => {

## Error Handling

Exceptions thrown in event listeners are caught and forwarded to the error handler — they do not crash the worker or abort the current run. However, because listeners run synchronously, an exception still interrupts subsequent listeners for the same event. Use `onError` to catch these:
Exceptions thrown in event listeners are caught and forwarded to the error handler — they do not crash the worker, abort the current run, or interrupt subsequent listeners for the same event. If a listener returns a rejected Promise (async listener), the rejection is also forwarded to `onError`. Use `onError` to catch both:

```ts
durably.onError((error, event) => {
Expand All @@ -317,6 +336,7 @@ interface BaseEvent {
type DurablyEvent =
| RunTriggerEvent
| RunLeasedEvent
| RunLeaseRenewedEvent
| RunCompleteEvent
| RunFailEvent
| RunCancelEvent
Expand Down
11 changes: 10 additions & 1 deletion website/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,16 @@ import type {
DurablyEvent,
EventType,
} from '@coji/durably'
import { createDurablyHandler, toClientRun } from '@coji/durably'
import {
createDurablyHandler,
toClientRun,
DurablyError,
NotFoundError,
ValidationError,
ConflictError,
CancelledError,
LeaseLostError,
} from '@coji/durably'
```

### `Run` Type
Expand Down
17 changes: 17 additions & 0 deletions website/public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,23 @@ interface RunFilter<
}
```

## Error Classes

Durably exports typed error classes for programmatic error handling:

```ts
import {
DurablyError, // Base class with statusCode (extends Error)
NotFoundError, // 404 — resource not found
ValidationError, // 400 — invalid input or request
ConflictError, // 409 — operation conflicts with current state
CancelledError, // Run was cancelled during execution
LeaseLostError, // Worker lost lease ownership
} from '@coji/durably'
```

`DurablyError` subclasses (`NotFoundError`, `ValidationError`, `ConflictError`) carry a `statusCode` property and are used by the HTTP handler to return appropriate responses.

## License

MIT
Expand Down