@warlock.js/core 5.12.0 → 5.14.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 (53) hide show
  1. package/CHANGELOG.md +86 -54
  2. package/esm/cli/commands/build.command.mjs.map +1 -1
  3. package/esm/cli/commands/dev-server.command.mjs +2 -0
  4. package/esm/cli/commands/dev-server.command.mjs.map +1 -1
  5. package/esm/database/utils.d.mts +5 -1
  6. package/esm/database/utils.d.mts.map +1 -1
  7. package/esm/database/utils.mjs +7 -3
  8. package/esm/database/utils.mjs.map +1 -1
  9. package/esm/dev-server/file-event-handler.mjs +23 -5
  10. package/esm/dev-server/file-event-handler.mjs.map +1 -1
  11. package/esm/dev-server/files-watcher.mjs +6 -3
  12. package/esm/dev-server/files-watcher.mjs.map +1 -1
  13. package/esm/dev-server/translation-type-generator.mjs +28 -0
  14. package/esm/dev-server/translation-type-generator.mjs.map +1 -0
  15. package/esm/dev-server/tsconfig-manager.mjs +1 -0
  16. package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
  17. package/esm/dev-server/type-generator.mjs +41 -5
  18. package/esm/dev-server/type-generator.mjs.map +1 -1
  19. package/esm/encryption/index.mjs +1 -1
  20. package/esm/errors/esbuild-binary-missing-error.mjs +20 -0
  21. package/esm/errors/esbuild-binary-missing-error.mjs.map +1 -0
  22. package/esm/generations/features/auth-google.feature.mjs +18 -0
  23. package/esm/generations/features/auth-google.feature.mjs.map +1 -0
  24. package/esm/generations/features/auth-passkeys.feature.mjs +19 -0
  25. package/esm/generations/features/auth-passkeys.feature.mjs.map +1 -0
  26. package/esm/generations/features/bull-board.feature.mjs +65 -0
  27. package/esm/generations/features/bull-board.feature.mjs.map +1 -0
  28. package/esm/generations/features/index.mjs +8 -0
  29. package/esm/generations/features/index.mjs.map +1 -1
  30. package/esm/generations/features/queue.feature.mjs +70 -0
  31. package/esm/generations/features/queue.feature.mjs.map +1 -0
  32. package/esm/generations/features/shared/insert-connector-entry.mjs +68 -0
  33. package/esm/generations/features/shared/insert-connector-entry.mjs.map +1 -0
  34. package/esm/generations/features/shared/insert-queue-dashboard-block.mjs +55 -0
  35. package/esm/generations/features/shared/insert-queue-dashboard-block.mjs.map +1 -0
  36. package/esm/generations/features/web.feature.mjs +4 -1
  37. package/esm/generations/features/web.feature.mjs.map +1 -1
  38. package/esm/generations/stubs.mjs +4 -4
  39. package/esm/generations/stubs.mjs.map +1 -1
  40. package/esm/http/middleware/cache-response-middleware.d.mts +12 -0
  41. package/esm/http/middleware/cache-response-middleware.d.mts.map +1 -1
  42. package/esm/http/middleware/cache-response-middleware.mjs +15 -3
  43. package/esm/http/middleware/cache-response-middleware.mjs.map +1 -1
  44. package/esm/index.mjs +1 -1
  45. package/esm/production/esbuild-preflight.mjs +23 -13
  46. package/esm/production/esbuild-preflight.mjs.map +1 -1
  47. package/llms-full.txt +56 -24
  48. package/llms.txt +2 -2
  49. package/package.json +11 -12
  50. package/skills/run-app/SKILL.md +6 -2
  51. package/skills/use-localization/SKILL.md +24 -21
  52. package/skills/use-middleware/SKILL.md +24 -0
  53. package/skills/write-cli-command/SKILL.md +2 -1
package/llms.txt CHANGED
@@ -24,7 +24,7 @@
24
24
  - [request-tracing](@warlock.js/core/request-tracing/SKILL.md): 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.
25
25
  - [resolve-path](@warlock.js/core/resolve-path/SKILL.md): 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.
