Skip to content
Open
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
12 changes: 4 additions & 8 deletions .github/actions/setup-js/action.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Set up JavaScript workspace
description: >-
Install pnpm and an official Node.js 26 build, prove that it has native
Temporal, and install the locked dependencies of the workspace in `js/`.
Install pnpm and an official Node.js build, then install the locked
dependencies of the workspace in `js/`.

inputs:
node-version:
Expand All @@ -23,13 +23,9 @@ runs:
node-version: ${{ inputs.node-version }}
node-version-file: ${{ inputs.node-version == '' && 'js/.node-version' || '' }}

# Official Node.js 26 binaries enable Temporal; some builds from source do
# not, and River refuses to run without it.
- name: Require native Temporal
- name: Report Node.js version
shell: bash
run: |
node --version
test "$(node -p 'typeof Temporal')" = object
run: node --version

- run: pnpm install --frozen-lockfile
shell: bash
Expand Down
26 changes: 19 additions & 7 deletions .github/workflows/js.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@ concurrency:
permissions:
contents: read

# The full runtime deliberately requires native Temporal from Node 26. Every
# job installs the official build selected by js/.node-version (or the matrix)
# and asserts `typeof Temporal` before doing anything else.
# Node 24 exercises the Temporal polyfill; Node 26 exercises native Temporal.
# Jobs use js/.node-version unless a matrix selects a different release.

defaults:
run:
Expand Down Expand Up @@ -90,10 +89,15 @@ jobs:
- run: pnpm run license:check

packages:
name: Package archives
name: Package archives (Node ${{ matrix.node-version || 'from .node-version' }})
runs-on: ubuntu-latest
timeout-minutes: 20

strategy:
fail-fast: false
matrix:
node-version: ["24", ""]

# The packed `node:test` suite runs its Postgres tests against this
# database, and RIVER_REQUIRE_POSTGRES makes them fail instead of skip
# when DATABASE_URL is missing.
Expand Down Expand Up @@ -121,6 +125,8 @@ jobs:
persist-credentials: false

- uses: ./.github/actions/setup-js
with:
node-version: ${{ matrix.node-version }}

- run: pnpm run build:all

Expand Down Expand Up @@ -172,8 +178,8 @@ jobs:
strategy:
fail-fast: false
matrix:
# The minimum supported release and the current Node 26 release.
node-version: ["26.0.0", ""]
# Minimum and current releases for both supported Node lines.
node-version: ["24.0.0", "24", "26.0.0", ""]
Comment on lines +181 to +182

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we pin to specific patch releases like this or do you think we should just stick with "24" and "26" to pull in the latest from each as we do in Go?


steps:
- uses: actions/checkout@v7
Expand All @@ -200,14 +206,18 @@ jobs:
- run: pnpm run test

test_integration:
name: Test (integration, Postgres ${{ matrix.postgres-version }})
name: Test (integration, Node ${{ matrix.node-version || 'from .node-version' }}, Postgres ${{ matrix.postgres-version }})
runs-on: ubuntu-latest
timeout-minutes: 20

strategy:
fail-fast: false
matrix:
postgres-version: [14, 15, 16, 17, 18]
node-version: [""]
include:
- node-version: "24"
postgres-version: 18

services:
postgres:
Expand All @@ -232,6 +242,8 @@ jobs:
persist-credentials: false

- uses: ./.github/actions/setup-js
with:
node-version: ${{ matrix.node-version }}

