@warlock.js/core 5.35.0 → 5.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/esm/cli/commands/migrate.command.mjs +10 -0
- package/esm/cli/commands/migrate.command.mjs.map +1 -1
- package/esm/cli/commands/release/release-command.action.mjs +245 -0
- package/esm/cli/commands/release/release-command.action.mjs.map +1 -0
- package/esm/cli/commands/release.command.mjs +66 -0
- package/esm/cli/commands/release.command.mjs.map +1 -0
- package/esm/cli/framework-cli-commands.mjs +2 -0
- package/esm/cli/framework-cli-commands.mjs.map +1 -1
- package/esm/database/migrate-action.mjs +24 -2
- package/esm/database/migrate-action.mjs.map +1 -1
- package/esm/dev-server/loader/load-hook.d.mts.map +1 -1
- package/esm/dev-server/loader/load-hook.mjs +6 -0
- package/esm/dev-server/loader/load-hook.mjs.map +1 -1
- package/esm/dev-server/module-loader.mjs +1 -1
- package/esm/dev-server/parse-imports.mjs +1 -1
- package/esm/dev-server/type-generator.mjs +1 -1
- package/esm/e2e/cdp.d.mts.map +1 -1
- package/esm/e2e/cdp.mjs +9 -2
- package/esm/e2e/cdp.mjs.map +1 -1
- package/esm/e2e/page.d.mts +9 -1
- package/esm/e2e/page.d.mts.map +1 -1
- package/esm/e2e/page.mjs +45 -7
- package/esm/e2e/page.mjs.map +1 -1
- package/esm/e2e/types.d.mts +18 -0
- package/esm/e2e/types.d.mts.map +1 -1
- package/esm/e2e/types.mjs.map +1 -1
- package/esm/errors/error-codes.d.mts +24 -0
- package/esm/errors/error-codes.d.mts.map +1 -1
- package/esm/errors/error-codes.mjs +24 -0
- package/esm/errors/error-codes.mjs.map +1 -1
- package/esm/generations/features/test.feature.mjs +7 -7
- package/esm/generations/features/test.feature.mjs.map +1 -1
- package/esm/generations/features/web.feature.mjs +3 -3
- package/esm/generations/features/web.feature.mjs.map +1 -1
- package/esm/index.d.mts +6 -3
- package/esm/index.mjs +4 -2
- package/esm/localization/index.mjs +3 -1
- package/esm/localization/translation-usage-graph.d.mts +49 -0
- package/esm/localization/translation-usage-graph.d.mts.map +1 -0
- package/esm/localization/translation-usage-graph.mjs +128 -0
- package/esm/localization/translation-usage-graph.mjs.map +1 -0
- package/esm/localization/translation-usage.d.mts +85 -0
- package/esm/localization/translation-usage.d.mts.map +1 -0
- package/esm/localization/translation-usage.mjs +307 -0
- package/esm/localization/translation-usage.mjs.map +1 -0
- package/esm/production/build-budgets.d.mts +18 -0
- package/esm/production/build-budgets.d.mts.map +1 -1
- package/esm/production/build-budgets.mjs.map +1 -1
- package/esm/production/route-registration-snapshot.mjs +12 -3
- package/esm/production/route-registration-snapshot.mjs.map +1 -1
- package/esm/release/activate.mjs +261 -0
- package/esm/release/activate.mjs.map +1 -0
- package/esm/release/candidate.mjs +107 -0
- package/esm/release/candidate.mjs.map +1 -0
- package/esm/release/cascade-migration-provider.mjs +32 -0
- package/esm/release/cascade-migration-provider.mjs.map +1 -0
- package/esm/release/config.mjs +60 -0
- package/esm/release/config.mjs.map +1 -0
- package/esm/release/gate.mjs +67 -0
- package/esm/release/gate.mjs.map +1 -0
- package/esm/release/migrations-proof.mjs +100 -0
- package/esm/release/migrations-proof.mjs.map +1 -0
- package/esm/release/prune.mjs +42 -0
- package/esm/release/prune.mjs.map +1 -0
- package/esm/release/report.mjs +25 -0
- package/esm/release/report.mjs.map +1 -0
- package/esm/release/runner.mjs +55 -0
- package/esm/release/runner.mjs.map +1 -0
- package/esm/release/semver.mjs +53 -0
- package/esm/release/semver.mjs.map +1 -0
- package/esm/release/stage.mjs +122 -0
- package/esm/release/stage.mjs.map +1 -0
- package/esm/release/store.mjs +131 -0
- package/esm/release/store.mjs.map +1 -0
- package/esm/release/types.d.mts +102 -0
- package/esm/release/types.d.mts.map +1 -0
- package/esm/release/verify.mjs +94 -0
- package/esm/release/verify.mjs.map +1 -0
- package/esm/router/index.d.mts +2 -2
- package/esm/router/index.mjs +2 -2
- package/esm/router/router.d.mts +4 -0
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs +14 -3
- package/esm/router/router.mjs.map +1 -1
- package/esm/router/types.d.mts +45 -0
- package/esm/router/types.d.mts.map +1 -1
- package/esm/router/unguarded-route-diagnostics.d.mts +6 -0
- package/esm/router/unguarded-route-diagnostics.d.mts.map +1 -1
- package/esm/router/unguarded-route-diagnostics.mjs +10 -2
- package/esm/router/unguarded-route-diagnostics.mjs.map +1 -1
- package/esm/tooling/check/api-route-references.d.mts +16 -0
- package/esm/tooling/check/api-route-references.d.mts.map +1 -0
- package/esm/tooling/check/api-route-references.mjs +146 -0
- package/esm/tooling/check/api-route-references.mjs.map +1 -0
- package/esm/tooling/check/conventions-step.d.mts +6 -3
- package/esm/tooling/check/conventions-step.d.mts.map +1 -1
- package/esm/tooling/check/conventions-step.mjs +19 -10
- package/esm/tooling/check/conventions-step.mjs.map +1 -1
- package/esm/tooling/check/run-check.d.mts.map +1 -1
- package/esm/tooling/check/run-check.mjs +4 -2
- package/esm/tooling/check/run-check.mjs.map +1 -1
- package/esm/tooling/check/security-conventions.mjs +19 -1
- package/esm/tooling/check/security-conventions.mjs.map +1 -1
- package/esm/tooling/check/translation-conventions.mjs +32 -0
- package/esm/tooling/check/translation-conventions.mjs.map +1 -0
- package/esm/tooling/inspect/collect-files.mjs +1 -1
- package/esm/tooling/inspect/collect-routes.mjs +3 -2
- package/esm/tooling/inspect/collect-routes.mjs.map +1 -1
- package/esm/upgrade/codemods/index.mjs +2 -0
- package/esm/upgrade/codemods/index.mjs.map +1 -1
- package/esm/upgrade/codemods/vite-vitest.mjs +170 -0
- package/esm/upgrade/codemods/vite-vitest.mjs.map +1 -0
- package/esm/upgrade/notes.mjs +4 -0
- package/esm/upgrade/notes.mjs.map +1 -1
- package/esm/validation/init.mjs +11 -4
- package/esm/validation/init.mjs.map +1 -1
- package/esm/validation/translate-rule.mjs +63 -0
- package/esm/validation/translate-rule.mjs.map +1 -0
- package/esm/warlock-config/index.d.mts +3 -2
- package/esm/warlock-config/types.d.mts +8 -0
- package/esm/warlock-config/types.d.mts.map +1 -1
- package/llms-full.txt +221 -18
- package/llms.txt +1 -0
- package/package.json +13 -13
- package/skills/deploy-release/SKILL.md +76 -0
- package/skills/index.md +1 -1
- package/skills/register-route/SKILL.md +25 -0
- package/skills/test-service/SKILL.md +8 -8
- package/skills/upgrade-from-5-29/SKILL.md +91 -0
- package/skills/use-localization/SKILL.md +4 -0
- package/skills/warlock-check/SKILL.md +11 -8
- package/skills/warlock-conventions/SKILL.md +1 -1
package/llms-full.txt
CHANGED
|
@@ -30,7 +30,7 @@ When the framework is wrong or missing a capability, follow the `report-framewor
|
|
|
30
30
|
- **HTTP & routing:** register-route, warlock-routes, create-controller, use-middleware, write-middleware, validate-input, use-request-locals, build-restful, wire-socket
|
|
31
31
|
- **Responses & resources:** send-response, define-resource, use-model-transformers, use-localization
|
|
32
32
|
- **Domain layers:** write-use-case, use-repository, write-seeder
|
|
33
|
-
- **CLI & running the app:** generate-web-ui, run-app, write-cli-command, add-connector, health-checks, warlock-doctor, warlock-inspect, warlock-check, generate-openapi
|
|
33
|
+
- **CLI & running the app:** generate-web-ui, run-app, deploy-release, write-cli-command, add-connector, health-checks, warlock-doctor, warlock-inspect, warlock-check, generate-openapi
|
|
34
34
|
- **Agent tooling:** warlock-mcp, warlock-trace, warlock-agents-md, warlock-board
|
|
35
35
|
- **Storage, files & mail:** store-file, upload-file, process-image, send-mail
|
|
36
36
|
- **Security & utilities:** hash-password, encrypt-data, store-secrets, retry-operation, benchmark-code, request-memo, request-tracing
|
|
@@ -2487,6 +2487,86 @@ export const CommentResource = defineResource({
|
|
|
2487
2487
|
- The `create-controller` topic — calling `new XResource(record).toJSON()` from a controller.
|
|
2488
2488
|
|
|
2489
2489
|
|
|
2490
|
+
## deploy-release `@warlock.js/core/skills/deploy-release/SKILL.md`
|
|
2491
|
+
|
|
2492
|
+
---
|
|
2493
|
+
name: deploy-release
|
|
2494
|
+
description: 'Deploy a Warlock application with `warlock release` — freeze a clean commit as a candidate, run the gate, prove migrations, stage the build, switch `current` by atomic symlink, verify the health URL and roll back, configured by the `release` section of `warlock.config.ts`. Triggers: `warlock release`, `release candidate`, `release gate`, `release migrations --prove`, `release stage`, `release activate`, `release verify`, `release rollback`, `release prune`, `ReleaseConfig`, `release.restart.command`, `release.verify`, `.warlock/releases`; "deploy my app", "zero-downtime deploy", "roll back a deploy", "atomic release", "release gate", "prove migrations before deploy". Skip: the `/health` and `/ready` endpoints themselves — the `health-checks` topic; generating a PM2 ecosystem file — `warlock pm2` (the `run-app` topic); publishing the framework packages — `releasing-warlock-monorepo`; competing tools Capistrano, Deployer, hand-rolled symlink scripts.'
|
|
2495
|
+
---
|
|
2496
|
+
|
|
2497
|
+
# Warlock — deploy with `warlock release`
|
|
2498
|
+
|
|
2499
|
+
A recorded, step-by-step deploy for one host. Each step is a command; each writes into a per-release `manifest.json` and a shared `release.log`.
|
|
2500
|
+
|
|
2501
|
+
```text
|
|
2502
|
+
candidate -> gate -> migrations --prove -> stage -> activate -> verify
|
|
2503
|
+
\-> rollback
|
|
2504
|
+
```
|
|
2505
|
+
|
|
2506
|
+
Warlock never calls PM2, systemd or any process manager. The only restart is the command you set in `release.restart.command`.
|
|
2507
|
+
|
|
2508
|
+
## Configure
|
|
2509
|
+
|
|
2510
|
+
```ts title="warlock.config.ts"
|
|
2511
|
+
import { defineConfig } from "@warlock.js/core";
|
|
2512
|
+
|
|
2513
|
+
export default defineConfig({
|
|
2514
|
+
release: {
|
|
2515
|
+
gate: {
|
|
2516
|
+
checks: [
|
|
2517
|
+
{ name: "typecheck", command: "npx warlock typecheck" },
|
|
2518
|
+
{ name: "test", command: "npx warlock test" },
|
|
2519
|
+
{ name: "build", command: "NODE_ENV=production npx warlock build" },
|
|
2520
|
+
],
|
|
2521
|
+
},
|
|
2522
|
+
restart: { command: "pm2 reload my-app" },
|
|
2523
|
+
verify: { url: "http://127.0.0.1:2030/health" },
|
|
2524
|
+
},
|
|
2525
|
+
});
|
|
2526
|
+
```
|
|
2527
|
+
|
|
2528
|
+
Defaults: `directory` `.warlock`, `distDir` `dist`, gate = typecheck, check, test, build. Add `.warlock` to `.gitignore`.
|
|
2529
|
+
|
|
2530
|
+
## Run the steps
|
|
2531
|
+
|
|
2532
|
+
```bash
|
|
2533
|
+
npx warlock release candidate [--commit <sha>] [--allow-dirty]
|
|
2534
|
+
npx warlock release gate
|
|
2535
|
+
npx warlock release migrations --prove --target staging
|
|
2536
|
+
npx warlock release stage
|
|
2537
|
+
npx warlock release activate --preflight
|
|
2538
|
+
npx warlock release activate [--dry-run] [--allow-same]
|
|
2539
|
+
npx warlock release verify || npx warlock release rollback
|
|
2540
|
+
```
|
|
2541
|
+
|
|
2542
|
+
Every command takes an optional release id (`release gate 1.4.0-a1b2c3d`); without one it uses the newest candidate, gated or staged release. `--json` prints the result as JSON; a failed step exits 1.
|
|
2543
|
+
|
|
2544
|
+
- **candidate** freezes the commit with `git archive` into `.warlock/releases/<version>-<shortsha>/source`. Refuses a dirty tree without `--allow-dirty` and never overwrites a release.
|
|
2545
|
+
- **gate** runs the checks in order in the frozen source and stops at the first failure; results go into the manifest.
|
|
2546
|
+
- **migrations** without `--prove` only lists the pending set. With `--prove` it snapshots the ledger, tables and indexes, runs the pending migrations, snapshots again and compares with the expected set (`release.migrations.expected`, default the pending set; `expectedTables`). The migrations really run: use `--target` (a `release.migrations.targets` entry whose `env` is applied before the database connects) on a database that is safe to migrate. A failed proof blocks activation.
|
|
2547
|
+
- **stage** (needs a passing gate) copies `dist`, `package.json`, the lockfile, `release.include` entries and `release.json` into `staged/`, atomically.
|
|
2548
|
+
- **activate** needs staged artefacts, a passed gate (and proof), and a version strictly greater than the active one (semver; `--allow-same` accepts an equal version, a lower one is refused). It switches `current -> releases/<id>` with an atomic rename, keeps the old target as `previous`, then runs the restart command with `WARLOCK_RELEASE_ID`, `WARLOCK_RELEASE_DIR` and `WARLOCK_RELEASE_CURRENT` set. If that command fails, the pointer stays switched and the exit code is 1.
|
|
2549
|
+
- **verify** GETs `release.verify.url` with retries and a timeout and checks the status plus the `version` and `commit` fields of the JSON body against the active release (`verify.versionPath`, `commitPath` and `match` adjust that). Serve the `release.json` values from your health endpoint.
|
|
2550
|
+
- **rollback** switches `current` back to `previous` (and `previous` to the release just left), reruns the restart command. It does not roll migrations back: a migration may have destroyed data no reverse step restores; use `warlock migrate --rollback` deliberately if needed.
|
|
2551
|
+
- **list** and **status** show releases and the active one; **prune --keep <n>** is the only command that deletes releases and always keeps `current` and `previous`.
|
|
2552
|
+
|
|
2553
|
+
## Layout
|
|
2554
|
+
|
|
2555
|
+
```text
|
|
2556
|
+
.warlock/current -> releases/<id> .warlock/previous -> releases/<id>
|
|
2557
|
+
.warlock/release.log
|
|
2558
|
+
.warlock/releases/<id>/{manifest.json, source/, staged/}
|
|
2559
|
+
```
|
|
2560
|
+
|
|
2561
|
+
Run the app from `.warlock/current/staged` so a restart follows the pointer.
|
|
2562
|
+
|
|
2563
|
+
## Pitfalls
|
|
2564
|
+
|
|
2565
|
+
- Run `activate --dry-run` or `--preflight` first in CI; both change nothing.
|
|
2566
|
+
- Without `restart.command` activation only moves the symlink; the running process keeps serving the old code.
|
|
2567
|
+
- `verify` fails when the response does not report a version; set `verify.match` to `[]` to check only the status.
|
|
2568
|
+
|
|
2569
|
+
|
|
2490
2570
|
## encrypt-data `@warlock.js/core/skills/encrypt-data/SKILL.md`
|
|
2491
2571
|
|
|
2492
2572
|
---
|
|
@@ -4014,6 +4094,31 @@ router.get("/webhooks/status", webhookStatusController, { rateLimit: false });
|
|
|
4014
4094
|
- The `send-response` topic — picking the right `response.<helper>()`.
|
|
4015
4095
|
- The `warlock-conventions` topic — module layout, the `guarded` convention, path aliases.
|
|
4016
4096
|
|
|
4097
|
+
## A named route the browser must never call — `clientVisible: false`
|
|
4098
|
+
|
|
4099
|
+
A named API route is embedded in a page's browser route table only when that page's client code reaches its name. Mark an endpoint that browser code must never reach with `clientVisible: false`:
|
|
4100
|
+
|
|
4101
|
+
```ts
|
|
4102
|
+
router.post("/internal/reindex", reindexController, {
|
|
4103
|
+
name: "internal.reindex",
|
|
4104
|
+
clientVisible: false,
|
|
4105
|
+
});
|
|
4106
|
+
```
|
|
4107
|
+
|
|
4108
|
+
It is then never embedded in a page, and `warlock check` reports a browser reference to its name as `WL9020` (an error). The server still authorizes the call as for any route.
|
|
4109
|
+
|
|
4110
|
+
### Offer a route to some sites only — `sites`
|
|
4111
|
+
|
|
4112
|
+
In a multi-site app a route answers on every host, and a page of any site may embed it in its browser table. Declare the sites that may embed it on the route or on a group; a route's own `sites` replaces its group's, and a route with none is shared:
|
|
4113
|
+
|
|
4114
|
+
```ts
|
|
4115
|
+
router.group({ prefix: "/api/admin", sites: ["admin"] }, () => {
|
|
4116
|
+
router.post("/users", createUser, { name: "admin.users.create" });
|
|
4117
|
+
});
|
|
4118
|
+
```
|
|
4119
|
+
|
|
4120
|
+
This limits the browser table only; keep the route's guard. A guard in the route's middleware chain (group middleware included) also keeps it out of a guest's table, and a custom guard must say so with `markAuthGuard(fn)` unless its name contains `auth` or `guard`. `warlock inspect`, `warlock agents-md` and `WL9006` read the same chain, so a guard declared once on a group guards every route inside it.
|
|
4121
|
+
|
|
4017
4122
|
|
|
4018
4123
|
## report-framework-issue `@warlock.js/core/skills/report-framework-issue/SKILL.md`
|
|
4019
4124
|
|
|
@@ -8505,7 +8610,7 @@ and has no Warlock setup or HTTP global setup. `integration` discovers
|
|
|
8505
8610
|
It uses the framework setup, HTTP global setup and a generated `src/test-runner.ts`.
|
|
8506
8611
|
Keep pure service specs beside their source and mock repositories at the boundary.
|
|
8507
8612
|
Existing config files are preserved; copy the projects layout into them manually.
|
|
8508
|
-
Vitest
|
|
8613
|
+
Vitest 5 or newer is required for the generated runner (Vitest 5 removed `vitest/runners`; the runner extends `TestRunner` from `vitest`).
|
|
8509
8614
|
|
|
8510
8615
|
For a unit spec that needs configuration values, use:
|
|
8511
8616
|
|
|
@@ -8553,20 +8658,20 @@ Run with `npx vitest run --project integration`. Worker lifetime refuses watch,
|
|
|
8553
8658
|
threads, isolated module graphs, or a missing runner with a message explaining
|
|
8554
8659
|
the required settings. Keep `afterAll(teardownTest)` in the setup file: in worker
|
|
8555
8660
|
mode it marks the end of the file; Vitest's awaited worker cleanup closes the
|
|
8556
|
-
runtime once. The runner uses `
|
|
8557
|
-
in Vitest
|
|
8661
|
+
runtime once. The runner uses `TestRunner.onCleanupWorkerContext`, verified
|
|
8662
|
+
in Vitest 5.0.3's `TestRunner` declaration, runner implementation and worker cleanup source.
|
|
8558
8663
|
For an existing project, generate this runner without overwriting your config:
|
|
8559
8664
|
|
|
8560
8665
|
```ts title="src/test-runner.ts"
|
|
8561
8666
|
import { configureTestWorker } from "@warlock.js/core/tests";
|
|
8562
|
-
import {
|
|
8667
|
+
import { TestRunner } from "vitest";
|
|
8563
8668
|
|
|
8564
|
-
export default class WarlockTestRunner extends
|
|
8565
|
-
public constructor(config: ConstructorParameters<typeof
|
|
8669
|
+
export default class WarlockTestRunner extends TestRunner {
|
|
8670
|
+
public constructor(config: ConstructorParameters<typeof TestRunner>[0]) {
|
|
8566
8671
|
super(config);
|
|
8567
8672
|
configureTestWorker(
|
|
8568
8673
|
{ pool: this.pool, watch: config.watch, isolate: config.isolate },
|
|
8569
|
-
(cleanup) => this.onCleanupWorkerContext(cleanup),
|
|
8674
|
+
(cleanup) => this.onCleanupWorkerContext?.(cleanup),
|
|
8570
8675
|
);
|
|
8571
8676
|
}
|
|
8572
8677
|
}
|
|
@@ -8585,7 +8690,7 @@ This trades module isolation for startup cost: application singletons, config
|
|
|
8585
8690
|
mutations, mocks and module side effects can survive between files. Reset your
|
|
8586
8691
|
own state in `resetBetweenFiles`, restore mocks, and keep connector selection
|
|
8587
8692
|
consistent. Give each worker a distinct database/schema using
|
|
8588
|
-
`process.env.VITEST_POOL_ID` when preparing test connections. Never drop a shared
|
|
8693
|
+
`process.env.VITEST_POOL_ID` (1-based since Vitest 5) when preparing test connections. Never drop a shared
|
|
8589
8694
|
database from this hook while other workers are using it. The framework does not
|
|
8590
8695
|
create a memory replica set or a cross-worker lock; those belong to the application's
|
|
8591
8696
|
global setup. A shared HTTP server and a single replica set can contend on I/O,
|
|
@@ -9360,6 +9465,97 @@ one `#vessel` around `{children}`. Client navigation still swaps layout/page
|
|
|
9360
9465
|
children while preserving the document root. Remove temporary explicit form IDs
|
|
9361
9466
|
only when the application does not otherwise depend on those names.
|
|
9362
9467
|
|
|
9468
|
+
## 5.35 to 5.36 per-page API routes
|
|
9469
|
+
|
|
9470
|
+
A page document now carries only the named API routes its client code can reach. Before 5.36 every page carried every named route, so a browser call to a name the build cannot see worked on every page by accident. Check each `apiHref()` and `useSubmitForm({ route })` call:
|
|
9471
|
+
|
|
9472
|
+
- A name written as a string literal, a constant or a template with a static prefix is found automatically.
|
|
9473
|
+
- A name computed at run time, or taken from a module the build cannot follow, now throws on the page that calls it. Declare the names in that page's setup file, for example `export const config = { apiRoutes: ["orders.*"] };`, a layout or root `config` covers every page that renders it. Or call the endpoint with an explicit `path` and `method`.
|
|
9474
|
+
- `npx warlock check` reports the call sites that need a declaration as `WL9019`.
|
|
9475
|
+
|
|
9476
|
+
Routes registered with `clientVisible: false` are never embedded, and a browser call to one is an error (`WL9020`). Keep endpoints the browser must not reach on that option. Authorization is unchanged: the server still decides each request.
|
|
9477
|
+
|
|
9478
|
+
To measure a page's inline payload, set `build.budgets.payload` (default `64kb`). An over-budget route logs one line per process and never fails a request.
|
|
9479
|
+
|
|
9480
|
+
## 5.35 to 5.36 translation payloads
|
|
9481
|
+
|
|
9482
|
+
A page's hydration payload now carries only the translations its client code
|
|
9483
|
+
can reach, in route-dictionary (`locales.json`) and global-registry (`extend()`)
|
|
9484
|
+
apps alike. The groups every page uses (default `app` and `common`) arrive once
|
|
9485
|
+
per locale in a hashed, cached file that the page preloads. Nothing in a page's
|
|
9486
|
+
render changes: server output is the full dictionary, as before.
|
|
9487
|
+
|
|
9488
|
+
Run `warlock build`, then `warlock check`. A `WL9104` warning marks a page that
|
|
9489
|
+
renders a translation key computed at runtime. Declare the keys it renders in
|
|
9490
|
+
the page's setup file, for example
|
|
9491
|
+
`export const config = { translations: ["statuses.*", "members.title"] }`, so the
|
|
9492
|
+
page ships only what it uses.
|
|
9493
|
+
|
|
9494
|
+
In development, a warning names a key the page reads but its payload lacks, and
|
|
9495
|
+
resolves it from the full table so the page keeps working. In production the
|
|
9496
|
+
key renders as its own text and the miss is reported once through
|
|
9497
|
+
`onClientError` with `kind: "translations"`. Declare the key, or set
|
|
9498
|
+
`build: { translations: { prune: false } }` in the web connector options while migrating to
|
|
9499
|
+
restore the previous per-page payload.
|
|
9500
|
+
|
|
9501
|
+
Global-registry apps are covered too. Their shared table is served at
|
|
9502
|
+
`/__warlock/translations/<locale>.<hash>.json`, which is cached immutable in
|
|
9503
|
+
production. A direct `trans()` call from `@mongez/localization` is not checked
|
|
9504
|
+
for a pruning miss; the calls through `useTrans()` are.
|
|
9505
|
+
|
|
9506
|
+
## 5.35 to 5.36 Vite 8 and Vitest 5
|
|
9507
|
+
|
|
9508
|
+
`@warlock.js/web` requires Vite 8 (`^8.0.0`) and `@vitejs/plugin-react` 6, and the
|
|
9509
|
+
test kit requires Vitest 5. Vite 8 bundles with Rolldown and transforms
|
|
9510
|
+
TypeScript with Oxc. Vite 7 is not supported; plugin-react 6 needs Vite 8, so the
|
|
9511
|
+
family moves together. Vite 8 needs Node.js 20.19 or 22.12 and newer, Vitest 5
|
|
9512
|
+
needs 22.12 or newer; Warlock already requires 22.15.
|
|
9513
|
+
|
|
9514
|
+
```bash
|
|
9515
|
+
npx warlock upgrade --from 5.35 --to 5.36.0 --dry-run
|
|
9516
|
+
npx warlock upgrade --from 5.35 --to 5.36.0
|
|
9517
|
+
npm install
|
|
9518
|
+
npx warlock check
|
|
9519
|
+
```
|
|
9520
|
+
|
|
9521
|
+
The `vite-8-vitest-5` codemod:
|
|
9522
|
+
|
|
9523
|
+
- raises `vite` (8.3.3), `vitest` and `@vitest/*` (5.0.3) and `@vitejs/plugin-react`
|
|
9524
|
+
(6.1.2) in `dependencies`, `devDependencies`, `overrides` and `resolutions`,
|
|
9525
|
+
keeping `^`, `~` or exact style;
|
|
9526
|
+
- rewrites a generated `src/test-runner.ts` from `VitestTestRunner` in
|
|
9527
|
+
`vitest/runners` to `TestRunner` from `vitest`, and calls
|
|
9528
|
+
`this.onCleanupWorkerContext?.(cleanup)` (the hook is absent in vm pools);
|
|
9529
|
+
- lists, without editing, imports of the removed `vitest/coverage`,
|
|
9530
|
+
`vitest/reporters`, `vitest/environments`, `vitest/snapshot`, `vitest/suite`,
|
|
9531
|
+
`vitest/mocker` and `vitest/internal/module-runner` entry points, other names
|
|
9532
|
+
imported from `vitest/runners`, a top-level `esbuild` option in a Vite or
|
|
9533
|
+
Vitest config, and object-form `output.manualChunks`.
|
|
9534
|
+
|
|
9535
|
+
Do these by hand when the codemod lists them:
|
|
9536
|
+
|
|
9537
|
+
- Move a config's `esbuild: { jsx: "automatic" }` to
|
|
9538
|
+
`oxc: { jsx: { runtime: "automatic" } }`. `esbuild.target` is not converted, and
|
|
9539
|
+
Oxc does not lower native decorators: keep `lowerStage3Decorators()` from
|
|
9540
|
+
`@warlock.js/core/vite` first in `plugins` for decorated modules, as in the
|
|
9541
|
+
generated test config.
|
|
9542
|
+
- Replace object `manualChunks` with
|
|
9543
|
+
`build.rolldownOptions.output.codeSplitting.groups`; `build.rollupOptions` still
|
|
9544
|
+
works but is deprecated in favour of `build.rolldownOptions`.
|
|
9545
|
+
- A custom bundler `external` list that names Node builtins is still refused by the
|
|
9546
|
+
client build.
|
|
9547
|
+
|
|
9548
|
+
Vitest 5 changes defaults that can alter a green suite: `clearMocks` is true,
|
|
9549
|
+
`json` and `junit` reporters write to `.vitest/` instead of stdout, `-t` joins a
|
|
9550
|
+
test's names with `>`, `VITEST_POOL_ID` and `VITEST_WORKER_ID` start at 1, nested
|
|
9551
|
+
`vi.mock`/`vi.hoisted` calls throw, and an unawaited `resolves` or `rejects`
|
|
9552
|
+
assertion fails the test. Vitest also no longer searches parent directories for a
|
|
9553
|
+
config file; run it from the package that owns the config. Official guides:
|
|
9554
|
+
https://vite.dev/guide/migration and https://vitest.dev/guide/migration.
|
|
9555
|
+
|
|
9556
|
+
A project that uses `@warlock.js/ai`'s custom matchers needs Vitest 5 for their
|
|
9557
|
+
TypeScript types.
|
|
9558
|
+
|
|
9363
9559
|
|
|
9364
9560
|
## upload-file `@warlock.js/core/skills/upload-file/SKILL.md`
|
|
9365
9561
|
|
|
@@ -10347,6 +10543,10 @@ workers, and registered module cleanup hooks, including after partial startup.
|
|
|
10347
10543
|
A Node loader registration is process-scoped; its message channel is closed at
|
|
10348
10544
|
teardown. Module-owned timers/listeners must register their cleanup hooks.
|
|
10349
10545
|
|
|
10546
|
+
## Translation keys in pages (5.36)
|
|
10547
|
+
|
|
10548
|
+
`warlock check` reports `WL9104` when a page renders a translation key computed at runtime (`trans(key)` with no static prefix, or a translator passed on as a value). Such a page ships every translation group. Declare its keys in the page's setup file, `export const config = { translations: ["statuses.*", "members.title"] }`, to ship only what it uses. The `web` skill `localize-pages` covers the browser side.
|
|
10549
|
+
|
|
10350
10550
|
|
|
10351
10551
|
## use-middleware `@warlock.js/core/skills/use-middleware/SKILL.md`
|
|
10352
10552
|
|
|
@@ -12214,14 +12414,17 @@ and work through the manual-review list. Use `--lint=oxc` for new apps.
|
|
|
12214
12414
|
The following codes default to **warnings**, so upgrading does not fail existing projects.
|
|
12215
12415
|
They parse real TypeScript/JSX syntax with the same oxc-parser adapter as the existing conventions.
|
|
12216
12416
|
|
|
12217
|
-
| Code | Convention
|
|
12218
|
-
| -------- |
|
|
12219
|
-
| `WL9013` | A component file exceeds 150 lines
|
|
12220
|
-
| `WL9014` | More than one component in a file
|
|
12221
|
-
| `WL9015` | State/effects or event-handler functions inside a component
|
|
12222
|
-
| `WL9016` | Native select, checkbox, radio, date, time or datetime-local input
|
|
12223
|
-
| `WL9017` | DOM input/textarea/select native validation
|
|
12224
|
-
| `WL9018` | Exported `RequestHandler` in `src/app/**/controllers` lacks `responseSchema`
|
|
12417
|
+
| Code | Convention | Remedy |
|
|
12418
|
+
| -------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
12419
|
+
| `WL9013` | A component file exceeds 150 lines | Split components and extract hooks; configure `componentLineLimit`. |
|
|
12420
|
+
| `WL9014` | More than one component in a file | Move each component into its own TSX file. |
|
|
12421
|
+
| `WL9015` | State/effects or event-handler functions inside a component | Move `useState`, `useReducer`, `useEffect`, `useLayoutEffect` and handlers into `use-*.ts` hooks. |
|
|
12422
|
+
| `WL9016` | Native select, checkbox, radio, date, time or datetime-local input | Use the design-system control. Native controls are allowed in `src/web/design-system/**`. |
|
|
12423
|
+
| `WL9017` | DOM input/textarea/select native validation | Use shared schema validation instead of `required`, `pattern`, `min`, `max`, `minLength`, `maxLength` or input `type="email"`. Custom component props are allowed. |
|
|
12424
|
+
| `WL9018` | Exported `RequestHandler` in `src/app/**/controllers` lacks `responseSchema` | Assign `handler.responseSchema` to document each response status. |
|
|
12425
|
+
| `WL9019` | Browser route call (`apiHref`, `useSubmitForm({ route })`) takes a run-time name | Pass a string literal or a template with a static prefix, or declare the names with `config.apiRoutes` in a page setup file. |
|
|
12426
|
+
| `WL9020` | Browser code names a route registered with `clientVisible: false` (error) | Call the endpoint from the server, or remove `clientVisible: false` from the route. |
|
|
12427
|
+
| `WL9104` | Translation key computed at runtime in `src/web` (the page ships every group) | Declare the keys with `config.translations` in the page's setup file, for example `["statuses.*"]`. |
|
|
12225
12428
|
|
|
12226
12429
|
Web rules apply to `src/web/**/*.tsx`. Components are top-level JSX-returning functions
|
|
12227
12430
|
with a capitalized name (or an anonymous default export). Hook files and ordinary helpers
|
|
@@ -12471,7 +12674,7 @@ router.post("/posts", createPostController, { middleware: [authMiddleware("user"
|
|
|
12471
12674
|
router.post("/contact", contactController, { public: true });
|
|
12472
12675
|
```
|
|
12473
12676
|
|
|
12474
|
-
A route inside `guarded(() => …)` or `router.group({ middleware: [authMiddleware()] }, …)` is guarded. A middleware counts as a guard when its name contains `auth` or `guard`, or when it is a constant initialised with `markAuthGuard()` in the same file. `warlock dev` warns about the same routes at runtime.
|
|
12677
|
+
A route inside `guarded(() => …)` or `router.group({ middleware: [authMiddleware()] }, …)` is guarded. A middleware counts as a guard when its name contains `auth` or `guard`, or when it is a constant initialised with `markAuthGuard()` in the same file, or a local factory that returns `markAuthGuard(fn)` and is called in the `middleware` list. `warlock dev` warns about the same routes at runtime.
|
|
12475
12678
|
|
|
12476
12679
|
### `WL9007` — safe redirects
|
|
12477
12680
|
|
package/llms.txt
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
- [create-controller](@warlock.js/core/skills/create-controller/SKILL.md): Author HTTP controllers in @warlock.js/core — RequestHandler signature, validated input via seal schemas, response helpers, attaching metadata. Controllers are thin functions; business logic moves to services or use-cases. Triggers: `RequestHandler`, `Request<TSchema>`, `GuardedRequestHandler`, `request.validated`, `request.signal`, `request.input`, `controller.validation`, `controller.responseSchema`, `response.success`, `response.successCreate`; "write a controller", "attach a schema to a handler", "thin controller pattern", "guarded request type", "type the API response"; typical import `import { type RequestHandler } from "@warlock.js/core"`. Skip: response helper menu — the `send-response` topic; schema authoring — the `validate-input` topic; URL wiring — the `register-route` topic; competing patterns: `express` middleware functions, `@nestjs/common` `@Controller`/`@Get` decorators.
|
|
17
17
|
- [create-module](@warlock.js/core/skills/create-module/SKILL.md): Scaffold a new feature module under `src/app/<name>/` via `warlock generate.module` and the follow-up generators for controllers, models, repositories, resources, and validation schemas. Triggers: `warlock generate.module`, `generate.controller`, `generate.service`, `generate.model`, `generate.repository`, `generate.resource`, `generate.migration`, `--minimal`, `gen.m`; "scaffold a new module", "create CRUD bootstrap", "add a controller to a module", "generate a model"; typical CLI `npx warlock generate.module <name>`. Skip: framework-wide layout rules — the `warlock-conventions` topic; routes file shape — the `register-route` topic; controller shape — the `create-controller` topic; competing tooling: `@nestjs/cli`, `hygen`, hand-rolled folder layouts.
|
|
18
18
|
- [define-resource](@warlock.js/core/skills/define-resource/SKILL.md): Map model fields to wire-shape via `defineResource()` or `Resource` subclasses. Output-only — never put business logic, hydration, or reconciliation in a resource. Also covers the typed output: `ResourceOutput<typeof R>`, `defineResource<Out>()`, `Serialized<T, W>` and `ModelResourceRegistry`. Triggers: `defineResource`, `Resource`, `RegisterResource`, `toJSON`, `ResourceOutput`, `Serialized`, `ModelResourceRegistry`, `"self"`, `"localized"`, `"uploadsUrl"`; "shape an API response", "type a resource's output", "what does the client receive", "nest related resources", "rename a field on output", "self-referential tree resource"; typical import `import { defineResource } from "@warlock.js/core"`. Skip: localized columns — the `use-localization` topic; URL casting — the `build-url` topic; controller side — the `create-controller` topic; competing libs `@nestjs/swagger` `@ApiProperty`, `class-transformer`, hand-rolled DTO mappers.
|
|
19
|
+
- [deploy-release](@warlock.js/core/skills/deploy-release/SKILL.md): Deploy a Warlock application with `warlock release` — freeze a clean commit as a candidate, run the gate, prove migrations, stage the build, switch `current` by atomic symlink, verify the health URL and roll back, configured by the `release` section of `warlock.config.ts`. Triggers: `warlock release`, `release candidate`, `release gate`, `release migrations --prove`, `release stage`, `release activate`, `release verify`, `release rollback`, `release prune`, `ReleaseConfig`, `release.restart.command`, `release.verify`, `.warlock/releases`; "deploy my app", "zero-downtime deploy", "roll back a deploy", "atomic release", "release gate", "prove migrations before deploy". Skip: the `/health` and `/ready` endpoints themselves — the `health-checks` topic; generating a PM2 ecosystem file — `warlock pm2` (the `run-app` topic); publishing the framework packages — `releasing-warlock-monorepo`; competing tools Capistrano, Deployer, hand-rolled symlink scripts.
|
|
19
20
|
- [encrypt-data](@warlock.js/core/skills/encrypt-data/SKILL.md): Reversible AES-256-GCM `encrypt` / `decrypt` for secrets you need to read back; one-way HMAC-SHA256 `hmacHash` for deterministic fingerprints (lookup/dedup of encrypted columns). Keys come from `src/config/encryption.ts`. Triggers: `encrypt`, `decrypt`, `hmacHash`, `EncryptionConfigurations`, `APP_ENCRYPTION_KEY`, `APP_HMAC_KEY`; "store an API key reversibly", "fingerprint an encrypted column for lookup", "AES-256-GCM secret", "HMAC-SHA256 dedup key"; typical import `import { encrypt, decrypt, hmacHash } from "@warlock.js/core"`. Skip: password hashing — the `hash-password` topic; config wiring — the `configure-app` topic; competing libs Node `crypto` direct, `crypto-js`, `libsodium-wrappers`.
|
|
20
21
|
- [generate-openapi](@warlock.js/core/skills/generate-openapi/SKILL.md): Generate an OpenAPI 3.1 document from the registered routes with `warlock generate.openapi` (default `storage/openapi/openapi.json`), write a Postman collection from it with `warlock generate.postman` (default `storage/postman/collection.json`), and read it live in the dev-only API docs page at `/__warlock/docs`. Documents paths, path/query parameters, request bodies from Seal validation, responses from `handler.responseSchema`, 401/422 and bearer/cookie security from `authMiddleware()`. Triggers: `generate.openapi`, `openApiGeneratorCommand`, `buildOpenApiDocument`, `getDevelopmentOpenApiDocument`, `generate.postman`, `openApiToPostmanCollection`, `getDevelopmentPostmanCollection`, `responseSchema`, `--include-pages`, `/__warlock/docs`; "OpenAPI", "Swagger", "API docs", "import my API into Postman or Insomnia", "document my routes". Skip: declaring response types on a handler — the `create-controller` topic; the devtools dashboard — the `warlock-js-devtools` skill; listing routes — the `warlock-routes` topic.
|
|
21
22
|
- [generate-web-ui](@warlock.js/core/skills/generate-web-ui/SKILL.md): Generate web pages, schema-backed forms and CRUD UI with warlock generate page, form and crud-ui. Includes setup companions, design-system controls, useSubmitForm, optional guarded endpoints and module CRUD prerequisites.
|
package/package.json
CHANGED
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
"@mongez/slug": "^1.0.7",
|
|
26
26
|
"@mongez/supportive-is": "^3.0.0",
|
|
27
27
|
"@mongez/time-wizard": "^1.0.6",
|
|
28
|
-
"@warlock.js/cache": "5.
|
|
29
|
-
"@warlock.js/cascade": "5.
|
|
30
|
-
"@warlock.js/context": "5.
|
|
31
|
-
"@warlock.js/fs": "5.
|
|
32
|
-
"@warlock.js/logger": "5.
|
|
33
|
-
"@warlock.js/seal": "5.
|
|
28
|
+
"@warlock.js/cache": "5.37.0",
|
|
29
|
+
"@warlock.js/cascade": "5.37.0",
|
|
30
|
+
"@warlock.js/context": "5.37.0",
|
|
31
|
+
"@warlock.js/fs": "5.37.0",
|
|
32
|
+
"@warlock.js/logger": "5.37.0",
|
|
33
|
+
"@warlock.js/seal": "5.37.0",
|
|
34
34
|
"bcryptjs": "^3.0.3",
|
|
35
35
|
"chokidar": "^5.0.0",
|
|
36
36
|
"dayjs": "^1.11.19",
|
|
@@ -56,12 +56,12 @@
|
|
|
56
56
|
"@aws-sdk/lib-storage": "^3.955.0",
|
|
57
57
|
"@aws-sdk/s3-request-presigner": "^3.955.0",
|
|
58
58
|
"@react-email/render": "^2.0.5",
|
|
59
|
-
"@warlock.js/access": "5.
|
|
60
|
-
"@warlock.js/ai": "5.
|
|
61
|
-
"@warlock.js/ai-tools": "5.
|
|
62
|
-
"@warlock.js/devtools": "5.
|
|
63
|
-
"@warlock.js/herald": "5.
|
|
64
|
-
"@warlock.js/notifications": "5.
|
|
59
|
+
"@warlock.js/access": "5.37.0",
|
|
60
|
+
"@warlock.js/ai": "5.37.0",
|
|
61
|
+
"@warlock.js/ai-tools": "5.37.0",
|
|
62
|
+
"@warlock.js/devtools": "5.37.0",
|
|
63
|
+
"@warlock.js/herald": "5.37.0",
|
|
64
|
+
"@warlock.js/notifications": "5.37.0",
|
|
65
65
|
"nodemailer": "^8.0.5",
|
|
66
66
|
"react": "^19.2.3",
|
|
67
67
|
"react-dom": "^19.2.3",
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
"engines": {
|
|
147
147
|
"node": ">=22.15"
|
|
148
148
|
},
|
|
149
|
-
"version": "5.
|
|
149
|
+
"version": "5.37.0",
|
|
150
150
|
"type": "module",
|
|
151
151
|
"main": "./esm/index.mjs",
|
|
152
152
|
"module": "./esm/index.mjs",
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deploy-release
|
|
3
|
+
description: 'Deploy a Warlock application with `warlock release` — freeze a clean commit as a candidate, run the gate, prove migrations, stage the build, switch `current` by atomic symlink, verify the health URL and roll back, configured by the `release` section of `warlock.config.ts`. Triggers: `warlock release`, `release candidate`, `release gate`, `release migrations --prove`, `release stage`, `release activate`, `release verify`, `release rollback`, `release prune`, `ReleaseConfig`, `release.restart.command`, `release.verify`, `.warlock/releases`; "deploy my app", "zero-downtime deploy", "roll back a deploy", "atomic release", "release gate", "prove migrations before deploy". Skip: the `/health` and `/ready` endpoints themselves — the `health-checks` topic; generating a PM2 ecosystem file — `warlock pm2` (the `run-app` topic); publishing the framework packages — `releasing-warlock-monorepo`; competing tools Capistrano, Deployer, hand-rolled symlink scripts.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Warlock — deploy with `warlock release`
|
|
7
|
+
|
|
8
|
+
A recorded, step-by-step deploy for one host. Each step is a command; each writes into a per-release `manifest.json` and a shared `release.log`.
|
|
9
|
+
|
|
10
|
+
```text
|
|
11
|
+
candidate -> gate -> migrations --prove -> stage -> activate -> verify
|
|
12
|
+
\-> rollback
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Warlock never calls PM2, systemd or any process manager. The only restart is the command you set in `release.restart.command`.
|
|
16
|
+
|
|
17
|
+
## Configure
|
|
18
|
+
|
|
19
|
+
```ts title="warlock.config.ts"
|
|
20
|
+
import { defineConfig } from "@warlock.js/core";
|
|
21
|
+
|
|
22
|
+
export default defineConfig({
|
|
23
|
+
release: {
|
|
24
|
+
gate: {
|
|
25
|
+
checks: [
|
|
26
|
+
{ name: "typecheck", command: "npx warlock typecheck" },
|
|
27
|
+
{ name: "test", command: "npx warlock test" },
|
|
28
|
+
{ name: "build", command: "NODE_ENV=production npx warlock build" },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
restart: { command: "pm2 reload my-app" },
|
|
32
|
+
verify: { url: "http://127.0.0.1:2030/health" },
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Defaults: `directory` `.warlock`, `distDir` `dist`, gate = typecheck, check, test, build. Add `.warlock` to `.gitignore`.
|
|
38
|
+
|
|
39
|
+
## Run the steps
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx warlock release candidate [--commit <sha>] [--allow-dirty]
|
|
43
|
+
npx warlock release gate
|
|
44
|
+
npx warlock release migrations --prove --target staging
|
|
45
|
+
npx warlock release stage
|
|
46
|
+
npx warlock release activate --preflight
|
|
47
|
+
npx warlock release activate [--dry-run] [--allow-same]
|
|
48
|
+
npx warlock release verify || npx warlock release rollback
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Every command takes an optional release id (`release gate 1.4.0-a1b2c3d`); without one it uses the newest candidate, gated or staged release. `--json` prints the result as JSON; a failed step exits 1.
|
|
52
|
+
|
|
53
|
+
- **candidate** freezes the commit with `git archive` into `.warlock/releases/<version>-<shortsha>/source`. Refuses a dirty tree without `--allow-dirty` and never overwrites a release.
|
|
54
|
+
- **gate** runs the checks in order in the frozen source and stops at the first failure; results go into the manifest.
|
|
55
|
+
- **migrations** without `--prove` only lists the pending set. With `--prove` it snapshots the ledger, tables and indexes, runs the pending migrations, snapshots again and compares with the expected set (`release.migrations.expected`, default the pending set; `expectedTables`). The migrations really run: use `--target` (a `release.migrations.targets` entry whose `env` is applied before the database connects) on a database that is safe to migrate. A failed proof blocks activation.
|
|
56
|
+
- **stage** (needs a passing gate) copies `dist`, `package.json`, the lockfile, `release.include` entries and `release.json` into `staged/`, atomically.
|
|
57
|
+
- **activate** needs staged artefacts, a passed gate (and proof), and a version strictly greater than the active one (semver; `--allow-same` accepts an equal version, a lower one is refused). It switches `current -> releases/<id>` with an atomic rename, keeps the old target as `previous`, then runs the restart command with `WARLOCK_RELEASE_ID`, `WARLOCK_RELEASE_DIR` and `WARLOCK_RELEASE_CURRENT` set. If that command fails, the pointer stays switched and the exit code is 1.
|
|
58
|
+
- **verify** GETs `release.verify.url` with retries and a timeout and checks the status plus the `version` and `commit` fields of the JSON body against the active release (`verify.versionPath`, `commitPath` and `match` adjust that). Serve the `release.json` values from your health endpoint.
|
|
59
|
+
- **rollback** switches `current` back to `previous` (and `previous` to the release just left), reruns the restart command. It does not roll migrations back: a migration may have destroyed data no reverse step restores; use `warlock migrate --rollback` deliberately if needed.
|
|
60
|
+
- **list** and **status** show releases and the active one; **prune --keep <n>** is the only command that deletes releases and always keeps `current` and `previous`.
|
|
61
|
+
|
|
62
|
+
## Layout
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
.warlock/current -> releases/<id> .warlock/previous -> releases/<id>
|
|
66
|
+
.warlock/release.log
|
|
67
|
+
.warlock/releases/<id>/{manifest.json, source/, staged/}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Run the app from `.warlock/current/staged` so a restart follows the pointer.
|
|
71
|
+
|
|
72
|
+
## Pitfalls
|
|
73
|
+
|
|
74
|
+
- Run `activate --dry-run` or `--preflight` first in CI; both change nothing.
|
|
75
|
+
- Without `restart.command` activation only moves the symlink; the running process keeps serving the old code.
|
|
76
|
+
- `verify` fails when the response does not report a version; set `verify.match` to `[]` to check only the status.
|
package/skills/index.md
CHANGED
|
@@ -22,7 +22,7 @@ When the framework is wrong or missing a capability, follow the `report-framewor
|
|
|
22
22
|
- **HTTP & routing:** register-route, warlock-routes, create-controller, use-middleware, write-middleware, validate-input, use-request-locals, build-restful, wire-socket
|
|
23
23
|
- **Responses & resources:** send-response, define-resource, use-model-transformers, use-localization
|
|
24
24
|
- **Domain layers:** write-use-case, use-repository, write-seeder
|
|
25
|
-
- **CLI & running the app:** generate-web-ui, run-app, write-cli-command, add-connector, health-checks, warlock-doctor, warlock-inspect, warlock-check, generate-openapi
|
|
25
|
+
- **CLI & running the app:** generate-web-ui, run-app, deploy-release, write-cli-command, add-connector, health-checks, warlock-doctor, warlock-inspect, warlock-check, generate-openapi
|
|
26
26
|
- **Agent tooling:** warlock-mcp, warlock-trace, warlock-agents-md, warlock-board
|
|
27
27
|
- **Storage, files & mail:** store-file, upload-file, process-image, send-mail
|
|
28
28
|
- **Security & utilities:** hash-password, encrypt-data, store-secrets, retry-operation, benchmark-code, request-memo, request-tracing
|
|
@@ -279,3 +279,28 @@ router.get("/webhooks/status", webhookStatusController, { rateLimit: false });
|
|
|
279
279
|
- The `create-controller` topic — the handler shape, validation, request/response surface.
|
|
280
280
|
- The `send-response` topic — picking the right `response.<helper>()`.
|
|
281
281
|
- The `warlock-conventions` topic — module layout, the `guarded` convention, path aliases.
|
|
282
|
+
|
|
283
|
+
## A named route the browser must never call — `clientVisible: false`
|
|
284
|
+
|
|
285
|
+
A named API route is embedded in a page's browser route table only when that page's client code reaches its name. Mark an endpoint that browser code must never reach with `clientVisible: false`:
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
router.post("/internal/reindex", reindexController, {
|
|
289
|
+
name: "internal.reindex",
|
|
290
|
+
clientVisible: false,
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
It is then never embedded in a page, and `warlock check` reports a browser reference to its name as `WL9020` (an error). The server still authorizes the call as for any route.
|
|
295
|
+
|
|
296
|
+
### Offer a route to some sites only — `sites`
|
|
297
|
+
|
|
298
|
+
In a multi-site app a route answers on every host, and a page of any site may embed it in its browser table. Declare the sites that may embed it on the route or on a group; a route's own `sites` replaces its group's, and a route with none is shared:
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
router.group({ prefix: "/api/admin", sites: ["admin"] }, () => {
|
|
302
|
+
router.post("/users", createUser, { name: "admin.users.create" });
|
|
303
|
+
});
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
This limits the browser table only; keep the route's guard. A guard in the route's middleware chain (group middleware included) also keeps it out of a guest's table, and a custom guard must say so with `markAuthGuard(fn)` unless its name contains `auth` or `guard`. `warlock inspect`, `warlock agents-md` and `WL9006` read the same chain, so a guard declared once on a group guards every route inside it.
|
|
@@ -454,7 +454,7 @@ and has no Warlock setup or HTTP global setup. `integration` discovers
|
|
|
454
454
|
It uses the framework setup, HTTP global setup and a generated `src/test-runner.ts`.
|
|
455
455
|
Keep pure service specs beside their source and mock repositories at the boundary.
|
|
456
456
|
Existing config files are preserved; copy the projects layout into them manually.
|
|
457
|
-
Vitest
|
|
457
|
+
Vitest 5 or newer is required for the generated runner (Vitest 5 removed `vitest/runners`; the runner extends `TestRunner` from `vitest`).
|
|
458
458
|
|
|
459
459
|
For a unit spec that needs configuration values, use:
|
|
460
460
|
|
|
@@ -502,20 +502,20 @@ Run with `npx vitest run --project integration`. Worker lifetime refuses watch,
|
|
|
502
502
|
threads, isolated module graphs, or a missing runner with a message explaining
|
|
503
503
|
the required settings. Keep `afterAll(teardownTest)` in the setup file: in worker
|
|
504
504
|
mode it marks the end of the file; Vitest's awaited worker cleanup closes the
|
|
505
|
-
runtime once. The runner uses `
|
|
506
|
-
in Vitest
|
|
505
|
+
runtime once. The runner uses `TestRunner.onCleanupWorkerContext`, verified
|
|
506
|
+
in Vitest 5.0.3's `TestRunner` declaration, runner implementation and worker cleanup source.
|
|
507
507
|
For an existing project, generate this runner without overwriting your config:
|
|
508
508
|
|
|
509
509
|
```ts title="src/test-runner.ts"
|
|
510
510
|
import { configureTestWorker } from "@warlock.js/core/tests";
|
|
511
|
-
import {
|
|
511
|
+
import { TestRunner } from "vitest";
|
|
512
512
|
|
|
513
|
-
export default class WarlockTestRunner extends
|
|
514
|
-
public constructor(config: ConstructorParameters<typeof
|
|
513
|
+
export default class WarlockTestRunner extends TestRunner {
|
|
514
|
+
public constructor(config: ConstructorParameters<typeof TestRunner>[0]) {
|
|
515
515
|
super(config);
|
|
516
516
|
configureTestWorker(
|
|
517
517
|
{ pool: this.pool, watch: config.watch, isolate: config.isolate },
|
|
518
|
-
(cleanup) => this.onCleanupWorkerContext(cleanup),
|
|
518
|
+
(cleanup) => this.onCleanupWorkerContext?.(cleanup),
|
|
519
519
|
);
|
|
520
520
|
}
|
|
521
521
|
}
|
|
@@ -534,7 +534,7 @@ This trades module isolation for startup cost: application singletons, config
|
|
|
534
534
|
mutations, mocks and module side effects can survive between files. Reset your
|
|
535
535
|
own state in `resetBetweenFiles`, restore mocks, and keep connector selection
|
|
536
536
|
consistent. Give each worker a distinct database/schema using
|
|
537
|
-
`process.env.VITEST_POOL_ID` when preparing test connections. Never drop a shared
|
|
537
|
+
`process.env.VITEST_POOL_ID` (1-based since Vitest 5) when preparing test connections. Never drop a shared
|
|
538
538
|
database from this hook while other workers are using it. The framework does not
|
|
539
539
|
create a memory replica set or a cross-worker lock; those belong to the application's
|
|
540
540
|
global setup. A shared HTTP server and a single replica set can contend on I/O,
|