@warlock.js/core 5.28.0 → 5.29.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.
Files changed (79) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/esm/container/development-app-modules.d.mts +31 -0
  3. package/esm/container/development-app-modules.d.mts.map +1 -0
  4. package/esm/container/index.d.mts +2 -0
  5. package/esm/container/index.d.mts.map +1 -1
  6. package/esm/container/index.mjs.map +1 -1
  7. package/esm/dev-server/app-module-registry.mjs +52 -0
  8. package/esm/dev-server/app-module-registry.mjs.map +1 -0
  9. package/esm/dev-server/files-orchestrator.mjs +13 -2
  10. package/esm/dev-server/files-orchestrator.mjs.map +1 -1
  11. package/esm/index.d.mts +3 -1
  12. package/esm/index.mjs +2 -1
  13. package/esm/localization/parse-locale-dictionary.d.mts +6 -1
  14. package/esm/localization/parse-locale-dictionary.d.mts.map +1 -1
  15. package/esm/localization/parse-locale-dictionary.mjs +5 -1
  16. package/esm/localization/parse-locale-dictionary.mjs.map +1 -1
  17. package/esm/openapi/response-schemas.mjs +4 -1
  18. package/esm/openapi/response-schemas.mjs.map +1 -1
  19. package/esm/openapi/schema-registry.mjs +6 -0
  20. package/esm/openapi/schema-registry.mjs.map +1 -1
  21. package/esm/resource/index.d.mts +1 -0
  22. package/esm/resource/index.mjs +1 -0
  23. package/esm/resource/nullable-resource.d.mts +44 -0
  24. package/esm/resource/nullable-resource.d.mts.map +1 -0
  25. package/esm/resource/nullable-resource.mjs +38 -0
  26. package/esm/resource/nullable-resource.mjs.map +1 -0
  27. package/esm/resource/resource.d.mts.map +1 -1
  28. package/esm/resource/resource.mjs +22 -3
  29. package/esm/resource/resource.mjs.map +1 -1
  30. package/esm/resource/types.d.mts +10 -5
  31. package/esm/resource/types.d.mts.map +1 -1
  32. package/esm/router/response-schema-types.mjs +2 -0
  33. package/esm/router/response-schema-types.mjs.map +1 -1
  34. package/llms-full.txt +241 -241
  35. package/llms.txt +42 -42
  36. package/package.json +12 -12
  37. package/skills/README.md +39 -39
  38. package/skills/add-connector/SKILL.md +4 -4
  39. package/skills/benchmark-code/SKILL.md +3 -3
  40. package/skills/build-restful/SKILL.md +5 -5
  41. package/skills/build-url/SKILL.md +7 -7
  42. package/skills/configure-app/SKILL.md +7 -7
  43. package/skills/create-controller/SKILL.md +8 -8
  44. package/skills/create-module/SKILL.md +9 -9
  45. package/skills/define-resource/SKILL.md +4 -4
  46. package/skills/encrypt-data/SKILL.md +7 -7
  47. package/skills/generate-openapi/SKILL.md +5 -5
  48. package/skills/hash-password/SKILL.md +8 -8
  49. package/skills/health-checks/SKILL.md +6 -6
  50. package/skills/lower-stage3-decorators/SKILL.md +4 -4
  51. package/skills/process-image/SKILL.md +5 -5
  52. package/skills/register-route/SKILL.md +6 -6
  53. package/skills/request-memo/SKILL.md +4 -4
  54. package/skills/request-tracing/SKILL.md +6 -6
  55. package/skills/resolve-path/SKILL.md +6 -6
  56. package/skills/retry-operation/SKILL.md +4 -4
  57. package/skills/run-app/SKILL.md +11 -11
  58. package/skills/send-mail/SKILL.md +3 -3
  59. package/skills/send-response/SKILL.md +6 -6
  60. package/skills/store-file/SKILL.md +4 -4
  61. package/skills/test-http/SKILL.md +5 -5
  62. package/skills/test-service/SKILL.md +5 -5
  63. package/skills/update-packages/SKILL.md +6 -6
  64. package/skills/upload-file/SKILL.md +7 -7
  65. package/skills/use-app-context/SKILL.md +7 -7
  66. package/skills/use-localization/SKILL.md +6 -6
  67. package/skills/use-middleware/SKILL.md +6 -6
  68. package/skills/use-model-transformers/SKILL.md +6 -6
  69. package/skills/use-repository/SKILL.md +7 -7
  70. package/skills/use-request-locals/SKILL.md +4 -4
  71. package/skills/validate-input/SKILL.md +7 -7
  72. package/skills/warlock-conventions/SKILL.md +6 -6
  73. package/skills/warlock-doctor/SKILL.md +5 -5
  74. package/skills/warlock-routes/SKILL.md +6 -6
  75. package/skills/wire-socket/SKILL.md +4 -4
  76. package/skills/write-cli-command/SKILL.md +6 -6
  77. package/skills/write-middleware/SKILL.md +8 -8
  78. package/skills/write-seeder/SKILL.md +5 -5
  79. package/skills/write-use-case/SKILL.md +3 -3
package/llms-full.txt CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  ---
10
10
  name: add-connector
11
- description: 'Extend Warlock with a `BaseConnector` subclass and register its instance in `warlock.config.ts > connectors`. Covers runtime lifecycle (`boot`, `start`, `shutdown`, priority, phase, watched files) and the optional static `build.generate` / `build.emit` contribution used by `warlock build`. Triggers: `BaseConnector`, `connectors`, `ConnectorLifecyclePhase`, `ConnectorBuildContribution`, `build.generate`, `build.emit`; "add a queue worker", "wire a subsystem into bootstrap", "contribute files to warlock build", "control startup ordering", "graceful shutdown hook". Skip: app context accessors — `@warlock.js/core/use-app-context/SKILL.md`; general config shape — `@warlock.js/core/configure-app/SKILL.md`; competing pattern: hand-rolled process signal blocks, NestJS lifecycle hooks.'
11
+ description: 'Extend Warlock with a `BaseConnector` subclass and register its instance in `warlock.config.ts > connectors`. Covers runtime lifecycle (`boot`, `start`, `shutdown`, priority, phase, watched files) and the optional static `build.generate` / `build.emit` contribution used by `warlock build`. Triggers: `BaseConnector`, `connectors`, `ConnectorLifecyclePhase`, `ConnectorBuildContribution`, `build.generate`, `build.emit`; "add a queue worker", "wire a subsystem into bootstrap", "contribute files to warlock build", "control startup ordering", "graceful shutdown hook". Skip: app context accessors — the `use-app-context` topic; general config shape — the `configure-app` topic; competing pattern: hand-rolled process signal blocks, NestJS lifecycle hooks.'
12
12
  ---
13
13
 
14
14
  # Warlock — add a connector
