@owlmeans/server-api 0.1.18-rc.32 → 0.1.18-rc.34
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 +28 -4
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/server-api/SKILL.md +51 -1
- package/build/server.d.ts.map +1 -1
- package/build/server.js +2 -1
- package/build/server.js.map +1 -1
- package/build/types.d.ts +9 -0
- package/build/types.d.ts.map +1 -1
- package/build/utils/error.d.ts +54 -4
- package/build/utils/error.d.ts.map +1 -1
- package/build/utils/error.js +146 -11
- package/build/utils/error.js.map +1 -1
- package/build/utils/payload.d.ts.map +1 -1
- package/build/utils/payload.js +3 -2
- package/build/utils/payload.js.map +1 -1
- package/build/utils/server.d.ts +3 -2
- package/build/utils/server.d.ts.map +1 -1
- package/build/utils/server.js +5 -9
- package/build/utils/server.js.map +1 -1
- package/package.json +10 -10
- package/src/server.ts +2 -2
- package/src/types.ts +14 -1
- package/src/utils/error.ts +184 -13
- package/src/utils/payload.ts +3 -2
- package/src/utils/server.ts +8 -11
- package/tests/error-status.spec.ts +290 -0
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.
|
|
13
|
+
bun add @owlmeans/server-api@^0.1.18-rc.34
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
## Concepts
|
|
@@ -27,7 +27,12 @@ bun add @owlmeans/server-api@^0.1.18-rc.32
|
|
|
27
27
|
- **Validation** — the contract's schemas are compiled with AJV (`removeAdditional`, `useDefaults`,
|
|
28
28
|
`coerceTypes`), so a handler receives cleaned, coerced input.
|
|
29
29
|
- **Error mapping** — `AuthForbidden` / `AccessError` answer 403, `AuthorizationError` /
|
|
30
|
-
`AuthFailedError` answer 401,
|
|
30
|
+
`AuthFailedError` answer 401, a class declaring `static httpStatus` as an integer 4xx answers that
|
|
31
|
+
status, and anything else 500 — always with the marshalled `ResilientError` as the body. A refusal
|
|
32
|
+
of the caller's condition declares its status; a fault declares nothing and stays 500. The status
|
|
33
|
+
is taken from the error as thrown first, and from the ensured (rebuilt) error only when that
|
|
34
|
+
answers 500. An auth family is matched by class or by exact registered type name (the instance's
|
|
35
|
+
`type` or a `typeName` on its constructor chain), so duplicate module copies answer the same.
|
|
31
36
|
|
|
32
37
|
## Usage
|
|
33
38
|
|
|
@@ -82,6 +87,22 @@ export const rename = api.body(projectProtocols.rename, async ({ name }, context
|
|
|
82
87
|
})
|
|
83
88
|
```
|
|
84
89
|
|
|
90
|
+
A refusal that is not about permission declares its own 4xx on the class. The declaring package
|
|
91
|
+
never imports this one — the static property is read structurally and inherited by subclasses:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { ResilientError } from '@owlmeans/error'
|
|
95
|
+
|
|
96
|
+
export class ProjectBusy extends ResilientError {
|
|
97
|
+
public static override typeName = 'ProjectBusy'
|
|
98
|
+
public static httpStatus = 409 // honoured only as an integer 400–499; anything else answers 500
|
|
99
|
+
|
|
100
|
+
constructor(message: string = 'error') {
|
|
101
|
+
super(ProjectBusy.typeName, `project-busy:${message}`)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
85
106
|
### File upload
|
|
86
107
|
|
|
87
108
|
`uploadedFile(request)` is the only place a handler touches the Fastify multipart API. The protocol
|
|
@@ -171,7 +192,10 @@ code does not need it.
|
|
|
171
192
|
| `canServeModule(context, module)` | function | Whether an entrypoint belongs on this HTTP server |
|
|
172
193
|
| `provideRequest(alias, req, provision?)` | function | Build an `AbstractRequest` from a Fastify request |
|
|
173
194
|
| `executeResponse(response, reply, throwOnError?)` | function | Send an `AbstractResponse` onto a reply |
|
|
174
|
-
| `handleError(error, reply)` | function |
|
|
195
|
+
| `handleError(error, reply)` | function | Answer an error with `errorStatus` and the marshalled body |
|
|
196
|
+
| `errorStatus(error)` | function | 403 / 401 for auth errors (by class or type name), else the class's declared 4xx, else 500 |
|
|
197
|
+
| `declaredErrorStatus(error)` | function | The integer 4xx a class declares through `static httpStatus`, or `null` |
|
|
198
|
+
| `HttpStatusDeclaration` | type | `{ httpStatus?: unknown }` — the structural shape a declaring class has |
|
|
175
199
|
| `fixFormatDates(schema)` | function | Rewrite `date-time` object schemas as strings |
|
|
176
200
|
| `populateContext(req, context)`, `extractContext(req, ctx?, location?)` | function | Carry the request-scoped context on the raw request |
|
|
177
201
|
|
|
@@ -216,7 +240,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
216
240
|
your project's skill store (`.agents/skills/`):
|
|
217
241
|
|
|
218
242
|
```sh
|
|
219
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
243
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.30
|
|
220
244
|
```
|
|
221
245
|
|
|
222
246
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/server-api",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-09-
|
|
4
|
+
"version": "0.1.18-rc.34",
|
|
5
|
+
"generatedAt": "2026-09-19T13:42:56.206Z",
|
|
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.
|
|
10
|
+
**Install:** `bun add @owlmeans/server-api@^0.1.18-rc.34`
|
|
11
11
|
|
|
12
12
|
Make handlers from the protocol declaration so input and output types stay coupled to the shared
|
|
13
13
|
contract:
|
|
@@ -56,8 +56,58 @@ protocol unchanged, with a one-time warning; anything else that is not a plain f
|
|
|
56
56
|
that one route with `HandlerMisconfiguredError`, instead of the opaque
|
|
57
57
|
`TypeError: handler is not a function`.
|
|
58
58
|
|
|
59
|
+
## The status a thrown error answers
|
|
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:
|
|
63
|
+
|
|
64
|
+
| Error | Status |
|
|
65
|
+
|---|---|
|
|
66
|
+
| `AuthForbidden`, `AccessError` or a subclass — by class or registered type name | 403 |
|
|
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 |
|
|
70
|
+
|
|
71
|
+
- **A refusal of the caller's condition declares its status; a fault declares nothing.** 400 a
|
|
72
|
+
malformed request, 402 an unpaid balance or plan, 404 an addressed target that does not exist
|
|
73
|
+
(or is another organization's), 409 a target whose current state conflicts, 422 content or a body
|
|
74
|
+
that is understood and refused — and a missing configuration, a broken peer, a timeout or a bug
|
|
75
|
+
stays 500, because monitoring, logs, proxies and retry logic read a 5xx as the server failing.
|
|
76
|
+
The table and the leaf-class rule are the `error` skill's.
|
|
77
|
+
- Declare it on the class: `public static httpStatus = 409` (`override` only when an ancestor
|
|
78
|
+
already declares one). The declaration is structural — the package declaring an error never
|
|
79
|
+
imports `@owlmeans/server-api` — and a static property is inherited, so a subclass answers its
|
|
80
|
+
nearest declaring ancestor's status and redeclares to change it.
|
|
81
|
+
- The auth branches win over a declaration, in that order (`AuthForbidden extends
|
|
82
|
+
AuthorizationError`, so 403 is tested first). An entitlement or permission refusal extends
|
|
83
|
+
`AuthForbidden` rather than declaring 403.
|
|
84
|
+
- `handleError` resolves the status on the error AS THROWN first, and asks the ENSURED
|
|
85
|
+
(`ResilientError.ensure`) error only when that answers 500. The thrown object is the one whose
|
|
86
|
+
class is certainly what was raised; the rebuild is what gives a status to a marshalled error that
|
|
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
|
|
89
|
+
process holding duplicate module copies (`bun --preserve-symlinks`). `executeResponse` ensures
|
|
90
|
+
nothing and answers the rejected error's status. `@owlmeans/server-socket` answers an upgrade
|
|
91
|
+
through the same `handleError`.
|
|
92
|
+
- An auth family is recognised by `instanceof` OR by an exact registered type name — the instance's
|
|
93
|
+
`type` or any static `typeName` on its constructor chain — so a class from another module copy
|
|
94
|
+
answers the same status. Match whole names, never substrings: a subclass's `typeName` does not
|
|
95
|
+
reliably embed its parent's (`EntitlementRefusal` extends `AuthForbidden`). A declared
|
|
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.
|
|
101
|
+
|
|
59
102
|
`uploadedFile(request)` is the Fastify multipart boundary. Keep raw Fastify access there rather
|
|
60
103
|
than reaching through `request.original` in application code.
|
|
61
104
|
|
|
105
|
+
## Error exposure
|
|
106
|
+
|
|
107
|
+
`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.
|
|
111
|
+
|
|
62
112
|
Do not use unbound compatibility handler wrappers. For a WebSocket route use
|
|
63
113
|
`@owlmeans/server-socket`'s `connection(protocol, callback)`.
|
package/build/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAkC5D,KAAK,MAAM,GAAG,YAAY,CAAA;AAK1B,eAAO,MAAM,eAAe,UAAW,MAAM,KAAG,SAkJ/C,CAAA;AAED,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,aAAa,CAAC,CAAC,CAAC,OACrE,CAAC,UAAS,MAAM,KACpB,CAAC,GAAG,eAWN,CAAA"}
|
package/build/server.js
CHANGED
|
@@ -5,6 +5,7 @@ import { RouteMethod } from '@owlmeans/route';
|
|
|
5
5
|
import { createServerHandler, fixFormatDates } from './utils/index.js';
|
|
6
6
|
import { provideResponse } from '@owlmeans/entrypoint';
|
|
7
7
|
import { TOKEN_UPDATE } from '@owlmeans/auth-common';
|
|
8
|
+
import { INCIDENT_ID_HEADER } from './utils/error.js';
|
|
8
9
|
import Fastify from 'fastify';
|
|
9
10
|
import cors from '@fastify/cors';
|
|
10
11
|
import rawBody from 'fastify-raw-body';
|
|
@@ -79,7 +80,7 @@ export const createApiServer = (alias) => {
|
|
|
79
80
|
...Object.values(RouteMethod).map(method => method.toUpperCase()),
|
|
80
81
|
'HEAD', 'OPTIONS'
|
|
81
82
|
],
|
|
82
|
-
exposedHeaders: [TOKEN_UPDATE]
|
|
83
|
+
exposedHeaders: [TOKEN_UPDATE, INCIDENT_ID_HEADER]
|
|
83
84
|
});
|
|
84
85
|
await server.register(Multipart, {
|
|
85
86
|
throwFileSizeLimit: true,
|
package/build/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAClF,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE3E,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/build/types.d.ts
CHANGED
|
@@ -13,7 +13,16 @@ export interface Response extends FastifyReply {
|
|
|
13
13
|
export interface ApiServerAppend {
|
|
14
14
|
getApiServer: () => ApiServer;
|
|
15
15
|
}
|
|
16
|
+
export type HttpErrorExposure = 'production' | 'development';
|
|
17
|
+
export interface HttpErrorsConfig {
|
|
18
|
+
/** Production is the safe default; development is the only mode that exposes stacks. */
|
|
19
|
+
exposure?: HttpErrorExposure;
|
|
20
|
+
}
|
|
21
|
+
export interface HttpConfig {
|
|
22
|
+
errors?: HttpErrorsConfig;
|
|
23
|
+
}
|
|
16
24
|
export interface Config extends ServerConfig {
|
|
25
|
+
http?: HttpConfig;
|
|
17
26
|
}
|
|
18
27
|
export interface Context<C extends Config = Config> extends ServerContext<C>, ApiServerAppend {
|
|
19
28
|
}
|
package/build/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAC3E,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC5E,OAAO,kCAAkC,CAAA;AAEzC,MAAM,WAAW,SAAU,SAAQ,kBAAkB;IACnD,MAAM,EAAE,eAAe,CAAA;IAEvB,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5B;AAED,MAAM,WAAW,OAAQ,SAAQ,cAAc;CAAI;AAEnD,MAAM,WAAW,QAAS,SAAQ,YAAY;CAAI;AAElD,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,SAAS,CAAA;CAC9B;AAED,MAAM,WAAW,MAAO,SAAQ,YAAY;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAC3E,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC5E,OAAO,kCAAkC,CAAA;AAEzC,MAAM,WAAW,SAAU,SAAQ,kBAAkB;IACnD,MAAM,EAAE,eAAe,CAAA;IAEvB,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5B;AAED,MAAM,WAAW,OAAQ,SAAQ,cAAc;CAAI;AAEnD,MAAM,WAAW,QAAS,SAAQ,YAAY;CAAI;AAElD,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,SAAS,CAAA;CAC9B;AAED,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,aAAa,CAAA;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,wFAAwF;IACxF,QAAQ,CAAC,EAAE,iBAAiB,CAAA;CAC7B;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,gBAAgB,CAAA;CAC1B;AAED,MAAM,WAAW,MAAO,SAAQ,YAAY;IAC1C,IAAI,CAAC,EAAE,UAAU,CAAA;CAClB;AAED,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC,EAC1E,eAAe;CAAI"}
|
package/build/utils/error.d.ts
CHANGED
|
@@ -1,16 +1,66 @@
|
|
|
1
1
|
import type { FastifyReply } from 'fastify';
|
|
2
|
+
import type { Config, HttpErrorExposure } from '../types.js';
|
|
3
|
+
export declare const INCIDENT_ID_HEADER = "X-Incident-ID";
|
|
2
4
|
/**
|
|
3
|
-
*
|
|
5
|
+
* What an error class may declare about the HTTP status it is answered with.
|
|
6
|
+
*
|
|
7
|
+
* Structural on purpose: the classes that declare it live in packages that must not depend on an
|
|
8
|
+
* HTTP server (`@owlmeans/payment`, a product's contract package), so they write a plain
|
|
9
|
+
* `public static httpStatus = 409` and nothing imports this type.
|
|
10
|
+
*/
|
|
11
|
+
export interface HttpStatusDeclaration {
|
|
12
|
+
httpStatus?: unknown;
|
|
13
|
+
/** Explicit opt-in for a declared 5xx response. Client-error declarations need no opt-in. */
|
|
14
|
+
allowServerErrorStatus?: unknown;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The HTTP status an error's class declares, or `null` when it declares none that may be honoured.
|
|
18
|
+
*
|
|
19
|
+
* Read through the constructor chain — a static property is inherited, so a subclass answers what
|
|
20
|
+
* its nearest declaring ancestor says, and redeclares to change it. An integer 4xx is honoured
|
|
21
|
+
* directly. A 5xx is honoured only with `allowServerErrorStatus = true`, so a domain class must
|
|
22
|
+
* explicitly opt in to exposing a precise availability response. The read is structural, so a
|
|
23
|
+
* class from a duplicate module copy declares exactly what the imported one does.
|
|
24
|
+
*/
|
|
25
|
+
export declare const declaredErrorStatus: (error: unknown) => number | null;
|
|
26
|
+
/**
|
|
27
|
+
* The HTTP status an error is answered with, read off the error exactly as given.
|
|
4
28
|
*
|
|
5
29
|
* Two families reach here and both must be recognised. `AuthFailedError` / `AccessError` are this
|
|
6
30
|
* package's own, raised by the request pipeline itself. `AuthorizationError` / `AuthForbidden`
|
|
7
31
|
* come from `@owlmeans/auth` and are what the guards and gates actually throw — an unrecognised
|
|
8
32
|
* one answers 500, which reports a refused request as a crashed server: the client cannot tell
|
|
9
33
|
* "sign in again" from "the service is broken", and a consumer watching statuses concludes the
|
|
10
|
-
* application fell over.
|
|
34
|
+
* application fell over. A family member is recognised by class OR by registered type name
|
|
35
|
+
* ({@link typeNamesOf}), so a class from a duplicate module copy answers the same status.
|
|
11
36
|
*
|
|
12
37
|
* **Order matters.** `AuthForbidden extends AuthorizationError`, so the 403 branch has to be
|
|
13
|
-
* tested first or every refusal of a permission is reported as a failure to authenticate.
|
|
38
|
+
* tested first or every refusal of a permission is reported as a failure to authenticate. Both
|
|
39
|
+
* auth branches come before a class's own declaration, so an auth refusal can never be restated
|
|
40
|
+
* as something else. Everything that is neither an auth error nor a class declaring an allowed
|
|
41
|
+
* {@link declaredErrorStatus} answers 500.
|
|
42
|
+
*/
|
|
43
|
+
export declare const errorStatus: (error: unknown) => number;
|
|
44
|
+
export interface SerializedHttpError {
|
|
45
|
+
status: number;
|
|
46
|
+
body: string;
|
|
47
|
+
incidentId: string;
|
|
48
|
+
}
|
|
49
|
+
/** Invalid or absent configuration always resolves to the production-safe policy. */
|
|
50
|
+
export declare const errorExposure: (config?: Pick<Config, 'http'>) => HttpErrorExposure;
|
|
51
|
+
/** One central wire shape for every server error. */
|
|
52
|
+
export declare const serializeError: (error: Error, exposure?: HttpErrorExposure) => SerializedHttpError;
|
|
53
|
+
/** Apply safe response metadata carried by an error without coupling its package to Fastify. */
|
|
54
|
+
export declare const applyErrorHeaders: (error: unknown, reply: FastifyReply) => void;
|
|
55
|
+
/**
|
|
56
|
+
* Answer a thrown error: a status, and the marshalled `ResilientError` as the body.
|
|
57
|
+
*
|
|
58
|
+
* The status is resolved in two steps. The error AS THROWN comes first: it is the one object whose
|
|
59
|
+
* class is certainly the class that was raised, while `ResilientError.ensure` rebuilds through the
|
|
60
|
+
* registry of whichever `@owlmeans/error` copy this module loaded — under duplicate module copies
|
|
61
|
+
* that turns a perfectly recognisable auth refusal into a plain `ResilientError` answering 500.
|
|
62
|
+
* Only when the thrown error answers 500 is the ENSURED error asked, because the rebuild is what
|
|
63
|
+
* gives a status to a marshalled error that crossed a hop as a plain `Error`.
|
|
14
64
|
*/
|
|
15
|
-
export declare const handleError: (error: Error, reply: FastifyReply) => void;
|
|
65
|
+
export declare const handleError: (error: Error, reply: FastifyReply, exposure?: HttpErrorExposure) => void;
|
|
16
66
|
//# sourceMappingURL=error.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/utils/error.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAK3C
|
|
1
|
+
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/utils/error.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAK3C,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAE5D,eAAO,MAAM,kBAAkB,kBAAkB,CAAA;AAEjD;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,6FAA6F;IAC7F,sBAAsB,CAAC,EAAE,OAAO,CAAA;CACjC;AAOD;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,UAAW,OAAO,KAAG,MAAM,GAAG,IAc7D,CAAA;AAkCD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,WAAW,UAAW,OAAO,KAAG,MAkB5C,CAAA;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,qFAAqF;AACrF,eAAO,MAAM,aAAa,YAAa,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,iBACmB,CAAA;AAsBjF,qDAAqD;AACrD,eAAO,MAAM,cAAc,UAClB,KAAK,aAAY,iBAAiB,KACxC,mBAcF,CAAA;AAED,gGAAgG;AAChG,eAAO,MAAM,iBAAiB,UAAW,OAAO,SAAS,YAAY,KAAG,IAQvE,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,UACf,KAAK,SAAS,YAAY,aAAY,iBAAiB,SAY/D,CAAA"}
|
package/build/utils/error.js
CHANGED
|
@@ -1,33 +1,168 @@
|
|
|
1
1
|
import { FORBIDDEN_ERROR, SERVER_ERROR, UNAUTHORIZED_ERROR } from '@owlmeans/api';
|
|
2
2
|
import { AccessError, AuthFailedError } from '../errors.js';
|
|
3
3
|
import { AuthForbidden, AuthorizationError } from '@owlmeans/auth';
|
|
4
|
-
import { ResilientError } from '@owlmeans/error';
|
|
4
|
+
import { isResilientError, ResilientError, SEPARATOR } from '@owlmeans/error';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
export const INCIDENT_ID_HEADER = 'X-Incident-ID';
|
|
7
|
+
const CLIENT_ERROR_FIRST = 400;
|
|
8
|
+
const CLIENT_ERROR_LAST = 499;
|
|
9
|
+
const SERVER_ERROR_FIRST = 500;
|
|
10
|
+
const SERVER_ERROR_LAST = 599;
|
|
5
11
|
/**
|
|
6
|
-
*
|
|
12
|
+
* The HTTP status an error's class declares, or `null` when it declares none that may be honoured.
|
|
13
|
+
*
|
|
14
|
+
* Read through the constructor chain — a static property is inherited, so a subclass answers what
|
|
15
|
+
* its nearest declaring ancestor says, and redeclares to change it. An integer 4xx is honoured
|
|
16
|
+
* directly. A 5xx is honoured only with `allowServerErrorStatus = true`, so a domain class must
|
|
17
|
+
* explicitly opt in to exposing a precise availability response. The read is structural, so a
|
|
18
|
+
* class from a duplicate module copy declares exactly what the imported one does.
|
|
19
|
+
*/
|
|
20
|
+
export const declaredErrorStatus = (error) => {
|
|
21
|
+
if (error == null || typeof error !== 'object') {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const declaration = error.constructor;
|
|
25
|
+
const status = declaration?.httpStatus;
|
|
26
|
+
if (typeof status !== 'number' || !Number.isInteger(status)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
if (status >= CLIENT_ERROR_FIRST && status <= CLIENT_ERROR_LAST)
|
|
30
|
+
return status;
|
|
31
|
+
if (status >= SERVER_ERROR_FIRST && status <= SERVER_ERROR_LAST
|
|
32
|
+
&& declaration?.allowServerErrorStatus === true)
|
|
33
|
+
return status;
|
|
34
|
+
return null;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* The registered type names an error carries: its own `type`, and the static `typeName` of every
|
|
38
|
+
* class on its constructor chain.
|
|
39
|
+
*
|
|
40
|
+
* This is how a class is recognised when `instanceof` cannot see it. A process that loads a package
|
|
41
|
+
* twice — `bun --preserve-symlinks` over a linked workspace resolves one package through several
|
|
42
|
+
* `node_modules` paths — holds several copies of every error class, and an error built by one copy
|
|
43
|
+
* is not an instance of another. The registered names are the same in every copy. A subclass's
|
|
44
|
+
* `typeName` does not reliably embed its parent's (`EntitlementRefusal` extends `AuthForbidden`), so
|
|
45
|
+
* a family is recognised by walking the chain and matching names exactly, never by a substring.
|
|
46
|
+
*/
|
|
47
|
+
const typeNamesOf = (error) => {
|
|
48
|
+
const names = new Set();
|
|
49
|
+
const type = error.type;
|
|
50
|
+
if (typeof type === 'string') {
|
|
51
|
+
names.add(type);
|
|
52
|
+
}
|
|
53
|
+
let ctor = error.constructor;
|
|
54
|
+
while (typeof ctor === 'function') {
|
|
55
|
+
const name = ctor.typeName;
|
|
56
|
+
if (typeof name === 'string') {
|
|
57
|
+
names.add(name);
|
|
58
|
+
}
|
|
59
|
+
ctor = Object.getPrototypeOf(ctor);
|
|
60
|
+
}
|
|
61
|
+
return names;
|
|
62
|
+
};
|
|
63
|
+
const isOfFamily = (names, family) => family.some(typeName => names.has(typeName));
|
|
64
|
+
/**
|
|
65
|
+
* The HTTP status an error is answered with, read off the error exactly as given.
|
|
7
66
|
*
|
|
8
67
|
* Two families reach here and both must be recognised. `AuthFailedError` / `AccessError` are this
|
|
9
68
|
* package's own, raised by the request pipeline itself. `AuthorizationError` / `AuthForbidden`
|
|
10
69
|
* come from `@owlmeans/auth` and are what the guards and gates actually throw — an unrecognised
|
|
11
70
|
* one answers 500, which reports a refused request as a crashed server: the client cannot tell
|
|
12
71
|
* "sign in again" from "the service is broken", and a consumer watching statuses concludes the
|
|
13
|
-
* application fell over.
|
|
72
|
+
* application fell over. A family member is recognised by class OR by registered type name
|
|
73
|
+
* ({@link typeNamesOf}), so a class from a duplicate module copy answers the same status.
|
|
14
74
|
*
|
|
15
75
|
* **Order matters.** `AuthForbidden extends AuthorizationError`, so the 403 branch has to be
|
|
16
|
-
* tested first or every refusal of a permission is reported as a failure to authenticate.
|
|
76
|
+
* tested first or every refusal of a permission is reported as a failure to authenticate. Both
|
|
77
|
+
* auth branches come before a class's own declaration, so an auth refusal can never be restated
|
|
78
|
+
* as something else. Everything that is neither an auth error nor a class declaring an allowed
|
|
79
|
+
* {@link declaredErrorStatus} answers 500.
|
|
17
80
|
*/
|
|
18
|
-
export const
|
|
19
|
-
|
|
20
|
-
|
|
81
|
+
export const errorStatus = (error) => {
|
|
82
|
+
if (error == null || typeof error !== 'object') {
|
|
83
|
+
return SERVER_ERROR;
|
|
84
|
+
}
|
|
85
|
+
const names = typeNamesOf(error);
|
|
86
|
+
if (error instanceof AuthForbidden || error instanceof AccessError
|
|
87
|
+
|| isOfFamily(names, [AuthForbidden.typeName, AccessError.typeName])) {
|
|
21
88
|
// An established identity that lacks the permission. Re-authenticating changes nothing.
|
|
22
|
-
|
|
89
|
+
return FORBIDDEN_ERROR;
|
|
23
90
|
}
|
|
24
|
-
|
|
91
|
+
if (error instanceof AuthorizationError || error instanceof AuthFailedError
|
|
92
|
+
|| isOfFamily(names, [AuthorizationError.typeName, AuthFailedError.typeName])) {
|
|
25
93
|
// No usable credential — absent, expired, revoked, or naming a session this server no longer
|
|
26
94
|
// holds. The caller is told to sign in, not that signing in would not help.
|
|
27
|
-
|
|
95
|
+
return UNAUTHORIZED_ERROR;
|
|
28
96
|
}
|
|
97
|
+
return declaredErrorStatus(error) ?? SERVER_ERROR;
|
|
98
|
+
};
|
|
99
|
+
/** Invalid or absent configuration always resolves to the production-safe policy. */
|
|
100
|
+
export const errorExposure = (config) => config?.http?.errors?.exposure === 'development' ? 'development' : 'production';
|
|
101
|
+
/**
|
|
102
|
+
* Preserve a typed/marshalled error when possible without asking `ensure` to convert a plain
|
|
103
|
+
* error. In particular, `ensure` deliberately rethrows SyntaxError; an HTTP boundary still has
|
|
104
|
+
* to serialize it safely and let the incident log retain the original object.
|
|
105
|
+
*/
|
|
106
|
+
const normalizeForWire = (error) => {
|
|
107
|
+
if (error instanceof SyntaxError || isResilientError(error)) {
|
|
108
|
+
return error;
|
|
109
|
+
}
|
|
110
|
+
if (error.message.includes(SEPARATOR)) {
|
|
111
|
+
const ensured = ResilientError.ensure(error);
|
|
112
|
+
// The catch-all converter's legacy constructor shape shifts an ordinary error's fields.
|
|
113
|
+
// Only keep `ensure`'s result when a registered wire prefix actually rebuilt something.
|
|
114
|
+
if (ensured.type !== error.message || ensured.message !== error.stack) {
|
|
115
|
+
return ensured;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return error;
|
|
119
|
+
};
|
|
120
|
+
/** One central wire shape for every server error. */
|
|
121
|
+
export const serializeError = (error, exposure = 'production') => {
|
|
122
|
+
const normalized = normalizeForWire(error);
|
|
123
|
+
const thrown = errorStatus(error);
|
|
124
|
+
const status = thrown !== SERVER_ERROR ? thrown : errorStatus(normalized);
|
|
125
|
+
const incidentId = randomUUID();
|
|
126
|
+
// The server log owns every diagnostic. Production callers receive only its correlation key;
|
|
127
|
+
// development keeps the typed wire form so local clients can retain their usual error details.
|
|
128
|
+
Object.assign(error, { incidentId });
|
|
129
|
+
if (isResilientError(normalized))
|
|
130
|
+
normalized.incidentId = incidentId;
|
|
131
|
+
const body = exposure === 'production'
|
|
132
|
+
? incidentId
|
|
133
|
+
: ResilientError.marshal(normalized, { includeStack: true, incidentId }).message;
|
|
134
|
+
return { status, body, incidentId };
|
|
135
|
+
};
|
|
136
|
+
/** Apply safe response metadata carried by an error without coupling its package to Fastify. */
|
|
137
|
+
export const applyErrorHeaders = (error, reply) => {
|
|
138
|
+
if (error == null || typeof error !== 'object') {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const retryAfter = error.retryAfter;
|
|
142
|
+
if (typeof retryAfter === 'number' && Number.isFinite(retryAfter) && retryAfter > 0) {
|
|
143
|
+
reply.header('Retry-After', String(Math.ceil(retryAfter)));
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* Answer a thrown error: a status, and the marshalled `ResilientError` as the body.
|
|
148
|
+
*
|
|
149
|
+
* The status is resolved in two steps. The error AS THROWN comes first: it is the one object whose
|
|
150
|
+
* class is certainly the class that was raised, while `ResilientError.ensure` rebuilds through the
|
|
151
|
+
* registry of whichever `@owlmeans/error` copy this module loaded — under duplicate module copies
|
|
152
|
+
* that turns a perfectly recognisable auth refusal into a plain `ResilientError` answering 500.
|
|
153
|
+
* Only when the thrown error answers 500 is the ENSURED error asked, because the rebuild is what
|
|
154
|
+
* gives a status to a marshalled error that crossed a hop as a plain `Error`.
|
|
155
|
+
*/
|
|
156
|
+
export const handleError = (error, reply, exposure = 'production') => {
|
|
29
157
|
if (!reply.sent) {
|
|
30
|
-
|
|
158
|
+
const serialized = serializeError(error, exposure);
|
|
159
|
+
applyErrorHeaders(error, reply);
|
|
160
|
+
// Fastify/Pino's `err` serializer retains the original type, message and full stack. This is
|
|
161
|
+
// the one log entry that owns those details in production, keyed by the response incident ID.
|
|
162
|
+
reply.log.error({ err: error, incidentId: serialized.incidentId }, 'Request failed');
|
|
163
|
+
reply.header(INCIDENT_ID_HEADER, serialized.incidentId)
|
|
164
|
+
.code(serialized.status)
|
|
165
|
+
.send(serialized.body);
|
|
31
166
|
}
|
|
32
167
|
};
|
|
33
168
|
//# sourceMappingURL=error.js.map
|
package/build/utils/error.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/utils/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAEjF,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/utils/error.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../src/utils/payload.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../src/utils/payload.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,KAAK,CAAA;AAG1C,eAAO,MAAM,cAAc,UAAW,MAAM,OAAO,OAAO,cAAc,OAAO,KAAG,eAejF,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,YAAY,gBAAgB,CAAC,CAAC,CAAC,SAAS,QAAQ,iBAAiB,OAAO,KAAG,OAuB3G,CAAA;AAGD,eAAO,MAAM,cAAc,WAAY,eAAe,oBAgBrD,CAAA"}
|
package/build/utils/payload.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EntrypointOutcome } from '@owlmeans/entrypoint';
|
|
2
|
-
import { ACCEPTED, CREATED, OK
|
|
2
|
+
import { ACCEPTED, CREATED, OK } from '@owlmeans/api';
|
|
3
|
+
import { handleError } from './error.js';
|
|
3
4
|
export const provideRequest = (alias, req, provision) => {
|
|
4
5
|
provision = provision ?? false;
|
|
5
6
|
return {
|
|
@@ -30,7 +31,7 @@ export const executeResponse = (response, reply, throwOnError) => {
|
|
|
30
31
|
if (throwOnError ?? false) {
|
|
31
32
|
throw response.error;
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
+
handleError(response.error, reply);
|
|
34
35
|
return true;
|
|
35
36
|
}
|
|
36
37
|
else if (response.outcome != null) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payload.js","sourceRoot":"","sources":["../../src/utils/payload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAGxD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"payload.js","sourceRoot":"","sources":["../../src/utils/payload.ts"],"names":[],"mappings":"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"}
|
package/build/utils/server.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { FastifyReply, FastifyRequest } from 'fastify';
|
|
2
2
|
import type { ServerEntrypoint } from '@owlmeans/server-entrypoint';
|
|
3
3
|
import type { CommonEntrypoint } from '@owlmeans/entrypoint';
|
|
4
|
-
import type { ServerContext
|
|
5
|
-
type Config
|
|
4
|
+
import type { ServerContext } from '@owlmeans/server-context';
|
|
5
|
+
import type { Config as ApiConfig } from '../types.js';
|
|
6
|
+
type Config = ApiConfig;
|
|
6
7
|
type Context = ServerContext<Config>;
|
|
7
8
|
export declare const canServeModule: (context: Context, module: CommonEntrypoint) => module is ServerEntrypoint<unknown>;
|
|
8
9
|
export declare const createServerHandler: (module: ServerEntrypoint<FastifyRequest>, location: string) => (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/utils/server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE3D,OAAO,KAAK,EAAgB,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAG5D,OAAO,KAAK,EAAE,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/utils/server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE3D,OAAO,KAAK,EAAgB,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAG5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAO7D,OAAO,KAAK,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,aAAa,CAAA;AAEtD,KAAK,MAAM,GAAG,SAAS,CAAA;AACvB,KAAK,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;AAEpC,eAAO,MAAM,cAAc,YAAa,OAAO,UAAU,gBAAgB,KAAG,MAAM,IAAI,gBAAgB,CAAC,OAAO,CAc7G,CAAA;AAED,eAAO,MAAM,mBAAmB,WAAY,gBAAgB,CAAC,cAAc,CAAC,YAAY,MAAM,WAChF,cAAc,SAAS,YAAY,kBAuC9C,CAAA"}
|
package/build/utils/server.js
CHANGED
|
@@ -3,7 +3,7 @@ import { assertContext } from '@owlmeans/context';
|
|
|
3
3
|
import { provideResponse } from '@owlmeans/entrypoint';
|
|
4
4
|
import { ResilientError } from '@owlmeans/error';
|
|
5
5
|
import { OK } from '@owlmeans/api';
|
|
6
|
-
import { handleError } from './error.js';
|
|
6
|
+
import { errorExposure, handleError } from './error.js';
|
|
7
7
|
import { executeResponse, provideRequest } from './payload.js';
|
|
8
8
|
import { authorize } from './guards.js';
|
|
9
9
|
import { RouteProtocols } from '@owlmeans/route';
|
|
@@ -14,10 +14,9 @@ export const canServeModule = (context, module) => {
|
|
|
14
14
|
if (module.route.route.service != null && module.route.route.service !== context.cfg.service) {
|
|
15
15
|
return false;
|
|
16
16
|
}
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|| module.route.route.protocol === RouteProtocols.QUEUE) {
|
|
17
|
+
// HTTP serves only its own protocol. Custom transports are handled by their owning packages.
|
|
18
|
+
if (module.route.route.protocol != null
|
|
19
|
+
&& module.route.route.protocol !== RouteProtocols.WEB) {
|
|
21
20
|
return false;
|
|
22
21
|
}
|
|
23
22
|
return 'isIntermediate' in module.route;
|
|
@@ -51,15 +50,12 @@ export const createServerHandler = (module, location) => async (req, reply) => {
|
|
|
51
50
|
}
|
|
52
51
|
}
|
|
53
52
|
catch (error) {
|
|
54
|
-
console.error(`Error in ${module.alias} (${location})`);
|
|
55
|
-
console.error(JSON.stringify(error, null, 2));
|
|
56
|
-
console.error(error);
|
|
57
53
|
if (module.fixer != null) {
|
|
58
54
|
const fixer = context.service(module.fixer);
|
|
59
55
|
fixer.handle(reply, ResilientError.ensure(error));
|
|
60
56
|
return;
|
|
61
57
|
}
|
|
62
|
-
handleError(error, reply);
|
|
58
|
+
handleError(error, reply, errorExposure(context.cfg));
|
|
63
59
|
}
|
|
64
60
|
};
|
|
65
61
|
//# sourceMappingURL=server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/utils/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAE3C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAIjD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,EAAE,EAAE,MAAM,eAAe,CAAA;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/utils/server.ts"],"names":[],"mappings":"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"}
|