26
26
  - [retry-operation](@warlock.js/core/retry-operation/SKILL.md): 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`.
27
- - [run-app](@warlock.js/core/run-app/SKILL.md): 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`; "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.
27
+ - [run-app](@warlock.js/core/run-app/SKILL.md): 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.
28
28
  - [send-mail](@warlock.js/core/send-mail/SKILL.md): Send transactional email — `Mail` fluent builder, `sendMail()` direct call, React Email components. Test mode auto-captures into an in-memory mailbox; dev mode logs. Triggers: `Mail.to`, `sendMail`, `setMailMode`, `mailEvents`, `assertMailSent`, `getTestMailbox`, `wasMailSentTo`, `closeAllMailers`; "send a transactional email", "build a React Email template", "configure SMTP or SES", "assert an email was sent in tests"; typical import `import { Mail, sendMail } from "@warlock.js/core"`. Skip: per-config wiring — `@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`.
29
29
  - [send-response](@warlock.js/core/send-response/SKILL.md): Send HTTP responses via @warlock.js/core's Response helpers — success/error variants, status helpers, redirects, files, streams, and SSE. Picking the right helper carries the HTTP semantic without manual status codes. Triggers: `response.success`, `response.successCreate`, `response.notFound`, `response.forbidden`, `response.badRequest`, `response.sendFile`, `response.stream`, `response.sse`, `response.replay`, `ResourceNotFoundError`, `ForbiddenError`; "return a 201 from a controller", "send a file", "stream Server-Sent Events", "throw HTTP-shaped errors from services"; typical import `import type { RequestHandler, Response } from "@warlock.js/core"`. Skip: controller shape — `@warlock.js/core/create-controller/SKILL.md`; route registration — `@warlock.js/core/register-route/SKILL.md`; competing patterns: hand-rolled status codes via `reply.code(404).send(...)`, raw Fastify reply.
30
30
  - [store-file](@warlock.js/core/store-file/SKILL.md): Read/write/delete files via the `storage` singleton — disks, drivers (local/S3/R2/DO Spaces), `storage.use(name)`, `StorageFile` handles, presigned URLs. Triggers: `storage.put`, `storage.get`, `storage.use`, `StorageFile`, `storageConfigurations`, `getPresignedUrl`, `getPresignedUploadUrl`; "save an uploaded file", "switch between local and S3", "generate a presigned URL", "read file metadata"; typical import `import { storage } from "@warlock.js/core"`. Skip: multipart parsing + image chain — `@warlock.js/core/upload-file/SKILL.md`; image transforms — `@warlock.js/core/process-image/SKILL.md`; storage config shape — `@warlock.js/core/configure-app/SKILL.md`; competing libs `@aws-sdk/client-s3`, `multer`, `formidable`.
@@ -43,7 +43,7 @@
43
43
  - [warlock-doctor](@warlock.js/core/warlock-doctor/SKILL.md): Run `warlock doctor` — a read-only diagnostics command that checks routes / config / connectors / optional-peers / health endpoints / release hygiene and prints a pass/warn/fail report, exiting non-zero on any failure. Add your own probe with the `DoctorCheck` contract and `runChecks` / `formatReportLines`. Triggers: `warlock doctor`, `doctorCommand`, `DoctorCheck`, `CheckResult`, `CheckStatus`, `DoctorReport`, `runChecks`, `formatReportLines`, `printReport`, `defaultDoctorChecks`; "diagnose my app", "preflight / preflight check", "is the app healthy", "why are there 0 routes", "pre-release sanity check", "CI smoke check"; run as `npx warlock doctor`. Skip: the live `/health` + `/ready` HTTP probes — `@warlock.js/core/health-checks/SKILL.md`; authoring a general CLI command — `@warlock.js/core/write-cli-command/SKILL.md`; releasing the package — `releasing-warlock-monorepo`; competing tools `npm doctor`, `nest info`, hand-rolled preflight scripts.
