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
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ jobs:

node-floor:
name: Node 22.18 floor
# The published packages declare `engines.node: >=22.18.0`, but every suite
# The published packages declare `engines.node: ^22.18.0 || ^24.11.0 || >=26.0.0`,
# and 22.18.0 is its oldest release, but every suite
# above runs under bun, which ignores `engines.node` entirely, and
# `.tool-versions` pins node 24.16.0 — so nothing else here ever executes
# the floor version. This job does. It covers what a Node user hits first:
Expand Down
15 changes: 5 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,17 @@ databases, schedules, secrets — compose them into a **Prisma App**, and
`prisma deploy` provisions all of it. There is no infrastructure
configuration to write or maintain.

## You need Node 22.18 or newer
## You need a supported Node.js release

Composer supports Node.js 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer, each with the npm that Node release ships (npm 10 on Node 22). Check yours first:

```sh
node --version
```

**On anything older, Composer cannot load your app at all.** Your entry file is
TypeScript and `prisma` hands it straight to Node — the framework
never bundles or transforms your code. Node runs `.ts` files directly only from
**22.18.0**, the release that turns type stripping on by default. Before that,
`prisma deploy` stops at `ERR_UNKNOWN_FILE_EXTENSION` naming your own
entry file, which reads as a broken file rather than a Node that is too old.
**On an older Node, Composer cannot load your app at all.** Your entry file is TypeScript and `prisma` hands it straight to Node — the framework never bundles or transforms your code. Node runs `.ts` files directly only from **22.18.0**, the release that turns type stripping on by default. Before that, `prisma deploy` stops at `ERR_UNKNOWN_FILE_EXTENSION` naming your own entry file, which reads as a broken file rather than a Node that is too old.

22.17 is not close enough, and Node 20 never gets it: the default changed in
22.18.0 exactly. The `streams` module's local stand-in additionally needs
Node's built-in `node:sqlite`, unflagged since 22.13 — so 22.18 covers both.
22.17 is not close enough, and Node 20 never gets it: the default changed in 22.18.0 exactly. The `streams` module's local stand-in additionally needs Node's built-in `node:sqlite`, unflagged since 22.13, so 22.18 covers both. On the 24 line, 24.11 is the first long-term-support release. Every Prisma 8 tool supports this same range.

## Start with the skill

Expand Down
3 changes: 1 addition & 2 deletions docs/design/10-domains/deploy-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ already created). The `destroy` operation takes the same
and topology emission are out of scope (see § Out of scope).

