Releases: wheels-dev/wheels
Wheels 4.0.0
Wheels 4.0 — the release that started as 3.1 and grew into a major version. Closes multiple framework-maturity gaps against Rails, Laravel, and Django. See docs/releases/wheels-4.0-audit.md for the full audit trail (260+ merged PRs since 3.0.0). Contributors: @bpamiri, @zainforbjs, @chapmandu, @mlibbe, @MukundaKatta.
Added
Documentation
- Correct landing page license text from "MIT licensed" to "Apache 2.0 licensed"
- Add Debug Panel guide covering each tab, configuration settings, and when the bar appears
- Clarify BoxLang server management in cfml-engines guide; update vm-deployment tip to distinguish CommandBox server management from the
wheelsdev CLI
ORM & data layer
- Chainable query builder with
where(),orWhere(),whereNull(),whereBetween(),whereIn(),whereNotIn(),orderBy(),limit(), and more for injection-safe fluent queries (#1922) - Enum support with
enum()for named property values, auto-generatedis*()checkers, auto-scopes, and inclusion validation (#1921) - Query scopes with
scope()for reusable, composable query fragments in models (#1920) - Batch processing with
findEach()andfindInBatches()for memory-efficient record iteration (#1919) - Bulk insert/upsert operations (
insertAll()/upsertAll()) with per-adapter native UPSERT syntax across MySQL, PostgreSQL, SQL Server, SQLite, H2, CockroachDB, and Oracle (#2101) - Polymorphic associations via
belongsTo(polymorphic=true)andhasMany(as=...)with type-discriminator JOINs (#2104) - Advisory locks (
withAdvisoryLock(name, callback)) and pessimistic locking (.forUpdate()on QueryBuilder) forSELECT ... FOR UPDATE(#2103) - CockroachDB database adapter — seventh supported database, with
unique_rowid()PK convention andRETURNINGclause identity select (#1876, #1986, #1993, #1999) throwOnColumnNotFoundconfig setting for strict column validation in WHERE clauses (#1938)- SQL identifier quoting for reserved-word conflicts in table/column names (#1874)
Migrations
- Auto-migration generation from model/DB schema diff (
AutoMigrator.diff(modelName),writeMigration()) (#2102) - Auto-migration rename detection via explicit hints plus heuristic suggestions (normalized-token + Levenshtein) with new
wheels dbmigrate diffCLI command and MCP integration (#2112)
Routing
- Router modernization:
group()helper, typed constraints (whereNumber,whereAlpha,whereUuid,whereSlug,whereIn), API versioning via.version(1), performance indexes (#1891, #1894) - Route model binding with
binding=trueon resource routes orset(routeModelBinding=true)globally to auto-resolve model instances from route key parameters (#1929)
Middleware pipeline (new core framework)
- Middleware pipeline: closure-based chain running at dispatch level before controller instantiation, route-scoped via
.scope(middleware=[...])or global viaset(middleware=[...])(#1924) - Rate limiting middleware with
wheels.middleware.RateLimitersupporting fixed window, sliding window, and token bucket strategies with in-memory and database storage (#1931) - SecurityHeaders middleware emits Content-Security-Policy, HSTS, and Permissions-Policy headers (#2036)
hstsargument onSecurityHeadersmiddleware to suppress theStrict-Transport-Securityheader entirely, for apps behind TLS-terminating proxies that emit HSTS themselves (#2174)- Multi-tenant support with per-request datasource switching (#1951)
Views
- Composable pagination view helpers:
paginationInfo(),previousPageLink(),nextPageLink(),firstPageLink(),lastPageLink(),pageNumberLinks(), andpaginationNav()for building custom pagination UIs (#1930) - XSS helpers formalized:
h(),hAttr(),stripTags(),stripLinks()(#2097) - Redesigned v4.0 congratulations page for scaffolded apps (#2098)
vitePreloadTag()view helper emits<link rel="modulepreload">for a Vite entrypoint and its transitive chunk imports, suitable for Turbo Drive hover-preload patternsviteScriptTag()andviteStyleTag()now resolve transitive chunk imports from the Vite manifest: modulepreload links for JS chunks are emitted into<head>, and CSS from transitive chunks is included in the stylesheet tags (brings parity with Rails/Laravel Vite integrations)viteStrictManifestsetting (defaulttrue) — missing manifest entries now throwWheels.ViteAssetNotFoundin production. Set tofalseto restore 3.x silent behavior.
Background jobs & real-time
- Job worker daemon with CLI commands (
wheels jobs work/status/retry/purge/monitor) for persistent background job processing with optimistic locking, timeout recovery, and live monitoring (#1934) - Configurable exponential backoff for jobs via
this.baseDelayandthis.maxDelaywith formulaMin(baseDelay * 2^attempt, maxDelay)(#1934) - Pub/sub channels for SSE:
subscribeToChannel(),publish(),poll(), with DatabaseAdapter and in-memory implementations (#1940)
Dependency injection
- Expanded DI container with
asRequestScoped()for per-request service instances,service()global helper, declarativeinject()in controller config,bind()interface binding, auto-wiring of init() arguments, andconfig/services.cfmfor service registration (#1933)
Testing infrastructure
- HTTP test client (
TestClient) for integration testing with fluent assertions:visit(),assertOk(),assertSee(),assertJson(),assertJsonPath(), cookie tracking, session support (#2099) - Parallel test execution runner (
ParallelRunner) partitioning bundles acrosscfthreadworkers (#2100) - Browser testing via Playwright Java with
BrowserTestbase class, fluent DSL (navigation, interaction, keyboard, waiting, scoping, cookies, auth, dialogs, viewport, script, screenshots, assertions), andwheels browser:installcommand (#2113, #2115, #2116, #2121)
Package system
- Package system (
PackageLoader) withpackages/→vendor/activation model,package.jsonmanifests withprovides.mixinstargets, per-package error isolation (#1995) - Module system with dependency graph (requires/replaces/suggests topological sort) and lazy loading (#2017)
- LuCLI module distribution via wheels-cli-lucli repo (#2018)
/wheels/packagesdeveloper page now shows a "Browse registry" section listing all packages available fromwheels-dev/wheels-packages— package name, description, latest version, and a copy-to-clipboardwheels packages install <name>snippet per row. Rows matching an already-installed package show a✓ Installedbadge. Dev/testing only;$blockInProduction()gate keeps it off production servers. Registry data comes from the CLI'sRegistry.listAll()with 24h app-scope cache (#2271, partial — wheels.dev/packages static-site work deferred)
Engine adapters & cross-engine
- Engine adapter modules encapsulating Lucee, Adobe CF, and BoxLang engine-specific behavior (#2016)
- Interface-driven design contracts for framework extension points (#2014)
Migration & legacy
- Legacy compatibility adapter for 3.x → 4.0 migration soft-landing (#2015)
CLI & LuCLI
wheels newnow prints a non-blocking hint at the end of app scaffolding when a newer Wheels release is available on the user's channel (stable, bleeding-edge). Channel-aware (skips dev/rc), 24h-cached at$LUCLI_HOME/.update-check.json, 5s HTTP timeout, silent on any failure — never delays or breakswheels new. (#2556)wheels doctornow detects a stale installed CLI module at~/.wheels/modules/wheels/that shadows a source checkout and warns with a remediation command (symlink). Previously, contributors runningwheelsfrom a checkout could silently execute a pre-install Module.cfc, making merged fixes appear not to take effect. (#2223)- LuCLI Phase 2: zero-Docker local testing via
tools/test-local.sh(#2063) - LuCLI Phase 2: service layer, generators, MCP annotations (#1941)
- LuCLI Phase 3–4: scaffold, seed, in-process services (#2065)
- LuCLI-native Lucee 7 + SQLite CI pipeline (#2032)
- LuCLI tier 1 commands module + WheelsTest test suite (#2092, #2093)
- Playwright CLI commands for browser testing (#2013, #2021)
Distribution (new in 4.0)
- macOS — Homebrew tap at
wheels-dev/homebrew-wheelswith separate formulae for stable (wheels) and bleeding-edge (wheels-be) channels. Daily auto-update workflow polls the upstream release feeds and opens PRs. - Windows — Scoop bucket at
wheels-dev/scoop-wheelswithwheels/wheels-bemanifests. Hourly auto-update via the community Excavator bot. Legacy Chocolateywheelspackage oncommunity.chocolatey.org(CommandBox-based v1.x) is no longer maintained — see Windows install docs for the migration. (#2545, #2552) - Linux —
.deband.rpmpackages built bynfpmon every release and uploaded to the GitHub Release alongside the existing zip artifacts. The package installs/usr/bin/wheels, depends on OpenJDK 21, and on first run syncs the framework module into~/.wheels/. Nativeapt/yumrepositories atapt.wheels.dev/yum.wheels.devare planned for 4.0.x. (#2545) - WinGet — manifest drafts for
Wheels.WheelsandWheels.WheelsBEstaged for post-GA submission to themicrosoft/winget-pkgscommunity repo. (#2557)
Configuration & developer experience
env()helper for cross-scope environment variable access (#1985)- Pre-request logging (#1895)
- Debug panel redesign (W-001, W-002) (#2000, #2001)
- Gap migration detection in
migrateTo()— detects and runs previously-skipped migrations, not just the endpoint (#1928) - Calculated property SQL validation at model config time ...
Wheels 4.0.0-SNAPSHOT+1783 (snapshot)
Snapshot build from develop. Not for production use.
Wheels 4.0.0-SNAPSHOT+1782 (snapshot)
Snapshot build from develop. Not for production use.
Wheels 4.0.0-SNAPSHOT+1781 (snapshot)
Snapshot build from develop. Not for production use.
Wheels 4.0.0-SNAPSHOT+1780 (snapshot)
Snapshot build from develop. Not for production use.
Wheels 4.0.0-SNAPSHOT+1779 (snapshot)
Snapshot build from develop. Not for production use.
Wheels 4.0.0-SNAPSHOT+1778 (snapshot)
Snapshot build from develop. Not for production use.
Wheels 4.0.0-SNAPSHOT+1777 (snapshot)
Snapshot build from develop. Not for production use.
Wheels 4.0.0-SNAPSHOT+1776 (snapshot)
Snapshot build from develop. Not for production use.
Wheels 4.0.0-SNAPSHOT+1775 (snapshot)
Snapshot build from develop. Not for production use.