@owlmeans/server-api 0.1.18-rc.40 → 0.1.18-rc.42

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/README.md CHANGED
@@ -10,7 +10,7 @@ only when composing a context without that package. WebSocket routes belong to
10
10
  ## Installation
11
11
 
12
12
  ```bash
13
- bun add @owlmeans/server-api@^0.1.18-rc.40
13
+ bun add @owlmeans/server-api@^0.1.18-rc.42
14
14
  ```
15
15
 
16
16
  ## Concepts
@@ -240,7 +240,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
240
240
  your project's skill store (`.agents/skills/`):
241
241
 
242
242
  ```sh
243
- npx @owlmeans/agent-skills@^0.1.18-rc.36
243
+ npx @owlmeans/agent-skills@^0.1.18-rc.39
244
244
  ```
245
245
 
246
246
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/server-api",
4
- "version": "0.1.18-rc.40",
5
- "generatedAt": "2026-09-22T22:24:39.481Z",
4
+ "version": "0.1.18-rc.42",
5
+ "generatedAt": "2026-09-24T14:00:34.973Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -7,7 +7,7 @@ user-invocable: false
7
7
 
8
8
  # @owlmeans/server-api
9
9
 
10
- **Install:** `bun add @owlmeans/server-api@^0.1.18-rc.40`
10
+ **Install:** `bun add @owlmeans/server-api@^0.1.18-rc.42`
11
11
 
12
12
  Make handlers from the protocol declaration so input and output types stay coupled to the shared
13
13
  contract:
@@ -58,15 +58,15 @@ that one route with `HandlerMisconfiguredError`, instead of the opaque
58
58
 
59
59
  ## The status a thrown error answers
60
60
 
61
- A thrown error (or a rejected response) is answered with the marshalled `ResilientError` as the
62
- body and a status from `errorStatus(error)` (`./utils`), resolved in this order:
61
+ A thrown error (or a rejected response) is answered with a body chosen by the exposure (§ Error
62
+ exposure) and a status from `errorStatus(error)` (`./utils`), resolved in this order:
63
63
 
64
64
  | Error | Status |
65
65
  |---|---|
66
66
  | `AuthForbidden`, `AccessError` or a subclass — by class or registered type name | 403 |
67
67
  | `AuthorizationError`, `AuthFailedError` or a subclass — by class or registered type name | 401 |
68
- | a class declaring `static httpStatus` as an integer 400–499 | that status |
69
- | anything else, including a declaration outside 400–499 | 500 |
68
+ | a class declaring `static httpStatus` as an integer 400–499, or 500–599 with `static allowServerErrorStatus = true` | that status |
69
+ | anything else, including a 5xx declared without the opt-in | 500 |
70
70
 
71
71
  - **A refusal of the caller's condition declares its status; a fault declares nothing.** 400 a
72
72
  malformed request, 402 an unpaid balance or plan, 404 an addressed target that does not exist
@@ -85,7 +85,7 @@ body and a status from `errorStatus(error)` (`./utils`), resolved in this order:
85
85
  (`ResilientError.ensure`) error only when that answers 500. The thrown object is the one whose
86
86
  class is certainly what was raised; the rebuild is what gives a status to a marshalled error that
87
87
  crossed a hop as a plain `Error`. `ensure` returns an error from any `@owlmeans/error` copy
88
- untouched, so the body keeps the thrown class's `type` (`AuthFailedError|||api:auth:…`) even in a
88
+ untouched, so a development body keeps the thrown class's `type` (`AuthFailedError|||api:auth:…`) even in a
89
89
  process holding duplicate module copies (`bun --preserve-symlinks`). `executeResponse` ensures
90
90
  nothing and answers the rejected error's status. `@owlmeans/server-socket` answers an upgrade
91
91
  through the same `handleError`.
@@ -94,10 +94,11 @@ body and a status from `errorStatus(error)` (`./utils`), resolved in this order:
94
94
  answers the same status. Match whole names, never substrings: a subclass's `typeName` does not
95
95
  reliably embed its parent's (`EntitlementRefusal` extends `AuthForbidden`). A declared
96
96
  `httpStatus` is a structural static read and survives duplicate copies as it is.
