They are our own superseded options, not a stale dependency. The recommended form is already what the showcase example uses; the blast radius is three test files inside packages/auth, and the rpId/origin options are already ignored at runtime. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
12 KiB
Legacy, deprecated, and unused-config cleanup — Design
Date: 2026-08-19
Status: Approved for implementation
Scope: Remove the compatibility-flag surface, the "legacy" function runtime, dead migrations,
and deprecated APIs — before the framework's first public release.
Goal
WRNexus carries compatibility machinery for a public it does not yet have. Every branch of it is either switched off in the only apps that exist, or wired to nothing at all. Removing it now costs almost nothing; removing it after release costs a major version and other people's time.
Why this is safe now
Two pieces of evidence, both verified rather than assumed:
The only consumers already run without it, which matters for the config files themselves even
though the keys are inert. D:\Company\wrnexus\apps\admin and
D:\Company\wrnexus\apps\web — both test projects, neither deployed — set every compatibility flag
to false and legacyDefaultRuntime: "current". They are already on the modern path; removing the
flags means deleting the lines that say "off".
None of the seven keys change any behaviour. Verified by tracing every reference, not by
reading the types. legacyEmit, legacyEventProps, legacyComponentDiscovery, stringLayouts,
and legacyDefaultRuntime appear in exactly three places each: the type declaration in
config.ts, the scaffolder in create.ts, and the insertion in update.ts. No compiler,
codegen, or runtime code reads any of them. They are written into every generated config and then
ignored.
The remaining two are the same story with more machinery. resolveCompatibility (packages/styles/src/compatibility.ts)
produces a report — an effective date, a behaviour number, and advisory strings. Tracing every
consumer: the wrnexus compatibility command prints it, and config.ts raises one validation error
when the configured date is newer than the CLI supports. No compiler branch and no runtime
behaviour reads effectiveDate or effectiveBehaviour. The mechanism is scaffolding that was
never connected.
Non-goals
- The legacy
apiblock forms. Bare-body blocks usingwith ($data), andapientries insidessr {}/client {}, are replaced rather than deleted — that is the next spec's job. Removing them here would leave a gap with no working mechanism. - Adding any configuration key. This spec only removes them.
- Changing any behaviour that is currently switched on.
What gets removed
| Item | Where | Why it goes |
|---|---|---|
compatibility: { legacyEmit, legacyEventProps, legacyComponentDiscovery, stringLayouts } |
packages/styles/src/config.ts (CompatibilityConfig) |
Never read by any code |
functions: { legacyDefaultRuntime } |
packages/styles/src/config.ts (FunctionsConfig) |
Never read by any code |
compatibilityDate |
packages/styles/src/compatibility.ts (CompatibilityPolicy) |
Gates nothing |
frameworkBehaviour |
same | Gates nothing |
resolveCompatibility, CompatibilityReport, isCompatibilityDate, CURRENT_COMPATIBILITY_DATE, CURRENT_FRAMEWORK_BEHAVIOUR |
packages/styles/src/compatibility.ts |
Whole module serves only the two dead keys |
wrnexus compatibility <check|explain|upgrade> |
packages/cli/src/compatibility-command.ts, dispatch at packages/cli/src/index.ts:295, help text at :75 |
Reports on removed keys |
"legacy" variant of FunctionRuntime |
packages/syntax/src/v060.ts, branches in packages/compiler/src/client-codegen.ts and server-codegen.ts |
An unmarked function becomes shared (see below) |
Migrations below 0.8.0 |
packages/cli/src/update.ts |
111 migrations reach back to 0.2.8; no project exists below 0.8.x |
| Deprecated re-export shims | packages/compiler/src/{parser,tokenizer,types}.ts |
Two lines each, re-exporting @wrnexus/syntax |
Deprecated @wrnexus/auth options |
engine.ts (4 sites), http/index.ts (2), plugin.ts (2), types.ts (1) |
See "Deprecated auth options" |
The "legacy" function runtime
FunctionRuntime is "legacy" | "client" | "server" | "shared". "legacy" is what an unmarked
function foo() gets, and legacyDefaultRuntime decides how it behaves. Both codegens then test
membership: ["legacy", "client", "shared"] for the browser and ["legacy", "server", "shared"]
for the server — which is to say an unmarked function is currently emitted into both bundles,
exactly like shared.
legacyDefaultRuntime looks like it should modulate this, but it is never read (above), so the
mapping is unconditional: unmarked is always "legacy", and "legacy" is always emitted to both
bundles.
So the removal is mechanical: delete the "legacy" variant, and parse an unmarked function as
"shared". The emitted output for every existing unmarked function is unchanged, in every
configuration. FunctionRuntime
becomes "client" | "server" | "shared", and the membership tests lose one element each.
This is the one item where behaviour could drift if done carelessly, so its test is explicit: an unmarked function must still appear in both the browser and server modules.
Deprecated re-export shims
packages/compiler/src/parser.ts, tokenizer.ts, and types.ts are two-line files re-exporting
@wrnexus/syntax. They are marked deprecated, but codegen.ts and native-codegen.ts still
import from them, so deleting the files is not enough — those imports must be repointed at
@wrnexus/syntax first. Removing the files without that step breaks the build.
Deprecated auth options
@wrnexus/auth carries nine @deprecated markers. These are our own superseded options, not an
out-of-date dependency — there is no newer version to move to, only newer options we already added.
Removing them means deleting the old aliases and moving the few call sites that still use them.
| Group | Where | Replacement | Still used? |
|---|---|---|---|
onSignedIn / onSignedOut on HTTP route options and plugin options |
http/index.ts, plugin.ts |
the same names on createAuthEngine({ … }) |
only packages/auth/test/http.test.ts and plugin.test.ts |
onSuccessfullSignUp (misspelled alias) |
types.ts |
onSuccessfulSignUp |
nowhere — zero references |
rpId / origin on passkey verification |
engine.ts (4 sites) |
values bound to the issued challenge | packages/auth/test/engine.test.ts |
Two things this table settles:
- The recommended form is already in use.
examples/auth-showcase/app/lib/auth.tspassesonSignedIn/onSignedOuttocreateAuthEngine, which is the current API. The deprecated members are the same names on different option objects, so the example needs no change. - Neither test app uses any of them. Nothing in
D:\Company\wrnexusppsreferences these options.
So the blast radius is three test files inside packages/auth, which are exercising the deprecated
paths and are updated or removed alongside them. The rpId / origin options are already ignored at
runtime — verification uses the values bound to the issued challenge — so removing them changes no
behaviour, only the shape of the call.
Migration
examples/basic-app and both test apps need one pass each:
- Delete
compatibility,functions,compatibilityDate, andframeworkBehaviourfromwrnexus.config.ts— seven lines per app. packages/cli/src/create.tsstops scaffolding those keys, so new apps get a shorter config.
No .wrn source changes. Nothing in this spec alters page syntax.
The removed 0.2.x–0.7.x migrations mean a project below 0.8.0 can no longer be upgraded by
wrnexus update. No such project exists, and rescuing one would be a manual job either way.
Testing
- The
"legacy"runtime removal is behaviour-preserving: an unmarked function still appears in both the browser and server modules. This is the assertion most worth writing, because it is the only removal that could silently change output. - Config rejects the removed keys rather than ignoring them, so a stale config fails loudly with a message naming the key. A silently-ignored key would leave someone believing a flag still applies.
create.tsscaffolds a config without them, asserted against the generated file.wrnexus updatestill runs with the pre-0.8.0migrations gone, and reports correctly for an app already at the current version.examples/basic-appbuilds and its suite passes after its config is trimmed — the end-to-end guard that nothing depended on the removed surface.- The full gate (
bun run check:production) passes, including the editor bundles, which embed the compiler and must be rebuilt afterFunctionRuntimechanges.
What we give up
Deleting compatibilityDate and frameworkBehaviour removes the standard escape hatch for changing
a default after going public — the mechanism that lets an existing app keep old behaviour by pinning
a date. Today it is wired to nothing, so it protects nobody, and an unused mechanism rots rather
than matures. If a gate is needed later it can be reintroduced deliberately, against a real
behaviour change, instead of being carried empty. This is a considered trade rather than a free
deletion.