- run: pnpm run build:all

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ lint/java/tools: ## Lint Java's Go maintenance tools
golangci-lint run --fix ./java/bin/sync-migrations/*.go

# JavaScript targets, like the Rust ones, are separate from `lint` and `test`
# and need Node.js 26 and pnpm; they delegate to the workspace's own scripts.
# and need Node.js 24 or newer and pnpm; they delegate to the workspace's own scripts.
# Run `pnpm -C js install` first.
.PHONY: build/js
build/js: ## Build every JavaScript package
Expand Down
8 changes: 8 additions & 0 deletions js/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Support Node.js 24 across all packages. River automatically installs a `Temporal` polyfill when the runtime has no native implementation, preserving native `Temporal` on Node.js 26.

### Fixed

- Worker threads respect cancellation received while loading a handler module, before invoking its handler.

## [0.2.0] - 2026-10-07

### Added
Expand Down
18 changes: 7 additions & 11 deletions js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,17 @@ the same jobs in the same database.

## Requirements

- **Node.js 26 with native `Temporal`.** River uses `Temporal.Instant` for
- **Node.js 24 or newer.** River uses `Temporal.Instant` for
timestamps and `bigint` for IDs, so no value read from the database is lost.
Official Node.js 26 builds (nodejs.org, `actions/setup-node`, the official
Docker images) enable Temporal. Some distribution and Homebrew builds do
not; check with:

```sh
node -p "typeof Temporal" # must print "object"
```

Importing River automatically installs
[`@js-temporal/polyfill`](https://github.com/js-temporal/temporal-polyfill)
globally when `Temporal` is absent, including in worker threads. Node.js 26
uses its native implementation. No extra imports or runtime flags are needed.
- **TypeScript 6.0 or newer**, if you use TypeScript. Install `@types/node`
26 or newer (and `@types/pg` with `@riverqueue/driver-pg`) and list `"node"`
24 or newer (and `@types/pg` with `@riverqueue/driver-pg`) and list `"node"`
in `compilerOptions.types`. JavaScript users need neither.
- **ESM.** The packages ship one ES module build. CommonJS code can
`require()` them on Node 26, which returns the same module instance as
`require()` them on Node 24 or newer, which returns the same module instance as
`import`; TypeScript CommonJS projects need `module: "node20"` or
`"nodenext"`.

Expand Down
8 changes: 4 additions & 4 deletions js/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The `riverqueue` command runs River's database migrations and a throughput
benchmark, and upgrades code written for `riverqueue@0.1`. It requires
Node.js 26 or newer and mirrors the commands of River's Go CLI.
Node.js 24 or newer and mirrors the commands of River's Go CLI.

```sh
npm install --save-dev @riverqueue/cli
Expand Down Expand Up @@ -142,9 +142,9 @@ process.exitCode = await run(process.argv.slice(2), {

## Requirements

Node.js 26 or newer with native `Temporal`: `node -p "typeof Temporal"` must
print `object`. Official Node.js binaries include it; some builds compiled from
source, including some distribution and Homebrew packages, do not.
Comment on lines -145 to -147

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I think this pulled in Node 26 originally as part of my push to use the temporal stuff for non-terrible datetime handling.

Node.js 24 or newer. River automatically installs a global `Temporal`
polyfill when the runtime has no native implementation; Node.js 26 uses
native `Temporal`.

The CLI installs its own matching `riverqueue` and database packages.

Expand Down
4 changes: 2 additions & 2 deletions js/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"./dist/bin.js"
],
"engines": {
"node": ">=26"
"node": ">=24"
},
"bin": {
"riverqueue": "./dist/bin.js"
Expand Down Expand Up @@ -55,7 +55,7 @@
"riverqueue": "workspace:0.2.0"
},
"peerDependencies": {
"@types/node": ">=26",
"@types/node": ">=24",
"@types/pg": ">=8",
"typescript": "^5.0.0 || ^6.0.0"
},
Expand Down
27 changes: 13 additions & 14 deletions js/docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@ Go and Rust implementations. Run the `pnpm` commands below from `js/`, or use
River's top-level `make` targets (`make lint/js`, `make test/js`, and so on),
which delegate to `pnpm -C js`.

Use an official Node.js 26 build, which includes native Temporal. The
workspace's `.node-version` selects Node 26 for version managers such as fnm,
nvm (`nvm use $(cat .node-version)`), and `actions/setup-node`. Some Node.js
builds compiled from source, including some distribution and Homebrew
packages, lack Temporal, and most unit tests then fail with
`ReferenceError: Temporal is not defined`. Check before installing, then use
the pnpm version pinned by `packageManager` in `package.json`:

node -p "typeof Temporal" # must print "object"
Use Node.js 24 or newer. The workspace's `.node-version` selects Node 26 for
version managers such as fnm, nvm (`nvm use $(cat .node-version)`), and
`actions/setup-node`. River installs a global Temporal polyfill when the
runtime has no native implementation; the tests use the same initialization.
Use the pnpm version pinned by `packageManager` in `package.json`:

pnpm install

TypeScript 6.0 is the minimum compiler for the published declarations, which
Expand Down Expand Up @@ -190,13 +187,15 @@ to benchmark against a disposable database:

River's `.github/workflows/js.yaml` runs on pull requests and pushes to
`master` that change `js/`, River's migrations or SQL, or the workflow itself,
and on `js/v*` release tags. Each job installs the official Node.js build from
`js/.node-version` and fails unless `typeof Temporal` is `object`. The jobs
and on `js/v*` release tags. Each job installs an official Node.js build from
`js/.node-version` or its version matrix. The jobs
cover build, both type-check lanes, generated migrations (compared with
River's sources), API reports, TypeDoc, README snippets, the 0.1 fixture, lint,
formatting, licenses, packed archives and examples, unit tests on Node 26.0.0
and the current Node 26 release, and integration tests on Postgres 14
through 18.
formatting, licenses, packed archives and examples. Unit tests run on Node
24.0.0, 26.0.0, and the current releases of both lines. Packed packages run on
Node 24 and 26, including CommonJS consumers and TypeScript consumers with
`@types/node` 24. Integration tests cover Postgres 14 through 18 on Node 26
and Postgres 18 on Node 24.
Comment on lines +194 to +198

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excessive detail in here IMO and likely to drift, no need to mention the exact releases we run unit tests against.


Unit tests compare unique keys, protocol values, notification dispatch,
retry timing, cron schedules, and snooze counting with fixtures that
Expand Down
6 changes: 3 additions & 3 deletions js/docs/migrating-from-0.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ that still needs attention.

## Runtime requirements

The package needs Node.js 26 with native `Temporal` and ships as ESM; see
[requirements](../README.md#requirements). There is no second CommonJS build
and no `Temporal` polyfill.
The package needs Node.js 24 or newer and ships as ESM; see
[requirements](../README.md#requirements). River automatically installs a
global `Temporal` polyfill when the runtime has no native implementation.

## Codemod

Expand Down
8 changes: 4 additions & 4 deletions js/driver/pg/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# `@riverqueue/driver-pg`

This package is River's complete Postgres backend for Node.js 26 and newer.
This package is River's complete Postgres backend for Node.js 24 and newer.
It accepts a caller-owned `pg.Pool`, performs insertion and runtime operations,
and implements notifications, leadership, maintenance, and queue control.

Expand Down Expand Up @@ -94,9 +94,9 @@ driver, migrator, and CLI. Never interpolate an untrusted schema name.

## Requirements

Node.js 26 or newer with native `Temporal`: `node -p "typeof Temporal"` must
print `object`. Official Node.js binaries include it; some builds compiled from
source, including some distribution and Homebrew packages, do not.
Node.js 24 or newer. River automatically installs a global `Temporal`
polyfill when the runtime has no native implementation; Node.js 26 uses
native `Temporal`.

Install `riverqueue` at exactly this package's version. It is a peer dependency,
so npm rejects a mismatched pair instead of loading two copies.
Expand Down
2 changes: 1 addition & 1 deletion js/driver/pg/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
}
},
"engines": {
"node": ">=26"
"node": ">=24"
},
"files": [
"dist",
Expand Down
6 changes: 3 additions & 3 deletions js/driver/prisma/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ JavaScript's safe range in arguments or metadata stay exact.

## Requirements

Node.js 26 or newer with native `Temporal`: `node -p "typeof Temporal"` must
print `object`. Official Node.js binaries include it; some builds compiled from
source, including some distribution and Homebrew packages, do not.
Node.js 24 or newer. River automatically installs a global `Temporal`
polyfill when the runtime has no native implementation; Node.js 26 uses
native `Temporal`.

Install `riverqueue` at exactly this package's version. It is a peer dependency,
so npm rejects a mismatched pair instead of loading two copies.
Expand Down
2 changes: 1 addition & 1 deletion js/driver/prisma/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
}
},
"engines": {
"node": ">=26"
"node": ">=24"
},
"files": [
"dist",
Expand Down
8 changes: 4 additions & 4 deletions js/driver/sqlite/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# `@riverqueue/driver-sqlite`

This package is River's preview SQLite backend for Node 26 and newer. It uses
This package is River's preview SQLite backend for Node 24 and newer. It uses
the built-in synchronous `node:sqlite` API.

Apply River's generated canonical migrations explicitly before constructing a
Expand Down Expand Up @@ -241,9 +241,9 @@ listeners once per job.

## Requirements

Node.js 26 or newer with native `Temporal`: `node -p "typeof Temporal"` must
print `object`. Official Node.js binaries include it; some builds compiled from
source, including some distribution and Homebrew packages, do not.
Node.js 24 or newer. River automatically installs a global `Temporal`
polyfill when the runtime has no native implementation; Node.js 26 uses
native `Temporal`.

Install `riverqueue` at exactly this package's version. It is a peer dependency,
so npm rejects a mismatched pair instead of loading two copies.
Expand Down
4 changes: 2 additions & 2 deletions js/driver/sqlite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"sideEffects": false,
"engines": {
"node": ">=26"
"node": ">=24"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand Down Expand Up @@ -45,7 +45,7 @@
"provenance": true
},
"peerDependencies": {
"@types/node": ">=26",
"@types/node": ">=24",
"riverqueue": "workspace:0.2.0"
},
"peerDependenciesMeta": {
Expand Down
6 changes: 3 additions & 3 deletions js/etc/riverqueue.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ declarations, and file layout are omitted.
/**
* Verify the runtime features River relies on before database work begins.
*
* Official Node.js 26 builds expose Temporal by default. This explicit check
* gives custom builds and unsupported runtimes a useful failure instead of a
* later `ReferenceError` or a lossy timestamp fallback.
* River provides a Temporal polyfill when the runtime has no native
* implementation. This check gives unsupported runtimes a useful failure
* instead of a later `ReferenceError` or a lossy timestamp fallback.
*/
export declare function assertRuntimeSupport(): void;
```
Expand Down
2 changes: 1 addition & 1 deletion js/examples/graceful-shutdown/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"type": "module",
"engines": {
"node": ">=26"
"node": ">=24"
},
"scripts": {
"build": "tsc",
Expand Down
2 changes: 1 addition & 1 deletion js/examples/hooks-metrics/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"type": "module",
"engines": {
"node": ">=26"
"node": ">=24"
},
"scripts": {
"build": "tsc",
Expand Down
2 changes: 1 addition & 1 deletion js/examples/mixed-language/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"type": "module",
"engines": {
"node": ">=26"
"node": ">=24"
},
"scripts": {
"build": "tsc",
Expand Down
2 changes: 1 addition & 1 deletion js/examples/node-postgres/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"type": "module",
"engines": {
"node": ">=26"
"node": ">=24"
},
"scripts": {
"build": "tsc",
Expand Down
Loading
Loading