@databehave/server 0.1.0 → 0.1.2

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/docs/cli.md DELETED
@@ -1,76 +0,0 @@
1
- # CLI
2
-
3
- ```sh
4
- databehave-server databehave.jsonc # explicit path (required)
5
- databehave-server --open databehave.jsonc
6
- databehave-server --help
7
- ```
8
-
9
- The positional `<config>` argument is **required**. The CLI does not
10
- fall back to a default file — invoking `databehave-server` with no
11
- argument prints the usage text to `stderr` and exits with code **2**
12
- (bad usage). The same exit code is used for unknown flags, which
13
- additionally print:
14
-
15
- ```txt
16
- [@databehave/server] unknown option(s): <flag>
17
- ```
18
-
19
- Flags:
20
-
21
- | Flag | Effect |
22
- | --- | --- |
23
- | `--open` | After the server is listening, open the admin UI in the default browser. No-op (with a log line) when `admin` is absent or `admin.enabled !== true`. |
24
- | `-h`, `--help` | Print usage and exit `0`. |
25
-
26
- Exit codes:
27
-
28
- | Code | Meaning |
29
- | --- | --- |
30
- | `0` | Clean shutdown (SIGINT / SIGTERM). |
31
- | `1` | Bootstrap failure (config load, listen, etc.). |
32
- | `2` | Bad usage (missing `<config>`, unknown flag). |
33
-
34
- On successful boot the CLI loads the file, builds the config, calls
35
- `listen()` using the `server` section, and prints exactly:
36
-
37
- ```text
38
- [@databehave/server] listening on http://${host}:${port}
39
- ```
40
-
41
- via `console.info` (so it lands on stdout, not stderr). When
42
- `admin.enabled` is also true, a second line follows on the same
43
- stream:
44
-
45
- ```text
46
- [@databehave/server] admin panel ready at http://${host}:${port}${admin.path} (dev mock — disable in production)
47
- ```
48
-
49
- Walk errors and empty-schema reports print to `stderr` via
50
- `console.warn`. `SIGINT` and `SIGTERM` trigger a graceful shutdown
51
- via `RunHandle.close()`.
52
-
53
- Set `MOCK_SERVER_HOST` / `MOCK_SERVER_PORT` to override `server.host`
54
- / `server.port` when the config uses `${VAR}` interpolation.
55
-
56
- ## `--open` browser launcher
57
-
58
- `--open` and `admin.openBrowserOnStart: true` (in the JSONC
59
- config) both trigger the same launcher — they are ORed. With
60
- `admin.enabled: false`, the flag prints a log line and is a
61
- no-op:
62
-
63
- ```text
64
- [@databehave/server] --open requested but admin.enabled is false; skipping
65
- ```
66
-
67
- The launcher spawns a platform command, never a network request:
68
-
69
- | Platform | Command |
70
- | --------------- | --- |
71
- | `darwin` (macOS) | `open <url>` |
72
- | `win32` | `cmd /c start "" <url>` |
73
- | `linux` / other | `xdg-open <url>` |
74
-
75
- Spawn failures (missing `xdg-open`, headless CI) are caught and
76
- printed as a warn line; the server keeps running.
@@ -1,62 +0,0 @@
1
- # `admin`
2
-
3
- Opt-in admin panel + REST API for sticky error overrides and per-request
4
- `x-mock-*` header injection. Default **off** — when absent or
5
- `enabled: false` the kit serves nothing under the mount path and ignores
6
- `x-mock-*` headers (zero behaviour change for existing consumers). When
7
- enabled the kit writes a one-line stdout banner on boot (see
8
- [admin/index.md#security-notes](../admin/index.md#security-notes) for the
9
- canonical banner format) and mounts the UI + REST routes under `path`.
10
-
11
- ```jsonc
12
- "admin": {
13
- "enabled": true,
14
- "path": "/databehave",
15
- "bind": "loopback-only",
16
- "allowDestroy": true,
17
- "cors": "auto",
18
- "scenariosDir": "./mock-scenarios"
19
- }
20
- ```
21
-
22
- | Field | Default | Meaning |
23
- | -------------- | -------------------------------- | --- |
24
- | `enabled` | `false` | Master switch. Absent or `false` = feature off. |
25
- | `path` | `'/databehave'` | Mount point for UI + REST. Must start with `/`. |
26
- | `ui` | `true` | Serve the admin HTML / `ui.js` / `ui.css` at `${path}`. Set `false` for a REST-only deployment (no static assets). |
27
- | `headers` | `true` | Honour `x-mock-*` request headers. Set `false` to keep the sticky-override REST API enabled while ignoring per-request header overrides. |
28
- | `bind` | `'loopback-only'` | Refuse to enable when the server host is not loopback. Set `'any'` to opt out (CLI exits 1 if the policy is violated). |
29
- | `allowDestroy` | `true` | Honour `x-mock-destroy` / `destroy` ErrorMode. `false` → reject with HTTP 503. |
30
- | `cors` | `'auto'` | `'auto'` \| `'any'` \| `'same-origin'` \| `{ origin }`. `'auto'` resolves to `*` on loopback, `'same-origin'` when `bind: 'any'`. |
31
- | `scenariosDir` | `${cwd}/mock-scenarios` | Directory used by the file-backed scenarios endpoints. |
32
- | `openBrowserOnStart` | `false` | When `true`, spawn the platform browser opener on the admin URL after `listening`. Default off — the URL is logged but no browser is launched. ORed with the CLI's `--open` flag. |
33
- | `openapiBody` | undefined | **Programmatic only.** Override the OpenAPI document the admin route picker advertises at `GET ${path}/openapi-routes` and `GET ${path}/openapi.json`. Accepts the raw JSON text. When omitted, the kit reuses the same OAS bytes the dispatcher loaded from `openapi:`. Useful when the admin UI should expose a different surface than the dispatcher (e.g. an internal-only spec). |
34
-
35
- > **Security.** `bind: 'loopback-only'` is the default. Setting
36
- > `bind: 'any'` exposes the overrides REST API and the per-request
37
- > `x-mock-*` headers to the network — only do this in trusted
38
- > environments. See [../admin/index.md#security-notes](../admin/index.md#security-notes).
39
-
40
- > **Route list.** The admin UI lists OAS routes and hand-written
41
- > `endpoints` together. Routes registered only via programmatic
42
- > `routes` are also included; OAS wins on duplicates.
43
-
44
- Full REST surface, matcher / ErrorMode reference, `x-mock-*` header
45
- table, sticky-override resolution priority, scenarios file format — see
46
- [../admin/](../admin/index.md).
47
-
48
- ## Route collision detection
49
-
50
- Admin routes are mounted under `${path}` (`/openapi-routes`,
51
- `/overrides`, `/scenarios`, …). When a hand-written `endpoints` key
52
- collides with one of those admin routes, the kit refuses to boot
53
- rather than silently shadowing one with the other:
54
-
55
- ```text
56
- @databehave/server: admin route GET /databehave/overrides collides with a user-declared route. Move admin.path to a free prefix or rename the user route.
57
- ```
58
-
59
- Fix: change `admin.path` to a free prefix (`/_admin`, `/__mock`,
60
- …) or rename the user route. The collision check fires on every
61
- admin sub-route, including the static UI assets at `${path}/ui.js`
62
- and `${path}/ui.css`.
@@ -1,72 +0,0 @@
1
- # `cors`
2
-
3
- ```jsonc
4
- "cors": {
5
- "origin": ["https://app.example.com", "https://admin.example.com"],
6
- "credentials": true,
7
- "exposeHeaders": ["x-mock-status"],
8
- "allowMethods": ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
9
- "allowHeaders": ["content-type", "authorization"],
10
- "maxAge": 86400
11
- }
12
- ```
13
-
14
- All fields optional. When the `cors` section is present, @databehave/server
15
- handles `OPTIONS` preflight for every declared route and attaches
16
- CORS headers on every response.
17
-
18
- ## `cors.origin`
19
-
20
- `origin` accepts:
21
-
22
- - **omitted** — the loader installs a reflector that echoes the
23
- request `Origin` back (or `*` when the request has none).
24
- - **`string`** — single allowed origin. Treated as a one-entry
25
- allowlist.
26
- - **`string[]`** — explicit allowlist. The request `Origin` is matched
27
- case-insensitively against the list; only an exact match is echoed
28
- back. Off-allowlist requests get an empty `Access-Control-Allow-Origin`
29
- value, which the response builder omits entirely (the de-facto
30
- reject signal — no header is sent).
31
-
32
- ```jsonc
33
- "cors": {
34
- "origin": "https://app.example.com", // single origin
35
- "credentials": true
36
- }
37
- ```
38
-
39
- ```jsonc
40
- "cors": {
41
- "origin": ["https://app.example.com", "https://staging.example.com"],
42
- "credentials": true
43
- }
44
- ```
45
-
46
- For a fully custom reflector (e.g. regex matching), use the
47
- programmatic API — `cors.origin` there accepts a `(o: string) => string`
48
- function.
49
-
50
- ## `Vary: Origin`
51
-
52
- Whenever the `cors` section is present, the response decorator adds
53
- `Vary: Origin` to every response (preflight included). This prevents
54
- reverse proxies and CDNs from caching one origin's response and serving
55
- it to another after the body diverges on the request `Origin`.
56
-
57
- When a handler also sets `Vary` (e.g. `Vary: Accept-Encoding`), the two
58
- values are **merged additively**, not replaced. Tokens are deduplicated
59
- case-insensitively, the first occurrence's casing is preserved, and a
60
- literal `*` short-circuits to just `*` (HTTP semantic: "varies on every
61
- possible header").
62
-
63
- ```ts
64
- // Handler returns Vary: Accept-Encoding
65
- // CORS adds Vary: Origin
66
- // On the wire Vary: Accept-Encoding, Origin
67
- ```
68
-
69
- Other CORS response headers (`Access-Control-Allow-Origin`,
70
- `Access-Control-Allow-Credentials`, `Access-Control-Expose-Headers`)
71
- follow the standard "handler wins" merge rule — only `Vary` is
72
- additive.
@@ -1,162 +0,0 @@
1
- # `endpoints`
2
-
3
- The route table. Keys are `"METHOD <path>"`. Values pick one of three
4
- forms:
5
-
6
- ## 1. String (handler module path)
7
-
8
- ```jsonc
9
- "GET products/list": "./src/routes/products/get/list.js"
10
- ```
11
-
12
- Shorthand for `{ "handler": "..." }`. The module must:
13
-
14
- - be loadable via the runtime's native ESM `import()`,
15
- - `export default` a `Handler` (a function returning a
16
- `MockResponse` POJO, sync or async).
17
-
18
- CommonJS modules are not supported by the JSONC loader's resolver —
19
- its dynamic-import path runs through `pathToFileURL()` and assumes
20
- ESM. If the module's default export is missing or is not a function,
21
- boot fails with:
22
-
23
- ```text
24
- @databehave/server/config: ./src/routes/products/get/list.js (mapped to GET /api/v1/products/list) has no default-exported handler function
25
- ```
26
-
27
- ### Handler resolution
28
-
29
- Config files reference handlers by their compiled `.js` path so they
30
- stay valid for production `tsc`-built deployments. In dev / test
31
- scenarios where only the TypeScript source exists on disk, the
32
- resolver falls back to a sibling extension automatically — `.js`
33
- becomes `.ts`, `.tsx`, `.mts`, or `.cts` (in that order) — so test
34
- runners that register a TS loader (`vitest`, `tsx`, `ts-node`) pick
35
- the right file without any config change.
36
-
37
- Set `DATABEHAVE_PREFER_TS=1` to **prefer** `.ts` siblings over the
38
- declared `.js` even when both exist on disk. Useful in dev when an
39
- outdated `dist/` is still around and would otherwise win. The
40
- variable accepts only the literal string `"1"`; any other value
41
- (including `true`) is treated as off.
42
-
43
- ## 2. Handler object
44
-
45
- ```jsonc
46
- "POST products/bulk-update": {
47
- "handler": "./src/routes/products/update/bulk-update.js",
48
- "status": 500
49
- }
50
- ```
51
-
52
- The optional `status` field is a shortcut that registers an entry in
53
- `mockMode.pathOverrides` for this route. Use it to force an error
54
- status without editing the handler.
55
-
56
- ## 3. Static response
57
-
58
- ```jsonc
59
- "GET /health": {
60
- "response": {
61
- "status": 200,
62
- "json": { "ok": true },
63
- "headers": { "cache-control": "no-store" }
64
- }
65
- }
66
- ```
67
-
68
- No handler module is loaded; the response is returned verbatim. Use
69
- `"empty": true` instead of `"json"` for an empty body.
70
-
71
- ### `EndpointResponse` fields
72
-
73
- | Field | Required | Meaning |
74
- | --------- | -------- | --- |
75
- | `status` | yes | HTTP status integer. Boot fails with `endpoints["KEY"].response.status must be a number` if missing or non-numeric — there is no implicit `200`. |
76
- | `json` | no | JSON body. Mutually exclusive with `empty`. May be any JSON value (`null`, `[]`, `{}` are all valid). When omitted on a non-`empty` response the body is `null`. |
77
- | `empty` | no | When `true`, the response carries no body. Boot fails with `endpoints["KEY"].response.empty must be a boolean` if the value is non-boolean. |
78
- | `headers` | no | Plain `Record<string, string>` merged into the response. Non-string values raise `endpoints["KEY"].response.headers["H"] must be a string` at boot. Header keys are case-preserved as written. |
79
-
80
- `handler` and `response` are mutually exclusive. Setting both raises:
81
-
82
- ```text
83
- @databehave/server/config: endpoints["KEY"] cannot have both "handler" and "response"
84
- ```
85
-
86
- An empty `{}` value is also rejected:
87
-
88
- ```text
89
- @databehave/server/config: endpoints["KEY"] must have either "handler" or "response"
90
- ```
91
-
92
- A `status` field next to `response` (instead of inside it) is rejected
93
- with a hint pointing at the correct location:
94
-
95
- ```text
96
- @databehave/server/config: endpoints["KEY"].status cannot be combined with "response" (put status inside response)
97
- ```
98
-
99
- ## Programmatic `handlers` override
100
-
101
- `loadConfig(path, { handlers })` accepts an explicit
102
- module-path → function map that short-circuits the dynamic `import()`
103
- for matching keys. Use case: test environments (vitest under plain
104
- Node 18) that cannot load `.ts` source files through native
105
- `import()`. Construct the map via static imports or
106
- `import.meta.glob('eager')`, then pass it in.
107
-
108
- ```ts
109
- import { loadConfig } from '@databehave/server'
110
- import healthHandler from './src/routes/health.ts'
111
-
112
- await loadConfig('./databehave.jsonc', {
113
- handlers: {
114
- './src/routes/health.js': healthHandler,
115
- },
116
- })
117
- ```
118
-
119
- Keys are canonicalised before lookup: a leading `./` is stripped,
120
- repeated `/` are collapsed, and the trailing
121
- `.js` / `.mjs` / `.cjs` / `.ts` / `.tsx` / `.mts` / `.cts` is dropped
122
- — so `./src/routes/health.js`, `src/routes/health`, and
123
- `./src/routes/health.ts` all collapse to the same lookup key.
124
- Handlers not present in the map fall back to dynamic import. Values
125
- that are not functions raise at boot:
126
-
127
- ```text
128
- @databehave/server/config: handlers override for "./src/routes/health.js" (mapped to GET /health) is not a function (got TYPE)
129
- ```
130
-
131
- ## Programmatic `logger` injection
132
-
133
- `loadConfig(path, { logger })` redirects non-fatal
134
- warnings (OAS walker errors, empty schemas, mock-mode header
135
- diagnostics) to a custom sink. Defaults to `console.warn`. Inject a
136
- no-op logger from tests to silence the output, or a structured logger
137
- in production:
138
-
139
- ```ts
140
- await loadConfig('./databehave.jsonc', {
141
- logger: { warn: (m) => myLogger.warn({ src: 'databehave', m }) },
142
- })
143
- ```
144
-
145
- The injected logger only catches warnings emitted while loading the
146
- config and wiring the OpenAPI / mock-mode adapters — it does not
147
- intercept access logs (those go through the [`log`](./log.md)
148
- config) or per-request `console.warn` calls from user handlers.
149
-
150
- ## `basePath` collisions
151
-
152
- When the JSONC config sets `basePath`, every relative endpoint key
153
- (e.g. `"GET products/list"`) is rewritten to absolute
154
- (`"GET /api/v1/products/list"`). If two distinct keys collide after
155
- rewriting — typically because one was already absolute and the other
156
- relative to the same final path — the loader fails fast at boot:
157
-
158
- ```text
159
- @databehave/server/config: endpoints keys "GET /api/v1/products/list" and "GET products/list" both resolve to "GET /api/v1/products/list" after applying basePath
160
- ```
161
-
162
- There is no last-write-wins fallback. Pick one form per route.
@@ -1,107 +0,0 @@
1
- # Configuration reference
2
-
3
- `@databehave/server` is configured by one file (`databehave.jsonc`) or one
4
- `Config` object passed to `createServer`. This page covers
5
- the file form; the programmatic form mirrors it field-for-field.
6
-
7
- ## File format
8
-
9
- - Extension can be `.json` or `.jsonc`. Either way, JSONC features are
10
- accepted:
11
- - `// line` comments
12
- - `/* block */` comments
13
- - trailing commas
14
- - Every string value supports `${VAR}` and `${VAR:default}` env
15
- interpolation. Missing env without default → empty string.
16
- - Variable names accept ASCII letters, digits, and `_`, with the
17
- leading character restricted to a letter or `_`. Lowercase and
18
- mixed-case names are allowed (`${path}`, `${myVar}`); the loader
19
- does not require ALL_CAPS shell convention.
20
- - Every relative path is resolved against the **directory of the
21
- config file**.
22
-
23
- ## Environment variables (process-level)
24
-
25
- Two env vars influence boot behaviour beyond `${VAR}` interpolation:
26
-
27
- | Variable | Effect |
28
- | --- | --- |
29
- | `NODE_ENV` | When **not** equal to `'production'`, both `defineConfig` and the JSONC loader deep-freeze the resolved config so accidental mutation throws in tests. Production skips the freeze so callers may legitimately patch the object after construction. |
30
- | `DATABEHAVE_PREFER_TS=1` | Prefer a `.ts` / `.tsx` / `.mts` / `.cts` sibling over the declared `.js` for handler resolution, even when the `.js` exists on disk. Useful in dev when an outdated `dist/` is still around. Default off. |
31
-
32
- ## Top-level shape
33
-
34
- ```jsonc
35
- {
36
- "openapi": "./openapi.json", // optional, JSON only
37
- "server": { "host": "...", "port": 8000 },
38
- "basePath": "/api/v1", // optional
39
- "cors": { /* ... */ }, // optional
40
- "mockMode": { /* ... */ }, // optional
41
- "admin": { /* ... */ }, // optional, default off
42
- "log": false, // optional, default off
43
- "endpoints": { /* ... */ } // route → handler / response
44
- }
45
- ```
46
-
47
- ## Field reference
48
-
49
- - [openapi.md](./openapi.md) — `openapi:` (path to the OAS document).
50
- - [server.md](./server.md) — `server:` host / port and `basePath:`.
51
- - [endpoints.md](./endpoints.md) — `endpoints:` route table and handler shorthand forms.
52
- - [cors.md](./cors.md) — `cors:` block.
53
- - [mock-mode.md](./mock-mode.md) — `mockMode:` block.
54
- - [admin.md](./admin.md) — `admin:` block (config knobs only — admin behaviour is in [../admin/](../admin/index.md)).
55
- - [log.md](./log.md) — `log:` block.
56
- - [schema.md](./schema.md) — `schema:` (auto-schema config knobs — auto-mode behaviour is in [../openapi/auto-schema.md](../openapi/auto-schema.md)).
57
- - [validation.md](./validation.md) — `validation:` block.
58
-
59
- For the CLI that consumes the `server` section, see [../cli.md](../cli.md).
60
-
61
- ## Programmatic `Config`
62
-
63
- ```ts
64
- import { createServer, defineConfig } from '@databehave/server'
65
-
66
- const config = defineConfig({
67
- routes: {
68
- 'GET /api/v1/ping': () => ({ json: { ok: true } }),
69
- },
70
- hooks: {
71
- onRequest: (req) => { /* short-circuit by returning a MockResponse */ },
72
- onResponse: (req, res) => res,
73
- onError: (req, err) => ({ status: 500, json: { error: true } }),
74
- },
75
- cors: { origin: (o) => o || '*', credentials: true },
76
- openapi: undefined, // pass JSON OAS text here to enable
77
- onOpenApiWalkError: (m, p, err) => console.warn(`walk failed: ${m} ${p}`, err),
78
- onOpenApiEmptySchema: (m, p, s) => console.warn(`empty schema: ${m} ${p} ${s}`),
79
- })
80
-
81
- const server = createServer(config)
82
- ```
83
-
84
- `defineConfig` is an identity function — present for IDE support, it
85
- gives you the strict `Config` type inline. When `NODE_ENV !==
86
- 'production'` it also deep-freezes the returned config so accidental
87
- mutation (e.g. `config.routes['GET /x'] = …`, pushing into
88
- `cors.allowMethods`) throws in tests instead of silently corrupting the
89
- running server. Handler functions are intentionally left mutable — freeze
90
- stops at function boundaries. Production skips the freeze so callers may
91
- legitimately patch the object for env-specific wiring.
92
-
93
- ## Programmatic API surface
94
-
95
- Everything reachable from the package entry point. Every field above
96
- maps to a typed member of `Config` (see the source
97
- declaration in `src/types.ts`).
98
-
99
- | Export | What it is |
100
- | --- | --- |
101
- | `createServer(config)` | Build a `Server` from an in-memory config. Returns `{ fetch, listen }`. The HTTP runtime is `node:http` ↔ Web Fetch under the hood. |
102
- | `defineConfig(config)` | Identity helper for IDE intellisense + dev-mode deep-freeze (see above). |
103
- | `loadConfig(path, opts?)` | Read a JSONC file from disk and resolve every handler module. `opts.handlers` short-circuits dynamic `import()` (test-friendly bypass — see [endpoints.md](./endpoints.md#programmatic-handlers-override)); `opts.logger` redirects walker / mock-mode warnings to your sink (defaults to `console.warn`). |
104
- | `run({ config, open? })` / `RunHandle` | One-call boot: load JSONC, build the server, bind the listen port, optionally open the admin URL. `RunHandle.close()` is idempotent. Used by both the CLI and direct library consumers so log lines and error model match exactly. |
105
- | `seedFor({ endpoint, from?, dayOffset?, extra? })` | Pure helper for building deterministic databehave seed strings (`<endpoint>|<key>=<val>|date=<YYYY-MM-DD>|day=<n>`). Re-exported so consumers can derive their own seeds without reimplementing the format. |
106
- | `resolveStatus` / `MockModeConfig` / `MockModeLogger` | `resolveStatus(method, path, config)` is exported for unit tests. `MockModeConfig` (and its `logger` sink `MockModeLogger`) is the type of `JsonConfig.mockMode`. The wiring (`buildMockModeHooks`, `BodyResolver`, `MockModeHooks`) is internal — set `mockMode.enabled: true` in JSONC. |
107
- | `Config` and sibling types (`Method`, `MockRequest`, `MockResponse`, `Cors`, `Handler`, `ListenOptions`, `Server`, `AdminModeConfig`, `ErrorMode`, …) | Strict type declarations exported from `src/types.ts` / `src/admin/admin-types.ts`. |
@@ -1,43 +0,0 @@
1
- # `log`
2
-
3
- Opt-in per-request access log written to **stdout**. Off by default —
4
- no output and no per-request overhead.
5
-
6
- ```jsonc
7
- "log": true // shorthand: { access: true }
8
- ```
9
-
10
- Or as an object:
11
-
12
- ```jsonc
13
- "log": {
14
- "access": true, // default true when object given
15
- "includeAdmin": false, // log admin-panel hits too
16
- "colors": "auto", // 'auto' | 'always' | 'never'
17
- "format": "pretty" // 'pretty' | 'json'
18
- }
19
- ```
20
-
21
- | Field | Default | Meaning |
22
- | -------------- | ----------- | --- |
23
- | `access` | `true` | One line per request when `true`. |
24
- | `includeAdmin` | `false` | When `true`, log hits to `admin.path/*`. |
25
- | `colors` | `'auto'` | `'auto'` honours `process.stdout.isTTY`. No effect on `json` format. |
26
- | `format` | `'pretty'` | `'pretty'` (ANSI one-liner) or `'json'` (newline-terminated object). |
27
-
28
- Sticky / header-driven overrides are flagged with
29
- `[override:<mode.kind>]` (pretty) or `"override":"<kind>"` (json).
30
-
31
- Pretty (colours stripped):
32
-
33
- ```txt
34
- GET /api/v1/health → 200 4 ms · 56
35
- POST /api/v1/widgets/copy → 500 2 ms · 78 [override:http-status]
36
- ```
37
-
38
- JSON:
39
-
40
- ```json
41
- {"t":"2026-05-28T10:00:00.000Z","method":"GET","path":"/api/v1/health","status":200,"ms":4,"bytes":"56"}
42
- {"t":"2026-05-28T10:00:00.000Z","method":"POST","path":"/api/v1/widgets/copy","status":500,"ms":2,"bytes":"78","override":"http-status"}
43
- ```
@@ -1,84 +0,0 @@
1
- # `mockMode`
2
-
3
- ```jsonc
4
- "mockMode": {
5
- "enabled": true,
6
- "header": "x-mock-status",
7
- "healthPaths": ["/health", "/healthz", "/api/v1/health"],
8
- "defaultStatus": 200,
9
- "allowHeaderOverride": false,
10
- "pathOverrides": {
11
- "GET /api/v1/products/list": 500,
12
- "/api/v1/admin/users-delete": 401
13
- }
14
- }
15
- ```
16
-
17
- | Field | Default | Meaning |
18
- | --- | --- | --- |
19
- | `enabled` | `false` | Master switch. `false` / absent = feature off. |
20
- | `header` | `'x-mock-status'` | Response header tagged with the resolved status. Also the request header consulted when `allowHeaderOverride: true`. |
21
- | `healthPaths` | `[]` | Paths exempt from injection (typically health probes). |
22
- | `defaultStatus` | undefined | Status applied when no override matches. Omit for passthrough. |
23
- | `allowHeaderOverride` | `false` | When `true`, an incoming request may flip the mocked status by sending the same `header` (default `x-mock-status: 500`). Disabled by default so production-style mock servers don't expose status injection to arbitrary clients. |
24
- | `pathOverrides` | `{}` | Per-route status overrides. Keys are `"METHOD /path"` or `"/path"` (path must start with `/`). Values are HTTP status codes. |
25
- | `logger` | `console` | Programmatic-only sink for non-fatal mock-mode diagnostics (e.g. out-of-range header overrides). Pass the host application's logger to keep mock-server output in one place. Not settable from JSONC. |
26
-
27
- ## Resolution priority (canonical)
28
-
29
- For each request, mock-mode picks a status in this order — first hit
30
- wins. When `admin.enabled` is also set, the admin inject hook
31
- runs **before** mock-mode and can short-circuit any of the steps
32
- below; see [../admin/overrides.md#sticky-override-resolution](../admin/overrides.md#sticky-override-resolution)
33
- for the unified pipeline.
34
-
35
- 1. Request `header` value (only when `allowHeaderOverride: true`).
36
- 2. `pathOverrides["<METHOD> <path>"]` (method-scoped exact match).
37
- 3. `pathOverrides["<path>"]` (any-method exact match).
38
- 4. `defaultStatus`.
39
- 5. Passthrough — the real handler runs.
40
-
41
- `healthPaths` short-circuits the entire chain: matching paths are
42
- served by their handler regardless of any override.
43
-
44
- ## `pathOverrides` key validation
45
-
46
- Keys are validated at config load. Malformed keys raise at boot
47
- (via `AggregateError` so every gap surfaces at once) — there is no
48
- silent "this key never matches" failure mode.
49
-
50
- - Keys must match `(?:METHOD\s+)?/path`. The optional method prefix
51
- is one of `GET POST PUT DELETE PATCH HEAD OPTIONS`. The path must
52
- start with `/` and contain no whitespace.
53
- - Method names are normalised to upper-case: `"get /api/x"`,
54
- `"GET /api/x"`, and `"Get /api/x"` all canonicalise to
55
- `"GET /api/x"` so a lower-case key still matches.
56
- - Values must be finite numbers (HTTP status codes).
57
-
58
- ## Body resolution
59
-
60
- When a non-passthrough status `N` fires:
61
-
62
- 1. `status === 204` → empty body.
63
- 2. The body resolver returns the OAS `responses[N]` schema mock when
64
- the route is OAS-walker-backed and a response schema exists for
65
- that status (see [../openapi/fallback.md#per-status-body-generators](../openapi/fallback.md#per-status-body-generators)).
66
- 3. Fallback envelope `{ error: true, status: N }`.
67
-
68
- The configured `header` (default `x-mock-status`) is always tagged on
69
- the response with the resolved status code so a client can confirm
70
- which override fired.
71
-
72
- ## `allowHeaderOverride` validation
73
-
74
- When the request header is set but the value is not a finite integer
75
- in `[200, 600)`, the value is **ignored** and a single `warn` line
76
- is emitted via `logger` (default `console`) so the typo is visible
77
- without breaking legitimate clients that ship the header for other
78
- reasons:
79
-
80
- ```text
81
- @databehave/server/mock-mode: ignored x-mock-status="abc" (must be an integer in [200, 600))
82
- ```
83
-
84
- A per-endpoint shortcut also exists — see [endpoints.md](./endpoints.md).
@@ -1,8 +0,0 @@
1
- # `openapi`
2
-
3
- Path (relative to the config file) to an OpenAPI 3.x document.
4
- **JSON only** — convert YAML first via `yq -o=json openapi.yaml > openapi.json`.
5
- When set, the walker registers fallback handlers for every
6
- `paths.*.<method>` not declared in `endpoints`.
7
-
8
- See [../openapi/fallback.md](../openapi/fallback.md) for walker behaviour.
@@ -1,34 +0,0 @@
1
- # `schema`
2
-
3
- Top-level field that opts the server into **auto-schema mode** — every
4
- endpoint absent from `endpoints` is served from data generated by
5
- `@databehave/schema`'s `mock()` engine, derived directly from your
6
- OpenAPI document.
7
-
8
- **Minimal:**
9
-
10
- ```jsonc
11
- {
12
- "openapi": "./openapi.json",
13
- "schema": { "enabled": true }, // "enabled" gates auto-mode; defaults equivalent to { "seed": "stable" }
14
- "endpoints": {}
15
- }
16
- ```
17
-
18
- **With knobs:**
19
-
20
- ```jsonc
21
- {
22
- "openapi": "./openapi.json",
23
- "schema": {
24
- "enabled": true,
25
- "seed": 42, // number = fixed; "stable" = derive from endpoint|query|params; "random" = fresh per request; default "stable"
26
- "locale": "ja",
27
- "arrayCount": 20 // default array length when the OAS lacks minItems
28
- }
29
- }
30
- ```
31
-
32
- For the architecture, install path, missing-install error, precedence,
33
- and default-behaviour rules, see
34
- [../openapi/auto-schema.md](../openapi/auto-schema.md).