@warlock.js/core 5.33.0 → 5.34.1

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.
Files changed (120) hide show
  1. package/CHANGELOG.md +36 -46
  2. package/esm/cli/commands/check/check-command.action.mjs +2 -1
  3. package/esm/cli/commands/check/check-command.action.mjs.map +1 -1
  4. package/esm/cli/commands/check.command.mjs +2 -2
  5. package/esm/cli/commands/check.command.mjs.map +1 -1
  6. package/esm/cli/commands/e2e.command.mjs +63 -0
  7. package/esm/cli/commands/e2e.command.mjs.map +1 -0
  8. package/esm/cli/commands/load-e2e-spec.mjs +47 -0
  9. package/esm/cli/commands/load-e2e-spec.mjs.map +1 -0
  10. package/esm/cli/framework-cli-commands.mjs +2 -0
  11. package/esm/cli/framework-cli-commands.mjs.map +1 -1
  12. package/esm/dev-server/health-checker/checkers/oxlint-health-checker.mjs +1 -1
  13. package/esm/dev-server/health-checker/default-health-checkers.mjs +1 -6
  14. package/esm/dev-server/health-checker/default-health-checkers.mjs.map +1 -1
  15. package/esm/dev-server/health-checker/file-health-checker.contract.d.mts +0 -2
  16. package/esm/dev-server/health-checker/file-health-checker.contract.d.mts.map +1 -1
  17. package/esm/dev-server/health-checker/file-health-result.d.mts +1 -1
  18. package/esm/dev-server/health-checker/file-health-result.mjs.map +1 -1
  19. package/esm/e2e/browser.d.mts +4 -0
  20. package/esm/e2e/browser.d.mts.map +1 -0
  21. package/esm/e2e/browser.mjs +88 -0
  22. package/esm/e2e/browser.mjs.map +1 -0
  23. package/esm/e2e/cdp.d.mts +152 -0
  24. package/esm/e2e/cdp.d.mts.map +1 -0
  25. package/esm/e2e/cdp.mjs +106 -0
  26. package/esm/e2e/cdp.mjs.map +1 -0
  27. package/esm/e2e/fixture.mjs +195 -0
  28. package/esm/e2e/fixture.mjs.map +1 -0
  29. package/esm/e2e/index.d.mts +5 -0
  30. package/esm/e2e/index.mjs +6 -0
  31. package/esm/e2e/owned-process.mjs +193 -0
  32. package/esm/e2e/owned-process.mjs.map +1 -0
  33. package/esm/e2e/page.d.mts +64 -0
  34. package/esm/e2e/page.d.mts.map +1 -0
  35. package/esm/e2e/page.mjs +376 -0
  36. package/esm/e2e/page.mjs.map +1 -0
  37. package/esm/e2e/run-e2e.d.mts +17 -0
  38. package/esm/e2e/run-e2e.d.mts.map +1 -0
  39. package/esm/e2e/run-e2e.mjs +143 -0
  40. package/esm/e2e/run-e2e.mjs.map +1 -0
  41. package/esm/e2e/types.d.mts +123 -0
  42. package/esm/e2e/types.d.mts.map +1 -0
  43. package/esm/e2e/types.mjs +24 -0
  44. package/esm/e2e/types.mjs.map +1 -0
  45. package/esm/generations/features/web.feature.mjs +7 -4
  46. package/esm/generations/features/web.feature.mjs.map +1 -1
  47. package/esm/generations/stubs.mjs +18 -35
  48. package/esm/generations/stubs.mjs.map +1 -1
  49. package/esm/index.d.mts +2 -2
  50. package/esm/secrets/errors.d.mts +1 -1
  51. package/esm/secrets/errors.d.mts.map +1 -1
  52. package/esm/secrets/errors.mjs +2 -0
  53. package/esm/secrets/errors.mjs.map +1 -1
  54. package/esm/secrets/file-backend.mjs +1 -0
  55. package/esm/secrets/file-backend.mjs.map +1 -1
  56. package/esm/secrets/index.d.mts +2 -2
  57. package/esm/secrets/keyring-backend.mjs +1 -0
  58. package/esm/secrets/keyring-backend.mjs.map +1 -1
  59. package/esm/secrets/memory-backend.d.mts +1 -0
  60. package/esm/secrets/memory-backend.d.mts.map +1 -1
  61. package/esm/secrets/memory-backend.mjs +1 -0
  62. package/esm/secrets/memory-backend.mjs.map +1 -1
  63. package/esm/secrets/resolve-driver.mjs +14 -0
  64. package/esm/secrets/resolve-driver.mjs.map +1 -0
  65. package/esm/secrets/secret-store.d.mts +4 -2
  66. package/esm/secrets/secret-store.d.mts.map +1 -1
  67. package/esm/secrets/secret-store.mjs +22 -8
  68. package/esm/secrets/secret-store.mjs.map +1 -1
  69. package/esm/secrets/secrets.d.mts +2 -0
  70. package/esm/secrets/secrets.d.mts.map +1 -1
  71. package/esm/secrets/secrets.mjs +13 -1
  72. package/esm/secrets/secrets.mjs.map +1 -1
  73. package/esm/secrets/types.d.mts +8 -0
  74. package/esm/secrets/types.d.mts.map +1 -1
  75. package/esm/tooling/check/lint-step.d.mts +3 -3
  76. package/esm/tooling/check/lint-step.d.mts.map +1 -1
  77. package/esm/tooling/check/lint-step.mjs +3 -47
  78. package/esm/tooling/check/lint-step.mjs.map +1 -1
  79. package/esm/tooling/check/run-check.d.mts +2 -2
  80. package/esm/tooling/check/run-check.mjs +4 -2
  81. package/esm/tooling/check/run-check.mjs.map +1 -1
  82. package/esm/tooling/problem.d.mts +1 -1
  83. package/esm/tooling/problem.d.mts.map +1 -1
  84. package/esm/tooling/problem.mjs.map +1 -1
  85. package/esm/updater/package-manager.mjs +5 -1
  86. package/esm/updater/package-manager.mjs.map +1 -1
  87. package/esm/upgrade/codemods/index.mjs +2 -1
  88. package/esm/upgrade/codemods/index.mjs.map +1 -1
  89. package/esm/upgrade/codemods/oxc-toolchain.mjs +59 -10
  90. package/esm/upgrade/codemods/oxc-toolchain.mjs.map +1 -1
  91. package/esm/upgrade/codemods/oxlint-rule-schemas.mjs +6 -0
  92. package/esm/upgrade/codemods/oxlint-rule-schemas.mjs.map +1 -0
  93. package/esm/upgrade/codemods/port-eslint-rules.mjs +343 -0
  94. package/esm/upgrade/codemods/port-eslint-rules.mjs.map +1 -0
  95. package/esm/upgrade/codemods/toolchain-templates.mjs +1 -1
  96. package/esm/upgrade/codemods/toolchain-templates.mjs.map +1 -1
  97. package/esm/upgrade/codemods/validate-oxlint-rule.mjs +60 -0
  98. package/esm/upgrade/codemods/validate-oxlint-rule.mjs.map +1 -0
  99. package/esm/upgrade/notes.mjs +14 -7
  100. package/esm/upgrade/notes.mjs.map +1 -1
  101. package/esm/warlock-config/types.d.mts +0 -1
  102. package/esm/warlock-config/types.d.mts.map +1 -1
  103. package/llms-full.txt +269 -44
  104. package/llms.txt +4 -3
  105. package/package.json +19 -20
  106. package/skills/create-module/SKILL.md +2 -2
  107. package/skills/run-app/SKILL.md +3 -3
  108. package/skills/store-secrets/SKILL.md +60 -2
  109. package/skills/test-browser/SKILL.md +119 -0
  110. package/skills/upgrade-from-5-29/SKILL.md +70 -29
  111. package/skills/warlock-check/SKILL.md +3 -3
  112. package/skills/warlock-doctor/SKILL.md +2 -3
  113. package/skills/write-cli-command/SKILL.md +6 -2
  114. package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs +0 -152
  115. package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs.map +0 -1
  116. package/esm/dev-server/health-checker/eslint-support.mjs +0 -27
  117. package/esm/dev-server/health-checker/eslint-support.mjs.map +0 -1
  118. package/esm/dev-server/health-checker/workers/eslint-health.worker.d.mts +0 -1
  119. package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs +0 -183
  120. package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs.map +0 -1
package/llms-full.txt CHANGED
@@ -1879,8 +1879,8 @@ await warmupProductCache();
1879
1879
 
1880
1880
  ## Verify generated imports
1881
1881
 
1882
- Generated web starter files match the scaffold’s Prettier configuration, using
1883
- `@ianvs/prettier-plugin-sort-imports` with Babel independently of TypeScript 7.
1882
+ Generated web starter files match the scaffold’s Oxfmt configuration and its
1883
+ built-in import sorting.
1884
1884
  After editing generated code, run `npm run format` and `npm run lint:check`.
1885
1885
  Formatting retains unused imports; the linter reports them.
