Skip to content

Releases: wheels-dev/wheels

Wheels 4.0.0

12 May 05:59
3914c90

Choose a tag to compare

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 wheels dev 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-generated is*() checkers, auto-scopes, and inclusion validation (#1921)
  • Query scopes with scope() for reusable, composable query fragments in models (#1920)
  • Batch processing with findEach() and findInBatches() 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) and hasMany(as=...) with type-discriminator JOINs (#2104)
  • Advisory locks (withAdvisoryLock(name, callback)) and pessimistic locking (.forUpdate() on QueryBuilder) for SELECT ... FOR UPDATE (#2103)
  • CockroachDB database adapter — seventh supported database, with unique_rowid() PK convention and RETURNING clause identity select (#1876, #1986, #1993, #1999)
  • throwOnColumnNotFound config 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 diff CLI 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=true on resource routes or set(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 via set(middleware=[...]) (#1924)
  • Rate limiting middleware with wheels.middleware.RateLimiter supporting 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)
  • hsts argument on SecurityHeaders middleware to suppress the Strict-Transport-Security header 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(), and paginationNav() 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 patterns
  • viteScriptTag() and viteStyleTag() 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)
  • viteStrictManifest setting (default true) — missing manifest entries now throw Wheels.ViteAssetNotFound in production. Set to false to 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.baseDelay and this.maxDelay with formula Min(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, declarative inject() in controller config, bind() interface binding, auto-wiring of init() arguments, and config/services.cfm for 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 across cfthread workers (#2100)
  • Browser testing via Playwright Java with BrowserTest base class, fluent DSL (navigation, interaction, keyboard, waiting, scoping, cookies, auth, dialogs, viewport, script, screenshots, assertions), and wheels browser:install command (#2113, #2115, #2116, #2121)

Package system

  • Package system (PackageLoader) with packages/vendor/ activation model, package.json manifests with provides.mixins targets, 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/packages developer page now shows a "Browse registry" section listing all packages available from wheels-dev/wheels-packages — package name, description, latest version, and a copy-to-clipboard wheels packages install <name> snippet per row. Rows matching an already-installed package show a ✓ Installed badge. Dev/testing only; $blockInProduction() gate keeps it off production servers. Registry data comes from the CLI's Registry.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 new now 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 breaks wheels new. (#2556)
  • wheels doctor now detects a stale installed CLI module at ~/.wheels/modules/wheels/ that shadows a source checkout and warns with a remediation command (symlink). Previously, contributors running wheels from 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-wheels with 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-wheels with wheels / wheels-be manifests. Hourly auto-update via the community Excavator bot. Legacy Chocolatey wheels package on community.chocolatey.org (CommandBox-based v1.x) is no longer maintained — see Windows install docs for the migration. (#2545, #2552)
  • Linux.deb and .rpm packages built by nfpm on 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/. Native apt/yum repositories at apt.wheels.dev / yum.wheels.dev are planned for 4.0.x. (#2545)
  • WinGet — manifest drafts for Wheels.Wheels and Wheels.WheelsBE staged for post-GA submission to the microsoft/winget-pkgs community 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 ...
Read more

Wheels 4.0.0-SNAPSHOT+1783 (snapshot)

10 May 14:04
d63e9be

Choose a tag to compare

Pre-release

Snapshot build from develop. Not for production use.

Wheels 4.0.0-SNAPSHOT+1782 (snapshot)

10 May 13:57
49f51e1

Choose a tag to compare

Pre-release

Snapshot build from develop. Not for production use.

Wheels 4.0.0-SNAPSHOT+1781 (snapshot)

10 May 12:55
6ea3d8b

Choose a tag to compare

Pre-release

Snapshot build from develop. Not for production use.

Wheels 4.0.0-SNAPSHOT+1780 (snapshot)

10 May 07:27
422fcdb

Choose a tag to compare

Pre-release

Snapshot build from develop. Not for production use.

Wheels 4.0.0-SNAPSHOT+1779 (snapshot)

10 May 06:19
d7c3a66

Choose a tag to compare

Pre-release

Snapshot build from develop. Not for production use.

Wheels 4.0.0-SNAPSHOT+1778 (snapshot)

10 May 05:14
8ce5bc9

Choose a tag to compare

Pre-release

Snapshot build from develop. Not for production use.

Wheels 4.0.0-SNAPSHOT+1777 (snapshot)

10 May 04:47
2b443e6

Choose a tag to compare

Pre-release

Snapshot build from develop. Not for production use.

Wheels 4.0.0-SNAPSHOT+1776 (snapshot)

10 May 04:30
24b83e8

Choose a tag to compare

Pre-release

Snapshot build from develop. Not for production use.

Wheels 4.0.0-SNAPSHOT+1775 (snapshot)

10 May 03:34
109f3f1

Choose a tag to compare

Pre-release

Snapshot build from develop. Not for production use.