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
5 changes: 4 additions & 1 deletion .github/workflows/js.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,10 @@ jobs:
# The packed examples that need a database use its default schema.
- run: node cli/dist/bin.js migrate-up --database-url "$DATABASE_URL"

- run: pnpm run package:check
# The packed CLI suite launches the installed executable with NODE_OPTIONS
# cleared, exercising its own Temporal setup on Node 24.
- name: Verify package archives and standalone CLI
run: pnpm run package:check

examples:
name: Examples
Expand Down
2 changes: 1 addition & 1 deletion js/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Add `@riverqueue/driver-knex` for inserting jobs on Postgres inside Knex transactions, including batches, unique jobs, and nested savepoints. Configure its `knexTransactionAdapter` on `PgDriver` to use native Knex transactions with `completeTx` in workers. [PR #1516](https://github.com/riverqueue/river/pull/1516).
- Support native Prisma interactive transactions with `completeTx` in workers and `{ tx }` on Postgres client operations. Configure `prismaTransactionAdapter` from `@riverqueue/driver-prisma` on `PgDriver` to commit application writes and job completion together. [PR #1517](https://github.com/riverqueue/river/pull/1517).
- 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.
- Support Node.js 24 across all packages. On runtimes without native `Temporal`, preload an application-provided polyfill before importing River, including in worker threads. River validates runtime support on library import and never installs or replaces a global polyfill. The standalone `riverqueue` CLI supplies its own polyfill when needed. Node.js 26 uses native `Temporal`.

### Changed

Expand Down
55 changes: 51 additions & 4 deletions js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ the same jobs in the same database.

- **Node.js 24 or newer.** River uses `Temporal.Instant` for
timestamps and `bigint` for IDs, so no value read from the database is lost.
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.
Node.js 26 provides native `Temporal`. On runtimes without it, including
Node.js 24, preload a polyfill as described below. River checks for Temporal
on import and never installs or replaces it.
- **TypeScript 6.0 or newer**, if you use TypeScript. Install `@types/node`
24 or newer (and `@types/pg` with `@riverqueue/driver-pg`) and list `"node"`
in `compilerOptions.types`. JavaScript users need neither.
Expand All @@ -24,6 +23,54 @@ the same jobs in the same database.
Keep every `@riverqueue/*` package on the same version as `riverqueue`; the
packages declare that as an exact peer dependency.

### Temporal on Node.js 24

Install a Temporal polyfill of your choice. For example:

```sh
npm install @js-temporal/polyfill
```

Create `temporal-setup.mjs` in your application:

```js
if (globalThis.Temporal === undefined) {
const { Temporal } = await import("@js-temporal/polyfill");
Object.defineProperty(globalThis, "Temporal", {
configurable: true,
value: Temporal,
writable: true,
});
}
```

Preload it before your application starts:

```sh
node --import ./temporal-setup.mjs app.mjs
```

Node also preloads this module in worker threads, including those started by
`@riverqueue/worker-threads`. Setting a global only in your main application
module does not initialize worker threads. A plain import of
`@js-temporal/polyfill` does not install a global either.

For package scripts, use an absolute path in `NODE_OPTIONS`:

```sh
NODE_OPTIONS="--import=/absolute/path/temporal-setup.mjs" npm start
```

The standalone `riverqueue` CLI supplies its own polyfill and needs no preload.
Importing `@riverqueue/cli` programmatically uses the application's Temporal,
just like the other library packages.

Use the same global Temporal constructors throughout your application when
passing values to River. Instances from another polyfill or a separate copy
may fail River's `instanceof` checks. Choose the implementation before importing
River and keep it in place for the lifetime of the process. Node.js 26 needs
no polyfill or preload.

## Quickstart with Postgres

Install the core package, the Postgres driver, migrations, and a validator
Expand Down
11 changes: 8 additions & 3 deletions js/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,14 @@ process.exitCode = await run(process.argv.slice(2), {

## Requirements

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`.
Node.js 24 or newer. The `riverqueue` executable supplies its own Temporal
polyfill when the global is absent and preserves native or application-provided
Temporal. No preload is needed to run CLI commands.

When importing `run` from `@riverqueue/cli` programmatically, the application
must supply a global Temporal implementation, as with other River libraries.
On Node.js 24, [preload a polyfill](../README.md#temporal-on-nodejs-24) before
importing it. Programmatic imports never install a global.

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

Expand Down
1 change: 1 addition & 0 deletions js/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"provenance": true
},
"dependencies": {
"@js-temporal/polyfill": "^0.5.1",
"@riverqueue/driver-pg": "workspace:0.2.0",
"@riverqueue/migrate": "workspace:0.2.0",
"pg": "^8.23.1",
Expand Down
22 changes: 20 additions & 2 deletions js/cli/src/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@

import process from "node:process";

import { run } from "./run.js";
try {
// The executable owns its runtime setup. Importing @riverqueue/cli as a
// library continues to use the application's Temporal implementation.
const temporal: unknown = Reflect.get(globalThis, "Temporal");
if (temporal === undefined) {
const { Temporal } = await import("@js-temporal/polyfill");
Object.defineProperty(globalThis, "Temporal", {
configurable: true,
value: Temporal,
writable: true,
});
}

process.exitCode = await run(process.argv.slice(2));
const { run } = await import("./run.js");
process.exitCode = await run(process.argv.slice(2));
} catch (error: unknown) {
const message =
error instanceof Error ? error.message : "could not initialize CLI";
process.stderr.write(`riverqueue: ${message}\n`);
process.exitCode = 1;
}
17 changes: 14 additions & 3 deletions js/docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,17 @@ which delegate to `pnpm -C js`.

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.
`actions/setup-node`. River requires a global Temporal implementation. Test
configurations explicitly preload `scripts/temporal-setup.mjs`, which installs the
development polyfill when necessary in both the main process and worker
threads. The polyfill is not a runtime dependency of the published library
packages; the standalone CLI declares and initializes its own.
For example commands on Node 24, preload it yourself:

```sh
export NODE_OPTIONS="--import=$(pwd)/scripts/temporal-setup.mjs"
```

Use the pnpm version pinned by `packageManager` in `package.json`:

pnpm install
Expand Down Expand Up @@ -65,7 +74,9 @@ consumer with Node's built-in `node --test`, so plain JavaScript exercises the
installed tarballs with no transpiler or workspace alias in between: SQLite and
worker-thread jobs end to end with exact int64 args, one shared `riverqueue`
instance and error class hierarchy across packages, `require(esm)`, subpath
exports, the CLI, and the test helpers. Its Postgres tests run in a throwaway
exports, the CLI, and the test helpers. CLI subprocesses clear `NODE_OPTIONS`
to verify help, version, and SQLite migrations without an application preload.
The same suite runs on Node 24 and Node 26 in CI. Its Postgres tests run in a throwaway
schema when `DATABASE_URL` is set. Database-free examples also run in this
gate. `package:examples` runs the same packed examples with Postgres when
`DATABASE_URL` is set. Neither command publishes anything.
Expand Down
5 changes: 3 additions & 2 deletions js/docs/migrating-from-0.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ that still needs attention.
## Runtime requirements

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.
[requirements](../README.md#requirements). On runtimes without native `Temporal`,
[preload an application-provided polyfill](../README.md#temporal-on-nodejs-24)
before importing River.

## Codemod

Expand Down
3 changes: 2 additions & 1 deletion js/driver/knex/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ response when `queryContext?.river` is true. River needs the Postgres response's

## Requirements

Knex 3.1 or newer within the 3.x line, `pg` 8.x, and Node.js 24 or newer. This
Knex 3.1 or newer within the 3.x line, `pg` 8.x, and Node.js 24 or newer with
a global `Temporal` implementation ([Node.js 24 setup](../../README.md#temporal-on-nodejs-24)). This
adapter supports Knex's Postgres client (`client: "pg"` or `"postgresql"`).
For SQLite, use `@riverqueue/driver-sqlite`.

Expand Down
6 changes: 3 additions & 3 deletions js/driver/pg/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ driver, migrator, and CLI. Never interpolate an untrusted schema name.

## Requirements

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`.
Node.js 24 or newer with a global `Temporal` implementation. Node.js 26
provides it natively; on Node.js 24, [preload a polyfill](../../README.md#temporal-on-nodejs-24)
before importing River, including in worker threads. River never installs one.

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: 3 additions & 1 deletion js/driver/pg/src/driver.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ function insertParams(
metadata: {},
priority: 1,
queue: "default",
scheduledAt: Temporal.Now.instant(),
// The polyfill's clock can be fractionally ahead of Postgres. Default
// fixtures should already be due when a test immediately claims them.
scheduledAt: Temporal.Now.instant().subtract({ seconds: 1 }),
state: "available",
tags: [],
uniqueKey: null,
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 @@ -137,9 +137,9 @@ At shutdown, stop `run`, then disconnect Prisma and close the `pg` pool.

## Requirements

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`.
Node.js 24 or newer with a global `Temporal` implementation. Node.js 26
provides it natively; on Node.js 24, [preload a polyfill](../../README.md#temporal-on-nodejs-24)
before importing River, including in worker threads. River never installs one.

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
6 changes: 3 additions & 3 deletions js/driver/sqlite/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,9 @@ A client writes at most one insert notification per queue per `fetchCooldown`

## Requirements

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`.
Node.js 24 or newer with a global `Temporal` implementation. Node.js 26
provides it natively; on Node.js 24, [preload a polyfill](../../README.md#temporal-on-nodejs-24)
before importing River, including in worker threads. River never installs one.

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
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.
*
* 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.
* Runtimes without native Temporal must preload an application-provided
* polyfill before importing River, including in worker threads. River checks
* these requirements on import and when constructing a client.
*/
export declare function assertRuntimeSupport(): void;
```
Expand Down
4 changes: 4 additions & 0 deletions js/examples/graceful-shutdown/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Graceful shutdown example

On Node.js 24, [set up Temporal](../../README.md#temporal-on-nodejs-24) and
set `NODE_OPTIONS` to preload it before running the commands below. Node.js 26
provides Temporal natively.

This self-contained SQLite example starts one active handler and requests a
graceful stop while it is still running. River stops fetching, allows the
handler to finish, persists completion, and then resolves `RunHandle.stop`.
Expand Down
4 changes: 4 additions & 0 deletions js/examples/hooks-metrics/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Hooks and metrics example

On Node.js 24, [set up Temporal](../../README.md#temporal-on-nodejs-24) and
set `NODE_OPTIONS` to preload it before running the commands below. Node.js 26
provides Temporal natively.

This SQLite example connects River's ordered hooks, Koa-style middleware, and
after-commit events to a tiny in-memory metrics collector. A production service
would replace the collector with its existing metrics or tracing SDK; River
Expand Down
4 changes: 4 additions & 0 deletions js/examples/mixed-language/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Mixed-language producer example

On Node.js 24, [set up Temporal](../../README.md#temporal-on-nodejs-24) and
set `NODE_OPTIONS` to preload it before running the commands below. Node.js 26
provides Temporal natively.

This Postgres example inserts a versioned payload under the stable job kind
`mixed_language.generate_report`, then reads the durable row back without any
JavaScript-private metadata. A Go, Rust, or JavaScript worker can register that
Expand Down
4 changes: 4 additions & 0 deletions js/examples/node-postgres/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# River TypeScript Example: node-postgres

On Node.js 24, [set up Temporal](../../README.md#temporal-on-nodejs-24) and
set `NODE_OPTIONS` to preload it before running the commands below. Node.js 26
provides Temporal natively.

A minimal example demonstrating how to use the [River](https://github.com/riverqueue/river/tree/master/js) TypeScript client with `node-postgres` (`pg`) to insert background jobs into Postgres.

The example defines typed jobs and shows single insertion, scheduling, batch
Expand Down
4 changes: 4 additions & 0 deletions js/examples/pg-worker/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Postgres worker example

On Node.js 24, [set up Temporal](../../README.md#temporal-on-nodejs-24) and
set `NODE_OPTIONS` to preload it before running the commands below. Node.js 26
provides Temporal natively.

This example migrates a Postgres database, inserts a job, and works it in
the same process: the handler snoozes once as if a payment provider were busy,
then succeeds and inserts a follow-up job through the worker's own client. It
Expand Down
4 changes: 4 additions & 0 deletions js/examples/prisma/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# River TypeScript Example: Prisma

On Node.js 24, [set up Temporal](../../README.md#temporal-on-nodejs-24) and
set `NODE_OPTIONS` to preload it before running the commands below. Node.js 26
provides Temporal natively.

A minimal example demonstrating how to use the [River](https://github.com/riverqueue/river/tree/master/js) TypeScript client with [Prisma](https://www.prisma.io/) to insert background jobs into Postgres.

The example defines typed jobs and shows single insertion, scheduling, batch
Expand Down
6 changes: 5 additions & 1 deletion js/examples/sqlite-worker/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# SQLite worker example

On Node.js 24, [set up Temporal](../../README.md#temporal-on-nodejs-24) and
set `NODE_OPTIONS` to preload it before running the commands below. Node.js 26
provides Temporal natively.

This example applies River's SQLite migrations to an in-memory database,
inserts a typed job, works it in-process, observes its committed completion,
and shuts down cleanly. It needs only Node.js 24 and the workspace packages.
and shuts down cleanly. It needs Node.js 24 or newer and the workspace packages.

From the repository root:

Expand Down
4 changes: 4 additions & 0 deletions js/examples/worker-thread-cpu/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CPU worker-thread example

On Node.js 24, [set up Temporal](../../README.md#temporal-on-nodejs-24) and
set `NODE_OPTIONS` to preload it before running the commands below. Node.js 26
provides Temporal natively.

This self-contained SQLite example routes a CPU-heavy prime calculation through
the bounded `@riverqueue/worker-threads` executor. The main event loop remains
available for River's database, completion, cancellation, and shutdown work.
Expand Down
6 changes: 3 additions & 3 deletions js/migrate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ checked for drift, so don't edit it by hand.

## Requirements

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`.
Node.js 24 or newer with a global `Temporal` implementation. Node.js 26
provides it natively; on Node.js 24, [preload a polyfill](../README.md#temporal-on-nodejs-24)
before importing River, including in worker threads. River never installs one.

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
8 changes: 3 additions & 5 deletions js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
"sideEffects": [
"./dist/index.js",
"./dist/unstable-driver.js",
"./dist/internal/temporal.js",
"./dist/internal/runtime-check.js",
"./src/index.ts",
"./src/unstable-driver.ts",
"./src/internal/temporal.ts"
"./src/internal/runtime-check.ts"
],
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand Down Expand Up @@ -79,9 +79,6 @@
"access": "public",
"provenance": true
},
"dependencies": {
"@js-temporal/polyfill": "^0.5.1"
},
"peerDependencies": {
"@types/node": ">=24"
},
Expand All @@ -93,6 +90,7 @@
"devDependencies": {
"@arethetypeswrong/cli": "^0.18.5",
"@eslint/js": "^10.0.1",
"@js-temporal/polyfill": "^0.5.1",
"@types/node": "^26.6.5",
"@types/pg": "^8.23.1",
"@vitest/coverage-v8": "^4.1.12",
Expand Down
Loading
Loading