@warlock.js/core 5.2.3 → 5.3.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 +6 -0
- package/esm/application/application.d.mts.map +1 -1
- package/esm/application/application.mjs.map +1 -1
- package/esm/benchmark/benchmark-snapshots.d.mts.map +1 -1
- package/esm/benchmark/benchmark.d.mts.map +1 -1
- package/esm/benchmark/channels/console.channel.mjs.map +1 -1
- package/esm/benchmark/channels/noop.channel.d.mts.map +1 -1
- package/esm/benchmark/profiler.d.mts.map +1 -1
- package/esm/benchmark/profiler.mjs.map +1 -1
- package/esm/bootstrap/setup.d.mts.map +1 -1
- package/esm/bootstrap/setup.mjs.map +1 -1
- package/esm/bootstrap.d.mts.map +1 -1
- package/esm/cache/database-cache-driver.d.mts.map +1 -1
- package/esm/cache/database-cache-driver.mjs.map +1 -1
- package/esm/cli/cli-commands.manager.mjs.map +1 -1
- package/esm/cli/cli-commands.utils.mjs.map +1 -1
- package/esm/cli/commands/doctor/boot-for-diagnostics.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/connectors.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/handler-signature.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/health.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/optional-peers.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/routes.check.mjs.map +1 -1
- package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/column-dsl-parser.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/templates/stubs.mjs +13 -14
- package/esm/cli/commands/generate/templates/stubs.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/name-parser.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/prompt.mjs.map +1 -1
- package/esm/cli/commands/routes/diff-page-routes.mjs.map +1 -1
- package/esm/cli/commands/routes/format-routes-table.mjs.map +1 -1
- package/esm/cli/commands/routes/route-row.mjs.map +1 -1
- package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -1
- package/esm/cli/commands/routes/routes-diff.action.mjs.map +1 -1
- package/esm/cli/commands/storage-put.action.mjs.map +1 -1
- package/esm/cli/commands/typings-generator.command.mjs.map +1 -1
- package/esm/cli/commands-loader.mjs.map +1 -1
- package/esm/cli/start.mjs.map +1 -1
- package/esm/cli/string-similarity.mjs.map +1 -1
- package/esm/commands/cli-command.mjs.map +1 -1
- package/esm/config/config-loader.mjs.map +1 -1
- package/esm/config/config-setter.d.mts.map +1 -1
- package/esm/config/load-config-files.mjs.map +1 -1
- package/esm/connectors/access-connector.d.mts.map +1 -1
- package/esm/connectors/ai-connector.d.mts.map +1 -1
- package/esm/connectors/assert-no-reserved-connector-names.mjs.map +1 -1
- package/esm/connectors/assert-unique-connector-names.mjs.map +1 -1
- package/esm/connectors/base-connector.d.mts.map +1 -1
- package/esm/connectors/base-connector.mjs.map +1 -1
- package/esm/connectors/cache-connector.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.mjs.map +1 -1
- package/esm/connectors/database-connector.d.mts.map +1 -1
- package/esm/connectors/describe-server-address.mjs.map +1 -1
- package/esm/connectors/herald-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.mjs.map +1 -1
- package/esm/connectors/logger-connector.d.mts.map +1 -1
- package/esm/connectors/mail-connector.d.mts.map +1 -1
- package/esm/connectors/notifications-connector.d.mts.map +1 -1
- package/esm/connectors/register-configured-connectors.mjs.map +1 -1
- package/esm/connectors/socket-connector.d.mts.map +1 -1
- package/esm/connectors/socket-connector.mjs.map +1 -1
- package/esm/connectors/storage.connector.d.mts.map +1 -1
- package/esm/container/index.d.mts.map +1 -1
- package/esm/database/create-database-action.mjs.map +1 -1
- package/esm/database/drop-tables-action.mjs.map +1 -1
- package/esm/database/migrate-action.mjs.map +1 -1
- package/esm/database/seed-command-action.mjs.map +1 -1
- package/esm/database/seeds/seed-records-table-migration.mjs.map +1 -1
- package/esm/database/seeds/seeders.manager.mjs.map +1 -1
- package/esm/database/seeds/seeds-table-migration.mjs.map +1 -1
- package/esm/dev-server/check-for-updates.mjs.map +1 -1
- package/esm/dev-server/dependency-graph.d.mts.map +1 -1
- package/esm/dev-server/dependency-graph.mjs.map +1 -1
- package/esm/dev-server/dev-logger.mjs.map +1 -1
- package/esm/dev-server/development-server.mjs.map +1 -1
- package/esm/dev-server/file-event-handler.mjs.map +1 -1
- package/esm/dev-server/file-manager.d.mts.map +1 -1
- package/esm/dev-server/file-manager.mjs.map +1 -1
- package/esm/dev-server/file-operations.d.mts.map +1 -1
- package/esm/dev-server/files-orchestrator.mjs.map +1 -1
- package/esm/dev-server/files-watcher.mjs.map +1 -1
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs.map +1 -1
- package/esm/dev-server/health-checker/checkers/typescript-health-checker.mjs.map +1 -1
- package/esm/dev-server/health-checker/file-health-result.d.mts.map +1 -1
- package/esm/dev-server/health-checker/file-health-result.mjs.map +1 -1
- package/esm/dev-server/health-checker/files-healthcare.manager.mjs.map +1 -1
- package/esm/dev-server/layer-executor.mjs.map +1 -1
- package/esm/dev-server/loader/hook-thread.d.mts.map +1 -1
- package/esm/dev-server/loader/own-resolver.mjs.map +1 -1
- package/esm/dev-server/loader/register-loader.mjs.map +1 -1
- package/esm/dev-server/loader/resolve-hook.mjs.map +1 -1
- package/esm/dev-server/loader/source-slug.mjs.map +1 -1
- package/esm/dev-server/loader/transpile-cache.mjs.map +1 -1
- package/esm/dev-server/loader/version-registry.mjs.map +1 -1
- package/esm/dev-server/manifest-manager.d.mts.map +1 -1
- package/esm/dev-server/manifest-manager.mjs.map +1 -1
- package/esm/dev-server/module-loader.mjs.map +1 -1
- package/esm/dev-server/package-json-manager.mjs.map +1 -1
- package/esm/dev-server/parse-imports.mjs.map +1 -1
- package/esm/dev-server/ready-block.mjs.map +1 -1
- package/esm/dev-server/special-files-collector.d.mts.map +1 -1
- package/esm/dev-server/special-files-collector.mjs.map +1 -1
- package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
- package/esm/dev-server/type-generator.mjs.map +1 -1
- package/esm/dev-server/utils.mjs.map +1 -1
- package/esm/encryption/encrypt.mjs.map +1 -1
- package/esm/encryption/hash.mjs.map +1 -1
- package/esm/generations/add-command.action.mjs.map +1 -1
- package/esm/generations/features/shadcn.feature.mjs.map +1 -1
- package/esm/generations/features/shared/migration-timestamp.mjs.map +1 -1
- package/esm/generations/features/tailwind.feature.mjs.map +1 -1
- package/esm/generations/features/web.feature.mjs +2 -1
- package/esm/generations/features/web.feature.mjs.map +1 -1
- package/esm/generations/stubs.mjs +53 -54
- package/esm/generations/stubs.mjs.map +1 -1
- package/esm/http/boot-port-preflight.d.mts.map +1 -1
- package/esm/http/context/request-context.d.mts.map +1 -1
- package/esm/http/context/request-context.mjs.map +1 -1
- package/esm/http/context/request-memo.d.mts.map +1 -1
- package/esm/http/context/request-memo.mjs.map +1 -1
- package/esm/http/createHttpApplication.d.mts.map +1 -1
- package/esm/http/createHttpApplication.mjs.map +1 -1
- package/esm/http/health.d.mts.map +1 -1
- package/esm/http/index.d.mts +1 -1
- package/esm/http/middleware/cache-response-middleware.mjs.map +1 -1
- package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
- package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
- package/esm/http/middleware/inject-request-context.mjs +2 -21
- package/esm/http/middleware/inject-request-context.mjs.map +1 -1
- package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
- package/esm/http/middleware/utils/idempotency-key.mjs.map +1 -1
- package/esm/http/ready-report.d.mts.map +1 -1
- package/esm/http/request-controller.d.mts.map +1 -1
- package/esm/http/request.d.mts +25 -6
- package/esm/http/request.d.mts.map +1 -1
- package/esm/http/request.mjs +48 -0
- package/esm/http/request.mjs.map +1 -1
- package/esm/http/response.d.mts +3 -3
- package/esm/http/response.d.mts.map +1 -1
- package/esm/http/response.mjs.map +1 -1
- package/esm/http/server.d.mts.map +1 -1
- package/esm/http/server.mjs.map +1 -1
- package/esm/http/types.d.mts +36 -5
- package/esm/http/types.d.mts.map +1 -1
- package/esm/http/uploaded-file.d.mts.map +1 -1
- package/esm/http/uploaded-file.mjs.map +1 -1
- package/esm/http/uploads-config.d.mts.map +1 -1
- package/esm/image/image.d.mts.map +1 -1
- package/esm/image/image.mjs.map +1 -1
- package/esm/index.d.mts +2 -2
- package/esm/logger/logger.mjs.map +1 -1
- package/esm/mail/config.d.mts.map +1 -1
- package/esm/mail/events.d.mts.map +1 -1
- package/esm/mail/mail.d.mts.map +1 -1
- package/esm/mail/mail.mjs.map +1 -1
- package/esm/mail/mailer-pool.d.mts.map +1 -1
- package/esm/mail/mailer-pool.mjs.map +1 -1
- package/esm/mail/send-mail.mjs.map +1 -1
- package/esm/mail/test-mailbox.d.mts.map +1 -1
- package/esm/mail/test-mailbox.mjs.map +1 -1
- package/esm/production/assert-generated-imports.mjs.map +1 -1
- package/esm/production/build-app-production.mjs.map +1 -1
- package/esm/production/dist-build-manifest.mjs.map +1 -1
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/production/production-supervisor.mjs.map +1 -1
- package/esm/production/promote-dist.mjs.map +1 -1
- package/esm/production/resolve-build-config.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.d.mts.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-query-builder.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/filter-applicator.mjs.map +1 -1
- package/esm/repositories/repository.manager.d.mts.map +1 -1
- package/esm/repositories/repository.manager.mjs.map +1 -1
- package/esm/resource/register-resource.d.mts.map +1 -1
- package/esm/resource/resource-field-builder.d.mts.map +1 -1
- package/esm/resource/resource-field-builder.mjs.map +1 -1
- package/esm/resource/resource.d.mts.map +1 -1
- package/esm/resource/resource.mjs.map +1 -1
- package/esm/restful/restful.d.mts.map +1 -1
- package/esm/restful/restful.mjs.map +1 -1
- package/esm/router/log-request-lifecycle.mjs.map +1 -1
- package/esm/router/normalize-route-path.d.mts.map +1 -1
- package/esm/router/positional-handler-diagnostics.d.mts.map +1 -1
- package/esm/router/positional-handler-diagnostics.mjs.map +1 -1
- package/esm/router/route-registry.d.mts.map +1 -1
- package/esm/router/route-registry.mjs.map +1 -1
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs.map +1 -1
- package/esm/socket/utils.d.mts.map +1 -1
- package/esm/storage/config.d.mts.map +1 -1
- package/esm/storage/context/storage-driver-context.d.mts.map +1 -1
- package/esm/storage/drivers/cloud-driver.d.mts.map +1 -1
- package/esm/storage/drivers/cloud-driver.mjs.map +1 -1
- package/esm/storage/drivers/do-spaces-driver.d.mts.map +1 -1
- package/esm/storage/drivers/local-driver.d.mts.map +1 -1
- package/esm/storage/drivers/local-driver.mjs.map +1 -1
- package/esm/storage/drivers/r2-driver.d.mts.map +1 -1
- package/esm/storage/scoped-storage.d.mts.map +1 -1
- package/esm/storage/scoped-storage.mjs.map +1 -1
- package/esm/storage/storage-file.d.mts.map +1 -1
- package/esm/storage/storage-file.mjs.map +1 -1
- package/esm/storage/storage.d.mts.map +1 -1
- package/esm/storage/utils/safe-fetch.mjs.map +1 -1
- package/esm/tests/start-http-development-server.d.mts.map +1 -1
- package/esm/tests/test-connectors-selection.mjs.map +1 -1
- package/esm/tests/test-helpers.d.mts.map +1 -1
- package/esm/tests/test-setup-timeout.mjs.map +1 -1
- package/esm/tests/vitest-setup.d.mts.map +1 -1
- package/esm/tests/vitest-setup.mjs.map +1 -1
- package/esm/updater/update-warlock-packages.mjs.map +1 -1
- package/esm/use-cases/use-case-broadcast.d.mts.map +1 -1
- package/esm/use-cases/use-case-events.d.mts.map +1 -1
- package/esm/use-cases/use-case-pipeline.d.mts.map +1 -1
- package/esm/use-cases/use-case.d.mts.map +1 -1
- package/esm/use-cases/use-cases-registry.d.mts.map +1 -1
- package/esm/use-cases/use-cases-registry.mjs.map +1 -1
- package/esm/utils/database-log.d.mts.map +1 -1
- package/esm/utils/database-log.mjs.map +1 -1
- package/esm/utils/environment.d.mts.map +1 -1
- package/esm/utils/framework-vesion.mjs.map +1 -1
- package/esm/utils/get-localized.mjs.map +1 -1
- package/esm/utils/load-environment.mjs.map +1 -1
- package/esm/utils/normalized-path.d.mts.map +1 -1
- package/esm/utils/paths.d.mts.map +1 -1
- package/esm/utils/promise-all-object.d.mts.map +1 -1
- package/esm/utils/promise-all-object.mjs.map +1 -1
- package/esm/utils/sluggable.mjs.map +1 -1
- package/esm/utils/version-compare.mjs.map +1 -1
- package/esm/validation/database/exists-except-current-id.mjs.map +1 -1
- package/esm/validation/database/exists-except-current-user.mjs.map +1 -1
- package/esm/validation/database/unique-except-current-id.mjs.map +1 -1
- package/esm/validation/plugins/file-plugin.mjs.map +1 -1
- package/esm/validation/plugins/localized-plugin.mjs +2 -2
- package/esm/validation/plugins/localized-plugin.mjs.map +1 -1
- package/esm/validation/types.d.mts +17 -4
- package/esm/validation/types.d.mts.map +1 -1
- package/esm/validation/validators/file-validator.mjs.map +1 -1
- package/esm/vite/lower-stage3-decorators.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.mjs.map +1 -1
- package/llms-full.txt +113 -87
- package/llms.txt +2 -2
- package/package.json +12 -12
- package/skills/README.md +2 -2
- package/skills/create-controller/SKILL.md +9 -9
- package/skills/send-response/SKILL.md +51 -37
- package/skills/store-file/SKILL.md +8 -3
- package/skills/upload-file/SKILL.md +9 -7
- package/skills/use-app-context/SKILL.md +2 -2
- package/skills/use-localization/SKILL.md +6 -2
- package/skills/use-repository/SKILL.md +2 -2
- package/skills/use-request-locals/SKILL.md +3 -3
- package/skills/validate-input/SKILL.md +2 -2
- package/skills/warlock-conventions/SKILL.md +2 -2
- package/skills/wire-socket/SKILL.md +2 -2
- package/skills/write-cli-command/SKILL.md +4 -1
- package/skills/write-middleware/SKILL.md +13 -15
package/llms.txt
CHANGED
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
- [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 `pnpm 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.
|
|
43
43
|
- [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 `pnpm warlock routes` / `pnpm 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`.
|
|
44
44
|
- [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`.
|
|
45
|
-
- [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/`. Triggers: `command`, `CLICommand`, `CLICommandPreload`, `CLICommandOption`, `preload`, `preAction`, `persistent`, `colors`; "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-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.
|
|
45
|
+
- [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-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.
|
|
47
47
|
- [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`.
|
|
48
48
|
- [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,13 @@
|
|
|
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.
|
|
29
|
-
"@warlock.js/cache": "5.
|
|
30
|
-
"@warlock.js/cascade": "5.
|
|
31
|
-
"@warlock.js/context": "5.
|
|
32
|
-
"@warlock.js/logger": "5.
|
|
33
|
-
"@warlock.js/seal": "5.
|
|
34
|
-
"@warlock.js/fs": "5.
|
|
28
|
+
"@warlock.js/auth": "5.3.0",
|
|
29
|
+
"@warlock.js/cache": "5.3.0",
|
|
30
|
+
"@warlock.js/cascade": "5.3.0",
|
|
31
|
+
"@warlock.js/context": "5.3.0",
|
|
32
|
+
"@warlock.js/logger": "5.3.0",
|
|
33
|
+
"@warlock.js/seal": "5.3.0",
|
|
34
|
+
"@warlock.js/fs": "5.3.0",
|
|
35
35
|
"chokidar": "^5.0.0",
|
|
36
36
|
"dayjs": "^1.11.19",
|
|
37
37
|
"es-module-lexer": "^2.0.0",
|
|
@@ -57,10 +57,10 @@
|
|
|
57
57
|
"react": "^19.2.3",
|
|
58
58
|
"react-dom": "^19.2.3",
|
|
59
59
|
"@react-email/render": "^2.0.5",
|
|
60
|
-
"@warlock.js/herald": "5.
|
|
61
|
-
"@warlock.js/ai": "5.
|
|
62
|
-
"@warlock.js/access": "5.
|
|
63
|
-
"@warlock.js/notifications": "5.
|
|
60
|
+
"@warlock.js/herald": "5.3.0",
|
|
61
|
+
"@warlock.js/ai": "5.3.0",
|
|
62
|
+
"@warlock.js/access": "5.3.0",
|
|
63
|
+
"@warlock.js/notifications": "5.3.0"
|
|
64
64
|
},
|
|
65
65
|
"peerDependenciesMeta": {
|
|
66
66
|
"sharp": {
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
],
|
|
124
124
|
"author": "hassanzohdy",
|
|
125
125
|
"license": "MIT",
|
|
126
|
-
"version": "5.
|
|
126
|
+
"version": "5.3.0",
|
|
127
127
|
"type": "module",
|
|
128
128
|
"main": "./esm/index.mjs",
|
|
129
129
|
"module": "./esm/index.mjs",
|
package/skills/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Each folder holds one `SKILL.md` (an agent-facing how-to for a single task). The
|
|
|
10
10
|
- [send-response](send-response/SKILL.md) — `Response` helpers: success/error variants, status helpers, redirects, files, streams, SSE.
|
|
11
11
|
- [validate-input](validate-input/SKILL.md) — author seal schemas, attach them to controllers, infer types, layer DTOs.
|
|
12
12
|
- [use-middleware](use-middleware/SKILL.md) — attach built-in HTTP middleware (rateLimit, concurrencyLimit, maxBodySize, …) via the `middleware` namespace.
|
|
13
|
-
- [write-middleware](write-middleware/SKILL.md) — author HTTP middleware: the `(request, response)` signature, short-circuit, request enrichment.
|
|
13
|
+
- [write-middleware](write-middleware/SKILL.md) — author HTTP middleware: the `({ request, response })` signature, short-circuit, request enrichment.
|
|
14
14
|
- [build-url](build-url/SKILL.md) — HTTP URL helpers (`url`, `publicUrl`, `assetsUrl`, `uploadsUrl`) anchored at `app.baseUrl`.
|
|
15
15
|
- [upload-file](upload-file/SKILL.md) — handle multipart uploads: `request.file()`, `v.file()` validation, `UploadedFile.save()`.
|
|
16
16
|
- [health-checks](health-checks/SKILL.md) — built-in `/health` + `/ready` endpoints, the `health` registry, and graceful request draining.
|
|
@@ -45,7 +45,7 @@ Each folder holds one `SKILL.md` (an agent-facing how-to for a single task). The
|
|
|
45
45
|
|
|
46
46
|
- [warlock-doctor](warlock-doctor/SKILL.md) — `warlock doctor`: read-only diagnostics (routes/config/connectors/optional-peers/health/release-hygiene) with a pass/warn/fail report and non-zero exit on failure.
|
|
47
47
|
- [warlock-routes](warlock-routes/SKILL.md) — `warlock routes`: list the registered HTTP routes as a verb-colored table (method/path/name/action/middleware/source); filter by method/path/name or emit JSON. Read-only, no connectors.
|
|
48
|
-
- [write-cli-command](write-cli-command/SKILL.md) — author a custom `warlock <cmd>` via the `command()` factory
|
|
48
|
+
- [write-cli-command](write-cli-command/SKILL.md) — author a custom `warlock <cmd>` via the `command()` factory, or inspect built-in `warlock add` feature scaffolding such as the Web starter.
|
|
49
49
|
- [run-app](run-app/SKILL.md) — `warlock dev` / `warlock build` / `warlock start` operational commands.
|
|
50
50
|
- [update-packages](update-packages/SKILL.md) — bump every `@warlock.js/*` dependency with `warlock update`.
|
|
51
51
|
|
|
@@ -12,7 +12,7 @@ A controller is a thin function: pull inputs from `request`, call work, return t
|
|
|
12
12
|
```ts title="src/app/<module>/controllers/<action>.controller.ts"
|
|
13
13
|
import { type RequestHandler } from "@warlock.js/core";
|
|
14
14
|
|
|
15
|
-
export const listProductsController: RequestHandler = async (request, response) => {
|
|
15
|
+
export const listProductsController: RequestHandler = async ({ request, response }) => {
|
|
16
16
|
return response.success({ products: [] });
|
|
17
17
|
};
|
|
18
18
|
```
|
|
@@ -67,10 +67,10 @@ import { type Request, type RequestHandler } from "@warlock.js/core";
|
|
|
67
67
|
import { type CreateProductSchema, createProductSchema } from "../schema/create-product.schema";
|
|
68
68
|
import { createProductService } from "../services/create-product.service";
|
|
69
69
|
|
|
70
|
-
export const createProductController: RequestHandler<Request<CreateProductSchema>> = async (
|
|
70
|
+
export const createProductController: RequestHandler<Request<CreateProductSchema>> = async ({
|
|
71
71
|
request,
|
|
72
72
|
response,
|
|
73
|
-
) => {
|
|
73
|
+
}) => {
|
|
74
74
|
const product = await createProductService(request.validated());
|
|
75
75
|
|
|
76
76
|
return response.successCreate({ product });
|
|
@@ -96,10 +96,10 @@ Routes behind `authMiddleware` need `request.user` typed. Project conventions ad
|
|
|
96
96
|
import { type GuardedRequestHandler } from "app/auth/requests/guarded.request";
|
|
97
97
|
import { type CreateProductSchema, createProductSchema } from "../schema/create-product.schema";
|
|
98
98
|
|
|
99
|
-
export const createProductController: GuardedRequestHandler<CreateProductSchema> = async (
|
|
99
|
+
export const createProductController: GuardedRequestHandler<CreateProductSchema> = async ({
|
|
100
100
|
request,
|
|
101
101
|
response,
|
|
102
|
-
) => {
|
|
102
|
+
}) => {
|
|
103
103
|
// request.user is typed
|
|
104
104
|
const product = await createProductService(request.validated());
|
|
105
105
|
return response.successCreate({ product });
|
|
@@ -146,7 +146,7 @@ If your controller is over ~30 lines, the work probably belongs in a service.
|
|
|
146
146
|
import { type RequestHandler } from "@warlock.js/core";
|
|
147
147
|
import { listProductsService } from "../services/list-products.service";
|
|
148
148
|
|
|
149
|
-
export const listProductsController: RequestHandler = async (request, response) => {
|
|
149
|
+
export const listProductsController: RequestHandler = async ({ request, response }) => {
|
|
150
150
|
const { data: products, pagination } = await listProductsService({
|
|
151
151
|
...request.all(),
|
|
152
152
|
organization_id: request.user.organizationId,
|
|
@@ -163,10 +163,10 @@ import { type Request, type RequestHandler } from "@warlock.js/core";
|
|
|
163
163
|
import { type CreateProductSchema, createProductSchema } from "../schema/create-product.schema";
|
|
164
164
|
import { createProductService } from "../services/create-product.service";
|
|
165
165
|
|
|
166
|
-
export const createProductController: RequestHandler<Request<CreateProductSchema>> = async (
|
|
166
|
+
export const createProductController: RequestHandler<Request<CreateProductSchema>> = async ({
|
|
167
167
|
request,
|
|
168
168
|
response,
|
|
169
|
-
) => {
|
|
169
|
+
}) => {
|
|
170
170
|
const product = await createProductService(request.validated());
|
|
171
171
|
|
|
172
172
|
return response.successCreate({ product });
|
|
@@ -200,7 +200,7 @@ export async function getProductService(id: string) {
|
|
|
200
200
|
import type { RequestHandler } from "@warlock.js/core";
|
|
201
201
|
import { getProductService } from "../services/get-product.service";
|
|
202
202
|
|
|
203
|
-
export const getProductController: RequestHandler = async (request, response) => {
|
|
203
|
+
export const getProductController: RequestHandler = async ({ request, response }) => {
|
|
204
204
|
const product = await getProductService(request.input("id"));
|
|
205
205
|
|
|
206
206
|
return response.success({ product });
|
|
@@ -10,9 +10,9 @@ description: 'Send HTTP responses via @warlock.js/core''s Response helpers — s
|
|
|
10
10
|
## The shape
|
|
11
11
|
|
|
12
12
|
```ts
|
|
13
|
-
import type { RequestHandler
|
|
13
|
+
import type { RequestHandler } from "@warlock.js/core";
|
|
14
14
|
|
|
15
|
-
export const myController: RequestHandler = async (request, response
|
|
15
|
+
export const myController: RequestHandler = async ({ request, response }) => {
|
|
16
16
|
// …choose a helper and return it
|
|
17
17
|
return response.success({ data: "…" });
|
|
18
18
|
};
|
|
@@ -22,11 +22,11 @@ Always `return response.<helper>(...)`. The return value drives Fastify's send.
|
|
|
22
22
|
|
|
23
23
|
## Success helpers
|
|
24
24
|
|
|
25
|
-
| Method
|
|
26
|
-
|
|
|
27
|
-
| `response.success(data?)`
|
|
28
|
-
| `response.successCreate(data)`
|
|
29
|
-
| `response.noContent()`
|
|
25
|
+
| Method | Status | When |
|
|
26
|
+
| ------------------------------ | ------ | -------------------------------- |
|
|
27
|
+
| `response.success(data?)` | 200 | normal read / update |
|
|
28
|
+
| `response.successCreate(data)` | 201 | resource created (POST) |
|
|
29
|
+
| `response.noContent()` | 204 | delete succeeded, no body needed |
|
|
30
30
|
|
|
31
31
|
```ts
|
|
32
32
|
return response.success({ products: [...] });
|
|
@@ -40,13 +40,13 @@ return response.noContent();
|
|
|
40
40
|
|
|
41
41
|
## Client-error helpers
|
|
42
42
|
|
|
43
|
-
| Method
|
|
44
|
-
|
|
|
45
|
-
| `response.badRequest(data)`
|
|
46
|
-
| `response.unauthorized(data?)`
|
|
47
|
-
| `response.forbidden(data?)`
|
|
48
|
-
| `response.notFound(data?)`
|
|
49
|
-
| `response.conflict(data?)`
|
|
43
|
+
| Method | Status | When |
|
|
44
|
+
| ------------------------------ | ------ | ------------------------------------ |
|
|
45
|
+
| `response.badRequest(data)` | 400 | malformed or invalid input |
|
|
46
|
+
| `response.unauthorized(data?)` | 401 | missing/invalid auth token |
|
|
47
|
+
| `response.forbidden(data?)` | 403 | authenticated but not allowed |
|
|
48
|
+
| `response.notFound(data?)` | 404 | record missing |
|
|
49
|
+
| `response.conflict(data?)` | 409 | uniqueness violation, state conflict |
|
|
50
50
|
|
|
51
51
|
```ts
|
|
52
52
|
return response.badRequest({ error: t("validation.invalid") });
|
|
@@ -65,8 +65,8 @@ Most error helpers accept an optional payload — if you omit it, they send a de
|
|
|
65
65
|
## Redirects
|
|
66
66
|
|
|
67
67
|
```ts
|
|
68
|
-
return response.redirect("/login");
|
|
69
|
-
return response.redirect("/new-home", 301);
|
|
68
|
+
return response.redirect("/login"); // 302
|
|
69
|
+
return response.redirect("/new-home", 301); // permanent
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
## Files
|
|
@@ -98,19 +98,19 @@ stream.end();
|
|
|
98
98
|
|
|
99
99
|
## Throwing HTTP errors
|
|
100
100
|
|
|
101
|
-
Most of the time, controllers don't need to
|
|
101
|
+
Most of the time, controllers don't need to _choose_ an error helper — they throw from the service layer instead. The request middleware (`http/middleware/inject-request-context.ts`) catches every `HttpError` subclass and produces the matching response. The error classes mirror the helpers above:
|
|
102
102
|
|
|
103
103
|
```ts
|
|
104
104
|
import {
|
|
105
|
-
ResourceNotFoundError,
|
|
106
|
-
UnAuthorizedError,
|
|
107
|
-
ForbiddenError,
|
|
108
|
-
BadRequestError,
|
|
109
|
-
ConflictError,
|
|
110
|
-
NotAcceptableError,
|
|
111
|
-
NotAllowedError,
|
|
112
|
-
ServerError,
|
|
113
|
-
HttpError,
|
|
105
|
+
ResourceNotFoundError, // 404
|
|
106
|
+
UnAuthorizedError, // 401
|
|
107
|
+
ForbiddenError, // 403
|
|
108
|
+
BadRequestError, // 400
|
|
109
|
+
ConflictError, // 409
|
|
110
|
+
NotAcceptableError, // 406
|
|
111
|
+
NotAllowedError, // 405
|
|
112
|
+
ServerError, // 500
|
|
113
|
+
HttpError, // base class — `new HttpError(status, message, payload?)` for arbitrary codes
|
|
114
114
|
} from "@warlock.js/core";
|
|
115
115
|
|
|
116
116
|
throw new ResourceNotFoundError("product.notFound");
|
|
@@ -118,13 +118,27 @@ throw new ForbiddenError("permission.denied", { resource: "product", id });
|
|
|
118
118
|
throw new ConflictError("user.duplicateEmail");
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
-
Each class takes `(message, payload?)`. The
|
|
121
|
+
Each class takes `(message, payload?)`. The framework keeps the optional
|
|
122
|
+
detail nested under `payload`:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"error": "Product not found",
|
|
127
|
+
"payload": { "id": 42 }
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Every unhandled error response also carries
|
|
132
|
+
`Cache-Control: private, no-store`, regardless of status. The floor is
|
|
133
|
+
applied once at the shared error funnel, so API-route errors cannot be stored
|
|
134
|
+
and replayed across users. In development mode, the stack trace is included
|
|
135
|
+
too.
|
|
122
136
|
|
|
123
137
|
Pick the class, throw from the service or use-case, and forget about response shaping at the call site. The controller stays focused on the success path:
|
|
124
138
|
|
|
125
139
|
```ts
|
|
126
|
-
export const getProductController: RequestHandler = async (request, response) => {
|
|
127
|
-
const product = await getProductService(request.input("id"));
|
|
140
|
+
export const getProductController: RequestHandler = async ({ request, response }) => {
|
|
141
|
+
const product = await getProductService(request.input("id")); // throws ResourceNotFoundError on miss
|
|
128
142
|
return response.success({ product });
|
|
129
143
|
};
|
|
130
144
|
```
|
|
@@ -136,9 +150,9 @@ See [`create-controller`](../create-controller/SKILL.md) for the "throw from ser
|
|
|
136
150
|
```ts
|
|
137
151
|
const sse = response.sse();
|
|
138
152
|
|
|
139
|
-
sse.send("tick", { count: 1 });
|
|
140
|
-
sse.send("tick", { count: 2 }, "msg-2");
|
|
141
|
-
sse.comment("keep-alive");
|
|
153
|
+
sse.send("tick", { count: 1 }); // event name, data, optional id
|
|
154
|
+
sse.send("tick", { count: 2 }, "msg-2"); // third arg is the SSE event id
|
|
155
|
+
sse.comment("keep-alive"); // invisible to the client, prevents timeout
|
|
142
156
|
sse.end();
|
|
143
157
|
```
|
|
144
158
|
|
|
@@ -178,11 +192,11 @@ response.clearCookie("session_id");
|
|
|
178
192
|
|
|
179
193
|
Every response cookie gets `httpOnly: true`, `sameSite: "lax"`, and `secure: true` outside development, unless you override them:
|
|
180
194
|
|
|
181
|
-
| Flag
|
|
182
|
-
|
|
183
|
-
| `httpOnly` | `true`
|
|
184
|
-
| `sameSite` | `"lax"`
|
|
185
|
-
| `secure`
|
|
195
|
+
| Flag | Default | Why it's the default |
|
|
196
|
+
| ---------- | ----------------------------- | --------------------------------------------------------- |
|
|
197
|
+
| `httpOnly` | `true` | without it, any injected script can read the cookie |
|
|
198
|
+
| `sameSite` | `"lax"` | without it, the cookie rides along on cross-site requests |
|
|
199
|
+
| `secure` | `true`, except in development | without it, the cookie travels in cleartext |
|
|
186
200
|
|
|
187
201
|
`secure` is relaxed in development only — browsers drop a `Secure` cookie over plain http, which would silently break every local login. It stays on in test and staging.
|
|
188
202
|
|
|
@@ -345,12 +345,17 @@ Set `STORAGE_DRIVER=r2` in production, leave unset in dev — same code uses loc
|
|
|
345
345
|
### Uploading a request file
|
|
346
346
|
|
|
347
347
|
```ts
|
|
348
|
-
import
|
|
348
|
+
import { type GuardedRequestHandler } from "app/auth/requests/guarded.request";
|
|
349
349
|
import { storage } from "@warlock.js/core";
|
|
350
350
|
|
|
351
|
-
export const uploadAvatarController:
|
|
351
|
+
export const uploadAvatarController: GuardedRequestHandler = async ({ request, response }) => {
|
|
352
352
|
const upload = request.file("avatar");
|
|
353
|
-
|
|
353
|
+
|
|
354
|
+
if (!upload) {
|
|
355
|
+
return response.badRequest({ error: "missing file" });
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const file = await storage.put(upload, `avatars/${request.user.id}/${upload.name}`);
|
|
354
359
|
|
|
355
360
|
return response.successCreate({ url: file.url, hash: file.hash });
|
|
356
361
|
};
|
|
@@ -9,8 +9,8 @@ Multipart uploads come in as `UploadedFile` instances. The class wraps Fastify's
|
|
|
9
9
|
|
|
10
10
|
## The shape
|
|
11
11
|
|
|
12
|
-
```ts title="src/app/uploads/schema/
|
|
13
|
-
import { v } from "@warlock.js/seal";
|
|
12
|
+
```ts title="src/app/uploads/schema/upload-avatar.schema.ts"
|
|
13
|
+
import { v, type Infer } from "@warlock.js/seal";
|
|
14
14
|
|
|
15
15
|
export const uploadAvatarSchema = v.object({
|
|
16
16
|
avatar: v
|
|
@@ -19,16 +19,18 @@ export const uploadAvatarSchema = v.object({
|
|
|
19
19
|
.maxSize({ unit: "MB", size: 5 })
|
|
20
20
|
.mimeType(["image/jpeg", "image/png", "image/webp"]),
|
|
21
21
|
});
|
|
22
|
+
|
|
23
|
+
export type UploadAvatarSchema = Infer<typeof uploadAvatarSchema>;
|
|
22
24
|
```
|
|
23
25
|
|
|
24
26
|
```ts title="src/app/uploads/controllers/upload-avatar.controller.ts"
|
|
25
|
-
import { type GuardedRequestHandler } from "app/auth/
|
|
27
|
+
import { type GuardedRequestHandler } from "app/auth/requests/guarded.request";
|
|
26
28
|
import { type UploadAvatarSchema, uploadAvatarSchema } from "../schema/upload-avatar.schema";
|
|
27
29
|
|
|
28
|
-
export const uploadAvatarController: GuardedRequestHandler<UploadAvatarSchema> = async (
|
|
30
|
+
export const uploadAvatarController: GuardedRequestHandler<UploadAvatarSchema> = async ({
|
|
29
31
|
request,
|
|
30
32
|
response,
|
|
31
|
-
) => {
|
|
33
|
+
}) => {
|
|
32
34
|
const { avatar } = request.validated();
|
|
33
35
|
|
|
34
36
|
const file = await avatar
|
|
@@ -54,7 +56,7 @@ Inside a controller:
|
|
|
54
56
|
```ts
|
|
55
57
|
import type { RequestHandler, UploadedFile } from "@warlock.js/core";
|
|
56
58
|
|
|
57
|
-
export const uploadController: RequestHandler = async (request, response) => {
|
|
59
|
+
export const uploadController: RequestHandler = async ({ request, response }) => {
|
|
58
60
|
// option A — direct from request, no validation
|
|
59
61
|
const file: UploadedFile | undefined = request.file("avatar");
|
|
60
62
|
|
|
@@ -236,7 +238,7 @@ export const uploadFilesSchema = v.object({
|
|
|
236
238
|
```ts title="src/app/uploads/controllers/create-upload.controller.ts"
|
|
237
239
|
import type { RequestHandler } from "@warlock.js/core";
|
|
238
240
|
|
|
239
|
-
export const createUploadController: RequestHandler = async (request, response) => {
|
|
241
|
+
export const createUploadController: RequestHandler = async ({ request, response }) => {
|
|
240
242
|
const { files } = request.validated();
|
|
241
243
|
|
|
242
244
|
const saved = await Promise.all(
|
|
@@ -232,9 +232,9 @@ const template = await readFile(appPath("mailers/templates/welcome.html"), "utf-
|
|
|
232
232
|
### Health endpoint
|
|
233
233
|
|
|
234
234
|
```ts title="src/app/system/controllers/health.controller.ts"
|
|
235
|
-
import { Application, type RequestHandler
|
|
235
|
+
import { Application, type RequestHandler } from "@warlock.js/core";
|
|
236
236
|
|
|
237
|
-
export const healthController: RequestHandler = async (
|
|
237
|
+
export const healthController: RequestHandler = async ({ response }) => {
|
|
238
238
|
return response.success({
|
|
239
239
|
status: "ok",
|
|
240
240
|
environment: Application.environment,
|
|
@@ -229,10 +229,14 @@ The error's message is locale-aware — the `inject-request-context` middleware
|
|
|
229
229
|
### Translated response message in a controller
|
|
230
230
|
|
|
231
231
|
```ts
|
|
232
|
-
|
|
232
|
+
import { type GuardedRequestHandler } from "app/auth/requests/guarded.request";
|
|
233
|
+
import { type CreateProductSchema } from "../schema/create-product.schema";
|
|
234
|
+
import { createProductService } from "../services/create-product.service";
|
|
235
|
+
|
|
236
|
+
export const createProductController: GuardedRequestHandler<CreateProductSchema> = async ({
|
|
233
237
|
request,
|
|
234
238
|
response,
|
|
235
|
-
) => {
|
|
239
|
+
}) => {
|
|
236
240
|
const product = await createProductService(request.validated());
|
|
237
241
|
return response.success({
|
|
238
242
|
message: request.t("products.created"),
|
|
@@ -276,10 +276,10 @@ export async function listFaqsService(filters: FaqListOptions) {
|
|
|
276
276
|
```
|
|
277
277
|
|
|
278
278
|
```ts title="src/app/faqs/controllers/list-faqs.controller.ts"
|
|
279
|
-
import
|
|
279
|
+
import { type GuardedRequestHandler } from "app/auth/requests/guarded.request";
|
|
280
280
|
import { listFaqsService } from "../services/list-faqs.service";
|
|
281
281
|
|
|
282
|
-
export const listFaqsController:
|
|
282
|
+
export const listFaqsController: GuardedRequestHandler = async ({ request, response }) => {
|
|
283
283
|
const { data, pagination } = await listFaqsService({
|
|
284
284
|
...request.all(),
|
|
285
285
|
organization_id: request.user.organizationId,
|
|
@@ -30,7 +30,7 @@ public locals: RequestLocals = {};
|
|
|
30
30
|
```ts title="src/app/observability/middleware/request-timing.middleware.ts"
|
|
31
31
|
import type { Middleware } from "@warlock.js/core";
|
|
32
32
|
|
|
33
|
-
export const requestTimingMiddleware: Middleware = request => {
|
|
33
|
+
export const requestTimingMiddleware: Middleware = (request) => {
|
|
34
34
|
request.startedAt = Date.now();
|
|
35
35
|
};
|
|
36
36
|
```
|
|
@@ -38,7 +38,7 @@ export const requestTimingMiddleware: Middleware = request => {
|
|
|
38
38
|
```ts title="src/app/observability/controllers/timing.controller.ts"
|
|
39
39
|
import type { RequestHandler } from "@warlock.js/core";
|
|
40
40
|
|
|
41
|
-
export const timingController: RequestHandler = async (request, response) => {
|
|
41
|
+
export const timingController: RequestHandler = async ({ request, response }) => {
|
|
42
42
|
return response.success({ elapsedMs: Date.now() - request.startedAt });
|
|
43
43
|
};
|
|
44
44
|
```
|
|
@@ -123,7 +123,7 @@ Feature-local files such as `src/app/organizations/request-locals.d.ts` are equa
|
|
|
123
123
|
|
|
124
124
|
Two rules the scaffold's own comments spell out, and both bite silently:
|
|
125
125
|
|
|
126
|
-
- **Keep the trailing `export {}`.** `declare module "x"` inside a file with no top-level import or export declares an
|
|
126
|
+
- **Keep the trailing `export {}`.** `declare module "x"` inside a file with no top-level import or export declares an _ambient_ module, which REPLACES `@warlock.js/core`'s real typings instead of merging into them — every framework export vanishes. The `export {}` is what makes the file a module and the block an augmentation. It is not an unused statement to clean up.
|
|
127
127
|
- **Keep them `interface`, not `type`.** This project otherwise prefers `type`; these are the named exception, because declaration merging is interface-only. `type RequestUser = { ... }` is a duplicate-identifier error, not an augmentation.
|
|
128
128
|
|
|
129
129
|
On a project scaffolded before 5.1 there is no `src/typings.d.ts`, and `tsconfig.json` carries `"typeRoots": ["./src/typings.d.ts"]` — wrong twice, since `typeRoots` takes directories of `@types` packages rather than files, and that file did not exist. Drop the `typeRoots` entry, create the file, and list it under `include`.
|
|
@@ -26,10 +26,10 @@ import type { Request, RequestHandler } from "@warlock.js/core";
|
|
|
26
26
|
import { type CreateProductSchema, createProductSchema } from "../schema/create-product.schema";
|
|
27
27
|
import { createProductService } from "../services/create-product.service";
|
|
28
28
|
|
|
29
|
-
export const createProductController: RequestHandler<Request<CreateProductSchema>> = async (
|
|
29
|
+
export const createProductController: RequestHandler<Request<CreateProductSchema>> = async ({
|
|
30
30
|
request,
|
|
31
31
|
response,
|
|
32
|
-
) => {
|
|
32
|
+
}) => {
|
|
33
33
|
const product = await createProductService(request.validated());
|
|
34
34
|
|
|
35
35
|
return response.successCreate({ product });
|
|
@@ -38,11 +38,11 @@ This skill is the foundation. Every other warlock skill (`register-route`, `crea
|
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
```ts title="src/app/<module>/controllers/create-<thing>.controller.ts"
|
|
41
|
-
import { type GuardedRequestHandler } from "app/auth/
|
|
41
|
+
import { type GuardedRequestHandler } from "app/auth/requests/guarded.request";
|
|
42
42
|
import { type CreateThingSchema, createThingSchema } from "../schema/create-thing.schema";
|
|
43
43
|
import { createThingService } from "../services/create-thing.service";
|
|
44
44
|
|
|
45
|
-
export const createThingController: GuardedRequestHandler<CreateThingSchema> = async (request, response) => {
|
|
45
|
+
export const createThingController: GuardedRequestHandler<CreateThingSchema> = async ({ request, response }) => {
|
|
46
46
|
const thing = await createThingService(request.validated());
|
|
47
47
|
return response.success({ thing });
|
|
48
48
|
};
|
|
@@ -123,10 +123,10 @@ export async function notifyUserService(user: User, payload: unknown) {
|
|
|
123
123
|
Then from a controller:
|
|
124
124
|
|
|
125
125
|
```ts
|
|
126
|
-
import type { GuardedRequestHandler } from "app/auth/
|
|
126
|
+
import type { GuardedRequestHandler } from "app/auth/requests/guarded.request";
|
|
127
127
|
import { notifyUserService } from "../services/notify-user.service";
|
|
128
128
|
|
|
129
|
-
export const sendNotificationController: GuardedRequestHandler = async (request, response) => {
|
|
129
|
+
export const sendNotificationController: GuardedRequestHandler = async ({ request, response }) => {
|
|
130
130
|
await notifyUserService(request.user, request.input("payload"));
|
|
131
131
|
return response.success({ delivered: true });
|
|
132
132
|
};
|
|
@@ -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/`. Triggers: `command`, `CLICommand`, `CLICommandPreload`, `CLICommandOption`, `preload`, `preAction`, `persistent`, `colors`; "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`, `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
|
|
@@ -205,6 +205,7 @@ warlock generate (alias: g) — interactive picker
|
|
|
205
205
|
| Feature | Installs |
|
|
206
206
|
| ------------- | --------------------------------------------------------------------------------------------------- |
|
|
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
|
+
| `web` | Warlock Web + React stack; scaffolds the application page, localization, contact endpoint, and Web configuration |
|
|
208
209
|
| `react` | `react` + `react-dom` + types |
|
|
209
210
|
| `image` | `sharp` (for the `Image` class) |
|
|
210
211
|
| `mail` | `nodemailer` + types |
|
|
@@ -222,6 +223,8 @@ warlock generate (alias: g) — interactive picker
|
|
|
222
223
|
|
|
223
224
|
Run `warlock add --list` to see what's currently registered. Pass `--packageManager <yarn\|pnpm\|npm>` to override auto-detection (defaults to whichever lockfile is present).
|
|
224
225
|
|
|
226
|
+
The Web starter keeps the page's universal non-component setup in `src/web/index.register.ts`, while `src/web/index.page.tsx` re-exports `register`. Warlock still discovers and runs the hook from the page namespace in both SSR and the browser, but editing the page component remains inside React's Fast Refresh boundary. Do not move the `register()` declaration inline beside the component: React Refresh treats that non-component export as incompatible and reloads the page, losing component state.
|
|
227
|
+
|
|
225
228
|
### Misc
|
|
226
229
|
|
|
227
230
|
| Command | Purpose |
|