97
- - The status never changes what a client rebuilds: `@owlmeans/api` rehydrates the class from the
98
- body for any non-2xx answer, so a caller branches on the class, never on the number. Nothing in
99
- the framework treats a 404 specially — a missing route is a Fastify JSON body the client turns
100
- into `ApiClientError('404')`, a refusal is a marshalled string rebuilt as its class.
97
+ - **The status is what a production client can act on.** Only a development body is rebuilt into
98
+ its class; a production body is the incident id alone, so a client knows a refusal by its status
99
+ (§ Error exposure) — give a refusal the client must act on its own distinct 4xx. Nothing in the
100
+ framework treats a 404 specially — a missing route is a Fastify JSON body the client turns into
101
+ a 404 `ApiStatusError`.
101
102
 
102
103
  `uploadedFile(request)` is the Fastify multipart boundary. Keep raw Fastify access there rather
103
104
  than reaching through `request.original` in application code.
@@ -105,9 +106,17 @@ than reaching through `request.original` in application code.
105
106
  ## Error exposure
106
107
 
107
108
  `handleError` always assigns an incident UUID, attaches it to the logged error and returns it in the
108
- `X-Incident-ID` response header (exposed through CORS). A production response body contains only
109
- that id; an explicit `cfg.http.errors.exposure = 'development'` uses the typed marshalled form with
110
- message and stack. Keep the default production-safe, and tell a client to report the incident id.
109
+ `X-Incident-ID` response header (`INCIDENT_ID_HEADER` in `./utils`, the same name and value
110
+ `@owlmeans/api` exports; exposed through CORS).
111
+
112
+ - **Production (the default):** a non-2xx body carries ONLY that incident id, under the resolved
113
+ HTTP status. A typed refusal is never exposed — its class, message and packed fields stay in the
114
+ server log under the id.
115
+ - **Development** (an explicit `cfg.http.errors.exposure = 'development'`): the typed marshalled
116
+ form with message and stack, which the client rebuilds into its class.
117
+ - A client reads a production failure with `@owlmeans/api`'s `./status` subpath: `httpStatusOf(e)`
118
+ (the status a 428 or 409 is acted on by) and `incidentIdOf(e)` (the id a person reports). Keep
119
+ the default production-safe, and tell a client to report the incident id.
111
120
 
112
121
  Do not use unbound compatibility handler wrappers. For a WebSocket route use
113
122
  `@owlmeans/server-socket`'s `connection(protocol, callback)`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/server-api",
3
- "version": "0.1.18-rc.40",
3
+ "version": "0.1.18-rc.42",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -33,15 +33,15 @@
33
33
  "@fastify/helmet": "^13.1.0",
34
34
  "@fastify/middie": "^9.0.2",
35
35
  "@fastify/multipart": "^9.0.1",
36
- "@owlmeans/api": "^0.1.18-rc.36",
37
- "@owlmeans/auth": "^0.1.18-rc.34",
38
- "@owlmeans/auth-common": "^0.1.18-rc.36",
36
+ "@owlmeans/api": "^0.1.18-rc.38",
37
+ "@owlmeans/auth": "^0.1.18-rc.36",
38
+ "@owlmeans/auth-common": "^0.1.18-rc.38",
39
39
  "@owlmeans/context": "^0.1.18-rc.32",
40
- "@owlmeans/error": "^0.1.18-rc.34",
41
- "@owlmeans/entrypoint": "^0.1.18-rc.35",
40
+ "@owlmeans/error": "^0.1.18-rc.36",
41
+ "@owlmeans/entrypoint": "^0.1.18-rc.37",
42
42
  "@owlmeans/route": "^0.1.18-rc.33",
43
- "@owlmeans/server-context": "^0.1.18-rc.35",
44
- "@owlmeans/server-entrypoint": "^0.1.18-rc.34",
43
+ "@owlmeans/server-context": "^0.1.18-rc.37",
44
+ "@owlmeans/server-entrypoint": "^0.1.18-rc.36",
45
45
  "ajv-errors": "^3.0.0",
46
46
  "fastify": "^5.0.0",
47
47
  "fastify-raw-body": "^5.0.0",