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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 3 additions & 3 deletions web/content/blog/posts/background-jobs-without-redis.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ One last note for anyone building interactive apps on top of the queue. Wheels 4

Check your production dependency list. If Redis is in your stack only for the job queue, consider the one-less-service version. The framework now has an opinion about this layer, and the opinion is that for most apps, your database is already a perfectly good place to keep a queue.

- [Background jobs guide](https://guides.wheels.dev/v4-0-0-snapshot/digging-deeper/background-jobs/) — job definition, enqueueing, retries, worker operation.
- [Multi-tenancy guide](https://guides.wheels.dev/v4-0-0-snapshot/digging-deeper/multi-tenancy/) — how tenant-aware datasource switching interacts with jobs and the rest of the request lifecycle.
- [SSE guide](https://guides.wheels.dev/v4-0-0-snapshot/digging-deeper/server-sent-events/) — pub/sub channels, push from job completion to browser.
- [Background jobs guide](https://guides.wheels.dev/v4-0-0/digging-deeper/background-jobs/) — job definition, enqueueing, retries, worker operation.
- [Multi-tenancy guide](https://guides.wheels.dev/v4-0-0/digging-deeper/multi-tenancy/) — how tenant-aware datasource switching interacts with jobs and the rest of the request lifecycle.
- [SSE guide](https://guides.wheels.dev/v4-0-0/digging-deeper/server-sent-events/) — pub/sub channels, push from job completion to browser.

If you are currently running Wheels with an external queue service and think the DB-backed path might be a fit, we would love to hear how the migration goes. The surface is new, the edges are still being polished, and the feedback from the first wave of adopters is what shapes the 4.0.x series.

Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ Thanks to the contributors who made this migration possible: [@bpamiri](https://
- [Contributing guide](https://github.com/wheels-dev/wheels/blob/develop/CONTRIBUTING.md) — workflow, branch conventions, and the Definition of Done for a PR.
- Wheels CLI installers — the [Homebrew tap](https://github.com/wheels-dev/homebrew-wheels) for macOS / Linux and the [`scoop-wheels`](https://github.com/wheels-dev/scoop-wheels) bucket for Windows. This is the install most contributors want.
- [LuCLI repo](https://github.com/bpamiri/LuCLI) — the underlying binary, if you want to run it standalone outside of Wheels; engine-level issues and feature requests belong here, not on the Wheels tracker.
- [Running tests locally](https://guides.wheels.dev/v4-0-0-snapshot/testing/running-tests-locally/) — LuCLI path and the Docker matrix fallback for cross-engine coverage.
- [Running tests locally](https://guides.wheels.dev/v4-0-0/testing/running-tests-locally/) — LuCLI path and the Docker matrix fallback for cross-engine coverage.
- [`wheels-cli-lucli`](https://github.com/wheels-dev/wheels-cli-lucli) — the distribution repo for the LuCLI module itself.

If you have been holding off on a Wheels PR because the dev setup was too heavy, now is a good time to try again.
Expand Down
6 changes: 3 additions & 3 deletions web/content/blog/posts/multi-tenancy-built-in.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ If you've been thinking "we'll deal with multi-tenancy later," 4.0 makes "now" a

## Where to go next

- [Multi-tenancy guide](https://guides.wheels.dev/v4-0-0-snapshot/digging-deeper/multi-tenancy/) — the user-facing reference for tenant resolution, datasource switching, and migrations per tenant.
- [Background jobs guide](https://guides.wheels.dev/v4-0-0-snapshot/digging-deeper/background-jobs/) — how the tenant context is restored when a worker picks up a job.
- [Middleware pipeline](https://guides.wheels.dev/v4-0-0-snapshot/core-concepts/middleware-pipeline/) — where the tenant resolver lives.
- [Multi-tenancy guide](https://guides.wheels.dev/v4-0-0/digging-deeper/multi-tenancy/) — the user-facing reference for tenant resolution, datasource switching, and migrations per tenant.
- [Background jobs guide](https://guides.wheels.dev/v4-0-0/digging-deeper/background-jobs/) — how the tenant context is restored when a worker picks up a job.
- [Middleware pipeline](https://guides.wheels.dev/v4-0-0/core-concepts/middleware-pipeline/) — where the tenant resolver lives.
- [Full audit § Multi-tenancy](https://github.com/wheels-dev/wheels/blob/develop/docs/releases/wheels-4.0-audit.md) — the PR-level receipts.

We'd love to hear from teams running production SaaS on this — especially the pattern of tenant identity source (subdomain vs. path vs. header vs. JWT claim) and how it interacts with your auth layer. That's the area where we expect the 4.0.x polish cycle to want the most feedback.
Expand Down
4 changes: 2 additions & 2 deletions web/content/blog/posts/security-hardening-in-wheels-4.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ Across these forty-plus PRs, no single change stands out as dramatic. That is th
## Where to go next

- [Full audit § Security hardening](https://github.com/wheels-dev/wheels/blob/develop/docs/releases/wheels-4.0-audit.md) — per-PR receipts for every hardening change in this post, plus the ones that did not fit.
- [Upgrading from Wheels 3.x](https://guides.wheels.dev/v4-0-0-snapshot/upgrading/3x-to-4x/) — detect / fix / opt-out guidance for each of the seven breaking defaults.
- [Security documentation](https://guides.wheels.dev/v4-0-0-snapshot/deployment/security-hardening/) — the user-facing reference for middleware, headers, and session hardening.
- [Upgrading from Wheels 3.x](https://guides.wheels.dev/v4-0-0/upgrading/3x-to-4x/) — detect / fix / opt-out guidance for each of the seven breaking defaults.
- [Security documentation](https://guides.wheels.dev/v4-0-0/deployment/security-hardening/) — the user-facing reference for middleware, headers, and session hardening.
- [SECURITY.md](https://github.com/wheels-dev/wheels/blob/develop/SECURITY.md) — responsible-disclosure process. That channel is read.

The framework cannot make your application secure. It can refuse to make it easy to be insecure. That is what 4.0 does.
Expand Down
4 changes: 2 additions & 2 deletions web/content/blog/posts/testing-in-wheels-4.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,9 @@ For local development, the canonical stack is LuCLI with SQLite — `bash tools/

## Where to go next

- [Testing guide](https://guides.wheels.dev/v4-0-0-snapshot/testing/) — the user-facing walkthrough for WheelsTest, TestClient, and the spec layout.
- [Testing guide](https://guides.wheels.dev/v4-0-0/testing/) — the user-facing walkthrough for WheelsTest, TestClient, and the spec layout.
- [Browser testing deep reference](https://github.com/wheels-dev/wheels/blob/develop/.ai/wheels/testing/browser-testing.md) — the full DSL surface, every gotcha we hit during the port, and the classloader/Playwright internals.
- [Running tests locally](https://guides.wheels.dev/v4-0-0-snapshot/testing/running-tests-locally/) — LuCLI and Docker paths.
- [Running tests locally](https://guides.wheels.dev/v4-0-0/testing/running-tests-locally/) — LuCLI and Docker paths.

If you have been treating testing as the part of the Wheels workflow you do not really do, 4.0 is the release where that calculus changes. First green unit spec in under five minutes. First green browser test in under 30. Feedback welcome on everything that is not yet obvious — contributors this cycle include @bpamiri, @zainforbjs, @chapmandu, @mlibbe, @MukundaKatta, and Dependabot, and the feedback loop that shaped the testing surface is exactly the one we want to keep open for 4.0.x.

14 changes: 7 additions & 7 deletions web/content/blog/posts/wheels-deploy-kamal-port.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ service: ${APP_NAME}
image: ${REGISTRY}/${APP_NAME}
```

`${VAR}` references resolve through the same lookup chain Kamal uses: CLI `--env` overrides → `.kamal/secrets` (with destination overlay) → `System.getenv` → empty string. Only uppercase-and-underscore tokens are expanded, so shell-style `${service}` placeholders elsewhere in the config aren't captured by accident. For the handful of cases that use ERB for control flow or computed values, the resolution moves into `.kamal/secrets` (or a `.kamal/secrets.<destination>` overlay) and the result is referenced back through `${VAR}`. The [migrating-from-kamal guide](https://guides.wheels.dev/v4-0-0-snapshot/deployment/migrating-from-kamal/) walks through each pattern.
`${VAR}` references resolve through the same lookup chain Kamal uses: CLI `--env` overrides → `.kamal/secrets` (with destination overlay) → `System.getenv` → empty string. Only uppercase-and-underscore tokens are expanded, so shell-style `${service}` placeholders elsewhere in the config aren't captured by accident. For the handful of cases that use ERB for control flow or computed values, the resolution moves into `.kamal/secrets` (or a `.kamal/secrets.<destination>` overlay) and the result is referenced back through `${VAR}`. The [migrating-from-kamal guide](https://guides.wheels.dev/v4-0-0/deployment/migrating-from-kamal/) walks through each pattern.

The net effect is that `wheels deploy` is *more* schema-compatible with Kamal than the "we replaced X with Y" framing would suggest. There is no new syntax to learn. The single change is a removal — ERB out, everything else identical.

Expand Down Expand Up @@ -137,15 +137,15 @@ The CFML code lives in `cli/lucli/services/deploy/`. Three direct dependencies

Tests live in `cli/lucli/tests/specs/deploy/` and extend the same `wheels.wheelstest` base class the rest of the framework uses. A `FakeSshPool` records every command for offline assertions. A real-SSH fixture (`tools/deploy-sshd-up.sh`) brings up a disposable sshd for the couple of specs that need to exercise real transport.

The full architecture reference — covering the code layout, the commands-are-strings invariant, the URLClassLoader JAR isolation, and the non-goals — lives in the guides under [Architecture of `wheels deploy`](https://guides.wheels.dev/v4-0-0-snapshot/deployment/architecture/). It is the place to start if you want to extend, debug, or evaluate the implementation.
The full architecture reference — covering the code layout, the commands-are-strings invariant, the URLClassLoader JAR isolation, and the non-goals — lives in the guides under [Architecture of `wheels deploy`](https://guides.wheels.dev/v4-0-0/deployment/architecture/). It is the place to start if you want to extend, debug, or evaluate the implementation.

## Where to go next

- [Your first deploy](https://guides.wheels.dev/v4-0-0-snapshot/deployment/first-deploy/) is the hands-on walkthrough — scaffold through first rollout.
- [Migrating from Kamal](https://guides.wheels.dev/v4-0-0-snapshot/deployment/migrating-from-kamal/) is the guide for teams coming from Ruby Kamal, including the full compatibility contract and the ERB-to-Mustache conversion.
- [Architecture of `wheels deploy`](https://guides.wheels.dev/v4-0-0-snapshot/deployment/architecture/) covers the port strategy, the commands-are-strings invariant, and the classloader isolation in depth.
- [Deployment landing page](https://guides.wheels.dev/v4-0-0-snapshot/deployment/) covers when to reach for `wheels deploy` versus the VM or Compose paths.
- [Config reference](https://guides.wheels.dev/v4-0-0-snapshot/deployment/config-reference/), [secrets](https://guides.wheels.dev/v4-0-0-snapshot/deployment/secrets/), [hooks](https://guides.wheels.dev/v4-0-0-snapshot/deployment/hooks/), [accessories](https://guides.wheels.dev/v4-0-0-snapshot/deployment/accessories/).
- [Your first deploy](https://guides.wheels.dev/v4-0-0/deployment/first-deploy/) is the hands-on walkthrough — scaffold through first rollout.
- [Migrating from Kamal](https://guides.wheels.dev/v4-0-0/deployment/migrating-from-kamal/) is the guide for teams coming from Ruby Kamal, including the full compatibility contract and the ERB-to-Mustache conversion.
- [Architecture of `wheels deploy`](https://guides.wheels.dev/v4-0-0/deployment/architecture/) covers the port strategy, the commands-are-strings invariant, and the classloader isolation in depth.
- [Deployment landing page](https://guides.wheels.dev/v4-0-0/deployment/) covers when to reach for `wheels deploy` versus the VM or Compose paths.
- [Config reference](https://guides.wheels.dev/v4-0-0/deployment/config-reference/), [secrets](https://guides.wheels.dev/v4-0-0/deployment/secrets/), [hooks](https://guides.wheels.dev/v4-0-0/deployment/hooks/), [accessories](https://guides.wheels.dev/v4-0-0/deployment/accessories/).

If you are currently running a Wheels app in production with ad-hoc deploy tooling — or running Ruby Kamal alongside Wheels — we would love to hear what the switch to `wheels deploy` is like. The command is new, the design space for polish in 4.0.x is open, and the feedback loop from early adopters is what will shape it.

2 changes: 1 addition & 1 deletion web/packages/ui/src/components/starlight/EditLink.astro
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ if (isApiFunction) {
}
} else {
// Guides. entry.id already includes the version segment (e.g.
// 'v4-0-0-snapshot/basics/associations'), which matches the on-disk layout.
// 'v4-0-1-snapshot/basics/associations'), which matches the on-disk layout.
href = `https://github.com/wheels-dev/wheels/edit/develop/web/sites/guides/src/content/docs/${id}.mdx`;
}
---
Expand Down
2 changes: 1 addition & 1 deletion web/packages/ui/src/components/starlight/PageTitle.astro
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const frontmatterCategory = (entry.data as { category?: string }).category;
let eyebrow: string | null = frontmatterCategory ?? null;

if (!eyebrow) {
// id looks like 'v4-0-0-snapshot/command-line-tools/quick-start'.
// id looks like 'v4-0-1-snapshot/command-line-tools/quick-start'.
// Strip the version prefix, then take the first segment.
const withoutVersion = id.replace(/^v[^/]+\//, '');
const firstSegment = withoutVersion.split('/')[0] ?? '';
Expand Down
11 changes: 6 additions & 5 deletions web/packages/ui/src/data/versions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,16 @@ export interface VersionMeta {

/** Wheels Guides — the narrative docs at guides.wheels.dev. */
export const GUIDES_VERSIONS: VersionMeta[] = [
{ slug: 'v4-0-0-snapshot', label: 'v4.0.0-SNAPSHOT', sidebarLabel: 'v4.0.0-SNAPSHOT (dev)', collapsed: false, status: 'snapshot' },
{ slug: 'v3-0-0', label: 'v3.0.0', sidebarLabel: 'v3.0.0 (current)', collapsed: true, status: 'current' },
{ slug: 'v4-0-1-snapshot', label: 'v4.0.1-SNAPSHOT', sidebarLabel: 'v4.0.1-SNAPSHOT (dev)', collapsed: false, status: 'snapshot' },
{ slug: 'v4-0-0', label: 'v4.0.0', sidebarLabel: 'v4.0.0 (current)', collapsed: false, status: 'current' },
{ slug: 'v3-0-0', label: 'v3.0.0', collapsed: true, status: 'archived' },
{ slug: 'v2-5-0', label: 'v2.5.0', collapsed: true, status: 'archived' },
];

/** Wheels API Reference — function-level docs at api.wheels.dev. */
export const API_VERSIONS: VersionMeta[] = [
{ slug: 'v4-0-0-snapshot', label: 'v4.0.0-SNAPSHOT', sidebarLabel: 'v4.0.0 (snapshot)', collapsed: false, status: 'snapshot' },
{ slug: 'v3-0-0', label: 'v3.0.0', sidebarLabel: 'v3.0.0 (current)', collapsed: true, status: 'current' },
{ slug: 'v4-0-0', label: 'v4.0.0', sidebarLabel: 'v4.0.0 (current)', collapsed: false, status: 'current' },
{ slug: 'v3-0-0', label: 'v3.0.0', collapsed: true, status: 'archived' },
{ slug: 'v2-5-0', label: 'v2.5.0', collapsed: true, status: 'archived' },
{ slug: 'v2-4-0', label: 'v2.4.0', collapsed: true, status: 'archived' },
{ slug: 'v2-3-0', label: 'v2.3.0', collapsed: true, status: 'archived' },
Expand Down Expand Up @@ -126,7 +127,7 @@ interface CollectionEntry {
*
* Caller provides `entries` from `await getCollection('docs')` so this
* function stays framework-agnostic and testable. `entryId` is the
* current route's entry id (e.g., 'v4-0-0-snapshot/introduction/readme/
* current route's entry id (e.g., 'v4-0-1-snapshot/start-here/installing/
* beginner-tutorial-hello-world').
*
* Returns { options, currentVersion } or null if we couldn't resolve
Expand Down
2 changes: 1 addition & 1 deletion web/scripts/visual-regression.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const SITES = [
{
name: 'guides',
dist: resolve(WEB_ROOT, 'sites/guides/dist'),
canary: '/v4-0-0-snapshot/start-here/tutorial/',
canary: '/v4-0-0/start-here/tutorial/',
},
{
name: 'api',
Expand Down
22 changes: 11 additions & 11 deletions web/sites/api/src/content/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ from the framework's inline documentation annotations.

## Available versions

| Version | Functions | Status |
| ------------------------------------------ | --------- | ------------------------ |
| **[v4.0.0 (snapshot)](/v4-0-0-snapshot/)** | — | development (unreleased) |
| **[v3.0.0](/v3-0-0/)** | 309 | current release |
| [v2.5.0](/v2-5-0/) | 309 | |
| [v2.4.0](/v2-4-0/) | 298 | |
| [v2.3.0](/v2-3-0/) | 298 | |
| [v2.2.0](/v2-2-0/) | 298 | |
| [v2.1.0](/v2-1-0/) | 298 | |
| [v2.0.0](/v2-0-0/) | 254 | |
| [v1.4.5](/v1-4-5/) | 219 | |
| Version | Functions | Status |
| ---------------------- | --------- | --------------- |
| **[v4.0.0](/v4-0-0/)** | 378 | current release |
| [v3.0.0](/v3-0-0/) | 309 | |
| [v2.5.0](/v2-5-0/) | 309 | |
| [v2.4.0](/v2-4-0/) | 298 | |
| [v2.3.0](/v2-3-0/) | 298 | |
| [v2.2.0](/v2-2-0/) | 298 | |
| [v2.1.0](/v2-1-0/) | 298 | |
| [v2.0.0](/v2-0-0/) | 254 | |
| [v1.4.5](/v1-4-5/) | 219 | |

## How to use

Expand Down
32 changes: 32 additions & 0 deletions web/sites/api/src/content/docs/v4-0-0/configuration/addformat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: addFormat()
description: "Adds a new MIME type to your Wheels application for use with responding to multiple formats."
sidebar:
label: addFormat()
order: 0
---

## Signature

`addFormat()` — returns `void`

**Available in:** `controller`, `model`, `mapper`, `migrator`, `migration`, `tabledefinition`
**Category:** Miscellaneous Functions

## Description

Adds a new MIME type to your Wheels application for use with responding to multiple formats.



## Parameters

<div class="wd-params-table">

| Name | Type | Required | Default | Description |
| ---- | ---- | -------- | ------- | ----------- |
| `extension` | `string` | yes | — | File extension to add. |
| `mimeType` | `string` | yes | — | Matching MIME type to associate with the file extension. |

</div>

34 changes: 34 additions & 0 deletions web/sites/api/src/content/docs/v4-0-0/configuration/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
title: api()
description: "Scope routes under an API path prefix. Shorthand for <code>.group(path=\"api\", name=\"api\", ...)</code>. Typically used in combination with <code>version()</code>"
sidebar:
label: api()
order: 0
---

## Signature

`api()` — returns `struct`

**Available in:** `mapper`
**Category:** Routing

## Description

Scope routes under an API path prefix. Shorthand for <code>.group(path="api", name="api", ...)</code>. Typically used in combination with <code>version()</code> to organize versioned API endpoints.



## Parameters

<div class="wd-params-table">

| Name | Type | Required | Default | Description |
| ---- | ---- | -------- | ------- | ----------- |
| `path` | `string` | no | `api` | URL path prefix for the API. Defaults to `"api"`. |
| `name` | `string` | no | `api` | Name prefix for route names. Defaults to `"api"`. |
| `constraints` | `struct` | no | — | Variable patterns to apply to all child routes. |
| `callback` | `any` | no | — | A callback function to define nested routes within this API scope. |

</div>

23 changes: 23 additions & 0 deletions web/sites/api/src/content/docs/v4-0-0/configuration/collection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
title: collection()
description: "A collection route doesn't require an id because it acts on a collection of objects."
sidebar:
label: collection()
order: 0
---

## Signature

`collection()` — returns `struct`

**Available in:** `mapper`
**Category:** Routing

## Description

A collection route doesn't require an id because it acts on a collection of objects.
photos/search is an example of a collection route, because it acts on (and displays) a collection of objects.




22 changes: 22 additions & 0 deletions web/sites/api/src/content/docs/v4-0-0/configuration/constraints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
title: constraints()
description: "Set variable patterns to use for matching."
sidebar:
label: constraints()
order: 0
---

## Signature

`constraints()` — returns `struct`

**Available in:** `mapper`
**Category:** Routing

## Description

Set variable patterns to use for matching.




Loading
Loading