@raiyyan/brisk 0.0.0-stage → 0.1.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 (140) hide show
  1. package/AGENTS.md +467 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1417 -2
  4. package/dist/bin/brisk.d.ts +3 -0
  5. package/dist/bin/brisk.d.ts.map +1 -0
  6. package/dist/bin/brisk.js +13 -0
  7. package/dist/bin/brisk.js.map +1 -0
  8. package/dist/src/cli/agents.d.ts +7 -0
  9. package/dist/src/cli/agents.d.ts.map +1 -0
  10. package/dist/src/cli/agents.js +55 -0
  11. package/dist/src/cli/agents.js.map +1 -0
  12. package/dist/src/cli/cli.d.ts +5 -0
  13. package/dist/src/cli/cli.d.ts.map +1 -0
  14. package/dist/src/cli/cli.js +127 -0
  15. package/dist/src/cli/cli.js.map +1 -0
  16. package/dist/src/cli/client.d.ts +12 -0
  17. package/dist/src/cli/client.d.ts.map +1 -0
  18. package/dist/src/cli/client.js +76 -0
  19. package/dist/src/cli/client.js.map +1 -0
  20. package/dist/src/cli/generate.d.ts +9 -0
  21. package/dist/src/cli/generate.d.ts.map +1 -0
  22. package/dist/src/cli/generate.js +39 -0
  23. package/dist/src/cli/generate.js.map +1 -0
  24. package/dist/src/cli/init.d.ts +19 -0
  25. package/dist/src/cli/init.d.ts.map +1 -0
  26. package/dist/src/cli/init.js +125 -0
  27. package/dist/src/cli/init.js.map +1 -0
  28. package/dist/src/cli/names.d.ts +14 -0
  29. package/dist/src/cli/names.d.ts.map +1 -0
  30. package/dist/src/cli/names.js +25 -0
  31. package/dist/src/cli/names.js.map +1 -0
  32. package/dist/src/cli/system.d.ts +35 -0
  33. package/dist/src/cli/system.d.ts.map +1 -0
  34. package/dist/src/cli/system.js +83 -0
  35. package/dist/src/cli/system.js.map +1 -0
  36. package/dist/src/cli/templates/auth.d.ts +4 -0
  37. package/dist/src/cli/templates/auth.d.ts.map +1 -0
  38. package/dist/src/cli/templates/auth.js +90 -0
  39. package/dist/src/cli/templates/auth.js.map +1 -0
  40. package/dist/src/cli/templates/database.d.ts +4 -0
  41. package/dist/src/cli/templates/database.d.ts.map +1 -0
  42. package/dist/src/cli/templates/database.js +107 -0
  43. package/dist/src/cli/templates/database.js.map +1 -0
  44. package/dist/src/cli/templates/docker.d.ts +4 -0
  45. package/dist/src/cli/templates/docker.d.ts.map +1 -0
  46. package/dist/src/cli/templates/docker.js +102 -0
  47. package/dist/src/cli/templates/docker.js.map +1 -0
  48. package/dist/src/cli/templates/feature.d.ts +8 -0
  49. package/dist/src/cli/templates/feature.d.ts.map +1 -0
  50. package/dist/src/cli/templates/feature.js +145 -0
  51. package/dist/src/cli/templates/feature.js.map +1 -0
  52. package/dist/src/cli/templates/project.d.ts +24 -0
  53. package/dist/src/cli/templates/project.d.ts.map +1 -0
  54. package/dist/src/cli/templates/project.js +166 -0
  55. package/dist/src/cli/templates/project.js.map +1 -0
  56. package/dist/src/client/client.d.ts +64 -0
  57. package/dist/src/client/client.d.ts.map +1 -0
  58. package/dist/src/client/client.js +143 -0
  59. package/dist/src/client/client.js.map +1 -0
  60. package/dist/src/client/index.d.ts +8 -0
  61. package/dist/src/client/index.d.ts.map +1 -0
  62. package/dist/src/client/index.js +7 -0
  63. package/dist/src/client/index.js.map +1 -0
  64. package/dist/src/config/env.d.ts +28 -0
  65. package/dist/src/config/env.d.ts.map +1 -0
  66. package/dist/src/config/env.js +35 -0
  67. package/dist/src/config/env.js.map +1 -0
  68. package/dist/src/config/logger.d.ts +32 -0
  69. package/dist/src/config/logger.d.ts.map +1 -0
  70. package/dist/src/config/logger.js +31 -0
  71. package/dist/src/config/logger.js.map +1 -0
  72. package/dist/src/di/container.d.ts +186 -0
  73. package/dist/src/di/container.d.ts.map +1 -0
  74. package/dist/src/di/container.js +208 -0
  75. package/dist/src/di/container.js.map +1 -0
  76. package/dist/src/di/index.d.ts +8 -0
  77. package/dist/src/di/index.d.ts.map +1 -0
  78. package/dist/src/di/index.js +8 -0
  79. package/dist/src/di/index.js.map +1 -0
  80. package/dist/src/errors/http-errors.d.ts +295 -0
  81. package/dist/src/errors/http-errors.d.ts.map +1 -0
  82. package/dist/src/errors/http-errors.js +311 -0
  83. package/dist/src/errors/http-errors.js.map +1 -0
  84. package/dist/src/errors/index.d.ts +8 -0
  85. package/dist/src/errors/index.d.ts.map +1 -0
  86. package/dist/src/errors/index.js +8 -0
  87. package/dist/src/errors/index.js.map +1 -0
  88. package/dist/src/http/app.d.ts +200 -0
  89. package/dist/src/http/app.d.ts.map +1 -0
  90. package/dist/src/http/app.js +45 -0
  91. package/dist/src/http/app.js.map +1 -0
  92. package/dist/src/http/context.d.ts +66 -0
  93. package/dist/src/http/context.d.ts.map +1 -0
  94. package/dist/src/http/context.js +96 -0
  95. package/dist/src/http/context.js.map +1 -0
  96. package/dist/src/http/controller.d.ts +178 -0
  97. package/dist/src/http/controller.d.ts.map +1 -0
  98. package/dist/src/http/controller.js +317 -0
  99. package/dist/src/http/controller.js.map +1 -0
  100. package/dist/src/http/error-handler.d.ts +59 -0
  101. package/dist/src/http/error-handler.d.ts.map +1 -0
  102. package/dist/src/http/error-handler.js +124 -0
  103. package/dist/src/http/error-handler.js.map +1 -0
  104. package/dist/src/http/instance.d.ts +14 -0
  105. package/dist/src/http/instance.d.ts.map +1 -0
  106. package/dist/src/http/instance.js +309 -0
  107. package/dist/src/http/instance.js.map +1 -0
  108. package/dist/src/http/middleware.d.ts +54 -0
  109. package/dist/src/http/middleware.d.ts.map +1 -0
  110. package/dist/src/http/middleware.js +101 -0
  111. package/dist/src/http/middleware.js.map +1 -0
  112. package/dist/src/http/openapi.d.ts +34 -0
  113. package/dist/src/http/openapi.d.ts.map +1 -0
  114. package/dist/src/http/openapi.js +123 -0
  115. package/dist/src/http/openapi.js.map +1 -0
  116. package/dist/src/http/path-params.d.ts +24 -0
  117. package/dist/src/http/path-params.d.ts.map +1 -0
  118. package/dist/src/http/path-params.js +2 -0
  119. package/dist/src/http/path-params.js.map +1 -0
  120. package/dist/src/http/routes.d.ts +119 -0
  121. package/dist/src/http/routes.d.ts.map +1 -0
  122. package/dist/src/http/routes.js +2 -0
  123. package/dist/src/http/routes.js.map +1 -0
  124. package/dist/src/http/server.d.ts +23 -0
  125. package/dist/src/http/server.d.ts.map +1 -0
  126. package/dist/src/http/server.js +22 -0
  127. package/dist/src/http/server.js.map +1 -0
  128. package/dist/src/index.d.ts +22 -0
  129. package/dist/src/index.d.ts.map +1 -0
  130. package/dist/src/index.js +19 -0
  131. package/dist/src/index.js.map +1 -0
  132. package/dist/src/testing/index.d.ts +7 -0
  133. package/dist/src/testing/index.d.ts.map +1 -0
  134. package/dist/src/testing/index.js +7 -0
  135. package/dist/src/testing/index.js.map +1 -0
  136. package/dist/src/testing/test-client.d.ts +65 -0
  137. package/dist/src/testing/test-client.d.ts.map +1 -0
  138. package/dist/src/testing/test-client.js +77 -0
  139. package/dist/src/testing/test-client.js.map +1 -0
  140. package/package.json +88 -3
