@warlock.js/core 5.26.0 → 5.27.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 +19 -0
- package/esm/cli/commands/openapi-generator.action.mjs +43 -0
- package/esm/cli/commands/openapi-generator.action.mjs.map +1 -0
- package/esm/cli/commands/openapi-generator.command.mjs +44 -0
- package/esm/cli/commands/openapi-generator.command.mjs.map +1 -0
- package/esm/cli/framework-cli-commands.mjs +2 -0
- package/esm/cli/framework-cli-commands.mjs.map +1 -1
- package/esm/dev-server/build-development-openapi-document.mjs +36 -0
- package/esm/dev-server/build-development-openapi-document.mjs.map +1 -0
- package/esm/dev-server/collect-development-api-routes.mjs +27 -0
- package/esm/dev-server/collect-development-api-routes.mjs.map +1 -0
- package/esm/dev-server/dev-logger.mjs +9 -1
- package/esm/dev-server/dev-logger.mjs.map +1 -1
- package/esm/dev-server/development-server.mjs +6 -3
- package/esm/dev-server/development-server.mjs.map +1 -1
- package/esm/dev-server/files-orchestrator.mjs +17 -1
- package/esm/dev-server/files-orchestrator.mjs.map +1 -1
- package/esm/dev-server/model-resource-typings.mjs +246 -0
- package/esm/dev-server/model-resource-typings.mjs.map +1 -0
- package/esm/dev-server/type-generator.mjs +46 -2
- package/esm/dev-server/type-generator.mjs.map +1 -1
- package/esm/http/index.d.mts +2 -1
- package/esm/http/request.d.mts +2 -2
- package/esm/http/request.d.mts.map +1 -1
- package/esm/http/serialized.d.mts +114 -0
- package/esm/http/serialized.d.mts.map +1 -0
- package/esm/index.d.mts +8 -3
- package/esm/index.mjs +4 -1
- package/esm/openapi/build-openapi-document.d.mts +17 -0
- package/esm/openapi/build-openapi-document.d.mts.map +1 -0
- package/esm/openapi/build-openapi-document.mjs +124 -0
- package/esm/openapi/build-openapi-document.mjs.map +1 -0
- package/esm/openapi/cast-schema.mjs +89 -0
- package/esm/openapi/cast-schema.mjs.map +1 -0
- package/esm/openapi/collect-openapi-document.mjs +40 -0
- package/esm/openapi/collect-openapi-document.mjs.map +1 -0
- package/esm/openapi/development-openapi-provider.d.mts +17 -0
- package/esm/openapi/development-openapi-provider.d.mts.map +1 -0
- package/esm/openapi/development-openapi-provider.mjs +21 -0
- package/esm/openapi/development-openapi-provider.mjs.map +1 -0
- package/esm/openapi/error-responses.mjs +67 -0
- package/esm/openapi/error-responses.mjs.map +1 -0
- package/esm/openapi/index.d.mts +3 -0
- package/esm/openapi/index.mjs +4 -0
- package/esm/openapi/openapi-child-output.mjs +57 -0
- package/esm/openapi/openapi-child-output.mjs.map +1 -0
- package/esm/openapi/openapi-protocol.mjs +30 -0
- package/esm/openapi/openapi-protocol.mjs.map +1 -0
- package/esm/openapi/paths.mjs +63 -0
- package/esm/openapi/paths.mjs.map +1 -0
- package/esm/openapi/request-schemas.mjs +138 -0
- package/esm/openapi/request-schemas.mjs.map +1 -0
- package/esm/openapi/response-schemas.mjs +85 -0
- package/esm/openapi/response-schemas.mjs.map +1 -0
- package/esm/openapi/schema-registry.mjs +143 -0
- package/esm/openapi/schema-registry.mjs.map +1 -0
- package/esm/openapi/security.mjs +98 -0
- package/esm/openapi/security.mjs.map +1 -0
- package/esm/openapi/types.d.mts +119 -0
- package/esm/openapi/types.d.mts.map +1 -0
- package/esm/production/registration-child-runner.mjs +139 -0
- package/esm/production/registration-child-runner.mjs.map +1 -0
- package/esm/production/route-registration-child.mjs +30 -2
- package/esm/production/route-registration-child.mjs.map +1 -1
- package/esm/production/route-registration-snapshot.mjs +22 -119
- package/esm/production/route-registration-snapshot.mjs.map +1 -1
- package/esm/resource/define-resource.d.mts +44 -14
- package/esm/resource/define-resource.d.mts.map +1 -1
- package/esm/resource/define-resource.mjs +0 -40
- package/esm/resource/define-resource.mjs.map +1 -1
- package/esm/resource/index.d.mts +1 -1
- package/esm/resource/resource.d.mts +4 -4
- package/esm/resource/resource.d.mts.map +1 -1
- package/esm/resource/resource.mjs.map +1 -1
- package/esm/resource/types.d.mts +89 -4
- package/esm/resource/types.d.mts.map +1 -1
- package/esm/restful/restful.d.mts +5 -5
- package/esm/restful/restful.d.mts.map +1 -1
- package/esm/router/index.d.mts +1 -1
- package/esm/router/named-api-routes-with-responses.mjs +59 -0
- package/esm/router/named-api-routes-with-responses.mjs.map +1 -0
- package/esm/router/response-schema-types.mjs +100 -0
- package/esm/router/response-schema-types.mjs.map +1 -0
- package/esm/router/router.d.mts +2 -2
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs +38 -11
- package/esm/router/router.mjs.map +1 -1
- package/esm/router/types.d.mts +16 -1
- package/esm/router/types.d.mts.map +1 -1
- package/llms-full.txt +265 -5
- package/llms.txt +3 -2
- package/package.json +13 -13
- package/skills/README.md +3 -2
- package/skills/create-controller/SKILL.md +39 -2
- package/skills/define-resource/SKILL.md +100 -1
- package/skills/generate-openapi/SKILL.md +120 -0
- package/skills/index.md +3 -3
- package/skills/validate-input/SKILL.md +2 -2
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: generate-openapi
|
|
3
|
+
description: 'Generate an OpenAPI 3.1 document from the registered routes with `warlock generate.openapi` (default `storage/openapi/openapi.json`) 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`, `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 — `@warlock.js/core/create-controller/SKILL.md`; the devtools dashboard — `@warlock.js/devtools/devtools-overview/SKILL.md`; listing routes — `@warlock.js/core/warlock-routes/SKILL.md`.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Warlock — generate an OpenAPI document
|
|
7
|
+
|
|
8
|
+
`warlock generate.openapi` writes an OpenAPI 3.1 JSON document derived from the routes your app registers. Nothing is annotated by hand: the document reads the route table, each handler's `validation`, `description` and `responseSchema`, and the auth middleware on the route.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npx warlock generate.openapi
|
|
12
|
+
# OpenAPI 3.1.0 written to <root>/storage/openapi/openapi.json (12 paths, 17 operations).
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Routes are read in the same isolated registration child process `warlock build` uses: your route modules are imported, **no connector starts** (no database, cache or socket), and the running dev server is untouched. If the app cannot be loaded the command fails; gaps in a single route never fail it, they are printed as warnings. The child honours `WARLOCK_ROUTE_REGISTRATION_TIMEOUT_MS`.
|
|
16
|
+
|
|
17
|
+
## Options
|
|
18
|
+
|
|
19
|
+
| Option | Default | Meaning |
|
|
20
|
+
| ----------------- | ---------------------------------------------- | ------------------------------------------------------------ |
|
|
21
|
+
| `--out`, `-o` | `storage/openapi/openapi.json` | Output file, relative to the project root. Parent folders are created. |
|
|
22
|
+
| `--title` | `name` in `package.json` (else `Warlock API`) | `info.title`. `info.version` is the `package.json` version (else `0.0.0`); `info.description` is its `description`. |
|
|
23
|
+
| `--server` | `http://<host>:<port>` from the `http` config | `servers[0].url`. A wildcard host (`0.0.0.0`, `::`) becomes `localhost`. |
|
|
24
|
+
| `--include-pages` | off | Also document page (SSR) routes; each is a `200` with `text/html`. |
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx warlock generate.openapi --out docs/openapi.json --title "Shop API" --server https://api.shop.test
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## What is documented
|
|
31
|
+
|
|
32
|
+
| Part | Source |
|
|
33
|
+
| ----------------------- | ----------------------------------------------------------------------------------------------- |
|
|
34
|
+
| Paths | `:id` becomes `{id}`. An `all` route expands into one operation per verb (get, post, put, patch, delete, options, head). |
|
|
35
|
+
| `operationId` | The route `name` (`name.<verb>` when an `all` route expands), else `<verb>_<path_slug>`. Duplicates are renamed with a warning. |
|
|
36
|
+
| Tag | The first static path segment (`/users/:id` is tagged `users`). |
|
|
37
|
+
| Summary / description | The route `label`; the route or handler `description`, plus a line about required user types and the cookie CSRF rule when the route is guarded. |
|
|
38
|
+
| Path parameters | `validation.params` gives typed ones; a path segment with no schema is a required `string`. |
|
|
39
|
+
| Request body / query | `validation.schema`, converted with the Seal schema's JSON Schema output. It is the JSON request body for POST/PUT/PATCH and query parameters for GET/HEAD/DELETE, following `validating` the way validation reads the request. |
|
|
40
|
+
| Responses | `handler.responseSchema`, one response per declared status (see below). A route with no 2xx declared gets a `200` description only. |
|
|
41
|
+
| `422` | Added when the route has `validation.schema`, shaped by `validation.response` (`errors`, `inputKey`, `inputError`, `status`; `Response.failedSchema` defaults to 422). Stored once as `components.schemas.ValidationFailed`. A route with only `validation.params` gets none. |
|
|
42
|
+
| `401` | Added when the route is guarded, as `{ error: string }` (`components.schemas.Unauthorized`). A status you declare yourself is kept. |
|
|
43
|
+
| Security | `authMiddleware()` tags the middleware it returns with a `Symbol.for("warlock.auth")` descriptor (`{ sources, userTypes }`); core reads it without importing auth. A header source is `bearerAuth` (HTTP bearer), a cookie source is `cookieAuth` (API key in that cookie); header plus cookie is an OR. Only the schemes a route uses are emitted. Middleware without a descriptor adds nothing. |
|
|
44
|
+
|
|
45
|
+
### Responses from `responseSchema`
|
|
46
|
+
|
|
47
|
+
The grammar is the one described in [`create-controller`](../create-controller/SKILL.md#declaring-response-types-with-responseschema): cast strings with suffixes, a resource, `[Resource]`, nested objects.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
loginController.responseSchema = {
|
|
51
|
+
200: { body: { user: UserResource, token: "string" } },
|
|
52
|
+
400: { body: { error: "string" } },
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
becomes
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
"200": {
|
|
60
|
+
"description": "Successful response",
|
|
61
|
+
"content": {
|
|
62
|
+
"application/json": {
|
|
63
|
+
"schema": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"properties": {
|
|
66
|
+
"user": { "$ref": "#/components/schemas/UserResource" },
|
|
67
|
+
"token": { "type": "string" }
|
|
68
|
+
},
|
|
69
|
+
"required": ["user", "token"]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- A cast maps to JSON Schema: `string` and `localized` to `string`, `url`/`uploadsUrl`/`storageUrl` to `string` with `format: uri`, `number`/`float` to `number`, `int` to `integer`, `boolean`, `object`, `array`, and `date` to the default date object (`iso`, `format`, `timestamp`, `humanTime`). `x[]` is an array; `x?` is `type: [T, "null"]`.
|
|
77
|
+
- A resource becomes a `components.schemas` entry named after its export (`UserResource`) and every use is a `$ref`. Nested, lazy and `"self"` fields reference the same entry. A resource that is not exported from a `*.resource.ts(x)` file gets a generic name.
|
|
78
|
+
- Every listed key is marked `required`.
|
|
79
|
+
|
|
80
|
+
## Warnings
|
|
81
|
+
|
|
82
|
+
What cannot be described statically is documented as `{}` and listed after the run. Common ones: a field builder or resolver function in a resource, an unknown cast, a `validation.validate` hook (custom middleware is not documented), a `validation.schema` that is not an object schema, an already-documented method+path, and a malformed auth descriptor. Fix them by declaring the field with a cast string.
|
|
83
|
+
|
|
84
|
+
## Live in development
|
|
85
|
+
|
|
86
|
+
With `@warlock.js/devtools` installed (`warlock add devtools`), `warlock dev` serves the same document:
|
|
87
|
+
|
|
88
|
+
- `http://localhost:<port>/__warlock/docs` renders it with Scalar. The dashboard has an **API** tab that embeds the same page.
|
|
89
|
+
- `GET /__warlock/api/openapi.json` returns the raw document, rebuilt from the routes registered in the running process on every request; it answers `503` when the host provides no generator.
|
|
90
|
+
- It is built in-process by `getDevelopmentOpenApiDocument()` (exported from `@warlock.js/core`; it throws outside `warlock dev`), so it follows your edits with no CLI run. Page routes are not included; each distinct warning is logged once per process.
|
|
91
|
+
- Devtools is dev-only and loopback-only, and the page makes no outside request (Scalar is vendored, no CDN).
|
|
92
|
+
|
|
93
|
+
## Use the file in a client
|
|
94
|
+
|
|
95
|
+
The output is plain OpenAPI 3.1 JSON, so any tool that reads an OpenAPI file can use it: import `storage/openapi/openapi.json` into Postman or Insomnia (their Import action), or feed it to a client generator. Support for 3.1 is the tool's; if an old version refuses it, update the tool. Regenerate the file whenever routes change, or commit it if you publish it.
|
|
96
|
+
|
|
97
|
+
## Programmatic use
|
|
98
|
+
|
|
99
|
+
`buildOpenApiDocument(routes, context)` is exported from `@warlock.js/core`: a pure function from `router.list()` routes to `{ document, warnings }`. `context` carries `info`, `servers`, `includePages`, `resolveResourceName` and `validationResponse`. Use it to build the document inside your own tooling; the CLI uses the same function inside the registration child.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { buildOpenApiDocument, router } from "@warlock.js/core";
|
|
103
|
+
|
|
104
|
+
const { document, warnings } = buildOpenApiDocument(router.list(), {
|
|
105
|
+
info: { title: "Shop API", version: "1.0.0" },
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Gotchas
|
|
110
|
+
|
|
111
|
+
- **The document describes what you declare.** A route with no `responseSchema` documents only a `200` description; nothing is inferred from the controller body.
|
|
112
|
+
- **Export each resource from its own `*.resource.ts` file** so the schema component is named after it.
|
|
113
|
+
- **Seal validators are converted in the child process**, so the document always reflects the schema the app actually registers.
|
|
114
|
+
- **The 401 body is `{ error: string }`.** The auth middleware's real rejection also carries an `errorCode`; the document does not list it.
|
|
115
|
+
|
|
116
|
+
## See also
|
|
117
|
+
|
|
118
|
+
- [`create-controller/SKILL.md`](../create-controller/SKILL.md) — `validation`, `description` and `responseSchema` on a handler.
|
|
119
|
+
- [`define-resource/SKILL.md`](../define-resource/SKILL.md) — resources, which become `components.schemas`.
|
|
120
|
+
- [`warlock-routes/SKILL.md`](../warlock-routes/SKILL.md) — list the routes the document is built from.
|
package/skills/index.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "The Warlock server framework: HTTP routes, controllers, responses, resources, repositories, CLI, mail, storage, security helpers. Exports `router`, `Request`, `Response`, `RequestHandler`, `useCase`, `RepositoryManager`, `defineResource`, `defineConfig`, `storage`, `sendMail`, `hashPassword`, `command`. Use for: add a route, write a controller, return a JSON response, hide a field from the response, upload a file, send an email, hash a password, add a CLI command, wire a socket, run or configure the app. Not this package: database models/queries → @warlock.js/cascade; input schemas → @warlock.js/seal; SSR pages → @warlock.js/web."
|
|
2
|
+
description: "The Warlock server framework: HTTP routes, controllers, responses, resources, repositories, CLI, mail, storage, security helpers. Exports `router`, `Request`, `Response`, `RequestHandler`, `useCase`, `RepositoryManager`, `defineResource`, `defineConfig`, `storage`, `sendMail`, `hashPassword`, `command`. Use for: add a route, write a controller, return a JSON response, hide a field from the response, upload a file, send an email, hash a password, add a CLI command, generate an OpenAPI document, type an API response, wire a socket, run or configure the app. Not this package: database models/queries → @warlock.js/cascade; input schemas → @warlock.js/seal; SSR pages → @warlock.js/web."
|
|
3
3
|
---
|
|
4
4
|
# @warlock.js/core
|
|
5
5
|
|
|
@@ -9,7 +9,7 @@ Core is the application runtime: it boots the app, auto-loads each module's spec
|
|
|
9
9
|
1. Scaffold a module (`create-module.md`), then declare routes in its `routes.ts` (`register-route.md`, `warlock-routes.md`).
|
|
10
10
|
2. Write a thin controller (`create-controller.md`); validate input with a schema attached to the handler (`validate-input.md`).
|
|
11
11
|
3. Put logic in a service or use case (`write-use-case.md`), and data access in a repository (`use-repository.md`).
|
|
12
|
-
4. Return via `response.*` (`send-response.md`) and shape output with a resource (`define-resource.md`).
|
|
12
|
+
4. Return via `response.*` (`send-response.md`) and shape output with a resource (`define-resource.md`); declare `responseSchema` for typed clients and docs (`create-controller.md`, `generate-openapi.md`).
|
|
13
13
|
5. Configure and run (`configure-app.md`, `run-app.md`); check setup with `warlock-doctor.md`.
|
|
14
14
|
|
|
15
15
|
## Topics by area
|
|
@@ -17,7 +17,7 @@ Core is the application runtime: it boots the app, auto-loads each module's spec
|
|
|
17
17
|
- **HTTP & routing:** register-route, warlock-routes, create-controller, use-middleware, write-middleware, validate-input, use-request-locals, build-restful, wire-socket
|
|
18
18
|
- **Responses & resources:** send-response, define-resource, use-model-transformers, use-localization
|
|
19
19
|
- **Domain layers:** write-use-case, use-repository, write-seeder
|
|
20
|
-
- **CLI & running the app:** run-app, write-cli-command, add-connector, health-checks, warlock-doctor
|
|
20
|
+
- **CLI & running the app:** run-app, write-cli-command, add-connector, health-checks, warlock-doctor, generate-openapi
|
|
21
21
|
- **Storage, files & mail:** store-file, upload-file, process-image, send-mail
|
|
22
22
|
- **Security & utilities:** hash-password, encrypt-data, retry-operation, benchmark-code, request-memo, request-tracing
|
|
23
23
|
- **Testing:** test-http, test-service
|
|
@@ -185,10 +185,10 @@ const updateAvatarSchema = v.object({
|
|
|
185
185
|
|
|
186
186
|
## What the framework sends on failure
|
|
187
187
|
|
|
188
|
-
The framework calls `response.failedSchema(result)` which sends `
|
|
188
|
+
The framework calls `response.failedSchema(result)` which sends `422` by default (set `validation.response.status` to change it) with the shape configured under `validation.response` (defaults shown):
|
|
189
189
|
|
|
190
190
|
```jsonc
|
|
191
|
-
//
|
|
191
|
+
// 422 Unprocessable Entity
|
|
192
192
|
{
|
|
193
193
|
"errors": [
|
|
194
194
|
{ "input": "email", "error": "The email must be a valid email" },
|