@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
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: create-controller
3
- 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.'
3
+ 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.'
4
4
  ---
5
5
 
6
6
  # Warlock — create a controller
@@ -48,7 +48,7 @@ Prefer `request.validated()` once a schema is attached — it's typed.
48
48
  | Content type | Parsed by | Notes |
49
49
  | ------------------------------------ | -------------------------------- | --------------------------------------------------------------------- |
50
50
  | `application/json` | Fastify (built-in) | objects/arrays parsed as-is |
51
- | `multipart/form-data` | `@fastify/multipart` | fields + files; see [`upload-file`](../upload-file/SKILL.md) |
51
+ | `multipart/form-data` | `@fastify/multipart` | fields + files; see the `upload-file` topic |
52
52
  | `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) |
53
53
 
54
54
  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.
@@ -57,7 +57,7 @@ All three content types are held to the same `http.bodyLimit` — an over-limit
57
57
 
58
58
  ## Returning output
59
59
 
60
- Pick the helper that matches the outcome. Full surface in [send-response](../send-response/SKILL.md). Quick map:
60
+ Pick the helper that matches the outcome. Full surface in the `send-response` topic. Quick map:
61
61
 
62
62
  | Helper | Status | Use when |
63
63
  | ---------------------------------- | ------ | ----------------------------- |
@@ -134,7 +134,7 @@ Use `RequestHandler<Request<TSchema>>` for public routes, `GuardedRequestHandler
134
134
  createProductController.description = "Create a new product (admin only)";
135
135
  ```
136
136
 
137
- `description` feeds the OpenAPI document (`warlock generate.openapi`, see [`generate-openapi`](../generate-openapi/SKILL.md)) and surfaces in dev-server logs.
137
+ `description` feeds the OpenAPI document (`warlock generate.openapi`, see the `generate-openapi` topic) and surfaces in dev-server logs.
138
138
 
139
139
  ## Declaring response types with `responseSchema`
140
140
 
@@ -262,7 +262,7 @@ export const getProductController: RequestHandler = async ({ request, response }
262
262
  };
263
263
  ```
264
264
 
265
- 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`.
265
+ 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`.
266
266
 
267
267
  ## Gotchas
268
268
 
@@ -273,6 +273,6 @@ No `if (!product)` branch in the controller. The error class carries the HTTP se
273
273
 
274
274
  ## See also
275
275
 
276
- - [`register-route/SKILL.md`](../register-route/SKILL.md) — wiring a controller to a URL.
277
- - [`send-response/SKILL.md`](../send-response/SKILL.md) — full Response helper surface.
278
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — controller-service-repository layering.
276
+ - The `register-route` topic — wiring a controller to a URL.
277
+ - The `send-response` topic — full Response helper surface.
278
+ - The `warlock-conventions` topic — controller-service-repository layering.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: create-module
3
- 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.'
3
+ 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.'
4
4
  ---
5
5
 
6
6
  # Warlock — create a module
@@ -180,11 +180,11 @@ await warmupProductCache();
180
180
 
181
181
  ## See also
182
182
 
183
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — module layout, file suffixes, path aliases.
184
- - [`register-route/SKILL.md`](../register-route/SKILL.md) — what to put in `routes.ts`.
185
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — the handler shape, attaching validation.
186
- - [`use-repository/SKILL.md`](../use-repository/SKILL.md) — what the generated `<module>.repository.ts` looks like and how to extend it.
187
- - [`define-resource/SKILL.md`](../define-resource/SKILL.md) — the output mapper for the model.
188
- - [`validate-input/SKILL.md`](../validate-input/SKILL.md) — wiring `schema/*.schema.ts` to controllers.
189
- - [`write-seeder/SKILL.md`](../write-seeder/SKILL.md) — populating the generated `seeds/` folder.
190
- - [`use-localization/SKILL.md`](../use-localization/SKILL.md) — populating the generated `utils/locales.ts`.
183
+ - The `warlock-conventions` topic — module layout, file suffixes, path aliases.
184
+ - The `register-route` topic — what to put in `routes.ts`.
185
+ - The `create-controller` topic — the handler shape, attaching validation.
186
+ - The `use-repository` topic — what the generated `<module>.repository.ts` looks like and how to extend it.
187
+ - The `define-resource` topic — the output mapper for the model.
188
+ - The `validate-input` topic — wiring `schema/*.schema.ts` to controllers.
189
+ - The `write-seeder` topic — populating the generated `seeds/` folder.
190
+ - The `use-localization` topic — populating the generated `utils/locales.ts`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: define-resource
3
- 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.'
3
+ 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.'
4
4
  ---