@@ -0,0 +1,317 @@
1
+ import { Readable } from "node:stream";
2
+ import { pipeline } from "node:stream/promises";
3
+ import { Router } from "express";
4
+ import { z } from "zod";
5
+ import { getContext, HandlerContext } from "./context.js";
6
+ import { MethodNotAllowedError, UnsupportedMediaTypeError, ValidationError } from "../errors/http-errors.js";
7
+ import { applyMiddlewares } from "./middleware.js";
8
+ /**
9
+ * Declares a feature's HTTP routes. `setup` runs once per app instance, inside its container, so it can
10
+ * `inject()` services. It declares routes and returns them by name; the names form the typed client, so
11
+ * `usersController` mounted as `users` gives `api.users.get(...)`.
12
+ *
13
+ * A handler's return value is the response: nothing sends 204, a `Readable` is streamed, a `Buffer` is sent
14
+ * as bytes, and anything else is JSON, parsed through `response` when given. Throw an error class to fail;
15
+ * never call `next()`.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * export const usersController = defineController({ prefix: "/users", use: [authenticate] }, (route) => {
20
+ * const users = inject(UsersService);
21
+ * return {
22
+ * me: route.get("/me", ({ user }) => user),
23
+ * get: route.get("/:id", { params: UserIdParams, response: UserResponse }, ({ params }) => users.get(params.id)),
24
+ * create: route.post("/", { body: CreateUserBody, response: UserResponse }, ({ body }) => users.create(body)),
25
+ * };
26
+ * });
27
+ * ```
28
+ */
29
+ export function defineController(options, setup) {
30
+ return {
31
+ prefix: options.prefix,
32
+ mount: (container, router, basePath) => mount({ container, router, basePath, use: options.use ?? [] }, (builder) =>
33
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- the loose builder implements every typed overload at runtime
34
+ setup(builder)),
35
+ };
36
+ }
37
+ /**
38
+ * Registers a controller's routes inside the container, with full paths on the app's one router: no nested
39
+ * router per controller, so a request pays no prefix trimming or parameter merging on its way to a route.
40
+ */
41
+ function mount({ container, router, basePath, use, }, register) {
42
+ const declared = [];
43
+ const returned = container.run(() => register(createBuilder({ router, container, declared, prefix: basePath, use })));
44
+ const named = new Map();
45
+ const manifest = returned === undefined ? undefined : toManifest(returned, named, basePath, "");
46
+ const missing = manifest ? declared.find((ref) => !named.has(ref)) : undefined;
47
+ if (missing) {
48
+ throw new TypeError(`Controller ${basePath} declares ${missing.method.toUpperCase()} ${missing.path} but does not return it: add it to the object setup returns`);
49
+ }
50
+ const endpoints = declared.map((ref) => ({
51
+ method: ref.method,
52
+ path: ref.path,
53
+ name: named.get(ref),
54
+ schemas: ref.schemas,
55
+ status: ref.status,
56
+ }));
57
+ return { endpoints, manifest };
58
+ }
59
+ /** A route as `route.get(...)` returns it: method and full path for the manifest, schemas for documentation. */
60
+ class RouteRef {
61
+ method;
62
+ path;
63
+ schemas;
64
+ status;
65
+ constructor(method, path, options) {
66
+ this.method = method;
67
+ this.path = path;
68
+ this.status = options.status;
69
+ const schemas = {};
70
+ for (const key of ["params", "query", "body", "headers", "response"]) {
71
+ const schema = options[key];
72
+ if (schema)
73
+ schemas[key] = schema;
74
+ }
75
+ this.schemas = schemas;
76
+ }
77
+ }
78
+ /** Turns the object a setup returned into a manifest, recording every route's dotted name. */
79
+ function toManifest(value, named, where, scope) {
80
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
81
+ throw new TypeError(`Controller ${where} must return an object of named routes, or nothing`);
82
+ }
83
+ const manifest = {};
84
+ for (const [name, entry] of Object.entries(value)) {
85
+ const dotted = scope === "" ? name : `${scope}.${name}`;
86
+ if (entry instanceof RouteRef) {
87
+ named.set(entry, dotted);
88
+ manifest[name] = [entry.method.toUpperCase(), entry.path];
89
+ }
90
+ else {
91
+ manifest[name] = toManifest(entry, named, `${where} (${dotted})`, dotted);
92
+ }
93
+ }
94
+ return manifest;
95
+ }
96
+ /**
97
+ * Answers requests no controller handled on a known path: `OPTIONS` gets 204, other methods 405, both with an
98
+ * `Allow` header that lists what every controller accepts there.
99
+ *
100
+ * The app mounts it twice. Before the controllers, for `OPTIONS` only, because an Express router would
101
+ * otherwise answer `OPTIONS` itself with just its own methods. After them, where only unmatched requests
102
+ * arrive. A request that matches a route never runs it.
103
+ */
104
+ export function methodGuard(routes) {
105
+ const router = Router();
106
+ const allowedByRequest = new WeakMap();
107
+ const allowed = new Map();
108
+ for (const { method, path } of routes) {
109
+ const methods = allowed.get(path) ?? new Set(["OPTIONS"]);
110
+ methods.add(method.toUpperCase());
111
+ if (method === "get")
112
+ methods.add("HEAD");
113
+ allowed.set(path, methods);
114
+ }
115
+ for (const [path, methods] of allowed) {
116
+ router.all(path, (req, _res, next) => {
117
+ const seen = allowedByRequest.get(req);
118
+ if (seen)
119
+ for (const method of methods)
120
+ seen.add(method);
121
+ else
122
+ allowedByRequest.set(req, new Set(methods));
123
+ next();
124
+ });
125
+ }
126
+ router.use((req, res, next) => {
127
+ const methods = allowedByRequest.get(req);
128
+ if (!methods) {
129
+ next();
130
+ return;
131
+ }
132
+ const allow = [...methods].toSorted();
133
+ if (req.method !== "OPTIONS")
134
+ throw new MethodNotAllowedError(allow);
135
+ res.set("allow", allow.join(", ")).status(204).end();
136
+ });
137
+ return router;
138
+ }
139
+ function createBuilder(state) {
140
+ const route = (method) => (path, optionsOrHandler, maybeHandler) => {
141
+ const [options, handler] = isFunction(optionsOrHandler)
142
+ ? [{}, optionsOrHandler]
143
+ : [optionsOrHandler, maybeHandler];
144
+ if (!isOptions(options) || !isFunction(handler)) {
145
+ throw new TypeError(`Invalid route ${method.toUpperCase()} ${path}: use (path, handler) or (path, options, handler)`);
146
+ }
147
+ if (options.status !== undefined && !isSuccessStatus(options.status)) {
148
+ throw new TypeError(`Invalid status ${String(options.status)} on ${method.toUpperCase()} ${path}: use a 2xx or 3xx status; throw an error class for failures`);
149
+ }
150
+ const fullPath = joinPath(state.prefix, path);
151
+ const middlewares = [...state.use, ...(options.use ?? [])];
152
+ const ref = new RouteRef(method, fullPath, options);
153
+ state.declared.push(ref);
154
+ state.router[method](fullPath, (req, res) => state.container.run(handle, req, res, method, middlewares, options, handler));
155
+ return ref;
156
+ };
157
+ return {
158
+ get: route("get"),
159
+ post: route("post"),
160
+ put: route("put"),
161
+ patch: route("patch"),
162
+ delete: route("delete"),
163
+ group(path, optionsOrSetup, maybeSetup) {
164
+ const [options, setup] = isFunction(optionsOrSetup)
165
+ ? [{}, optionsOrSetup]
166
+ : [optionsOrSetup, maybeSetup];
167
+ if (!isOptions(options) || !isFunction(setup)) {
168
+ throw new TypeError(`Invalid group ${path}: use (path, setup) or (path, options, setup)`);
169
+ }
170
+ return setup(createBuilder({
171
+ ...state,
172
+ prefix: joinPath(state.prefix, path),
173
+ use: [...state.use, ...(options.use ?? [])],
174
+ }));
175
+ },
176
+ };
177
+ }
178
+ /**
179
+ * One request: middlewares, input validation, handler, then the response the handler's result calls for.
180
+ * Awaits only what is really a promise, so synchronous middlewares and handlers stay synchronous.
181
+ */
182
+ async function handle(req, res, method, middlewares, options, handler) {
183
+ const base = getContext(req);
184
+ const ctx = new HandlerContext(base);
185
+ if (middlewares.length > 0) {
186
+ const pending = applyMiddlewares(base, ctx, middlewares);
187
+ if (pending)
188
+ await pending;
189
+ }
190
+ if (options.body && req.body === undefined && hasBody(req) && req.get("content-type") !== undefined) {
191
+ throw new UnsupportedMediaTypeError(`No parser accepted a ${req.get("content-type")} body`);
192
+ }
193
+ const parsing = parseInto(ctx, req, options);
194
+ if (parsing)
195
+ await parsing;
196
+ const returned = handler(ctx);
197
+ const result = returned instanceof Promise ? await returned : returned;
198
+ if (res.headersSent)
199
+ return;
200
+ const chosen = options.status ?? (res.statusCode === 200 ? undefined : res.statusCode);
201
+ if (result === undefined) {
202
+ res.status(chosen ?? 204).end();
203
+ return;
204
+ }
205
+ const status = chosen ?? (method === "post" ? 201 : 200);
206
+ if (result instanceof Readable) {
207
+ res.status(status);
208
+ await stream(result, res);
209
+ return;
210
+ }
211
+ if (result instanceof Uint8Array) {
212
+ res.status(status).send(Buffer.from(result.buffer, result.byteOffset, result.byteLength));
213
+ return;
214
+ }
215
+ const output = serializeOutput(options.response, result);
216
+ res.status(status).json(output instanceof Promise ? await output : output);
217
+ }
218
+ /** Whether the request carries a body at all, as body parsers decide it; a bodiless POST is not a 415. */
219
+ function hasBody(req) {
220
+ return req.get("transfer-encoding") !== undefined || req.get("content-length") !== undefined;
221
+ }
222
+ /** Pipes with backpressure. A client that disconnects mid-stream is normal, not a server error. */
223
+ async function stream(source, res) {
224
+ try {
225
+ await pipeline(source, res);
226
+ }
227
+ catch (error) {
228
+ if (!(error instanceof Error && "code" in error && error.code === "ERR_STREAM_PREMATURE_CLOSE"))
229
+ throw error;
230
+ }
231
+ }
232
+ /**
233
+ * Validates every input location onto the handler's context. Synchronous, which is the fast path; only when a
234
+ * schema has async refinements does validation switch to `safeParseAsync` and return a promise.
235
+ */
236
+ function parseInto(ctx, req, options) {
237
+ const params = parseInput(options.params, req.params, "params");
238
+ const query = parseInput(options.query, req.query, "query");
239
+ const body = parseInput(options.body, req.body, "body");
240
+ const headers = parseInput(options.headers, req.headers, "headers");
241
+ if (params instanceof Pending ||
242
+ query instanceof Pending ||
243
+ body instanceof Pending ||
244
+ headers instanceof Pending) {
245
+ return settleInto(ctx, params, query, body, headers);
246
+ }
247
+ ctx.params = params;
248
+ ctx.query = query;
249
+ ctx.body = body;
250
+ ctx.headers = headers;
251
+ return undefined;
252
+ }
253
+ async function settleInto(ctx, ...inputs) {
254
+ const [params, query, body, headers] = await Promise.all(inputs.map(settle));
255
+ ctx.params = params;
256
+ ctx.query = query;
257
+ ctx.body = body;
258
+ ctx.headers = headers;
259
+ }
260
+ /** An input whose schema needs async validation; never a value a schema can produce. */
261
+ class Pending {
262
+ promise;
263
+ constructor(promise) {
264
+ this.promise = promise;
265
+ }
266
+ }
267
+ const settle = (value) => (value instanceof Pending ? value.promise : value);
268
+ function parseInput(schema, value, location) {
269
+ if (!schema)
270
+ return value;
271
+ const input = value ?? {};
272
+ const result = safeParse(schema, input);
273
+ if (result instanceof Promise)
274
+ return new Pending(result.then((settled) => unwrapInput(settled, location)));
275
+ return unwrapInput(result, location);
276
+ }
277
+ function unwrapInput(result, location) {
278
+ if (result.success)
279
+ return result.data;
280
+ throw ValidationError.fromZod(result.error, location);
281
+ }
282
+ /** A result that breaks its own response schema is a server bug: a 500, never a 400. */
283
+ function serializeOutput(schema, value) {
284
+ if (!schema)
285
+ return value;
286
+ const result = safeParse(schema, value);
287
+ return result instanceof Promise ? result.then(unwrapOutput) : unwrapOutput(result);
288
+ }
289
+ function unwrapOutput(result) {
290
+ if (result.success)
291
+ return result.data;
292
+ throw new Error(`Response does not match its schema:\n${z.prettifyError(result.error)}`);
293
+ }
294
+ function safeParse(schema, value) {
295
+ try {
296
+ return schema.safeParse(value);
297
+ }
298
+ catch (error) {
299
+ if (error instanceof z.core.$ZodAsyncError)
300
+ return schema.safeParseAsync(value);
301
+ throw error;
302
+ }
303
+ }
304
+ function isSuccessStatus(status) {
305
+ return Number.isInteger(status) && status >= 200 && status <= 399;
306
+ }
307
+ function isFunction(value) {
308
+ return typeof value === "function";
309
+ }
310
+ function isOptions(value) {
311
+ return typeof value === "object" && value !== null;
312
+ }
313
+ /** Joins URL segments into one normalized path: `("/api", "/users/", ":id")` gives `/api/users/:id`. */
314
+ export function joinPath(...parts) {
315
+ return `/${parts.join("/").split("/").filter(Boolean).join("/")}`;
316
+ }
317
+ //# sourceMappingURL=controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.js","sourceRoot":"","sources":["../../../src/http/controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import type { ErrorRequestHandler } from "express";
2
+ import { type RequestContext } from "./context.ts";
3
+ import { HttpError } from "../errors/http-errors.ts";
4
+ /**
5
+ * Turns an error from a library (a database, an SDK) into an HTTP error, or returns `undefined` to leave it.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const uniqueViolation: ErrorMapper = (error) =>
10
+ * error instanceof DatabaseError && error.code === "23505" ? new ConflictError("Already exists") : undefined;
11
+ *
12
+ * defineApp({ controllers, errors: { mappers: [uniqueViolation] } });
13
+ * ```
14
+ */
15
+ export type ErrorMapper = (error: unknown) => HttpError | undefined;
16
+ /**
17
+ * Builds the body of an error response. The default builds an {@link ErrorResponse}.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const problemJson: ErrorFormatter = (error, ctx) => ({
22
+ * type: `https://errors.example.com/${error.code}`,
23
+ * title: error.message,
24
+ * status: error.status,
25
+ * instance: ctx.req.originalUrl,
26
+ * });
27
+ *
28
+ * defineApp({ controllers, errors: { format: problemJson } });
29
+ * ```
30
+ */
31
+ export type ErrorFormatter = (error: HttpError, ctx: RequestContext) => unknown;
32
+ export interface ErrorHandlingOptions {
33
+ mappers: readonly ErrorMapper[];
34
+ format: ErrorFormatter;
35
+ exposeInternalErrors: boolean;
36
+ onError: ((error: unknown, ctx: RequestContext) => void) | undefined;
37
+ }
38
+ /**
39
+ * The default error body: `{ error: { code, message, details, requestId } }`. Call it from your own formatter
40
+ * to add fields to the default shape.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * const withDocs: ErrorFormatter = (error, ctx) => ({
45
+ * ...defaultErrorFormatter(error, ctx),
46
+ * docs: `https://docs.example.com/errors/${error.code}`,
47
+ * });
48
+ * ```
49
+ */
50
+ export declare const defaultErrorFormatter: ErrorFormatter;
51
+ /**
52
+ * Turns every error into a response: classify it, log it by severity, report it to `onError`, then send the
53
+ * status, headers and body. A mapper, formatter or `onError` hook that throws is logged, never fatal.
54
+ *
55
+ * When the response has already started (a handler wrote part of a stream, then threw), the connection is
56
+ * destroyed instead: ending it normally would hand the client a truncated body that looks complete.
57
+ */
58
+ export declare function errorHandler(options: ErrorHandlingOptions): ErrorRequestHandler;
59
+ //# sourceMappingURL=error-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../../src/http/error-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAEL,SAAS,EAMV,MAAM,0BAA0B,CAAC;AAElC;;;;;;;;;;GAUG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,SAAS,GAAG,SAAS,CAAC;AAEpE;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC;AAEhF,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,MAAM,EAAE,cAAc,CAAC;IACvB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACtE;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAElC,CAAC;AAEH;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CAsB/E"}
@@ -0,0 +1,124 @@
1
+ import { STATUS_CODES } from "node:http";
2
+ import { z } from "zod";
3
+ import { getContext } from "./context.js";
4
+ import { BadRequestError, HttpError, InternalServerError, PayloadTooLargeError, UnsupportedMediaTypeError, ValidationError, } from "../errors/http-errors.js";
5
+ /**
6
+ * The default error body: `{ error: { code, message, details, requestId } }`. Call it from your own formatter
7
+ * to add fields to the default shape.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const withDocs: ErrorFormatter = (error, ctx) => ({
12
+ * ...defaultErrorFormatter(error, ctx),
13
+ * docs: `https://docs.example.com/errors/${error.code}`,
14
+ * });
15
+ * ```
16
+ */
17
+ export const defaultErrorFormatter = (error, ctx) => ({
18
+ error: { code: error.code, message: error.message, details: error.details, requestId: ctx.requestId },
19
+ });
20
+ /**
21
+ * Turns every error into a response: classify it, log it by severity, report it to `onError`, then send the
22
+ * status, headers and body. A mapper, formatter or `onError` hook that throws is logged, never fatal.
23
+ *
24
+ * When the response has already started (a handler wrote part of a stream, then threw), the connection is
25
+ * destroyed instead: ending it normally would hand the client a truncated body that looks complete.
26
+ */
27
+ export function errorHandler(options) {
28
+ return (error, req, res, _next) => {
29
+ const ctx = getContext(req);
30
+ const http = toHttpError(error, options, ctx);
31
+ if (http.status >= 500)
32
+ ctx.log.error({ err: error, code: http.code }, http.message);
33
+ else
34
+ ctx.log.debug({ code: http.code, status: http.status }, http.message);
35
+ try {
36
+ options.onError?.(error, ctx);
37
+ }
38
+ catch (hookError) {
39
+ ctx.log.error({ err: hookError }, "onError hook failed");
40
+ }
41
+ if (res.headersSent) {
42
+ if (!res.writableEnded)
43
+ res.destroy();
44
+ return;
45
+ }
46
+ res
47
+ .status(http.status)
48
+ .set(http.headers)
49
+ .json(formatBody(http, ctx, options.format));
50
+ };
51
+ }
52
+ function formatBody(error, ctx, format) {
53
+ try {
54
+ return format(error, ctx);
55
+ }
56
+ catch (formatError) {
57
+ ctx.log.error({ err: formatError }, "formatError failed, sending the default error body");
58
+ return defaultErrorFormatter(error, ctx);
59
+ }
60
+ }
61
+ function toHttpError(error, options, ctx) {
62
+ if (error instanceof HttpError)
63
+ return error;
64
+ for (const mapper of options.mappers) {
65
+ try {
66
+ const mapped = mapper(error);
67
+ if (mapped)
68
+ return mapped;
69
+ }
70
+ catch (mapperError) {
71
+ ctx.log.error({ err: mapperError }, "error mapper failed");
72
+ }
73
+ }
74
+ if (error instanceof z.ZodError)
75
+ return ValidationError.fromZod(error, "input");
76
+ const parserError = fromBodyParser(error) ?? fromHttpErrors(error);
77
+ if (parserError)
78
+ return parserError;
79
+ const message = options.exposeInternalErrors && error instanceof Error ? error.message : "Internal server error";
80
+ return new InternalServerError(message, { cause: error });
81
+ }
82
+ /** Errors raised while reading the request body, rewritten so parser internals never reach the client. */
83
+ function fromBodyParser(error) {
84
+ if (!(error instanceof Error) || !("type" in error) || typeof error.type !== "string")
85
+ return undefined;
86
+ switch (error.type) {
87
+ case "entity.parse.failed":
88
+ return new BadRequestError("Body must be a valid JSON object or array", {
89
+ code: "INVALID_JSON",
90
+ cause: error,
91
+ });
92
+ case "entity.too.large":
93
+ return new PayloadTooLargeError("Request body is too large", { cause: error });
94
+ case "encoding.unsupported":
95
+ case "charset.unsupported":
96
+ return new UnsupportedMediaTypeError(error.message, { cause: error });
97
+ default:
98
+ return undefined;
99
+ }
100
+ }
101
+ /**
102
+ * Errors from Express middleware that follow the `http-errors` convention: a numeric `status` and a boolean
103
+ * `expose` (body parsers, `express.static`, many community packages). The status is kept, and the message is
104
+ * sent only when `expose` is true.
105
+ *
106
+ * Errors without `expose` are not trusted: an upstream SDK's `{ status: 404 }` means a call to another
107
+ * service failed, which is this server's 500, not the client's 404. The one exception is Express's own
108
+ * `URIError` for an undecodable path parameter, which it marks 400.
109
+ */
110
+ function fromHttpErrors(error) {
111
+ if (error instanceof URIError && "status" in error && error.status === 400) {
112
+ return new BadRequestError("Malformed percent-encoding in the URL", {
113
+ code: "INVALID_URL",
114
+ cause: error,
115
+ });
116
+ }
117
+ if (!(error instanceof Error) || !("status" in error) || !("expose" in error))
118
+ return undefined;
119
+ const { status, expose } = error;
120
+ if (typeof status !== "number" || typeof expose !== "boolean" || status < 400 || status > 599)
121
+ return undefined;
122
+ return new HttpError(status, expose ? error.message : (STATUS_CODES[status] ?? "Error"), { cause: error });
123
+ }
124
+ //# sourceMappingURL=error-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../../src/http/error-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type ProviderEntry } from "../di/container.ts";
2
+ import type { AppInstance, AppOptions, ControllerMap, ListenOptions, StartOptions } from "./app.ts";
3
+ /** An instance with the controls only `defineApp` uses: each instance listens or starts once. */
4
+ export interface Runner<C extends ControllerMap> extends AppInstance<C> {
5
+ listen(port: number, options: Omit<ListenOptions, "providers">): Promise<void>;
6
+ start(options: Omit<StartOptions, "providers">): Promise<void>;
7
+ }
8
+ /**
9
+ * Builds one instance of an app: security headers, request ids and logging, your Express setup, JSON parsing,
10
+ * health check and OpenAPI routes, controllers, 404 and error handling, and the container that runs every
11
+ * lifecycle hook. `overrides` come after the app's own providers, so they win.
12
+ */
13
+ export declare function createInstance<C extends ControllerMap>(options: AppOptions<C>, overrides?: readonly ProviderEntry[]): Runner<C>;
14
+ //# sourceMappingURL=instance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../../src/http/instance.ts"],"names":[],"mappings":"AAKA,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAE5E,OAAO,KAAK,EAEV,WAAW,EACX,UAAU,EACV,aAAa,EAEb,aAAa,EACb,YAAY,EACb,MAAM,UAAU,CAAC;AAmBlB,iGAAiG;AACjG,MAAM,WAAW,MAAM,CAAC,CAAC,SAAS,aAAa,CAAE,SAAQ,WAAW,CAAC,CAAC,CAAC;IACrE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,aAAa,EACpD,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,EACtB,SAAS,GAAE,SAAS,aAAa,EAAO,GACvC,MAAM,CAAC,CAAC,CAAC,CA8MX"}