@warlock.js/core 5.34.3 → 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 +44 -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/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 +31 -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 +3 -0
- package/esm/http/error-response.d.mts.map +1 -1
- package/esm/http/error-response.mjs +8 -2
- package/esm/http/error-response.mjs.map +1 -1
- 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 +3 -2
- 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/server.mjs +1 -1
- package/esm/index.d.mts +8 -6
- 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/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 +763 -9
- package/llms.txt +2 -0
- 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 +7 -0
- package/skills/create-module/SKILL.md +9 -0
- package/skills/generate-web-ui/SKILL.md +71 -0
- package/skills/index.md +2 -2
- package/skills/run-app/SKILL.md +1 -1
- package/skills/send-mail/SKILL.md +25 -0
- package/skills/send-response/SKILL.md +50 -0
- package/skills/test-browser/SKILL.md +53 -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 +18 -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/write-cli-command/SKILL.md +18 -0
- package/esm/production/build-app-production.mjs +0 -14
- package/esm/production/build-app-production.mjs.map +0 -1
package/llms-full.txt
CHANGED
|
@@ -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
|
|
@@ -543,6 +543,61 @@ 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-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
|
+
|
|
546
601
|
## build-restful `@warlock.js/core/skills/build-restful/SKILL.md`
|
|
547
602
|
|
|
548
603
|
---
|
|
@@ -814,6 +869,62 @@ 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
929
|
## build-url `@warlock.js/core/skills/build-url/SKILL.md`
|
|
819
930
|
|
|
@@ -1452,6 +1563,13 @@ The synchronous formatter receives `HttpErrorResponseContext`: `{ status: number
|
|
|
1452
1563
|
|
|
1453
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.
|
|
1454
1565
|
|
|
1566
|
+
## Co-located configuration tests
|
|
1567
|
+
|
|
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
|
+
|
|
1455
1573
|
|
|
1456
1574
|
## create-controller `@warlock.js/core/skills/create-controller/SKILL.md`
|
|
1457
1575
|
|
|
@@ -1952,6 +2070,15 @@ built-in import sorting.
|
|
|
1952
2070
|
After editing generated code, run `npm run format` and `npm run lint:check`.
|
|
1953
2071
|
Formatting retains unused imports; the linter reports them.
|
|
1954
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
|
+
|
|
1955
2082
|
|
|
1956
2083
|
## define-resource `@warlock.js/core/skills/define-resource/SKILL.md`
|
|
1957
2084
|
|
|
@@ -2752,6 +2879,81 @@ const { document, warnings } = buildOpenApiDocument(router.list(), {
|
|
|
2752
2879
|
- The `warlock-routes` topic — list the routes the document is built from.
|
|
2753
2880
|
|
|
2754
2881
|
|
|
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
|
+
|
|
2755
2957
|
## hash-password `@warlock.js/core/skills/hash-password/SKILL.md`
|
|
2756
2958
|
|
|
2757
2959
|
---
|
|
@@ -4843,7 +5045,7 @@ Omit switches to run TypeScript and installed/configured Oxlint. Set `oxlint: fa
|
|
|
4843
5045
|
|
|
4844
5046
|
## `warlock build` — production bundle
|
|
4845
5047
|
|
|
4846
|
-
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.
|
|
4847
5049
|
|
|
4848
5050
|
### `build.*` config knobs
|
|
4849
5051
|
|
|
@@ -5782,6 +5984,31 @@ await Mail.to(user.email)
|
|
|
5782
5984
|
- The `configure-app` topic — `src/config/mail.ts` shape and `env()` patterns.
|
|
5783
5985
|
- The `warlock-conventions` topic — where mail-sending services live.
|
|
5784
5986
|
|
|
5987
|
+
## Preview development mail in devtools
|
|
5988
|
+
|
|
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
|
+
|
|
5785
6012
|
|
|
5786
6013
|
## send-response `@warlock.js/core/skills/send-response/SKILL.md`
|
|
5787
6014
|
|
|
@@ -6296,6 +6523,56 @@ The synchronous formatter receives `HttpErrorResponseContext`: `{ status: number
|
|
|
6296
6523
|
|
|
6297
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.
|
|
6298
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.
|
|
6544
|
+
|
|
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
|
+
|
|
6299
6576
|
|
|
6300
6577
|
## store-file `@warlock.js/core/skills/store-file/SKILL.md`
|
|
6301
6578
|
|
|
@@ -7032,6 +7309,59 @@ recorded process group (Windows: verified PID tree). It retains disposable files
|
|
|
7032
7309
|
if ownership verification fails and reports the failure in evidence. Do not add
|
|
7033
7310
|
process-name or port-based kills to a spec.
|
|
7034
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.
|
|
7350
|
+
|
|
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
|
+
|
|
7035
7365
|
|
|
7036
7366
|
## test-fakes `@warlock.js/core/skills/test-fakes/SKILL.md`
|
|
7037
7367
|
|
|
@@ -7625,6 +7955,97 @@ This is fine for normal test flow. It bites when you're inside a transaction the
|
|
|
7625
7955
|
- The `warlock-conventions` topic — module layout, where `tests/*.test.ts` files live.
|
|
7626
7956
|
- The `write-cli-command` topic — `warlock add test` scaffolds both global + worker setup files.
|
|
7627
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
|
+
```
|
|
7992
|
+
|
|
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
|
+
|
|
7628
8049
|
|
|
7629
8050
|
## test-service `@warlock.js/core/skills/test-service/SKILL.md`
|
|
7630
8051
|
|
|
@@ -7643,9 +8064,9 @@ For unit tests, you import the thing under test and call it directly. No HTTP, n
|
|
|
7643
8064
|
|
|
7644
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.**
|
|
7645
8066
|
|
|
7646
|
-
**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.**
|
|
7647
8068
|
|
|
7648
|
-
|
|
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.
|
|
7649
8070
|
|
|
7650
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.**
|
|
7651
8072
|
|
|
@@ -8047,7 +8468,7 @@ Node process, preserve the source package's module mode. ESM fixtures need a
|
|
|
8047
8468
|
CommonJS and named exports fail before the behavior under test runs. Assert the
|
|
8048
8469
|
child process exit status with its captured stderr before checking stdout.
|
|
8049
8470
|
|
|
8050
|
-
-
|
|
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.
|
|
8051
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.
|
|
8052
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.
|
|
8053
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.
|
|
@@ -8075,6 +8496,164 @@ setupTest({ importModule: (file) => import(file) });
|
|
|
8075
8496
|
|
|
8076
8497
|
The generated `src/test-setup.ts` already does this. Without it, `setupTest()` under Vitest warns once.
|
|
8077
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
|
|
8533
|
+
|
|
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
|
+
|
|
8078
8657
|
|
|
8079
8658
|
## update-packages `@warlock.js/core/skills/update-packages/SKILL.md`
|
|
8080
8659
|
|
|
@@ -8763,6 +9342,24 @@ existing Oxc configs retain their categories. A real lockfile beside a workspace
|
|
|
8763
9342
|
marker determines the installer: `package-lock.json` plus `pnpm-workspace.yaml`
|
|
8764
9343
|
without `pnpm-lock.yaml` now produces `npm install`.
|
|
8765
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.
|
|
9352
|
+
|
|
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
|
+
|
|
8766
9363
|
|
|
8767
9364
|
## upload-file `@warlock.js/core/skills/upload-file/SKILL.md`
|
|
8768
9365
|
|
|
@@ -9729,6 +10326,27 @@ This produces `account.title` and `account.actions.save`. Keep every leaf comple
|
|
|
9729
10326
|
|
|
9730
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.
|
|
9731
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.
|
|
10342
|
+
|
|
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
|
+
|
|
9732
10350
|
|
|
9733
10351
|
## use-middleware `@warlock.js/core/skills/use-middleware/SKILL.md`
|
|
9734
10352
|
|
|
@@ -11530,7 +12148,7 @@ npx warlock typecheck --changed # changed files + the files importing them
|
|
|
11530
12148
|
npx warlock typecheck --json --project tsconfig.app.json
|
|
11531
12149
|
```
|
|
11532
12150
|
|
|
11533
|
-
`--changed` checks
|
|
12151
|
+
`--changed` checks diagnostics for changed files and their transitive importers. The native compiler still performs an incremental program compilation.
|
|
11534
12152
|
|
|
11535
12153
|
## `warlock explain`
|
|
11536
12154
|
|
|
@@ -11591,6 +12209,77 @@ Migrate lint and format tooling with
|
|
|
11591
12209
|
`npx warlock upgrade --from 5.32 --to 5.33.0 --dry-run`, then remove `--dry-run`
|
|
11592
12210
|
and work through the manual-review list. Use `--lint=oxc` for new apps.
|
|
11593
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
|
|
12272
|
+
|
|
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
|
+
|
|
11594
12283
|
|
|
11595
12284
|
## warlock-conventions `@warlock.js/core/skills/warlock-conventions/SKILL.md`
|
|
11596
12285
|
|
|
@@ -12048,9 +12737,9 @@ npx warlock inspect --json > app.json # the full report
|
|
|
12048
12737
|
npx warlock inspect --json --section routes,models # only some sections
|
|
12049
12738
|
```
|
|
12050
12739
|
|
|
12051
|
-
|
|
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.
|
|
12052
12741
|
|
|
12053
|
-
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.
|
|
12054
12743
|
|
|
12055
12744
|
## Answer questions with jq
|
|
12056
12745
|
|
|
@@ -12168,6 +12857,49 @@ const valid = new Ajv2020({ strict: false }).validate(INSPECT_REPORT_JSON_SCHEMA
|
|
|
12168
12857
|
- The `warlock-routes` topic — the human route table.
|
|
12169
12858
|
- The `generate-openapi` topic — the same route schemas as an OpenAPI document.
|
|
12170
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`.
|
|
12890
|
+
|
|
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
|
+
|
|
12171
12903
|
|
|
12172
12904
|
## warlock-mcp `@warlock.js/core/skills/warlock-mcp/SKILL.md`
|
|
12173
12905
|
|
|
@@ -12292,6 +13024,10 @@ await server.close();
|
|
|
12292
13024
|
- The `warlock-trace` topic — send a request and read its timeline.
|
|
12293
13025
|
- The `expose-as-mcp-server` topic of @warlock.js/ai-tools — the transport underneath.
|
|
12294
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.
|
|
13030
|
+
|
|
12295
13031
|
|
|
12296
13032
|
## warlock-routes `@warlock.js/core/skills/warlock-routes/SKILL.md`
|
|
12297
13033
|
|
|
@@ -13173,6 +13909,24 @@ label filters; never stop processes by command or name. `spawnPty()` lazily
|
|
|
13173
13909
|
loads separately installed optional peer `node-pty`. The `warlock-js-process`
|
|
13174
13910
|
skill contains complete working examples.
|
|
13175
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.
|
|
13929
|
+
|
|
13176
13930
|
|
|
13177
13931
|
## write-middleware `@warlock.js/core/skills/write-middleware/SKILL.md`
|
|
13178
13932
|
|