44
44
  - [warlock-routes](@warlock.js/core/warlock-routes/SKILL.md): Run `warlock routes` — a read-only command that lists the registered HTTP routes as a verb-colored table (method / path / name / action / middleware-count / source), a sibling of `warlock doctor`. Filter with `--method` / `--path` / `--name`, or emit normalized rows as JSON with `--json`. Also covers `warlock routes:diff`, which compares live page routes against the last `warlock build`'s route snapshot and exits non-zero on drift. Triggers: `warlock routes`, `routesCommand`, `warlock routes:diff`, `routesDiffCommand`, "list my routes", "show all routes", "route table", "what endpoints does my app expose", "dump routes as JSON", "which routes have middleware", "route map for CI", "did my page routes drift from the last build"; run as `npx warlock routes` / `npx warlock routes:diff`. Skip: read-only health/preflight checks — `@warlock.js/core/warlock-doctor/SKILL.md`; defining/naming/grouping routes — `@warlock.js/core/register-route/SKILL.md`; authoring a general CLI command — `@warlock.js/core/write-cli-command/SKILL.md`; competing tools `nest`/`express` route listers, `php artisan route:list`.
45
45
  - [wire-socket](@warlock.js/core/wire-socket/SKILL.md): Configure Socket.IO via `src/config/socket.ts`, reach the live server through `getSocketServer()` (or `app.socket` post-bootstrap), register `connection` handlers once the late-phase socket connector has booted, emit from controllers/services, use rooms and namespaces. Triggers: `app.socket`, `getSocketServer`, `SocketOptions`, `socket.io` `Server`, `socket.join`, `socket.to`, `io.of`, `io.use`; "add realtime chat", "emit socket events from a service", "use rooms and namespaces", "per-socket JWT auth". Skip: connector lifecycle — `@warlock.js/core/add-connector/SKILL.md`; app context accessors — `@warlock.js/core/use-app-context/SKILL.md`; competing libs `ws`, `socket.io` direct without Warlock connector, `uWebSockets.js`.
46
- - [write-cli-command](@warlock.js/core/write-cli-command/SKILL.md): Author a custom `warlock <my-cmd>` command via the `command()` factory — name, description, action, options, preload, then register in `warlock.config.ts > cli.commands` or drop in `src/app/<module>/commands/`. Also covers built-in `warlock add` feature scaffolding, including the Web starter and `index.register.ts`. Triggers: `command`, `CLICommand`, `CLICommandPreload`, `CLICommandOption`, `preload`, `preAction`, `persistent`, `colors`, `warlock add`, `index.register.ts`; "write a custom warlock command", "one-off maintenance task", "ship a CLI from a package", "framework built-in commands"; typical import `import { command } from "@warlock.js/core"`. Skip: framework dev/build/start — `@warlock.js/core/run-app/SKILL.md`; warlock.config.ts wiring — `@warlock.js/core/configure-app/SKILL.md`; competing libs `commander`, `yargs`, `oclif`.
46
+ - [write-cli-command](@warlock.js/core/write-cli-command/SKILL.md): Author a custom `warlock <my-cmd>` command via the `command()` factory — name, description, action, options, preload, then register in `warlock.config.ts > cli.commands` or drop in `src/app/<module>/commands/`. Also covers built-in `warlock add` feature scaffolding, including the Web starter and `index.register.ts`. Triggers: `command`, `CLICommand`, `CLICommandPreload`, `CLICommandOption`, `preload`, `preAction`, `persistent`, `colors`, `warlock add`, `warlock add bull-board`, `index.register.ts`; "write a custom warlock command", "one-off maintenance task", "ship a CLI from a package", "framework built-in commands"; typical import `import { command } from "@warlock.js/core"`. Skip: framework dev/build/start — `@warlock.js/core/run-app/SKILL.md`; warlock.config.ts wiring — `@warlock.js/core/configure-app/SKILL.md`; competing libs `commander`, `yargs`, `oclif`.
47
47
  - [write-middleware](@warlock.js/core/write-middleware/SKILL.md): Author HTTP middleware for @warlock.js/core — the `({ request, response })` signature, short-circuit by returning a response, enrich the request with extra fields, register per-route, per-group, or app-wide. Triggers: `Middleware`, `MiddlewareResponse`, `router.group`, `guarded`, `request.detectIp`, `authMiddleware`; "write a custom middleware", "short-circuit a request", "enrich the request with extra fields", "per-route vs per-group middleware"; typical import `import type { Middleware } from "@warlock.js/core"`. Skip: built-in middleware catalog — `@warlock.js/core/use-middleware/SKILL.md`; route attachment — `@warlock.js/core/register-route/SKILL.md`; response helpers — `@warlock.js/core/send-response/SKILL.md`; competing patterns: `express` `(req, res, next)` middleware, Fastify `preHandler` hooks.
