Skip to content

Supported runtimes

Warlock is developed and built on Node. The production build it produces also starts and serves on Bun and Deno, through their Node compatibility layers. This page says which command runs where, gives the exact start commands, and lists what the verification covers.

CommandNode.js 22.15+Bun 1.4.3Deno 2.9.7
warlock dev✔ supported✖ refused with an error✖ refused with an error
warlock build✔ supportedruns, prints a notice — use Noderuns, prints a notice — use Node
warlock start✔ supported✔ supported✔ supported
the built entry directly✔ node dist/app.js✔ bun dist/app.js✔ deno run -A dist/app.js

The Bun and Deno versions are the ones the runtimes CI workflow pins and tests on every pull request. Newer patch releases are expected to work; a version not listed has not been verified.

warlock dev compiles TypeScript on the fly and hot-reloads modules through Node’s module loader hooks (module.register and module.registerHooks). Bun does not provide registerHooks, and neither runtime runs Node’s loader hooks the way the dev server needs them. Rather than start a half-working server, warlock dev stops before doing anything on Bun or Deno:

✖ warlock dev runs on Node.js only — this process is Bun 1.4.3.
The development server compiles TypeScript and hot-reloads modules through Node's module loader hooks.
Run it with Node.js 22.15 or newer: npx warlock dev
Bun can run the production build: build it with Node (npx warlock build),
then start it with bun dist/app.js.

The process exits with code 1.

warlock build is plain esbuild and Vite work, and it usually completes on Bun and Deno too. Only the Node build is verified, so on Bun or Deno the command prints a notice on stderr and carries on:

! warlock build is verified on Node.js; this process is Deno 2.9.7.
Build with Node (npx warlock build) for a supported artifact. Starting the built app on Deno is supported.

Build in CI or on the deploy host with Node, then ship dist/ together with node_modules (the bundle keeps packages external) and your .env files — exactly as for a Node deployment. See Deployment & production.

Either run the built entry directly:

Terminal window
NODE_ENV=production bun dist/app.js

or run the CLI with Bun, which keeps warlock start’s readiness banner, --role/--sites handling and signal forwarding:

Terminal window
NODE_ENV=production bun node_modules/@warlock.js/core/bin/warlock.js start
NODE_ENV=production bun node_modules/@warlock.js/core/bin/warlock.js start --role=api

bun --bun run start (with "start": "warlock start" in package.json) works too: --bun makes the warlock shim run on Bun instead of the node on your PATH. A plain bun run start runs the CLI on Node. The CLI starts the server with the runtime it is running on, so the server runs on Bun whenever the CLI does.

Terminal window
NODE_ENV=production deno run -A dist/app.js

or through the CLI:

Terminal window
NODE_ENV=production deno run -A node_modules/@warlock.js/core/bin/warlock.js start

Deno picks up node_modules from the package.json next to it, so no extra compatibility flag is needed. -A grants every permission: a Warlock app reads .env and config, binds its port, reads environment variables, writes logs, cache and uploads under storage/, and the CLI spawns the server process. A narrower --allow-* set is possible but is not what CI verifies.

The runtimes workflow builds the framework packages from the commit under test, installs a fixture app (packages/core/tests/acceptance/runtime-app) from those tarballs, runs warlock build on Node, then starts the build on each runtime — once directly and once through warlock start — and requires:

  • a server-rendered React page (@warlock.js/web, streaming SSR with a loader);
  • a JSON API route and a POST with a JSON body;
  • a file-cache round trip and a file log channel;
  • a file from public/ and a hashed client asset from the build;
  • /health, and a 404 for an unknown path;
  • a clean shutdown on SIGTERM that releases the port.

Run the same check locally with Node, plus Bun and Deno on your PATH:

Terminal window
node packages/core/tests/acceptance/run-runtime-acceptance.mjs --runtimes=node,bun,deno

Database drivers, Redis, queues, sockets and native add-ons such as sharp are not part of the fixture. They go through the same compatibility layers, but they have not been verified on Bun or Deno.

The published @warlock.js/core ships compiled JavaScript, and bin/warlock.js loads it with no loader at all. A checkout of the framework repository has only TypeScript, so there the bin compiles the CLI on the fly: it resolves TypeScript paths in the main thread and transpiles .ts files with esbuild in a module.register() loader worker. That needs Node.js 22.15 or newer and is refused on Bun and Deno.

To run a checkout’s CLI against an app, link the packages into the app’s node_modules (or use pnpm link), then call the bin from the app directory:

Terminal window
mkdir -p node_modules/@warlock.js
ln -s /path/to/framework/packages/core node_modules/@warlock.js/core
ln -s /path/to/framework/packages/cache node_modules/@warlock.js/cache
ln -s /path/to/framework/packages/logger node_modules/@warlock.js/logger
node /path/to/framework/packages/core/bin/warlock.js inspect --json

Bare subpaths such as @warlock.js/core/net resolve to the package’s net/index.ts, the same file TypeScript and Vitest use.