**Runtime.** The command family is runtime-agnostic — no bun-only APIs
anywhere in the CLI or assembly code — so it runs under both bun and node (≥ 22.18, where
type stripping imports the user's `.ts` entry natively). Under node, the CLI
anywhere in the CLI or assembly code — so it runs under both bun and node (Node.js 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer; type stripping imports the user's `.ts` entry natively from 22.18). Under node, the CLI
also registers a synchronous resolve hook (`node:module` `registerHooks`) so
that relative imports inside the entry graph may use `./x.js` or extensionless
`./x` specifiers for `.ts` source files without requiring
Expand Down
3 changes: 1 addition & 2 deletions docs/guides/deploying.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,7 @@ if (!result.ok) {
```

Run it with the two variables under [Credentials](#credentials) set, for
example `bun destroy-staging.ts`, or `node destroy-staging.ts` on Node 22.18
or newer. A `prisma auth login` session is not used.
example `bun destroy-staging.ts`, or `node destroy-staging.ts` on a supported Node.js release (22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer). A `prisma auth login` session is not used.

The three teardown shapes differ in what happens to state. Destroying a **stage** removes its resources, then deletes its Branch — and the Branch takes the stage's deploy state with it. Destroying **production** removes the resources and empties production's deploy state as it goes, but the production Branch survives, so an emptied state scope remains until the Project itself is removed. Deleting the **Project** (below, or from the Console) removes every Branch and all state in one stroke. Destroy never creates: tearing down a stage that was never deployed fails with "nothing deployed" rather than provisioning one first.

Expand Down
6 changes: 1 addition & 5 deletions docs/guides/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,7 @@ and [Building an app](building-an-app.md#databases) covers it.

You'll need:

- **Node 22.18 or newer** — check with `node --version` before anything else.
Composer hands your TypeScript entry file straight to Node, and Node runs
`.ts` directly only from 22.18.0, the release that turns type stripping on by
default. On anything older `prisma deploy` stops at
`ERR_UNKNOWN_FILE_EXTENSION` naming your own file; 22.17 is not close enough.
- **Node.js 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer**, with the npm that Node release ships (npm 10 on Node 22). Check with `node --version` before anything else. Composer hands your TypeScript entry file straight to Node, and Node runs `.ts` directly only from 22.18.0, the release that turns type stripping on by default. On anything older `prisma deploy` stops at `ERR_UNKNOWN_FILE_EXTENSION` naming your own file; 22.17 is not close enough.
- [Bun](https://bun.sh) — Prisma Compute runs Bun, so that's what the server
code targets (`Bun.serve`), and it's the fastest way to run things locally.
- pnpm (or npm).
Expand Down
2 changes: 1 addition & 1 deletion packages/0-framework/1-core/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"directory": "packages/0-framework/1-core/core"
},
"engines": {
"node": ">=22.18.0"
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
},
"private": true,
"types": "./dist/index.d.mts"
Expand Down
2 changes: 1 addition & 1 deletion packages/0-framework/2-authoring/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"directory": "packages/0-framework/2-authoring/nextjs"
},
"engines": {
"node": ">=22.18.0"
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
},
"private": true,
"types": "./dist/index.d.mts"
Expand Down
2 changes: 1 addition & 1 deletion packages/0-framework/2-authoring/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"directory": "packages/0-framework/2-authoring/node"
},
"engines": {
"node": ">=22.18.0"
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
},
"private": true,
"types": "./dist/index.d.mts"
Expand Down
2 changes: 1 addition & 1 deletion packages/0-framework/2-authoring/service-rpc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"directory": "packages/0-framework/2-authoring/service-rpc"
},
"engines": {
"node": ">=22.18.0"
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
},
"private": true,
"types": "./dist/index.d.mts"
Expand Down
2 changes: 1 addition & 1 deletion packages/0-framework/3-tooling/assemble/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"directory": "packages/0-framework/3-tooling/assemble"
},
"engines": {
"node": ">=22.18.0"
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
},
"private": true,
"types": "./dist/index.d.mts"
Expand Down
2 changes: 1 addition & 1 deletion packages/0-framework/3-tooling/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"directory": "packages/0-framework/3-tooling/cli"
},
"engines": {
"node": ">=22.18.0"
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
},
"private": true
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,24 @@ describe('nodeExecutable()', () => {
).toThrow(expect.objectContaining({ code: 'DEPLOY.NODE_MISSING' }));
});
}

test('names the supported Node.js range in the DEPLOY.NODE_MISSING fix', () => {
expect(() =>
nodeExecutable({
bun: true,
execPath: '/opt/bun/bin/bun',
env: { PATH: '/usr/bin' },
platform: 'linux',
exists: () => false,
}),
).toThrow(
expect.objectContaining({
fix:
'Install Node.js 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 ' +
'or newer, and put it on PATH, or run `prisma` under Node.',
}),
);
});
});

describe('alchemyInvocation()', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/0-framework/3-tooling/cli/src/entry-resolution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ let registered = false;
*/
export function registerEntryResolution(): void {
if (registered || typeof process.versions.bun === 'string') return;
// registerHooks landed in Node 22.15; the engine floor is 22.18.
// registerHooks landed in Node 22.15; the oldest supported Node.js is 22.18.
// Guard here covers any non-standard environment.
if (typeof mod.registerHooks !== 'function') return;
registered = true;
Expand Down
6 changes: 5 additions & 1 deletion packages/0-framework/3-tooling/cli/src/run-alchemy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ export function nodeExecutable(runtime: HostRuntime = currentRuntime()): string
throw new CliStructuredError(
'DEPLOY.NODE_MISSING',
'Composer starts Alchemy with Node, and no `node` was found on PATH.',
{ fix: 'Install Node 22.18 or newer and put it on PATH, or run `prisma` under Node.' },
{
fix:
'Install Node.js 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 ' +
'or newer, and put it on PATH, or run `prisma` under Node.',
},
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"directory": "packages/1-prisma-cloud/0-lowering/dev-emulators"
},
"engines": {
"node": ">=22.18.0"
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
},
"private": true,
"types": "./dist/index.d.mts"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"directory": "packages/1-prisma-cloud/0-lowering/local-target"
},
"engines": {
"node": ">=22.18.0"
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
},
"private": true,
"types": "./dist/index.d.mts"
Expand Down
2 changes: 1 addition & 1 deletion packages/1-prisma-cloud/0-lowering/lowering/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"directory": "packages/1-prisma-cloud/0-lowering/lowering"
},
"engines": {
"node": ">=22.18.0"
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
},
"private": true,
"types": "./dist/index.d.mts"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"directory": "packages/1-prisma-cloud/0-lowering/s3-protocol"
},
"engines": {
"node": ">=22.18.0"
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
},
"private": true,
"types": "./dist/index.d.mts"
Expand Down
2 changes: 1 addition & 1 deletion packages/1-prisma-cloud/1-extensions/target/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"directory": "packages/1-prisma-cloud/1-extensions/target"
},
"engines": {
"node": ">=22.18.0"
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
},
"private": true,
"types": "./dist/index.d.mts"
Expand Down
2 changes: 1 addition & 1 deletion packages/9-public/composer-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"directory": "packages/9-public/composer-cli"
},
"engines": {
"node": ">=22.18.0"
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
},
"publishConfig": {
"access": "public"
Expand Down
2 changes: 1 addition & 1 deletion packages/9-public/composer-prisma-cloud/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
"directory": "packages/9-public/composer-prisma-cloud"
},
"engines": {
"node": ">=22.18.0"
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
},
"publishConfig": {
"access": "public"
Expand Down
2 changes: 1 addition & 1 deletion packages/9-public/composer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"directory": "packages/9-public/composer"
},
"engines": {
"node": ">=22.18.0"
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
},
"publishConfig": {
"access": "public"
Expand Down
12 changes: 10 additions & 2 deletions scripts/check-cli-engine-pin-host.test.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import assert from 'node:assert/strict';
import { mkdirSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
import {
mkdirSync,
mkdtempSync,
realpathSync,
rmSync,
symlinkSync,
unlinkSync,
writeFileSync,
} from 'node:fs';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { afterEach, beforeEach, describe, it } from 'node:test';
Expand Down Expand Up @@ -99,7 +107,7 @@ describe('checkHost', () => {

it('fails when the family cannot be resolved from the host, naming the override and the root link', () => {
healthyTree();
rmSync(join(root, 'node_modules/@prisma/composer-cli'));
unlinkSync(join(root, 'node_modules/@prisma/composer-cli'));

const [failure, ...rest] = run();

Expand Down
4 changes: 2 additions & 2 deletions scripts/check-floor-imports.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
//
// Requires both packages to be built (`pnpm turbo run build
// --filter=@prisma/composer --filter=@prisma/composer-cli`). Uses whichever
// `node` is running it, which is what lets the node-floor job aim it at
// 22.18.0.
// `node` is running it, which is what lets the node-floor job aim it at the
// oldest release of each supported Node.js line.
//
// Usage: node scripts/check-floor-imports.mjs

Expand Down
37 changes: 37 additions & 0 deletions scripts/supported-node-range.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { readFileSync } from 'node:fs';
import { dirname, join, resolve } from 'node:path';
import { describe, it } from 'node:test';
import { fileURLToPath } from 'node:url';

const SUPPORTED_NODE_RANGE = '^22.18.0 || ^24.11.0 || >=26.0.0';

const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');

const WORKSPACE_DIRS = ['packages', 'examples', 'test', 'website'];

const manifests = execFileSync('git', ['ls-files', '--', ...WORKSPACE_DIRS], {
cwd: repoRoot,
encoding: 'utf-8',
})
.split('\n')
.filter((file) => file === 'package.json' || file.endsWith('/package.json'))
.map((file) => ({ file, json: JSON.parse(readFileSync(join(repoRoot, file), 'utf-8')) }));

describe('supported Node.js range', () => {
it('is declared by every published package', () => {
const published = manifests.filter(({ file }) => file.startsWith('packages/9-public/'));
assert.ok(published.length > 0);
for (const { file, json } of published) {
assert.equal(json.engines?.node, SUPPORTED_NODE_RANGE, file);
}
});

it('is the only engines.node any workspace package declares', () => {
for (const { file, json } of manifests) {
if (json.engines?.node === undefined) continue;
assert.equal(json.engines.node, SUPPORTED_NODE_RANGE, file);
}
});
});
1 change: 1 addition & 0 deletions skills/prisma-composer-core-concepts/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,7 @@ today the blocks above plus your own Modules are the whole set, so verify a
`admin.createUser` from a service wired to the `admin` port. A deployed
stack's rpc ports are reachable only from inside its graph, so the app
exposes its own operator route that makes that call.
11. **`prisma deploy` stops at `ERR_UNKNOWN_FILE_EXTENSION` naming the app's own `.ts` entry.** Node is too old to strip TypeScript types. Composer supports Node.js 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer, with the npm that Node release ships (npm 10 on Node 22); its packages declare `engines.node` as `^22.18.0 || ^24.11.0 || >=26.0.0`. Upgrade Node.

## What Composer doesn't do yet

Expand Down
Loading