48
48
  - [write-seeder](@warlock.js/core/write-seeder/SKILL.md): Author a seed file under `src/app/<module>/seeds/<name>.ts` using the `seeder()` factory — `name`, `dependsOn`, `once`, `order`, `batchSize`, `run({ track, now, batchSize })`. Auto-discovered by `warlock seed`; tracked in a `seeds` table; per-record refs in `seed_records` so `warlock seed --drop` can undo a seed. Triggers: `seeder`, `Seeder`, `SeedResult`, `SeedContext`, `SeedClock`, `track`, `now`, `batchSize`, `SeedersManager`, `warlock seed`, `--fresh`, `--drop`, `--list`, `--path`; "seed default roles", "undo a seed", "one-time data migration", "auto-discovered seeds", "order seeds by dependency", "deterministic seed timestamps", "inject a seed clock"; typical import `import { seeder } from "@warlock.js/core"`. Skip: module folder layout — `@warlock.js/core/create-module/SKILL.md`; repository CRUD — `@warlock.js/core/use-repository/SKILL.md`; CLI flags — `@warlock.js/core/write-cli-command/SKILL.md`; competing patterns: hand-rolled `node scripts/seed.js`, `typeorm-seeding`.
49
49
  - [write-use-case](@warlock.js/core/write-use-case/SKILL.md): Author `useCase()` pipelines for business logic — guards, schema, before/after middleware, retry, benchmark, broadcast, lifecycle callbacks; transport-agnostic and observable by default. Input is inferred from the `schema`. Triggers: `useCase`, `UseCaseContext`, `UseCaseResult`, `retry`, `benchmark`, `broadcast`, `description`, `globalUseCasesEvents`, `UseCaseBroadcastChannel`; "encapsulate a business operation", "share logic between HTTP and CLI", "add guards and lifecycle hooks", "broadcast a use case result", "transport-agnostic pipeline"; typical import `import { useCase } from "@warlock.js/core"`. Skip: thin handler shape — `@warlock.js/core/create-controller/SKILL.md`; schema details — `@warlock.js/core/validate-input/SKILL.md`; the standalone retry util — `@warlock.js/core/retry-operation/SKILL.md`; competing libs `@nestjs/cqrs`, `inversify`, hand-rolled service classes.
package/package.json CHANGED
@@ -25,13 +25,12 @@
25
25
  "@mongez/slug": "^1.0.7",
26
26
  "@mongez/supportive-is": "^2.1.4",
27
27
  "@mongez/time-wizard": "^1.0.6",
28
- "@warlock.js/auth": "5.12.0",
29
- "@warlock.js/cache": "5.12.0",
30
- "@warlock.js/cascade": "5.12.0",
31
- "@warlock.js/context": "5.12.0",
32
- "@warlock.js/logger": "5.12.0",
33
- "@warlock.js/seal": "5.12.0",
34
- "@warlock.js/fs": "5.12.0",
28
+ "@warlock.js/cache": "5.14.0",
29
+ "@warlock.js/cascade": "5.14.0",
30
+ "@warlock.js/context": "5.14.0",
31
+ "@warlock.js/logger": "5.14.0",
32
+ "@warlock.js/seal": "5.14.0",
33
+ "@warlock.js/fs": "5.14.0",
35
34
  "chokidar": "^5.0.0",
36
35
  "dayjs": "^1.11.19",
37
36
  "es-module-lexer": "^2.0.0",
@@ -57,10 +56,10 @@
57
56
  "react": "^19.2.3",
58
57
  "react-dom": "^19.2.3",
59
58
  "@react-email/render": "^2.0.5",
