@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.
- package/CHANGELOG.md +13 -0
- package/esm/container/development-app-modules.d.mts +31 -0
- package/esm/container/development-app-modules.d.mts.map +1 -0
- package/esm/container/index.d.mts +2 -0
- package/esm/container/index.d.mts.map +1 -1
- package/esm/container/index.mjs.map +1 -1
- package/esm/dev-server/app-module-registry.mjs +52 -0
- package/esm/dev-server/app-module-registry.mjs.map +1 -0
- package/esm/dev-server/files-orchestrator.mjs +13 -2
- package/esm/dev-server/files-orchestrator.mjs.map +1 -1
- package/esm/index.d.mts +3 -1
- package/esm/index.mjs +2 -1
- package/esm/localization/parse-locale-dictionary.d.mts +6 -1
- package/esm/localization/parse-locale-dictionary.d.mts.map +1 -1
- package/esm/localization/parse-locale-dictionary.mjs +5 -1
- package/esm/localization/parse-locale-dictionary.mjs.map +1 -1
- package/esm/openapi/response-schemas.mjs +4 -1
- package/esm/openapi/response-schemas.mjs.map +1 -1
- package/esm/openapi/schema-registry.mjs +6 -0
- package/esm/openapi/schema-registry.mjs.map +1 -1
- package/esm/resource/index.d.mts +1 -0
- package/esm/resource/index.mjs +1 -0
- package/esm/resource/nullable-resource.d.mts +44 -0
- package/esm/resource/nullable-resource.d.mts.map +1 -0
- package/esm/resource/nullable-resource.mjs +38 -0
- package/esm/resource/nullable-resource.mjs.map +1 -0
- package/esm/resource/resource.d.mts.map +1 -1
- package/esm/resource/resource.mjs +22 -3
- package/esm/resource/resource.mjs.map +1 -1
- package/esm/resource/types.d.mts +10 -5
- package/esm/resource/types.d.mts.map +1 -1
- package/esm/router/response-schema-types.mjs +2 -0
- package/esm/router/response-schema-types.mjs.map +1 -1
- package/llms-full.txt +241 -241
- package/llms.txt +42 -42
- package/package.json +12 -12
- package/skills/README.md +39 -39
- package/skills/add-connector/SKILL.md +4 -4
- package/skills/benchmark-code/SKILL.md +3 -3
- package/skills/build-restful/SKILL.md +5 -5
- package/skills/build-url/SKILL.md +7 -7
- package/skills/configure-app/SKILL.md +7 -7
- package/skills/create-controller/SKILL.md +8 -8
- package/skills/create-module/SKILL.md +9 -9
- package/skills/define-resource/SKILL.md +4 -4
- package/skills/encrypt-data/SKILL.md +7 -7
- package/skills/generate-openapi/SKILL.md +5 -5
- package/skills/hash-password/SKILL.md +8 -8
- package/skills/health-checks/SKILL.md +6 -6
- package/skills/lower-stage3-decorators/SKILL.md +4 -4
- package/skills/process-image/SKILL.md +5 -5
- package/skills/register-route/SKILL.md +6 -6
- package/skills/request-memo/SKILL.md +4 -4
- package/skills/request-tracing/SKILL.md +6 -6
- package/skills/resolve-path/SKILL.md +6 -6
- package/skills/retry-operation/SKILL.md +4 -4
- package/skills/run-app/SKILL.md +11 -11
- package/skills/send-mail/SKILL.md +3 -3
- package/skills/send-response/SKILL.md +6 -6
- package/skills/store-file/SKILL.md +4 -4
- package/skills/test-http/SKILL.md +5 -5
- package/skills/test-service/SKILL.md +5 -5
- package/skills/update-packages/SKILL.md +6 -6
- package/skills/upload-file/SKILL.md +7 -7
- package/skills/use-app-context/SKILL.md +7 -7
- package/skills/use-localization/SKILL.md +6 -6
- package/skills/use-middleware/SKILL.md +6 -6
- package/skills/use-model-transformers/SKILL.md +6 -6
- package/skills/use-repository/SKILL.md +7 -7
- package/skills/use-request-locals/SKILL.md +4 -4
- package/skills/validate-input/SKILL.md +7 -7
- package/skills/warlock-conventions/SKILL.md +6 -6
- package/skills/warlock-doctor/SKILL.md +5 -5
- package/skills/warlock-routes/SKILL.md +6 -6
- package/skills/wire-socket/SKILL.md +4 -4
- package/skills/write-cli-command/SKILL.md +6 -6
- package/skills/write-middleware/SKILL.md +8 -8
- package/skills/write-seeder/SKILL.md +5 -5
- 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 —
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
-
|
|
277
|
-
-
|
|
278
|
-
-
|
|
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 —
|
|
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
|
-
-
|
|
184
|
-
-
|
|
185
|
-
-
|
|
186
|
-
-
|
|
187
|
-
-
|
|
188
|
-
-
|
|
189
|
-
-
|
|
190
|
-
-
|
|
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 —
|
|
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
|
-
-
|
|
399
|
-
-
|
|
400
|
-
-
|
|
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 —
|
|
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
|
|
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
|
|
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
|
|
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
|
-
-
|
|
207
|
-
-
|
|
208
|
-
-
|
|
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 —
|
|
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
|
|
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
|
-
-
|
|
147
|
-
-
|
|
148
|
-
-
|
|
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 —
|
|
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 →
|
|
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
|
|
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
|
-
-
|
|
180
|
-
-
|
|
181
|
-
-
|
|
182
|
-
-
|
|
183
|
-
-
|
|
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 —
|
|
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
|
|
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
|
-
-
|
|
117
|
-
-
|
|
118
|
-
-
|
|
119
|
-
-
|
|
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 —
|
|
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
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
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 —
|
|
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
|
|
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
|
-
-
|
|
170
|
-
-
|
|
171
|
-
-
|
|
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 —
|
|
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
|
|
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
|
|
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
|
-
-
|
|
171
|
-
-
|
|
172
|
-
-
|
|
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 —
|
|
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
|
-
-
|
|
141
|
-
-
|
|
142
|
-
-
|
|
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`) —
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
230
|
-
-
|
|
231
|
-
-
|
|
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 —
|
|
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
|
-
-
|
|
167
|
-
-
|
|
168
|
-
-
|
|
169
|
-
-
|
|
170
|
-
-
|
|
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 —
|
|
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
|
|
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
|
-
-
|
|
109
|
-
-
|
|
108
|
+
- The `benchmark-code` topic — timing retried operations.
|
|
109
|
+
- The `write-use-case` topic — the use-case `retry` option.
|
package/skills/run-app/SKILL.md
CHANGED
|
@@ -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 —
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
-
|
|
511
|
-
-
|
|
512
|
-
-
|
|
513
|
-
-
|
|
514
|
-
-
|
|
515
|
-
-
|
|
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 —
|
|
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
|
-
-
|
|
402
|
-
-
|
|
401
|
+
- The `configure-app` topic — `src/config/mail.ts` shape and `env()` patterns.
|
|
402
|
+
- The `warlock-conventions` topic — where mail-sending services live.
|