1886
1886
 
@@ -4580,7 +4580,7 @@ export default defineConfig({
4580
4580
 
4581
4581
  - **`watch.include` / `watch.exclude`** — globs piped into the file watcher. Override when you have non-`.ts(x)` files driving reloads (e.g. SQL fixtures), or to exclude a generated folder.
4582
4582
  - **`generateTypings`** — turn off if you're committing generated typings and don't want them rewritten on every boot. The `--skip-typings` flag is the per-run version.
4583
- - **`healthCheckers`** — built-in switches (`{ typescript?, eslint?, oxlint? }`), custom file health checker contracts, or `false` to disable. The `--skip-health` flag disables all checkers for that run. TypeScript runs by default. ESLint uses core's dependency and requires only an app-root flat config; Oxlint requires its package installed in the app plus a config. ESLint is disabled by default; explicitly set `eslint: true` to retain it until removal in 5.34.0. Enabling it prints one deprecation warning and the Oxc migration command.
4583
+ - **`healthCheckers`** — built-in switches (`{ typescript?, oxlint? }`), custom checker arrays, or `false`. TypeScript and installed/configured Oxlint run by default. `--skip-health` disables checks for one run. ESLint support was removed in 5.34.0; use `npx warlock upgrade --from 5.33 --to 5.34.0` to remove stale options.
4584
4584
  - **`transpileCacheDebug`** — diagnostic only. Names `.warlock/transpile/*.js` files `<slug>.<hash>.js` and appends `// @source <path>` markers so you can eyeball which cache entry came from which source. Leave off in normal use.
4585
4585
  - **`checkForUpdates`** — on `warlock dev` start, check npm for a newer `@warlock.js/core` and print a one-line notice if one exists. Best-effort and non-blocking; auto-skipped in CI and non-TTY shells. In an interactive terminal the notice arms a **`u` shortcut** that updates every `@warlock.js/*` package, installs, and restarts the server; elsewhere it prints `npx warlock update` instead. The registry answer is cached for 24h in `.warlock/update-check.json`, so a day of restarts costs one lookup. See the `update-packages` topic.
4586
4586
  - **`restartOnConfigChange`** — restart the dev server when `warlock.config.ts` or any `.env*` changes (default `true`). Set `false` to get a warning instead and restart by hand. Neither file can be hot-reloaded, so without a restart the running services keep the old values.
@@ -4603,12 +4603,12 @@ import { defineConfig } from "@warlock.js/core";
4603
4603
 
4604
4604
  export default defineConfig({
4605
4605
  devServer: {
4606
- healthCheckers: { typescript: true, eslint: false, oxlint: true },
4606
+ healthCheckers: { typescript: true, oxlint: true },
4607
4607
  },
4608
4608
  });
4609
4609
  ```
4610
4610
 
4611
- Omit the switches to run TypeScript and installed/configured Oxlint; ESLint is off by default and requires `eslint: true`; `true` still requires the linter's config, and Oxlint also requires its app-installed package. Set `oxlint: false` to disable only Oxlint while leaving other defaults intact. A custom checker array replaces the built-in list. Config discovery also accepts `.oxlintrc.jsonc`, `oxlint.config.ts` and `oxlint.config.mts`; use JSON/JSONC on Node 22.15 because current Oxlint TypeScript configs need Node 22.18 or newer.
4611
+ Omit switches to run TypeScript and installed/configured Oxlint. Set `oxlint: false` to disable only Oxlint. A custom checker array replaces the built-in list. JSON/JSONC configs work on Node 22.15; current Oxlint TypeScript configs require Node 22.18 or newer.
4612
4612
 
4613
4613
  ## `warlock build` — production bundle
4614
4614
 
@@ -6491,7 +6491,7 @@ Client `PUT`s the bytes straight to R2 — your server never sees them.
6491
6491
 
6492
6492
  ---
6493
6493
  name: store-secrets
6494
- description: "Store app credentials with secrets.get/set/remove/has and environmentName; lazy optional OS keychain, read-only scoped environment variables, encrypted file fallback, typed SecretStoreError and memory-backed secretsFake. Use for provider keys and OAuth credentials the app must read back. Skip user passwords (hash-password) and encrypted database columns (encrypt-data)."
6494
+ description: "Store app credentials with secrets.get/set/remove/has, backend() and environmentName; lazy optional OS keychain, read-only scoped environment variables, encrypted file fallback, typed SecretStoreError and memory-backed secretsFake. Use for provider keys and OAuth credentials the app must read back. Skip user passwords (hash-password) and encrypted database columns (encrypt-data)."
6495
6495
  ---
6496
6496
 
6497
6497
  # Application secret store
@@ -6507,6 +6507,7 @@ const token = await secrets.get("vendor-token"); // string | undefined
6507
6507
  const exists = await secrets.has("vendor-token"); // boolean; true for ""
6508
6508
  const removed = await secrets.remove("vendor-token"); // boolean
6509
6509
  const variable = secrets.environmentName("vendor-token"); // variable name, not value
6510
+ const backend = await secrets.backend(); // "keychain" | "file" | "memory"; no credentials
6510
6511
 
6511
6512
  try {
6512
6513
  await secrets.remove("vendor-token");
@@ -6523,6 +6524,23 @@ Names are case-sensitive, nonempty strings of at most 256 UTF-16 code units. Val
6523
6524
 
6524
6525
  ## Configuration and backend decisions
6525
6526
 
6527
+ Use `await secrets.backend()` or `await store.backend()` to inspect active storage.
6528
+ The exported `SecretBackendType` is `"keychain" | "file" | "memory"`. Inspection
6529
+ triggers core's own lazy selection and shares the memoized keychain probe with
6530
+ credential operations, including concurrent calls; never run an independent probe.
6531
+ It returns the selected backend, not the source of an individual secret. Scoped
6532
+ environment reads do not change it. File selection does not create files or verify
6533
+ the encryption key/filesystem. A later locked keychain remains `"keychain"` and
6534
+ credential operations fail safely instead of silently changing selection.
6535
+
6536
+ Resolved `secrets.driver` overrides `SECRETS_DRIVER`: memory reports `"memory"`;
6537
+ auto reports `"keychain"` after a successful initial probe or `"file"` after failure.
6538
+ `secretsFake` and explicit `MemorySecretBackend` report `"memory"` without probing.
6539
+ Explicit backends bypass driver configuration. Custom `SecretBackend` implementations
6540
+ may declare readonly `type: SecretBackendType`; omitted metadata reports `"memory"`
6541
+ for compatibility with isolated injected test backends. Core file/keychain instances
6542
+ declare their own type.
6543
+
6526
6544
  - Default namespace: app name plus the SHA-256 hash of the absolute project root. For changing release directories, configure a stable unique namespace including the deployment environment.
6527
6545
  - Reads: OS keychain → exact scoped environment variable → encrypted file.
6528
6546
  - Optional peer: `npm install @napi-rs/keyring`. Core lazily uses its 2.1.1 `AsyncEntry` API. Linux requires `secret-service` for persistence; it does not use the memory-only keyutils fallback. Sources: [official README](https://github.com/Brooooooklyn/keyring-node#readme), [published declarations](https://www.npmjs.com/package/@napi-rs/keyring?activeTab=code), verified 2026-10-19.
@@ -6575,6 +6593,7 @@ import type { SecretBackend, SecretStoreOptions } from "@warlock.js/core/secrets
6575
6593
  const backend: SecretBackend = new MemorySecretBackend();
6576
6594
  const options: SecretStoreOptions = { namespace: "test-app", backend };
6577
6595
  const store = new SecretStore(options);
6596
+ await store.backend(); // "memory"
6578
6597
  await store.set("vendor-token", "test-credential");
6579
6598
  await store.get("vendor-token");
6580
6599
  await store.has("vendor-token");
@@ -6588,10 +6607,172 @@ Options also allow a `directory` and `encryptionKey` (string or lazy callback);
6588
6607
 
6589
6608
  ## Error and logging contract
6590
6609
 
6591
- `SecretStoreError.code` is `INVALID_NAME`, `INVALID_VALUE`, `INVALID_NAMESPACE`, `READ_ONLY`, `KEY_REQUIRED`, `UNAVAILABLE`, `UNSAFE_FILE` or `CORRUPT_FILE`. Errors never include names, values, paths or vendor causes. `KEY_REQUIRED` means provision/fix the app encryption key; `CORRUPT_FILE` means wrong key, invalid format or failed authentication. Never replace an unreadable store with an empty one.
6610
+ `SecretStoreError.code` is `INVALID_DRIVER`, `MEMORY_IN_PRODUCTION`, `INVALID_NAME`, `INVALID_VALUE`, `INVALID_NAMESPACE`, `READ_ONLY`, `KEY_REQUIRED`, `UNAVAILABLE`, `UNSAFE_FILE` or `CORRUPT_FILE`. Errors never include names, values, paths or vendor causes. `KEY_REQUIRED` means provision/fix the app encryption key; `CORRUPT_FILE` means wrong key, invalid format or failed authentication. Never replace an unreadable store with an empty one.
6592
6611
 
6593
6612
  The store never logs credentials. It registers encountered names and literal values with `@warlock.js/logger` for process-lifetime masking, including old/removed/failed-write values, interpolated messages, Error text/causes, and channel error reports. Keys and exact values stay protected even with permissive custom censors. Registration retains values in memory. It cannot mask direct console/third-party logging, earlier logs, transformed/encoded values, or credentials never encountered by the store. Keep secrets out of logs even with masking enabled. Fake assertion errors do not print values.
6594
6613
 
6614
+ ## Built-app tests with the memory driver
6615
+
6616
+ Run the built server with `SECRETS_DRIVER=memory`, or select the driver in
6617
+ `src/config/secrets.ts`. Explicit config takes precedence over the environment;
6618
+ `"auto"` (the default) keeps keychain/environment/file behavior. Unknown driver
6619
+ names fail with `SecretStoreError.code === "INVALID_DRIVER"`.
6620
+
6621
+ ```ts title="src/config/secrets.ts"
6622
+ import type { SecretsConfigurations } from "@warlock.js/core";
6623
+
6624
+ export default {
6625
+ namespace: "browser-test-server",
6626
+ driver: "memory",
6627
+ allowMemoryInProduction: true,
6628
+ } satisfies SecretsConfigurations;
6629
+ ```
6630
+
6631
+ `allowMemoryInProduction: true` is required only when the test server runs with
6632
+ `NODE_ENV=production`. Without it, memory selection fails closed with
6633
+ `MEMORY_IN_PRODUCTION`; a built bundle does not bypass this check. Set this
6634
+ option explicitly in the test deployment configuration. `SecretStore` accepts
6635
+ the same `driver` and `allowMemoryInProduction` options for standalone stores.
6636
+
6637
+ The selected memory driver bypasses keychain, scoped environment secrets and
6638
+ files, and still registers values for log masking. Values are isolated by app
6639
+ namespace, lost on process exit, and never shared across workers or with a
6640
+ separate browser-test process. Seed fixtures through application startup or a
6641
+ test-only endpoint in the server process using the normal `secrets.set()` API:
6642
+
6643
+ ```ts
6644
+ import { secrets } from "@warlock.js/core";
6645
+
6646
+ await secrets.set("vendor-token", "browser-test-credential");
6647
+ const token = await secrets.get("vendor-token");
6648
+ ```
6649
+
6650
+ The normal test harness still installs `secretsFake`; explicit `backend`
6651
+ instances and that fake continue to bypass configured backend selection.
6652
+
6653
+
6654
+ ## test-browser `@warlock.js/core/test-browser/SKILL.md`
6655
+
6656
+ ---
6657
+ name: test-browser
6658
+ description: Write or repair browser e2e specs using warlock e2e and @warlock.js/core/e2e, with owned app fixtures, direct CDP interaction, responsive screenshots and run evidence. Use for real browser flows, click races or viewport overflow; use test-http for API-only tests.
6659
+ ---
6660
+
6661
+ # Browser e2e
6662
+
6663
+ Use `@warlock.js/core/e2e`, a Node-only subpath, and default-export `defineE2E` from
6664
+ `e2e/*.e2e.ts`. Run `npx warlock e2e`. The CLI loads TypeScript itself; no Vitest
6665
+ bootstrap, Playwright, Puppeteer or external browser server is needed.
6666
+
6667
+ Every run copies the app into a temporary directory, excludes dotenv files and
6668
+ symlinks, links installed dependencies for reads, seeds its own environment and
6669
+ owns the app/browser process groups. Keep foreground commands in the fixture;
6670
+ never use a shared dev server or daemonize. Map the disposable `dataRoot` into the
6671
+ app's actual storage/database config. The harness cannot infer that mapping.
6672
+ `prepare` is where seeds and fake executables belong; `app.path` is relative to the
6673
+ copied app. Do not install dependencies through the shared `node_modules` link.
6674
+
6675
+ ## Complete standalone spec
6676
+
6677
+ This spec creates its own small app; it can be pasted into an existing project
6678
+ with core installed and run with `npx warlock e2e`.
6679
+
6680
+ ```ts
6681
+ import { mkdir, writeFile } from "node:fs/promises";
6682
+ import { join } from "node:path";
6683
+ import { defineE2E } from "@warlock.js/core/e2e";
6684
+
6685
+ export default defineE2E({
6686
+ app: {
6687
+ start: [process.execPath, "form-fixture.mjs"],
6688
+ portFromFile: true,
6689
+ path: ["fake-bin"],
6690
+ env: ({ dataRoot }) => ({ FORM_DATA: dataRoot }),
6691
+ prepare: async ({ directory, dataRoot }) => {
6692
+ await mkdir(join(directory, "fake-bin"));
6693
+ await writeFile(join(dataRoot, "prefix.txt"), "Saved");
6694
+ await writeFile(
6695
+ join(directory, "form-fixture.mjs"),
6696
+ `
6697
+ import { createServer } from 'node:http';
6698
+ import { readFileSync, writeFileSync } from 'node:fs';
6699
+ import { join } from 'node:path';
6700
+ const prefix = readFileSync(join(process.env.FORM_DATA, 'prefix.txt'), 'utf8');
6701
+ const server = createServer((request, response) => {
6702
+ if (request.url === '/favicon.ico') { response.writeHead(204); response.end(); return; }
6703
+ response.setHeader('Content-Type', 'text/html');
6704
+ response.end('<!doctype html><meta name="viewport" content="width=device-width,initial-scale=1">'
6705
+ + '<input id="name"><button id="save"><span>Save</span></button><h1 id="result">Ready</h1>'
6706
+ + '<script>document.querySelector("#save").onclick=()=>{history.pushState({},"","/saved");document.querySelector("#result").textContent='
6707
+ + JSON.stringify(prefix) + '+" "+document.querySelector("#name").value}</script>');
6708
+ });
6709
+ server.listen(0, '127.0.0.1', () => writeFileSync(process.env.WARLOCK_E2E_PORT_FILE, String(server.address().port)));
6710
+ `,
6711
+ );
6712
+ },
6713
+ },
6714
+ run: async ({ page, step }) => {
6715
+ await page.goto("/");
6716
+ await step("save the form", async () => {
6717
+ await page.fill("#name", "Ada");
6718
+ await page.type("#name", " Lovelace");
6719
+ await page.waitForNavigation(() => page.click("#save"), { url: "/saved" });
6720
+ await page.waitFor({ selector: "#result", text: "Saved Ada Lovelace" });
6721
+ });
6722
+ await page.captureViewports("saved");
6723
+ },
6724
+ });
6725
+ ```
6726
+
6727
+ For an existing app, replace `prepare` and `start`; default start is
6728
+ `[process.execPath, "dist/app.js"]`. Optional
6729
+ `build: [process.execPath, "node_modules/@warlock.js/core/bin/warlock.js", "build"]`
6730
+ builds inside the copy. Supply app-specific test service variables via `env` and
6731
+ exclude production data directories with `exclude: ["storage", "data"]`.
6732
+ By default the fixture supplies an OS-assigned `PORT`/`HTTP_PORT`. Prefer the
6733
+ port-file handshake above when the app supports binding `0` itself.
6734
+
6735
+ ## Interaction and evidence
6736
+
6737
+ - Use `page.click` for scrolling, settled boxes and hit testing, including a fresh
6738
+ check after hover. Do not replace it with `element.click()` or fixed sleeps.
6739
+ - `fill` clears editable content, then sends CDP text input; `type` appends Unicode
6740
+ text. Values do not appear in step names. `type` does not simulate shortcuts.
6741
+ - `waitFor({ selector, text, url, timeoutMs })` combines conditions. Arm navigation
6742
+ **before** clicking with `waitForNavigation(() => page.click(...))`.
6743
+ - `setViewport({ name, width, height, mobile })`, `assertViewport()` and
6744
+ `screenshot(name)` are available independently. `captureViewports(name)` checks
6745
+ and captures 1280×800 desktop and 390×844 phone by default, restoring the
6746
+ original viewport. `viewports` overrides the list. Final captures are automatic.
6747
+ - Overflow compares document/body scrollWidth **and innerWidth** against the
6748
+ requested width, so a phone that expands to 448 px fails. Keep the viewport meta
6749
+ tag; inspect the reported widest element IDs, classes and dimensions.
6750
+ - Health requires zero console errors, exceptions, HTTP statuses >=400 and failed
6751
+ loads (including cancellation). Narrow exceptions use
6752
+ `allowConsoleErrors: [/^Expected warning$/]` and
6753
+ `allowNetworkFailures: [{ url: "/missing", status: 404 }]`. Canceled-request rules
6754
+ can match `errorText`. Strings match substrings; regexps are also accepted.
6755
+ - `allowPendingRequests: ["/events"]` exempts an intentional stream from network
6756
+ idle, without allowing its errors. `waitForNetworkIdle()` remains available.
6757
+ - `page.evaluate<T>(expression)` is an escape hatch for assertions in the main
6758
+ document. Selector helpers do not enter frames or closed shadow roots.
6759
+
6760
+ Use `--browser /path/to/chrome` or `WARLOCK_E2E_BROWSER` if discovery fails.
6761
+ `browser: { args: ["--no-sandbox"] }` is an explicit option for controlled root
6762
+ containers; it is not a default. On Windows, command arrays should invoke JS tools
6763
+ through `process.execPath`, avoiding `.cmd` executables that require a shell.
6764
+
6765
+ Read `.warlock/e2e/run-*/evidence.json` and the PNGs/logs on failure. Evidence keeps
6766
+ allowed errors, step timings, viewport dimensions and recorded process identities.
6767
+ `--out` changes the evidence parent; `--path ./fake-bin,./engine-bin` prepends fake
6768
+ binary paths. `runE2E(spec, { outputDirectory, browser, path, signal })` is the
6769
+ programmatic runner; `E2ERunError.result` exposes evidence on failure.
6770
+
6771
+ Cleanup verifies the unique supervisor command line before stopping only its
6772
+ recorded process group (Windows: verified PID tree). It retains disposable files
6773
+ if ownership verification fails and reports the failure in evidence. Do not add
6774
+ process-name or port-based kills to a spec.
6775
+
6595
6776
 
6596
6777
  ## test-fakes `@warlock.js/core/test-fakes/SKILL.md`
6597
6778
 
@@ -8229,48 +8410,40 @@ The CLI preserves incremental state and changed-file filtering. Development
8229
8410
  health checks keep one native compiler session and update its snapshots. Syntax
8230
8411
  analysis uses OXC. `warlock upgrade` still parses/transforms with ts-morph's own
8231
8412
  bundled TypeScript, without using it as the app's compiler. New scaffolds default
8232
- to Oxlint/Oxfmt (`--lint=oxc`). Explicit `--lint=eslint` pins the app's compiler to
8233
- TypeScript **6.0.3** because typescript-eslint requires the old JavaScript API
8234
- and supports `>=4.8.4 <6.1.0`. Keep ESLint apps on 6.0.3 until switching tools:
8235
-
8236
- ```bash
8237
- npm install --save-dev --save-exact typescript@6.0.3
8238
- npx tsc --noEmit
8239
- ```
8240
-
8241
- The same tsconfig cleanups work with TS6. Starting in 5.33, ESLint mode prints
8242
- a deprecation warning naming removal in 5.34.0 and the Oxc migration command.
8413
+ to Oxlint/Oxfmt (`--lint=oxc`) with TypeScript 7.0.2. ESLint support was
8414
+ removed in 5.34.0; migrate legacy apps using the steps below.
8243
8415
 
8244
8416
  ## 5.32 to 5.33
8245
8417
 
8246
- **BREAKING:** ESLint background health checking and automatic ESLint selection
8247
- in `warlock check` are disabled by default. Oxlint/Oxfmt replace ESLint/Prettier
8248
- as the default toolchain. Legacy support is deprecated now and removed in
8249
- 5.34.0. Temporary opt-ins are `devServer.healthCheckers: { eslint: true }`,
8250
- `warlock check --lint eslint`, and `create-warlock --lint=eslint`; each warns
8251
- about removal and migration. The legacy scaffold stays on TypeScript 6.0.3;
8252
- Oxc apps use TypeScript 7.0.2.
8253
-
8254
- ESLint + Prettier are deprecated in 5.33.0 and will be removed in **5.34.0**.
8255
- New apps should use `--lint=oxc`. To migrate an existing app, install the new
8256
- core release and `ts-morph`, then run:
8418
+ The original `oxc-toolchain` codemod remains available:
8257
8419
 
8258
8420
  ```bash
8259
8421
  npx warlock upgrade --from 5.32 --to 5.33.0 --dry-run
8260
8422
  npx warlock upgrade --from 5.32 --to 5.33.0
8423
+ ```
8424
+
8425
+ ## 5.33 to 5.34 toolchain removal
8426
+
8427
+ **BREAKING:** ESLint and Prettier support was removed in **5.34.0**. Oxlint/Oxfmt is the only
8428
+ supported toolchain. `--lint eslint` and `healthCheckers.eslint` are removed.
8429
+ Install the new core release and `ts-morph`, then migrate an existing app:
8430
+
8431
+ ```bash
8432
+ npm install --save-dev ts-morph
8433
+ npx warlock upgrade --from 5.33 --to 5.34.0 --dry-run
8434
+ npx warlock upgrade --from 5.33 --to 5.34.0
8261
8435
  npm install
8262
8436
  npm run format
8263
8437
  npm run lint:check
8264
8438
  npx warlock check
8265
8439
  ```
8266
8440
 
8267
- Review every reported `file:line`: customized configs are preserved and need
8268
- manual translation before removal. The codemod installs the template's Oxlint
8269
- and Oxfmt versions and pins TypeScript to 7.0.2. It replaces standard lint/format
8270
- scripts, removes legacy dependencies, and deletes only recognized generated
8271
- configs. A generated `.prettierignore` becomes `.oxfmtignore`. Existing Oxc
8272
- configs are preserved. `--dry-run --json` reports the same planned changes
8273
- without writing files.
8441
+ The `oxc-toolchain-removal` step reapplies the preserved 5.33 `oxc-toolchain`
8442
+ migration when legacy configs, scripts or dependencies remain. Apps upgrading
8443
+ from 5.32 can still use `npx warlock upgrade --from 5.32 --to 5.33.0` first.
8444
+ It replaces standard scripts/dependencies, pins TypeScript 7.0.2, updates generated
8445
+ editor settings, removes `healthCheckers.eslint` and selects Oxlint. A generated
8446
+ `.prettierignore` becomes `.oxfmtignore`; Oxfmt scripts read it explicitly.
8274
8447
 
8275
8448
  The `oxc-toolchain` codemod also updates generated VS Code settings and literal
8276
8449
  ESLint/Oxlint health-check switches. Custom editor settings, checker arrays,
@@ -8278,6 +8451,55 @@ computed options and nonstandard scripts require review. Oxc diagnostics keep
8278
8451
  framework conventions in `warlock check`. Re-run the migration after addressing
8279
8452
  manual findings if needed; an app with only Oxc tooling is a no-op for this codemod.
8280
8453
 
8454
+ ## 5.33 to 5.34 plain object validation
8455
+
8456
+ **BREAKING:** Seal `v.object()` rejects arrays, dates, maps, sets, class instances
8457
+ and objects with custom prototypes, including under `.allowUnknown()` and
8458
+ `.stripUnknown()`. Plain and null-prototype dictionaries remain accepted. There
8459
+ is no permissive opt-in: choose an array/instance validator or construct a plain
8460
+ DTO explicitly before validation.
8461
+
8462
+ ```ts
8463
+ import { v } from "@warlock.js/seal";
8464
+
8465
+ class User {
8466
+ name = "Ada";
8467
+ }
8468
+ const instance = new User();
8469
+ const user = v.object({ name: v.string() });
8470
+ await v.validate(user, { name: instance.name });
8471
+ const names = v.array(v.string());
8472
+ await v.validate(names, ["Ada"]);
8473
+ ```
8474
+
8475
+ `v.record(valueValidator)` remains supported. To validate dictionary keys, use
8476
+ `v.record(keyValidator, valueValidator)`; malformed arguments now fail while
8477
+ defining the schema. Literal-key records infer partial dictionaries because
8478
+ only present keys are checked.
8479
+ Literal custom rules are translated without executing the config. Per-folder
8480
+ `no-restricted-imports` and `no-restricted-exports` become Oxlint overrides with
8481
+ file/exclusion patterns. Supported `@typescript-eslint/*` rules become native
8482
+ `typescript/*` rules (or native base rules). Options are checked against the
8483
+ pinned Oxlint 1.88.0 schema. Type-aware equivalents enable `options.typeAware`
8484
+ and add `oxlint-tsgolint@7.0.2004` to the app's dev dependencies.
8485
+ `unused-imports/no-unused-imports` uses `no-unused-vars` with safe import fixes;
8486
+ Oxlint shares the severity for imports and variables, so differing active
8487
+ severities are reported for review.
8488
+
8489
+ Only unsupported rules/options, computed or imported settings, selectors such
8490
+ as `no-restricted-syntax`, and conflicting existing Oxc rules need manual review.
8491
+ Each finding names its source `file:line`, rule and affected file patterns.
8492
+ Fully ported lint configs are removed; configs with findings remain for reference
8493
+ until those findings are addressed. Existing Oxc rules are kept on conflict.
8494
+ `--dry-run --json` reports the same plan without writes. Customized formatting,
8495
+ editor settings and nonstandard scripts can also require review.
8496
+
8497
+ **Upgrading:** new and migrated template configs enable
8498
+ `"categories": { "correctness": "error" }`. Address the additional diagnostics;
8499
+ existing Oxc configs retain their categories. A real lockfile beside a workspace
8500
+ marker determines the installer: `package-lock.json` plus `pnpm-workspace.yaml`
8501
+ without `pnpm-lock.yaml` now produces `npm install`.
8502
+
8281
8503
 
8282
8504
  ## upload-file `@warlock.js/core/upload-file/SKILL.md`
8283
8505
 
@@ -10970,7 +11192,7 @@ const { messages } = await watchBoard({ agent: "implementer", timeoutMs: 30 * 60
10970
11192
 
10971
11193
  ---
10972
11194
  name: warlock-check
10973
- description: 'Verify a change with one command: `warlock check [--changed] [--json]` runs an incremental typecheck, the app''s ESLint/oxlint, the tests affected by the change and framework convention checks, and reports one JSON object per problem `{ code, category, severity, source, file, line, column, message, hint, skill, docs, rule }` with exit 1 for problems in your code and 2 for problems with this machine (missing Redis/DB, busy port, Node version, missing tool). Also `warlock typecheck [--changed]`, `warlock explain <CODE>` and the stable `WL` error codes (`errorCodeOf`, `WarlockError`). Triggers: `warlock check`, `warlock typecheck`, `warlock explain`, `--changed`, `--base`, `runCheck`, `runTypecheck`, `CHECK_EXIT_CODES`, `ToolingProblem`, `WL7101`, `WL8003`, `errorCodeOf`, `explainErrorCode`, `WarlockError`, "verify my change", "run the checks", "is this failure my code or the machine", "what does WL3001 mean", "typecheck only changed files". Skip: app diagnostics at boot — the `warlock-doctor` topic; reading the app''s routes/models — the `warlock-inspect` topic; writing tests — the `test-http` / `test-service` topics.'
11195
+ description: 'Verify a change with one command: `warlock check [--changed] [--json]` runs an incremental typecheck, the app''s Oxlint, the tests affected by the change and framework convention checks, and reports one JSON object per problem `{ code, category, severity, source, file, line, column, message, hint, skill, docs, rule }` with exit 1 for problems in your code and 2 for problems with this machine (missing Redis/DB, busy port, Node version, missing tool). Also `warlock typecheck [--changed]`, `warlock explain <CODE>` and the stable `WL` error codes (`errorCodeOf`, `WarlockError`). Triggers: `warlock check`, `warlock typecheck`, `warlock explain`, `--changed`, `--base`, `runCheck`, `runTypecheck`, `CHECK_EXIT_CODES`, `ToolingProblem`, `WL7101`, `WL8003`, `errorCodeOf`, `explainErrorCode`, `WarlockError`, "verify my change", "run the checks", "is this failure my code or the machine", "what does WL3001 mean", "typecheck only changed files". Skip: app diagnostics at boot — the `warlock-doctor` topic; reading the app''s routes/models — the `warlock-inspect` topic; writing tests — the `test-http` / `test-service` topics.'
10974
11196
  ---
10975
11197
 
10976
11198
  # Warlock — `warlock check`, `typecheck`, `explain`
@@ -11090,10 +11312,10 @@ const types = runTypecheck({ files: ["src/app/posts/services/publish.service.ts"
11090
11312
  ## Gotchas
11091
11313
 
11092
11314
  - `check` boots nothing — no config, no connection, no port — so it runs the same with or without services; only tests that need them fail, and those report `WL8003`/`WL8004`.
11093
- - Lint defaults to Oxlint (an app-root config + the app-installed package). No Oxlint config skips lint. A missing package reports `WL8002`. ESLint requires `--lint eslint` (or `runCheck({ lint: "eslint" })`); its flat config alone never enables it. This deprecated selection prints one warning on stderr and is removed in 5.34.0.
11315
+ - Lint defaults to Oxlint (an app-root config + the app-installed package). No Oxlint config skips lint. A missing package reports `WL8002`. ESLint support was removed in 5.34.0; see `upgrade-from-5-29`.
11094
11316
  - Tests use the app's own vitest (`vitest run`, or `vitest related <files> --run` with `--changed`). Test files without vitest installed → `WL8002`.
11095
11317
  - The typecheck runs in a child `warlock typecheck` process; running out of memory is reported as `WL8007` instead of ending the run.
11096
- - The convention checks are the framework's own and run the same with ESLint, Oxlint or no linter. A deliberate exception gets `// warlock-check-ignore WL9006 -- why` on the problem's line or the line above (no code = every code on that line).
11318
+ - The convention checks are the framework's own and run the same with Oxlint or no linter. A deliberate exception gets `// warlock-check-ignore WL9006 -- why` on the problem's line or the line above (no code = every code on that line).
11097
11319
 
11098
11320
  ## See also
11099
11321
 
@@ -11541,9 +11763,8 @@ A `⚠ routes: 0 routes registered` line is the tell that a route module threw o
11541
11763
 
11542
11764
  `warlock doctor` checks application wiring and HTTP health probes. Use
11543
11765
  `npx warlock check` for source lint diagnostics and `npm run format:check` for
11544
- formatting. In 5.33, Oxlint/Oxfmt are the defaults; ESLint requires
11545
- `warlock check --lint eslint` or `devServer.healthCheckers.eslint: true` for
11546
- background checks. ESLint/Prettier are deprecated and removed in 5.34.0.
11766
+ formatting. Oxlint/Oxfmt are the only supported tools in 5.34.0. Use the
11767
+ `oxc-toolchain-removal` upgrade step to migrate remaining legacy configs.
11547
11768
  Migrate with `npx warlock upgrade --from 5.32 --to 5.33.0` (preview with `--dry-run`).
11548
11769
 
11549
11770
 
@@ -12349,7 +12570,7 @@ CLI commands, scheduled jobs, queue workers — anything running outside an HTTP
12349
12570
 
12350
12571
  ---
12351
12572
  name: write-cli-command
12352
- description: 'Author a custom `warlock <my-cmd>` command via the `command()` factory — name, description, action, options, preload, then register in `warlock.config.ts > cli.commands` or drop in `src/app/<module>/commands/`. Also covers built-in `warlock add` feature scaffolding, including the Web starter and `index.register.ts`. Triggers: `command`, `CLICommand`, `CLICommandPreload`, `CLICommandOption`, `preload`, `preAction`, `persistent`, `colors`, `warlock add`, `warlock add bull-board`, `index.register.ts`; "write a custom warlock command", "one-off maintenance task", "ship a CLI from a package", "framework built-in commands"; typical import `import { command } from "@warlock.js/core"`. Skip: framework dev/build/start — the `run-app` topic; warlock.config.ts wiring — the `configure-app` topic; competing libs `commander`, `yargs`, `oclif`.'
12573
+ description: 'Author a custom `warlock <my-cmd>` command via the `command()` factory — name, description, action, options, preload, then register in `warlock.config.ts > cli.commands` or drop in `src/app/<module>/commands/`. Also covers built-in `warlock add` feature scaffolding, including the Web starter and `index.setup.ts`. Triggers: `command`, `CLICommand`, `CLICommandPreload`, `CLICommandOption`, `preload`, `preAction`, `persistent`, `colors`, `warlock add`, `warlock add bull-board`, `index.setup.ts`; "write a custom warlock command", "one-off maintenance task", "ship a CLI from a package", "framework built-in commands"; typical import `import { command } from "@warlock.js/core"`. Skip: framework dev/build/start — the `run-app` topic; warlock.config.ts wiring — the `configure-app` topic; competing libs `commander`, `yargs`, `oclif`.'
12353
12574
  ---
12354
12575
 
12355
12576
  # Warlock — write a CLI command
@@ -12579,7 +12800,11 @@ Run `warlock add --list` to see what's currently registered. Pass `--packageMana
12579
12800
  is loaded automatically by `warlock dev` when installed, and is omitted from a
12580
12801
  production install with dev dependencies excluded.
12581
12802
 
12582
- The Web starter keeps the page's universal non-component setup in `src/web/index.register.ts`, while `src/web/index.page.tsx` re-exports `register`. Warlock still discovers and runs the hook from the page namespace in both SSR and the browser, but editing the page component remains inside React's Fast Refresh boundary. Do not move the `register()` declaration inline beside the component: React Refresh treats that non-component export as incompatible and reloads the page, losing component state.
12803
+ `warlock add web` creates `root.tsx` + `root.setup.ts` and `index.page.tsx` +
12804
+ `index.setup.ts`. Setup files own configuration and universal `register()`
12805
+ logic; UI files own components. Warlock composes the pairs on the server and
12806
+ projects browser-safe registration to the client, preserving the component
12807
+ Fast Refresh boundary.
12583
12808
 
12584
12809
  ### AI input measurement
12585
12810
 
package/llms.txt CHANGED
@@ -31,7 +31,8 @@
31
31
  - [send-mail](@warlock.js/core/send-mail/SKILL.md): Send transactional email — `Mail` fluent builder, `sendMail()` direct call, React Email components. Test mode auto-captures into an in-memory mailbox; dev mode logs. Triggers: `Mail.to`, `sendMail`, `setMailMode`, `mailEvents`, `assertMailSent`, `getTestMailbox`, `wasMailSentTo`, `closeAllMailers`; "send a transactional email", "build a React Email template", "configure SMTP or SES", "assert an email was sent in tests"; typical import `import { Mail, sendMail } from "@warlock.js/core"`. Skip: per-config wiring — the `configure-app` topic; layered service patterns — the `warlock-conventions` topic; competing libs `nodemailer` direct, `@sendgrid/mail`, `resend`, `mailgun.js`.
32
32
  - [send-response](@warlock.js/core/send-response/SKILL.md): Send HTTP responses via @warlock.js/core's Response helpers — success/error variants, status helpers, redirects, files, streams, and SSE. Picking the right helper carries the HTTP semantic without manual status codes. Triggers: `response.success`, `response.successCreate`, `response.notFound`, `response.forbidden`, `response.badRequest`, `response.sendFile`, `response.stream`, `response.sse`, `response.replay`, `retainEvents`, `retainChunks`, `backpressure`, `@warlock.js/core/errors`, `ResourceNotFoundError`, `ForbiddenError`, `request.cookie`, `request.hasCookie`, `CookieJarUnavailableError`; "return a 201 from a controller", "send a file", "stream Server-Sent Events", "throw HTTP-shaped errors from services"; typical import `import type { RequestHandler, Response } from "@warlock.js/core"`. Skip: controller shape — the `create-controller` topic; route registration — the `register-route` topic; competing patterns: hand-rolled status codes via `reply.code(404).send(...)`, raw Fastify reply.
33
33
  - [store-file](@warlock.js/core/store-file/SKILL.md): Read/write/delete files via the `storage` singleton — disks, drivers (local/S3/R2/DO Spaces), `storage.use(name)`, `StorageFile` handles, presigned URLs. Triggers: `storage.put`, `storage.get`, `storage.use`, `StorageFile`, `storageConfigurations`, `getPresignedUrl`, `getPresignedUploadUrl`; "save an uploaded file", "switch between local and S3", "generate a presigned URL", "read file metadata"; typical import `import { storage } from "@warlock.js/core"`. Skip: multipart parsing + image chain — the `upload-file` topic; image transforms — the `process-image` topic; storage config shape — the `configure-app` topic; competing libs `@aws-sdk/client-s3`, `multer`, `formidable`.
34
- - [store-secrets](@warlock.js/core/store-secrets/SKILL.md): Store app credentials with secrets.get/set/remove/has and environmentName; lazy optional OS keychain, read-only scoped environment variables, encrypted file fallback, typed SecretStoreError and memory-backed secretsFake. Use for provider keys and OAuth credentials the app must read back. Skip user passwords (hash-password) and encrypted database columns (encrypt-data).
34
+ - [store-secrets](@warlock.js/core/store-secrets/SKILL.md): Store app credentials with secrets.get/set/remove/has, backend() and environmentName; lazy optional OS keychain, read-only scoped environment variables, encrypted file fallback, typed SecretStoreError and memory-backed secretsFake. Use for provider keys and OAuth credentials the app must read back. Skip user passwords (hash-password) and encrypted database columns (encrypt-data).
35
+ - [test-browser](@warlock.js/core/test-browser/SKILL.md): Write or repair browser e2e specs using warlock e2e and @warlock.js/core/e2e, with owned app fixtures, direct CDP interaction, responsive screenshots and run evidence. Use for real browser flows, click races or viewport overflow; use test-http for API-only tests.
35
36
  - [test-fakes](@warlock.js/core/test-fakes/SKILL.md): The test fakes Warlock installs by default in the test environment — `mailFake`, `queueFake`, `cacheFake`, `storageFake`, `clockFake`, `idsFake`, `secretsFake` — and their assertions (`assertSent`, `assertPushed`, `assertHas`, `assertExists`, `travel`), plus how to opt out (`tests.fakes`, `setupTest({ fakes })`). Triggers: `mailFake`, `queueFake`, `cacheFake`, `storageFake`, `clockFake`, `idsFake`, `secretsFake`, `installTestFakes`, `restoreTestFakes`, `tests.fakes`, `FakeAssertionError`, `clock.now`, `ids.uuid`, `setDispatchInterceptor`; "assert an email was sent", "assert a job was dispatched", "freeze time in a test", "deterministic ids", "tests without Redis", "keep the real cache driver in tests". Typical import `import { mailFake, queueFake } from "@warlock.js/core/tests"`. Skip: HTTP request helpers — the `test-http` topic; fixtures — the `model-factories` topic.
36
37
  - [test-http](@warlock.js/core/test-http/SKILL.md): Integration tests against a real HTTP server — `startHttpTestServer()` boots one shared server in globalSetup, then `testGet` / `testPost` / `expectJson` make typed requests against it. Triggers: `startHttpTestServer`, `startHttpTestServer({ port })`, `startHttpTestServer({ port: 0 })`, `stopHttpTestServer`, `createTestHttpServer`, `TestHttpServer`, `createTestRequestClient`, `snapshotResponse`, `actingAs`, `AuthenticatedTestClient`, `testGet`, `testPost`, `testPut`, `testPatch`, `testDelete`, `expectJson`, `getTestServerUrl`, `testRequest`, `PortInUseError`, `assertPortIsAvailable`, `isPortAvailable`; "integration-test a controller", "end-to-end HTTP test", "globalSetup HTTP server", "assert status and body shape", "test server port already in use", "EADDRINUSE while running tests", "run tests while the dev server is up", "ephemeral test port", "test with the real Fastify plugins", "assert mail sent by a request", "response snapshot"; typical import `import { testGet, testPost, expectJson } from "@warlock.js/core/tests"`. Skip: pure unit tests — the `test-service` topic; controller shape — the `create-controller` topic; competing libs `supertest`, `light-my-request`, `nock`.
37
38
  - [test-service](@warlock.js/core/test-service/SKILL.md): Pure unit tests against services, repositories, models, and use-cases — `setupTest({ connectors })` bootstraps the framework with its own DB/cache connections so you can call your code directly, and `teardownTest()` closes it. Triggers: `setupTest`, `teardownTest`, `setupTest({ fakes })`, `tests.fakes`, `src/test-setup.ts`, `tests.connectors`, `tests.setupTimeout`, `Application.setEnvironment`, `clock`, `ids`, `warlock test --changed`; "unit-test a service", "test a repository query", "vitest setupFiles", "skip connectors for pure-logic tests", "run only the tests my change affects", "deterministic time in a test"; typical import `import { setupTest, teardownTest } from "@warlock.js/core/tests"`. Skip: HTTP integration — the `test-http` topic; warlock add test scaffold — the `write-cli-command` topic; competing tooling: jest direct, `supertest`, `nock`.
@@ -47,7 +48,7 @@
47
48
  - [validate-input](@warlock.js/core/validate-input/SKILL.md): Author seal schemas, attach them to controllers via `controller.validation = { schema }`, infer types via `Infer<typeof schema>`, and layer DB-aware (`unique`/`exists`) and file validators on top. Triggers: `v.object`, `v.string`, `v.email`, `Infer`, `controller.validation`, `.unique`, `.exists`, `uniqueExceptCurrentId`, `request.validated`; "validate a request body", "attach a schema to a controller", "DB-aware unique rule", "infer schema types"; typical import `import { v, type Infer } from "@warlock.js/seal"`. Skip: schema authoring foundations — the `seal-basics` topic of the `warlock-js-seal` skill; controller wiring — the `create-controller` topic; file rules deep-dive — the `upload-file` topic; competing libs `zod`, `joi`, `yup`, `class-validator`.
48
49
  - [warlock-agents-md](@warlock.js/core/warlock-agents-md/SKILL.md): Generate the app-specific section of AGENTS.md from the booted app with `warlock agents-md [--write] [--json]` — modules, a routes summary with unguarded writes flagged, models, the conventions `warlock check` enforces, the installed skills and the verify commands — between `<!-- warlock:generated:start -->` / `<!-- warlock:generated:end -->` markers so hand-written content is never touched; `warlock dev` refreshes it (opt out with `devServer.agentsMd: false`). Triggers: `warlock agents-md`, `AGENTS.md`, `warlock:generated:start`, `renderAgentsMdSection`, `writeAgentsMd`, `upsertGeneratedSection`, `listInstalledSkills`, `createAgentsMdRefresher`, `devServer.agentsMd`, `WL7501`; "give agents the app facts", "keep AGENTS.md up to date", "which skills apply to this app". Skip: the full machine-readable report — the `warlock-inspect` topic; verifying a change — the `warlock-check` topic.
49
50
  - [warlock-board](@warlock.js/core/warlock-board/SKILL.md): Coordinate two coding agents (an implementer and a reviewer) through `warlock board`: work items with a review state machine (todo, in-progress, ready-for-review, changes-requested, approved, blocked), append-only numbered messages per recipient in the git-ignored `.warlock/board/`, `VERDICT: APPROVED` / `VERDICT: CHANGES REQUESTED` lines that move the item, and a blocking `watch` with a timeout; `--json` on every action. Triggers: `warlock board`, `board init`, `board add`, `board status`, `board send`, `board watch`, `--verdict`, `WARLOCK_AGENT`, `.warlock/board`, `to-reviewer/`, `initBoard`, `addBoardItem`, `moveBoardItem`, `sendBoardMessage`, `watchBoard`, `readBoardStatus`, `WL7401`, `WL7402`, `WL7403`, `WL7404`; "hand this to the reviewer agent", "wait for the review", "two agents working together", "request changes", "approve the item". Skip: verifying the code itself — the `warlock-check` topic; app facts — the `warlock-inspect` topic.
50
- - [warlock-check](@warlock.js/core/warlock-check/SKILL.md): Verify a change with one command: `warlock check [--changed] [--json]` runs an incremental typecheck, the app's ESLint/oxlint, the tests affected by the change and framework convention checks, and reports one JSON object per problem `{ code, category, severity, source, file, line, column, message, hint, skill, docs, rule }` with exit 1 for problems in your code and 2 for problems with this machine (missing Redis/DB, busy port, Node version, missing tool). Also `warlock typecheck [--changed]`, `warlock explain <CODE>` and the stable `WL` error codes (`errorCodeOf`, `WarlockError`). Triggers: `warlock check`, `warlock typecheck`, `warlock explain`, `--changed`, `--base`, `runCheck`, `runTypecheck`, `CHECK_EXIT_CODES`, `ToolingProblem`, `WL7101`, `WL8003`, `errorCodeOf`, `explainErrorCode`, `WarlockError`, "verify my change", "run the checks", "is this failure my code or the machine", "what does WL3001 mean", "typecheck only changed files". Skip: app diagnostics at boot — the `warlock-doctor` topic; reading the app's routes/models — the `warlock-inspect` topic; writing tests — the `test-http` / `test-service` topics.
51
+ - [warlock-check](@warlock.js/core/warlock-check/SKILL.md): Verify a change with one command: `warlock check [--changed] [--json]` runs an incremental typecheck, the app's Oxlint, the tests affected by the change and framework convention checks, and reports one JSON object per problem `{ code, category, severity, source, file, line, column, message, hint, skill, docs, rule }` with exit 1 for problems in your code and 2 for problems with this machine (missing Redis/DB, busy port, Node version, missing tool). Also `warlock typecheck [--changed]`, `warlock explain <CODE>` and the stable `WL` error codes (`errorCodeOf`, `WarlockError`). Triggers: `warlock check`, `warlock typecheck`, `warlock explain`, `--changed`, `--base`, `runCheck`, `runTypecheck`, `CHECK_EXIT_CODES`, `ToolingProblem`, `WL7101`, `WL8003`, `errorCodeOf`, `explainErrorCode`, `WarlockError`, "verify my change", "run the checks", "is this failure my code or the machine", "what does WL3001 mean", "typecheck only changed files". Skip: app diagnostics at boot — the `warlock-doctor` topic; reading the app's routes/models — the `warlock-inspect` topic; writing tests — the `test-http` / `test-service` topics.
51
52
  - [warlock-conventions](@warlock.js/core/warlock-conventions/SKILL.md): Framework-wide invariants for projects built on @warlock.js/core — module layout, canonical imports, layered flow, file naming, and the non-negotiable rules every other warlock skill assumes. Triggers: `src/app/<module>`, `routes.ts`, `main.ts`, `Request<TSchema>`, `RequestHandler`, `GuardedRequestHandler`, `app/<module>/...`; "where do files go in this project", "canonical Warlock imports", "module layout rules", "controller-service-repository layering"; typical import `import { router, type RequestHandler } from "@warlock.js/core"`. Skip: scaffold a new module — the `create-module` topic; route shape — the `register-route` topic; controller shape — the `create-controller` topic; competing patterns: `express` ad-hoc layouts, `@nestjs/common` decorator-driven structure.
52
53
  - [warlock-doctor](@warlock.js/core/warlock-doctor/SKILL.md): Run `warlock doctor` — a read-only diagnostics command that checks routes / config / connectors / optional-peers / health endpoints / release hygiene and prints a pass/warn/fail report, exiting non-zero on any failure. Add your own probe with the `DoctorCheck` contract and `runChecks` / `formatReportLines`. Triggers: `warlock doctor`, `warlock doctor --json`, `toDoctorJson`, `WL1001`, `doctorCommand`, `DoctorCheck`, `CheckResult`, `CheckStatus`, `DoctorReport`, `runChecks`, `formatReportLines`, `printReport`, `defaultDoctorChecks`; "diagnose my app", "preflight / preflight check", "is the app healthy", "why are there 0 routes", "pre-release sanity check", "CI smoke check"; run as `npx warlock doctor`. Skip: the live `/health` + `/ready` HTTP probes — the `health-checks` topic; authoring a general CLI command — the `write-cli-command` topic; releasing the package — `releasing-warlock-monorepo`; competing tools `npm doctor`, `nest info`, hand-rolled preflight scripts.
53
54
  - [warlock-inspect](@warlock.js/core/warlock-inspect/SKILL.md): Ask the app instead of grepping it: `warlock inspect --json` prints the facts of the booted app — routes (middleware chain, auth guard, CSRF, rate limit, logging, request/response JSON Schema), models (table, columns, relations, resource), resources, config keys and types, event listeners, pages (layouts, loaders, actions), queue jobs, scheduled tasks and installed @warlock.js versions — as a versioned report (`schemaVersion: 1`) with a published JSON Schema; `inspectApp()` returns the same object. Triggers: `warlock inspect`, `--section`, `inspect_routes`, `inspectApp`, `bootForInspect`, `InspectReport`, `INSPECT_REPORT_JSON_SCHEMA`, `inspect-report.schema.json`, `@warlock.js/core/tooling`; "which routes are unguarded", "what does this app expose", "what models and columns exist", "which config keys are set", "list event listeners", "app facts as JSON for an agent". Skip: a route table for humans — the `warlock-routes` topic; health diagnostics — the `warlock-doctor` topic; verifying a change — the `warlock-check` topic; an OpenAPI file — the `generate-openapi` topic.
@@ -55,7 +56,7 @@
55
56
  - [warlock-routes](@warlock.js/core/warlock-routes/SKILL.md): Run `warlock routes` — a read-only command that lists the registered HTTP routes as a verb-colored table (method / path / name / action / middleware-count / source), a sibling of `warlock doctor`. Filter with `--method` / `--path` / `--name`, or emit normalized rows as JSON with `--json`. Also covers `warlock routes:diff`, which compares live page routes against the last `warlock build`'s route snapshot and exits non-zero on drift. Triggers: `warlock routes`, `routesCommand`, `warlock routes:diff`, `routesDiffCommand`, "list my routes", "show all routes", "route table", "what endpoints does my app expose", "dump routes as JSON", "which routes have middleware", "route map for CI", "did my page routes drift from the last build"; run as `npx warlock routes` / `npx warlock routes:diff`. Skip: read-only health/preflight checks — the `warlock-doctor` topic; defining/naming/grouping routes — the `register-route` topic; authoring a general CLI command — the `write-cli-command` topic; competing tools `nest`/`express` route listers, `php artisan route:list`.
56
57
  - [warlock-trace](@warlock.js/core/warlock-trace/SKILL.md): Verify runtime behaviour without a browser: `warlock trace <method> <url> [--body json|@file] [--header k:v] [--json]` sends one request to the running `warlock dev` server and prints what devtools recorded for it — middleware chain with timings, validation result and errors, handler time, queries with N+1 flags, logs, cache operations, captured mails, the response and timings — as a versioned report (`schemaVersion: 1`); `traceRequest()` returns the same object. Triggers: `warlock trace`, `traceRequest`, `TraceReport`, `buildTraceReport`, `x-request-id`, `.warlock/dev-server.json`, `WL8008`, `WL8009`, `WL5004`, `--id-header`; "did validation pass for this request", "which queries does this endpoint run", "is there an N+1 on this route", "what middleware runs on this route", "check the endpoint after my change", "request timeline as JSON". Skip: the devtools dashboard in a browser — the `devtools-overview` skill of @warlock.js/devtools; static route facts (guard, rate limit, schemas) — the `warlock-inspect` topic; typecheck/lint/tests — the `warlock-check` topic.
57
58
  - [wire-socket](@warlock.js/core/wire-socket/SKILL.md): Configure Socket.IO via `src/config/socket.ts`, reach the live server through `getSocketServer()` (or `app.socket` post-bootstrap), register `connection` handlers once the late-phase socket connector has booted, emit from controllers/services, use rooms and namespaces. Triggers: `app.socket`, `getSocketServer`, `SocketOptions`, `socket.io` `Server`, `socket.join`, `socket.to`, `io.of`, `io.use`; "add realtime chat", "emit socket events from a service", "use rooms and namespaces", "per-socket JWT auth". Skip: connector lifecycle — the `add-connector` topic; app context accessors — the `use-app-context` topic; competing libs `ws`, `socket.io` direct without Warlock connector, `uWebSockets.js`.
58
- - [write-cli-command](@warlock.js/core/write-cli-command/SKILL.md): Author a custom `warlock <my-cmd>` command via the `command()` factory — name, description, action, options, preload, then register in `warlock.config.ts > cli.commands` or drop in `src/app/<module>/commands/`. Also covers built-in `warlock add` feature scaffolding, including the Web starter and `index.register.ts`. Triggers: `command`, `CLICommand`, `CLICommandPreload`, `CLICommandOption`, `preload`, `preAction`, `persistent`, `colors`, `warlock add`, `warlock add bull-board`, `index.register.ts`; "write a custom warlock command", "one-off maintenance task", "ship a CLI from a package", "framework built-in commands"; typical import `import { command } from "@warlock.js/core"`. Skip: framework dev/build/start — the `run-app` topic; warlock.config.ts wiring — the `configure-app` topic; competing libs `commander`, `yargs`, `oclif`.
59
+ - [write-cli-command](@warlock.js/core/write-cli-command/SKILL.md): Author a custom `warlock <my-cmd>` command via the `command()` factory — name, description, action, options, preload, then register in `warlock.config.ts > cli.commands` or drop in `src/app/<module>/commands/`. Also covers built-in `warlock add` feature scaffolding, including the Web starter and `index.setup.ts`. Triggers: `command`, `CLICommand`, `CLICommandPreload`, `CLICommandOption`, `preload`, `preAction`, `persistent`, `colors`, `warlock add`, `warlock add bull-board`, `index.setup.ts`; "write a custom warlock command", "one-off maintenance task", "ship a CLI from a package", "framework built-in commands"; typical import `import { command } from "@warlock.js/core"`. Skip: framework dev/build/start — the `run-app` topic; warlock.config.ts wiring — the `configure-app` topic; competing libs `commander`, `yargs`, `oclif`.
59
60
  - [write-middleware](@warlock.js/core/write-middleware/SKILL.md): Author HTTP middleware for @warlock.js/core — the `({ request, response })` signature, short-circuit by returning a response, enrich the request with extra fields, register per-route, per-group, or app-wide. Triggers: `Middleware`, `MiddlewareResponse`, `router.group`, `guarded`, `request.detectIp`, `authMiddleware`; "write a custom middleware", "short-circuit a request", "enrich the request with extra fields", "per-route vs per-group middleware"; typical import `import type { Middleware } from "@warlock.js/core"`. Skip: built-in middleware catalog — the `use-middleware` topic; route attachment — the `register-route` topic; response helpers — the `send-response` topic; competing patterns: `express` `(req, res, next)` middleware, Fastify `preHandler` hooks.
60
61
  - [write-seeder](@warlock.js/core/write-seeder/SKILL.md): Author a seed file under `src/app/<module>/seeds/<name>.ts` using the `seeder()` factory — `name`, `dependsOn`, `once`, `order`, `batchSize`, `run({ track, now, batchSize })`. Auto-discovered by `warlock seed`; tracked in a `seeds` table; per-record refs in `seed_records` so `warlock seed --drop` can undo a seed. Triggers: `seeder`, `Seeder`, `SeedResult`, `SeedContext`, `SeedClock`, `track`, `now`, `batchSize`, `SeedersManager`, `warlock seed`, `--fresh`, `--drop`, `--list`, `--path`; "seed default roles", "undo a seed", "one-time data migration", "auto-discovered seeds", "order seeds by dependency", "deterministic seed timestamps", "inject a seed clock"; typical import `import { seeder } from "@warlock.js/core"`. Skip: module folder layout — the `create-module` topic; repository CRUD — the `use-repository` topic; CLI flags — the `write-cli-command` topic; competing patterns: hand-rolled `node scripts/seed.js`, `typeorm-seeding`.
61
62
  - [write-use-case](@warlock.js/core/write-use-case/SKILL.md): Author `useCase()` pipelines for business logic — guards, schema, before/after middleware, retry, benchmark, broadcast, lifecycle callbacks; transport-agnostic and observable by default. Input is inferred from the `schema`. Triggers: `useCase`, `UseCaseContext`, `UseCaseResult`, `retry`, `benchmark`, `broadcast`, `description`, `globalUseCasesEvents`, `UseCaseBroadcastChannel`; "encapsulate a business operation", "share logic between HTTP and CLI", "add guards and lifecycle hooks", "broadcast a use case result", "transport-agnostic pipeline"; typical import `import { useCase } from "@warlock.js/core"`. Skip: thin handler shape — the `create-controller` topic; schema details — the `validate-input` topic; the standalone retry util — the `retry-operation` topic; competing libs `@nestjs/cqrs`, `inversify`, hand-rolled service classes.
package/package.json CHANGED
@@ -25,18 +25,17 @@
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.33.0",
29
- "@warlock.js/cascade": "5.33.0",
30
- "@warlock.js/context": "5.33.0",
31
- "@warlock.js/fs": "5.33.0",
32
- "@warlock.js/logger": "5.33.0",
33
- "@warlock.js/seal": "5.33.0",
28
+ "@warlock.js/cache": "5.34.1",
29
+ "@warlock.js/cascade": "5.34.1",
30
+ "@warlock.js/context": "5.34.1",
31
+ "@warlock.js/fs": "5.34.1",
32
+ "@warlock.js/logger": "5.34.1",
33
+ "@warlock.js/seal": "5.34.1",
34
34
  "bcryptjs": "^3.0.3",
35
35
  "chokidar": "^5.0.0",
36
36
  "dayjs": "^1.11.19",
37
37
  "es-module-lexer": "^2.0.0",
38
38
  "esbuild": "^0.28.2",
39
- "eslint": "^9.18.0",
40
39
  "fast-glob": "^3.3.3",
41
40
  "fastify": "^5.12.5",
42
41
  "find-my-way": "^9.9.0",
@@ -56,12 +55,12 @@
56
55
  "@aws-sdk/lib-storage": "^3.955.0",
57
56
  "@aws-sdk/s3-request-presigner": "^3.955.0",
58
57
  "@react-email/render": "^2.0.5",
59
- "@warlock.js/access": "5.33.0",
60
- "@warlock.js/ai": "5.33.0",
61
- "@warlock.js/ai-tools": "5.33.0",
62
- "@warlock.js/devtools": "5.33.0",
63
- "@warlock.js/herald": "5.33.0",
64
- "@warlock.js/notifications": "5.33.0",
58
+ "@warlock.js/access": "5.34.1",
59
+ "@warlock.js/ai": "5.34.1",
60
+ "@warlock.js/ai-tools": "5.34.1",
61
+ "@warlock.js/devtools": "5.34.1",
62
+ "@warlock.js/herald": "5.34.1",
63
+ "@warlock.js/notifications": "5.34.1",
65
64
  "nodemailer": "^8.0.5",
66
65
  "react": "^19.2.3",
67
66
  "react-dom": "^19.2.3",
@@ -146,7 +145,7 @@
146
145
  "engines": {
147
146
  "node": ">=22.15"
148
147
  },
149
- "version": "5.33.0",
148
+ "version": "5.34.1",
150
149
  "type": "module",
151
150
  "main": "./esm/index.mjs",
152
151
  "module": "./esm/index.mjs",
@@ -170,6 +169,12 @@
170
169
  "default": "./esm/tests/index.mjs"
171
170
  }
172
171
  },
172
+ "./e2e": {
173
+ "import": {
174
+ "types": "./esm/e2e/index.d.mts",
175
+ "default": "./esm/e2e/index.mjs"
176
+ }
177
+ },
173
178
  "./vite": {
174
179
  "import": {
175
180
  "types": "./esm/vite/index.d.mts",
@@ -206,12 +211,6 @@
206
211
  "default": "./esm/production/route-registration-child.mjs"
207
212
  }
208
213
  },
209
- "./dev-server/health-checker/workers/eslint-health.worker": {
210
- "import": {
211
- "types": "./esm/dev-server/health-checker/workers/eslint-health.worker.d.mts",
212
- "default": "./esm/dev-server/health-checker/workers/eslint-health.worker.mjs"
213
- }
214
- },
215
214
  "./dev-server/health-checker/workers/oxlint-health.worker": {
216
215
  "import": {
217
216
  "types": "./esm/dev-server/health-checker/workers/oxlint-health.worker.d.mts",
@@ -191,7 +191,7 @@ await warmupProductCache();
191
191
 
192
192
  ## Verify generated imports
193
193
 
194
- Generated web starter files match the scaffold’s Prettier configuration, using
195
- `@ianvs/prettier-plugin-sort-imports` with Babel independently of TypeScript 7.
194
+ Generated web starter files match the scaffold’s Oxfmt configuration and its
195
+ built-in import sorting.
196
196
  After editing generated code, run `npm run format` and `npm run lint:check`.
197
197
  Formatting retains unused imports; the linter reports them.