5
5
 
6
6
  # Warlock — define a resource
@@ -395,6 +395,6 @@ export const CommentResource = defineResource({
395
395
 
396
396
  ## See also
397
397
 
398
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — the output-only rule and where logic actually lives.
399
- - [`build-restful/SKILL.md`](../build-restful/SKILL.md) — wiring a resource into a CRUD pipeline.
400
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — calling `new XResource(record).toJSON()` from a controller.
398
+ - The `warlock-conventions` topic — the output-only rule and where logic actually lives.
399
+ - The `build-restful` topic — wiring a resource into a CRUD pipeline.
400
+ - The `create-controller` topic — calling `new XResource(record).toJSON()` from a controller.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: encrypt-data
3
- 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`.'
3
+ 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`.'
4
4
  ---
5
5
 
6
6
  # Warlock — encrypt and fingerprint
@@ -12,7 +12,7 @@ Two helpers, two different jobs:
12
12
  | `encrypt` / `decrypt` | reversible | AES-256-GCM | Secrets you need to read back (API keys, OAuth tokens). |
13
13
  | `hmacHash` | one-way | HMAC-SHA256 | Deterministic fingerprint for lookup/dedup of encrypted values. |
14
14
 
15
- 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.
15
+ 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.
16
16
 
17
17
  ## The shape
18
18
 
@@ -124,7 +124,7 @@ await idempotencyKeysRepository.first({ hash: hmacHash(JSON.stringify(request))
124
124
  log.info("api-key", "used", { fingerprint: hmacHash(apiKey).slice(0, 8) });
125
125
  ```
126
126
 
127
- **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)).
127
+ **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).
128
128
 
129
129
  ## Common patterns
130
130
 
@@ -199,10 +199,10 @@ Same payload → same key → returns the cached result. Different keys (because
199
199
 
200
200
  `encrypt` / `decrypt` / `hmacHash` use Node's built-in `crypto` — no extra dependency. They're available out of the box.
201
201
 
202
- Password hashing (`hashPassword`) needs `bcryptjs`, which is its own install — see [`hash-password/SKILL.md`](../hash-password/SKILL.md).
202
+ Password hashing (`hashPassword`) needs `bcryptjs`, which is its own install — see the `hash-password` topic.
203
203
 
204
204
  ## See also
205
205
 
206
- - [`hash-password/SKILL.md`](../hash-password/SKILL.md) — bcrypt password hashing (the third member of the encryption module, but a different job entirely).
207
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — `src/config/encryption.ts` and env wiring.
208
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — service layering, where the encryption boundary should sit.
206
+ - The `hash-password` topic — bcrypt password hashing (the third member of the encryption module, but a different job entirely).
207
+ - The `configure-app` topic — `src/config/encryption.ts` and env wiring.
208
+ - The `warlock-conventions` topic — service layering, where the encryption boundary should sit.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: generate-openapi
3
- 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`.'
3
+ 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.'
4
4
  ---
5
5
 
6
6
  # Warlock — generate an OpenAPI document
@@ -44,7 +44,7 @@ npx warlock generate.openapi --out docs/openapi.json --title "Shop API" --server
44
44
 
45
45
  ### Responses from `responseSchema`
46
46
 
47
- 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.
47
+ 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.
48
48
 
49
49
  ```ts
50
50
  loginController.responseSchema = {
@@ -143,6 +143,6 @@ const { document, warnings } = buildOpenApiDocument(router.list(), {
143
143
 
144
144
  ## See also
145
145
 
146
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — `validation`, `description` and `responseSchema` on a handler.
147
- - [`define-resource/SKILL.md`](../define-resource/SKILL.md) — resources, which become `components.schemas`.
148
- - [`warlock-routes/SKILL.md`](../warlock-routes/SKILL.md) — list the routes the document is built from.
146
+ - The `create-controller` topic — `validation`, `description` and `responseSchema` on a handler.
147
+ - The `define-resource` topic — resources, which become `components.schemas`.
148
+ - The `warlock-routes` topic — list the routes the document is built from.
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: hash-password
3
- 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`.'
3
+ 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`.'
4
4
  ---
5
5
 
6
6
  # Warlock — hash a password
7
7
 
8
8
  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."
9
9
 
10
- If your value needs to be read back later → [`encrypt-data/SKILL.md`](../encrypt-data/SKILL.md).
10
+ If your value needs to be read back later → the `encrypt-data` topic.
11
11
  If you need a fast searchable fingerprint of an encrypted value → also `encrypt-data` (`hmacHash`).
12
12
 
13
13
  ## The shape
@@ -168,7 +168,7 @@ There is no `warlock add` feature for password hashing — `bcryptjs` is a plain
168
168
 
169
169
  ## Gotchas
170
170
 
171
- - **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)).
171
+ - **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).
172
172
  - **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.
173
173
  - **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.
174
174
  - **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.
@@ -176,8 +176,8 @@ There is no `warlock add` feature for password hashing — `bcryptjs` is a plain
176
176
 
177
177
  ## See also
178
178
 
179
- - [`use-model-transformers/SKILL.md`](../use-model-transformers/SKILL.md) — `useHashedPassword` + the two other model transformers (`useComputedSlug`, `useComputedModel`).
180
- - [`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).
181
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — `src/config/encryption.ts` and the `password.salt` knob.
182
- - [`define-resource/SKILL.md`](../define-resource/SKILL.md) — where to filter the password field out of API responses.
183
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — service layering, where the password boundary should sit.
179
+ - The `use-model-transformers` topic — `useHashedPassword` + the two other model transformers (`useComputedSlug`, `useComputedModel`).
180
+ - The `encrypt-data` topic — reversible AES-256-GCM `encrypt`/`decrypt` + one-way HMAC `hmacHash` (the other two members of the encryption module).
181
+ - The `configure-app` topic — `src/config/encryption.ts` and the `password.salt` knob.
182
+ - The `define-resource` topic — where to filter the password field out of API responses.
183
+ - The `warlock-conventions` topic — service layering, where the password boundary should sit.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: health-checks
3
- 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.'
3
+ 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.'
4
4
  ---
5
5
 
6
6
  # Warlock — health checks & graceful shutdown
@@ -109,11 +109,11 @@ For an even smoother handoff, give the load balancer time to observe the 503 bef
109
109
  - **`/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`.
110
110
  - **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.
111
111
  - **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`.
112
- - **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`.
112
+ - **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.
113
113
 
114
114
  ## See also
115
115
 
116
- - [`use-app-context/SKILL.md`](../use-app-context/SKILL.md) — `Application.onShutdown` / `onceBooted` / `isShuttingDown`, the lifecycle hooks the endpoints build on.
117
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — the `src/config/http.ts` shape.
118
- - [`add-connector/SKILL.md`](../add-connector/SKILL.md) — connector boot/shutdown order, where draining slots in.
119
- - [`use-middleware/SKILL.md`](../use-middleware/SKILL.md) — maintenance mode and other built-in middleware.
116
+ - The `use-app-context` topic — `Application.onShutdown` / `onceBooted` / `isShuttingDown`, the lifecycle hooks the endpoints build on.
117
+ - The `configure-app` topic — the `src/config/http.ts` shape.
118
+ - The `add-connector` topic — connector boot/shutdown order, where draining slots in.
119
+ - The `use-middleware` topic — maintenance mode and other built-in middleware.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: lower-stage3-decorators
3
- 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`.'
3
+ 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.'
4
4
  ---
5
5
 
6
6
  # Lower Stage-3 decorators for Vite/Vitest
@@ -51,6 +51,6 @@ It's the **same esbuild transform the production `tsx`/build path uses**, so a f
51
51
 
52
52
  ## See also
53
53
 
54
- - [`@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.
55
- - [`@warlock.js/core/test-http/SKILL.md`](@warlock.js/core/test-http/SKILL.md) — integration tests over the real HTTP server.
56
- - [`@warlock.js/cascade/write-migration/SKILL.md`](@warlock.js/cascade/write-migration/SKILL.md) — the decorated models this plugin lets you load.
54
+ - The `test-service` topic — `setupTest()` bootstrap for unit-testing services/models once the harness loads.
55
+ - The `test-http` topic — integration tests over the real HTTP server.
56
+ - The `write-migration` topic of the `warlock-js-cascade` skill — the decorated models this plugin lets you load.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: process-image
3
- 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`.'
3
+ 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`.'
4
4
  ---
5
5
 
6
6
  # Warlock — process an image
@@ -153,7 +153,7 @@ The wrapper exposes the underlying `sharp.Sharp` via `.image` for anything not s
153
153
 
154
154
  `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.
155
155
 
156
- 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.
156
+ See the `upload-file` topic for the multipart entry point and the `store-file` topic for persisting the output through storage drivers.
157
157
 
158
158
  ## Gotchas
159
159
 
@@ -166,6 +166,6 @@ See [`upload-file`](../upload-file/SKILL.md) for the multipart entry point and [
166
166
 
167
167
  ## See also
168
168
 
169
- - [`upload-file/SKILL.md`](../upload-file/SKILL.md) — multipart uploads, where image transforms typically start.
170
- - [`store-file/SKILL.md`](../store-file/SKILL.md) — persisting image output through storage drivers (local / S3 / R2 / Spaces).
171
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — where image-processing services live.
169
+ - The `upload-file` topic — multipart uploads, where image transforms typically start.
170
+ - The `store-file` topic — persisting image output through storage drivers (local / S3 / R2 / Spaces).
171
+ - The `warlock-conventions` topic — where image-processing services live.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: register-route
3
- 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`.'
3
+ 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`.'
4
4
  ---
5
5
 
6
6
  # Warlock — register a route
@@ -29,7 +29,7 @@ router.post("/orders/:id", createOrderController, { name: "orders.create" });
29
29
  Use a concrete HTTP verb for browser forms. `router.any()` is represented as
30
30
  `all` metadata and requires callers to choose an explicit path and method.
31
31
 
32
- 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)).
32
+ 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).
33
33
 
34
34
  ## HTTP verbs
35
35
 
@@ -98,7 +98,7 @@ router.prefix("/products", () => {
98
98
  });
99
99
  ```
100
100
 
101
- If `guarded` doesn't exist in your project yet, see the shared utility convention in [warlock-conventions](../warlock-conventions/SKILL.md).
101
+ If `guarded` doesn't exist in your project yet, see the shared utility convention in the `warlock-conventions` topic.
102
102
 
103
103
  ## RESTful resource chain
104
104
 
@@ -167,6 +167,6 @@ router.get(["/health", "/healthz"], healthController);
167
167
 
168
168
  ## See also
169
169
 
170
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — the handler shape, validation, request/response surface.
171
- - [`send-response/SKILL.md`](../send-response/SKILL.md) — picking the right `response.<helper>()`.
172
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — module layout, the `guarded` convention, path aliases.
170
+ - The `create-controller` topic — the handler shape, validation, request/response surface.
171
+ - The `send-response` topic — picking the right `response.<helper>()`.
172
+ - The `warlock-conventions` topic — module layout, the `guarded` convention, path aliases.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: request-memo
3
- 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()`.'
3
+ 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()`.'
4
4
  ---
5
5
 
6
6
  # Warlock — memoize work for one request
@@ -137,6 +137,6 @@ Reusing one key for different result types is a caller bug: TypeScript cannot co
137
137
 
138
138
  ## See also
139
139
 
140
- - [`use-request-locals/SKILL.md`](../use-request-locals/SKILL.md) — typed per-request data written by middleware.
141
- - [`write-middleware/SKILL.md`](../write-middleware/SKILL.md) — authoring and registering middleware.
142
- - [`create-controller/SKILL.md`](../create-controller/SKILL.md) — consuming request-scoped values from controllers.
140
+ - The `use-request-locals` topic — typed per-request data written by middleware.
141
+ - The `write-middleware` topic — authoring and registering middleware.
142
+ - The `create-controller` topic — consuming request-scoped values from controllers.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: request-tracing
3
- 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.'
3
+ 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.'
4
4
  ---
5
5
 
6
6
  # Warlock — request tracing hooks
@@ -131,7 +131,7 @@ instead of adding a separate hook API. They add three phases:
131
131
  trace keeps the same id through Warlock.
132
132
  2. Otherwise `traceId` falls back to `request.id` (the framework's own
133
133
  per-request correlation id — see
134
- [`use-middleware/SKILL.md`](../use-middleware/SKILL.md#request-id-correlation)
134
+ the `use-middleware` topic (its "Request ID correlation" section)
135
135
  for how that id is generated/inherited/echoed).
136
136
 
137
137
  `ctx.requestId` is always `request.id`, regardless of which branch produced
@@ -152,7 +152,7 @@ deriveTraceId(undefined, "req-abc123");
152
152
 
153
153
  Tracing does **not** add a response header of its own. `core` already echoes
154
154
  `request.id` back as `X-Request-Id` on every response (see
155
- [`use-middleware/SKILL.md`](../use-middleware/SKILL.md#request-id-correlation)) —
155
+ the `use-middleware` topic, "Request ID correlation" section) —
156
156
  apps correlate through that existing header. When a valid inbound
157
157
  `traceparent` is present, `ctx.traceId` inside your hooks carries that trace
158
158
  id even though the response header still reflects `request.id`; if you need
@@ -226,6 +226,6 @@ export default {
226
226
 
227
227
  ## See also
228
228
 
229
- - [`use-middleware/SKILL.md`](../use-middleware/SKILL.md#request-id-correlation) — `X-Request-Id` inheritance/echo, the header tracing correlates through.
230
- - [`use-app-context/SKILL.md`](../use-app-context/SKILL.md) — request-scoped ALS context (`request`/`response`) tracing hooks run inside.
231
- - [`@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.
229
+ - The `use-middleware` topic ("Request ID correlation" section) — `X-Request-Id` inheritance/echo, the header tracing correlates through.
230
+ - The `use-app-context` topic — request-scoped ALS context (`request`/`response`) tracing hooks run inside.
231
+ - 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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: resolve-path
3
- 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.'
3
+ 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.'
4
4
  ---
5
5
 
6
6
  # Warlock — resolve a path
@@ -163,8 +163,8 @@ If you find yourself reaching for `path.join(process.cwd(), ...)` — that's a s
163
163
 
164
164
  ## See also
165
165
 
166
- - [`build-url/SKILL.md`](../build-url/SKILL.md) — HTTP URL helpers (`url`, `uploadsUrl`, `publicUrl`, `assetsUrl`) — companion for rendering URLs (not filesystem paths).
167
- - [`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).
168
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — `src/config/uploads.ts` for the `root` override.
169
- - [`store-file/SKILL.md`](../store-file/SKILL.md) — the storage abstraction (most uploads code goes through `storage.put(...)`, not raw paths).
170
- - [`upload-file/SKILL.md`](../upload-file/SKILL.md) — the `UploadedFile` shape that pairs with `uploadsPath`.
166
+ - The `build-url` topic — HTTP URL helpers (`url`, `uploadsUrl`, `publicUrl`, `assetsUrl`) — companion for rendering URLs (not filesystem paths).
167
+ - The `use-app-context` topic — `Application` static (env, runtime strategy, version, uptime) + `app` runtime accessor (Fastify, socket.io, router, database).
168
+ - The `configure-app` topic — `src/config/uploads.ts` for the `root` override.
169
+ - The `store-file` topic — the storage abstraction (most uploads code goes through `storage.put(...)`, not raw paths).
170
+ - The `upload-file` topic — the `UploadedFile` shape that pairs with `uploadsPath`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: retry-operation
3
- 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`.'
3
+ 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`.'
4
4
  ---
5
5
 
6
6
  # Warlock — retry an operation
@@ -93,7 +93,7 @@ const result = await measure("publish-event", () =>
93
93
 
94
94
  ## Use-case integration
95
95
 
96
- `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.
96
+ `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.
97
97
 
98
98
  ## Gotchas
99
99
 
@@ -105,5 +105,5 @@ const result = await measure("publish-event", () =>
105
105
  ## See also
106
106
 
107
107
  - [`mongez-reinforcements-async`](mongez-reinforcements-async) — full `retry` / `retryable` reference.
108
- - [`@warlock.js/core/benchmark-code/SKILL.md`](@warlock.js/core/benchmark-code/SKILL.md) — timing retried operations.
109
- - [`@warlock.js/core/write-use-case/SKILL.md`](@warlock.js/core/write-use-case/SKILL.md) — the use-case `retry` option.
108
+ - The `benchmark-code` topic — timing retried operations.
109
+ - The `write-use-case` topic — the use-case `retry` option.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: run-app
3
- 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.'
3
+ 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.'
4
4
  ---
5
5
 
6
6
  # Warlock — run the app
@@ -46,7 +46,7 @@ Once the server is ready, `warlock dev` listens for single keypresses:
46
46
  | `c` | Clear the console. |
47
47
  | `q` | Graceful shutdown, exit `0` — the same path as `Ctrl+C`. |
48
48
  | `h` | Print the shortcuts that are armed right now. |
49
- | `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). |
49
+ | `u` | **Only while an update notice is showing** — update every `@warlock.js/*` package, install, and restart. See the `update-packages` topic. |
50
50
  | `Ctrl+C` | Graceful shutdown, unchanged. |
51
51
 
52
52
  A "restart" is a fresh process (`r` and `u` both use it) — see [The supervisor](#the-supervisor) below for how that works.
@@ -152,7 +152,7 @@ export default defineConfig({
152
152
  - **`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.
153
153
  - **`healthCheckers`** — custom file health checker contracts (or `false` to disable). The `--skip-health` flag is the per-run version.
154
154
  - **`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.
155
- - **`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).
155
+ - **`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.
156
156
  - **`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.
157
157
  - **`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.
158
158
 
@@ -219,7 +219,7 @@ It exists so `warlock start` can tell a promoted build from a directory that mer
219
219
 
220
220
  `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.
221
221
 
222
- 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).
222
+ 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).
223
223
 
224
224
  ## `warlock start` — run the production bundle
225
225
 
@@ -396,7 +396,7 @@ If you need the same fact inside the app, use `Application.onceBooted()` / `Appl
396
396
 
397
397
  `*` — `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.
398
398
 
399
- 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).
399
+ 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.
400
400
 
401
401
  ## Common patterns
402
402
 
@@ -507,9 +507,9 @@ NODE_OPTIONS=--max-old-space-size=4096 npx warlock start
507
507
 
508
508
  ## See also
509
509
 
510
- - [`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).
511
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — `warlock.config.ts` shape and `defineConfig`.
512
- - [`use-app-context/SKILL.md`](../use-app-context/SKILL.md) — `Application.environment` vs `Application.runtimeStrategy`.
513
- - [`add-connector/SKILL.md`](../add-connector/SKILL.md) — Early vs Late connector phases (why HTTP/socket boot late in dev).
514
- - [`update-packages/SKILL.md`](../update-packages/SKILL.md) — `warlock update` + the dev-server update notice (`devServer.checkForUpdates`).
515
- - [`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.
510
+ - 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).
511
+ - The `configure-app` topic — `warlock.config.ts` shape and `defineConfig`.
512
+ - The `use-app-context` topic — `Application.environment` vs `Application.runtimeStrategy`.
513
+ - The `add-connector` topic — Early vs Late connector phases (why HTTP/socket boot late in dev).
514
+ - The `update-packages` topic — `warlock update` + the dev-server update notice (`devServer.checkForUpdates`).
515
+ - The `warlock-routes` topic — `warlock routes` / `warlock routes:diff`, the latter comparing live page routes against the manifest `warlock build` writes.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: send-mail
3
- 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`.'
3
+ 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`.'
4
4
  ---
5
5
 
6
6
  # Warlock — send a mail
@@ -398,5 +398,5 @@ await Mail.to(user.email)
398
398
 
399
399
  ## See also
400
400
 
401
- - [`configure-app/SKILL.md`](../configure-app/SKILL.md) — `src/config/mail.ts` shape and `env()` patterns.
402
- - [`warlock-conventions/SKILL.md`](../warlock-conventions/SKILL.md) — where mail-sending services live.
401
+ - The `configure-app` topic — `src/config/mail.ts` shape and `env()` patterns.
402
+ - The `warlock-conventions` topic — where mail-sending services live.