@@ -243,16 +243,16 @@ See the [Registering a connector](#registering-a-connector) section above for th
243
243
 
244
244
  ## See also
245
245
 
246
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — `warlock.config.ts`, config files, env.
247
- - [`use-app-context/SKILL.md`](../use-app-context/SKILL.md) — checking environment + paths inside `start()`.
248
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — module layout, canonical imports.
246
+ - The `configure-app` topic — `warlock.config.ts`, config files, env.
247
+ - The `use-app-context` topic — checking environment + paths inside `start()`.
248
+ - The `warlock-conventions` topic — module layout, canonical imports.
249
249
 
250
250
 
251
251
  ## benchmark-code `@warlock.js/core/benchmark-code/SKILL.md`
252
252
 
253
253
  ---
254
254
  name: benchmark-code
255
- description: 'Wrap a function with `measure(name, fn, options?)` to time it and classify the latency — onComplete/onError/onFinish hooks, `latencyRange` thresholds, `BenchmarkProfiler` for percentiles, `BenchmarkSnapshots` for raw captures. Triggers: `measure`, `BenchmarkProfiler`, `BenchmarkSnapshots`, `BenchmarkChannel`, `ConsoleChannel`, `latencyRange`, `shouldBenchmarkError`; "time this operation", "profile a slow service", "emit p50/p95/p99 metrics", "classify latency against thresholds"; typical import `import { measure, BenchmarkProfiler } from "@warlock.js/core"`. Skip: retry composition — `@warlock.js/core/retry-operation/SKILL.md`; benchmark config wiring — `@warlock.js/core/configure-app/SKILL.md`; competing libs `prom-client`, `pino`, `perf_hooks`, `console.time`.'
255
+ description: 'Wrap a function with `measure(name, fn, options?)` to time it and classify the latency — onComplete/onError/onFinish hooks, `latencyRange` thresholds, `BenchmarkProfiler` for percentiles, `BenchmarkSnapshots` for raw captures. Triggers: `measure`, `BenchmarkProfiler`, `BenchmarkSnapshots`, `BenchmarkChannel`, `ConsoleChannel`, `latencyRange`, `shouldBenchmarkError`; "time this operation", "profile a slow service", "emit p50/p95/p99 metrics", "classify latency against thresholds"; typical import `import { measure, BenchmarkProfiler } from "@warlock.js/core"`. Skip: retry composition — the `retry-operation` topic; benchmark config wiring — the `configure-app` topic; competing libs `prom-client`, `pino`, `perf_hooks`, `console.time`.'
256
256
  ---
257
257
 
258
258
  # Warlock — benchmark code
@@ -504,15 +504,15 @@ The `latency` is the *total* time including retries — useful for the SLO you a
504
504
 
505
505
  ## See also
506
506
 
507
- - [`retry-operation/SKILL.md`](../retry-operation/SKILL.md) — wrapping flaky operations with retry; composes inside `measure()`.
508
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — wiring `src/config/benchmark.ts`.
507
+ - The `retry-operation` topic — wrapping flaky operations with retry; composes inside `measure()`.
508
+ - The `configure-app` topic — wiring `src/config/benchmark.ts`.
509
509
 
510
510
 
511
511
  ## build-restful `@warlock.js/core/build-restful/SKILL.md`
512
512
 
513
513
  ---
514
514
  name: build-restful
515
- description: 'Generate standard CRUD endpoints — via `router.route(path).list().show().create().update().destroy()` chain or the `Restful` base class. Pick the chain by default; reach for `Restful` when you want repository-bound defaults. Triggers: `router.route`, `Restful`, `router.restfulResource`, `RouteResource`, `.crud`, `.nest`, `beforeCreate`, `onCreate`; "build a CRUD API", "register list/show/create/update/destroy", "repository-bound default handlers", "override a single REST action"; typical import `import { router, Restful } from "@warlock.js/core"`. Skip: wider router surface — `@warlock.js/core/register-route/SKILL.md`; per-action controllers — `@warlock.js/core/create-controller/SKILL.md`; wire mapping — `@warlock.js/core/define-resource/SKILL.md`; competing pattern: hand-rolled controllers, `@nestjs/swagger` decorator-driven CRUD.'
515
+ description: 'Generate standard CRUD endpoints — via `router.route(path).list().show().create().update().destroy()` chain or the `Restful` base class. Pick the chain by default; reach for `Restful` when you want repository-bound defaults. Triggers: `router.route`, `Restful`, `router.restfulResource`, `RouteResource`, `.crud`, `.nest`, `beforeCreate`, `onCreate`; "build a CRUD API", "register list/show/create/update/destroy", "repository-bound default handlers", "override a single REST action"; typical import `import { router, Restful } from "@warlock.js/core"`. Skip: wider router surface — the `register-route` topic; per-action controllers — the `create-controller` topic; wire mapping — the `define-resource` topic; competing pattern: hand-rolled controllers, `@nestjs/swagger` decorator-driven CRUD.'
516
516
  ---
517
517
 
518
518
  # Warlock — build a RESTful resource
@@ -774,22 +774,22 @@ router.restfulResource("/orders", new OrdersRestful());
774
774
 
775
775
  ## See also
776
776
 
777
- - [`register-route/SKILL.md`](../register-route/SKILL.md) — the wider router surface (`group`, `prefix`, plain verbs).
778
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — what the chain's handlers look like.
779
- - [`define-resource/SKILL.md`](../define-resource/SKILL.md) — wire shape for the records the chain returns.
780
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — controller/service/repository layering rules.
777
+ - The `register-route` topic — the wider router surface (`group`, `prefix`, plain verbs).
778
+ - The `create-controller` topic — what the chain's handlers look like.
779
+ - The `define-resource` topic — wire shape for the records the chain returns.
780
+ - The `warlock-conventions` topic — controller/service/repository layering rules.
781
781
 
782
782
 
783
783
  ## build-url `@warlock.js/core/build-url/SKILL.md`
784
784
 
785
785
  ---
786
786
  name: build-url
787
- description: 'HTTP URL helpers — `url`, `publicUrl`, `assetsUrl`, `uploadsUrl`, anchored at `app.baseUrl`. Use to render `src` / `href` / API URLs in resources and responses. `setBaseUrl` is wired by the HTTP connector from `config.get("app.baseUrl")`. Triggers: `url`, `publicUrl`, `assetsUrl`, `uploadsUrl`, `setBaseUrl`, `BASE_URL`; "render an avatar src URL", "absolute download link", "embed asset URL in email", "URL helpers vs path helpers"; typical import `import { url, publicUrl, uploadsUrl } from "@warlock.js/core"`. Skip: filesystem paths — `@warlock.js/core/resolve-path/SKILL.md`; signed CDN URLs — `@warlock.js/core/store-file/SKILL.md`; resource output — `@warlock.js/core/define-resource/SKILL.md`; competing patterns: hand-rolled `${baseUrl}/...` template strings.'
787
+ description: 'HTTP URL helpers — `url`, `publicUrl`, `assetsUrl`, `uploadsUrl`, anchored at `app.baseUrl`. Use to render `src` / `href` / API URLs in resources and responses. `setBaseUrl` is wired by the HTTP connector from `config.get("app.baseUrl")`. Triggers: `url`, `publicUrl`, `assetsUrl`, `uploadsUrl`, `setBaseUrl`, `BASE_URL`; "render an avatar src URL", "absolute download link", "embed asset URL in email", "URL helpers vs path helpers"; typical import `import { url, publicUrl, uploadsUrl } from "@warlock.js/core"`. Skip: filesystem paths — the `resolve-path` topic; signed CDN URLs — the `store-file` topic; resource output — the `define-resource` topic; competing patterns: hand-rolled `${baseUrl}/...` template strings.'
788
788
  ---
789
789
 
790
790
  # Warlock — build a URL
791
791
 
792
- Path helpers ([`resolve-path/SKILL.md`](../resolve-path/SKILL.md)) give you absolute filesystem paths. URL helpers give you absolute HTTP URLs. Different jobs, often confused.
792
+ Path helpers (the `resolve-path` topic) give you absolute filesystem paths. URL helpers give you absolute HTTP URLs. Different jobs, often confused.
793
793
 
794
794
  Use URL helpers whenever you need to render a string that goes into a browser, an HTTP client, or an email — `<img src=...>`, `<a href=...>`, an API response that includes a download link, a webhook payload.
795
795
 
@@ -957,18 +957,18 @@ Don't call `setBaseUrl` per request — it's process-global and races every othe
957
957
 
958
958
  ## See also
959
959
 
960
- - [`resolve-path/SKILL.md`](../resolve-path/SKILL.md) — filesystem path helpers (`uploadsPath`, `publicPath`, ...). Use for read/write; use the URL helpers for rendering.
961
- - [`store-file/SKILL.md`](../store-file/SKILL.md) — the `storage.url(path)` driver method, which can produce signed CDN URLs for non-local drivers (S3 etc.). Use that over `uploadsUrl` when you're on a remote driver.
962
- - [`upload-file/SKILL.md`](../upload-file/SKILL.md) — the `UploadedFile` lifecycle; where the path that feeds `uploadsUrl` comes from.
963
- - [`define-resource/SKILL.md`](../define-resource/SKILL.md) — the `url` / `uploadsUrl` / `storageUrl` casts (and resolver functions) are the right place to call URL helpers.
964
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — `src/config/app.ts` and the `baseUrl` env wiring.
960
+ - The `resolve-path` topic — filesystem path helpers (`uploadsPath`, `publicPath`, ...). Use for read/write; use the URL helpers for rendering.
961
+ - The `store-file` topic — the `storage.url(path)` driver method, which can produce signed CDN URLs for non-local drivers (S3 etc.). Use that over `uploadsUrl` when you're on a remote driver.
962
+ - The `upload-file` topic — the `UploadedFile` lifecycle; where the path that feeds `uploadsUrl` comes from.
963
+ - The `define-resource` topic — the `url` / `uploadsUrl` / `storageUrl` casts (and resolver functions) are the right place to call URL helpers.
964
+ - The `configure-app` topic — `src/config/app.ts` and the `baseUrl` env wiring.
965
965
 
966
966
 
967
967
  ## configure-app `@warlock.js/core/configure-app/SKILL.md`
968
968
 
969
969
  ---
970
970
  name: configure-app
971
- description: 'Configure a Warlock app — the two layers (`warlock.config.ts` for framework-level wiring, `src/config/*.ts` for subsystems), `.env` + `env()`, the `config()` getter for runtime reads, and `app.publicUrl`/`PUBLIC_APP_URL` (the app''s public origin). Triggers: `defineConfig`, `config.get`, `config.key`, `env`, `ConfigRegistry`, `HttpConfigurations`, `AppConfigurations`, `publicUrl`, `PUBLIC_APP_URL`, `getPublicUrl`; "add a new config file", "warlock.config.ts vs src/config", "read env values", "runtime config lookup", "app public origin/URL"; typical import `import { defineConfig, config, env } from "@warlock.js/core"`. Skip: cache driver registration — `@warlock.js/cache/cache-basics/SKILL.md`; mail config — `@warlock.js/core/send-mail/SKILL.md`; storage config — `@warlock.js/core/store-file/SKILL.md`; sitemap refusal on a missing origin — `@warlock.js/web/generate-sitemap/SKILL.md`; competing libs `dotenv` direct, `convict`, `node-config`.'
971
+ description: 'Configure a Warlock app — the two layers (`warlock.config.ts` for framework-level wiring, `src/config/*.ts` for subsystems), `.env` + `env()`, the `config()` getter for runtime reads, and `app.publicUrl`/`PUBLIC_APP_URL` (the app''s public origin). Triggers: `defineConfig`, `config.get`, `config.key`, `env`, `ConfigRegistry`, `HttpConfigurations`, `AppConfigurations`, `publicUrl`, `PUBLIC_APP_URL`, `getPublicUrl`; "add a new config file", "warlock.config.ts vs src/config", "read env values", "runtime config lookup", "app public origin/URL"; typical import `import { defineConfig, config, env } from "@warlock.js/core"`. Skip: cache driver registration — the `cache-basics` topic of the `warlock-js-cache` skill; mail config — the `send-mail` topic; storage config — the `store-file` topic; sitemap refusal on a missing origin — the `generate-sitemap` topic of the `warlock-js-web` skill; competing libs `dotenv` direct, `convict`, `node-config`.'
972
972
  ---
973
973
 
974
974
  # Warlock — configure the app
@@ -1059,7 +1059,7 @@ for apps that don't opt in.
1059
1059
  A malformed directive value (containing `;`, or with an unbalanced `'`
1060
1060
  count) throws `InvalidCspDirectiveError` at boot, not silently — fix the
1061
1061
  value in `src/config/http.ts` rather than expecting the framework to repair
1062
- it. See [`send-response/SKILL.md`](../send-response/SKILL.md) for how the
1062
+ it. See the `send-response` topic for how the
1063
1063
  header lands on the response, and `@warlock.js/web`'s docs for how the
1064
1064
  same nonce reaches the page's `<script>` tags.
1065
1065
 
@@ -1240,7 +1240,7 @@ requires the value. `@warlock.js/web`'s sitemap is the first such consumer:
1240
1240
  with `web.sitemap.enabled: true` and no `app.publicUrl`/`PUBLIC_APP_URL` set,
1241
1241
  generation refuses (`MissingPublicUrlError`, naming both) rather than falling
1242
1242
  back to a request-derived host — a sitemap pointing at the wrong host is
1243
- worse than none. See `@warlock.js/web/generate-sitemap/SKILL.md`.
1243
+ worse than none. See the `generate-sitemap` topic of the `warlock-js-web` skill.
1244
1244
 
1245
1245
  ## Common patterns
1246
1246
 
@@ -1270,17 +1270,17 @@ Internal `@warlock.js/*` packages read from `config.get("<name>")` the same way
1270
1270
 
1271
1271
  ## See also
1272
1272
 
1273
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — module layout, canonical imports, framework-wide invariants.
1274
- - [`@warlock.js/cache/pick-cache-driver/SKILL.md`](../../../cache/skills/pick-cache-driver/SKILL.md) — picking and registering a cache driver in `src/config/cache.ts`.
1275
- - [`send-mail/SKILL.md`](../send-mail/SKILL.md) — the `src/config/mail.ts` shape.
1276
- - [`store-file/SKILL.md`](../store-file/SKILL.md) — the `src/config/storage.ts` shape.
1273
+ - The `warlock-conventions` topic — module layout, canonical imports, framework-wide invariants.
1274
+ - The `pick-cache-driver` topic of the `warlock-js-cache` skill — picking and registering a cache driver in `src/config/cache.ts`.
1275
+ - The `send-mail` topic — the `src/config/mail.ts` shape.
1276
+ - The `store-file` topic — the `src/config/storage.ts` shape.
1277
1277
 
1278
1278
 
1279
1279
  ## create-controller `@warlock.js/core/create-controller/SKILL.md`
1280
1280
 
1281
1281
  ---
1282
1282
  name: create-controller
1283
- description: '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.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 — `@warlock.js/core/send-response/SKILL.md`; schema authoring — `@warlock.js/core/validate-input/SKILL.md`; URL wiring — `@warlock.js/core/register-route/SKILL.md`; competing patterns: `express` middleware functions, `@nestjs/common` `@Controller`/`@Get` decorators.'
1283
+ description: '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.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.'
1284
1284
  ---
1285
1285
 
1286
1286
  # Warlock — create a controller
@@ -1328,7 +1328,7 @@ Prefer `request.validated()` once a schema is attached — it's typed.
1328
1328
  | Content type | Parsed by | Notes |
1329
1329
  | ------------------------------------ | -------------------------------- | --------------------------------------------------------------------- |
1330
1330
  | `application/json` | Fastify (built-in) | objects/arrays parsed as-is |
1331
- | `multipart/form-data` | `@fastify/multipart` | fields + files; see [`upload-file`](../upload-file/SKILL.md) |
1331
+ | `multipart/form-data` | `@fastify/multipart` | fields + files; see the `upload-file` topic |
1332
1332
  | `application/x-www-form-urlencoded` | Warlock's own content-type parser (`http/parse-urlencoded-body.ts`) | plain HTML forms, OAuth `form_post` callbacks (e.g. Apple Sign in) |
1333
1333
 
1334
1334
  For urlencoded bodies: fields decode via `URLSearchParams`. A key sent more than once (`tag=a&tag=b`) becomes an array (`request.input("tag")` → `["a", "b"]`); every other key is a plain string. Bracket-notation keys (`a[b]=1`) are **not** expanded by the urlencoded parser itself — nesting only happens through the same shared bracket-key logic every body type already goes through, so it behaves exactly like a JSON or query-string key of that shape, no differently than today.
@@ -1337,7 +1337,7 @@ All three content types are held to the same `http.bodyLimit` — an over-limit
1337
1337
 
1338
1338
  ## Returning output
1339
1339
 
1340
- Pick the helper that matches the outcome. Full surface in [send-response](../send-response/SKILL.md). Quick map:
1340
+ Pick the helper that matches the outcome. Full surface in the `send-response` topic. Quick map:
1341
1341
 
1342
1342
  | Helper | Status | Use when |
1343
1343
  | ---------------------------------- | ------ | ----------------------------- |
@@ -1414,7 +1414,7 @@ Use `RequestHandler<Request<TSchema>>` for public routes, `GuardedRequestHandler
1414
1414
  createProductController.description = "Create a new product (admin only)";
1415
1415
  ```
1416
1416
 
1417
- `description` feeds the OpenAPI document (`warlock generate.openapi`, see [`generate-openapi`](../generate-openapi/SKILL.md)) and surfaces in dev-server logs.
1417
+ `description` feeds the OpenAPI document (`warlock generate.openapi`, see the `generate-openapi` topic) and surfaces in dev-server logs.
1418
1418
 
1419
1419
  ## Declaring response types with `responseSchema`
1420
1420
 
@@ -1542,7 +1542,7 @@ export const getProductController: RequestHandler = async ({ request, response }
1542
1542
  };
1543
1543
  ```
1544
1544
 
1545
- No `if (!product)` branch in the controller. The error class carries the HTTP semantic (`404` for `ResourceNotFoundError`, `403` for `ForbiddenError`, `400` for `BadRequestError`, `409` for `ConflictError`, `500` for `ServerError`). Full list in [`send-response`](../send-response/SKILL.md#throwing-http-errors). `product.notFound` is a translation key — see localization conventions in your project's `utils/locales.ts`.
1545
+ No `if (!product)` branch in the controller. The error class carries the HTTP semantic (`404` for `ResourceNotFoundError`, `403` for `ForbiddenError`, `400` for `BadRequestError`, `409` for `ConflictError`, `500` for `ServerError`). Full list in the `send-response` topic ("Throwing HTTP errors" section). `product.notFound` is a translation key — see localization conventions in your project's `utils/locales.ts`.
1546
1546
 
1547
1547
  ## Gotchas
1548
1548
 
@@ -1553,16 +1553,16 @@ No `if (!product)` branch in the controller. The error class carries the HTTP se
1553
1553
 
1554
1554
  ## See also
1555
1555
 
1556
- - [`register-route/SKILL.md`](../register-route/SKILL.md) — wiring a controller to a URL.
1557
- - [`send-response/SKILL.md`](../send-response/SKILL.md) — full Response helper surface.
1558
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — controller-service-repository layering.
1556
+ - The `register-route` topic — wiring a controller to a URL.
1557
+ - The `send-response` topic — full Response helper surface.
1558
+ - The `warlock-conventions` topic — controller-service-repository layering.
1559
1559
 
1560
1560
 
1561
1561
  ## create-module `@warlock.js/core/create-module/SKILL.md`
1562
1562
 
1563
1563
  ---
1564
1564
  name: create-module
1565
- description: '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 — `@warlock.js/core/warlock-conventions/SKILL.md`; routes file shape — `@warlock.js/core/register-route/SKILL.md`; controller shape — `@warlock.js/core/create-controller/SKILL.md`; competing tooling: `@nestjs/cli`, `hygen`, hand-rolled folder layouts.'
1565
+ description: '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.'
1566
1566
  ---
1567
1567
 
1568
1568
  # Warlock — create a module
@@ -1742,21 +1742,21 @@ await warmupProductCache();
1742
1742
 
1743
1743
  ## See also
1744
1744
 
1745
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — module layout, file suffixes, path aliases.
1746
- - [`register-route/SKILL.md`](../register-route/SKILL.md) — what to put in `routes.ts`.
1747
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — the handler shape, attaching validation.
1748
- - [`use-repository/SKILL.md`](../use-repository/SKILL.md) — what the generated `<module>.repository.ts` looks like and how to extend it.
1749
- - [`define-resource/SKILL.md`](../define-resource/SKILL.md) — the output mapper for the model.
1750
- - [`validate-input/SKILL.md`](../validate-input/SKILL.md) — wiring `schema/*.schema.ts` to controllers.
1751
- - [`write-seeder/SKILL.md`](../write-seeder/SKILL.md) — populating the generated `seeds/` folder.
1752
- - [`use-localization/SKILL.md`](../use-localization/SKILL.md) — populating the generated `utils/locales.ts`.
1745
+ - The `warlock-conventions` topic — module layout, file suffixes, path aliases.
1746
+ - The `register-route` topic — what to put in `routes.ts`.
1747
+ - The `create-controller` topic — the handler shape, attaching validation.
1748
+ - The `use-repository` topic — what the generated `<module>.repository.ts` looks like and how to extend it.
1749
+ - The `define-resource` topic — the output mapper for the model.
1750
+ - The `validate-input` topic — wiring `schema/*.schema.ts` to controllers.
1751
+ - The `write-seeder` topic — populating the generated `seeds/` folder.
1752
+ - The `use-localization` topic — populating the generated `utils/locales.ts`.
1753
1753
 
1754
1754
 
1755
1755
  ## define-resource `@warlock.js/core/define-resource/SKILL.md`
1756
1756
 
1757
1757
  ---
1758
1758
  name: define-resource
1759
- description: '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 — `@warlock.js/core/use-localization/SKILL.md`; URL casting — `@warlock.js/core/build-url/SKILL.md`; controller side — `@warlock.js/core/create-controller/SKILL.md`; competing libs `@nestjs/swagger` `@ApiProperty`, `class-transformer`, hand-rolled DTO mappers.'
1759
+ description: '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.'
1760
1760
  ---
1761
1761
 
1762
1762
  # Warlock — define a resource
@@ -2151,16 +2151,16 @@ export const CommentResource = defineResource({
2151
2151
 
2152
2152
  ## See also
2153
2153
 
2154
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — the output-only rule and where logic actually lives.
2155
- - [`build-restful/SKILL.md`](../build-restful/SKILL.md) — wiring a resource into a CRUD pipeline.
2156
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — calling `new XResource(record).toJSON()` from a controller.
2154
+ - The `warlock-conventions` topic — the output-only rule and where logic actually lives.
2155
+ - The `build-restful` topic — wiring a resource into a CRUD pipeline.
2156
+ - The `create-controller` topic — calling `new XResource(record).toJSON()` from a controller.
2157
2157
 
2158
2158
 
2159
2159
  ## encrypt-data `@warlock.js/core/encrypt-data/SKILL.md`
2160
2160
 
2161
2161
  ---
2162
2162
  name: encrypt-data
2163
- description: '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 — `@warlock.js/core/hash-password/SKILL.md`; config wiring — `@warlock.js/core/configure-app/SKILL.md`; competing libs Node `crypto` direct, `crypto-js`, `libsodium-wrappers`.'
2163
+ description: '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`.'
2164
2164
  ---
2165
2165
 
2166
2166
  # Warlock — encrypt and fingerprint
@@ -2172,7 +2172,7 @@ Two helpers, two different jobs:
2172
2172
  | `encrypt` / `decrypt` | reversible | AES-256-GCM | Secrets you need to read back (API keys, OAuth tokens). |
2173
2173
  | `hmacHash` | one-way | HMAC-SHA256 | Deterministic fingerprint for lookup/dedup of encrypted values. |
2174
2174
 
2175
- Both live in `@warlock.js/core`. For **password hashing** (a one-way job with very different requirements), reach for [`hash-password/SKILL.md`](../hash-password/SKILL.md) instead — bcrypt is the right tool there, not encrypt.
2175
+ Both live in `@warlock.js/core`. For **password hashing** (a one-way job with very different requirements), reach for the `hash-password` topic instead — bcrypt is the right tool there, not encrypt.
2176
2176
 
2177
2177
  ## The shape
2178
2178
 
@@ -2284,7 +2284,7 @@ await idempotencyKeysRepository.first({ hash: hmacHash(JSON.stringify(request))
2284
2284
  log.info("api-key", "used", { fingerprint: hmacHash(apiKey).slice(0, 8) });
2285
2285
  ```
2286
2286
 
2287
- **Do not use `hmacHash` for passwords.** It's fast — a brute-force attack with a stolen database would clear common passwords in minutes. That's what bcrypt is for (see [`hash-password/SKILL.md`](../hash-password/SKILL.md)).
2287
+ **Do not use `hmacHash` for passwords.** It's fast — a brute-force attack with a stolen database would clear common passwords in minutes. That's what bcrypt is for (see the `hash-password` topic).
2288
2288
 
2289
2289
  ## Common patterns
2290
2290
 
@@ -2359,20 +2359,20 @@ Same payload → same key → returns the cached result. Different keys (because
2359
2359
 
2360
2360
  `encrypt` / `decrypt` / `hmacHash` use Node's built-in `crypto` — no extra dependency. They're available out of the box.
2361
2361
 
2362
- Password hashing (`hashPassword`) needs `bcryptjs`, which is its own install — see [`hash-password/SKILL.md`](../hash-password/SKILL.md).
2362
+ Password hashing (`hashPassword`) needs `bcryptjs`, which is its own install — see the `hash-password` topic.
2363
2363
 
2364
2364
  ## See also
2365
2365
 
2366
- - [`hash-password/SKILL.md`](../hash-password/SKILL.md) — bcrypt password hashing (the third member of the encryption module, but a different job entirely).
2367
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — `src/config/encryption.ts` and env wiring.
2368
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — service layering, where the encryption boundary should sit.
2366
+ - The `hash-password` topic — bcrypt password hashing (the third member of the encryption module, but a different job entirely).
2367
+ - The `configure-app` topic — `src/config/encryption.ts` and env wiring.
2368
+ - The `warlock-conventions` topic — service layering, where the encryption boundary should sit.
2369
2369
 
2370
2370
 
2371
2371
  ## generate-openapi `@warlock.js/core/generate-openapi/SKILL.md`
2372
2372
 
2373
2373
  ---
2374
2374
  name: generate-openapi
2375
- description: '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 — `@warlock.js/core/create-controller/SKILL.md`; the devtools dashboard — `@warlock.js/devtools/devtools-overview/SKILL.md`; listing routes — `@warlock.js/core/warlock-routes/SKILL.md`.'
2375
+ description: '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 `devtools-overview` topic of the `warlock-js-devtools` skill; listing routes — the `warlock-routes` topic.'
2376
2376
  ---
2377
2377
 
2378
2378
  # Warlock — generate an OpenAPI document
@@ -2416,7 +2416,7 @@ npx warlock generate.openapi --out docs/openapi.json --title "Shop API" --server
2416
2416
 
2417
2417
  ### Responses from `responseSchema`
2418
2418
 
2419
- The grammar is the one described in [`create-controller`](../create-controller/SKILL.md#declaring-response-types-with-responseschema): cast strings with suffixes, a resource, `[Resource]`, nested objects.
2419
+ The grammar is the one described in the `create-controller` topic ("Declaring response types with `responseSchema`" section): cast strings with suffixes, a resource, `[Resource]`, nested objects.
2420
2420
 
2421
2421
  ```ts
2422
2422
  loginController.responseSchema = {
@@ -2515,23 +2515,23 @@ const { document, warnings } = buildOpenApiDocument(router.list(), {
2515
2515
 
2516
2516
  ## See also
2517
2517
 
2518
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — `validation`, `description` and `responseSchema` on a handler.
2519
- - [`define-resource/SKILL.md`](../define-resource/SKILL.md) — resources, which become `components.schemas`.
2520
- - [`warlock-routes/SKILL.md`](../warlock-routes/SKILL.md) — list the routes the document is built from.
2518
+ - The `create-controller` topic — `validation`, `description` and `responseSchema` on a handler.
2519
+ - The `define-resource` topic — resources, which become `components.schemas`.
2520
+ - The `warlock-routes` topic — list the routes the document is built from.
2521
2521
 
2522
2522
 
2523
2523
  ## hash-password `@warlock.js/core/hash-password/SKILL.md`
2524
2524
 
2525
2525
  ---
2526
2526
  name: hash-password
2527
- description: 'One-way bcrypt password hashing — `hashPassword` / `verifyPassword`, plus the declarative `useHashedPassword()` schema transformer that auto-hashes a model''s password field on save. Salt rounds come from `src/config/encryption.ts`. Triggers: `hashPassword`, `verifyPassword`, `useHashedPassword`, `password.salt`, `bcryptjs`; "hash a user password", "verify login credentials", "auto-hash on save", "rotate a password"; typical import `import { hashPassword, verifyPassword } from "@warlock.js/core"`. Skip: reversible secrets — `@warlock.js/core/encrypt-data/SKILL.md`; the other transformers — `@warlock.js/core/use-model-transformers/SKILL.md`; config wiring — `@warlock.js/core/configure-app/SKILL.md`; competing libs `bcrypt` native, `argon2`, `scrypt`.'
2527
+ description: 'One-way bcrypt password hashing — `hashPassword` / `verifyPassword`, plus the declarative `useHashedPassword()` schema transformer that auto-hashes a model''s password field on save. Salt rounds come from `src/config/encryption.ts`. Triggers: `hashPassword`, `verifyPassword`, `useHashedPassword`, `password.salt`, `bcryptjs`; "hash a user password", "verify login credentials", "auto-hash on save", "rotate a password"; typical import `import { hashPassword, verifyPassword } from "@warlock.js/core"`. Skip: reversible secrets — the `encrypt-data` topic; the other transformers — the `use-model-transformers` topic; config wiring — the `configure-app` topic; competing libs `bcrypt` native, `argon2`, `scrypt`.'
2528
2528
  ---
2529
2529
 
2530
2530
  # Warlock — hash a password
2531
2531
 
2532
2532
  For storing user-typed passwords, bcrypt is the one right answer — and the only one. It's **deliberately slow** (~250ms per call with the default salt rounds), which is what makes it expensive to brute-force a stolen password database. Use it for nothing else: not API keys, not session tokens, not "this needs to be one-way."
2533
2533
 
2534
- If your value needs to be read back later → [`encrypt-data/SKILL.md`](../encrypt-data/SKILL.md).
2534
+ If your value needs to be read back later → the `encrypt-data` topic.
2535
2535
  If you need a fast searchable fingerprint of an encrypted value → also `encrypt-data` (`hmacHash`).
2536
2536
 
2537
2537
  ## The shape
@@ -2692,7 +2692,7 @@ There is no `warlock add` feature for password hashing — `bcryptjs` is a plain
2692
2692
 
2693
2693
  ## Gotchas
2694
2694
 
2695
- - **Never use `hashPassword` for non-password values.** A 250ms tax on every API request because someone reached for it as a "general one-way hash" — that's a performance bug waiting to happen. For one-way fingerprinting of encrypted values, use `hmacHash` (see [`encrypt-data/SKILL.md`](../encrypt-data/SKILL.md)).
2695
+ - **Never use `hashPassword` for non-password values.** A 250ms tax on every API request because someone reached for it as a "general one-way hash" — that's a performance bug waiting to happen. For one-way fingerprinting of encrypted values, use `hmacHash` (see the `encrypt-data` topic).
2696
2696
  - **Don't `hashPassword` on the request hot path twice.** Each call is ~250ms. If you `hashPassword(input)` then call it again as part of `verifyPassword`'s internals (you don't), you're stacking the tax. `verifyPassword(plain, storedHash)` takes the plaintext and the stored hash — that's the API.
2697
2697
  - **Don't swap `bcryptjs` for native `bcrypt` without a reason.** `bcryptjs` is portable across every platform (no native build), and the perf delta isn't meaningful in the password-verification workflow.
2698
2698
  - **The transformer detects changes by comparison.** If your model's password field is `null` and you save `null` again, it's a no-op. If it's a hash and you save the same hash string, also no-op. Only an actual value change triggers re-hashing.
@@ -2700,18 +2700,18 @@ There is no `warlock add` feature for password hashing — `bcryptjs` is a plain
2700
2700
 
2701
2701
  ## See also
2702
2702
 
2703
- - [`use-model-transformers/SKILL.md`](../use-model-transformers/SKILL.md) — `useHashedPassword` + the two other model transformers (`useComputedSlug`, `useComputedModel`).
2704
- - [`encrypt-data/SKILL.md`](../encrypt-data/SKILL.md) — reversible AES-256-GCM `encrypt`/`decrypt` + one-way HMAC `hmacHash` (the other two members of the encryption module).
2705
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — `src/config/encryption.ts` and the `password.salt` knob.
2706
- - [`define-resource/SKILL.md`](../define-resource/SKILL.md) — where to filter the password field out of API responses.
2707
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — service layering, where the password boundary should sit.
2703
+ - The `use-model-transformers` topic — `useHashedPassword` + the two other model transformers (`useComputedSlug`, `useComputedModel`).
2704
+ - The `encrypt-data` topic — reversible AES-256-GCM `encrypt`/`decrypt` + one-way HMAC `hmacHash` (the other two members of the encryption module).
2705
+ - The `configure-app` topic — `src/config/encryption.ts` and the `password.salt` knob.
2706
+ - The `define-resource` topic — where to filter the password field out of API responses.
2707
+ - The `warlock-conventions` topic — service layering, where the password boundary should sit.
2708
2708
 
2709
2709
 
2710
2710
  ## health-checks `@warlock.js/core/health-checks/SKILL.md`
2711
2711
 
2712
2712
  ---
2713
2713
  name: health-checks
2714
- description: 'Built-in liveness (`/health`) and readiness (`/ready`) endpoints plus graceful HTTP request draining for zero-downtime deploys — the `health` registry (`health.addCheck`/`removeCheck`), the `http.health.*` and `http.gracefulShutdown.*` config, and how readiness ties into `Application.isShuttingDown`. Triggers: `health`, `health.addCheck`, `health.removeCheck`, `HealthCheck`, `/health`, `/ready`, `http.health`, `http.gracefulShutdown`, `forceCloseConnections`, "liveness probe", "readiness probe", "graceful shutdown", "drain in-flight requests", "zero-downtime deploy", "kubernetes health check", "503 until ready"; typical import `import { health } from "@warlock.js/core"`. Skip: the `Application.onShutdown` / `onceBooted` lifecycle hooks — `@warlock.js/core/use-app-context/SKILL.md`; maintenance-mode 503s — `@warlock.js/core/use-middleware/SKILL.md`; connector lifecycle — `@warlock.js/core/add-connector/SKILL.md`; competing libs `@fastify/under-pressure`, `terminus`, hand-rolled `/health` controllers.'
2714
+ description: 'Built-in liveness (`/health`) and readiness (`/ready`) endpoints plus graceful HTTP request draining for zero-downtime deploys — the `health` registry (`health.addCheck`/`removeCheck`), the `http.health.*` and `http.gracefulShutdown.*` config, and how readiness ties into `Application.isShuttingDown`. Triggers: `health`, `health.addCheck`, `health.removeCheck`, `HealthCheck`, `/health`, `/ready`, `http.health`, `http.gracefulShutdown`, `forceCloseConnections`, "liveness probe", "readiness probe", "graceful shutdown", "drain in-flight requests", "zero-downtime deploy", "kubernetes health check", "503 until ready"; typical import `import { health } from "@warlock.js/core"`. Skip: the `Application.onShutdown` / `onceBooted` lifecycle hooks — the `use-app-context` topic; maintenance-mode 503s — the `use-middleware` topic; connector lifecycle — the `add-connector` topic; competing libs `@fastify/under-pressure`, `terminus`, hand-rolled `/health` controllers.'
2715
2715
  ---
2716
2716
 
2717
2717
  # Warlock — health checks & graceful shutdown
@@ -2820,21 +2820,21 @@ For an even smoother handoff, give the load balancer time to observe the 503 bef
2820
2820
  - **`/health` is registered straight on Fastify, not the app router.** It's infra, so it's immune to HMR and route scanning — but if your app also defines a `/health` route you'll have a collision. Rename via `http.health.path`.
2821
2821
  - **Readiness needs a finished boot.** Before `Application.isBooted` (e.g. while late-phase connectors are still starting) `/ready` is 503 by design — that's the point.
2822
2822
  - **A hanging `onShutdown` hook delays the drain.** App hooks run before connector teardown and are only bounded by your process manager's kill timeout; keep them fast. The HTTP drain itself is bounded by `gracefulShutdown.timeout`.
2823
- - **The `maintenance` middleware is a different 503.** It allowlists `/health` by default so probes pass during maintenance — but maintenance mode is operator-toggled downtime, not readiness. See `@warlock.js/core/use-middleware/SKILL.md`.
2823
+ - **The `maintenance` middleware is a different 503.** It allowlists `/health` by default so probes pass during maintenance — but maintenance mode is operator-toggled downtime, not readiness. See the `use-middleware` topic.
2824
2824
 
2825
2825
  ## See also
2826
2826
 
2827
- - [`use-app-context/SKILL.md`](../use-app-context/SKILL.md) — `Application.onShutdown` / `onceBooted` / `isShuttingDown`, the lifecycle hooks the endpoints build on.
2828
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — the `src/config/http.ts` shape.
2829
- - [`add-connector/SKILL.md`](../add-connector/SKILL.md) — connector boot/shutdown order, where draining slots in.
2830
- - [`use-middleware/SKILL.md`](../use-middleware/SKILL.md) — maintenance mode and other built-in middleware.
2827
+ - The `use-app-context` topic — `Application.onShutdown` / `onceBooted` / `isShuttingDown`, the lifecycle hooks the endpoints build on.
2828
+ - The `configure-app` topic — the `src/config/http.ts` shape.
2829
+ - The `add-connector` topic — connector boot/shutdown order, where draining slots in.
2830
+ - The `use-middleware` topic — maintenance mode and other built-in middleware.
2831
2831
 
2832
2832
 
2833
2833
  ## lower-stage3-decorators `@warlock.js/core/lower-stage3-decorators/SKILL.md`
2834
2834
 
2835
2835
  ---
2836
2836
  name: lower-stage3-decorators
2837
- description: 'Vite/Vitest plugin `lowerStage3Decorators()` that lowers TC39 Stage-3 (native) decorators with esbuild before oxc / the SSR rewrite mangles them — so `@RegisterModel`-decorated Cascade models load under Vitest 4 / Vite 8. Triggers: `lowerStage3Decorators`, "SyntaxError: Invalid or unexpected token" on a decorated class, "@(0, __vite_ssr_import__.X)()", "decorator crashes vitest", "models won''t load in tests", "Vite 8 oxc decorators"; typical import `import { lowerStage3Decorators } from "@warlock.js/core/vite"` in `vite.config.ts` / `vitest.config.ts`. Skip: writing the tests themselves — `@warlock.js/core/test-service/SKILL.md` / `@warlock.js/core/test-http/SKILL.md`; runtime migrations — `@warlock.js/cascade/write-migration/SKILL.md`.'
2837
+ description: 'Vite/Vitest plugin `lowerStage3Decorators()` that lowers TC39 Stage-3 (native) decorators with esbuild before oxc / the SSR rewrite mangles them — so `@RegisterModel`-decorated Cascade models load under Vitest 4 / Vite 8. Triggers: `lowerStage3Decorators`, "SyntaxError: Invalid or unexpected token" on a decorated class, "@(0, __vite_ssr_import__.X)()", "decorator crashes vitest", "models won''t load in tests", "Vite 8 oxc decorators"; typical import `import { lowerStage3Decorators } from "@warlock.js/core/vite"` in `vite.config.ts` / `vitest.config.ts`. Skip: writing the tests themselves — the `test-service` topic / the `test-http` topic; runtime migrations — the `write-migration` topic of the `warlock-js-cascade` skill.'
2838
2838
  ---
2839
2839
 
2840
2840
  # Lower Stage-3 decorators for Vite/Vitest
@@ -2885,16 +2885,16 @@ It's the **same esbuild transform the production `tsx`/build path uses**, so a f
2885
2885
 
2886
2886
  ## See also
2887
2887
 
2888
- - [`@warlock.js/core/test-service/SKILL.md`](@warlock.js/core/test-service/SKILL.md) — `setupTest()` bootstrap for unit-testing services/models once the harness loads.
2889
- - [`@warlock.js/core/test-http/SKILL.md`](@warlock.js/core/test-http/SKILL.md) — integration tests over the real HTTP server.
2890
- - [`@warlock.js/cascade/write-migration/SKILL.md`](@warlock.js/cascade/write-migration/SKILL.md) — the decorated models this plugin lets you load.
2888
+ - The `test-service` topic — `setupTest()` bootstrap for unit-testing services/models once the harness loads.
2889
+ - The `test-http` topic — integration tests over the real HTTP server.
2890
+ - The `write-migration` topic of the `warlock-js-cascade` skill — the decorated models this plugin lets you load.
2891
2891
 
2892
2892
 
2893
2893
  ## process-image `@warlock.js/core/process-image/SKILL.md`
2894
2894
 
2895
2895
  ---
2896
2896
  name: process-image
2897
- description: 'Transform images with the `Image` class — resize, crop, rotate, format, quality, watermark, blur, etc. — using a deferred pipeline that runs only at `save()` / `toBuffer()` / `toBase64()` / `toDataUrl()` time. Requires sharp via `warlock add image`. Triggers: `Image`, `Image.fromFile`, `Image.fromBuffer`, `Image.fromUrl`, `.resize`, `.crop`, `.watermark`, `.toBuffer`, `.toDataUrl`, `.apply`; "resize an image", "generate a thumbnail", "watermark a product photo", "build an image pipeline"; typical import `import { Image } from "@warlock.js/core"`. Skip: multipart upload entry — `@warlock.js/core/upload-file/SKILL.md`; storage persistence — `@warlock.js/core/store-file/SKILL.md`; competing libs `sharp` direct, `jimp`, `imagemagick`, `gm`.'
2897
+ description: 'Transform images with the `Image` class — resize, crop, rotate, format, quality, watermark, blur, etc. — using a deferred pipeline that runs only at `save()` / `toBuffer()` / `toBase64()` / `toDataUrl()` time. Requires sharp via `warlock add image`. Triggers: `Image`, `Image.fromFile`, `Image.fromBuffer`, `Image.fromUrl`, `.resize`, `.crop`, `.watermark`, `.toBuffer`, `.toDataUrl`, `.apply`; "resize an image", "generate a thumbnail", "watermark a product photo", "build an image pipeline"; typical import `import { Image } from "@warlock.js/core"`. Skip: multipart upload entry — the `upload-file` topic; storage persistence — the `store-file` topic; competing libs `sharp` direct, `jimp`, `imagemagick`, `gm`.'
2898
2898
  ---
2899
2899
 
2900
2900
  # Warlock — process an image
@@ -3047,7 +3047,7 @@ The wrapper exposes the underlying `sharp.Sharp` via `.image` for anything not s
3047
3047
 
3048
3048
  `UploadedFile` (from multipart uploads) has its own chainable `.resize().format().quality().save(...)` that runs through `Image` internally — same deferred execution, same transforms. Use it when the source is a multipart upload; reach for `new Image(...)` directly when the source is a buffer/path/URL.
3049
3049
 
3050
- See [`upload-file`](../upload-file/SKILL.md) for the multipart entry point and [`store-file`](../store-file/SKILL.md) for persisting the output through storage drivers.
3050
+ See the `upload-file` topic for the multipart entry point and the `store-file` topic for persisting the output through storage drivers.
3051
3051
 
3052
3052
  ## Gotchas
3053
3053
 
@@ -3060,16 +3060,16 @@ See [`upload-file`](../upload-file/SKILL.md) for the multipart entry point and [
3060
3060
 
3061
3061
  ## See also
3062
3062
 
3063
- - [`upload-file/SKILL.md`](../upload-file/SKILL.md) — multipart uploads, where image transforms typically start.
3064
- - [`store-file/SKILL.md`](../store-file/SKILL.md) — persisting image output through storage drivers (local / S3 / R2 / Spaces).
3065
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — where image-processing services live.
3063
+ - The `upload-file` topic — multipart uploads, where image transforms typically start.
3064
+ - The `store-file` topic — persisting image output through storage drivers (local / S3 / R2 / Spaces).
3065
+ - The `warlock-conventions` topic — where image-processing services live.
3066
3066
 
3067
3067
 
3068
3068
  ## register-route `@warlock.js/core/register-route/SKILL.md`
3069
3069
 
3070
3070
  ---
3071
3071
  name: register-route
3072
- description: 'Register HTTP routes via @warlock.js/core''s router — single routes, prefix groups, middleware-guarded blocks, and RESTful resource chains. Routes always live in `src/app/<module>/routes.ts`. Triggers: `router.get`, `router.post`, `router.prefix`, `router.group`, `router.route`, `guarded`; "add a route", "wire a controller to a URL", "group routes by prefix", "register a RESTful resource"; typical import `import { router } from "@warlock.js/core"`. Skip: handler shape — `@warlock.js/core/create-controller/SKILL.md`; CRUD chain details — `@warlock.js/core/build-restful/SKILL.md`; middleware authoring — `@warlock.js/core/write-middleware/SKILL.md`; competing libs `express`, `fastify`, `koa`, `@nestjs/common`.'
3072
+ description: 'Register HTTP routes via @warlock.js/core''s router — single routes, prefix groups, middleware-guarded blocks, and RESTful resource chains. Routes always live in `src/app/<module>/routes.ts`. Triggers: `router.get`, `router.post`, `router.prefix`, `router.group`, `router.route`, `guarded`; "add a route", "wire a controller to a URL", "group routes by prefix", "register a RESTful resource"; typical import `import { router } from "@warlock.js/core"`. Skip: handler shape — the `create-controller` topic; CRUD chain details — the `build-restful` topic; middleware authoring — the `write-middleware` topic; competing libs `express`, `fastify`, `koa`, `@nestjs/common`.'
3073
3073
  ---
3074
3074
 
3075
3075
  # Warlock — register a route
@@ -3098,7 +3098,7 @@ router.post("/orders/:id", createOrderController, { name: "orders.create" });
3098
3098
  Use a concrete HTTP verb for browser forms. `router.any()` is represented as
3099
3099
  `all` metadata and requires callers to choose an explicit path and method.
3100
3100
 
3101
- That's the entire contract for a simple route. `router` is a singleton; method calls register routes synchronously. The handler is a plain function (controllers are typed as `RequestHandler` — see [create-controller](../create-controller/SKILL.md)).
3101
+ That's the entire contract for a simple route. `router` is a singleton; method calls register routes synchronously. The handler is a plain function (controllers are typed as `RequestHandler` — see the `create-controller` topic).
3102
3102
 
3103
3103
  ## HTTP verbs
3104
3104
 
@@ -3167,7 +3167,7 @@ router.prefix("/products", () => {
3167
3167
  });
3168
3168
  ```
3169
3169
 
3170
- If `guarded` doesn't exist in your project yet, see the shared utility convention in [warlock-conventions](../warlock-conventions/SKILL.md).
3170
+ If `guarded` doesn't exist in your project yet, see the shared utility convention in the `warlock-conventions` topic.
3171
3171
 
3172
3172
  ## RESTful resource chain
3173
3173
 
@@ -3236,16 +3236,16 @@ router.get(["/health", "/healthz"], healthController);
3236
3236
 
3237
3237
  ## See also
3238
3238
 
3239
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — the handler shape, validation, request/response surface.
3240
- - [`send-response/SKILL.md`](../send-response/SKILL.md) — picking the right `response.<helper>()`.
3241
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — module layout, the `guarded` convention, path aliases.
3239
+ - The `create-controller` topic — the handler shape, validation, request/response surface.
3240
+ - The `send-response` topic — picking the right `response.<helper>()`.
3241
+ - The `warlock-conventions` topic — module layout, the `guarded` convention, path aliases.
3242
3242
 
3243
3243
 
3244
3244
  ## request-memo `@warlock.js/core/request-memo/SKILL.md`
3245
3245
 
3246
3246
  ---
3247
3247
  name: request-memo
3248
- description: 'Memoize async work for one HTTP request with `requestMemo<T>(key, fn)` — the v5 replacement for removed `fromRequest`, with single-flight promise sharing, settled-success reuse, rejection eviction, and no cross-request fallback. Triggers: `requestMemo`, `fromRequest`, `fromRequest removed`, `request-scoped cache`, `single-flight`, `current request memo`; "migrate off fromRequest", "load this once per request", "deduplicate concurrent loaders", "cache a repository lookup during one request"; typical import `import { requestMemo } from "@warlock.js/core"`. Skip: middleware-written request state — `@warlock.js/core/use-request-locals/SKILL.md`; process-wide or cross-request caching — `@warlock.js/core/use-middleware/SKILL.md`; competing patterns: dynamic `request[key]` properties, module-global `Map`, payload `request.get()` / `request.set()`.'
3248
+ description: 'Memoize async work for one HTTP request with `requestMemo<T>(key, fn)` — the v5 replacement for removed `fromRequest`, with single-flight promise sharing, settled-success reuse, rejection eviction, and no cross-request fallback. Triggers: `requestMemo`, `fromRequest`, `fromRequest removed`, `request-scoped cache`, `single-flight`, `current request memo`; "migrate off fromRequest", "load this once per request", "deduplicate concurrent loaders", "cache a repository lookup during one request"; typical import `import { requestMemo } from "@warlock.js/core"`. Skip: middleware-written request state — the `use-request-locals` topic; process-wide or cross-request caching — the `use-middleware` topic; competing patterns: dynamic `request[key]` properties, module-global `Map`, payload `request.get()` / `request.set()`.'
3249
3249
  ---
3250
3250
 
3251
3251
  # Warlock — memoize work for one request
@@ -3382,16 +3382,16 @@ Reusing one key for different result types is a caller bug: TypeScript cannot co
3382
3382
 
3383
3383
  ## See also
3384
3384
 
3385
- - [`use-request-locals/SKILL.md`](../use-request-locals/SKILL.md) — typed per-request data written by middleware.
3386
- - [`write-middleware/SKILL.md`](../write-middleware/SKILL.md) — authoring and registering middleware.
3387
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — consuming request-scoped values from controllers.
3385
+ - The `use-request-locals` topic — typed per-request data written by middleware.
3386
+ - The `write-middleware` topic — authoring and registering middleware.
3387
+ - The `create-controller` topic — consuming request-scoped values from controllers.
3388
3388
 
3389
3389
 
3390
3390
  ## request-tracing `@warlock.js/core/request-tracing/SKILL.md`
3391
3391
 
3392
3392
  ---
3393
3393
  name: request-tracing
3394
- description: 'Vendor-neutral request tracing hooks (`http.tracing`) — observe request start/end and named phase spans (`route.match`, `middleware`, `validation`, `handler`, `response.write`) without adopting an OTel/vendor dependency in `core`. Covers the `TracingHooks` shape, trace id derivation from an inbound W3C `traceparent` vs `request.id`, that a throwing hook is caught and reported once (never breaks the request), and zero overhead when disabled. Triggers: `http.tracing`, `TracingHooks`, `onRequestStart`, `onRequestEnd`, `onPhase`, `traceparent`, `traceId`, `dispatchPhase`, "instrument requests", "trace a request", "slow phase logging", "APM / OTel bridge for Warlock". Skip: request-id header echo/inheritance (`X-Request-Id`) — `@warlock.js/core/use-middleware/SKILL.md`; app-level structured logging — `@warlock.js/logger/logger-basics/SKILL.md`; competing libs `@opentelemetry/api` direct instrumentation, `express-request-id`, hand-rolled `X-Trace-Id` middleware.'
3394
+ description: 'Vendor-neutral request tracing hooks (`http.tracing`) — observe request start/end and named phase spans (`route.match`, `middleware`, `validation`, `handler`, `response.write`) without adopting an OTel/vendor dependency in `core`. Covers the `TracingHooks` shape, trace id derivation from an inbound W3C `traceparent` vs `request.id`, that a throwing hook is caught and reported once (never breaks the request), and zero overhead when disabled. Triggers: `http.tracing`, `TracingHooks`, `onRequestStart`, `onRequestEnd`, `onPhase`, `traceparent`, `traceId`, `dispatchPhase`, "instrument requests", "trace a request", "slow phase logging", "APM / OTel bridge for Warlock". Skip: request-id header echo/inheritance (`X-Request-Id`) — the `use-middleware` topic; app-level structured logging — the `logger-basics` topic of the `warlock-js-logger` skill; competing libs `@opentelemetry/api` direct instrumentation, `express-request-id`, hand-rolled `X-Trace-Id` middleware.'
3395
3395
  ---
3396
3396
 
3397
3397
  # Warlock — request tracing hooks
@@ -3522,7 +3522,7 @@ instead of adding a separate hook API. They add three phases:
3522
3522
  trace keeps the same id through Warlock.
3523
3523
  2. Otherwise `traceId` falls back to `request.id` (the framework's own
3524
3524
  per-request correlation id — see
3525
- [`use-middleware/SKILL.md`](../use-middleware/SKILL.md#request-id-correlation)
3525
+ the `use-middleware` topic (its "Request ID correlation" section)
3526
3526
  for how that id is generated/inherited/echoed).
3527
3527
 
3528
3528
  `ctx.requestId` is always `request.id`, regardless of which branch produced
@@ -3543,7 +3543,7 @@ deriveTraceId(undefined, "req-abc123");
3543
3543
 
3544
3544
  Tracing does **not** add a response header of its own. `core` already echoes
3545
3545
  `request.id` back as `X-Request-Id` on every response (see
3546
- [`use-middleware/SKILL.md`](../use-middleware/SKILL.md#request-id-correlation)) —
3546
+ the `use-middleware` topic, "Request ID correlation" section) —
3547
3547
  apps correlate through that existing header. When a valid inbound
3548
3548
  `traceparent` is present, `ctx.traceId` inside your hooks carries that trace
3549
3549
  id even though the response header still reflects `request.id`; if you need
@@ -3617,16 +3617,16 @@ export default {
3617
3617
 
3618
3618
  ## See also
3619
3619
 
3620
- - [`use-middleware/SKILL.md`](../use-middleware/SKILL.md#request-id-correlation) — `X-Request-Id` inheritance/echo, the header tracing correlates through.
3621
- - [`use-app-context/SKILL.md`](../use-app-context/SKILL.md) — request-scoped ALS context (`request`/`response`) tracing hooks run inside.
3622
- - [`@warlock.js/logger/logger-basics/SKILL.md`](../../../logger/skills/logger-basics/SKILL.md) — structured logging; tracing hooks are the place to bridge phase timing into your log channel.
3620
+ - The `use-middleware` topic ("Request ID correlation" section) — `X-Request-Id` inheritance/echo, the header tracing correlates through.
3621
+ - The `use-app-context` topic — request-scoped ALS context (`request`/`response`) tracing hooks run inside.
3622
+ - The `logger-basics` topic of the `warlock-js-logger` skill — structured logging; tracing hooks are the place to bridge phase timing into your log channel.
3623
3623
 
3624
3624
 
3625
3625
  ## resolve-path `@warlock.js/core/resolve-path/SKILL.md`
3626
3626
 
3627
3627
  ---
3628
3628
  name: resolve-path
3629
- description: 'Path helpers anchored at `process.cwd()` — `rootPath`, `srcPath`, `appPath`, `configPath`, `publicPath`, `storagePath`, `uploadsPath`, `cachePath`, `logsPath`, `tempPath`, `warlockPath`, `sanitizePath`. Optional `uploads.root` config overrides the uploads anchor. Triggers: `appPath`, `configPath`, `uploadsPath`, `storagePath`, `publicPath`, `cachePath`, `logsPath`, `tempPath`, `sanitizePath`, `paths`; "resolve a path inside src/app", "absolute upload destination", "sanitize a user filename", "ship uploads to a mounted volume"; typical import `import { appPath, uploadsPath } from "@warlock.js/core"`. Skip: HTTP URL helpers — `@warlock.js/core/build-url/SKILL.md`; app metadata — `@warlock.js/core/use-app-context/SKILL.md`; storage abstraction — `@warlock.js/core/store-file/SKILL.md`; competing patterns: `path.join(process.cwd(), ...)`, hand-rolled directory constants.'
3629
+ description: 'Path helpers anchored at `process.cwd()` — `rootPath`, `srcPath`, `appPath`, `configPath`, `publicPath`, `storagePath`, `uploadsPath`, `cachePath`, `logsPath`, `tempPath`, `warlockPath`, `sanitizePath`. Optional `uploads.root` config overrides the uploads anchor. Triggers: `appPath`, `configPath`, `uploadsPath`, `storagePath`, `publicPath`, `cachePath`, `logsPath`, `tempPath`, `sanitizePath`, `paths`; "resolve a path inside src/app", "absolute upload destination", "sanitize a user filename", "ship uploads to a mounted volume"; typical import `import { appPath, uploadsPath } from "@warlock.js/core"`. Skip: HTTP URL helpers — the `build-url` topic; app metadata — the `use-app-context` topic; storage abstraction — the `store-file` topic; competing patterns: `path.join(process.cwd(), ...)`, hand-rolled directory constants.'
3630
3630
  ---
3631
3631
 
3632
3632
  # Warlock — resolve a path
@@ -3789,18 +3789,18 @@ If you find yourself reaching for `path.join(process.cwd(), ...)` — that's a s
3789
3789
 
3790
3790
  ## See also
3791
3791
 
3792
- - [`build-url/SKILL.md`](../build-url/SKILL.md) — HTTP URL helpers (`url`, `uploadsUrl`, `publicUrl`, `assetsUrl`) — companion for rendering URLs (not filesystem paths).
3793
- - [`use-app-context/SKILL.md`](../use-app-context/SKILL.md) — `Application` static (env, runtime strategy, version, uptime) + `app` runtime accessor (Fastify, socket.io, router, database).
3794
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — `src/config/uploads.ts` for the `root` override.
3795
- - [`store-file/SKILL.md`](../store-file/SKILL.md) — the storage abstraction (most uploads code goes through `storage.put(...)`, not raw paths).
3796
- - [`upload-file/SKILL.md`](../upload-file/SKILL.md) — the `UploadedFile` shape that pairs with `uploadsPath`.
3792
+ - The `build-url` topic — HTTP URL helpers (`url`, `uploadsUrl`, `publicUrl`, `assetsUrl`) — companion for rendering URLs (not filesystem paths).
3793
+ - The `use-app-context` topic — `Application` static (env, runtime strategy, version, uptime) + `app` runtime accessor (Fastify, socket.io, router, database).
3794
+ - The `configure-app` topic — `src/config/uploads.ts` for the `root` override.
3795
+ - The `store-file` topic — the storage abstraction (most uploads code goes through `storage.put(...)`, not raw paths).
3796
+ - The `upload-file` topic — the `UploadedFile` shape that pairs with `uploadsPath`.
3797
3797
 
3798
3798
 
3799
3799
  ## retry-operation `@warlock.js/core/retry-operation/SKILL.md`
3800
3800
 
3801
3801
  ---
3802
3802
  name: retry-operation
3803
- description: 'Wrap a flaky operation with `retry(fn, options)` — now provided by `@mongez/reinforcements` (not `@warlock.js/core`). `attempts` total tries, `delay` + `backoff` (linear/exponential/fn), `maxDelay`, `jitter`, `shouldRetry` to bail on permanent errors, `signal` to cancel, plus `retryable()` to pre-bind options. Triggers: `retry`, `retryable`, `RetryOptions`, `attempts`, `backoff`, `jitter`, `maxDelay`, `shouldRetry`, `signal`; "retry a flaky API call", "handle transient errors", "exponential backoff with jitter", "wrap an external request"; typical import `import { retry } from "@mongez/reinforcements"`. Skip: timing the retried op — `@warlock.js/core/benchmark-code/SKILL.md`; use-case-level `retry` option — `@warlock.js/core/write-use-case/SKILL.md`; competing libs `p-retry`, `async-retry`, `cockatiel`.'
3803
+ description: 'Wrap a flaky operation with `retry(fn, options)` — now provided by `@mongez/reinforcements` (not `@warlock.js/core`). `attempts` total tries, `delay` + `backoff` (linear/exponential/fn), `maxDelay`, `jitter`, `shouldRetry` to bail on permanent errors, `signal` to cancel, plus `retryable()` to pre-bind options. Triggers: `retry`, `retryable`, `RetryOptions`, `attempts`, `backoff`, `jitter`, `maxDelay`, `shouldRetry`, `signal`; "retry a flaky API call", "handle transient errors", "exponential backoff with jitter", "wrap an external request"; typical import `import { retry } from "@mongez/reinforcements"`. Skip: timing the retried op — the `benchmark-code` topic; use-case-level `retry` option — the `write-use-case` topic; competing libs `p-retry`, `async-retry`, `cockatiel`.'
3804
3804
  ---
3805
3805
 
3806
3806
  # Warlock — retry an operation
@@ -3893,7 +3893,7 @@ const result = await measure("publish-event", () =>
3893
3893
 
3894
3894
  ## Use-case integration
3895
3895
 
3896
- `useCase()` accepts a `retry` option (the same `RetryOptions`) that wraps the **handler** — see [`@warlock.js/core/write-use-case/SKILL.md`](@warlock.js/core/write-use-case/SKILL.md). Reach for that when "retry" means "re-run the handler"; reach for raw `retry()` when only one step inside is flaky.
3896
+ `useCase()` accepts a `retry` option (the same `RetryOptions`) that wraps the **handler** — see the `write-use-case` topic. Reach for that when "retry" means "re-run the handler"; reach for raw `retry()` when only one step inside is flaky.
3897
3897
 
3898
3898
  ## Gotchas
3899
3899
 
@@ -3905,15 +3905,15 @@ const result = await measure("publish-event", () =>
3905
3905
  ## See also
3906
3906
 
3907
3907
  - [`mongez-reinforcements-async`](mongez-reinforcements-async) — full `retry` / `retryable` reference.
3908
- - [`@warlock.js/core/benchmark-code/SKILL.md`](@warlock.js/core/benchmark-code/SKILL.md) — timing retried operations.
3909
- - [`@warlock.js/core/write-use-case/SKILL.md`](@warlock.js/core/write-use-case/SKILL.md) — the use-case `retry` option.
3908
+ - The `benchmark-code` topic — timing retried operations.
3909
+ - The `write-use-case` topic — the use-case `retry` option.
3910
3910
 
3911
3911
 
3912
3912
  ## run-app `@warlock.js/core/run-app/SKILL.md`
3913
3913
 
3914
3914
  ---
3915
3915
  name: run-app
3916
- description: 'Three operational commands — `warlock dev` (HMR + type-gen + health checks), `warlock build` (esbuild bundle), `warlock start` (spawn the production bundle). All flags, all `warlock.config.ts` knobs that shape them. Triggers: `warlock dev`, `warlock build`, `warlock start`, `devServer`, `--fresh`, `--skip-typings`, `--skip-health`, `outdir`, `outFile`, `sourcemap`, `PortInUseError`, `assertPortIsAvailable`, `EADDRINUSE`, `EsbuildBinaryMissingError`; "start the dev server", "build for production", "run the bundle", "skip type generation", "tune watch globs", "dev server keyboard shortcuts", "press r to restart", "press q to quit", "restart the dev server", "port already in use"; typical config `warlock.config.ts > devServer / build`. Skip: writing a custom CLI — `@warlock.js/core/write-cli-command/SKILL.md`; config shape — `@warlock.js/core/configure-app/SKILL.md`; competing tooling `nodemon`, `tsx`, `ts-node-dev`, `esbuild` direct.'
3916
+ description: 'Three operational commands — `warlock dev` (HMR + type-gen + health checks), `warlock build` (esbuild bundle), `warlock start` (spawn the production bundle). All flags, all `warlock.config.ts` knobs that shape them. Triggers: `warlock dev`, `warlock build`, `warlock start`, `devServer`, `--fresh`, `--skip-typings`, `--skip-health`, `outdir`, `outFile`, `sourcemap`, `PortInUseError`, `assertPortIsAvailable`, `EADDRINUSE`, `EsbuildBinaryMissingError`; "start the dev server", "build for production", "run the bundle", "skip type generation", "tune watch globs", "dev server keyboard shortcuts", "press r to restart", "press q to quit", "restart the dev server", "port already in use"; typical config `warlock.config.ts > devServer / build`. Skip: writing a custom CLI — the `write-cli-command` topic; config shape — the `configure-app` topic; competing tooling `nodemon`, `tsx`, `ts-node-dev`, `esbuild` direct.'
3917
3917
  ---
3918
3918
 
3919
3919
  # Warlock — run the app
@@ -3959,7 +3959,7 @@ Once the server is ready, `warlock dev` listens for single keypresses:
3959
3959
  | `c` | Clear the console. |
3960
3960
  | `q` | Graceful shutdown, exit `0` — the same path as `Ctrl+C`. |
3961
3961
  | `h` | Print the shortcuts that are armed right now. |
3962
- | `u` | **Only while an update notice is showing** — update every `@warlock.js/*` package, install, and restart. See [`update-packages/SKILL.md`](../update-packages/SKILL.md). |
3962
+ | `u` | **Only while an update notice is showing** — update every `@warlock.js/*` package, install, and restart. See the `update-packages` topic. |
3963
3963
  | `Ctrl+C` | Graceful shutdown, unchanged. |
3964
3964
 
3965
3965
  A "restart" is a fresh process (`r` and `u` both use it) — see [The supervisor](#the-supervisor) below for how that works.
@@ -4065,7 +4065,7 @@ export default defineConfig({
4065
4065
  - **`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.
4066
4066
  - **`healthCheckers`** — custom file health checker contracts (or `false` to disable). The `--skip-health` flag is the per-run version.
4067
4067
  - **`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.
4068
- - **`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 [`update-packages/SKILL.md`](../update-packages/SKILL.md).
4068
+ - **`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.
4069
4069
  - **`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.
4070
4070
  - **`timings`** — print a one-line, per-phase breakdown next to the `hmr update` line on every hot reload: `watcher`, `debounce`, `graph`, `reimport`, `connectors`. `watcher` is the raw-fs-notification-to-stabilised-event gap (chokidar's `awaitWriteFinish` window); `debounce` is the handler's own adaptive wait; the rest are self-explanatory. Opt-in, off by default — a disabled flag costs one boolean check per reload, since the watcher-settle bookkeeping only runs when this is on. Use it to see which phase a slow reload is actually spending time in.
4071
4071
 
@@ -4132,7 +4132,7 @@ It exists so `warlock start` can tell a promoted build from a directory that mer
4132
4132
 
4133
4133
  `warlock start` uses the same `resolveBuildConfig()` helper to find the bundle, so the two commands stay in sync no matter how you override the config. If `build` and `start` disagree on where the bundle is, it's because `warlock.config.ts` is being read with different cwds — never the case in normal operation.
4134
4134
 
4135
- If the app uses `@warlock.js/web` page routes, a successful `warlock build` also writes `page-routes.manifest.json` next to the bundle (same `outdir`) — the snapshot `warlock routes:diff` compares the live dev surface against. See [`warlock-routes/SKILL.md`](../warlock-routes/SKILL.md#warlock-routesdiff--catch-page-route-drift-before-it-ships).
4135
+ If the app uses `@warlock.js/web` page routes, a successful `warlock build` also writes `page-routes.manifest.json` next to the bundle (same `outdir`) — the snapshot `warlock routes:diff` compares the live dev surface against. See the `warlock-routes` topic (its `routes:diff` section).
4136
4136
 
4137
4137
  ## `warlock start` — run the production bundle
4138
4138
 
@@ -4309,7 +4309,7 @@ If you need the same fact inside the app, use `Application.onceBooted()` / `Appl
4309
4309
 
4310
4310
  `*` — `environment` follows `NODE_ENV`. The dev command doesn't force it, but the default in most projects is `development`. `start` doesn't force it either; deployments set `NODE_ENV=production` themselves.
4311
4311
 
4312
- If you need conditional behavior, branch on `Application.environment` (the orthogonal "what world am I talking to?" axis), not `runtimeStrategy` (the "how is the framework itself running?" axis). See [`use-app-context/SKILL.md`](../use-app-context/SKILL.md).
4312
+ If you need conditional behavior, branch on `Application.environment` (the orthogonal "what world am I talking to?" axis), not `runtimeStrategy` (the "how is the framework itself running?" axis). See the `use-app-context` topic.
4313
4313
 
4314
4314
  ## Common patterns
4315
4315
 
@@ -4420,19 +4420,19 @@ NODE_OPTIONS=--max-old-space-size=4096 npx warlock start
4420
4420
 
4421
4421
  ## See also
4422
4422
 
4423
- - [`write-cli-command/SKILL.md`](../write-cli-command/SKILL.md) — author a custom CLI command + the rest of the built-in commands (migrate / seed / generate.* / add / storage.put / jwt.generate).
4424
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — `warlock.config.ts` shape and `defineConfig`.
4425
- - [`use-app-context/SKILL.md`](../use-app-context/SKILL.md) — `Application.environment` vs `Application.runtimeStrategy`.
4426
- - [`add-connector/SKILL.md`](../add-connector/SKILL.md) — Early vs Late connector phases (why HTTP/socket boot late in dev).
4427
- - [`update-packages/SKILL.md`](../update-packages/SKILL.md) — `warlock update` + the dev-server update notice (`devServer.checkForUpdates`).
4428
- - [`warlock-routes/SKILL.md`](../warlock-routes/SKILL.md) — `warlock routes` / `warlock routes:diff`, the latter comparing live page routes against the manifest `warlock build` writes.
4423
+ - The `write-cli-command` topic — author a custom CLI command + the rest of the built-in commands (migrate / seed / generate.* / add / storage.put / jwt.generate).
4424
+ - The `configure-app` topic — `warlock.config.ts` shape and `defineConfig`.
4425
+ - The `use-app-context` topic — `Application.environment` vs `Application.runtimeStrategy`.
4426
+ - The `add-connector` topic — Early vs Late connector phases (why HTTP/socket boot late in dev).
4427
+ - The `update-packages` topic — `warlock update` + the dev-server update notice (`devServer.checkForUpdates`).
4428
+ - The `warlock-routes` topic — `warlock routes` / `warlock routes:diff`, the latter comparing live page routes against the manifest `warlock build` writes.
4429
4429
 
4430
4430
 
4431
4431
  ## send-mail `@warlock.js/core/send-mail/SKILL.md`
4432
4432
 
4433
4433
  ---
4434
4434
  name: send-mail
4435
- description: '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 — `@warlock.js/core/configure-app/SKILL.md`; layered service patterns — `@warlock.js/core/warlock-conventions/SKILL.md`; competing libs `nodemailer` direct, `@sendgrid/mail`, `resend`, `mailgun.js`.'
4435
+ description: '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`.'
4436
4436
  ---
4437
4437
 
4438
4438
  # Warlock — send a mail
@@ -4830,15 +4830,15 @@ await Mail.to(user.email)
4830
4830
 
4831
4831
  ## See also
4832
4832
 
4833
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — `src/config/mail.ts` shape and `env()` patterns.
4834
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — where mail-sending services live.
4833
+ - The `configure-app` topic — `src/config/mail.ts` shape and `env()` patterns.
4834
+ - The `warlock-conventions` topic — where mail-sending services live.
4835
4835
 
4836
4836
 
4837
4837
  ## send-response `@warlock.js/core/send-response/SKILL.md`
4838
4838
 
4839
4839
  ---
4840
4840
  name: send-response
4841
- description: '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`, `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 — `@warlock.js/core/create-controller/SKILL.md`; route registration — `@warlock.js/core/register-route/SKILL.md`; competing patterns: hand-rolled status codes via `reply.code(404).send(...)`, raw Fastify reply.'
4841
+ description: '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`, `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.'
4842
4842
  ---
4843
4843
 
4844
4844
  # Warlock — send a response
@@ -4983,7 +4983,7 @@ export const getProductController: RequestHandler = async ({ request, response }
4983
4983
  };
4984
4984
  ```
4985
4985
 
4986
- See [`create-controller`](../create-controller/SKILL.md) for the "throw from service, return from controller" pattern.
4986
+ See the `create-controller` topic for the "throw from service, return from controller" pattern.
4987
4987
 
4988
4988
  ## Server-Sent Events
4989
4989
 
@@ -5063,7 +5063,7 @@ These mutate the response in place; chain or call before the final `return respo
5063
5063
  ### `Content-Security-Policy` (5.12.0)
5064
5064
 
5065
5065
  Opt in via `http.csp.enabled` in `src/config/http.ts` (see
5066
- [`configure-app/SKILL.md`](../configure-app/SKILL.md) for the full shape).
5066
+ the `configure-app` topic for the full shape).
5067
5067
  When enabled, every response through the shared request funnel
5068
5068
  (`http/middleware/inject-request-context.ts`, the same seam that stamps
5069
5069
  `X-Request-Id`) gets a `Content-Security-Policy` header — `-Report-Only`
@@ -5143,16 +5143,16 @@ return response.header("X-Cache", "HIT").replay({
5143
5143
 
5144
5144
  ## See also
5145
5145
 
5146
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — what calls `response.<helper>()` from.
5147
- - [`register-route/SKILL.md`](../register-route/SKILL.md) — wiring the controller to a URL.
5148
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — when to use HTTP error helpers vs `throw`.
5146
+ - The `create-controller` topic — what calls `response.<helper>()` from.
5147
+ - The `register-route` topic — wiring the controller to a URL.
5148
+ - The `warlock-conventions` topic — when to use HTTP error helpers vs `throw`.
5149
5149
 
5150
5150
 
5151
5151
  ## store-file `@warlock.js/core/store-file/SKILL.md`
5152
5152
 
5153
5153
  ---
5154
5154
  name: store-file
5155
- description: '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 — `@warlock.js/core/upload-file/SKILL.md`; image transforms — `@warlock.js/core/process-image/SKILL.md`; storage config shape — `@warlock.js/core/configure-app/SKILL.md`; competing libs `@aws-sdk/client-s3`, `multer`, `formidable`.'
5155
+ description: '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`.'
5156
5156
  ---
5157
5157
 
5158
5158
  # Warlock — store a file
@@ -5547,16 +5547,16 @@ Client `PUT`s the bytes straight to R2 — your server never sees them.
5547
5547
 
5548
5548
  ## See also
5549
5549
 
5550
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — `src/config/storage.ts` shape and `env()` patterns.
5551
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — module layout for upload flows (`src/app/uploads/`).
5552
- - [`send-response/SKILL.md`](../send-response/SKILL.md) — `response.sendFile(absPath)` for serving local files.
5550
+ - The `configure-app` topic — `src/config/storage.ts` shape and `env()` patterns.
5551
+ - The `warlock-conventions` topic — module layout for upload flows (`src/app/uploads/`).
5552
+ - The `send-response` topic — `response.sendFile(absPath)` for serving local files.
5553
5553
 
5554
5554
 
5555
5555
  ## test-http `@warlock.js/core/test-http/SKILL.md`
5556
5556
 
5557
5557
  ---
5558
5558
  name: test-http
5559
- description: '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 })`, `stopHttpTestServer`, `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"; typical import `import { testGet, testPost, expectJson } from "@warlock.js/core/tests"`. Skip: pure unit tests — `@warlock.js/core/test-service/SKILL.md`; controller shape — `@warlock.js/core/create-controller/SKILL.md`; competing libs `supertest`, `light-my-request`, `nock`.'
5559
+ description: '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 })`, `stopHttpTestServer`, `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"; 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`.'
5560
5560
  ---
5561
5561
 
5562
5562
  # Warlock — HTTP integration tests
@@ -5906,17 +5906,17 @@ This is fine for normal test flow. It bites when you're inside a transaction the
5906
5906
 
5907
5907
  ## See also
5908
5908
 
5909
- - [`test-service/SKILL.md`](../test-service/SKILL.md) — pure unit tests against services/repositories/models (no HTTP).
5910
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — the controller shape your HTTP tests are exercising.
5911
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — module layout, where `tests/*.test.ts` files live.
5912
- - [`write-cli-command/SKILL.md`](../write-cli-command/SKILL.md) — `warlock add test` scaffolds both global + worker setup files.
5909
+ - The `test-service` topic — pure unit tests against services/repositories/models (no HTTP).
5910
+ - The `create-controller` topic — the controller shape your HTTP tests are exercising.
5911
+ - The `warlock-conventions` topic — module layout, where `tests/*.test.ts` files live.
5912
+ - The `write-cli-command` topic — `warlock add test` scaffolds both global + worker setup files.
5913
5913
 
5914
5914
 
5915
5915
  ## test-service `@warlock.js/core/test-service/SKILL.md`
5916
5916
 
5917
5917
  ---
5918
5918
  name: test-service
5919
- description: '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`, `src/test-setup.ts`, `tests.connectors`, `tests.setupTimeout`, `Application.setEnvironment`; "unit-test a service", "test a repository query", "vitest setupFiles", "skip connectors for pure-logic tests"; typical import `import { setupTest, teardownTest } from "@warlock.js/core/tests"`. Skip: HTTP integration — `@warlock.js/core/test-http/SKILL.md`; warlock add test scaffold — `@warlock.js/core/write-cli-command/SKILL.md`; competing tooling: jest direct, `supertest`, `nock`.'
5919
+ description: '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`, `src/test-setup.ts`, `tests.connectors`, `tests.setupTimeout`, `Application.setEnvironment`; "unit-test a service", "test a repository query", "vitest setupFiles", "skip connectors for pure-logic tests"; 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`.'
5920
5920
  ---
5921
5921
 
5922
5922
  # Warlock — test a service
@@ -6067,7 +6067,7 @@ export default defineConfig({
6067
6067
  });
6068
6068
  ```
6069
6069
 
6070
- The `mongezVite()` plugin handles TypeScript path resolution and the framework's module shape. Without it, your imports break the moment vitest tries to load a Warlock module. `lowerStage3Decorators()` goes **first** — it lets decorated Cascade models (`@RegisterModel`, …) load under Vitest 4 / Vite 8; see [`@warlock.js/core/lower-stage3-decorators/SKILL.md`](@warlock.js/core/lower-stage3-decorators/SKILL.md).
6070
+ The `mongezVite()` plugin handles TypeScript path resolution and the framework's module shape. Without it, your imports break the moment vitest tries to load a Warlock module. `lowerStage3Decorators()` goes **first** — it lets decorated Cascade models (`@RegisterModel`, …) load under Vitest 4 / Vite 8; see the `lower-stage3-decorators` topic.
6071
6071
 
6072
6072
  ### Keep Vitest setup out of development
6073
6073
 
@@ -6307,9 +6307,9 @@ In both cases the guard's scope matches the resource's scope, which is the point
6307
6307
 
6308
6308
  ## See also
6309
6309
 
6310
- - [`test-http/SKILL.md`](../test-http/SKILL.md) — integration tests via the real HTTP server (`startHttpTestServer` + `testGet` / `testPost` / `expectJson`).
6311
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — where tests live in a module (`tests/*.test.ts`).
6312
- - [`write-cli-command/SKILL.md`](../write-cli-command/SKILL.md) — `warlock add test` for the initial scaffold.
6310
+ - The `test-http` topic — integration tests via the real HTTP server (`startHttpTestServer` + `testGet` / `testPost` / `expectJson`).
6311
+ - The `warlock-conventions` topic — where tests live in a module (`tests/*.test.ts`).
6312
+ - The `write-cli-command` topic — `warlock add test` for the initial scaffold.
6313
6313
 
6314
6314
  ## App modules and `importModule`
6315
6315
 
@@ -6326,7 +6326,7 @@ The generated `src/test-setup.ts` already does this. Without it, `setupTest()` u
6326
6326
 
6327
6327
  ---
6328
6328
  name: update-packages
6329
- description: 'Keep a project current with `warlock update` — bump every `@warlock.js/*` dependency in package.json to its latest published version (range operator preserved), then run the lockfile-detected package manager install. Also covers the `warlock dev` update notice, its `u` update-and-restart keyboard shortcut, and the `devServer.checkForUpdates` toggle. Triggers: `warlock update`, `--no-install`, `--dry-run`, `--check`, `checkForUpdates`, `fetchLatestVersion`, `isNewerVersion`; "update warlock packages", "upgrade the framework", "is there a new warlock version", "update notice in the dev server", "press u to update", "dev server keyboard shortcut", "update check offline", "bump @warlock.js/* to latest"; typical CLI `warlock update`. Skip: dev/build/start runtime — `@warlock.js/core/run-app/SKILL.md`; writing a custom command — `@warlock.js/core/write-cli-command/SKILL.md`; installing a NEW feature package (auth, mail, storage) — that is `warlock add`; releasing/publishing the framework — workspace release tooling, not this command.'
6329
+ description: 'Keep a project current with `warlock update` — bump every `@warlock.js/*` dependency in package.json to its latest published version (range operator preserved), then run the lockfile-detected package manager install. Also covers the `warlock dev` update notice, its `u` update-and-restart keyboard shortcut, and the `devServer.checkForUpdates` toggle. Triggers: `warlock update`, `--no-install`, `--dry-run`, `--check`, `checkForUpdates`, `fetchLatestVersion`, `isNewerVersion`; "update warlock packages", "upgrade the framework", "is there a new warlock version", "update notice in the dev server", "press u to update", "dev server keyboard shortcut", "update check offline", "bump @warlock.js/* to latest"; typical CLI `warlock update`. Skip: dev/build/start runtime — the `run-app` topic; writing a custom command — the `write-cli-command` topic; installing a NEW feature package (auth, mail, storage) — that is `warlock add`; releasing/publishing the framework — workspace release tooling, not this command.'
6330
6330
  ---
6331
6331
 
6332
6332
  # Warlock — update the framework
@@ -6405,7 +6405,7 @@ Pressing `u` on that notice runs the whole upgrade without leaving the dev serve
6405
6405
 
6406
6406
  1. The shortcut is disarmed and the terminal handed back, so the package manager owns stdin.
6407
6407
  2. Every `@warlock.js/*` dependency is rewritten to latest and installed — exactly what `warlock update` does.
6408
- 3. The worker shuts down (freeing the http port) and exits `75`; the `warlock dev` supervisor spawns a replacement on the new version. See [`run-app/SKILL.md`](../run-app/SKILL.md) for the supervisor.
6408
+ 3. The worker shuts down (freeing the http port) and exits `75`; the `warlock dev` supervisor spawns a replacement on the new version. See the `run-app` topic for the supervisor.
6409
6409
 
6410
6410
  What happens when it doesn't go to plan:
6411
6411
 
@@ -6417,7 +6417,7 @@ What happens when it doesn't go to plan:
6417
6417
 
6418
6418
  The shortcut is only offered when stdin is an interactive TTY. When it isn't (CI, piped stdin, a process supervisor), the notice falls back to the `Run npx warlock update` line instead — it never silently does nothing.
6419
6419
 
6420
- `u` joins the dev server's standing shortcut bar (`r` restart, `c` clear, `q` quit, `h` help) — press `h` to list whatever is armed. See [`run-app/SKILL.md`](../run-app/SKILL.md) for the bar and the raw-mode / `Ctrl+C` contract.
6420
+ `u` joins the dev server's standing shortcut bar (`r` restart, `c` clear, `q` quit, `h` help) — press `h` to list whatever is armed. See the `run-app` topic for the bar and the raw-mode / `Ctrl+C` contract.
6421
6421
 
6422
6422
  It is automatically skipped when:
6423
6423
 
@@ -6454,16 +6454,16 @@ Two small zero-dependency utilities back the tooling and are exported from `@war
6454
6454
 
6455
6455
  ## See also
6456
6456
 
6457
- - [`run-app/SKILL.md`](../run-app/SKILL.md) — `warlock dev` / `build` / `start` and the `devServer.*` config knobs.
6458
- - [`write-cli-command/SKILL.md`](../write-cli-command/SKILL.md) — author your own `warlock <cmd>`.
6459
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — `warlock.config.ts` shape and `defineConfig`.
6457
+ - The `run-app` topic — `warlock dev` / `build` / `start` and the `devServer.*` config knobs.
6458
+ - The `write-cli-command` topic — author your own `warlock <cmd>`.
6459
+ - The `configure-app` topic — `warlock.config.ts` shape and `defineConfig`.
6460
6460
 
6461
6461
 
6462
6462
  ## upload-file `@warlock.js/core/upload-file/SKILL.md`
6463
6463
 
6464
6464
  ---
6465
6465
  name: upload-file
6466
- description: 'Handle multipart file uploads — read via `request.file()` or `request.validated()`, validate with `v.file()`, save via `UploadedFile.save()` or the storage layer, transform images inline. Triggers: `UploadedFile`, `request.file`, `v.file`, `.save`, `.saveAs`, `.resize`, `.format`, `.quality`, `.image`, `.mimeType`, `.maxSize`; "accept a file upload", "validate file size and mime", "save to S3 or local disk", "resize an uploaded image on save"; typical import `import type { UploadedFile, RequestHandler } from "@warlock.js/core"`. Skip: storage drivers + presigned URLs — `@warlock.js/core/store-file/SKILL.md`; image-only transforms — `@warlock.js/core/process-image/SKILL.md`; schema rules — `@warlock.js/core/validate-input/SKILL.md`; competing libs `multer`, `formidable`, `busboy`.'
6466
+ description: 'Handle multipart file uploads — read via `request.file()` or `request.validated()`, validate with `v.file()`, save via `UploadedFile.save()` or the storage layer, transform images inline. Triggers: `UploadedFile`, `request.file`, `v.file`, `.save`, `.saveAs`, `.resize`, `.format`, `.quality`, `.image`, `.mimeType`, `.maxSize`; "accept a file upload", "validate file size and mime", "save to S3 or local disk", "resize an uploaded image on save"; typical import `import type { UploadedFile, RequestHandler } from "@warlock.js/core"`. Skip: storage drivers + presigned URLs — the `store-file` topic; image-only transforms — the `process-image` topic; schema rules — the `validate-input` topic; competing libs `multer`, `formidable`, `busboy`.'
6467
6467
  ---
6468
6468
 
6469
6469
  # Warlock — upload a file
@@ -6671,7 +6671,7 @@ v.file() // must be UploadedFile
6671
6671
  .minHeight(200).maxHeight(4000);
6672
6672
  ```
6673
6673
 
6674
- Size accepts either bytes (`.maxSize(5_242_880)`) or `{ unit, size }` (`{ unit: "MB", size: 5 }`). See [`validate-input`](../validate-input/SKILL.md) for the full validation pattern.
6674
+ Size accepts either bytes (`.maxSize(5_242_880)`) or `{ unit, size }` (`{ unit: "MB", size: 5 }`). See the `validate-input` topic for the full validation pattern.
6675
6675
 
6676
6676
  ### Optional file field
6677
6677
 
@@ -6754,7 +6754,7 @@ await storage.use("s3").put(await uploadedFile.buffer(), "documents/report.pdf",
6754
6754
 
6755
6755
  ### Stream a download from a stored file
6756
6756
 
6757
- The reverse direction — sending a stored file back via `response.sendFile(...)` is the cleanest path. For dynamic content, use `response.stream(...)`. See [`send-response`](../send-response/SKILL.md).
6757
+ The reverse direction — sending a stored file back via `response.sendFile(...)` is the cleanest path. For dynamic content, use `response.stream(...)`. See the `send-response` topic.
6758
6758
 
6759
6759
  ## Gotchas
6760
6760
 
@@ -6768,17 +6768,17 @@ The reverse direction — sending a stored file back via `response.sendFile(...)
6768
6768
 
6769
6769
  ## See also
6770
6770
 
6771
- - [`validate-input/SKILL.md`](../validate-input/SKILL.md) — `v.file()` rules and the validation pipeline.
6772
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — pulling files from `request.validated()` vs `request.file()`.
6773
- - [`send-response/SKILL.md`](../send-response/SKILL.md) — `response.sendFile(...)` for serving the stored file back.
6774
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — storage configuration and driver selection.
6771
+ - The `validate-input` topic — `v.file()` rules and the validation pipeline.
6772
+ - The `create-controller` topic — pulling files from `request.validated()` vs `request.file()`.
6773
+ - The `send-response` topic — `response.sendFile(...)` for serving the stored file back.
6774
+ - The `warlock-conventions` topic — storage configuration and driver selection.
6775
6775
 
6776
6776
 
6777
6777
  ## use-app-context `@warlock.js/core/use-app-context/SKILL.md`
6778
6778
 
6779
6779
  ---
6780
6780
  name: use-app-context
6781
- description: 'Read app-wide context — the `Application` static class (env, version, uptime, runtime strategy, boot lifecycle) plus the `app` runtime accessor (live Fastify, socket.io, router, database via the DI container). Triggers: `Application.isProduction`, `Application.environment`, `Application.runtimeStrategy`, `Application.uptime`, `Application.version`, `Application.onceBooted`, `Application.whenBooted`, `Application.isBooted`, `Application.onShutdown`, `Application.isShuttingDown`, `app.http`, `app.socket`, `app.database`, `app.router`; "branch on environment", "reach the live Fastify instance", "framework version in health endpoint", "dev vs production runtime check", "run code once the app is fully booted", "after all connectors started", "app booted hook", "run cleanup before shutdown", "graceful shutdown hook"; typical import `import { Application, app } from "@warlock.js/core"`. Skip: path helpers — `@warlock.js/core/resolve-path/SKILL.md`; connector start order — `@warlock.js/core/add-connector/SKILL.md`; competing patterns: bare `process.env.NODE_ENV`, ad-hoc Fastify imports.'
6781
+ description: 'Read app-wide context — the `Application` static class (env, version, uptime, runtime strategy, boot lifecycle) plus the `app` runtime accessor (live Fastify, socket.io, router, database via the DI container). Triggers: `Application.isProduction`, `Application.environment`, `Application.runtimeStrategy`, `Application.uptime`, `Application.version`, `Application.onceBooted`, `Application.whenBooted`, `Application.isBooted`, `Application.onShutdown`, `Application.isShuttingDown`, `app.http`, `app.socket`, `app.database`, `app.router`; "branch on environment", "reach the live Fastify instance", "framework version in health endpoint", "dev vs production runtime check", "run code once the app is fully booted", "after all connectors started", "app booted hook", "run cleanup before shutdown", "graceful shutdown hook"; typical import `import { Application, app } from "@warlock.js/core"`. Skip: path helpers — the `resolve-path` topic; connector start order — the `add-connector` topic; competing patterns: bare `process.env.NODE_ENV`, ad-hoc Fastify imports.'
6782
6782
  ---
6783
6783
 
6784
6784
  # Warlock — use the application context
@@ -6909,11 +6909,11 @@ Application.onceBooted(() => {
6909
6909
 
6910
6910
  Hooks run **LIFO** (reverse of registration — last opened, first closed), each is awaited, and a throwing hook is caught + logged so it can't block the rest. `Application.isShuttingDown` flips `true` the moment shutdown begins — the built-in `/ready` endpoint reads it to report not-ready so a load balancer drains the instance first. Like `onceBooted`, registering after shutdown has begun runs the callback immediately.
6911
6911
 
6912
- The framework triggers this for you (the connectors manager runs the hooks at the start of shutdown); app code only ever registers via `onShutdown`. For the HTTP-side story — `/health`, `/ready`, and graceful request draining — see `@warlock.js/core/health-checks/SKILL.md`.
6912
+ The framework triggers this for you (the connectors manager runs the hooks at the start of shutdown); app code only ever registers via `onShutdown`. For the HTTP-side story — `/health`, `/ready`, and graceful request draining — see the `health-checks` topic.
6913
6913
 
6914
6914
  ## Paths
6915
6915
 
6916
- For path helpers (`appPath`, `configPath`, `uploadsPath`, …) anchored at `process.cwd()`, the `paths.*` aggregate, and the `uploads.root` config override, see [`resolve-path/SKILL.md`](../resolve-path/SKILL.md).
6916
+ For path helpers (`appPath`, `configPath`, `uploadsPath`, …) anchored at `process.cwd()`, the `paths.*` aggregate, and the `uploads.root` config override, see the `resolve-path` topic.
6917
6917
 
6918
6918
  The most-used helpers are also surfaced as no-argument getters on `Application`:
6919
6919
 
@@ -7072,17 +7072,17 @@ cors: {
7072
7072
 
7073
7073
  ## See also
7074
7074
 
7075
- - [`resolve-path/SKILL.md`](../resolve-path/SKILL.md) — path helpers (`appPath`, `configPath`, `uploadsPath`, …) and the `paths.*` aggregate.
7076
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — `src/config/*.ts` and `warlock.config.ts`.
7077
- - [`add-connector/SKILL.md`](../add-connector/SKILL.md) — using `Application.runtimeStrategy` inside a connector's `start()`.
7078
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — module layout, where paths point to.
7075
+ - The `resolve-path` topic — path helpers (`appPath`, `configPath`, `uploadsPath`, …) and the `paths.*` aggregate.
7076
+ - The `configure-app` topic — `src/config/*.ts` and `warlock.config.ts`.
7077
+ - The `add-connector` topic — using `Application.runtimeStrategy` inside a connector's `start()`.
7078
+ - The `warlock-conventions` topic — module layout, where paths point to.
7079
7079
 
7080
7080
 
7081
7081
  ## use-localization `@warlock.js/core/use-localization/SKILL.md`
7082
7082
 
7083
7083
  ---
7084
7084
  name: use-localization
7085
- description: 'Multi-locale translations via `groupedTranslations` (declare keys), `t()` (look up; always use it in app code, never `request.t()`), `request.getLocaleCode()` (detect locale from headers/query), `getLocalized` (pick the right value from a localized-array column). Triggers: `groupedTranslations`, `t`, `request.t`, `request.trans`, `request.transFrom`, `request.getLocaleCode`, `request.setLocaleCode`, `getLocalized`; "add a translation key", "resolve a localized error message", "detect request locale", "pick the right per-locale column value"; typical import `import { t, getLocalized } from "@warlock.js/core"`. Skip: resource output — `@warlock.js/core/define-resource/SKILL.md`; module scaffold — `@warlock.js/core/create-module/SKILL.md`; competing libs `i18next`, `react-intl`, raw `@mongez/localization`.'
7085
+ description: 'Multi-locale translations via `groupedTranslations` (declare keys), `t()` (look up; always use it in app code, never `request.t()`), `request.getLocaleCode()` (detect locale from headers/query), `getLocalized` (pick the right value from a localized-array column). Triggers: `groupedTranslations`, `t`, `request.t`, `request.trans`, `request.transFrom`, `request.getLocaleCode`, `request.setLocaleCode`, `getLocalized`; "add a translation key", "resolve a localized error message", "detect request locale", "pick the right per-locale column value"; typical import `import { t, getLocalized } from "@warlock.js/core"`. Skip: resource output — the `define-resource` topic; module scaffold — the `create-module` topic; competing libs `i18next`, `react-intl`, raw `@mongez/localization`.'
7086
7086
  ---
7087
7087
 
7088
7088
  # Warlock — translate keys + pick localized values
@@ -7197,7 +7197,7 @@ When an app uses `@warlock.js/web`, `warlock dev` writes `.warlock/typings/trans
7197
7197
  `useTrans()` now works correctly across hydration (5.15.0): the hydration
7198
7198
  payload ships a `translations` key with the active locale's keywords, and
7199
7199
  `@warlock.js/web` registers them into this same lookup table before the
7200
- client hydrates — see `@warlock.js/web/write-the-root/SKILL.md`'s "`useTrans()`
7200
+ client hydrates — see the `write-the-root` topic of the `warlock-js-web` skill's "`useTrans()`
7201
7201
  survives hydration" section for the failure this fixed.
7202
7202
 
7203
7203
  ### Locale on a specific lookup
@@ -7398,10 +7398,10 @@ GET /products/42?locale=ar
7398
7398
 
7399
7399
  ## See also
7400
7400
 
7401
- - [`send-response/SKILL.md`](../send-response/SKILL.md) — error helpers that pair with translated messages (`response.notFound({ error: t("...") })`).
7402
- - [`define-resource/SKILL.md`](../define-resource/SKILL.md) — using `getLocalized` inside resource output for clean per-locale responses.
7403
- - [`create-module/SKILL.md`](../create-module/SKILL.md) — the `utils/locales.json` file is part of the generated module scaffold.
7404
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — `utils/locales.json` is auto-loaded; the suffix is mandatory.
7401
+ - The `send-response` topic — error helpers that pair with translated messages (`response.notFound({ error: t("...") })`).
7402
+ - The `define-resource` topic — using `getLocalized` inside resource output for clean per-locale responses.
7403
+ - The `create-module` topic — the `utils/locales.json` file is part of the generated module scaffold.
7404
+ - The `warlock-conventions` topic — `utils/locales.json` is auto-loaded; the suffix is mandatory.
7405
7405
 
7406
7406
  ## Route-owned web translations (5.17)
7407
7407
 
@@ -7424,7 +7424,7 @@ When a route-locales snapshot applies, `t()` (through the request binding) and `
7424
7424
 
7425
7425
  ---
7426
7426
  name: use-middleware
7427
- description: 'Attach built-in HTTP middleware to routes via the `middleware` namespace from `@warlock.js/core` — rateLimit, concurrencyLimit, maxBodySize, idempotency, maintenance, ipFilter, cache. Plus `X-Request-Id` correlation, wired automatically. Triggers: `middleware.rateLimit`, `middleware.concurrencyLimit`, `middleware.maxBodySize`, `middleware.idempotency`, `middleware.maintenance`, `middleware.ipFilter`, `middleware.cache`, `X-Request-Id`, `Idempotency-Key`; "add rate limiting", "dedupe writes by idempotency key", "cap concurrent requests", "block IPs", "cache a GET response"; typical import `import { middleware } from "@warlock.js/core"`. Skip: author custom middleware — `@warlock.js/core/write-middleware/SKILL.md`; cache singleton — `@warlock.js/cache/cache-basics/SKILL.md`; competing libs `@fastify/rate-limit` direct, `express-rate-limit`, `helmet`.'
7427
+ description: 'Attach built-in HTTP middleware to routes via the `middleware` namespace from `@warlock.js/core` — rateLimit, concurrencyLimit, maxBodySize, idempotency, maintenance, ipFilter, cache. Plus `X-Request-Id` correlation, wired automatically. Triggers: `middleware.rateLimit`, `middleware.concurrencyLimit`, `middleware.maxBodySize`, `middleware.idempotency`, `middleware.maintenance`, `middleware.ipFilter`, `middleware.cache`, `X-Request-Id`, `Idempotency-Key`; "add rate limiting", "dedupe writes by idempotency key", "cap concurrent requests", "block IPs", "cache a GET response"; typical import `import { middleware } from "@warlock.js/core"`. Skip: author custom middleware — the `write-middleware` topic; cache singleton — the `cache-basics` topic of the `warlock-js-cache` skill; competing libs `@fastify/rate-limit` direct, `express-rate-limit`, `helmet`.'
7428
7428
  ---
7429
7429
 
7430
7430
  # Warlock — use built-in middleware
@@ -7440,7 +7440,7 @@ middleware.maxBodySize("2mb");
7440
7440
  // ... etc
7441
7441
  ```
7442
7442
 
7443
- For authoring your OWN middleware (custom guards, enrichment, etc.), see [`write-middleware`](../write-middleware/SKILL.md).
7443
+ For authoring your OWN middleware (custom guards, enrichment, etc.), see the `write-middleware` topic.
7444
7444
 
7445
7445
  ## The catalog
7446
7446
 
@@ -7692,17 +7692,17 @@ export default {
7692
7692
 
7693
7693
  ## See also
7694
7694
 
7695
- - [`write-middleware/SKILL.md`](../write-middleware/SKILL.md) — author your OWN middleware (custom guards, enrichment).
7696
- - [`register-route/SKILL.md`](../register-route/SKILL.md) — where middleware attaches: `router.group` and route-options.
7697
- - [`send-response/SKILL.md`](../send-response/SKILL.md) — the response helpers used to short-circuit (`tooManyRequests`, `contentTooLarge`, `serviceUnavailable`, etc.).
7698
- - [`@warlock.js/cache/cache-basics/SKILL.md`](../../../cache/skills/cache-basics/SKILL.md) — the cache singleton (`@warlock.js/cache`) that backs `middleware.idempotency` and `middleware.cache`.
7695
+ - The `write-middleware` topic — author your OWN middleware (custom guards, enrichment).
7696
+ - The `register-route` topic — where middleware attaches: `router.group` and route-options.
7697
+ - The `send-response` topic — the response helpers used to short-circuit (`tooManyRequests`, `contentTooLarge`, `serviceUnavailable`, etc.).
7698
+ - The `cache-basics` topic of the `warlock-js-cache` skill — the cache singleton (`@warlock.js/cache`) that backs `middleware.idempotency` and `middleware.cache`.
7699
7699
 
7700
7700
 
7701
7701
  ## use-model-transformers `@warlock.js/core/use-model-transformers/SKILL.md`
7702
7702
 
7703
7703
  ---
7704
7704
  name: use-model-transformers
7705
- description: 'Three schema-side helpers — `useHashedPassword()` (bcrypt on save) attaches via `.addTransformer(...)`; `useComputedSlug(field?, scope?)` (auto-slug from another field) and `useComputedModel(callback)` (arbitrary computed-on-save value) attach via `v.computed(...)`. Triggers: `useHashedPassword`, `useComputedSlug`, `useComputedModel`, `.addTransformer`, `v.computed`, `ComputedCallback`; "auto-hash a password field", "auto-slug from title on save", "derive a value at write time", "declarative model transformers"; typical import `import { useHashedPassword, useComputedSlug } from "@warlock.js/core"`. Skip: bcrypt setup details — `@warlock.js/core/hash-password/SKILL.md`; repository writes — `@warlock.js/core/use-repository/SKILL.md`; output filtering — `@warlock.js/core/define-resource/SKILL.md`; competing patterns: manual `await hashPassword(input)` in services, ORM lifecycle hooks.'
7705
+ description: 'Three schema-side helpers — `useHashedPassword()` (bcrypt on save) attaches via `.addTransformer(...)`; `useComputedSlug(field?, scope?)` (auto-slug from another field) and `useComputedModel(callback)` (arbitrary computed-on-save value) attach via `v.computed(...)`. Triggers: `useHashedPassword`, `useComputedSlug`, `useComputedModel`, `.addTransformer`, `v.computed`, `ComputedCallback`; "auto-hash a password field", "auto-slug from title on save", "derive a value at write time", "declarative model transformers"; typical import `import { useHashedPassword, useComputedSlug } from "@warlock.js/core"`. Skip: bcrypt setup details — the `hash-password` topic; repository writes — the `use-repository` topic; output filtering — the `define-resource` topic; competing patterns: manual `await hashPassword(input)` in services, ORM lifecycle hooks.'
7706
7706
  ---
7707
7707
 
7708
7708
  # Warlock — declare model transformers
@@ -7762,7 +7762,7 @@ What it does at save time:
7762
7762
  | Existing row, password unchanged | Pass through (no re-hashing — stored hash preserved). |
7763
7763
  | Empty / undefined value | Pass through untouched. |
7764
7764
 
7765
- Calls `authService.hashPassword(String(value))` under the hood — same bcryptjs path as the standalone `hashPassword()` helper. See [`hash-password/SKILL.md`](../hash-password/SKILL.md) for full bcrypt setup (salt rounds, `npm install bcryptjs`).
7765
+ Calls `authService.hashPassword(String(value))` under the hood — same bcryptjs path as the standalone `hashPassword()` helper. See the `hash-password` topic for full bcrypt setup (salt rounds, `npm install bcryptjs`).
7766
7766
 
7767
7767
  ### Why declarative wins
7768
7768
 
@@ -7916,17 +7916,17 @@ Rule of thumb: transformers are for **pure, deterministic** transforms of the ro
7916
7916
 
7917
7917
  ## See also
7918
7918
 
7919
- - [`hash-password/SKILL.md`](../hash-password/SKILL.md) — the bcrypt setup that `useHashedPassword` calls under the hood; salt rounds, `npm install bcryptjs`.
7920
- - [`use-repository/SKILL.md`](../use-repository/SKILL.md) — where `create` / `save` calls happen that trigger the transformers.
7921
- - [`define-resource/SKILL.md`](../define-resource/SKILL.md) — filtering transformed fields (`password`) out of API responses.
7922
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — schema files live in `src/app/<module>/models/<entity>/<entity>.model.ts`.
7919
+ - The `hash-password` topic — the bcrypt setup that `useHashedPassword` calls under the hood; salt rounds, `npm install bcryptjs`.
7920
+ - The `use-repository` topic — where `create` / `save` calls happen that trigger the transformers.
7921
+ - The `define-resource` topic — filtering transformed fields (`password`) out of API responses.
7922
+ - The `warlock-conventions` topic — schema files live in `src/app/<module>/models/<entity>/<entity>.model.ts`.
7923
7923
 
7924
7924
 
7925
7925
  ## use-repository `@warlock.js/core/use-repository/SKILL.md`
7926
7926
 
7927
7927
  ---
7928
7928
  name: use-repository
7929
- description: 'Subclass `RepositoryManager` for data access — declare `source`, `filterBy`, `defaultOptions`, then call `list()`/`listCached()`/`find()`/`create()`/`update()`/`delete()`, the active/cached/cursor variants, and the `filterBy`-aware aggregates `sum()`/`avg()`/`min()`/`max()`/`groupBy()`/`aggregate()`. Triggers: `RepositoryManager`, `FilterRules`, `RepositoryOptions`, `.list`, `.listCached`, `.find`, `.findCached`, `.create`, `.update`, `.delete`, `.sum`, `.avg`, `.min`, `.max`, `.groupBy`, `.aggregate`, `simpleSelectColumns`; "create a repository", "filter rules for a list endpoint", "cursor vs page pagination", "cached vs uncached read", "sum/avg/group-by with filters"; typical import `import { RepositoryManager } from "@warlock.js/core"`. Skip: cache singleton — `@warlock.js/cache/cache-basics/SKILL.md`; use-case pipelines — `@warlock.js/core/write-use-case/SKILL.md`; wire mapping — `@warlock.js/core/define-resource/SKILL.md`; competing libs `typeorm` Repository, `prisma.client.<model>`, `@nestjs/typeorm`.'
7929
+ description: 'Subclass `RepositoryManager` for data access — declare `source`, `filterBy`, `defaultOptions`, then call `list()`/`listCached()`/`find()`/`create()`/`update()`/`delete()`, the active/cached/cursor variants, and the `filterBy`-aware aggregates `sum()`/`avg()`/`min()`/`max()`/`groupBy()`/`aggregate()`. Triggers: `RepositoryManager`, `FilterRules`, `RepositoryOptions`, `.list`, `.listCached`, `.find`, `.findCached`, `.create`, `.update`, `.delete`, `.sum`, `.avg`, `.min`, `.max`, `.groupBy`, `.aggregate`, `simpleSelectColumns`; "create a repository", "filter rules for a list endpoint", "cursor vs page pagination", "cached vs uncached read", "sum/avg/group-by with filters"; typical import `import { RepositoryManager } from "@warlock.js/core"`. Skip: cache singleton — the `cache-basics` topic of the `warlock-js-cache` skill; use-case pipelines — the `write-use-case` topic; wire mapping — the `define-resource` topic; competing libs `typeorm` Repository, `prisma.client.<model>`, `@nestjs/typeorm`.'
7930
7930
  ---
7931
7931
 
7932
7932
  # Warlock — use a repository
@@ -8348,19 +8348,19 @@ The third arg is a context object — `{ allValues, dateFormat, ... }` — for f
8348
8348
 
8349
8349
  ## See also
8350
8350
 
8351
- - [`create-module/SKILL.md`](../create-module/SKILL.md) — `warlock generate.repository` and where the file lives.
8352
- - [`@warlock.js/cache/cache-basics/SKILL.md`](../../../cache/skills/cache-basics/SKILL.md) — the cache singleton behind `listCached` / `getCached`. See sibling skills (`pick-cache-driver`, `use-swr`) for related tasks.
8353
- - [`write-use-case/SKILL.md`](../write-use-case/SKILL.md) — calling repositories from a use-case handler.
8354
- - [`define-resource/SKILL.md`](../define-resource/SKILL.md) — mapping repository output to the wire format.
8355
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — services that consume repositories from the controller edge.
8356
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — controller → service → repository → model layering.
8351
+ - The `create-module` topic — `warlock generate.repository` and where the file lives.
8352
+ - The `cache-basics` topic of the `warlock-js-cache` skill — the cache singleton behind `listCached` / `getCached`. See sibling skills (`pick-cache-driver`, `use-swr`) for related tasks.
8353
+ - The `write-use-case` topic — calling repositories from a use-case handler.
8354
+ - The `define-resource` topic — mapping repository output to the wire format.
8355
+ - The `create-controller` topic — services that consume repositories from the controller edge.
8356
+ - The `warlock-conventions` topic — controller → service → repository → model layering.
8357
8357
 
8358
8358
 
8359
8359
  ## use-request-locals `@warlock.js/core/use-request-locals/SKILL.md`
8360
8360
 
8361
8361
  ---
8362
8362
  name: use-request-locals
8363
- description: 'Carry typed, server-only data through one HTTP request with `request.locals`, usually written by middleware and read by downstream middleware or controllers. Augment `RequestLocals` in the module that owns each key; v5 no longer permits arbitrary `request.foo` properties. Triggers: `request.locals`, `RequestLocals`, `request.post`, `request.organization`, `Property does not exist on type Request`, `Request index signature`; "attach data to a request", "share middleware data with a controller", "type request locals", "migrate dynamic request properties"; typical type augmentation `declare module "@warlock.js/core" { interface RequestLocals { ... } }`. Skip: computed-on-demand single-flight values and removed `fromRequest` — `@warlock.js/core/request-memo/SKILL.md`; middleware mechanics — `@warlock.js/core/write-middleware/SKILL.md`; authenticated user typing — augment `RequestUser`, not `RequestLocals`; competing patterns: `(request as any).foo`, `request.set()`, module-global mutable state.'
8363
+ description: 'Carry typed, server-only data through one HTTP request with `request.locals`, usually written by middleware and read by downstream middleware or controllers. Augment `RequestLocals` in the module that owns each key; v5 no longer permits arbitrary `request.foo` properties. Triggers: `request.locals`, `RequestLocals`, `request.post`, `request.organization`, `Property does not exist on type Request`, `Request index signature`; "attach data to a request", "share middleware data with a controller", "type request locals", "migrate dynamic request properties"; typical type augmentation `declare module "@warlock.js/core" { interface RequestLocals { ... } }`. Skip: computed-on-demand single-flight values and removed `fromRequest` — the `request-memo` topic; middleware mechanics — the `write-middleware` topic; authenticated user typing — augment `RequestUser`, not `RequestLocals`; competing patterns: `(request as any).foo`, `request.set()`, module-global mutable state.'
8364
8364
  ---
8365
8365
 
8366
8366
  # Warlock — use typed request locals
@@ -8522,16 +8522,16 @@ Writing `request.locals.organization` does not affect `request.all()`, `request.
8522
8522
 
8523
8523
  ## See also
8524
8524
 
8525
- - [`request-memo/SKILL.md`](../request-memo/SKILL.md) — the `fromRequest()` migration and single-flight request memoization.
8526
- - [`write-middleware/SKILL.md`](../write-middleware/SKILL.md) — middleware execution and registration.
8527
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — reading locals in downstream controllers.
8525
+ - The `request-memo` topic — the `fromRequest()` migration and single-flight request memoization.
8526
+ - The `write-middleware` topic — middleware execution and registration.
8527
+ - The `create-controller` topic — reading locals in downstream controllers.
8528
8528
 
8529
8529
 
8530
8530
  ## validate-input `@warlock.js/core/validate-input/SKILL.md`
8531
8531
 
8532
8532
  ---
8533
8533
  name: validate-input
8534
- description: '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 — `@warlock.js/seal/seal-basics/SKILL.md`; controller wiring — `@warlock.js/core/create-controller/SKILL.md`; file rules deep-dive — `@warlock.js/core/upload-file/SKILL.md`; competing libs `zod`, `joi`, `yup`, `class-validator`.'
8534
+ description: '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`.'
8535
8535
  ---
8536
8536
 
8537
8537
  # Warlock — validate a request
@@ -8604,7 +8604,7 @@ From `@warlock.js/seal` — **always import from seal directly**. `@warlock.js/c
8604
8604
  | `v.literal(...values)` | exact literals | — |
8605
8605
  | `v.instanceof(Ctor, msg?)` | `Ctor` instances | — |
8606
8606
  | `v.lazy(() => schema)` | recursive/forward refs | — |
8607
- | `v.file(msg?)` | `UploadedFile` | `.image()`, `.accept(exts)`, `.mimeType(types)`, `.minSize(n)`, `.maxSize(n)`, `.minWidth(n)`, `.maxWidth(n)` — see [`upload-file`](../upload-file/SKILL.md) |
8607
+ | `v.file(msg?)` | `UploadedFile` | `.image()`, `.accept(exts)`, `.mimeType(types)`, `.minSize(n)`, `.maxSize(n)`, `.minWidth(n)`, `.maxWidth(n)` — see the `upload-file` topic |
8608
8608
  | `v.computed(callback)` | derived value | — |
8609
8609
  | `v.managed(callback)` | framework-injected value | — |
8610
8610
 
@@ -8699,7 +8699,7 @@ const uploadAvatarSchema = v.object({
8699
8699
  });
8700
8700
  ```
8701
8701
 
8702
- Full file chain: `.image()`, `.accept(extensions)`, `.mimeType(types)`, `.pdf()`, `.excel()`, `.word()`, `.minSize(n)`, `.maxSize(n)`, `.minWidth(px)`, `.maxWidth(px)`, `.minHeight(px)`, `.maxHeight(px)`. See [`upload-file`](../upload-file/SKILL.md) for the full upload flow.
8702
+ Full file chain: `.image()`, `.accept(extensions)`, `.mimeType(types)`, `.pdf()`, `.excel()`, `.word()`, `.minSize(n)`, `.maxSize(n)`, `.minWidth(px)`, `.maxWidth(px)`, `.minHeight(px)`, `.maxHeight(px)`. See the `upload-file` topic for the full upload flow.
8703
8703
 
8704
8704
  ### Optional file field
8705
8705
 
@@ -8835,17 +8835,17 @@ updateProductController.validation = {
8835
8835
 
8836
8836
  ## See also
8837
8837
 
8838
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — how the schema attaches and how `request.validated()` reads.
8839
- - [`upload-file/SKILL.md`](../upload-file/SKILL.md) — `v.file()` and how it interacts with multipart uploads.
8840
- - [`send-response/SKILL.md`](../send-response/SKILL.md) — the 400 helper used internally by `failedSchema`.
8841
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — `Request<Schema>` type-alias convention.
8838
+ - The `create-controller` topic — how the schema attaches and how `request.validated()` reads.
8839
+ - The `upload-file` topic — `v.file()` and how it interacts with multipart uploads.
8840
+ - The `send-response` topic — the 400 helper used internally by `failedSchema`.
8841
+ - The `warlock-conventions` topic — `Request<Schema>` type-alias convention.
8842
8842
 
8843
8843
 
8844
8844
  ## warlock-conventions `@warlock.js/core/warlock-conventions/SKILL.md`
8845
8845
 
8846
8846
  ---
8847
8847
  name: warlock-conventions
8848
- description: '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 — `@warlock.js/core/create-module/SKILL.md`; route shape — `@warlock.js/core/register-route/SKILL.md`; controller shape — `@warlock.js/core/create-controller/SKILL.md`; competing patterns: `express` ad-hoc layouts, `@nestjs/common` decorator-driven structure.'
8848
+ description: '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.'
8849
8849
  ---
8850
8850
 
8851
8851
  # Warlock — framework-wide conventions
@@ -8969,24 +8969,24 @@ Within the same module, use relative paths (`./`, `../`).
8969
8969
 
8970
8970
  ## Type augmentations
8971
8971
 
8972
- `src/typings.d.ts` is the project's module-augmentation file. It ships with the scaffold, is listed explicitly in `tsconfig.json`'s `include`, and is where application-wide `declare module "@warlock.js/core"` blocks live — `RequestLocals` and `RequestUser` are already there as empty interfaces. Keep its trailing `export {}` (without it the block declares an ambient module and erases the framework's real typings) and keep the augmented declarations as `interface`, the one sanctioned exception to this project's prefer-`type` rule, since declaration merging is interface-only. See [`use-request-locals`](../use-request-locals/SKILL.md).
8972
+ `src/typings.d.ts` is the project's module-augmentation file. It ships with the scaffold, is listed explicitly in `tsconfig.json`'s `include`, and is where application-wide `declare module "@warlock.js/core"` blocks live — `RequestLocals` and `RequestUser` are already there as empty interfaces. Keep its trailing `export {}` (without it the block declares an ambient module and erases the framework's real typings) and keep the augmented declarations as `interface`, the one sanctioned exception to this project's prefer-`type` rule, since declaration merging is interface-only. See the `use-request-locals` topic.
8973
8973
 
8974
8974
  ## Decorators
8975
8975
 
8976
- Cascade uses `@RegisterModel()` for the model registry and `@BelongsTo` / `@HasMany` / `@MorphTo` for relations. The scaffolded `tsconfig.json` has `"experimentalDecorators": false` — these are native Stage-3 decorators, not legacy. Never set that flag to `true`; see [`lower-stage3-decorators`](../lower-stage3-decorators/SKILL.md).
8976
+ Cascade uses `@RegisterModel()` for the model registry and `@BelongsTo` / `@HasMany` / `@MorphTo` for relations. The scaffolded `tsconfig.json` has `"experimentalDecorators": false` — these are native Stage-3 decorators, not legacy. Never set that flag to `true`; see the `lower-stage3-decorators` topic.
8977
8977
 
8978
8978
  ## See also
8979
8979
 
8980
- - [`register-route/SKILL.md`](../register-route/SKILL.md) — how to wire URLs to controllers.
8981
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — controller signature, validation, response shape.
8982
- - [`send-response/SKILL.md`](../send-response/SKILL.md) — the full Response helper surface.
8980
+ - The `register-route` topic — how to wire URLs to controllers.
8981
+ - The `create-controller` topic — controller signature, validation, response shape.
8982
+ - The `send-response` topic — the full Response helper surface.
8983
8983
 
8984
8984
 
8985
8985
  ## warlock-doctor `@warlock.js/core/warlock-doctor/SKILL.md`
8986
8986
 
8987
8987
  ---
8988
8988
  name: warlock-doctor
8989
- description: '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`, `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 — `@warlock.js/core/health-checks/SKILL.md`; authoring a general CLI command — `@warlock.js/core/write-cli-command/SKILL.md`; releasing the package — `releasing-warlock-monorepo`; competing tools `npm doctor`, `nest info`, hand-rolled preflight scripts.'
8989
+ description: '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`, `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.'
8990
8990
  ---
8991
8991
 
8992
8992
  # Warlock — `warlock doctor`
@@ -9118,22 +9118,22 @@ A `⚠ routes: 0 routes registered` line is the tell that a route module threw o
9118
9118
 
9119
9119
  ## See also
9120
9120
 
9121
- - [`health-checks/SKILL.md`](../health-checks/SKILL.md) — the live `/health` + `/ready` HTTP probes and the `health` registry the `health` doctor check reports on.
9122
- - [`write-cli-command/SKILL.md`](../write-cli-command/SKILL.md) — authoring a command + its `preload` plan, the shape `doctorCommand` is built from.
9123
- - [`add-connector/SKILL.md`](../add-connector/SKILL.md) — connector registration, which the `connectors` check enumerates.
9124
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — project layout the checks assume.
9121
+ - The `health-checks` topic — the live `/health` + `/ready` HTTP probes and the `health` registry the `health` doctor check reports on.
9122
+ - The `write-cli-command` topic — authoring a command + its `preload` plan, the shape `doctorCommand` is built from.
9123
+ - The `add-connector` topic — connector registration, which the `connectors` check enumerates.
9124
+ - The `warlock-conventions` topic — project layout the checks assume.
9125
9125
 
9126
9126
 
9127
9127
  ## warlock-routes `@warlock.js/core/warlock-routes/SKILL.md`
9128
9128
 
9129
9129
  ---
9130
9130
  name: warlock-routes
9131
- description: '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 — `@warlock.js/core/warlock-doctor/SKILL.md`; defining/naming/grouping routes — `@warlock.js/core/register-route/SKILL.md`; authoring a general CLI command — `@warlock.js/core/write-cli-command/SKILL.md`; competing tools `nest`/`express` route listers, `php artisan route:list`.'
9131
+ description: '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`.'
9132
9132
  ---
9133
9133
 
9134
9134
  # Warlock — `warlock routes`
9135
9135
 
9136
- `warlock routes` lists every registered HTTP route as a table. It's the read-only sibling of [`warlock doctor`](../warlock-doctor/SKILL.md): it boots the app far enough to register route modules — but **starts no connectors**, so it never opens a database, cache, or socket connection.
9136
+ `warlock routes` lists every registered HTTP route as a table. It's the read-only sibling of the `warlock-doctor` topic: it boots the app far enough to register route modules — but **starts no connectors**, so it never opens a database, cache, or socket connection.
9137
9137
 
9138
9138
  ```bash
9139
9139
  npx warlock routes
@@ -9251,17 +9251,17 @@ and exits non-zero.
9251
9251
 
9252
9252
  ## See also
9253
9253
 
9254
- - [`warlock-doctor/SKILL.md`](../warlock-doctor/SKILL.md) — the read-only diagnostics sibling; its `routes` check warns when this table would be empty.
9255
- - [`register-route/SKILL.md`](../register-route/SKILL.md) — defining, naming, and grouping the routes this command lists.
9256
- - [`run-app/SKILL.md`](../run-app/SKILL.md) — `warlock build`, the command that produces the snapshot `routes:diff` compares against.
9257
- - [`write-cli-command/SKILL.md`](../write-cli-command/SKILL.md) — the command + `preload` shape `routesCommand` is built from.
9254
+ - The `warlock-doctor` topic — the read-only diagnostics sibling; its `routes` check warns when this table would be empty.
9255
+ - The `register-route` topic — defining, naming, and grouping the routes this command lists.
9256
+ - The `run-app` topic — `warlock build`, the command that produces the snapshot `routes:diff` compares against.
9257
+ - The `write-cli-command` topic — the command + `preload` shape `routesCommand` is built from.
9258
9258
 
9259
9259
 
9260
9260
  ## wire-socket `@warlock.js/core/wire-socket/SKILL.md`
9261
9261
 
9262
9262
  ---
9263
9263
  name: wire-socket
9264
- description: '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 — `@warlock.js/core/add-connector/SKILL.md`; app context accessors — `@warlock.js/core/use-app-context/SKILL.md`; competing libs `ws`, `socket.io` direct without Warlock connector, `uWebSockets.js`.'
9264
+ description: '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`.'
9265
9265
  ---
9266
9266
 
9267
9267
  # Warlock — wire a Socket.IO server
@@ -9544,16 +9544,16 @@ CLI commands, scheduled jobs, queue workers — anything running outside an HTTP
9544
9544
 
9545
9545
  ## See also
9546
9546
 
9547
- - [`use-app-context/SKILL.md`](../use-app-context/SKILL.md) — `app.socket` runtime accessor + `Application` static metadata.
9548
- - [`add-connector/SKILL.md`](../add-connector/SKILL.md) — how connectors order their boot sequence around your modules.
9549
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — where socket-related code lives (a setup function for handler registration, `services/` for emit sites).
9547
+ - The `use-app-context` topic — `app.socket` runtime accessor + `Application` static metadata.
9548
+ - The `add-connector` topic — how connectors order their boot sequence around your modules.
9549
+ - The `warlock-conventions` topic — where socket-related code lives (a setup function for handler registration, `services/` for emit sites).
9550
9550
 
9551
9551
 
9552
9552
  ## write-cli-command `@warlock.js/core/write-cli-command/SKILL.md`
9553
9553
 
9554
9554
  ---
9555
9555
  name: write-cli-command
9556
- 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 — `@warlock.js/core/run-app/SKILL.md`; warlock.config.ts wiring — `@warlock.js/core/configure-app/SKILL.md`; competing libs `commander`, `yargs`, `oclif`.'
9556
+ 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`.'
9557
9557
  ---
9558
9558
 
9559
9559
  # Warlock — write a CLI command
@@ -9703,7 +9703,7 @@ console.log(colors.green("✓") + " user promoted");
9703
9703
 
9704
9704
  The framework ships a fixed set of commands you call but don't author. Knowing their flags up front saves a `--help` round-trip.
9705
9705
 
9706
- > For `warlock dev` / `warlock build` / `warlock start` — see [`run-app/SKILL.md`](../run-app/SKILL.md). Everything below covers the rest.
9706
+ > For `warlock dev` / `warlock build` / `warlock start` — see the `run-app` topic. Everything below covers the rest.
9707
9707
 
9708
9708
  ### Database + migrations
9709
9709
 
@@ -9749,7 +9749,7 @@ warlock generate.typings
9749
9749
  warlock generate (alias: g) — interactive picker
9750
9750
  ```
9751
9751
 
9752
- `<m>/<n>` is `<module>/<name>` (e.g. `products/create-product`). See [`create-module/SKILL.md`](../create-module/SKILL.md) for the full scaffolding flow.
9752
+ `<m>/<n>` is `<module>/<name>` (e.g. `products/create-product`). See the `create-module` topic for the full scaffolding flow.
9753
9753
 
9754
9754
  ### Feature installation
9755
9755
 
@@ -9864,16 +9864,16 @@ export default command({
9864
9864
 
9865
9865
  ## See also
9866
9866
 
9867
- - [`run-app/SKILL.md`](../run-app/SKILL.md) — the operational commands (`dev` / `build` / `start`) with all flags and config knobs.
9868
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — wiring `defineConfig` and `warlock.config.ts`.
9869
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — module layout, canonical imports.
9867
+ - The `run-app` topic — the operational commands (`dev` / `build` / `start`) with all flags and config knobs.
9868
+ - The `configure-app` topic — wiring `defineConfig` and `warlock.config.ts`.
9869
+ - The `warlock-conventions` topic — module layout, canonical imports.
9870
9870
 
9871
9871
 
9872
9872
  ## write-middleware `@warlock.js/core/write-middleware/SKILL.md`
9873
9873
 
9874
9874
  ---
9875
9875
  name: write-middleware
9876
- description: '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 — `@warlock.js/core/use-middleware/SKILL.md`; route attachment — `@warlock.js/core/register-route/SKILL.md`; response helpers — `@warlock.js/core/send-response/SKILL.md`; competing patterns: `express` `(req, res, next)` middleware, Fastify `preHandler` hooks.'
9876
+ description: '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.'
9877
9877
  ---
9878
9878
 
9879
9879
  # Warlock — write a middleware
@@ -9924,7 +9924,7 @@ export const requireFeatureFlag: Middleware = async ({ request, response }) => {
9924
9924
  };
9925
9925
  ```
9926
9926
 
9927
- If you short-circuit, the controller never runs. The response helper you pick (`forbidden`, `unauthorized`, `badRequest`, etc.) sets the status — see [`send-response`](../send-response/SKILL.md).
9927
+ If you short-circuit, the controller never runs. The response helper you pick (`forbidden`, `unauthorized`, `badRequest`, etc.) sets the status — see the `send-response` topic.
9928
9928
 
9929
9929
  ## Enriching the request
9930
9930
 
@@ -10073,7 +10073,7 @@ export const optionalAuth: Middleware = async ({ request, response }) => {
10073
10073
 
10074
10074
  ## Using built-in middleware
10075
10075
 
10076
- `@warlock.js/core` ships seven built-in middlewares (rate limit, concurrency cap, body cap, idempotency, maintenance, IP filter, response cache) under the `middleware` namespace from `@warlock.js/core`. They cover the patterns most apps need — see [`use-middleware`](../use-middleware/SKILL.md) for the catalog, per-primitive deep-dives, error semantics, and gotchas specific to using them.
10076
+ `@warlock.js/core` ships seven built-in middlewares (rate limit, concurrency cap, body cap, idempotency, maintenance, IP filter, response cache) under the `middleware` namespace from `@warlock.js/core`. They cover the patterns most apps need — see the `use-middleware` topic for the catalog, per-primitive deep-dives, error semantics, and gotchas specific to using them.
10077
10077
 
10078
10078
  `X-Request-Id` correlation is wired automatically (inherit + echo on every response) — the same skill covers that too. It's not a middleware; nothing to register.
10079
10079
 
@@ -10090,18 +10090,18 @@ export const optionalAuth: Middleware = async ({ request, response }) => {
10090
10090
 
10091
10091
  ## See also
10092
10092
 
10093
- - [`use-middleware/SKILL.md`](../use-middleware/SKILL.md) — the built-in middleware catalog (`rateLimit`, `idempotency`, `maxBodySize`, etc.) + request-id correlation.
10094
- - [`register-route/SKILL.md`](../register-route/SKILL.md) — where middleware attaches: `router.group` and route-options.
10095
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — how the controller picks up `request.locals.user`, `request.validated()`, etc., set by upstream middleware.
10096
- - [`send-response/SKILL.md`](../send-response/SKILL.md) — the response helpers used to short-circuit.
10097
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — the `guarded()` / `guardedAdmin()` / `publicRoutes()` convention.
10093
+ - The `use-middleware` topic — the built-in middleware catalog (`rateLimit`, `idempotency`, `maxBodySize`, etc.) + request-id correlation.
10094
+ - The `register-route` topic — where middleware attaches: `router.group` and route-options.
10095
+ - The `create-controller` topic — how the controller picks up `request.locals.user`, `request.validated()`, etc., set by upstream middleware.
10096
+ - The `send-response` topic — the response helpers used to short-circuit.
10097
+ - The `warlock-conventions` topic — the `guarded()` / `guardedAdmin()` / `publicRoutes()` convention.
10098
10098
 
10099
10099
 
10100
10100
  ## write-seeder `@warlock.js/core/write-seeder/SKILL.md`
10101
10101
 
10102
10102
  ---
10103
10103
  name: write-seeder
10104
- description: '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 — `@warlock.js/core/create-module/SKILL.md`; repository CRUD — `@warlock.js/core/use-repository/SKILL.md`; CLI flags — `@warlock.js/core/write-cli-command/SKILL.md`; competing patterns: hand-rolled `node scripts/seed.js`, `typeorm-seeding`.'
10104
+ description: '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`.'
10105
10105
  ---
10106
10106
 
10107
10107
  # Warlock — write a seeder
@@ -10472,17 +10472,17 @@ warlock seed --transaction # (default true) — pass --transaction=false to s
10472
10472
 
10473
10473
  ## See also
10474
10474
 
10475
- - [`create-module/SKILL.md`](../create-module/SKILL.md) — the `seeds/` folder layout in a module, `generate.module` scaffolding.
10476
- - [`use-repository/SKILL.md`](../use-repository/SKILL.md) — the model `create` / `first` calls used inside seeds.
10477
- - [`write-cli-command/SKILL.md`](../write-cli-command/SKILL.md) — the `warlock seed` CLI flags and full preload shape.
10478
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — `seeds/` is the canonical folder name (singular `seed/` is incorrect).
10475
+ - The `create-module` topic — the `seeds/` folder layout in a module, `generate.module` scaffolding.
10476
+ - The `use-repository` topic — the model `create` / `first` calls used inside seeds.
10477
+ - The `write-cli-command` topic — the `warlock seed` CLI flags and full preload shape.
10478
+ - The `warlock-conventions` topic — `seeds/` is the canonical folder name (singular `seed/` is incorrect).
10479
10479
 
10480
10480
 
10481
10481
  ## write-use-case `@warlock.js/core/write-use-case/SKILL.md`
10482
10482
 
10483
10483
  ---
10484
10484
  name: write-use-case
10485
- description: '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 — `@warlock.js/core/create-controller/SKILL.md`; schema details — `@warlock.js/core/validate-input/SKILL.md`; the standalone retry util — `@warlock.js/core/retry-operation/SKILL.md`; competing libs `@nestjs/cqrs`, `inversify`, hand-rolled service classes.'
10485
+ description: '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.'
10486
10486
  ---
10487
10487
 
10488
10488
  # Warlock — write a use case
@@ -10772,8 +10772,8 @@ For the plain-function flavour (`warlock generate.use-case <module>/<verb-noun>`
10772
10772
 
10773
10773
  ## See also
10774
10774
 
10775
- - [`@warlock.js/core/create-controller/SKILL.md`](@warlock.js/core/create-controller/SKILL.md) — the usual next step: invoke this use case from an HTTP controller.
10776
- - [`@warlock.js/herald/publish-message/SKILL.md`](@warlock.js/herald/publish-message/SKILL.md) — the message bus the `broadcast` adapter publishes to (cross-package).
10775
+ - The `create-controller` topic — the usual next step: invoke this use case from an HTTP controller.
10776
+ - The `publish-message` topic of the `warlock-js-herald` skill — the message bus the `broadcast` adapter publishes to (cross-package).
10777
10777
 
10778
10778
 
10779
10779