@owlmeans/server-api 0.1.18-rc.50 → 0.1.18-rc.51

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.
Files changed (55) hide show
  1. package/README.md +20 -19
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/server-api/SKILL.md +9 -9
  4. package/build/server.js +3 -3
  5. package/build/server.js.map +1 -1
  6. package/build/utils/context/types.d.ts +13 -0
  7. package/build/utils/context/types.d.ts.map +1 -0
  8. package/build/utils/context/types.js +2 -0
  9. package/build/utils/context/types.js.map +1 -0
  10. package/build/utils/context.d.ts +3 -3
  11. package/build/utils/context.d.ts.map +1 -1
  12. package/build/utils/context.js +5 -2
  13. package/build/utils/context.js.map +1 -1
  14. package/build/utils/error/types.d.ts +52 -0
  15. package/build/utils/error/types.d.ts.map +1 -0
  16. package/build/utils/error/types.js +2 -0
  17. package/build/utils/error/types.js.map +1 -0
  18. package/build/utils/error.d.ts +4 -47
  19. package/build/utils/error.d.ts.map +1 -1
  20. package/build/utils/error.js +154 -187
  21. package/build/utils/error.js.map +1 -1
  22. package/build/utils/guards.d.ts.map +1 -1
  23. package/build/utils/guards.js +6 -6
  24. package/build/utils/guards.js.map +1 -1
  25. package/build/utils/index.d.ts +4 -1
  26. package/build/utils/index.d.ts.map +1 -1
  27. package/build/utils/index.js +1 -1
  28. package/build/utils/index.js.map +1 -1
  29. package/build/utils/payload/types.d.ts +18 -0
  30. package/build/utils/payload/types.d.ts.map +1 -0
  31. package/build/utils/payload/types.js +2 -0
  32. package/build/utils/payload/types.js.map +1 -0
  33. package/build/utils/payload.d.ts +3 -15
  34. package/build/utils/payload.d.ts.map +1 -1
  35. package/build/utils/payload.js +40 -60
  36. package/build/utils/payload.js.map +1 -1
  37. package/build/utils/schema.d.ts +7 -0
  38. package/build/utils/schema.d.ts.map +1 -0
  39. package/build/utils/schema.js +20 -0
  40. package/build/utils/schema.js.map +1 -0
  41. package/build/utils/server.js +6 -6
  42. package/build/utils/server.js.map +1 -1
  43. package/package.json +12 -12
  44. package/src/server.ts +3 -3
  45. package/src/utils/context/types.ts +13 -0
  46. package/src/utils/context.ts +9 -4
  47. package/src/utils/error/types.ts +52 -0
  48. package/src/utils/error.ts +156 -186
  49. package/src/utils/guards.ts +6 -6
  50. package/src/utils/index.ts +4 -1
  51. package/src/utils/payload/types.ts +18 -0
  52. package/src/utils/payload.ts +41 -63
  53. package/src/utils/schema.ts +23 -0
  54. package/src/utils/server.ts +6 -6
  55. package/tests/error-status.spec.ts +11 -11
@@ -1,68 +1,48 @@
1
1
  import { EntrypointOutcome } from '@owlmeans/entrypoint';
2
2
  import { ACCEPTED, CREATED, OK } from '@owlmeans/api';
