Repository navigation
Warlock.js 5.37.0
1. Breaking changes
- Document-store rollbacks no longer erase fields by default (
cascade). On MongoDB,
a plaindropColumn()ordropColumns()insidedown()is skipped with a warning
instead of running$unseton every document. Forward drops (up(),drop: [...])
are unchanged. To erase the data on purpose, pass{ destructive: true }. - Add-only alters derive their rollback (
cascade). AMigration.alterthat only adds
columns or fields no longer needs a hand-writtendown(). Delete thatdown()so the
rollback is derived; on SQL it drops the added columns, on document stores it only
updates the ledger. - Rollback and fresh migrations ask first (
core,cascade).warlock migrate --rollback,
warlock migrate --fresh,cascade migrate:rollbackandcascade migrate --freshprint a
destructive-operation summary. Outside development (Warlock's environment forwarlock migrate;
NODE_ENVother thandevelopmentfor thecascadeCLI), a rollback that removes data asks for confirmation and
refuses without a terminal. Scripts and CI jobs must pass--yes(or--force). A rollback
that removes no data does not ask. ProgrammaticrollbackMigrations()andfreshMigrate()
do not prompt; callconfirmRollback()first to get the same guard.
No codemod is needed for 5.37.0. The changes above are manual: review the add-only
down() methods, pass { destructive: true } where erasing data is intended, and add
--yes to deploy scripts.
2. Named API route table controls and guard detection
web.namedApiRoutescontrols what each page embeds.falseembeds no named routes, so
a named-route call throws (in development the error names the option; explicitpathand
methodstill work). An allowlist of route names withprefix.*patterns, or a filter
function(route, { site, guest }), narrows the table.{ include, privatePrefixes }
combines both. Every form can only remove routes from what the page's client code reaches.privatePrefixeskeeps every route under the listed path prefixes (for example
/api/admin/or/api/webhooks/) out of every table, for any viewer and any site.
Prefixes match whole path segments.- Per-site routes. The
sitesoption on a route, or onrouter.group({ sites }), names
the multi-site keys that may offer it in the browser. A route with nositesis shared by
every site, and a route's own value replaces its group's. - Guarded routes for guests. A route with an auth guard in its middleware chain (group
middleware included) is left out of the table for a guest. A viewer counts as a guest only
whenweb.sessionis configured and resolves no user. Withoutweb.sessionthe viewer is
unknown, and guarded routes stay in the table. - Guard detection in route inspection.
warlock inspectand the generatedAGENTS.md
list of mutating routes without an auth guard now count middleware that declares itself a
guard withmarkAuthGuard(), including one registered on arouter.group.warlock check
(WL9006) handles inlinemarkAuthGuard(fn, false)and local guard factories used as
middleware: [requireAdmin()]. - Types and seams. The new option types are exported from
web. The server seam
resolveNamedApiRoutes(names, options)takes{ config, site, guest }.
3. Safer migrations on document stores
- Rollback plan.
migrationRunner.planRollback(batches)lists, per migration, the
collections or tables dropped or truncated and the fields or columns dropped or unset,
with document and row counts. The counts come from new optionalcountRowsand
countWithColumnsdriver methods (MongoDB;countRowson Postgres). - Confirmation.
confirmRollback({ all, batches, yes, development })prints that plan and
asks outside development. The CLI rollback and fresh commands accept--yes/-yand--force. - Guards in hand-written
down().Migration#isDocumentStorelets a migration skip a
SQL-only drop on a document store.
4. warlock release for application deploys
warlock release is a deploy pipeline for applications, configured by a new release
section in warlock.config.ts:
candidatefreezes a clean commit into.warlock/releases/<version>-<shortsha>/with
a manifest (version, commit, time, Node version, lockfile hash).gateruns the named checks in order (default: typecheck, check, test, build) and
stops at the first failure.migrations --proveruns the pending migrations for real and checks the ledger, tables
and indexes against the expected set.stage, thenactivatechecks the staged files, the gate and that the version is
newer than the active one, switchescurrentwith an atomic symlink, keepspreviousand
runs the restart command you configure. Warlock never calls a process manager itself.verifychecks the version and commit reported atrelease.verify.url.rollback
switches back and reruns the restart command; it does not roll migrations back.list,statusandprune --keep <n>(the only command that deletes releases).
Every action accepts --json, and each release keeps a manifest plus a shared release.log.
5. Eval repeats and variance-aware gating (ai)
agent.eval({ repeat })runs every case several times (default1). Results carry pass
counts, per-run results and score and pass-rate intervals. Reports add overall and
per-category statistics (EvalCase.category), plusstableFailuresandflakyCases.
toJUnitshowspassed 2/3 runs.gate: "interval"flags a regression only when the baseline interval lies above the
current one by more thantolerance, so run-to-run noise no longer reads as a regression.
The defaultgate: "point"is unchanged.failOnStableFailuresfails the verdict when a
case failed every run.difftakes{ tolerance, gate, confidence, failOnStableFailures }as its third
argument; a plain tolerance number still works.EvalRegressionaddsregressedMetrics,
stableFailuresandflaky.- Baselines store the repeat count and intervals. Baselines saved by earlier versions
still load, and each of their cases counts as one sample.
6. Fixes and docs
- Validation messages (
core). The default validation translation no longer
returns a message with an unfilled:placeholder. The aliases:lengthand
:matchingInputare mapped, and a translation that still cannot be filled falls back to
seal's message. In development, the first unfillablevalidation.*key logs a warning.
App overrides keep working. - Test processes exit (
logger).FileLog's background flush timer no longer keeps the
process alive, so finished Vitest runs and CLI commands stop waiting on it.flush()and
flushSync()still write buffered entries. - Stylesheet imports in page discovery (
core). The dev loader answers stylesheet imports
(.css,.scss,.sass,.less,.styl) with an empty module, so sitemap discovery no
longer fails withERR_UNKNOWN_FILE_EXTENSIONon a page that imports one. - Hydration mismatch hint (
web). In development, a React hydration mismatch logs the
first mismatching node: its element path, the server text and the client text. Production
builds are unchanged. - Intl guidance (docs). The
create-a-pageandlocalize-pagesskills and the
localization and client/server boundary docs warn thatIntl.DisplayNames,
localeComparesorting and similarIntloutput can differ between Node and browsers and
cause hydration mismatches. They recommend a shared pre-computed table or rendering after
hydration.
7. Verification
Checks ran on Node 22.15, each through a memory- and CPU-capped runner:
- Typecheck and the strictness gate: clean.
- Full test suite: core 4,552 passed, web 3,411 passed, the other packages 5,682
passed, no failures. A new spec spawns Vitest on a fixture app in both test
lifetimes and asserts the process exits on its own. - create-warlock standalone, release-tool and workspace script tests: passed.
- Skill snippet, docs build, format, lint, public text, spec coverage, MDX and skill
reference checks: clean.
The publish run repeats the full family gate (typecheck, tests, build, pack,
local-registry install and the blog app) before anything reaches npm.