60
- "@warlock.js/herald": "5.12.0",
61
- "@warlock.js/ai": "5.12.0",
62
- "@warlock.js/access": "5.12.0",
63
- "@warlock.js/notifications": "5.12.0"
59
+ "@warlock.js/herald": "5.14.0",
60
+ "@warlock.js/ai": "5.14.0",
61
+ "@warlock.js/access": "5.14.0",
62
+ "@warlock.js/notifications": "5.14.0"
64
63
  },
65
64
  "peerDependenciesMeta": {
66
65
  "sharp": {
@@ -123,7 +122,7 @@
123
122
  ],
124
123
  "author": "hassanzohdy",
125
124
  "license": "MIT",
126
- "version": "5.12.0",
125
+ "version": "5.14.0",
127
126
  "type": "module",
128
127
  "main": "./esm/index.mjs",
129
128
  "module": "./esm/index.mjs",
@@ -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`; "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 — `@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.'
4
4
  ---
5
5
 
6
6
  # Warlock — run the app
@@ -143,6 +143,7 @@ export default defineConfig({
143
143
  restartOnConfigChange: true, // restart when warlock.config.ts / .env* changes
144
144
  healthCheckers: [...] /* or false */,
145
145
  transpileCacheDebug: false, // name cache files <slug>.<hash>.js w/ // @source markers
146
+ timings: false, // print a per-phase reload timing breakdown
146
147
  },
147
148
  });
148
149
  ```
@@ -153,6 +154,7 @@ export default defineConfig({
153
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.
154
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
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
+ - **`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.
156
158
 
157
159
  ## `warlock build` — production bundle
158
160
 
@@ -258,7 +260,7 @@ Means `docker stop` / `kubectl delete pod` works as expected: SIGTERM reaches th
258
260
 
259
261
  ### pnpm needs esbuild's install script allowed
260
262
 
261
- pnpm 10+ will not run a dependency's install script unless the app names it. esbuild's script links its platform-native binary, and `warlock build` shells out to that binary — so the app installs cleanly and then cannot build:
263
+ pnpm 10+ will not run a dependency's install script unless the app names it. esbuild's script links its platform-native binary, and both `warlock build` and `warlock dev` shell out to that binary — so the app installs cleanly and then cannot build or start dev:
262
264
 
263
265
  ```
264
266
  [ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: esbuild@0.27.7
@@ -273,6 +275,8 @@ allowBuilds:
273
275
 
274
276
  Note pnpm reads this from `pnpm-workspace.yaml`, **not** from `package.json`'s `pnpm` field — pnpm 11 warns that the field is ignored and then carries on, so settings left there fail silently.
275
277
 
278
+ `warlock dev` checks for esbuild's native binary before starting (as `warlock build` already did) and fails fast with `EsbuildBinaryMissingError`, naming the same fix, instead of surfacing an opaque low-level error later from inside the bundler.
279
+
276
280
  Nothing else is needed for pnpm. Warlock never requires an app to declare a package it does not import: generated code is checked at build time against the app's own `dependencies`, so `warlock build` failing over an unfamiliar package name is a framework bug, not a missing dependency.
277
281
 
278
282
  ### Output streams — what a supervisor may trust
@@ -20,7 +20,7 @@ import { groupedTranslations } from "@mongez/localization";
20
20
 
21
21
  groupedTranslations("products", {
22
22
  notFound: { en: "Product not found", ar: "المنتج غير موجود" },
23
- created: { en: "Product created", ar: "تم إنشاء المنتج" },
23
+ created: { en: "Product created", ar: "تم إنشاء المنتج" },
24
24
  });
25
25
 
26
26
  // 2. Look up in a controller / service
@@ -45,11 +45,11 @@ Every module owns its translation namespace under `src/app/<module>/utils/locale
45
45
  import { groupedTranslations } from "@mongez/localization";
46
46
 