3
- import { handleError } from './error.js';
4
- export const provideRequest = (alias, req, provision) => {
5
- provision = provision ?? false;
6
- return {
7
- alias,
8
- auth: req._auth ?? undefined,
9
- // Resolved once by `authorize` and carried by reference, exactly like `_auth`, so a handler
10
- // reached through any path sees the same entity the guard admitted.
11
- entity: req._entity ?? undefined,
12
- params: req.params,
13
- body: req.body,
14
- headers: req.headers,
15
- query: req.query,
16
- path: req.url,
17
- original: provision ? req : undefined
3
+ import { httpErrorHelper } from './error.js';
4
+ export const createPayloadHelper = () => {
5
+ const provideRequest = (alias, req, provision) => {
6
+ provision = provision ?? false;
7
+ return {
8
+ alias,
9
+ auth: req._auth ?? undefined,
10
+ // Resolved once by `authorize` and carried by reference, exactly like `_auth`, so a handler
11
+ // reached through any path sees the same entity the guard admitted.
12
+ entity: req._entity ?? undefined,
13
+ params: req.params,
14
+ body: req.body,
15
+ headers: req.headers,
16
+ query: req.query,
17
+ path: req.url,
18
+ original: provision ? req : undefined
19
+ };
18
20
  };
19
- };
20
- /**
21
- * Emits the response onto the fastify reply if the handler produced an
22
- * error or an outcome. Returns `true` when something was actually sent so
23
- * callers don't rely on `reply.sent`, which in fastify v5 only flips once
24
- * the raw socket finishes writing (asynchronously) and therefore reads
25
- * `false` synchronously right after `reply.send()`.
26
- *
27
- * @throws {Error}
28
- */
29
- export const executeResponse = (response, reply, throwOnError) => {
30
- if (response.error != null) {
31
- if (throwOnError ?? false) {
32
- throw response.error;
33
- }
34
- handleError(response.error, reply);
35
- return true;
36
- }
37
- else if (response.outcome != null) {
38
- switch (response.outcome) {
39
- case EntrypointOutcome.Accepted:
40
- reply.code(ACCEPTED).send(response.value);
41
- break;
42
- case EntrypointOutcome.Created:
43
- reply.code(CREATED).send(response.value);
44
- break;
45
- case EntrypointOutcome.Ok:
46
- default:
47
- reply.code(OK).send(response.value);
48
- }
49
- return true;
50
- }
51
- return false;
52
- };
53
- export const fixFormatDates = (schema) => {
54
- if (schema.type === 'object') {
55
- if (schema.format === 'date-time') {
56
- schema.type = 'string';
57
- schema.format = 'date-time';
58
- if (schema.required != null) {
59
- delete schema.required;
21
+ const executeResponse = (response, reply, throwOnError) => {
22
+ if (response.error != null) {
23
+ if (throwOnError ?? false) {
24
+ throw response.error;
60
25
  }
26
+ httpErrorHelper.handleError(response.error, reply);
27
+ return true;
61
28
  }
62
- else if (schema.properties != null) {
63
- schema.properties = Object.fromEntries(Object.entries(schema.properties).map(([key, value]) => [key, fixFormatDates(value)]));
29
+ else if (response.outcome != null) {
30
+ switch (response.outcome) {
31
+ case EntrypointOutcome.Accepted:
32
+ reply.code(ACCEPTED).send(response.value);
33
+ break;
34
+ case EntrypointOutcome.Created:
35
+ reply.code(CREATED).send(response.value);
36
+ break;
37
+ case EntrypointOutcome.Ok:
38
+ default:
39
+ reply.code(OK).send(response.value);
40
+ }
41
+ return true;
64
42
  }
65
- }
66
- return schema;
43
+ return false;
44
+ };
45
+ return { provideRequest, executeResponse };
67
46
  };
47
+ export const payloadHelper = createPayloadHelper();
68
48
  //# sourceMappingURL=payload.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"payload.js","sourceRoot":"","sources":["../../src/utils/payload.ts"],"names":[],"mappings":"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"}
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,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAG5C,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAkB,EAAE;IACrD,MAAM,cAAc,GAAG,CAAC,KAAa,EAAE,GAAY,EAAE,SAAmB,EAAmB,EAAE;QAC3F,SAAS,GAAG,SAAS,IAAI,KAAK,CAAA;QAC9B,OAAO;YACL,KAAK;YACL,IAAI,EAAG,GAAW,CAAC,KAAK,IAAI,SAAS;YACrC,4FAA4F;YAC5F,oEAAoE;YACpE,MAAM,EAAG,GAAW,CAAC,OAAO,IAAI,SAAS;YACzC,MAAM,EAAE,GAAG,CAAC,MAA4D;YACxE,IAAI,EAAE,GAAG,CAAC,IAA2B;YACrC,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,KAAK,EAAE,GAAG,CAAC,KAA2D;YACtE,IAAI,EAAE,GAAG,CAAC,GAAG;YACb,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;SACtC,CAAA;IACH,CAAC,CAAA;IAED,MAAM,eAAe,GAAG,CAAI,QAA6B,EAAE,KAAe,EAAE,YAAsB,EAAW,EAAE;QAC7G,IAAI,QAAQ,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;YAC3B,IAAI,YAAY,IAAI,KAAK,EAAE,CAAC;gBAC1B,MAAM,QAAQ,CAAC,KAAK,CAAA;YACtB,CAAC;YACD,eAAe,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;YAClD,OAAO,IAAI,CAAA;QACb,CAAC;aAAM,IAAI,QAAQ,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC;YACpC,QAAQ,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACzB,KAAK,iBAAiB,CAAC,QAAQ;oBAC7B,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;oBACzC,MAAK;gBACP,KAAK,iBAAiB,CAAC,OAAO;oBAC5B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;oBACxC,MAAK;gBACP,KAAK,iBAAiB,CAAC,EAAE,CAAC;gBAC1B;oBACE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;YACvC,CAAC;YACD,OAAO,IAAI,CAAA;QACb,CAAC;QAED,OAAO,KAAK,CAAA;IACd,CAAC,CAAA;IAED,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,CAAA;AAC5C,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,mBAAmB,EAAE,CAAA"}
@@ -0,0 +1,7 @@
1
+ import type { AnySchemaObject } from 'ajv';
2
+ /**
3
+ * A request schema Fastify can serve: a `date-time` object becomes a `date-time` string (and loses
4
+ * its `required` list), recursively through object properties. Mutates and returns `schema`.
5
+ */
6
+ export declare const fixFormatDates: (schema: AnySchemaObject) => AnySchemaObject;
7
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/utils/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,KAAK,CAAA;AAE1C;;;GAGG;AACH,eAAO,MAAM,cAAc,WAAY,eAAe,KAAG,eAgBxD,CAAA"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * A request schema Fastify can serve: a `date-time` object becomes a `date-time` string (and loses
3
+ * its `required` list), recursively through object properties. Mutates and returns `schema`.
4
+ */
5
+ export const fixFormatDates = (schema) => {
6
+ if (schema.type === 'object') {
7
+ if (schema.format === 'date-time') {
8
+ schema.type = 'string';
9
+ schema.format = 'date-time';
10
+ if (schema.required != null) {
11
+ delete schema.required;
12
+ }
13
+ }
14
+ else if (schema.properties != null) {
15
+ schema.properties = Object.fromEntries(Object.entries(schema.properties).map(([key, value]) => [key, fixFormatDates(value)]));
16
+ }
17
+ }
18
+ return schema;
19
+ };
20
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/utils/schema.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAuB,EAAmB,EAAE;IACzE,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,MAAM,CAAC,IAAI,GAAG,QAAQ,CAAA;YACtB,MAAM,CAAC,MAAM,GAAG,WAAW,CAAA;YAC3B,IAAI,MAAM,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC;gBAC5B,OAAO,MAAM,CAAC,QAAQ,CAAA;YACxB,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC;YACrC,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,WAAW,CACpC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,cAAc,CAAC,KAAwB,CAAC,CAAC,CAAC,CACzG,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC,CAAA"}
@@ -3,8 +3,8 @@ import { provideResponse } from '@owlmeans/entrypoint';
3
3
  import { ResilientError } from '@owlmeans/error';
4
4
  import { OK } from '@owlmeans/api';
5
5
  import { logger } from '@owlmeans/log';
6
- import { errorExposure, handleError } from './error.js';
7
- import { executeResponse, provideRequest } from './payload.js';
6
+ import { httpErrorHelper } from './error.js';
7
+ import { payloadHelper } from './payload.js';
8
8
  import { authorize } from './guards.js';
9
9
  import { RouteProtocols } from '@owlmeans/route';
10
10
  export const canServeModule = (context, module) => {
@@ -29,12 +29,12 @@ export const createServerHandler = (module, location) => async (req, reply) => {
29
29
  context = authorized[0];
30
30
  module = authorized[1];
31
31
  const response = provideResponse(reply);
32
- const request = provideRequest(module.alias, req, true);
32
+ const request = payloadHelper.provideRequest(module.alias, req, true);
33
33
  const gates = module.getGates();
34
34
  for (const [srv, params] of gates) {
35
35
  const gate = context.service(srv);
36
36
  await gate.assert(request, response, params);
37
- executeResponse(response, reply, true);
37
+ payloadHelper.executeResponse(response, reply, true);
38
38
  }
39
39
  await module.handle(request, response);
40
40
  // Don't rely on `reply.sent` here: in fastify v5 it is backed by
@@ -43,7 +43,7 @@ export const createServerHandler = (module, location) => async (req, reply) => {
43
43
  // synchronously right after `reply.send()`. Track the emitted state
44
44
  // explicitly instead, and only fall back to a default response when
45
45
  // neither `executeResponse` nor the handler itself (hijack) replied.
46
- const responded = executeResponse(response, reply, true);
46
+ const responded = payloadHelper.executeResponse(response, reply, true);
47
47
  if (!responded && !reply.sent) {
48
48
  logger('http').warn(`SENDS DEFAULT RESPONSE: ${module.alias}`);
49
49
  reply.code(OK).send(response.value);
@@ -55,7 +55,7 @@ export const createServerHandler = (module, location) => async (req, reply) => {
55
55
  fixer.handle(reply, ResilientError.ensure(error));
56
56
  return;
57
57
  }
58
- handleError(error, reply, errorExposure(context.cfg));
58
+ httpErrorHelper.handleError(error, reply, httpErrorHelper.errorExposure(context.cfg));
59
59
  }
60
60
  };
61
61
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/utils/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/utils/server.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/server-api",
3
- "version": "0.1.18-rc.50",
3
+ "version": "0.1.18-rc.51",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -33,16 +33,16 @@
33
33
  "@fastify/helmet": "^13.1.1",
34
34
  "@fastify/middie": "^9.3.4",
35
35
  "@fastify/multipart": "^10.1.2",
36
- "@owlmeans/api": "^0.1.18-rc.46",
37
- "@owlmeans/auth": "^0.1.18-rc.39",
38
- "@owlmeans/auth-common": "^0.1.18-rc.46",
39
- "@owlmeans/context": "^0.1.18-rc.33",
40
- "@owlmeans/entrypoint": "^0.1.18-rc.40",
41
- "@owlmeans/error": "^0.1.18-rc.37",
42
- "@owlmeans/log": "^0.1.18-rc.1",
43
- "@owlmeans/route": "^0.1.18-rc.34",
44
- "@owlmeans/server-context": "^0.1.18-rc.42",
45
- "@owlmeans/server-entrypoint": "^0.1.18-rc.39",
36
+ "@owlmeans/api": "^0.1.18-rc.47",
37
+ "@owlmeans/auth": "^0.1.18-rc.40",
38
+ "@owlmeans/auth-common": "^0.1.18-rc.47",
39
+ "@owlmeans/context": "^0.1.18-rc.34",
40
+ "@owlmeans/entrypoint": "^0.1.18-rc.41",
41
+ "@owlmeans/error": "^0.1.18-rc.38",
42
+ "@owlmeans/log": "^0.1.18-rc.2",
43
+ "@owlmeans/route": "^0.1.18-rc.35",
44
+ "@owlmeans/server-context": "^0.1.18-rc.43",
45
+ "@owlmeans/server-entrypoint": "^0.1.18-rc.40",
46
46
  "ajv-errors": "^3.0.0",
47
47
  "fastify": "^5.12.5",
48
48
  "fastify-raw-body": "^6.0.1",
@@ -54,7 +54,7 @@
54
54
  },
55
55
  "devDependencies": {
56
56
  "@owlmeans/dep-config": "workspace:*",
57
- "@owlmeans/resource": "^0.1.18-rc.39",
57
+ "@owlmeans/resource": "^0.1.18-rc.40",
58
58
  "@types/bun": "^1.4.2",
59
59
  "@types/node": "^26.6.4",
60
60
  "nodemon": "^3.1.14",
package/src/server.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { ServerContext } from '@owlmeans/server-context'
2
2
  import type { ApiServer, ApiServerAppend } from './types.js'
3
3
  import { assertContext, createService } from '@owlmeans/context'
4
- import { canServeModule, executeResponse, provideRequest, createServerHandler, fixFormatDates } from './utils/index.js'
4
+ import { canServeModule, payloadHelper, createServerHandler, fixFormatDates } from './utils/index.js'
5
5
  import { DEFAULT_ALIAS, CLOSED_HOST, PORT, OPENED_HOST } from './consts.js'
6
6
  import type { ServerEntrypoint } from '@owlmeans/server-entrypoint'
7
7
  import { RouteMethod } from '@owlmeans/route'
@@ -144,13 +144,13 @@ export const createApiServer = (alias: string): ApiServer => {
144
144
  // An intermediate route can be created without a handler by the binding layer.
145
145
  if (module.route.match(request, module.mount()) && module.handle != null) {
146
146
  const response = provideResponse(reply)
147
- const currentRequest = provideRequest(module.alias, request, true)
147
+ const currentRequest = payloadHelper.provideRequest(module.alias, request, true)
148
148
  currentRequest.original._ctx = context
149
149
  const result: Context = await module.handle(currentRequest, response)
150
150
  if (result != null) {
151
151
  context = result
152
152
  }
153
- responded = executeResponse(response, reply, true) || responded
153
+ responded = payloadHelper.executeResponse(response, reply, true) || responded
154
154
  }
155
155
  return context
156
156
  }, Promise.resolve(context))
@@ -0,0 +1,13 @@
1
+ import type { Config, Context } from '../../types.js'
2
+
3
+ /** Carries the serving context on one raw request. */
4
+ export interface RequestContextHelper {
5
+ /** Stores the context on the request. */
6
+ populateContext: <C extends Config, T extends Context<C>>(context: T) => void
7
+ /**
8
+ * The context stored on the request, else `ctx`.
9
+ *
10
+ * @throws {SyntaxError} when neither is there
11
+ */
12
+ extractContext: <C extends Config, T extends Context<C>>(ctx?: T, location?: string) => T
13
+ }
@@ -1,9 +1,14 @@
1
1
  import { assertContext } from '@owlmeans/context'
2
2
  import type { Config, Context, Request } from '../types.js'
3
3
  import { DEFAULT_ALIAS } from '../consts.js'
4
+ import type { RequestContextHelper } from './context/types.js'
4
5
 
5
- export const populateContext = <C extends Config, T extends Context<C>>(req: Request, context: T): void =>
6
- void ((req as any)._ctx = context)
6
+ export const makeRequestContextHelper = (req: Request): RequestContextHelper => {
7
+ const populateContext = <C extends Config, T extends Context<C>>(context: T): void =>
8
+ void ((req as any)._ctx = context)
7
9
 
8
- export const extractContext = <C extends Config, T extends Context<C>>(req: Request, ctx?: T, location?: string): T =>
9
- assertContext<C, T>((req as any)._ctx ?? ctx, location ?? DEFAULT_ALIAS)
10
+ const extractContext = <C extends Config, T extends Context<C>>(ctx?: T, location?: string): T =>
11
+ assertContext<C, T>((req as any)._ctx ?? ctx, location ?? DEFAULT_ALIAS)
12
+
13
+ return { populateContext, extractContext }
14
+ }
@@ -0,0 +1,52 @@
1
+ import type { FastifyReply } from 'fastify'
2
+ import type { Config, HttpErrorExposure } from '../../types.js'
3
+ import type { SerializedHttpError } from '../types.js'
4
+
5
+ /** How the HTTP boundary answers a thrown error: its status, its wire body, its headers and its log entry. */
6
+ export interface HttpErrorHelper {
7
+ /**
8
+ * The HTTP status an error's class declares, or `null` when it declares none that may be honoured.
9
+ *
10
+ * Read through the constructor chain — a static property is inherited, so a subclass answers what
11
+ * its nearest declaring ancestor says, and redeclares to change it. An integer 4xx is honoured
12
+ * directly. A 5xx is honoured only with `allowServerErrorStatus = true`, so a domain class must
13
+ * explicitly opt in to exposing a precise availability response. The read is structural, so a
14
+ * class from a duplicate module copy declares exactly what the imported one does.
15
+ */
16
+ declaredErrorStatus: (error: unknown) => number | null
17
+ /**
18
+ * The HTTP status an error is answered with, read off the error exactly as given.
19
+ *
20
+ * Two families reach here and both must be recognised. `AuthFailedError` / `AccessError` are this
21
+ * package's own, raised by the request pipeline itself. `AuthorizationError` / `AuthForbidden`
22
+ * come from `@owlmeans/auth` and are what the guards and gates actually throw — an unrecognised
23
+ * one answers 500, which reports a refused request as a crashed server: the client cannot tell
24
+ * "sign in again" from "the service is broken", and a consumer watching statuses concludes the
25
+ * application fell over. A family member is recognised by class OR by registered type name, so a
26
+ * class from a duplicate module copy answers the same status.
27
+ *
28
+ * **Order matters.** `AuthForbidden extends AuthorizationError`, so the 403 branch has to be
29
+ * tested first or every refusal of a permission is reported as a failure to authenticate. Both
30
+ * auth branches come before a class's own declaration, so an auth refusal can never be restated
31
+ * as something else. Everything that is neither an auth error nor a class declaring an allowed
32
+ * {@link HttpErrorHelper.declaredErrorStatus} answers 500.
33
+ */
34
+ errorStatus: (error: unknown) => number
35
+ /** Invalid or absent configuration always resolves to the production-safe policy. */
36
+ errorExposure: (config?: Pick<Config, 'http'>) => HttpErrorExposure
37
+ /** One central wire shape for every server error. */
38
+ serializeError: (error: Error, exposure?: HttpErrorExposure) => SerializedHttpError
39
+ /** Apply safe response metadata carried by an error without coupling its package to Fastify. */
40
+ applyErrorHeaders: (error: unknown, reply: FastifyReply) => void
41
+ /**
42
+ * Answer a thrown error: a status, and the marshalled `ResilientError` as the body.
43
+ *
44
+ * The status is resolved in two steps. The error AS THROWN comes first: it is the one object whose
45
+ * class is certainly the class that was raised, while `ResilientError.ensure` rebuilds through the
46
+ * registry of whichever `@owlmeans/error` copy this module loaded — under duplicate module copies
47
+ * that turns a perfectly recognisable auth refusal into a plain `ResilientError` answering 500.
48
+ * Only when the thrown error answers 500 is the ENSURED error asked, because the rebuild is what
49
+ * gives a status to a marshalled error that crossed a hop as a plain `Error`.
50
+ */
51
+ handleError: (error: Error, reply: FastifyReply, exposure?: HttpErrorExposure) => void
52
+ }