@setu-ts/exceptions 0.0.0-bootstrap.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +179 -2
  3. package/dist/errors/exceptions.d.ts +146 -0
  4. package/dist/errors/exceptions.d.ts.map +1 -0
  5. package/dist/errors/exceptions.js +189 -0
  6. package/dist/errors/exceptions.js.map +1 -0
  7. package/dist/errors/http-error.d.ts +111 -0
  8. package/dist/errors/http-error.d.ts.map +1 -0
  9. package/dist/errors/http-error.js +81 -0
  10. package/dist/errors/http-error.js.map +1 -0
  11. package/dist/formatters/error-formatter.d.ts +81 -0
  12. package/dist/formatters/error-formatter.d.ts.map +1 -0
  13. package/dist/formatters/error-formatter.js +58 -0
  14. package/dist/formatters/error-formatter.js.map +1 -0
  15. package/dist/formatters/problem-details.d.ts +118 -0
  16. package/dist/formatters/problem-details.d.ts.map +1 -0
  17. package/dist/formatters/problem-details.js +108 -0
  18. package/dist/formatters/problem-details.js.map +1 -0
  19. package/dist/formatters/rfc7807-formatter.d.ts +20 -0
  20. package/dist/formatters/rfc7807-formatter.d.ts.map +1 -0
  21. package/dist/formatters/rfc7807-formatter.js +27 -0
  22. package/dist/formatters/rfc7807-formatter.js.map +1 -0
  23. package/dist/formatters/rfc9457-formatter.d.ts +29 -0
  24. package/dist/formatters/rfc9457-formatter.d.ts.map +1 -0
  25. package/dist/formatters/rfc9457-formatter.js +43 -0
  26. package/dist/formatters/rfc9457-formatter.js.map +1 -0
  27. package/dist/index.d.ts +33 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +33 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/middleware/error-handler.d.ts +112 -0
  32. package/dist/middleware/error-handler.d.ts.map +1 -0
  33. package/dist/middleware/error-handler.js +210 -0
  34. package/dist/middleware/error-handler.js.map +1 -0
  35. package/dist/middleware/error-responder-impl.d.ts +52 -0
  36. package/dist/middleware/error-responder-impl.d.ts.map +1 -0
  37. package/dist/middleware/error-responder-impl.js +148 -0
  38. package/dist/middleware/error-responder-impl.js.map +1 -0
  39. package/package.json +44 -3
  40. package/src/errors/exceptions.ts +212 -0
  41. package/src/errors/http-error.ts +132 -0
  42. package/src/formatters/error-formatter.ts +127 -0
  43. package/src/formatters/problem-details.ts +176 -0
  44. package/src/formatters/rfc7807-formatter.ts +48 -0
  45. package/src/formatters/rfc9457-formatter.ts +69 -0
  46. package/src/index.ts +57 -0
  47. package/src/middleware/error-handler.ts +337 -0
  48. package/src/middleware/error-responder-impl.ts +178 -0