47
47
  groupedTranslations("products", {
48
- notFound: { en: "Product not found", ar: "المنتج غير موجود" },
49
- outOfStock: { en: "Product out of stock", ar: "المنتج غير متوفر" },
50
- created: { en: "Product created", ar: "تم إنشاء المنتج" },
51
- updated: { en: "Product updated", ar: "تم تحديث المنتج" },
52
- deleted: { en: "Product deleted", ar: "تم حذف المنتج" },
48
+ notFound: { en: "Product not found", ar: "المنتج غير موجود" },
49
+ outOfStock: { en: "Product out of stock", ar: "المنتج غير متوفر" },
50
+ created: { en: "Product created", ar: "تم إنشاء المنتج" },
51
+ updated: { en: "Product updated", ar: "تم تحديث المنتج" },
52
+ deleted: { en: "Product deleted", ar: "تم حذف المنتج" },
53
53
  });
54
54
  ```
55
55
 
@@ -96,6 +96,10 @@ request.trans("products.notFound");
96
96
 
97
97
  All three lookups go through `@mongez/localization`'s `trans()` under the hood, with the locale pulled from the request context (or the global default).
98
98
 
99
+ ### Web `useTrans()` key checking
100
+
101
+ When an app uses `@warlock.js/web`, `warlock dev` writes `.warlock/typings/translations.d.ts` from literal `groupedTranslations("group", { key: ... })` registrations. It augments web's `TranslationKeyRegistry`, so `useTrans()("products.notFound")` is checked against registered keys and a typo fails TypeScript. Before the generated file exists, `useTrans()` accepts `string` for a non-breaking first boot. Dynamic groups/keys and placeholders are not inferred.
102
+
99
103
  ### Locale on a specific lookup
100
104
 
101
105
  ```ts
@@ -123,7 +127,7 @@ Configure the default:
123
127
 
124
128
  ```ts title="src/config/app.ts"
125
129
  export default {
126
- localeCode: "en", // app-wide default
130
+ localeCode: "en", // app-wide default
127
131
  // ...
128
132
  };
129
133
  ```
@@ -142,20 +146,19 @@ When a column stores per-locale values as an array:
142
146
 
143
147
  ```ts
144
148
  // Schema (Seal):
145
- name_translations: v.array(
149
+ name_translations: (v.array(
146
150
  v.object({
147
- localeCode: v.string(), // "en", "ar", ...
151
+ localeCode: v.string(), // "en", "ar", ...
148
152
  value: v.string(),
149
- })
153
+ }),
150
154
  ),
151
-
152
- // Stored row (DB):
153
- {
154
- name_translations: [
155
- { localeCode: "en", value: "Hello World" },
156
- { localeCode: "ar", value: "مرحبا" },
157
- ],
158
- }
155
+ // Stored row (DB):
156
+ {
157
+ name_translations: [
158
+ { localeCode: "en", value: "Hello World" },
159
+ { localeCode: "ar", value: "مرحبا" },
160
+ ],
161
+ });
159
162
  ```
160
163
 
161
164
  Pick the right one for the current request:
