@warlock.js/core 5.34.2 → 5.35.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 +62 -0
- package/esm/cli/cli-commands.manager.mjs +1 -1
- package/esm/cli/cli-commands.manager.mjs.map +1 -1
- package/esm/cli/commands/add.command.mjs +1 -0
- package/esm/cli/commands/add.command.mjs.map +1 -1
- package/esm/cli/commands/build.command.mjs +30 -3
- package/esm/cli/commands/build.command.mjs.map +1 -1
- package/esm/cli/commands/check/check-command.action.mjs +1 -0
- package/esm/cli/commands/check/check-command.action.mjs.map +1 -1
- package/esm/cli/commands/check.command.mjs +5 -0
- package/esm/cli/commands/check.command.mjs.map +1 -1
- package/esm/cli/commands/explain/explain-command.action.mjs +21 -0
- package/esm/cli/commands/explain/explain-command.action.mjs.map +1 -1
- package/esm/cli/commands/explain.command.mjs +14 -4
- package/esm/cli/commands/explain.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generate.command.mjs +19 -1
- package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/endpoint.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/endpoint.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/web.generator.mjs +169 -0
- package/esm/cli/commands/generate/generators/web.generator.mjs.map +1 -0
- package/esm/cli/commands/generate/templates/web-stubs.mjs +231 -0
- package/esm/cli/commands/generate/templates/web-stubs.mjs.map +1 -0
- package/esm/cli/commands/inspect/inspect-command.action.mjs +4 -3
- package/esm/cli/commands/inspect/inspect-command.action.mjs.map +1 -1
- package/esm/cli/commands/inspect.command.mjs +13 -8
- package/esm/cli/commands/inspect.command.mjs.map +1 -1
- package/esm/cli/commands/mcp/mcp-command.action.mjs +1 -1
- package/esm/cli/commands/mcp/mcp-command.action.mjs.map +1 -1
- package/esm/config/config-loader.d.mts.map +1 -1
- package/esm/config/config-loader.mjs +2 -0
- package/esm/config/config-loader.mjs.map +1 -1
- package/esm/config/load-config-files.mjs +4 -0
- package/esm/config/load-config-files.mjs.map +1 -1
- package/esm/dev-server/file-event-handler.mjs +11 -1
- package/esm/dev-server/file-event-handler.mjs.map +1 -1
- package/esm/dev-server/file-manager.d.mts +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/files-orchestrator.mjs +16 -1
- package/esm/dev-server/files-orchestrator.mjs.map +1 -1
- package/esm/dev-server/files-watcher.mjs +13 -4
- package/esm/dev-server/files-watcher.mjs.map +1 -1
- package/esm/dev-server/loader/loader-port-acks.mjs +21 -9
- package/esm/dev-server/loader/loader-port-acks.mjs.map +1 -1
- package/esm/dev-server/loader/register-loader.mjs +31 -12
- package/esm/dev-server/loader/register-loader.mjs.map +1 -1
- package/esm/dev-server/module-loader.mjs +14 -0
- package/esm/dev-server/module-loader.mjs.map +1 -1
- package/esm/dev-server/special-file-patterns.mjs +4 -1
- package/esm/dev-server/special-file-patterns.mjs.map +1 -1
- package/esm/e2e/browser.d.mts.map +1 -1
- package/esm/e2e/browser.mjs +3 -1
- package/esm/e2e/browser.mjs.map +1 -1
- package/esm/e2e/index.d.mts +2 -2
- package/esm/e2e/page.d.mts +6 -1
- package/esm/e2e/page.d.mts.map +1 -1
- package/esm/e2e/page.mjs +36 -1
- package/esm/e2e/page.mjs.map +1 -1
- package/esm/e2e/types.d.mts +37 -0
- package/esm/e2e/types.d.mts.map +1 -1
- package/esm/e2e/types.mjs.map +1 -1
- package/esm/errors/error-codes.d.mts +48 -0
- package/esm/errors/error-codes.d.mts.map +1 -1
- package/esm/errors/error-codes.mjs +48 -0
- package/esm/errors/error-codes.mjs.map +1 -1
- package/esm/errors/index.d.mts +2 -2
- package/esm/errors/index.mjs +2 -2
- package/esm/generations/add-command.action.mjs +2 -0
- package/esm/generations/add-command.action.mjs.map +1 -1
- package/esm/generations/design-system-payload.mjs +53 -0
- package/esm/generations/design-system-payload.mjs.map +1 -0
- package/esm/generations/features/design-system.feature.mjs +38 -0
- package/esm/generations/features/design-system.feature.mjs.map +1 -0
- package/esm/generations/features/index.mjs +3 -1
- package/esm/generations/features/index.mjs.map +1 -1
- package/esm/generations/features/notifications.feature.mjs +13 -2
- package/esm/generations/features/notifications.feature.mjs.map +1 -1
- package/esm/generations/features/test.feature.mjs +48 -13
- package/esm/generations/features/test.feature.mjs.map +1 -1
- package/esm/generations/stubs.mjs +9 -5
- package/esm/generations/stubs.mjs.map +1 -1
- package/esm/http/error-mapper.d.mts +1 -1
- package/esm/http/error-mapper.mjs +3 -3
- package/esm/http/error-mapper.mjs.map +1 -1
- package/esm/http/error-response.d.mts +15 -0
- package/esm/http/error-response.d.mts.map +1 -0
- package/esm/http/error-response.mjs +47 -0
- package/esm/http/error-response.mjs.map +1 -0
- package/esm/http/errors/errors.d.mts +8 -0
- package/esm/http/errors/errors.d.mts.map +1 -1
- package/esm/http/errors/errors.mjs +13 -1
- package/esm/http/errors/errors.mjs.map +1 -1
- package/esm/http/index.d.mts +5 -3
- package/esm/http/index.mjs +3 -2
- package/esm/http/list-endpoint.d.mts +35 -0
- package/esm/http/list-endpoint.d.mts.map +1 -0
- package/esm/http/list-endpoint.mjs +28 -0
- package/esm/http/list-endpoint.mjs.map +1 -0
- package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
- package/esm/http/middleware/inject-request-context.mjs +12 -2
- package/esm/http/middleware/inject-request-context.mjs.map +1 -1
- package/esm/http/response.d.mts +2 -0
- package/esm/http/response.d.mts.map +1 -1
- package/esm/http/response.mjs +2 -0
- package/esm/http/response.mjs.map +1 -1
- package/esm/http/server.d.mts.map +1 -1
- package/esm/http/server.mjs +2 -0
- package/esm/http/server.mjs.map +1 -1
- package/esm/http/types.d.mts +7 -0
- package/esm/http/types.d.mts.map +1 -1
- package/esm/index.d.mts +10 -7
- package/esm/index.mjs +3 -2
- package/esm/openapi/security.mjs +12 -4
- package/esm/openapi/security.mjs.map +1 -1
- package/esm/openapi/types.d.mts +2 -0
- package/esm/openapi/types.d.mts.map +1 -1
- package/esm/production/build-budgets.d.mts +46 -0
- package/esm/production/build-budgets.d.mts.map +1 -0
- package/esm/production/build-budgets.mjs +109 -0
- package/esm/production/build-budgets.mjs.map +1 -0
- package/esm/production/production-builder.mjs +13 -1
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/production/resolve-build-config.d.mts +1 -1
- package/esm/production/resolve-build-config.d.mts.map +1 -1
- package/esm/production/resolve-build-config.mjs.map +1 -1
- package/esm/restful/restful.d.mts +1 -1
- package/esm/tests/fakes/storage-fake.d.mts +3 -1
- package/esm/tests/fakes/storage-fake.d.mts.map +1 -1
- package/esm/tests/fakes/storage-fake.mjs +3 -3
- package/esm/tests/fakes/storage-fake.mjs.map +1 -1
- package/esm/tests/fakes/test-fakes.d.mts +3 -1
- package/esm/tests/fakes/test-fakes.d.mts.map +1 -1
- package/esm/tests/fakes/test-fakes.mjs +3 -2
- package/esm/tests/fakes/test-fakes.mjs.map +1 -1
- package/esm/tests/index.d.mts +4 -2
- package/esm/tests/index.mjs +3 -1
- package/esm/tests/load-unit-config.mjs +24 -0
- package/esm/tests/load-unit-config.mjs.map +1 -0
- package/esm/tests/response-contract.d.mts +32 -0
- package/esm/tests/response-contract.d.mts.map +1 -0
- package/esm/tests/response-contract.mjs +116 -0
- package/esm/tests/response-contract.mjs.map +1 -0
- package/esm/tests/start-http-development-server.d.mts.map +1 -1
- package/esm/tests/start-http-development-server.mjs +10 -2
- package/esm/tests/start-http-development-server.mjs.map +1 -1
- package/esm/tests/test-lifecycle-state.mjs.map +1 -1
- package/esm/tests/test-locales.mjs +33 -0
- package/esm/tests/test-locales.mjs.map +1 -0
- package/esm/tests/test-runtime-scope.d.mts +15 -0
- package/esm/tests/test-runtime-scope.d.mts.map +1 -0
- package/esm/tests/test-runtime-scope.mjs +80 -0
- package/esm/tests/test-runtime-scope.mjs.map +1 -0
- package/esm/tests/vitest-setup.d.mts +13 -3
- package/esm/tests/vitest-setup.d.mts.map +1 -1
- package/esm/tests/vitest-setup.mjs +146 -18
- package/esm/tests/vitest-setup.mjs.map +1 -1
- package/esm/tooling/check/affected-files.d.mts +5 -0
- package/esm/tooling/check/affected-files.d.mts.map +1 -0
- package/esm/tooling/check/affected-files.mjs +56 -0
- package/esm/tooling/check/affected-files.mjs.map +1 -0
- package/esm/tooling/check/changed-files.d.mts +2 -0
- package/esm/tooling/check/changed-files.d.mts.map +1 -1
- package/esm/tooling/check/changed-files.mjs +2 -2
- package/esm/tooling/check/changed-files.mjs.map +1 -1
- package/esm/tooling/check/conventions-step.d.mts +2 -1
- package/esm/tooling/check/conventions-step.d.mts.map +1 -1
- package/esm/tooling/check/conventions-step.mjs +48 -6
- package/esm/tooling/check/conventions-step.mjs.map +1 -1
- package/esm/tooling/check/fixes.d.mts +8 -0
- package/esm/tooling/check/fixes.d.mts.map +1 -0
- package/esm/tooling/check/fixes.mjs +31 -0
- package/esm/tooling/check/fixes.mjs.map +1 -0
- package/esm/tooling/check/index.d.mts +12 -0
- package/esm/tooling/check/index.mjs +3 -2
- package/esm/tooling/check/run-check.d.mts +2 -0
- package/esm/tooling/check/run-check.d.mts.map +1 -1
- package/esm/tooling/check/run-check.mjs +20 -4
- package/esm/tooling/check/run-check.mjs.map +1 -1
- package/esm/tooling/check/run-tests.mjs +1 -1
- package/esm/tooling/check/source-ast.d.mts +1 -0
- package/esm/tooling/check/web-conventions.d.mts +10 -0
- package/esm/tooling/check/web-conventions.d.mts.map +1 -0
- package/esm/tooling/check/web-conventions.mjs +126 -0
- package/esm/tooling/check/web-conventions.mjs.map +1 -0
- package/esm/tooling/index.d.mts +7 -2
- package/esm/tooling/index.mjs +4 -2
- package/esm/tooling/inspect/collect-files.mjs +172 -0
- package/esm/tooling/inspect/collect-files.mjs.map +1 -0
- package/esm/tooling/inspect/collect-pages.mjs +3 -2
- package/esm/tooling/inspect/collect-pages.mjs.map +1 -1
- package/esm/tooling/inspect/collect-routes.mjs +1 -1
- package/esm/tooling/inspect/collect-routes.mjs.map +1 -1
- package/esm/tooling/inspect/explain-file.d.mts +12 -0
- package/esm/tooling/inspect/explain-file.d.mts.map +1 -0
- package/esm/tooling/inspect/explain-file.mjs +45 -0
- package/esm/tooling/inspect/explain-file.mjs.map +1 -0
- package/esm/tooling/inspect/index.d.mts +3 -2
- package/esm/tooling/inspect/index.mjs +2 -1
- package/esm/tooling/inspect/inspect-app.d.mts.map +1 -1
- package/esm/tooling/inspect/inspect-app.mjs +9 -3
- package/esm/tooling/inspect/inspect-app.mjs.map +1 -1
- package/esm/tooling/inspect/inspect-report.schema.mjs +34 -1
- package/esm/tooling/inspect/inspect-report.schema.mjs.map +1 -1
- package/esm/tooling/inspect/inspect-report.types.d.mts +17 -1
- package/esm/tooling/inspect/inspect-report.types.d.mts.map +1 -1
- package/esm/tooling/inspect/inspect-report.types.mjs +2 -1
- package/esm/tooling/inspect/inspect-report.types.mjs.map +1 -1
- package/esm/tooling/mcp/mcp-tools.d.mts.map +1 -1
- package/esm/tooling/mcp/mcp-tools.mjs +8 -2
- package/esm/tooling/mcp/mcp-tools.mjs.map +1 -1
- package/esm/tooling/problem.d.mts +4 -0
- package/esm/tooling/problem.d.mts.map +1 -1
- package/esm/tooling/problem.mjs +1 -0
- package/esm/tooling/problem.mjs.map +1 -1
- package/esm/tooling/syntax/ast.d.mts +2 -0
- package/esm/tooling/typecheck/run-typecheck.mjs +5 -1
- package/esm/tooling/typecheck/run-typecheck.mjs.map +1 -1
- package/esm/upgrade/codemods/toolchain-templates.mjs +3 -1
- package/esm/upgrade/codemods/toolchain-templates.mjs.map +1 -1
- package/esm/vite/index.d.mts +3 -1
- package/esm/vite/index.mjs +3 -1
- package/esm/vite/test-setup-reporter.d.mts +16 -0
- package/esm/vite/test-setup-reporter.d.mts.map +1 -0
- package/esm/vite/test-setup-reporter.mjs +55 -0
- package/esm/vite/test-setup-reporter.mjs.map +1 -0
- package/esm/vite/test-workers.d.mts +5 -0
- package/esm/vite/test-workers.d.mts.map +1 -0
- package/esm/vite/test-workers.mjs +38 -0
- package/esm/vite/test-workers.mjs.map +1 -0
- package/esm/warlock-config/index.d.mts +2 -1
- package/esm/warlock-config/types.d.mts +3 -0
- package/esm/warlock-config/types.d.mts.map +1 -1
- package/llms-full.txt +883 -70
- package/llms.txt +58 -56
- package/package.json +15 -14
- package/schemas/inspect-report.schema.json +47 -1
- package/skills/build-budgets/SKILL.md +51 -0
- package/skills/build-restful/SKILL.md +56 -0
- package/skills/configure-app/SKILL.md +34 -0
- package/skills/create-module/SKILL.md +9 -0
- package/skills/generate-openapi/SKILL.md +1 -1
- package/skills/generate-web-ui/SKILL.md +71 -0
- package/skills/index.md +3 -3
- package/skills/run-app/SKILL.md +1 -1
- package/skills/send-mail/SKILL.md +25 -0
- package/skills/send-response/SKILL.md +77 -0
- package/skills/test-browser/SKILL.md +54 -0
- package/skills/test-http/SKILL.md +91 -0
- package/skills/test-service/SKILL.md +161 -3
- package/skills/upgrade-from-5-29/SKILL.md +22 -0
- package/skills/use-localization/SKILL.md +21 -0
- package/skills/warlock-check/SKILL.md +72 -1
- package/skills/warlock-inspect/SKILL.md +45 -2
- package/skills/warlock-mcp/SKILL.md +4 -0
- package/skills/warlock-trace/SKILL.md +2 -2
- package/skills/write-cli-command/SKILL.md +20 -2
- package/esm/production/build-app-production.mjs +0 -14
- package/esm/production/build-app-production.mjs.map +0 -1
package/llms-full.txt
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
## Package orientation
|
|
8
8
|
|
|
9
9
|
---
|
|
10
|
-
description:
|
|
10
|
+
description: 'Warlock application runtime: `router`, `Request`, `Response`, `config`, `env`, `sendMail` and `defineConfig`. Use for "add an API route", controllers, middleware, CLI commands, config, uploads, mail, sockets, OpenAPI, testing or upgrades. Not this package → @warlock.js/web for React SSR pages; @warlock.js/cascade for database queries; @warlock.js/seal for standalone schemas; @warlock.js/auth for login.'
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
# @warlock.js/core
|
|
@@ -26,11 +26,11 @@ When the framework is wrong or missing a capability, follow the `report-framewor
|
|
|
26
26
|
|
|
27
27
|
## Topics by area
|
|
28
28
|
|
|
29
|
-
- **Foundations:** warlock-conventions, create-module, make-endpoint, configure-app, use-app-context, resolve-path, build-url, update-packages, lower-stage3-decorators, upgrade-from-5-29
|
|
29
|
+
- **Foundations:** warlock-conventions, create-module, make-endpoint, configure-app, build-budgets, use-app-context, resolve-path, build-url, update-packages, lower-stage3-decorators, upgrade-from-5-29
|
|
30
30
|
- **HTTP & routing:** register-route, warlock-routes, create-controller, use-middleware, write-middleware, validate-input, use-request-locals, build-restful, wire-socket
|
|
31
31
|
- **Responses & resources:** send-response, define-resource, use-model-transformers, use-localization
|
|
32
32
|
- **Domain layers:** write-use-case, use-repository, write-seeder
|
|
33
|
-
- **CLI & running the app:** run-app, write-cli-command, add-connector, health-checks, warlock-doctor, warlock-inspect, warlock-check, generate-openapi
|
|
33
|
+
- **CLI & running the app:** generate-web-ui, run-app, write-cli-command, add-connector, health-checks, warlock-doctor, warlock-inspect, warlock-check, generate-openapi
|
|
34
34
|
- **Agent tooling:** warlock-mcp, warlock-trace, warlock-agents-md, warlock-board
|
|
35
35
|
- **Storage, files & mail:** store-file, upload-file, process-image, send-mail
|
|
36
36
|
- **Security & utilities:** hash-password, encrypt-data, store-secrets, retry-operation, benchmark-code, request-memo, request-tracing
|
|
@@ -45,7 +45,7 @@ When the framework is wrong or missing a capability, follow the `report-framewor
|
|
|
45
45
|
- Prefer typed model getters over scattered `.get<T>("field")` casts.
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
## add-connector `@warlock.js/core/add-connector/SKILL.md`
|
|
48
|
+
## add-connector `@warlock.js/core/skills/add-connector/SKILL.md`
|
|
49
49
|
|
|
50
50
|
---
|
|
51
51
|
name: add-connector
|
|
@@ -277,7 +277,7 @@ See the [Registering a connector](#registering-a-connector) section above for th
|
|
|
277
277
|
- The `warlock-conventions` topic — module layout, canonical imports.
|
|
278
278
|
|
|
279
279
|
|
|
280
|
-
## benchmark-code `@warlock.js/core/benchmark-code/SKILL.md`
|
|
280
|
+
## benchmark-code `@warlock.js/core/skills/benchmark-code/SKILL.md`
|
|
281
281
|
|
|
282
282
|
---
|
|
283
283
|
name: benchmark-code
|
|
@@ -543,7 +543,62 @@ The `latency` is the _total_ time including retries — useful for the SLO you a
|
|
|
543
543
|
- The `configure-app` topic — wiring `src/config/benchmark.ts`.
|
|
544
544
|
|
|
545
545
|
|
|
546
|
-
## build-
|
|
546
|
+
## build-budgets `@warlock.js/core/skills/build-budgets/SKILL.md`
|
|
547
|
+
|
|
548
|
+
---
|
|
549
|
+
name: build-budgets
|
|
550
|
+
description: "Configure build.budgets in warlock.config.ts, inspect per-page gzip JS/CSS and deferred assets, compare the last build, and enforce limits with warlock build --json. Use for client bundle size regressions and CI transfer-size gates. For field metrics use web measure-web-vitals."
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
# Client build budgets
|
|
554
|
+
|
|
555
|
+
```ts title="warlock.config.ts"
|
|
556
|
+
import { defineConfig } from "@warlock.js/core";
|
|
557
|
+
|
|
558
|
+
export default defineConfig({
|
|
559
|
+
build: {
|
|
560
|
+
budgets: {
|
|
561
|
+
page: { js: "150kb", css: "50kb" },
|
|
562
|
+
pages: { "/admin/**": { js: "220kb" } },
|
|
563
|
+
mode: "error",
|
|
564
|
+
},
|
|
565
|
+
},
|
|
566
|
+
});
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
Run `npx warlock build` for the sorted table, or `npx warlock build --json`
|
|
570
|
+
for `{ success, budgets, error? }`. Progress goes to stderr in JSON mode.
|
|
571
|
+
`budgets` contains `version: 1`, `compression: "gzip"`, `pages` and
|
|
572
|
+
`violations`. API-only builds report an empty page list.
|
|
573
|
+
|
|
574
|
+
Page limits cover the hydration entry, page, layouts, root, setup registrations
|
|
575
|
+
and error boundary, plus their transitive static imports. Files are counted once
|
|
576
|
+
per page and gzipped individually. Dynamic imports beyond those required roots
|
|
577
|
+
are reported separately and do not consume the budget. Shared files count in
|
|
578
|
+
each page's first visit. This measures emitted JS/CSS, not HTML, fonts or images.
|
|
579
|
+
|
|
580
|
+
`pages` keys match route paths with `*` (within a segment), `**` (across
|
|
581
|
+
segments), or `?` (one non-slash character). Defaults apply first; matching rules
|
|
582
|
+
merge in insertion order and the last supplied field wins. Unsupported glob
|
|
583
|
+
syntax fails with an actionable error. Units are case-insensitive `b`, `kb` and
|
|
584
|
+
`mb`, with 1024 bytes per kb; equality passes. Omitted fields are unlimited.
|
|
585
|
+
|
|
586
|
+
Existing apps without budgets receive a report only. Configured budgets default
|
|
587
|
+
to `mode: "error"`; `"warn"` logs violations and completes. New project templates
|
|
588
|
+
configure 150kb JS / 50kb CSS. A failure names the page, limit, actual size and
|
|
589
|
+
the ten largest rendered modules. Those module sizes are Rollup attribution
|
|
590
|
+
estimates in uncompressed rendered bytes, not gzip shares; compression is not
|
|
591
|
+
additive across modules inside a file.
|
|
592
|
+
|
|
593
|
+
The successful build stores `build-budgets.json` in `build.outdir` (normally
|
|
594
|
+
`dist`). Preserve that directory before rebuilding to obtain deltas. A failed
|
|
595
|
+
build rolls back to the previous artifact and baseline. JSON output still
|
|
596
|
+
contains its attempted report. Changing output directories or deleting dist
|
|
597
|
+
starts a fresh baseline; new routes display no delta. Vite's private
|
|
598
|
+
`client/.vite/budget-modules.json` contains the full module attribution.
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
## build-restful `@warlock.js/core/skills/build-restful/SKILL.md`
|
|
547
602
|
|
|
548
603
|
---
|
|
549
604
|
name: build-restful
|
|
@@ -814,8 +869,64 @@ router.restfulResource("/orders", new OrdersRestful());
|
|
|
814
869
|
- The `define-resource` topic — wire shape for the records the chain returns.
|
|
815
870
|
- The `warlock-conventions` topic — controller/service/repository layering rules.
|
|
816
871
|
|
|
872
|
+
## Bounded list endpoints
|
|
873
|
+
|
|
874
|
+
Use `listEndpoint(request, listCallback, options)` from `@warlock.js/core` to
|
|
875
|
+
produce `{ <pluralKey>: data, pagination }` from a page-based repository list.
|
|
876
|
+
The callback receives repository options, with sorting translated into the
|
|
877
|
+
repository's `[column, direction]` tuple. Wrap repository methods in an arrow
|
|
878
|
+
to preserve their receiver. Both `list` and `listCached` work.
|
|
879
|
+
|
|
880
|
+
```ts
|
|
881
|
+
import { listEndpoint, type Request, type RequestHandler } from "@warlock.js/core";
|
|
882
|
+
import { v, type Infer } from "@warlock.js/seal";
|
|
883
|
+
import { productsRepository } from "../repositories/products.repository";
|
|
884
|
+
|
|
885
|
+
const querySchema = v.object({
|
|
886
|
+
page: v.int().min(1).default(1),
|
|
887
|
+
limit: v.int().min(1).default(20),
|
|
888
|
+
orderBy: v.literal("createdAt", "name").optional(),
|
|
889
|
+
orderDir: v.literal("asc", "desc").optional(),
|
|
890
|
+
status: v.literal("active", "archived").optional(),
|
|
891
|
+
});
|
|
892
|
+
type Query = Infer<typeof querySchema>;
|
|
893
|
+
|
|
894
|
+
export const listProducts: RequestHandler<Request<Query>> = async ({ request, response }) =>
|
|
895
|
+
response.success(
|
|
896
|
+
await listEndpoint(request, (options) => productsRepository.listCached(options), {
|
|
897
|
+
pluralKey: "products",
|
|
898
|
+
maxLimit: 100,
|
|
899
|
+
orderBy: ["createdAt", "name"],
|
|
900
|
+
filters: ["status"],
|
|
901
|
+
}),
|
|
902
|
+
);
|
|
903
|
+
|
|
904
|
+
listProducts.validation = { schema: querySchema, validating: ["query"] };
|
|
905
|
+
// Attach responseSchema with your ProductResource and pagination fields too.
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
`request.validated()` must already contain schema-validated query values. Seal
|
|
909
|
+
handles coercion and filter validation; the helper does not read raw query or
|
|
910
|
+
body values. Configure the repository's `filterBy` for the chosen filter fields.
|
|
911
|
+
Only explicitly listed `filters` are forwarded; pagination and sort controls
|
|
912
|
+
cannot be declared as filters in the typed API. Filters remain flat repository
|
|
913
|
+
options, rather than a nested `filters` object.
|
|
914
|
+
|
|
915
|
+
Defaults are page 1, limit 20, maxLimit 100. The helper clamps a positive integer
|
|
916
|
+
limit to maxLimit (use `.max(100)` in the query schema instead if you prefer a
|
|
917
|
+
422 for excessive requests). Invalid page/limit or unlisted sort columns throw
|
|
918
|
+
`AppError("invalidListQuery", 422, ...)`. Sort directions are restricted to
|
|
919
|
+
`asc`/`desc`, defaulting to `asc` when a column is provided. With no column,
|
|
920
|
+
repository default ordering applies. Invalid helper configuration throws a
|
|
921
|
+
`TypeError`. The plural key must be nonempty and cannot be `pagination`.
|
|
922
|
+
|
|
923
|
+
The helper forces page pagination and preserves the repository's pagination
|
|
924
|
+
metadata without recalculating it. Cursor and unpaginated endpoints need their
|
|
925
|
+
own controller. Add authorization constraints inside the callback using trusted
|
|
926
|
+
server values; never accept them as client filters.
|
|
927
|
+
|
|
817
928
|
|
|
818
|
-
## build-url `@warlock.js/core/build-url/SKILL.md`
|
|
929
|
+
## build-url `@warlock.js/core/skills/build-url/SKILL.md`
|
|
819
930
|
|
|
820
931
|
---
|
|
821
932
|
name: build-url
|
|
@@ -999,7 +1110,7 @@ Don't call `setBaseUrl` per request — it's process-global and races every othe
|
|
|
999
1110
|
- The `configure-app` topic — `src/config/app.ts` and the `baseUrl` env wiring.
|
|
1000
1111
|
|
|
1001
1112
|
|
|
1002
|
-
## configure-app `@warlock.js/core/configure-app/SKILL.md`
|
|
1113
|
+
## configure-app `@warlock.js/core/skills/configure-app/SKILL.md`
|
|
1003
1114
|
|
|
1004
1115
|
---
|
|
1005
1116
|
name: configure-app
|
|
@@ -1425,8 +1536,42 @@ Keep the domain error class in an app module imported by both the service and
|
|
|
1425
1536
|
this config. Return `undefined` for errors you do not recognize; a mapped 5xx
|
|
1426
1537
|
is opaque unless the mapper explicitly chooses a public message.
|
|
1427
1538
|
|
|
1539
|
+
## Format every JSON error response
|
|
1540
|
+
|
|
1541
|
+
`http.errorMapper` classifies thrown non-`HttpError` errors as `{ status, message?, payload? }`. It does not see existing `HttpError` subclasses, returned validation errors, helper responses, unmatched routes, or global rate-limit failures.
|
|
1542
|
+
|
|
1543
|
+
Use `http.errorResponse` when the application needs one JSON error envelope across those paths:
|
|
1544
|
+
|
|
1545
|
+
```ts title="src/config/http.ts"
|
|
1546
|
+
import type { HttpConfigurations } from "@warlock.js/core";
|
|
1547
|
+
|
|
1548
|
+
export default {
|
|
1549
|
+
errorMapper: () => ({ status: 500 }),
|
|
1550
|
+
errorResponse({ status, requestId }) {
|
|
1551
|
+
return {
|
|
1552
|
+
error: {
|
|
1553
|
+
code: status >= 500 ? "serverError" : `http${status}`,
|
|
1554
|
+
message: status >= 500 ? "Internal server error." : "Request failed.",
|
|
1555
|
+
},
|
|
1556
|
+
requestId,
|
|
1557
|
+
};
|
|
1558
|
+
},
|
|
1559
|
+
} satisfies HttpConfigurations;
|
|
1560
|
+
```
|
|
1561
|
+
|
|
1562
|
+
The synchronous formatter receives `HttpErrorResponseContext`: `{ status: number, body: unknown, requestId: string }`. Narrow `body` before reading fields; it is the parsed, serialized JSON body. Return a JSON-serializable object or `undefined` to keep the original. The formatter changes only the body, preserving the HTTP status and headers except for recalculated content length, a `private, no-store` cache policy, and the correlation header. If it throws or cannot serialize its result, core logs the formatter error and retains the original body.
|
|
1563
|
+
|
|
1564
|
+
The hook covers status 400–599 with `application/json` or an `application/*+json` content type, including Fastify parsing errors, unmatched-route 404s, validation 422s, rate-limit 429s, and 500s. Warlock responses use the ID already stamped from `request.id`. Before Warlock creates a Request, the formatter receives Fastify's `request.id` and echoes that same value under `http.requestId.header` (default `X-Request-Id`). Setting `http.requestId.enabled: false` suppresses that header; without a stamped Warlock ID, the formatter falls back to Fastify's request ID even for routed responses. Keep request-ID headers enabled when the body must correlate with Warlock request logs. HTML pages, files, raw text, streams, and errors after headers have been sent are not reformatted. With no formatter configured, existing response shapes are unchanged.
|
|
1565
|
+
|
|
1566
|
+
## Co-located configuration tests
|
|
1428
1567
|
|
|
1429
|
-
|
|
1568
|
+
`src/config/mail.spec.ts` and `src/config/mail.test.tsx` are never imported as
|
|
1569
|
+
configuration. Discovery and reload also exclude `test`, `tests`, `__tests__`,
|
|
1570
|
+
`__mocks__`, `__fixtures__`, and `fixtures` directories at any depth. A real
|
|
1571
|
+
`src/config/tests.ts` remains ordinary configuration.
|
|
1572
|
+
|
|
1573
|
+
|
|
1574
|
+
## create-controller `@warlock.js/core/skills/create-controller/SKILL.md`
|
|
1430
1575
|
|
|
1431
1576
|
---
|
|
1432
1577
|
name: create-controller
|
|
@@ -1725,7 +1870,7 @@ export const remoteCatalogController: RequestHandler = async ({ request, respons
|
|
|
1725
1870
|
```
|
|
1726
1871
|
|
|
1727
1872
|
|
|
1728
|
-
## create-module `@warlock.js/core/create-module/SKILL.md`
|
|
1873
|
+
## create-module `@warlock.js/core/skills/create-module/SKILL.md`
|
|
1729
1874
|
|
|
1730
1875
|
---
|
|
1731
1876
|
name: create-module
|
|
@@ -1925,8 +2070,17 @@ built-in import sorting.
|
|
|
1925
2070
|
After editing generated code, run `npm run format` and `npm run lint:check`.
|
|
1926
2071
|
Formatting retains unused imports; the linter reports them.
|
|
1927
2072
|
|
|
2073
|
+
## Notification translation defaults
|
|
2074
|
+
|
|
2075
|
+
After configuring `app.localeCodes`, run `npx warlock add notifications`.
|
|
2076
|
+
New notification dictionaries include `notFound` as a string in every configured
|
|
2077
|
+
locale. English ("Notification not found") is the explicit fallback for each
|
|
2078
|
+
locale without a bundled translation, including locale sets without English.
|
|
2079
|
+
Replace fallback text with your translations before publishing. Existing module
|
|
2080
|
+
files and existing translation keys are preserved.
|
|
2081
|
+
|
|
1928
2082
|
|
|
1929
|
-
## define-resource `@warlock.js/core/define-resource/SKILL.md`
|
|
2083
|
+
## define-resource `@warlock.js/core/skills/define-resource/SKILL.md`
|
|
1930
2084
|
|
|
1931
2085
|
---
|
|
1932
2086
|
name: define-resource
|
|
@@ -2333,7 +2487,7 @@ export const CommentResource = defineResource({
|
|
|
2333
2487
|
- The `create-controller` topic — calling `new XResource(record).toJSON()` from a controller.
|
|
2334
2488
|
|
|
2335
2489
|
|
|
2336
|
-
## encrypt-data `@warlock.js/core/encrypt-data/SKILL.md`
|
|
2490
|
+
## encrypt-data `@warlock.js/core/skills/encrypt-data/SKILL.md`
|
|
2337
2491
|
|
|
2338
2492
|
---
|
|
2339
2493
|
name: encrypt-data
|
|
@@ -2547,11 +2701,11 @@ Password hashing (`hashPassword`) needs `bcryptjs`, which is its own install —
|
|
|
2547
2701
|
- The `warlock-conventions` topic — service layering, where the encryption boundary should sit.
|
|
2548
2702
|
|
|
2549
2703
|
|
|
2550
|
-
## generate-openapi `@warlock.js/core/generate-openapi/SKILL.md`
|
|
2704
|
+
## generate-openapi `@warlock.js/core/skills/generate-openapi/SKILL.md`
|
|
2551
2705
|
|
|
2552
2706
|
---
|
|
2553
2707
|
name: generate-openapi
|
|
2554
|
-
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 `
|
|
2708
|
+
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 `warlock-js-devtools` skill; listing routes — the `warlock-routes` topic.'
|
|
2555
2709
|
---
|
|
2556
2710
|
|
|
2557
2711
|
# Warlock — generate an OpenAPI document
|
|
@@ -2725,7 +2879,82 @@ const { document, warnings } = buildOpenApiDocument(router.list(), {
|
|
|
2725
2879
|
- The `warlock-routes` topic — list the routes the document is built from.
|
|
2726
2880
|
|
|
2727
2881
|
|
|
2728
|
-
##
|
|
2882
|
+
## generate-web-ui `@warlock.js/core/skills/generate-web-ui/SKILL.md`
|
|
2883
|
+
|
|
2884
|
+
---
|
|
2885
|
+
name: generate-web-ui
|
|
2886
|
+
description: "Generate web pages, schema-backed forms and CRUD UI with warlock generate page, form and crud-ui. Includes setup companions, design-system controls, useSubmitForm, optional guarded endpoints and module CRUD prerequisites."
|
|
2887
|
+
---
|
|
2888
|
+
|
|
2889
|
+
# Generate web UI
|
|
2890
|
+
|
|
2891
|
+
Run these commands from an app with `@warlock.js/web` installed and the app's
|
|
2892
|
+
design system in `src/web/design-system/components/`. Controls are default
|
|
2893
|
+
exports, imported individually through `web/design-system/components/<name>`;
|
|
2894
|
+
they implement Mongez `useFormControl` and accept `name`, `label`, `hint` and
|
|
2895
|
+
`error`. Forms import `Form` directly from `@mongez/react-form`.
|
|
2896
|
+
|
|
2897
|
+
```sh
|
|
2898
|
+
npx warlock generate page account/settings
|
|
2899
|
+
npx warlock generate page dashboard --interactive
|
|
2900
|
+
npx warlock generate form contact
|
|
2901
|
+
npx warlock generate form feedback --module posts --endpoint
|
|
2902
|
+
npx warlock generate crud-ui posts --dry-run --json
|
|
2903
|
+
npx warlock generate crud-ui posts
|
|
2904
|
+
```
|
|
2905
|
+
|
|
2906
|
+
`page account/settings` creates `src/web/account/settings.page.tsx` and
|
|
2907
|
+
`settings.setup.ts`, with `/account/settings`, a named route, a loader and
|
|
2908
|
+
`config.metadata`. `--interactive` also creates a counter feature hook under
|
|
2909
|
+
`hooks/`; replace that example with the feature's behavior. Executable page
|
|
2910
|
+
exports belong in `.setup.ts`, UI in `.page.tsx`, state and effects in hooks.
|
|
2911
|
+
|
|
2912
|
+
`form contact` creates `src/web/forms/contact-form.tsx`, a props file, a
|
|
2913
|
+
`use-contact-form` hook using `useSubmitForm`, and
|
|
2914
|
+
`src/shared/schema/contact.schema.ts`. The starter schema has one `name`
|
|
2915
|
+
string field; edit that shared schema and the controls together. Its default
|
|
2916
|
+
submit target is `/contact`; supply a real endpoint or set the form's `path`.
|
|
2917
|
+
With `--module posts`, output lives under `src/web/posts/`, the shared schema
|
|
2918
|
+
under `src/shared/schema/posts/`, and the target is `/posts/contact`.
|
|
2919
|
+
The module must exist. `--endpoint` requires `--module`, adds a controller
|
|
2920
|
+
with `validation` and `responseSchema`, and appends a guarded POST route.
|
|
2921
|
+
This initial endpoint validates and echoes the input; replace that body with a
|
|
2922
|
+
service call to persist it. It does not create database persistence for you.
|
|
2923
|
+
|
|
2924
|
+
For CRUD UI, first generate a full module with
|
|
2925
|
+
`npx warlock generate module posts`. Define its create/update schemas and
|
|
2926
|
+
list query schema, then run `npx warlock generate crud-ui posts`.
|
|
2927
|
+
The generator loads the real Seal schemas using the app loader, so their
|
|
2928
|
+
imports must resolve. These schema modules must be safe to import on both
|
|
2929
|
+
client and server. Plain string fields map to TextInput, password names to
|
|
2930
|
+
PasswordInput, email/date/time/date-time formats to the corresponding controls,
|
|
2931
|
+
and booleans to CheckboxInput. Unions, nullable fields, enums, numeric fields,
|
|
2932
|
+
nested objects and arrays require custom control decisions and are rejected
|
|
2933
|
+
before writing output. An empty module schema produces an empty form.
|
|
2934
|
+
|
|
2935
|
+
CRUD output contains list/create/edit pages and setup companions, separate
|
|
2936
|
+
create/update forms, their hooks and props, list/edit/delete hooks, and a
|
|
2937
|
+
delete action component. Lists use the existing GET endpoint and its
|
|
2938
|
+
`data`/`pagination` contract (`page` and `pages`). Literal fields in
|
|
2939
|
+
`listPostsQuerySchema` become filter inputs; `page` is controlled by navigation
|
|
2940
|
+
and `limit` remains the endpoint default. Edit reads GET `/posts/:id`, create
|
|
2941
|
+
submits POST `/posts`, update uses PUT `/posts/:id`, and delete uses DELETE
|
|
2942
|
+
`/posts/:id`. Successful deletion refreshes the list. Mutation forms show
|
|
2943
|
+
validation errors, pending state and success feedback.
|
|
2944
|
+
|
|
2945
|
+
CRUD generation accepts the conventional full module route file, allowing
|
|
2946
|
+
formatting and comments. It fails closed for custom routes/guards instead of
|
|
2947
|
+
inferring authorization. Pages require `requireUser({ userType: "user" })`,
|
|
2948
|
+
matching the starter's `guarded` helper. Configure `web.session` with the auth
|
|
2949
|
+
page-session resolver before visiting these pages. Endpoint guards still
|
|
2950
|
+
run on every API request; customized authorization requires a hand-written UI.
|
|
2951
|
+
|
|
2952
|
+
All three commands support `--dry-run --json` through the normal generation
|
|
2953
|
+
report. Existing generated files are skipped unless `--force` is explicit.
|
|
2954
|
+
Route registration is idempotent and retains existing module content.
|
|
2955
|
+
|
|
2956
|
+
|
|
2957
|
+
## hash-password `@warlock.js/core/skills/hash-password/SKILL.md`
|
|
2729
2958
|
|
|
2730
2959
|
---
|
|
2731
2960
|
name: hash-password
|
|
@@ -2912,7 +3141,7 @@ There is no `warlock add` feature for password hashing — `bcryptjs` is a plain
|
|
|
2912
3141
|
- The `warlock-conventions` topic — service layering, where the password boundary should sit.
|
|
2913
3142
|
|
|
2914
3143
|
|
|
2915
|
-
## health-checks `@warlock.js/core/health-checks/SKILL.md`
|
|
3144
|
+
## health-checks `@warlock.js/core/skills/health-checks/SKILL.md`
|
|
2916
3145
|
|
|
2917
3146
|
---
|
|
2918
3147
|
name: health-checks
|
|
@@ -3054,7 +3283,7 @@ For an even smoother handoff, give the load balancer time to observe the 503 bef
|
|
|
3054
3283
|
- The `use-middleware` topic — maintenance mode and other built-in middleware.
|
|
3055
3284
|
|
|
3056
3285
|
|
|
3057
|
-
## lower-stage3-decorators `@warlock.js/core/lower-stage3-decorators/SKILL.md`
|
|
3286
|
+
## lower-stage3-decorators `@warlock.js/core/skills/lower-stage3-decorators/SKILL.md`
|
|
3058
3287
|
|
|
3059
3288
|
---
|
|
3060
3289
|
name: lower-stage3-decorators
|
|
@@ -3114,7 +3343,7 @@ It's the **same esbuild transform the production `tsx`/build path uses**, so a f
|
|
|
3114
3343
|
- The `write-migration` topic of the `warlock-js-cascade` skill — the decorated models this plugin lets you load.
|
|
3115
3344
|
|
|
3116
3345
|
|
|
3117
|
-
## make-endpoint `@warlock.js/core/make-endpoint/SKILL.md`
|
|
3346
|
+
## make-endpoint `@warlock.js/core/skills/make-endpoint/SKILL.md`
|
|
3118
3347
|
|
|
3119
3348
|
---
|
|
3120
3349
|
name: make-endpoint
|
|
@@ -3228,7 +3457,7 @@ Every `generate.*` command, `make`, and `add` take both. `--dry-run` writes (and
|
|
|
3228
3457
|
- The `generate-openapi` topic — what `responseSchema` documents.
|
|
3229
3458
|
|
|
3230
3459
|
|
|
3231
|
-
## model-factories `@warlock.js/core/model-factories/SKILL.md`
|
|
3460
|
+
## model-factories `@warlock.js/core/skills/model-factories/SKILL.md`
|
|
3232
3461
|
|
|
3233
3462
|
---
|
|
3234
3463
|
name: model-factories
|
|
@@ -3324,7 +3553,7 @@ it("lists users", async () => {
|
|
|
3324
3553
|
- The `write-seeder` topic — seeding a development database.
|
|
3325
3554
|
|
|
3326
3555
|
|
|
3327
|
-
## process-image `@warlock.js/core/process-image/SKILL.md`
|
|
3556
|
+
## process-image `@warlock.js/core/skills/process-image/SKILL.md`
|
|
3328
3557
|
|
|
3329
3558
|
---
|
|
3330
3559
|
name: process-image
|
|
@@ -3501,7 +3730,7 @@ See the `upload-file` topic for the multipart entry point and the `store-file` t
|
|
|
3501
3730
|
- The `warlock-conventions` topic — where image-processing services live.
|
|
3502
3731
|
|
|
3503
3732
|
|
|
3504
|
-
## register-route `@warlock.js/core/register-route/SKILL.md`
|
|
3733
|
+
## register-route `@warlock.js/core/skills/register-route/SKILL.md`
|
|
3505
3734
|
|
|
3506
3735
|
---
|
|
3507
3736
|
name: register-route
|
|
@@ -3786,7 +4015,7 @@ router.get("/webhooks/status", webhookStatusController, { rateLimit: false });
|
|
|
3786
4015
|
- The `warlock-conventions` topic — module layout, the `guarded` convention, path aliases.
|
|
3787
4016
|
|
|
3788
4017
|
|
|
3789
|
-
## report-framework-issue `@warlock.js/core/report-framework-issue/SKILL.md`
|
|
4018
|
+
## report-framework-issue `@warlock.js/core/skills/report-framework-issue/SKILL.md`
|
|
3790
4019
|
|
|
3791
4020
|
---
|
|
3792
4021
|
name: report-framework-issue
|
|
@@ -3949,7 +4178,7 @@ GitHub CLI flags: [list](https://cli.github.com/manual/gh_issue_list),
|
|
|
3949
4178
|
[authentication](https://cli.github.com/manual/gh_auth_status).
|
|
3950
4179
|
|
|
3951
4180
|
|
|
3952
|
-
## request-memo `@warlock.js/core/request-memo/SKILL.md`
|
|
4181
|
+
## request-memo `@warlock.js/core/skills/request-memo/SKILL.md`
|
|
3953
4182
|
|
|
3954
4183
|
---
|
|
3955
4184
|
name: request-memo
|
|
@@ -4095,7 +4324,7 @@ Reusing one key for different result types is a caller bug: TypeScript cannot co
|
|
|
4095
4324
|
- The `create-controller` topic — consuming request-scoped values from controllers.
|
|
4096
4325
|
|
|
4097
4326
|
|
|
4098
|
-
## request-tracing `@warlock.js/core/request-tracing/SKILL.md`
|
|
4327
|
+
## request-tracing `@warlock.js/core/skills/request-tracing/SKILL.md`
|
|
4099
4328
|
|
|
4100
4329
|
---
|
|
4101
4330
|
name: request-tracing
|
|
@@ -4333,7 +4562,7 @@ export default httpConfig;
|
|
|
4333
4562
|
- 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.
|
|
4334
4563
|
|
|
4335
4564
|
|
|
4336
|
-
## resolve-path `@warlock.js/core/resolve-path/SKILL.md`
|
|
4565
|
+
## resolve-path `@warlock.js/core/skills/resolve-path/SKILL.md`
|
|
4337
4566
|
|
|
4338
4567
|
---
|
|
4339
4568
|
name: resolve-path
|
|
@@ -4508,7 +4737,7 @@ If you find yourself reaching for `path.join(process.cwd(), ...)` — that's a s
|
|
|
4508
4737
|
- The `upload-file` topic — the `UploadedFile` shape that pairs with `uploadsPath`.
|
|
4509
4738
|
|
|
4510
4739
|
|
|
4511
|
-
## retry-operation `@warlock.js/core/retry-operation/SKILL.md`
|
|
4740
|
+
## retry-operation `@warlock.js/core/skills/retry-operation/SKILL.md`
|
|
4512
4741
|
|
|
4513
4742
|
---
|
|
4514
4743
|
name: retry-operation
|
|
@@ -4630,7 +4859,7 @@ const result = await measure("publish-event", () =>
|
|
|
4630
4859
|
- The `write-use-case` topic — the use-case `retry` option.
|
|
4631
4860
|
|
|
4632
4861
|
|
|
4633
|
-
## run-app `@warlock.js/core/run-app/SKILL.md`
|
|
4862
|
+
## run-app `@warlock.js/core/skills/run-app/SKILL.md`
|
|
4634
4863
|
|
|
4635
4864
|
---
|
|
4636
4865
|
name: run-app
|
|
@@ -4816,7 +5045,7 @@ Omit switches to run TypeScript and installed/configured Oxlint. Set `oxlint: fa
|
|
|
4816
5045
|
|
|
4817
5046
|
## `warlock build` — production bundle
|
|
4818
5047
|
|
|
4819
|
-
esbuild bundle of the app down to a single JS file in `dist/`.
|
|
5048
|
+
esbuild bundle of the app down to a single JS file in `dist/`. `--json` prints the client budget report; build settings come from `warlock.config.ts > build`. See the `build-budgets` topic for per-page gzip limits and deltas.
|
|
4820
5049
|
|
|
4821
5050
|
### `build.*` config knobs
|
|
4822
5051
|
|
|
@@ -5350,7 +5579,7 @@ directly. Regression tests cover both paths without inherited loader hooks,
|
|
|
5350
5579
|
including configured declaration roots, source updates and deleted declarations.
|
|
5351
5580
|
|
|
5352
5581
|
|
|
5353
|
-
## send-mail `@warlock.js/core/send-mail/SKILL.md`
|
|
5582
|
+
## send-mail `@warlock.js/core/skills/send-mail/SKILL.md`
|
|
5354
5583
|
|
|
5355
5584
|
---
|
|
5356
5585
|
name: send-mail
|
|
@@ -5755,8 +5984,33 @@ await Mail.to(user.email)
|
|
|
5755
5984
|
- The `configure-app` topic — `src/config/mail.ts` shape and `env()` patterns.
|
|
5756
5985
|
- The `warlock-conventions` topic — where mail-sending services live.
|
|
5757
5986
|
|
|
5987
|
+
## Preview development mail in devtools
|
|
5758
5988
|
|
|
5759
|
-
|
|
5989
|
+
Run `npx warlock add devtools`, start development, and open `/__warlock` (or your
|
|
5990
|
+
configured devtools path). In the mail panel, select a captured message to read
|
|
5991
|
+
its recipients, subject, text, and sandboxed HTML preview. Send while the panel's
|
|
5992
|
+
collector is attached; captures are held in memory and disappear on restart.
|
|
5993
|
+
|
|
5994
|
+
Use development mail mode so previews do not deliver real email:
|
|
5995
|
+
|
|
5996
|
+
```ts
|
|
5997
|
+
import { setMailMode, sendMail } from "@warlock.js/core";
|
|
5998
|
+
|
|
5999
|
+
setMailMode("development");
|
|
6000
|
+
await sendMail({
|
|
6001
|
+
to: "reader@example.com",
|
|
6002
|
+
subject: "Preview",
|
|
6003
|
+
html: "<p>Your code is 123456</p>",
|
|
6004
|
+
});
|
|
6005
|
+
```
|
|
6006
|
+
|
|
6007
|
+
Development sends emit `mailEvents.onCaptured`, which devtools consumes; console
|
|
6008
|
+
logs show metadata rather than sensitive bodies. Devtools is mounted only for
|
|
6009
|
+
the development runtime and is not mounted in production. Keep production mail
|
|
6010
|
+
mode configured for actual delivery. Test-mode assertions use the test mailbox.
|
|
6011
|
+
|
|
6012
|
+
|
|
6013
|
+
## send-response `@warlock.js/core/skills/send-response/SKILL.md`
|
|
5760
6014
|
|
|
5761
6015
|
---
|
|
5762
6016
|
name: send-response
|
|
@@ -6242,8 +6496,85 @@ export function requireProduct<T>(product: T | null | undefined): T {
|
|
|
6242
6496
|
}
|
|
6243
6497
|
```
|
|
6244
6498
|
|
|
6499
|
+
## Format every JSON error response
|
|
6500
|
+
|
|
6501
|
+
`http.errorMapper` classifies thrown non-`HttpError` errors as `{ status, message?, payload? }`. It does not see existing `HttpError` subclasses, returned validation errors, helper responses, unmatched routes, or global rate-limit failures.
|
|
6502
|
+
|
|
6503
|
+
Use `http.errorResponse` when the application needs one JSON error envelope across those paths:
|
|
6504
|
+
|
|
6505
|
+
```ts title="src/config/http.ts"
|
|
6506
|
+
import type { HttpConfigurations } from "@warlock.js/core";
|
|
6507
|
+
|
|
6508
|
+
export default {
|
|
6509
|
+
errorMapper: () => ({ status: 500 }),
|
|
6510
|
+
errorResponse({ status, requestId }) {
|
|
6511
|
+
return {
|
|
6512
|
+
error: {
|
|
6513
|
+
code: status >= 500 ? "serverError" : `http${status}`,
|
|
6514
|
+
message: status >= 500 ? "Internal server error." : "Request failed.",
|
|
6515
|
+
},
|
|
6516
|
+
requestId,
|
|
6517
|
+
};
|
|
6518
|
+
},
|
|
6519
|
+
} satisfies HttpConfigurations;
|
|
6520
|
+
```
|
|
6521
|
+
|
|
6522
|
+
The synchronous formatter receives `HttpErrorResponseContext`: `{ status: number, body: unknown, requestId: string }`. Narrow `body` before reading fields; it is the parsed, serialized JSON body. Return a JSON-serializable object or `undefined` to keep the original. The formatter changes only the body, preserving the HTTP status and headers except for recalculated content length, a `private, no-store` cache policy, and the correlation header. If it throws or cannot serialize its result, core logs the formatter error and retains the original body.
|
|
6523
|
+
|
|
6524
|
+
The hook covers status 400–599 with `application/json` or an `application/*+json` content type, including Fastify parsing errors, unmatched-route 404s, validation 422s, rate-limit 429s, and 500s. Warlock responses use the ID already stamped from `request.id`. Before Warlock creates a Request, the formatter receives Fastify's `request.id` and echoes that same value under `http.requestId.header` (default `X-Request-Id`). Setting `http.requestId.enabled: false` suppresses that header; without a stamped Warlock ID, the formatter falls back to Fastify's request ID even for routed responses. Keep request-ID headers enabled when the body must correlate with Warlock request logs. HTML pages, files, raw text, streams, and errors after headers have been sent are not reformatted. With no formatter configured, existing response shapes are unchanged.
|
|
6525
|
+
|
|
6526
|
+
## Typed domain failures
|
|
6527
|
+
|
|
6528
|
+
`AppError<Details, Code>` is available from `@warlock.js/core` and the lightweight
|
|
6529
|
+
`@warlock.js/core/errors` entry. Its constructor is
|
|
6530
|
+
`new AppError(code, status, publicMessage, details?)`. Status must be an integer
|
|
6531
|
+
from 400 to 599. The message and details are explicitly public, including for
|
|
6532
|
+
5xx errors: never put diagnostics, credentials or private records there.
|
|
6533
|
+
|
|
6534
|
+
```ts
|
|
6535
|
+
import { AppError } from "@warlock.js/core/errors";
|
|
6536
|
+
|
|
6537
|
+
throw new AppError("quotaExceeded", 429, "Try again later.", { retryAfter: 30 });
|
|
6538
|
+
```
|
|
6539
|
+
|
|
6540
|
+
The default response is
|
|
6541
|
+
`{ error: { code: "quotaExceeded", message: "Try again later.", details: { retryAfter: 30 } } }`.
|
|
6542
|
+
Absent details are omitted; `false`, `0` and `null` details are preserved. Errors
|
|
6543
|
+
retain the private, no-store cache policy, and server failures are logged.
|
|
6245
6544
|
|
|
6246
|
-
|
|
6545
|
+
`AppError` and `HttpError` are separate `Error` subclasses. Existing `HttpError`
|
|
6546
|
+
constructors, inheritance, `payload` and legacy `{ error: message, payload? }`
|
|
6547
|
+
bodies remain unchanged. Both bypass `http.errorMapper`; ordinary errors retain
|
|
6548
|
+
the existing opaque 500 fallback. Choose `AppError` for new domain failures
|
|
6549
|
+
that need stable codes, and keep existing HTTP error subclasses as they are.
|
|
6550
|
+
|
|
6551
|
+
`http.errorResponse` receives an optional typed `appError` alongside `body`,
|
|
6552
|
+
`status` and `requestId`. No per-error mapper is needed:
|
|
6553
|
+
|
|
6554
|
+
```ts
|
|
6555
|
+
import type { HttpErrorResponseFormatter } from "@warlock.js/core";
|
|
6556
|
+
|
|
6557
|
+
const errorResponse: HttpErrorResponseFormatter = ({ appError, requestId }) => {
|
|
6558
|
+
if (!appError) return undefined; // keep other JSON errors unchanged
|
|
6559
|
+
return {
|
|
6560
|
+
error: {
|
|
6561
|
+
code: appError.code,
|
|
6562
|
+
message: appError.publicMessage,
|
|
6563
|
+
...(appError.details === undefined ? {} : { details: appError.details }),
|
|
6564
|
+
},
|
|
6565
|
+
requestId,
|
|
6566
|
+
};
|
|
6567
|
+
};
|
|
6568
|
+
// Set this function as http.errorResponse in your HTTP configuration.
|
|
6569
|
+
```
|
|
6570
|
+
|
|
6571
|
+
The formatter sees the original instance only for thrown `AppError`s handled
|
|
6572
|
+
by the request lifecycle. Early Fastify rejections and returned helper responses
|
|
6573
|
+
have no `appError`; use `body` and `status` for those cases. Do not spread an
|
|
6574
|
+
error instance into the response.
|
|
6575
|
+
|
|
6576
|
+
|
|
6577
|
+
## store-file `@warlock.js/core/skills/store-file/SKILL.md`
|
|
6247
6578
|
|
|
6248
6579
|
---
|
|
6249
6580
|
name: store-file
|
|
@@ -6691,7 +7022,7 @@ Client `PUT`s the bytes straight to R2 — your server never sees them.
|
|
|
6691
7022
|
- The `send-response` topic — `response.sendFile(absPath)` for serving local files.
|
|
6692
7023
|
|
|
6693
7024
|
|
|
6694
|
-
## store-secrets `@warlock.js/core/store-secrets/SKILL.md`
|
|
7025
|
+
## store-secrets `@warlock.js/core/skills/store-secrets/SKILL.md`
|
|
6695
7026
|
|
|
6696
7027
|
---
|
|
6697
7028
|
name: store-secrets
|
|
@@ -6855,7 +7186,7 @@ The normal test harness still installs `secretsFake`; explicit `backend`
|
|
|
6855
7186
|
instances and that fake continue to bypass configured backend selection.
|
|
6856
7187
|
|
|
6857
7188
|
|
|
6858
|
-
## test-browser `@warlock.js/core/test-browser/SKILL.md`
|
|
7189
|
+
## test-browser `@warlock.js/core/skills/test-browser/SKILL.md`
|
|
6859
7190
|
|
|
6860
7191
|
---
|
|
6861
7192
|
name: test-browser
|
|
@@ -6962,6 +7293,7 @@ port-file handshake above when the app supports binding `0` itself.
|
|
|
6962
7293
|
document. Selector helpers do not enter frames or closed shadow roots.
|
|
6963
7294
|
|
|
6964
7295
|
Use `--browser /path/to/chrome` or `WARLOCK_E2E_BROWSER` if discovery fails.
|
|
7296
|
+
Set `browser.startupTimeoutMs` (default 30 seconds) when the browser is slow to start, for example on shared CI runners.
|
|
6965
7297
|
`browser: { args: ["--no-sandbox"] }` is an explicit option for controlled root
|
|
6966
7298
|
containers; it is not a default. On Windows, command arrays should invoke JS tools
|
|
6967
7299
|
through `process.execPath`, avoiding `.cmd` executables that require a shell.
|
|
@@ -6977,8 +7309,61 @@ recorded process group (Windows: verified PID tree). It retains disposable files
|
|
|
6977
7309
|
if ownership verification fails and reports the failure in evidence. Do not add
|
|
6978
7310
|
process-name or port-based kills to a spec.
|
|
6979
7311
|
|
|
7312
|
+
## Accessibility checks
|
|
7313
|
+
|
|
7314
|
+
Call `await page.assertAccessible()` after the content is visible and again after
|
|
7315
|
+
opening menus, dialogs or validation feedback. The harness reads its pinned local
|
|
7316
|
+
axe-core distribution and injects it with CDP `Runtime.evaluate`; it never fetches
|
|
7317
|
+
a CDN script or adds axe to the app bundle. A missing dependency, injection error,
|
|
7318
|
+
unmatched selector or scan exception fails the step.
|
|
7319
|
+
|
|
7320
|
+
```ts
|
|
7321
|
+
await page.assertAccessible();
|
|
7322
|
+
await page.assertAccessible({
|
|
7323
|
+
selector: "main",
|
|
7324
|
+
tags: ["wcag2a", "wcag2aa", "wcag21aa"],
|
|
7325
|
+
allow: [{ id: "color-contrast", selector: ".legacy-caption" }],
|
|
7326
|
+
timeoutMs: 30_000,
|
|
7327
|
+
});
|
|
7328
|
+
```
|
|
7329
|
+
|
|
7330
|
+
Defaults run all enabled axe rules on the document. `tags` uses axe's `runOnly`
|
|
7331
|
+
tag filter; `selector` limits the scanned region. Allowances match exact rule IDs
|
|
7332
|
+
and, when supplied, an exact single-element target selector. Omitting `selector`
|
|
7333
|
+
from an allowance permits that rule everywhere; keep exceptions narrow. Nested
|
|
7334
|
+
frame/shadow targets cannot match a simple selector allowance. Allowed nodes stay
|
|
7335
|
+
in evidence with `allowed: true`; other nodes of the same rule still fail.
|
|
7336
|
+
|
|
7337
|
+
Evidence's `accessibility` array contains URL, viewport, timestamp, violations
|
|
7338
|
+
with rule IDs and target selectors, and incomplete checks for manual review.
|
|
7339
|
+
Incomplete checks do not fail automatically. A passing automated scan does not
|
|
7340
|
+
replace keyboard, screen-reader, focus-order or contrast review. See the
|
|
7341
|
+
[axe API](https://github.com/dequelabs/axe-core/blob/develop/doc/API.md) for scan
|
|
7342
|
+
semantics. Run at both 390 and 1280 pixels with `assertViewport()` as well.
|
|
7343
|
+
|
|
7344
|
+
## Watched development checks
|
|
7345
|
+
|
|
7346
|
+
Use an owned app copy with `app.env: { NODE_ENV: "development" }` and
|
|
7347
|
+
`app.start: [process.execPath, "node_modules/@warlock.js/core/bin/warlock.js", "dev"]`.
|
|
7348
|
+
Do not attach to an existing server. The fixture's `directory` is available in
|
|
7349
|
+
`run`; use `node:fs/promises` to edit only files inside that copy.
|
|
6980
7350
|
|
|
6981
|
-
|
|
7351
|
+
Wait for a visible revision marker after each edit; a successful file write alone
|
|
7352
|
+
does not prove watcher invalidation or browser hydration. Exercise several edits,
|
|
7353
|
+
including setup-file creation, changes and removal. After each revision, request
|
|
7354
|
+
the page again, exercise a hydrated control, check imported `shared` in loaders
|
|
7355
|
+
and middleware, and verify concurrent requests keep distinct values. Do not
|
|
7356
|
+
allowlist request-store or hydration errors to make these checks pass.
|
|
7357
|
+
|
|
7358
|
+
For forms beneath layouts, compare server-rendered IDs with the hydrated hook
|
|
7359
|
+
values (for example expose the hook value in a click handler), check repeated
|
|
7360
|
+
instances, label `for` and ARIA references, then navigate away and back. Comparing
|
|
7361
|
+
DOM IDs alone can miss a mismatch: React may retain the server attribute while
|
|
7362
|
+
client handlers capture another ID. Capture evidence and screenshots at every
|
|
7363
|
+
revision. Do not replace React's `useId()` globally or hide hydration warnings.
|
|
7364
|
+
|
|
7365
|
+
|
|
7366
|
+
## test-fakes `@warlock.js/core/skills/test-fakes/SKILL.md`
|
|
6982
7367
|
|
|
6983
7368
|
---
|
|
6984
7369
|
name: test-fakes
|
|
@@ -7107,7 +7492,7 @@ An unknown fake name or a non-boolean value fails setup with the list of known f
|
|
|
7107
7492
|
- The `model-factories` topic — deterministic fixtures.
|
|
7108
7493
|
|
|
7109
7494
|
|
|
7110
|
-
## test-http `@warlock.js/core/test-http/SKILL.md`
|
|
7495
|
+
## test-http `@warlock.js/core/skills/test-http/SKILL.md`
|
|
7111
7496
|
|
|
7112
7497
|
---
|
|
7113
7498
|
name: test-http
|
|
@@ -7570,8 +7955,99 @@ This is fine for normal test flow. It bites when you're inside a transaction the
|
|
|
7570
7955
|
- The `warlock-conventions` topic — module layout, where `tests/*.test.ts` files live.
|
|
7571
7956
|
- The `write-cli-command` topic — `warlock add test` scaffolds both global + worker setup files.
|
|
7572
7957
|
|
|
7958
|
+
## Worker dictionaries and owned runtime resources
|
|
7959
|
+
|
|
7960
|
+
The generated `src/test-setup.ts` calls:
|
|
7961
|
+
|
|
7962
|
+
```ts
|
|
7963
|
+
await setupTest({ importModule: (file) => import(file) });
|
|
7964
|
+
```
|
|
7965
|
+
|
|
7966
|
+
`setupTest()` reads `src/app/*/utils/locales.json` after configuration in each
|
|
7967
|
+
Vitest worker. Server translation helpers therefore see all configured locales
|
|
7968
|
+
without manual registration. Invalid dictionaries fail setup. `teardownTest()`
|
|
7969
|
+
unregisters those dictionaries; legacy executable locale files still require
|
|
7970
|
+
explicit imports in worker setup.
|
|
7971
|
+
|
|
7972
|
+
Global HTTP fixtures should use `await startHttpTestServer({ port: 0, web: false })`
|
|
7973
|
+
for API-only suites and `await stopHttpTestServer()` in teardown. Stop releases
|
|
7974
|
+
connectors and the file runtime's loader channel, queued work, watchers, health
|
|
7975
|
+
workers, and registered module cleanup hooks, including after partial startup.
|
|
7976
|
+
A Node loader registration is process-scoped; its message channel is closed at
|
|
7977
|
+
teardown. Module-owned timers/listeners must register their cleanup hooks.
|
|
7978
|
+
|
|
7979
|
+
A module that owns a long-running handle can export an awaited cleanup:
|
|
7980
|
+
|
|
7981
|
+
```ts
|
|
7982
|
+
// src/app/messages/main.ts
|
|
7983
|
+
import { createServer } from "node:net";
|
|
7984
|
+
const listener = createServer();
|
|
7985
|
+
listener.listen(0, "127.0.0.1");
|
|
7986
|
+
export async function cleanup(): Promise<void> {
|
|
7987
|
+
await new Promise<void>((resolve, reject) => {
|
|
7988
|
+
listener.close((error) => (error ? reject(error) : resolve()));
|
|
7989
|
+
});
|
|
7990
|
+
}
|
|
7991
|
+
```
|
|
7573
7992
|
|
|
7574
|
-
|
|
7993
|
+
HTTP test teardown awaits these cleanup functions and reports failures after
|
|
7994
|
+
attempting every module's cleanup. Avoid leaving module-owned handles running
|
|
7995
|
+
outside this lifecycle.
|
|
7996
|
+
|
|
7997
|
+
## Response contracts from declared schemas
|
|
7998
|
+
|
|
7999
|
+
`assertResponseContracts` from `@warlock.js/core/tests` sends HTTP requests and
|
|
8000
|
+
asserts the serialized JSON body against the handler's `responseSchema` for the
|
|
8001
|
+
actual status. It reuses Core's OpenAPI schema translation, including resource
|
|
8002
|
+
classes, nested objects, arrays, nullable casts and resource self references.
|
|
8003
|
+
It checks required fields and primitive types without coercion; extra fields
|
|
8004
|
+
are allowed. URL/date format annotations are not checked beyond their declared
|
|
8005
|
+
JSON shape. Builders, computed resolvers and resources without a static shape
|
|
8006
|
+
fail with a diagnostic rather than silently passing.
|
|
8007
|
+
|
|
8008
|
+
```ts
|
|
8009
|
+
import { it } from "vitest";
|
|
8010
|
+
import { router } from "@warlock.js/core";
|
|
8011
|
+
import { assertResponseContracts } from "@warlock.js/core/tests";
|
|
8012
|
+
|
|
8013
|
+
it("matches product response declarations", async () => {
|
|
8014
|
+
await assertResponseContracts({
|
|
8015
|
+
routes: router.list().filter((route) => route.path === "/products"),
|
|
8016
|
+
cases: () => [{ path: "/products?page=1&limit=10", status: 200 }],
|
|
8017
|
+
requireAllStatuses: true,
|
|
8018
|
+
});
|
|
8019
|
+
});
|
|
8020
|
+
```
|
|
8021
|
+
|
|
8022
|
+
With the shared test server, retain `startHttpTestServer({ port: 0 })` in global
|
|
8023
|
+
setup and `setupTest` in the worker. The routes and their handler schemas must
|
|
8024
|
+
also be loaded in the worker running this assertion (import their routes module
|
|
8025
|
+
there); global setup runs in a different process. Omit `routes` to discover all
|
|
8026
|
+
registered routes with `responseSchema` in that worker. Empty discovery fails.
|
|
8027
|
+
Explicit routes lacking a schema also fail.
|
|
8028
|
+
|
|
8029
|
+
Static GET routes default to one request without headers. Parameterized routes
|
|
8030
|
+
and non-GET methods require explicit `cases(route)` fixtures; an empty case
|
|
8031
|
+
list fails. Each case can supply a concrete `path`, `init` (headers, serialized
|
|
8032
|
+
body, etc.) and expected `status`. Supply fixtures with valid IDs, seeded data,
|
|
8033
|
+
authentication and invalid inputs to exercise the desired outcomes. An `all` route also needs a concrete fixture `method`. Redirects
|
|
8034
|
+
are not followed. POST/PUT/PATCH/DELETE fixtures execute real side effects;
|
|
8035
|
+
use isolated test data and clean it up.
|
|
8036
|
+
|
|
8037
|
+
Undeclared response statuses fail. Only statuses actually returned are checked
|
|
8038
|
+
by default; `requireAllStatuses: true` additionally fails if any declared status
|
|
8039
|
+
has no exercised fixture. HEAD, 204 and 304 responses must have an empty body; declare it with
|
|
8040
|
+
`{ body: {} }`. Other statuses must carry matching JSON.
|
|
8041
|
+
|
|
8042
|
+
For an in-process server, pass `client: app` from `createTestHttpServer()` and
|
|
8043
|
+
close it in teardown. For signed-in requests, pass `client: await actingAs(user)`
|
|
8044
|
+
and dispose that client afterward. These clients use the same assertion and
|
|
8045
|
+
run the real HTTP middleware and serialization paths.
|
|
8046
|
+
|
|
8047
|
+
Cleanup hooks that own asynchronous handles should return a promise for completed closure. For MessagePorts, subscribe to `close` before calling `close()` and await the event before checking reference state.
|
|
8048
|
+
|
|
8049
|
+
|
|
8050
|
+
## test-service `@warlock.js/core/skills/test-service/SKILL.md`
|
|
7575
8051
|
|
|
7576
8052
|
---
|
|
7577
8053
|
name: test-service
|
|
@@ -7588,9 +8064,9 @@ For unit tests, you import the thing under test and call it directly. No HTTP, n
|
|
|
7588
8064
|
|
|
7589
8065
|
⛔ **If your project was generated before 4.14.0, fix BOTH the comment and the call.** The comment is false, **and** the generated `setupTest({ connectors: true })` is now an _explicit_ value that overrides your `src/config/tests.ts`. **Bare `setupTest()` is the correct call.**
|
|
7590
8066
|
|
|
7591
|
-
**The lifetime is FILE-SCOPED
|
|
8067
|
+
**The default lifetime is FILE-SCOPED.** Your setup file bootstraps the framework and its `afterAll(teardownTest)` closes it, once per test file. **One owner, one pairing — correct under every pool, every isolation setting, and watch mode.**
|
|
7592
8068
|
|
|
7593
|
-
|
|
8069
|
+
Worker lifetime is available as an opt-in in 5.35.0. See the runtime lifetime section below for its runner, pool and isolation requirements.
|
|
7594
8070
|
|
|
7595
8071
|
⚠ **Changed in 4.13.0 — the import is a subpath now.** `setupTest` used to be re-exported from the package root; it is not any more, because that put the test helpers into every application's production module graph. `import { setupTest } from "@warlock.js/core"` now fails with _"has no exported member"_ — **add `/tests` to the specifier and nothing else changes.**
|
|
7596
8072
|
|
|
@@ -7992,7 +8468,7 @@ Node process, preserve the source package's module mode. ESM fixtures need a
|
|
|
7992
8468
|
CommonJS and named exports fail before the behavior under test runs. Assert the
|
|
7993
8469
|
child process exit status with its captured stderr before checking stdout.
|
|
7994
8470
|
|
|
7995
|
-
-
|
|
8471
|
+
- **By default `setupTest` boots per test file** — every version of this skill through 4.13.0 said otherwise. **You pay one framework bootstrap per test file**, which is what 4.13.0 already cost; the difference is that it is now a chosen lifetime rather than a side effect of a module flag resetting.
|
|
7996
8472
|
- ⛔ **Never delete the `afterAll(teardownTest)` from your setup file.** Without it nothing closes what `setupTest` opened, and the connectors outlive the file that started them.
|
|
7997
8473
|
- ⛔ **You can't swap the connector set by calling `setupTest` again — it rejects now.** Through 4.13.0 the second call was silently ignored. Tear down first, or don't call it.
|
|
7998
8474
|
- **These connections are separate from the HTTP test server's.** A row inserted by a service-level test is on this connection; the HTTP test server has its own. They only see each other if both point at the same physical DB **and** the inserting test has committed.
|
|
@@ -8020,8 +8496,166 @@ setupTest({ importModule: (file) => import(file) });
|
|
|
8020
8496
|
|
|
8021
8497
|
The generated `src/test-setup.ts` already does this. Without it, `setupTest()` under Vitest warns once.
|
|
8022
8498
|
|
|
8499
|
+
## Runtime lifetime, unit projects and setup profiling
|
|
8500
|
+
|
|
8501
|
+
`warlock add test` (also used by `create-warlock --features=test`) generates two
|
|
8502
|
+
Vitest projects. `unit` discovers `src/**/*.spec.ts` outside `tests/` directories
|
|
8503
|
+
and has no Warlock setup or HTTP global setup. `integration` discovers
|
|
8504
|
+
`src/**/*.test.ts`, specs inside `src/**/tests/`, and `tests/**/*.{test,spec}.ts`.
|
|
8505
|
+
It uses the framework setup, HTTP global setup and a generated `src/test-runner.ts`.
|
|
8506
|
+
Keep pure service specs beside their source and mock repositories at the boundary.
|
|
8507
|
+
Existing config files are preserved; copy the projects layout into them manually.
|
|
8508
|
+
Vitest 4.1.10 or newer is required for the generated runner.
|
|
8509
|
+
|
|
8510
|
+
For a unit spec that needs configuration values, use:
|
|
8511
|
+
|
|
8512
|
+
```ts
|
|
8513
|
+
import { afterAll, expect } from "vitest";
|
|
8514
|
+
import { setupTest, teardownTest } from "@warlock.js/core/tests";
|
|
8515
|
+
|
|
8516
|
+
await setupTest({
|
|
8517
|
+
mode: "unit",
|
|
8518
|
+
importModule: (file) => import(file),
|
|
8519
|
+
file: expect.getState().testPath,
|
|
8520
|
+
});
|
|
8521
|
+
afterAll(teardownTest);
|
|
8522
|
+
```
|
|
8523
|
+
|
|
8524
|
+
Unit mode loads `warlock.config.ts` and configuration values once per module graph.
|
|
8525
|
+
It skips bootstrap/env/prestart hooks, connectors, config special handlers,
|
|
8526
|
+
`filesOrchestrator.init()`, the native ESM loader, and fake installation. Keep
|
|
8527
|
+
configuration imports free of side effects; arbitrary code imported by a config
|
|
8528
|
+
file can still perform I/O. A plain unit spec usually needs no `setupTest()` at all.
|
|
8529
|
+
`connectors: false` still bootstraps; it is not the cheap unit mode. Use separate
|
|
8530
|
+
projects for unit and integration runtimes.
|
|
8531
|
+
|
|
8532
|
+
### Opt into one runtime per fork
|
|
8023
8533
|
|
|
8024
|
-
|
|
8534
|
+
The default remains `tests.lifetime: "file"`. To reuse a runtime, change the
|
|
8535
|
+
integration project's `isolate` to `false`, keep `pool: "forks"` and
|
|
8536
|
+
`runner: "./src/test-runner.ts"`, then configure:
|
|
8537
|
+
|
|
8538
|
+
```ts title="src/config/tests.ts"
|
|
8539
|
+
import { resetWorkerDatabase } from "../test-database";
|
|
8540
|
+
|
|
8541
|
+
export default {
|
|
8542
|
+
lifetime: "worker",
|
|
8543
|
+
// Application-owned callback: truncate only this worker's test database.
|
|
8544
|
+
// Called before every file's fixtures, including the first file.
|
|
8545
|
+
resetBetweenFiles: async () => {
|
|
8546
|
+
await resetWorkerDatabase();
|
|
8547
|
+
},
|
|
8548
|
+
profileSetup: true,
|
|
8549
|
+
};
|
|
8550
|
+
```
|
|
8551
|
+
|
|
8552
|
+
Run with `npx vitest run --project integration`. Worker lifetime refuses watch,
|
|
8553
|
+
threads, isolated module graphs, or a missing runner with a message explaining
|
|
8554
|
+
the required settings. Keep `afterAll(teardownTest)` in the setup file: in worker
|
|
8555
|
+
mode it marks the end of the file; Vitest's awaited worker cleanup closes the
|
|
8556
|
+
runtime once. The runner uses `VitestTestRunner.onCleanupWorkerContext`, verified
|
|
8557
|
+
in Vitest 4.1.10's `runners.d.ts`, runner implementation and worker cleanup source.
|
|
8558
|
+
For an existing project, generate this runner without overwriting your config:
|
|
8559
|
+
|
|
8560
|
+
```ts title="src/test-runner.ts"
|
|
8561
|
+
import { configureTestWorker } from "@warlock.js/core/tests";
|
|
8562
|
+
import { VitestTestRunner } from "vitest/runners";
|
|
8563
|
+
|
|
8564
|
+
export default class WarlockTestRunner extends VitestTestRunner {
|
|
8565
|
+
public constructor(config: ConstructorParameters<typeof VitestTestRunner>[0]) {
|
|
8566
|
+
super(config);
|
|
8567
|
+
configureTestWorker(
|
|
8568
|
+
{ pool: this.pool, watch: config.watch, isolate: config.isolate },
|
|
8569
|
+
(cleanup) => this.onCleanupWorkerContext(cleanup),
|
|
8570
|
+
);
|
|
8571
|
+
}
|
|
8572
|
+
}
|
|
8573
|
+
```
|
|
8574
|
+
|
|
8575
|
+
The first file bootstraps; subsequent files reuse connectors and config. Between
|
|
8576
|
+
files, selected fakes are restored and reinstalled, resetting mail/queue records,
|
|
8577
|
+
cache, secrets, clock, IDs and a private storage directory. Worker storage is
|
|
8578
|
+
separate from the global HTTP server's storage: use an in-process test HTTP server
|
|
8579
|
+
when assertions need to see request-side fake state. The hook runs after fake
|
|
8580
|
+
reset and before fixtures. It must finish before returning. Within a file,
|
|
8581
|
+
repeated `setupTest()` calls remain idempotent; use `beforeEach`/`afterEach` for
|
|
8582
|
+
per-test cleanup.
|
|
8583
|
+
|
|
8584
|
+
This trades module isolation for startup cost: application singletons, config
|
|
8585
|
+
mutations, mocks and module side effects can survive between files. Reset your
|
|
8586
|
+
own state in `resetBetweenFiles`, restore mocks, and keep connector selection
|
|
8587
|
+
consistent. Give each worker a distinct database/schema using
|
|
8588
|
+
`process.env.VITEST_POOL_ID` when preparing test connections. Never drop a shared
|
|
8589
|
+
database from this hook while other workers are using it. The framework does not
|
|
8590
|
+
create a memory replica set or a cross-worker lock; those belong to the application's
|
|
8591
|
+
global setup. A shared HTTP server and a single replica set can contend on I/O,
|
|
8592
|
+
locks and shared fixtures even though workers run concurrently. Scope rows and
|
|
8593
|
+
HTTP fixtures per worker, or run tests that require global mutation separately
|
|
8594
|
+
with `maxWorkers: 1`. No test-server teardown ownership changes are needed here.
|
|
8595
|
+
|
|
8596
|
+
`getTestRuntimeStats()` from `@warlock.js/core/tests` returns process-local
|
|
8597
|
+
`{ bootstraps, files }`. For example, after setup in worker mode:
|
|
8598
|
+
|
|
8599
|
+
```ts
|
|
8600
|
+
import { expect, it } from "vitest";
|
|
8601
|
+
import { getTestRuntimeStats } from "@warlock.js/core/tests";
|
|
8602
|
+
|
|
8603
|
+
it("reuses this worker's runtime", () => {
|
|
8604
|
+
expect(getTestRuntimeStats().bootstraps).toBe(1);
|
|
8605
|
+
});
|
|
8606
|
+
```
|
|
8607
|
+
|
|
8608
|
+
Across one integration project, a successful run with no worker recycling needs
|
|
8609
|
+
at most `maxWorkers` bootstraps. Multiple projects, worker crashes and memory-limit
|
|
8610
|
+
recycling can create additional processes; count distinct PIDs in profiles.
|
|
8611
|
+
|
|
8612
|
+
### Measure and size CI workers
|
|
8613
|
+
|
|
8614
|
+
Set `tests.profileSetup: true` or run
|
|
8615
|
+
`WARLOCK_TEST_PROFILE=1 npx vitest run --project integration`. Pass
|
|
8616
|
+
`file: expect.getState().testPath` to `setupTest()` for file labels (the generated
|
|
8617
|
+
setup does this). Each file emits one line with milliseconds for environment, config,
|
|
8618
|
+
bootstrap, orchestrator, app config, connectors, fakes, reset and teardown as
|
|
8619
|
+
applicable. Reused files report reset instead of startup steps. A summary per
|
|
8620
|
+
worker reports file/bootstrap counts, summed step timings and final shutdown.
|
|
8621
|
+
The generated config also installs `TestSetupReporter` from `@warlock.js/core/vite`
|
|
8622
|
+
(`reporters: ["default", new TestSetupReporter()]`), which aggregates file counts,
|
|
8623
|
+
bootstrap counts and timings across PIDs into a run summary. Summed timings are
|
|
8624
|
+
not wall time. Worker shutdown is included when its summary reaches the reporter
|
|
8625
|
+
before the run-end callback; individual worker summaries always report it.
|
|
8626
|
+
|
|
8627
|
+
The generated config sets `maxWorkers: recommendedTestWorkers()` from
|
|
8628
|
+
`@warlock.js/core/vite`. It takes the smaller of CPU affinity and the tightest
|
|
8629
|
+
visible cgroup v2 quota, rounding fractional CPUs down with a minimum of one.
|
|
8630
|
+
For example `cpu.max = 1200000 100000` recommends 12 workers on a 48-CPU host.
|
|
8631
|
+
The format follows the [kernel cgroup v2 CPU interface](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#cpu-interface-files).
|
|
8632
|
+
It reads `/proc/self/cgroup` and visible ancestors under `/sys/fs/cgroup`; hosts
|
|
8633
|
+
without readable v2 quotas use affinity. Set `maxWorkers` explicitly on cgroup v1,
|
|
8634
|
+
custom cgroup mounts, or when database/memory limits require fewer workers.
|
|
8635
|
+
|
|
8636
|
+
### Release gate
|
|
8637
|
+
|
|
8638
|
+
```bash
|
|
8639
|
+
npx warlock test --changed --since "$LAST_RELEASE_REF"
|
|
8640
|
+
npx vitest run --project integration
|
|
8641
|
+
# Nightly (all projects):
|
|
8642
|
+
npx vitest run
|
|
8643
|
+
```
|
|
8644
|
+
|
|
8645
|
+
The first command includes affected tests, using the existing changed-test CLI;
|
|
8646
|
+
the second always covers integration wiring. Keep the full suite nightly and
|
|
8647
|
+
compare cold and reused setup profiles on the same revision, worker count and
|
|
8648
|
+
database fixture before claiming a speedup.
|
|
8649
|
+
|
|
8650
|
+
For a manual fake harness outside `setupTest`, the same private storage behavior
|
|
8651
|
+
is available with `await installTestFakes(true, { isolatedStorage: true })`, paired
|
|
8652
|
+
with `await restoreTestFakes()`. Both are exported from `@warlock.js/core/tests`.
|
|
8653
|
+
`await storageFake.install({ isolated: true })` / `await storageFake.restore()`
|
|
8654
|
+
provides it for a storage-only harness. Existing installs share the HTTP root by
|
|
8655
|
+
default; the isolated option never replaces or deletes that shared root.
|
|
8656
|
+
|
|
8657
|
+
|
|
8658
|
+
## update-packages `@warlock.js/core/skills/update-packages/SKILL.md`
|
|
8025
8659
|
|
|
8026
8660
|
---
|
|
8027
8661
|
name: update-packages
|
|
@@ -8169,7 +8803,7 @@ Two small zero-dependency utilities back the tooling and are exported from `@war
|
|
|
8169
8803
|
- The `configure-app` topic — `warlock.config.ts` shape and `defineConfig`.
|
|
8170
8804
|
|
|
8171
8805
|
|
|
8172
|
-
## upgrade-from-5-29 `@warlock.js/core/upgrade-from-5-29/SKILL.md`
|
|
8806
|
+
## upgrade-from-5-29 `@warlock.js/core/skills/upgrade-from-5-29/SKILL.md`
|
|
8173
8807
|
|
|
8174
8808
|
---
|
|
8175
8809
|
name: upgrade-from-5-29
|
|
@@ -8649,6 +9283,10 @@ It replaces standard scripts/dependencies, pins TypeScript 7.0.2, updates genera
|
|
|
8649
9283
|
editor settings, removes `healthCheckers.eslint` and selects Oxlint. A generated
|
|
8650
9284
|
`.prettierignore` becomes `.oxfmtignore`; Oxfmt scripts read it explicitly.
|
|
8651
9285
|
|
|
9286
|
+
Generated migration configs enable `typescript/no-explicit-any`, `curly`, and
|
|
9287
|
+
`no-console` as errors, matching new apps. Fix those diagnostics before running
|
|
9288
|
+
`npm run lint:check` and `npx warlock check` again.
|
|
9289
|
+
|
|
8652
9290
|
The `oxc-toolchain` codemod also updates generated VS Code settings and literal
|
|
8653
9291
|
ESLint/Oxlint health-check switches. Custom editor settings, checker arrays,
|
|
8654
9292
|
computed options and nonstandard scripts require review. Oxc diagnostics keep
|
|
@@ -8704,8 +9342,26 @@ existing Oxc configs retain their categories. A real lockfile beside a workspace
|
|
|
8704
9342
|
marker determines the installer: `package-lock.json` plus `pnpm-workspace.yaml`
|
|
8705
9343
|
without `pnpm-lock.yaml` now produces `npm install`.
|
|
8706
9344
|
|
|
9345
|
+
## 5.34 to 5.35 resource hidden input
|
|
9346
|
+
|
|
9347
|
+
Model resources now receive hidden input columns (subject to `resourceColumns`)
|
|
9348
|
+
to support derivations such as `has_secret: Boolean(data.secret)`. Model
|
|
9349
|
+
serialization removes `hidden` top-level keys from the final resource result.
|
|
9350
|
+
Review resources that copy input under renamed or nested keys and return explicit
|
|
9351
|
+
public projections. Direct resource construction does not invoke model filtering.
|
|
8707
9352
|
|
|
8708
|
-
##
|
|
9353
|
+
## 5.34 to 5.35 document hydration
|
|
9354
|
+
|
|
9355
|
+
Initial Web hydration now includes `root.tsx`, matching the document ancestry
|
|
9356
|
+
used by SSR so `useId()` and generated Mongez form IDs agree beneath layouts.
|
|
9357
|
+
Root components and providers execute in both realms. Move server-only work into
|
|
9358
|
+
`root.setup.ts` loaders and pass browser-safe results as `data`; retain exactly
|
|
9359
|
+
one `#vessel` around `{children}`. Client navigation still swaps layout/page
|
|
9360
|
+
children while preserving the document root. Remove temporary explicit form IDs
|
|
9361
|
+
only when the application does not otherwise depend on those names.
|
|
9362
|
+
|
|
9363
|
+
|
|
9364
|
+
## upload-file `@warlock.js/core/skills/upload-file/SKILL.md`
|
|
8709
9365
|
|
|
8710
9366
|
---
|
|
8711
9367
|
name: upload-file
|
|
@@ -9018,7 +9674,7 @@ The reverse direction — sending a stored file back via `response.sendFile(...)
|
|
|
9018
9674
|
- The `warlock-conventions` topic — storage configuration and driver selection.
|
|
9019
9675
|
|
|
9020
9676
|
|
|
9021
|
-
## use-app-context `@warlock.js/core/use-app-context/SKILL.md`
|
|
9677
|
+
## use-app-context `@warlock.js/core/skills/use-app-context/SKILL.md`
|
|
9022
9678
|
|
|
9023
9679
|
---
|
|
9024
9680
|
name: use-app-context
|
|
@@ -9329,7 +9985,7 @@ cors: {
|
|
|
9329
9985
|
- The `warlock-conventions` topic — module layout, where paths point to.
|
|
9330
9986
|
|
|
9331
9987
|
|
|
9332
|
-
## use-localization `@warlock.js/core/use-localization/SKILL.md`
|
|
9988
|
+
## use-localization `@warlock.js/core/skills/use-localization/SKILL.md`
|
|
9333
9989
|
|
|
9334
9990
|
---
|
|
9335
9991
|
name: use-localization
|
|
@@ -9670,8 +10326,29 @@ This produces `account.title` and `account.actions.save`. Keep every leaf comple
|
|
|
9670
10326
|
|
|
9671
10327
|
When a route-locales snapshot applies, `t()` (through the request binding) and `request.transFrom(locale, key, placeholders?)` resolve through it; without one, they retain the existing global-registry behavior. The underlying `transFromKeywords(locale, keywords, key, placeholders?, converter?)` is supplied by `@mongez/localization` 3.5.0. Error pages rebind to their own snapshot, while framework fallback uses the app/root snapshot. Development HMR add/edit/delete publishes a new snapshot, and the snapshot revision keeps page-cache entries from reusing old JSON. Generated `.warlock` typings contain the flattened route keys; do not maintain a second handwritten key union.
|
|
9672
10328
|
|
|
10329
|
+
## Worker dictionaries and owned runtime resources
|
|
10330
|
+
|
|
10331
|
+
The generated `src/test-setup.ts` calls:
|
|
10332
|
+
|
|
10333
|
+
```ts
|
|
10334
|
+
await setupTest({ importModule: (file) => import(file) });
|
|
10335
|
+
```
|
|
10336
|
+
|
|
10337
|
+
`setupTest()` reads `src/app/*/utils/locales.json` after configuration in each
|
|
10338
|
+
Vitest worker. Server translation helpers therefore see all configured locales
|
|
10339
|
+
without manual registration. Invalid dictionaries fail setup. `teardownTest()`
|
|
10340
|
+
unregisters those dictionaries; legacy executable locale files still require
|
|
10341
|
+
explicit imports in worker setup.
|
|
9673
10342
|
|
|
9674
|
-
|
|
10343
|
+
Global HTTP fixtures should use `await startHttpTestServer({ port: 0, web: false })`
|
|
10344
|
+
for API-only suites and `await stopHttpTestServer()` in teardown. Stop releases
|
|
10345
|
+
connectors and the file runtime's loader channel, queued work, watchers, health
|
|
10346
|
+
workers, and registered module cleanup hooks, including after partial startup.
|
|
10347
|
+
A Node loader registration is process-scoped; its message channel is closed at
|
|
10348
|
+
teardown. Module-owned timers/listeners must register their cleanup hooks.
|
|
10349
|
+
|
|
10350
|
+
|
|
10351
|
+
## use-middleware `@warlock.js/core/skills/use-middleware/SKILL.md`
|
|
9675
10352
|
|
|
9676
10353
|
---
|
|
9677
10354
|
name: use-middleware
|
|
@@ -9969,7 +10646,7 @@ router.post("/private/preview", ({ response }) => response.success({ ready: true
|
|
|
9969
10646
|
```
|
|
9970
10647
|
|
|
9971
10648
|
|
|
9972
|
-
## use-model-transformers `@warlock.js/core/use-model-transformers/SKILL.md`
|
|
10649
|
+
## use-model-transformers `@warlock.js/core/skills/use-model-transformers/SKILL.md`
|
|
9973
10650
|
|
|
9974
10651
|
---
|
|
9975
10652
|
name: use-model-transformers
|
|
@@ -10202,7 +10879,7 @@ Rule of thumb: transformers are for **pure, deterministic** transforms of the ro
|
|
|
10202
10879
|
- The `warlock-conventions` topic — schema files live in `src/app/<module>/models/<entity>/<entity>.model.ts`.
|
|
10203
10880
|
|
|
10204
10881
|
|
|
10205
|
-
## use-repository `@warlock.js/core/use-repository/SKILL.md`
|
|
10882
|
+
## use-repository `@warlock.js/core/skills/use-repository/SKILL.md`
|
|
10206
10883
|
|
|
10207
10884
|
---
|
|
10208
10885
|
name: use-repository
|
|
@@ -10636,7 +11313,7 @@ The third arg is a context object — `{ allValues, dateFormat, ... }` — for f
|
|
|
10636
11313
|
- The `warlock-conventions` topic — controller → service → repository → model layering.
|
|
10637
11314
|
|
|
10638
11315
|
|
|
10639
|
-
## use-request-locals `@warlock.js/core/use-request-locals/SKILL.md`
|
|
11316
|
+
## use-request-locals `@warlock.js/core/skills/use-request-locals/SKILL.md`
|
|
10640
11317
|
|
|
10641
11318
|
---
|
|
10642
11319
|
name: use-request-locals
|
|
@@ -10807,7 +11484,7 @@ Writing `request.locals.organization` does not affect `request.all()`, `request.
|
|
|
10807
11484
|
- The `create-controller` topic — reading locals in downstream controllers.
|
|
10808
11485
|
|
|
10809
11486
|
|
|
10810
|
-
## validate-input `@warlock.js/core/validate-input/SKILL.md`
|
|
11487
|
+
## validate-input `@warlock.js/core/skills/validate-input/SKILL.md`
|
|
10811
11488
|
|
|
10812
11489
|
---
|
|
10813
11490
|
name: validate-input
|
|
@@ -11165,7 +11842,7 @@ updateProductController.validation = {
|
|
|
11165
11842
|
- The `warlock-conventions` topic — `Request<Schema>` type-alias convention.
|
|
11166
11843
|
|
|
11167
11844
|
|
|
11168
|
-
## warlock-agents-md `@warlock.js/core/warlock-agents-md/SKILL.md`
|
|
11845
|
+
## warlock-agents-md `@warlock.js/core/skills/warlock-agents-md/SKILL.md`
|
|
11169
11846
|
|
|
11170
11847
|
---
|
|
11171
11848
|
name: warlock-agents-md
|
|
@@ -11265,7 +11942,7 @@ refresher.dispose();
|
|
|
11265
11942
|
- The `warlock-conventions` topic — the conventions listed in the section.
|
|
11266
11943
|
|
|
11267
11944
|
|
|
11268
|
-
## warlock-board `@warlock.js/core/warlock-board/SKILL.md`
|
|
11945
|
+
## warlock-board `@warlock.js/core/skills/warlock-board/SKILL.md`
|
|
11269
11946
|
|
|
11270
11947
|
---
|
|
11271
11948
|
name: warlock-board
|
|
@@ -11392,7 +12069,7 @@ const { messages } = await watchBoard({ agent: "implementer", timeoutMs: 30 * 60
|
|
|
11392
12069
|
- The `warlock-inspect` topic — the app's facts as JSON before you change it.
|
|
11393
12070
|
|
|
11394
12071
|
|
|
11395
|
-
## warlock-check `@warlock.js/core/warlock-check/SKILL.md`
|
|
12072
|
+
## warlock-check `@warlock.js/core/skills/warlock-check/SKILL.md`
|
|
11396
12073
|
|
|
11397
12074
|
---
|
|
11398
12075
|
name: warlock-check
|
|
@@ -11471,7 +12148,7 @@ npx warlock typecheck --changed # changed files + the files importing them
|
|
|
11471
12148
|
npx warlock typecheck --json --project tsconfig.app.json
|
|
11472
12149
|
```
|
|
11473
12150
|
|
|
11474
|
-
`--changed` checks
|
|
12151
|
+
`--changed` checks diagnostics for changed files and their transitive importers. The native compiler still performs an incremental program compilation.
|
|
11475
12152
|
|
|
11476
12153
|
## `warlock explain`
|
|
11477
12154
|
|
|
@@ -11532,8 +12209,79 @@ Migrate lint and format tooling with
|
|
|
11532
12209
|
`npx warlock upgrade --from 5.32 --to 5.33.0 --dry-run`, then remove `--dry-run`
|
|
11533
12210
|
and work through the manual-review list. Use `--lint=oxc` for new apps.
|
|
11534
12211
|
|
|
12212
|
+
## Web and response contract conventions
|
|
12213
|
+
|
|
12214
|
+
The following codes default to **warnings**, so upgrading does not fail existing projects.
|
|
12215
|
+
They parse real TypeScript/JSX syntax with the same oxc-parser adapter as the existing conventions.
|
|
12216
|
+
|
|
12217
|
+
| Code | Convention | Remedy |
|
|
12218
|
+
| -------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
12219
|
+
| `WL9013` | A component file exceeds 150 lines | Split components and extract hooks; configure `componentLineLimit`. |
|
|
12220
|
+
| `WL9014` | More than one component in a file | Move each component into its own TSX file. |
|
|
12221
|
+
| `WL9015` | State/effects or event-handler functions inside a component | Move `useState`, `useReducer`, `useEffect`, `useLayoutEffect` and handlers into `use-*.ts` hooks. |
|
|
12222
|
+
| `WL9016` | Native select, checkbox, radio, date, time or datetime-local input | Use the design-system control. Native controls are allowed in `src/web/design-system/**`. |
|
|
12223
|
+
| `WL9017` | DOM input/textarea/select native validation | Use shared schema validation instead of `required`, `pattern`, `min`, `max`, `minLength`, `maxLength` or input `type="email"`. Custom component props are allowed. |
|
|
12224
|
+
| `WL9018` | Exported `RequestHandler` in `src/app/**/controllers` lacks `responseSchema` | Assign `handler.responseSchema` to document each response status. |
|
|
12225
|
+
|
|
12226
|
+
Web rules apply to `src/web/**/*.tsx`. Components are top-level JSX-returning functions
|
|
12227
|
+
with a capitalized name (or an anonymous default export). Hook files and ordinary helpers
|
|
12228
|
+
are excluded. Handler checks recognize inline JSX callbacks, `handle*`/`on*` functions,
|
|
12229
|
+
and local function values referenced by event props. They do not execute code or infer
|
|
12230
|
+
runtime spreads, factory contracts or values imported from other files.
|
|
12231
|
+
|
|
12232
|
+
Create `.warlock-check.json` in the project root:
|
|
12233
|
+
|
|
12234
|
+
```json
|
|
12235
|
+
{
|
|
12236
|
+
"componentLineLimit": 180,
|
|
12237
|
+
"rules": {
|
|
12238
|
+
"WL9013": "warning",
|
|
12239
|
+
"WL9015": "error",
|
|
12240
|
+
"WL9016": "off"
|
|
12241
|
+
}
|
|
12242
|
+
}
|
|
12243
|
+
```
|
|
12244
|
+
|
|
12245
|
+
`rules` accepts `"off"`, `"warning"`, or `"error"` for convention codes, including
|
|
12246
|
+
existing codes. Per-line `// warlock-check-ignore WL9015 -- reason` works as before.
|
|
12247
|
+
Use `npx warlock explain WL9015` for the registry explanation.
|
|
12248
|
+
|
|
12249
|
+
```bash
|
|
12250
|
+
npx warlock check --changed --fix --json
|
|
12251
|
+
```
|
|
12252
|
+
|
|
12253
|
+
`--changed` selects tracked changes against HEAD plus untracked source files and their
|
|
12254
|
+
transitive import dependents (including re-exports, type imports and related tests).
|
|
12255
|
+
Resolution uses the project's compiler configuration, with relative/app imports as a
|
|
12256
|
+
fallback for files outside that configuration. Lint and conventions receive that selection;
|
|
12257
|
+
Vitest uses related mode. TypeScript still compiles incrementally as a program, then filters
|
|
12258
|
+
diagnostics to the selection and its importers; this is not a partial compilation.
|
|
12259
|
+
|
|
12260
|
+
`--fix` applies safe convention edits before the pipeline. Currently it removes `.js`
|
|
12261
|
+
suffixes from relative imports (`WL9002`). Ignored/disabled findings are not fixed.
|
|
12262
|
+
Component extraction, control replacement and response schemas require human decisions.
|
|
12263
|
+
Problems that have a safe edit include optional `fix: { start, end, text }` in JSON;
|
|
12264
|
+
`start` and `end` are zero-based source string offsets with an exclusive end.
|
|
12265
|
+
The report otherwise keeps schema version 1 and its existing shape. Applied fixes disappear
|
|
12266
|
+
from the final report; unresolved findings remain. Review the resulting source diff.
|
|
12267
|
+
|
|
12268
|
+
Programmatic usage: `await runCheck({ changed: true, fix: true })` from
|
|
12269
|
+
`@warlock.js/core/tooling`.
|
|
12270
|
+
|
|
12271
|
+
## Explain a source file
|
|
11535
12272
|
|
|
11536
|
-
|
|
12273
|
+
`npx warlock explain src/app/posts/services/list.service.ts --json` reports the file's module, convention role, exports, imports, importers and reaching routes/pages. It boots the app for route/page registrations; error-code explanations remain static. See the `warlock-inspect` topic for graph semantics and the boot-free `npx warlock inspect --section files --json` command.
|
|
12274
|
+
|
|
12275
|
+
Starter controllers declare a response contract for each returned status. For example:
|
|
12276
|
+
|
|
12277
|
+
```ts
|
|
12278
|
+
contactController.responseSchema = { 200: { body: { message: "string" } } };
|
|
12279
|
+
```
|
|
12280
|
+
|
|
12281
|
+
Use `npx warlock inspect --section files --json` for static inspection without booting the app; `npx warlock explain src/app/contact/controllers/contact.controller.ts` explains its entry points.
|
|
12282
|
+
|
|
12283
|
+
|
|
12284
|
+
## warlock-conventions `@warlock.js/core/skills/warlock-conventions/SKILL.md`
|
|
11537
12285
|
|
|
11538
12286
|
---
|
|
11539
12287
|
name: warlock-conventions
|
|
@@ -11789,7 +12537,7 @@ The comment covers the codes it names on its own line and the next; with no code
|
|
|
11789
12537
|
- The `send-response` topic — the full Response helper surface.
|
|
11790
12538
|
|
|
11791
12539
|
|
|
11792
|
-
## warlock-doctor `@warlock.js/core/warlock-doctor/SKILL.md`
|
|
12540
|
+
## warlock-doctor `@warlock.js/core/skills/warlock-doctor/SKILL.md`
|
|
11793
12541
|
|
|
11794
12542
|
---
|
|
11795
12543
|
name: warlock-doctor
|
|
@@ -11972,7 +12720,7 @@ formatting. Oxlint/Oxfmt are the only supported tools in 5.34.0. Use the
|
|
|
11972
12720
|
Migrate with `npx warlock upgrade --from 5.32 --to 5.33.0` (preview with `--dry-run`).
|
|
11973
12721
|
|
|
11974
12722
|
|
|
11975
|
-
## warlock-inspect `@warlock.js/core/warlock-inspect/SKILL.md`
|
|
12723
|
+
## warlock-inspect `@warlock.js/core/skills/warlock-inspect/SKILL.md`
|
|
11976
12724
|
|
|
11977
12725
|
---
|
|
11978
12726
|
name: warlock-inspect
|
|
@@ -11989,9 +12737,9 @@ npx warlock inspect --json > app.json # the full report
|
|
|
11989
12737
|
npx warlock inspect --json --section routes,models # only some sections
|
|
11990
12738
|
```
|
|
11991
12739
|
|
|
11992
|
-
|
|
12740
|
+
Except for `--section files` (static source inspection without app boot), it boots like `warlock doctor`: connectors boot (routes register) but none starts — no database, cache or socket connection, no port. With `--json`, stdout is only the JSON.
|
|
11993
12741
|
|
|
11994
|
-
Sections: `routes`, `models`, `resources`, `config`, `events`, `pages`, `jobs`, `schedules`, `packages`. An unknown section fails before the app boots.
|
|
12742
|
+
Sections: `routes`, `models`, `resources`, `config`, `events`, `pages`, `jobs`, `schedules`, `packages`, `files`. An unknown section fails before the app boots.
|
|
11995
12743
|
|
|
11996
12744
|
## Answer questions with jq
|
|
11997
12745
|
|
|
@@ -12109,8 +12857,51 @@ const valid = new Ajv2020({ strict: false }).validate(INSPECT_REPORT_JSON_SCHEMA
|
|
|
12109
12857
|
- The `warlock-routes` topic — the human route table.
|
|
12110
12858
|
- The `generate-openapi` topic — the same route schemas as an OpenAPI document.
|
|
12111
12859
|
|
|
12860
|
+
## Session middleware in groups
|
|
12861
|
+
|
|
12862
|
+
`sessionMiddleware({ userTypes: "user" })` publishes the same `warlock.auth`
|
|
12863
|
+
descriptor as `authMiddleware()`. Group middleware is inherited by nested
|
|
12864
|
+
routes, so `inspect` and the generated `agents-md` summary recognize these guards.
|
|
12865
|
+
`{ optional: true }` reports `auth.required: false`, with sources and user types
|
|
12866
|
+
retained. Such routes remain in the unguarded-write list. Legacy descriptors
|
|
12867
|
+
without `required` still mean required authentication.
|
|
12868
|
+
|
|
12869
|
+
```ts
|
|
12870
|
+
import { router } from "@warlock.js/core";
|
|
12871
|
+
import { sessionMiddleware } from "@warlock.js/auth";
|
|
12872
|
+
|
|
12873
|
+
router.group({ middleware: [sessionMiddleware({ userTypes: "user" })] }, () => {
|
|
12874
|
+
router.post("/settings", async ({ response }) => response.success({ saved: true }));
|
|
12875
|
+
});
|
|
12876
|
+
```
|
|
12877
|
+
|
|
12878
|
+
## Source files and reachability
|
|
12879
|
+
|
|
12880
|
+
```bash
|
|
12881
|
+
npx warlock inspect --section files --json
|
|
12882
|
+
npx warlock explain src/app/posts/services/list.service.ts
|
|
12883
|
+
npx warlock explain src/web/components/Post.tsx --json
|
|
12884
|
+
npx warlock explain WL3001 --json
|
|
12885
|
+
```
|
|
12886
|
+
|
|
12887
|
+
The `files` section reads every file under `src/`, without importing app modules or booting connectors. Each entry has `file`, `module`, `role`, `exports`, `imports` and `importedBy`. Paths are relative to the project root. Modules use `src/app/<module>` or the containing top-level `src` directory. Roles follow file names and directories: controller, service, repository, model, migration, schema, resource, routes, page, setup, layout, root, hook, component, util, type, config and spec; unmatched files use `source`. Non-code assets have empty exports and imports.
|
|
12888
|
+
|
|
12889
|
+
Each code file is parsed once with OXC per inspection. Export names include types, aliases, destructured bindings, `default` and `*` for star re-exports; star exports are recorded rather than expanded. Imports include type imports, side effects, re-exports and literal dynamic imports. Each import is `{ specifier, file }`: `file` is the resolved local path, or `null` for external/unresolved imports. Resolution follows inherited tsconfig path aliases, fallback targets, baseUrl, extensionless files and directory indexes within `src/`. Computed dynamic imports and CommonJS `require()` calls are not graph edges. Parse/config failures appear in `warnings`.
|
|
12112
12890
|
|
|
12113
|
-
|
|
12891
|
+
File explanations accept a project-relative or absolute path. They boot the app to read the existing routes/pages sections, then walk the static graph to find entry points reaching the file, including page layouts and loaders. Type-only edges participate; this is source reachability, not proof that runtime execution calls a function. Every route declared in a reaching route module is listed. Boot warnings remain in the JSON explanation and human output, so missing registrations are visible. `warlock explain <WLxxxx>` keeps its static error-code behavior.
|
|
12892
|
+
|
|
12893
|
+
```ts
|
|
12894
|
+
import { inspectApp, explainFile } from "@warlock.js/core/tooling";
|
|
12895
|
+
|
|
12896
|
+
const report = await inspectApp({ sections: ["files"] });
|
|
12897
|
+
const source = explainFile("src/app/posts/services/list.service.ts", report);
|
|
12898
|
+
console.log(source.role, source.exports, source.importedBy);
|
|
12899
|
+
```
|
|
12900
|
+
|
|
12901
|
+
For route/page reachability in code, call `inspectApp({ sections: ["files", "routes", "pages"] })` in an already booted app. `inspect_files` exposes the section through MCP with a case-insensitive `filter` on file, module or role.
|
|
12902
|
+
|
|
12903
|
+
|
|
12904
|
+
## warlock-mcp `@warlock.js/core/skills/warlock-mcp/SKILL.md`
|
|
12114
12905
|
|
|
12115
12906
|
---
|
|
12116
12907
|
name: warlock-mcp
|
|
@@ -12233,8 +13024,12 @@ await server.close();
|
|
|
12233
13024
|
- The `warlock-trace` topic — send a request and read its timeline.
|
|
12234
13025
|
- The `expose-as-mcp-server` topic of @warlock.js/ai-tools — the transport underneath.
|
|
12235
13026
|
|
|
13027
|
+
## Source file graph
|
|
13028
|
+
|
|
13029
|
+
Call `inspect_files` with `{ "filter": "service" }` for file paths, modules, convention roles, export names, resolved imports and reverse `importedBy` edges. This section scans `src/` without executing source modules; files-only requests skip the lazy app boot. `inspect_app` includes a `files` count. See `npx warlock explain src/app/posts/services/list.service.ts --json` for reaching routes and pages.
|
|
12236
13030
|
|
|
12237
|
-
|
|
13031
|
+
|
|
13032
|
+
## warlock-routes `@warlock.js/core/skills/warlock-routes/SKILL.md`
|
|
12238
13033
|
|
|
12239
13034
|
---
|
|
12240
13035
|
name: warlock-routes
|
|
@@ -12374,11 +13169,11 @@ and exits non-zero.
|
|
|
12374
13169
|
- The `write-cli-command` topic — the command + `preload` shape `routesCommand` is built from.
|
|
12375
13170
|
|
|
12376
13171
|
|
|
12377
|
-
## warlock-trace `@warlock.js/core/warlock-trace/SKILL.md`
|
|
13172
|
+
## warlock-trace `@warlock.js/core/skills/warlock-trace/SKILL.md`
|
|
12378
13173
|
|
|
12379
13174
|
---
|
|
12380
13175
|
name: warlock-trace
|
|
12381
|
-
description: 'Verify runtime behaviour without a browser: `warlock trace <method> <url> [--body json|@file] [--header k:v] [--json]` sends one request to the running `warlock dev` server and prints what devtools recorded for it — middleware chain with timings, validation result and errors, handler time, queries with N+1 flags, logs, cache operations, captured mails, the response and timings — as a versioned report (`schemaVersion: 1`); `traceRequest()` returns the same object. Triggers: `warlock trace`, `traceRequest`, `TraceReport`, `buildTraceReport`, `x-request-id`, `.warlock/dev-server.json`, `WL8008`, `WL8009`, `WL5004`, `--id-header`; "did validation pass for this request", "which queries does this endpoint run", "is there an N+1 on this route", "what middleware runs on this route", "check the endpoint after my change", "request timeline as JSON". Skip: the devtools dashboard in a browser — the `devtools
|
|
13176
|
+
description: 'Verify runtime behaviour without a browser: `warlock trace <method> <url> [--body json|@file] [--header k:v] [--json]` sends one request to the running `warlock dev` server and prints what devtools recorded for it — middleware chain with timings, validation result and errors, handler time, queries with N+1 flags, logs, cache operations, captured mails, the response and timings — as a versioned report (`schemaVersion: 1`); `traceRequest()` returns the same object. Triggers: `warlock trace`, `traceRequest`, `TraceReport`, `buildTraceReport`, `x-request-id`, `.warlock/dev-server.json`, `WL8008`, `WL8009`, `WL5004`, `--id-header`; "did validation pass for this request", "which queries does this endpoint run", "is there an N+1 on this route", "what middleware runs on this route", "check the endpoint after my change", "request timeline as JSON". Skip: the devtools dashboard in a browser — the `warlock-js-devtools` skill; static route facts (guard, rate limit, schemas) — the `warlock-inspect` topic; typecheck/lint/tests — the `warlock-check` topic.'
|
|
12382
13177
|
---
|
|
12383
13178
|
|
|
12384
13179
|
# Warlock — `warlock trace`
|
|
@@ -12456,10 +13251,10 @@ Devtools is checked before anything is sent. With `--json` a failure prints `{ "
|
|
|
12456
13251
|
|
|
12457
13252
|
- The `warlock-inspect` topic — the static facts of a route (guard, rate limit, schemas).
|
|
12458
13253
|
- The `warlock-check` topic — typecheck, lint, tests and conventions in one run.
|
|
12459
|
-
- The `devtools
|
|
13254
|
+
- The `warlock-js-devtools` skill — the dashboard behind the timeline.
|
|
12460
13255
|
|
|
12461
13256
|
|
|
12462
|
-
## wire-socket `@warlock.js/core/wire-socket/SKILL.md`
|
|
13257
|
+
## wire-socket `@warlock.js/core/skills/wire-socket/SKILL.md`
|
|
12463
13258
|
|
|
12464
13259
|
---
|
|
12465
13260
|
name: wire-socket
|
|
@@ -12770,7 +13565,7 @@ CLI commands, scheduled jobs, queue workers — anything running outside an HTTP
|
|
|
12770
13565
|
- The `warlock-conventions` topic — where socket-related code lives (a setup function for handler registration, `services/` for emit sites).
|
|
12771
13566
|
|
|
12772
13567
|
|
|
12773
|
-
## write-cli-command `@warlock.js/core/write-cli-command/SKILL.md`
|
|
13568
|
+
## write-cli-command `@warlock.js/core/skills/write-cli-command/SKILL.md`
|
|
12774
13569
|
|
|
12775
13570
|
---
|
|
12776
13571
|
name: write-cli-command
|
|
@@ -13111,11 +13906,29 @@ Set a unique owner and registry per app instance, then import `processes` from
|
|
|
13111
13906
|
the generated module and await `processes.spawn({ kind: "worker", command,
|
|
13112
13907
|
args, labels })`. Consume output and await `handle.exited`. Use handle stops or
|
|
13113
13908
|
label filters; never stop processes by command or name. `spawnPty()` lazily
|
|
13114
|
-
loads separately installed optional peer `node-pty`. The `
|
|
13115
|
-
skill
|
|
13909
|
+
loads separately installed optional peer `node-pty`. The `warlock-js-process`
|
|
13910
|
+
skill contains complete working examples.
|
|
13911
|
+
|
|
13912
|
+
## Add the shared design system
|
|
13913
|
+
|
|
13914
|
+
```bash
|
|
13915
|
+
npx warlock add tailwind
|
|
13916
|
+
npx warlock add design-system
|
|
13917
|
+
```
|
|
13918
|
+
|
|
13919
|
+
`design-system` refuses before dependency installation when Tailwind is absent
|
|
13920
|
+
from `package.json` or `src/web/app.css`. It adds pinned shadcn primitives and
|
|
13921
|
+
Mongez controls to `src/web/design-system`, preserving existing source files.
|
|
13922
|
+
Use `--no-install` to record dependencies only, or `--dry-run --json` to preview
|
|
13923
|
+
files and dependencies without writing. For example, a generated control is
|
|
13924
|
+
`import TextInput from "web/design-system/components/text-input";` with
|
|
13925
|
+
`<TextInput id="profile-name" name="name" label="Name" />`.
|
|
13926
|
+
See Web's `build-accessible-controls` topic for the complete control contract.
|
|
13927
|
+
|
|
13928
|
+
Built-in `npx warlock add notifications` preloads `config: ["app"]` so generated locale stubs respect `app.localeCodes`, without bootstrap hooks or connectors.
|
|
13116
13929
|
|
|
13117
13930
|
|
|
13118
|
-
## write-middleware `@warlock.js/core/write-middleware/SKILL.md`
|
|
13931
|
+
## write-middleware `@warlock.js/core/skills/write-middleware/SKILL.md`
|
|
13119
13932
|
|
|
13120
13933
|
---
|
|
13121
13934
|
name: write-middleware
|
|
@@ -13343,7 +14156,7 @@ export const optionalAuth: Middleware = async ({ request, response }) => {
|
|
|
13343
14156
|
- The `warlock-conventions` topic — the `guarded()` / `guardedAdmin()` / `publicRoutes()` convention.
|
|
13344
14157
|
|
|
13345
14158
|
|
|
13346
|
-
## write-seeder `@warlock.js/core/write-seeder/SKILL.md`
|
|
14159
|
+
## write-seeder `@warlock.js/core/skills/write-seeder/SKILL.md`
|
|
13347
14160
|
|
|
13348
14161
|
---
|
|
13349
14162
|
name: write-seeder
|
|
@@ -13738,7 +14551,7 @@ warlock seed --transaction # (default true) — pass --transaction=false to s
|
|
|
13738
14551
|
- The `warlock-conventions` topic — `seeds/` is the canonical folder name (singular `seed/` is incorrect).
|
|
13739
14552
|
|
|
13740
14553
|
|
|
13741
|
-
## write-use-case `@warlock.js/core/write-use-case/SKILL.md`
|
|
14554
|
+
## write-use-case `@warlock.js/core/skills/write-use-case/SKILL.md`
|
|
13742
14555
|
|
|
13743
14556
|
---
|
|
13744
14557
|
name: write-use-case
|