package/dist/index.js ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @module
3
+ *
4
+ * Exception factory functions, `HttpError`, error formatters, and the global
5
+ * error handler middleware.
6
+ *
7
+ * This is a **plain package** (not a plugin) — it depends on
8
+ * `@setu-ts/common` only and exposes types, factories, and a
9
+ * middleware factory. Register the middleware via the application's pipeline:
10
+ *
11
+ * ```typescript
12
+ * import { errorHandler } from '@setu-ts/exceptions';
13
+ *
14
+ * app.middleware.add(errorHandler({ format: 'rfc9457' }), {
15
+ * priority: 0,
16
+ * name: 'error-handler',
17
+ * });
18
+ * ```
19
+ *
20
+ * Every export here is public API and documented in PUBLIC_API.md
21
+ * (AI_GUIDELINES §10).
22
+ */
23
+ // Error type
24
+ export { HttpError } from "./errors/http-error.js";
25
+ // Exception factories
26
+ export { badRequest, conflict, forbidden, internalServerError, notFound, notImplemented, serviceUnavailable, STATUS_TITLES, statusTitle, tooManyRequests, unauthorized, validationError, } from "./errors/exceptions.js";
27
+ // Formatters
28
+ export { defaultFormatter, selectFormatter } from "./formatters/error-formatter.js";
29
+ export { ERROR_TYPE_BASE, rfc9457Formatter } from "./formatters/rfc9457-formatter.js";
30
+ export { rfc7807Formatter } from "./formatters/rfc7807-formatter.js";
31
+ // Middleware
32
+ export { errorHandler } from "./middleware/error-handler.js";
33
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,aAAa;AACb,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAGnD,sBAAsB;AACtB,OAAO,EACL,UAAU,EACV,QAAQ,EACR,SAAS,EACT,mBAAmB,EACnB,QAAQ,EACR,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,eAAe,EACf,YAAY,EACZ,eAAe,GAChB,MAAM,wBAAwB,CAAC;AAEhC,aAAa;AACb,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAMpF,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAEtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAErE,aAAa;AACb,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Global error-handler middleware factory.
3
+ *
4
+ * Returns a {@linkcode MiddlewareFunction} that wraps `next()` in a try/catch,
5
+ * so any error thrown by downstream middleware or the route handler is caught,
6
+ * logged (when a logger is registered), formatted, and sent as a JSON error
7
+ * response. Register it as the **outermost** middleware (lowest priority number)
8
+ * so it wraps the entire pipeline.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { errorHandler } from '@setu-ts/exceptions';
13
+ *
14
+ * app.middleware.add(errorHandler({
15
+ * format: 'rfc9457',
16
+ * includeStackTrace: config.get('NODE_ENV') === 'development',
17
+ * logErrors: true,
18
+ * }), { priority: 0, name: 'error-handler' });
19
+ * ```
20
+ *
21
+ * @module
22
+ */
23
+ import type { HandlerResult, IRequestContext, MiddlewareFunction } from '@setu-ts/common';
24
+ import { HttpError } from '../errors/http-error.ts';
25
+ import { type ErrorFormat, type ErrorHandlerFormatter } from '../formatters/error-formatter.ts';
26
+ /**
27
+ * Options for the {@linkcode errorHandler} middleware factory.
28
+ *
29
+ * @since 0.1.0
30
+ */
31
+ export interface ErrorHandlerOptions {
32
+ /**
33
+ * The error body format: `'default'`, `'rfc9457'`, the deprecated
34
+ * `'rfc7807'`, or a custom formatter function. Defaults to `'default'`.
35
+ */
36
+ readonly format?: ErrorFormat | ErrorHandlerFormatter;
37
+ /**
38
+ * When `true`, the error `stack` trace is included in the response body.
39
+ * **Never** enable this in production — pass a config-derived boolean (e.g.
40
+ * `config.get('NODE_ENV') === 'development'`), never read `process.env`
41
+ * directly (AI_GUIDELINES §4.1). Defaults to `false`.
42
+ *
43
+ * Note the stack is the _secondary_ disclosure: the primary one is the
44
+ * error **message**, which for a failed query carries the SQL and its bound
45
+ * parameter values. Masking that is `maskInternalErrors`' job.
46
+ *
47
+ * The two compose safely, and masking wins: an error masked by
48
+ * {@linkcode ErrorHandlerOptions.maskInternalErrors} carries **no** stack in
49
+ * the body even when this option is `true`, because a stack begins with the
50
+ * very message that was masked. Set `maskInternalErrors: false` to see the
51
+ * stack of an internal error.
52
+ */
53
+ readonly includeStackTrace?: boolean;
54
+ /**
55
+ * When `true` (the default), a caught value that was **not** an
56
+ * {@linkcode HttpError} and resolves to a status `>= 500` is masked in the
57
+ * response: its `detail`/`message` becomes the status title
58
+ * (`'Internal Server Error'`) and the raw message — which for a failed
59
+ * query carries the SQL and its bound parameter values — is dropped from the
60
+ * body. The log is unaffected: `logErrors` still records the unmasked error
61
+ * and its cause chain, so an operator loses nothing unless `logErrors` is
62
+ * _also_ `false`, the configuration that already logs nothing.
63
+ *
64
+ * A deliberately thrown `HttpError` is never masked — `instanceof HttpError`
65
+ * is the line between "the developer wrote this for a caller" and "this
66
+ * escaped from a driver". `false` restores the previous behaviour verbatim.
67
+ */
68
+ readonly maskInternalErrors?: boolean;
69
+ /**
70
+ * When `true` (the default), caught errors are logged at `error` level via
71
+ * the `ILogger` resolved from `ctx.services` — but only if a logger is
72
+ * registered. When no logger is present, logging is silently skipped.
73
+ */
74
+ readonly logErrors?: boolean;
75
+ /**
76
+ * Lets the application write its own response for a caught error. The hook
77
+ * receives the normalized error after status hints, internal-error masking,
78
+ * and status resolution, so `error.statusCode` is safe to serve. Return a
79
+ * {@linkcode HandlerResult} produced by `ctx.response` to use that response;
80
+ * it may resolve that result asynchronously. Return or resolve to `undefined`
81
+ * to fall through to the configured formatter unchanged.
82
+ *
83
+ * The hook owns its returned response's status, headers, and body. It is
84
+ * called only for errors caught by this middleware, not responder-based
85
+ * terminals such as an unmatched-path `404`.
86
+ */
87
+ readonly respond?: (error: HttpError, ctx: IRequestContext) => HandlerResult | undefined | Promise<HandlerResult | undefined>;
88
+ }
89
+ /**
90
+ * Creates a global error-handler middleware.
91
+ *
92
+ * Behavior:
93
+ * 1. Calls `next()` inside a try/catch.
94
+ * 2. If `next()` throws an `HttpError`, its `statusCode` is used as-is.
95
+ * 3. If `next()` throws any other `Error`, it is wrapped in a `500`
96
+ * `internalServerError` carrying the original as `cause`.
97
+ * 4. When configured, lets the application write its own response. A returned
98
+ * or resolved `HandlerResult` short-circuits formatting; `undefined` falls
99
+ * through.
100
+ * 5. When `logErrors` is on and a logger is registered, the unmasked error is
101
+ * logged with the status the selected response serves.
102
+ * 6. Otherwise, formats the error via {@linkcode selectFormatter}, optionally
103
+ * enriches it with a `stack` trace, and sends it with the right status and
104
+ * content type. The middleware **returns a `HandlerResult`** (short-circuit)
105
+ * and never re-invokes `next()`.
106
+ *
107
+ * @param options - Error handler configuration
108
+ * @returns A middleware function
109
+ * @since 0.1.0
110
+ */
111
+ export declare function errorHandler(options?: ErrorHandlerOptions): MiddlewareFunction;
112
+ //# sourceMappingURL=error-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAW,eAAe,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAUnG,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGpD,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,qBAAqB,EAE3B,MAAM,kCAAkC,CAAC;AAI1C;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,qBAAqB,CAAC;IACtD;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IACrC;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CACjB,KAAK,EAAE,SAAS,EAChB,GAAG,EAAE,eAAe,KACjB,aAAa,GAAG,SAAS,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;CACrE;AAuBD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,kBAAkB,CAiK9E"}
@@ -0,0 +1,210 @@
1
+ import { brandErrorResponder, CAPABILITIES, ERROR_RESPONDER_STATE_KEY, httpStatusHintOf, resolveResponseStatus, serializeError, } from '@setu-ts/common';
2
+ import { HttpError } from "../errors/http-error.js";
3
+ import { internalServerError, statusTitle } from "../errors/exceptions.js";
4
+ import { buildErrorFromInit, createErrorResponder } from "./error-responder-impl.js";
5
+ import { selectFormatter, } from "../formatters/error-formatter.js";
6
+ import { rfc9457Formatter } from "../formatters/rfc9457-formatter.js";
7
+ import { rfc7807Formatter } from "../formatters/rfc7807-formatter.js";
8
+ /** The `application/problem+json` content type for Problem Details responses. */
9
+ const PROBLEM_JSON = 'application/problem+json';
10
+ /** The default JSON content type. */
11
+ const JSON_CONTENT_TYPE = 'application/json; charset=utf-8';
12
+ /**
13
+ * The formatters whose bodies are Problem Details and therefore require the
14
+ * `application/problem+json` media type (RFC 9457 §3).
15
+ *
16
+ * Membership is keyed on the RESOLVED formatter rather than on the format
17
+ * string, because both formatters are exported: `format: rfc9457Formatter`
18
+ * passes a reference and must carry the same media type as `format: 'rfc9457'`.
19
+ * A body served as `application/json` is ignored by generic problem-details
20
+ * clients, so a formatter missing from this set is a silent interoperability
21
+ * defect rather than a visible failure.
22
+ */
23
+ const PROBLEM_DETAILS_FORMATTERS = new Set([
24
+ rfc9457Formatter,
25
+ rfc7807Formatter,
26
+ ]);
27
+ /**
28
+ * Creates a global error-handler middleware.
29
+ *
30
+ * Behavior:
31
+ * 1. Calls `next()` inside a try/catch.
32
+ * 2. If `next()` throws an `HttpError`, its `statusCode` is used as-is.
33
+ * 3. If `next()` throws any other `Error`, it is wrapped in a `500`
34
+ * `internalServerError` carrying the original as `cause`.
35
+ * 4. When configured, lets the application write its own response. A returned
36
+ * or resolved `HandlerResult` short-circuits formatting; `undefined` falls
37
+ * through.
38
+ * 5. When `logErrors` is on and a logger is registered, the unmasked error is
39
+ * logged with the status the selected response serves.
40
+ * 6. Otherwise, formats the error via {@linkcode selectFormatter}, optionally
41
+ * enriches it with a `stack` trace, and sends it with the right status and
42
+ * content type. The middleware **returns a `HandlerResult`** (short-circuit)
43
+ * and never re-invokes `next()`.
44
+ *
45
+ * @param options - Error handler configuration
46
+ * @returns A middleware function
47
+ * @since 0.1.0
48
+ */
49
+ export function errorHandler(options) {
50
+ const format = options?.format ?? 'default';
51
+ const includeStackTrace = options?.includeStackTrace ?? false;
52
+ const maskInternalErrors = options?.maskInternalErrors ?? true;
53
+ const logErrors = options?.logErrors ?? true;
54
+ const respond = options?.respond;
55
+ const formatter = selectFormatter(format);
56
+ const contentType = PROBLEM_DETAILS_FORMATTERS.has(formatter) ? PROBLEM_JSON : JSON_CONTENT_TYPE;
57
+ // The responder is built ONCE at factory time from the formatter and content
58
+ // type already resolved above, and published per request below: one
59
+ // `Map.set` of a pre-built object, not a re-resolution (AI_GUIDELINES §14).
60
+ const responder = createErrorResponder(formatter, contentType);
61
+ const handleError = async (ctx, next) => {
62
+ // Publish the responder BEFORE `next()` so every site inside the pipeline
63
+ // — the kernel's own terminals and every short-circuiting middleware —
64
+ // answers in this application's configured format (M70f).
65
+ ctx.state.set(ERROR_RESPONDER_STATE_KEY, responder);
66
+ try {
67
+ await next();
68
+ return;
69
+ }
70
+ catch (rawError) {
71
+ // Normalize: HttpError passes through, anything else becomes a 500.
72
+ const isHttpError = rawError instanceof HttpError;
73
+ const error = isHttpError ? rawError : internalServerError(rawError instanceof Error ? rawError.message : 'Internal Server Error', rawError instanceof Error ? rawError : undefined);
74
+ // A thrower that cannot import this package states the status its own
75
+ // error means by branding it with an `HttpStatusHint` (M89b) — the seam
76
+ // `@setu-ts/database-plugin` uses for its query-shape refusals, which
77
+ // are caller errors and were answered as masked 500s. The hint carries
78
+ // its own caller-safe `detail`, so the response is built from THAT and
79
+ // never from the error's message, and `buildErrorFromInit` is the same
80
+ // constructor `respondWithError` reaches: one mapping, one body shape.
81
+ //
82
+ // Read only for a non-`HttpError`. A deliberately thrown `HttpError`
83
+ // already states its own status, and letting a brand override it would
84
+ // give one error two answers.
85
+ const hint = isHttpError ? undefined : httpStatusHintOf(rawError);
86
+ // Mask a driver-shaped 500 for the response: the raw message becomes the
87
+ // status title so the body carries neither the statement nor the values.
88
+ // A deliberately thrown HttpError is never masked, and a 4xx is left
89
+ // alone — only a non-HttpError with status >= 500 is rewritten.
90
+ //
91
+ // A hinted error is exempt, and the exemption is narrow by construction
92
+ // rather than by trust: what it serves is the hint's own `detail`, a
93
+ // fixed sentence the brand site wrote, never the `Error`'s message. So
94
+ // there is no driver diagnostic in the body for masking to remove. It is
95
+ // checked FIRST because a hinted 501 satisfies all three masking clauses
96
+ // and would otherwise be rewritten into the exact symptom this closes.
97
+ let responseError = error;
98
+ if (hint !== undefined) {
99
+ responseError = buildErrorFromInit(hint);
100
+ }
101
+ else if (maskInternalErrors && !isHttpError && error.statusCode >= 500) {
102
+ // Preserve the log's cause chain without leaking the message. `cause`
103
+ // is `unknown` on `Error`; only an `Error` value is a valid
104
+ // `HttpError` cause, so narrow rather than cast.
105
+ const cause = error.cause instanceof Error ? error.cause : undefined;
106
+ responseError = new HttpError(error.statusCode, statusTitle(error.statusCode), undefined, cause);
107
+ }
108
+ // An `HttpError` carries whatever status its thrower passed: the
109
+ // constructor validates nothing, and its own JSDoc says the factory
110
+ // functions are what "guarantee a correct status code". So
111
+ // `throw new HttpError(4004, ...)` — a typo for `404` — reached
112
+ // `response.status()` and threw `RangeError` out of the one `catch` that
113
+ // exists to contain throws. The status is clamped BEFORE the log and the
114
+ // body are built, so the logged status, the body's own status member and
115
+ // the written status are one number. Rebuilding rather than clamping at
116
+ // the write also makes `responseError === error` false, which suppresses
117
+ // the stack — correct by that check's own stated invariant, since the
118
+ // error being SERVED is no longer the error that was LOGGED.
119
+ const servedStatus = resolveResponseStatus(responseError.statusCode, ctx);
120
+ if (servedStatus !== responseError.statusCode) {
121
+ responseError = new HttpError(servedStatus, responseError.message, responseError.details, responseError.cause instanceof Error ? responseError.cause : undefined);
122
+ }
123
+ // The hook sees the same safe error and served status that the formatter
124
+ // would receive. It can perform asynchronous view rendering; only
125
+ // `undefined` means "use the framework formatter". A hook failure stays
126
+ // an application failure, but records the original caught diagnostic
127
+ // before it propagates to the surrounding pipeline.
128
+ let result;
129
+ try {
130
+ result = await respond?.(responseError, ctx);
131
+ }
132
+ catch (hookError) {
133
+ if (logErrors) {
134
+ logError(ctx, error, responseError.statusCode);
135
+ }
136
+ throw hookError;
137
+ }
138
+ if (result !== undefined) {
139
+ // The application-owned result can choose a status different from the
140
+ // normalized error (for example a 418 HTML page for a caught 500), so
141
+ // inspect the completed writer rather than reporting the fallback.
142
+ if (logErrors) {
143
+ logError(ctx, error, ctx.response.snapshot().status);
144
+ }
145
+ return result;
146
+ }
147
+ // Log the UNMASKED error, so the log keeps the SQL, the bound parameters
148
+ // and the cause chain regardless of masking — and report the status the
149
+ // formatter below will actually serve. A hint may change a normalized
150
+ // `500` to `501`, so logging the pre-hint status would mislead operators.
151
+ if (logErrors) {
152
+ logError(ctx, error, responseError.statusCode);
153
+ }
154
+ const body = formatter(responseError, ctx);
155
+ // Masking wins over `includeStackTrace`. A stack's first line is
156
+ // `<name>: <message>`, so attaching the unmasked error's stack would put
157
+ // the SQL and its bound parameter values straight back into the body that
158
+ // was just masked — defeating the mask through the one option documented
159
+ // as unsafe in production. A masked error therefore carries no stack at
160
+ // all; the unmasked one is in the log, where `logErrors` already sent it.
161
+ //
162
+ // The condition is `responseError === error` rather than `!masked`
163
+ // because a hinted response is the second case with the same property:
164
+ // its body deliberately carries the brand site's sentence and not the
165
+ // error's own text, and a stack's first line is `<name>: <message>`. So
166
+ // the stack is attached exactly when the error being SERVED is the error
167
+ // that was LOGGED, which is the invariant both exclusions want.
168
+ if (includeStackTrace && responseError === error && error.stack !== undefined) {
169
+ body.stack = error.stack;
170
+ }
171
+ // Serialize and send via `.send(bytes)` rather than `.json(body)` so
172
+ // the content-type header we set is not overwritten by json()'s own
173
+ // `application/json` default — RFC 9457 requires `application/problem+json`.
174
+ const bytes = new TextEncoder().encode(JSON.stringify(body));
175
+ return ctx.response
176
+ .status(responseError.statusCode)
177
+ .header('content-type', contentType)
178
+ .send(bytes);
179
+ }
180
+ };
181
+ // Brand the middleware with the SAME responder it publishes into `ctx.state`
182
+ // below, so the kernel can seed it into the state of the pre-pipeline sites
183
+ // — the drain `503`, the malformed-request `400`, and the request-lifecycle
184
+ // hooks — which run before this middleware and would otherwise always take
185
+ // the fallback shape (M70f re-review, findings 1 & 2).
186
+ brandErrorResponder(handleError, responder);
187
+ return handleError;
188
+ }
189
+ /**
190
+ * Logs an error via the registered `ILogger`, if one is available. Silently
191
+ * does nothing when no logger capability is registered.
192
+ *
193
+ * @param ctx - The request context
194
+ * @param error - The error to log, carrying the unmasked message and cause
195
+ * @param statusCode - The status actually served, which a hint may have changed
196
+ */
197
+ function logError(ctx, error, statusCode) {
198
+ if (!ctx.services.has(CAPABILITIES.LOGGER)) {
199
+ return;
200
+ }
201
+ const logger = ctx.services.get(CAPABILITIES.LOGGER);
202
+ // Serialize the cause (X2-5): a raw `Error` in log metadata renders as `{}`
203
+ // under `JSON.stringify` because `message`/`stack` are non-enumerable.
204
+ logger.error(error.message, {
205
+ statusCode,
206
+ requestId: ctx.id,
207
+ ...(error.cause !== undefined && { cause: serializeError(error.cause) }),
208
+ });
209
+ }
210
+ //# sourceMappingURL=error-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The `exceptions` implementation of the request-scoped error responder seam
3
+ * (M70f).
4
+ *
5
+ * This module is **internal**: it is not exported from `src/index.ts`. The
6
+ * interface it satisfies (`IErrorResponder`), the state key, the init type, and
7
+ * the `respondWithError` free function all live in `@setu-ts/common`; exporting
8
+ * the implementation as well would give one concept two public names. A
9
+ * `barrel-exports.test.ts` assertion pins that it stays out of the barrel.
10
+ *
11
+ * @module
12
+ */
13
+ import type { ErrorResponseInit, IErrorResponder } from '@setu-ts/common';
14
+ import { HttpError } from '../errors/http-error.ts';
15
+ import type { ErrorHandlerFormatter } from '../formatters/error-formatter.ts';
16
+ /**
17
+ * Builds the {@linkcode HttpError} that carries one error response's status,
18
+ * title and disclosure.
19
+ *
20
+ * This is the single owner of that mapping, and it has **two** callers: the
21
+ * responder below, and `errorHandler`'s status-hint path (M89b), which answers
22
+ * a thrown error branded with an `HttpStatusHint` — a type that IS an
23
+ * `ErrorResponseInit`. Sharing it is what makes the two agree: a hinted throw
24
+ * and a `respondWithError` call carrying the same values produce byte-identical
25
+ * bodies under `default`, `rfc9457`, `rfc7807` and any custom formatter. A
26
+ * second hand-rolled construction would silently diverge — the `default`
27
+ * formatter reads `details.detail` while the Problem Details formatters read
28
+ * the {@linkcode RESPONDER_DETAIL} symbol, so getting either half wrong loses
29
+ * the disclosure in exactly one format.
30
+ *
31
+ * @param init - The status, title, disclosure and optional structured details
32
+ * @returns An `HttpError` every formatter renders with the disclosure intact
33
+ */
34
+ export declare function buildErrorFromInit(init: ErrorResponseInit): HttpError;
35
+ /**
36
+ * Builds an {@linkcode IErrorResponder} from the formatter and content type
37
+ * that `errorHandler` has **already** resolved at factory time.
38
+ *
39
+ * The responder keeps the `HttpError` construction inside the only package that
40
+ * owns it: it builds an `HttpError` from the init (so `buildProblemDetails` sees
41
+ * a real `statusCode` and the validation `errors` extension, and
42
+ * `maskInternalErrors` would never mask a deliberately 4xx), runs the resolved
43
+ * formatter over it, and writes status, `content-type` and the serialized body
44
+ * — the same three-step tail `errorHandler`'s catch path performs.
45
+ *
46
+ * @param formatter - The resolved error formatter
47
+ * @param contentType - The resolved response content type
48
+ * @returns A responder publishing the application's error format
49
+ * @since 0.1.0
50
+ */
51
+ export declare function createErrorResponder(formatter: ErrorHandlerFormatter, contentType: string): IErrorResponder;
52
+ //# sourceMappingURL=error-responder-impl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-responder-impl.d.ts","sourceRoot":"","sources":["../../src/middleware/error-responder-impl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAEV,iBAAiB,EACjB,eAAe,EAEhB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAqC9E;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,SAAS,CAkCrE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,qBAAqB,EAChC,WAAW,EAAE,MAAM,GAClB,eAAe,CA6CjB"}
@@ -0,0 +1,148 @@
1
+ import { resolveResponseStatus } from '@setu-ts/common';
2
+ import { HttpError } from "../errors/http-error.js";
3
+ import { RESPONDER_DETAIL } from "../formatters/problem-details.js";
4
+ /**
5
+ * The `application/problem+json` content type for Problem Details responses.
6
+ *
7
+ * Mirrors the constant `errorHandler` uses to decide the media type: a
8
+ * formatter answers Problem Details (and so derives `instance` from the
9
+ * request path) exactly when its resolved content type is this one.
10
+ */
11
+ const PROBLEM_JSON = 'application/problem+json';
12
+ /**
13
+ * Narrows an {@linkcode ErrorResponderTarget} to a full {@linkcode
14
+ * IRequestContext} when it genuinely is one.
15
+ *
16
+ * The responder is reached two ways. In-pipeline sites — the kernel's terminals
17
+ * and every short-circuiting middleware — pass the live `IRequestContext`,
18
+ * which carries `id`, `services`, and `startTime`. The kernel's PRE-pipeline
19
+ * sites — the shutdown-drain `503` and the malformed-request `400` — pass a
20
+ * bare target of just `state`, `response`, and an optional safe `request`
21
+ * path, because no request context exists yet. The three members checked here
22
+ * are present on every real context and absent from every bare target, so the
23
+ * test distinguishes the two without a lie.
24
+ *
25
+ * @param target - The responder target to test
26
+ * @returns `true` when the target is a full request context
27
+ */
28
+ function isFullRequestContext(target) {
29
+ const ctx = target;
30
+ return (typeof ctx.id === 'string' &&
31
+ typeof ctx.services === 'object' &&
32
+ ctx.services !== null &&
33
+ typeof ctx.startTime === 'number');
34
+ }
35
+ /**
36
+ * Builds the {@linkcode HttpError} that carries one error response's status,
37
+ * title and disclosure.
38
+ *
39
+ * This is the single owner of that mapping, and it has **two** callers: the
40
+ * responder below, and `errorHandler`'s status-hint path (M89b), which answers
41
+ * a thrown error branded with an `HttpStatusHint` — a type that IS an
42
+ * `ErrorResponseInit`. Sharing it is what makes the two agree: a hinted throw
43
+ * and a `respondWithError` call carrying the same values produce byte-identical
44
+ * bodies under `default`, `rfc9457`, `rfc7807` and any custom formatter. A
45
+ * second hand-rolled construction would silently diverge — the `default`
46
+ * formatter reads `details.detail` while the Problem Details formatters read
47
+ * the {@linkcode RESPONDER_DETAIL} symbol, so getting either half wrong loses
48
+ * the disclosure in exactly one format.
49
+ *
50
+ * @param init - The status, title, disclosure and optional structured details
51
+ * @returns An `HttpError` every formatter renders with the disclosure intact
52
+ */
53
+ export function buildErrorFromInit(init) {
54
+ // The title is the framework-default `error` member — the `message` of the
55
+ // `default` format and the `detail` fallback of the Problem Details
56
+ // formatters — so it stays `error.message`. The disclosure (`init.detail`)
57
+ // is kept verbatim by every format (the `ErrorResponseInit` contract in
58
+ // `@setu-ts/common`): it is carried in the error's structured `details`
59
+ // under a `detail` key, so the `default` format emits it as `details.detail`
60
+ // beside `message`, and `buildProblemDetails` promotes it to the Problem
61
+ // Details `detail` member (falling back to the title when a site supplies no
62
+ // disclosure). `init.details` (e.g. a validation `errors` array) is merged
63
+ // through unchanged. Carrying it this way — rather than in `error.message` —
64
+ // keeps the title verbatim, which a site's (non-)disclosure decision depends
65
+ // on.
66
+ const details = init.detail !== undefined
67
+ ? { ...init.details, detail: init.detail }
68
+ : init.details;
69
+ const error = new HttpError(init.status, init.title, details);
70
+ // Mark the error as responder-built and carry the disclosure on a
71
+ // module-private symbol, which is what `buildProblemDetails` promotes to the
72
+ // Problem Details `detail` member. The `details.detail` key above is what the
73
+ // `default` formatter emits (it serializes `details` verbatim); the Problem
74
+ // Details formatters read the symbol instead, so a THROWN error that happens
75
+ // to carry a `details.detail` key of its own keeps `detail: <message>` (M70f
76
+ // code review, finding 4). Non-enumerable so it never reaches a serialized
77
+ // body.
78
+ if (init.detail !== undefined) {
79
+ Object.defineProperty(error, RESPONDER_DETAIL, {
80
+ value: init.detail,
81
+ enumerable: false,
82
+ writable: false,
83
+ configurable: true,
84
+ });
85
+ }
86
+ return error;
87
+ }
88
+ /**
89
+ * Builds an {@linkcode IErrorResponder} from the formatter and content type
90
+ * that `errorHandler` has **already** resolved at factory time.
91
+ *
92
+ * The responder keeps the `HttpError` construction inside the only package that
93
+ * owns it: it builds an `HttpError` from the init (so `buildProblemDetails` sees
94
+ * a real `statusCode` and the validation `errors` extension, and
95
+ * `maskInternalErrors` would never mask a deliberately 4xx), runs the resolved
96
+ * formatter over it, and writes status, `content-type` and the serialized body
97
+ * — the same three-step tail `errorHandler`'s catch path performs.
98
+ *
99
+ * @param formatter - The resolved error formatter
100
+ * @param contentType - The resolved response content type
101
+ * @returns A responder publishing the application's error format
102
+ * @since 0.1.0
103
+ */
104
+ export function createErrorResponder(formatter, contentType) {
105
+ return {
106
+ respond(target, init) {
107
+ // Sanitized before the error is built, not just before the status is
108
+ // written, so the formatted body's `status` member agrees with the status
109
+ // actually written. Idempotent, so the common path through
110
+ // `respondWithError` — which already sanitized — reports nothing twice.
111
+ const status = resolveResponseStatus(init.status, target);
112
+ const error = buildErrorFromInit(status === init.status ? init : { ...init, status });
113
+ // The formatter's `ctx` parameter is a FULL `IRequestContext` by its
114
+ // contract (M70f re-review round 2, finding 2). The target is a full
115
+ // context only at the in-pipeline sites; the kernel's pre-pipeline sites
116
+ // hand over a bare target whose `services`, `id`, `params`, `query`,
117
+ // `signal` and `startTime` do not exist. Passing that partial object
118
+ // would let a custom formatter read a documented member and throw
119
+ // `TypeError: Cannot read properties of undefined` — replacing the
120
+ // configured 503/400 with an unhandled exception. So the formatter
121
+ // receives the context only when it genuinely is one, and `undefined`
122
+ // otherwise — the value its optional `ctx` parameter already declares.
123
+ const fullContext = isFullRequestContext(target) ? target : undefined;
124
+ let body = formatter(error, fullContext);
125
+ // The Problem Details `instance` member comes from the request path. At
126
+ // the in-pipeline sites the formatter read it off the full context; at
127
+ // the pre-pipeline sites there is no context, so the responder supplies
128
+ // the safe path the kernel captured separately — a separate internal
129
+ // mechanism, since the formatter's optional `ctx` parameter is the only
130
+ // public channel and it must stay honest (M70f re-review round 2,
131
+ // finding 2). A malformed request carries no path, in which case the
132
+ // member is simply absent. A fresh object is composed rather than
133
+ // mutating the formatter's return value, which a custom formatter may
134
+ // share or freeze.
135
+ if (fullContext === undefined &&
136
+ contentType === PROBLEM_JSON &&
137
+ target.request !== undefined) {
138
+ body = { ...body, instance: target.request.path };
139
+ }
140
+ const bytes = new TextEncoder().encode(JSON.stringify(body));
141
+ target.response
142
+ .status(status)
143
+ .header('content-type', contentType)
144
+ .send(bytes);
145
+ },
146
+ };
147
+ }
148
+ //# sourceMappingURL=error-responder-impl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-responder-impl.js","sourceRoot":"","sources":["../../src/middleware/error-responder-impl.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,14 +1,55 @@
1
1
  {
2
2
  "name": "@setu-ts/exceptions",
3
- "version": "0.0.0-bootstrap.0",
3
+ "version": "0.9.0",
4
4
  "description": "Exception hierarchy, RFC 9457 Problem Details, and the error-handler middleware",
5
- "license": "MIT",
6
5
  "repository": {
7
6
  "type": "git",
8
7
  "url": "git+https://github.com/setu-ts/setu-ts.git",
9
8
  "directory": "packages/exceptions"
10
9
  },
10
+ "homepage": "https://github.com/setu-ts/setu-ts/tree/main/packages/exceptions",
11
+ "bugs": {
12
+ "url": "https://github.com/setu-ts/setu-ts/issues"
13
+ },
11
14
  "publishConfig": {
12
15
  "access": "public"
16
+ },
17
+ "sideEffects": false,
18
+ "type": "module",
19
+ "license": "MIT",
20
+ "engines": {
21
+ "node": ">=22"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "src",
26
+ "README.md",
27
+ "LICENSE"
28
+ ],
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "default": "./dist/index.js"
33
+ }
34
+ },
35
+ "dependencies": {
36
+ "@setu-ts/common": "^0.9.0"
37
+ },
38
+ "devDependencies": {
39
+ "@setu-ts/auth-plugin": "^0.9.0",
40
+ "@setu-ts/feature-flags-plugin": "^0.9.0",
41
+ "@setu-ts/http-security-plugin": "^0.9.0",
42
+ "@setu-ts/logger-plugin": "^0.9.0",
43
+ "@setu-ts/multi-tenancy-plugin": "^0.9.0",
44
+ "@setu-ts/runtime": "^0.9.0",
45
+ "@setu-ts/session-plugin": "^0.9.0",
46
+ "@setu-ts/storage-plugin": "^0.9.0",
47
+ "@setu-ts/testing": "^0.9.0",
48
+ "@types/node": "^24",
49
+ "typescript": "6.0.3",
50
+ "vitest": "5.0.3"
51
+ },
52
+ "scripts": {
53
+ "build": "tsc -b"
13
54
  }
14
- }
55
+ }