@@ -179,12 +182,12 @@ getLocalized(
179
182
  ```
180
183
 
181
184
  - **`values`** — the localized-array column.
182
- - **`localeCode`** *(optional)* — pin to a specific locale. Defaults to the current request's locale (reads via `useRequestStore()`).
183
- - **`key`** *(default `"value"`)* — which property of the matched entry to return. Use a different key if your localized objects store the value under a different name.
185
+ - **`localeCode`** _(optional)_ — pin to a specific locale. Defaults to the current request's locale (reads via `useRequestStore()`).
186
+ - **`key`** _(default `"value"`)_ — which property of the matched entry to return. Use a different key if your localized objects store the value under a different name.
184
187
 
185
188
  ```ts
186
189
  const slug = getLocalized(product.get("slug_translations"), undefined, "value");
187
- const tagline = getLocalized(product.get("name_translations"), "fr"); // force French
190
+ const tagline = getLocalized(product.get("name_translations"), "fr"); // force French
188
191
  ```
189
192
 
190
193
  ### Use inside a resource for clean per-locale responses
@@ -174,6 +174,30 @@ router.get("/analytics/summary", summaryController, {
174
174
 
175
175
  `cacheKey` can be a string OR a function `(request) => string | Promise<string>` for per-request keys. Excludes failures and omits `["user", "settings"]` from the cached body by default.
176
176
 
177
+ ### Tag-based invalidation
178
+
179
+ Give it `tags` — a static list, or a function of the request — to evict the entry early with `cache.tags([...]).invalidate()` (`@warlock.js/cache`), instead of waiting out `ttl`. Mirrors `route.cache.tags` on `@warlock.js/web`'s page cache, so an API response and a page can share the same tag and be invalidated together:
180
+
181
+ ```ts
182
+ import { middleware } from "@warlock.js/core";
183
+ import { cache } from "@warlock.js/cache";
184
+
185
+ router.get("/orders/:id", getOrderController, {
186
+ middleware: [
187
+ middleware.cache({
188
+ cacheKey: (request) => `orders.${request.params.id}`,
189
+ ttl: 300,
190
+ tags: (request) => [`order.${request.params.id}`],
191
+ }),
192
+ ],
193
+ });
194
+
195
+ // Elsewhere, after the order changes:
196
+ await cache.tags([`order.${orderId}`]).invalidate();
197
+ ```
198
+
199
+ Untagged entries (no `tags` given) behave exactly as before — they only expire via `ttl`.
200
+
177
201
  ## Composed example
178
202
 
179
203
  Tight cap on logins, concurrency + idempotency on AI calls:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: write-cli-command
3
- description: 'Author a custom `warlock <my-cmd>` command via the `command()` factory — name, description, action, options, preload, then register in `warlock.config.ts > cli.commands` or drop in `src/app/<module>/commands/`. Also covers built-in `warlock add` feature scaffolding, including the Web starter and `index.register.ts`. Triggers: `command`, `CLICommand`, `CLICommandPreload`, `CLICommandOption`, `preload`, `preAction`, `persistent`, `colors`, `warlock add`, `index.register.ts`; "write a custom warlock command", "one-off maintenance task", "ship a CLI from a package", "framework built-in commands"; typical import `import { command } from "@warlock.js/core"`. Skip: framework dev/build/start — `@warlock.js/core/run-app/SKILL.md`; warlock.config.ts wiring — `@warlock.js/core/configure-app/SKILL.md`; competing libs `commander`, `yargs`, `oclif`.'
3
+ description: 'Author a custom `warlock <my-cmd>` command via the `command()` factory — name, description, action, options, preload, then register in `warlock.config.ts > cli.commands` or drop in `src/app/<module>/commands/`. Also covers built-in `warlock add` feature scaffolding, including the Web starter and `index.register.ts`. Triggers: `command`, `CLICommand`, `CLICommandPreload`, `CLICommandOption`, `preload`, `preAction`, `persistent`, `colors`, `warlock add`, `warlock add bull-board`, `index.register.ts`; "write a custom warlock command", "one-off maintenance task", "ship a CLI from a package", "framework built-in commands"; typical import `import { command } from "@warlock.js/core"`. Skip: framework dev/build/start — `@warlock.js/core/run-app/SKILL.md`; warlock.config.ts wiring — `@warlock.js/core/configure-app/SKILL.md`; competing libs `commander`, `yargs`, `oclif`.'
4
4
  ---
5
5
 
6
6
  # Warlock — write a CLI command
@@ -207,6 +207,7 @@ warlock generate (alias: g) — interactive picker
207
207
  | `react-email` | `react-email` + `@react-email/components` + `@react-email/render` + `@react-email/tailwind`; drops a `welcome-email.tsx` sample; patches `tsconfig.json` |
208
208
  | `web` | Warlock Web + React stack; scaffolds the application page, localization, contact endpoint, and Web configuration |
209
209
  | `react` | `react` + `react-dom` + types |
210
+ | `bull-board` | `@bull-board/api` + `@bull-board/fastify`; writes a `dashboard` block to `src/config/queue.ts`. `requires: ["queue"]` — adds the `queue` feature first automatically when it's missing |
210
211
  | `image` | `sharp` (for the `Image` class) |
211
212
  | `mail` | `nodemailer` + types |
212
213
  | `ses` | `@aws-sdk/client-sesv2` |