@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,309 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { createServer } from "node:http";
3
+ import express, { Router } from "express";
4
+ import { createLogger, Logger } from "../config/logger.js";
5
+ import { Container, provide } from "../di/container.js";
6
+ import { NotFoundError } from "../errors/http-errors.js";
7
+ import { createRequestContext, getContext } from "./context.js";
8
+ import { joinPath, methodGuard } from "./controller.js";
9
+ import { defaultErrorFormatter, errorHandler } from "./error-handler.js";
10
+ import { buildOpenApi } from "./openapi.js";
11
+ import { HttpServer } from "./server.js";
12
+ const DEFAULT_SECURITY_HEADERS = {
13
+ "x-content-type-options": "nosniff",
14
+ "x-frame-options": "DENY",
15
+ "referrer-policy": "no-referrer",
16
+ "cross-origin-resource-policy": "same-origin",
17
+ "content-security-policy": "default-src 'none'; frame-ancestors 'none'",
18
+ };
19
+ /** Accepts a caller's `x-request-id` only when it is short and plain, so it cannot forge log lines. */
20
+ const SAFE_REQUEST_ID = /^[\w.:-]{1,128}$/;
21
+ /**
22
+ * Builds one instance of an app: security headers, request ids and logging, your Express setup, JSON parsing,
23
+ * health check and OpenAPI routes, controllers, 404 and error handling, and the container that runs every
24
+ * lifecycle hook. `overrides` come after the app's own providers, so they win.
25
+ */
26
+ export function createInstance(options, overrides = []) {
27
+ const logger = options.logger ?? createLogger();
28
+ const hooks = options.hooks ?? {};
29
+ const http = options.http ?? {};
30
+ const errors = options.errors ?? {};
31
+ const container = new Container([provide(Logger, logger), ...(options.providers ?? []), ...overrides]);
32
+ const app = express();
33
+ const draining = { active: false, responses: new Map() };
34
+ app.disable("x-powered-by");
35
+ app.set("query parser", "simple");
36
+ if (http.trustProxy !== undefined)
37
+ app.set("trust proxy", http.trustProxy);
38
+ app.use(prelude({ logger, hooks, draining, container, headers: securityHeaders(http.securityHeaders) }));
39
+ options.configureExpress?.(app);
40
+ const router = Router();
41
+ const manifest = {};
42
+ const allRoutes = [];
43
+ for (const [name, controller] of Object.entries(options.controllers ?? {})) {
44
+ const mounted = controller.mount(container, router, joinPath(options.prefix ?? "/", controller.prefix));
45
+ for (const endpoint of mounted.endpoints)
46
+ allRoutes.push({ ...endpoint, tag: name });
47
+ if (mounted.manifest)
48
+ manifest[name] = mounted.manifest;
49
+ }
50
+ let document;
51
+ const openapi = () => (document ??= buildOpenApi(allRoutes, options.openapi ?? { title: "API", version: "0.0.0" }));
52
+ const routes = allRoutes.map(({ method, path }) => `${method.toUpperCase()} ${path}`);
53
+ const guard = methodGuard(allRoutes);
54
+ const json = express.json({
55
+ limit: http.bodyLimit ?? "1mb",
56
+ type: ["application/json", "application/*+json"],
57
+ });
58
+ app.use((req, res, next) => {
59
+ if (req.method === "OPTIONS")
60
+ guard(req, res, next);
61
+ else
62
+ json(req, res, next);
63
+ });
64
+ if (hooks.onRequest)
65
+ app.use(onRequest(hooks.onRequest));
66
+ const openapiPath = options.openapi?.path ?? "/openapi.json";
67
+ if (options.openapi && openapiPath !== false) {
68
+ app.get(openapiPath, (_req, res) => {
69
+ res.json(openapi());
70
+ });
71
+ }
72
+ if (http.healthPath !== false) {
73
+ app.get(http.healthPath ?? "/health", (_req, res) => {
74
+ res.json({ status: "ok" });
75
+ });
76
+ }
77
+ app.use(router);
78
+ app.use(guard);
79
+ app.use((req) => {
80
+ throw new NotFoundError(`Cannot ${req.method} ${req.path}`, { code: "ROUTE_NOT_FOUND" });
81
+ });
82
+ app.use(errorHandler({
83
+ mappers: errors.mappers ?? [],
84
+ format: errors.format ?? defaultErrorFormatter,
85
+ exposeInternalErrors: errors.exposeInternal ?? false,
86
+ onError: hooks.onError,
87
+ }));
88
+ let initializing;
89
+ let stopping;
90
+ let server;
91
+ const sockets = new Set();
92
+ const signals = ["SIGINT", "SIGTERM"];
93
+ const shutdownTimeoutMs = http.shutdownTimeoutMs ?? 10_000;
94
+ /** Shuts down and exits; a hook that never settles cannot keep the process alive past the deadline. */
95
+ const exitAfterShutdown = (code) => {
96
+ setTimeout(() => {
97
+ logger.fatal("shutdown did not finish in time, exiting");
98
+ process.exit(1);
99
+ }, shutdownTimeoutMs + 10_000).unref();
100
+ void shutdown().then(() => process.exit(code));
101
+ };
102
+ let firstSignal;
103
+ /**
104
+ * The first signal starts a graceful shutdown. While it runs, the listeners stay installed: tools often send a
105
+ * second, different signal (`node --watch` and some init systems forward SIGTERM right after the terminal's
106
+ * SIGINT), and without a listener Node would kill the process mid-drain. Repeating the same signal (Ctrl+C
107
+ * twice) is a person asking to stop now, so it exits immediately.
108
+ */
109
+ const onSignal = (signal) => {
110
+ if (firstSignal === undefined) {
111
+ firstSignal = signal;
112
+ logger.info({ signal }, "shutting down");
113
+ exitAfterShutdown(0);
114
+ }
115
+ else if (signal === firstSignal) {
116
+ logger.warn({ signal }, "second signal, exiting without waiting");
117
+ process.exit(1);
118
+ }
119
+ else {
120
+ logger.info({ signal }, "already shutting down");
121
+ }
122
+ };
123
+ const onCrash = (reason) => {
124
+ if (stopping) {
125
+ logger.error({ err: reason }, "unhandled error during shutdown");
126
+ return;
127
+ }
128
+ logger.fatal({ err: reason }, "unhandled error, shutting down");
129
+ exitAfterShutdown(1);
130
+ };
131
+ const reportHookError = (hook) => (error, provider) => {
132
+ logger.error({ err: error, provider }, `${hook} failed`);
133
+ };
134
+ const init = () => (initializing ??= container.init());
135
+ const becomeReady = async (manageProcess) => {
136
+ await container.ready();
137
+ if (manageProcess) {
138
+ for (const signal of signals)
139
+ process.on(signal, onSignal);
140
+ process.on("unhandledRejection", onCrash);
141
+ process.on("uncaughtException", onCrash);
142
+ }
143
+ };
144
+ /** Boots, and shuts down whatever already started when booting fails. */
145
+ const boot = async (run) => {
146
+ try {
147
+ return await run();
148
+ }
149
+ catch (error) {
150
+ await shutdown();
151
+ throw error;
152
+ }
153
+ };
154
+ const start = ({ manageProcess = true }) => boot(async () => {
155
+ await init();
156
+ await becomeReady(manageProcess);
157
+ logger.info("started without an HTTP server");
158
+ });
159
+ const listen = (port, listenOptions) => boot(() => serve(port, listenOptions));
160
+ const serve = async (port, { host, server: custom, manageProcess = true }) => {
161
+ await init();
162
+ const target = custom ?? createServer();
163
+ target.on("request", app);
164
+ target.on("connection", (socket) => {
165
+ sockets.add(socket);
166
+ draining.responses.set(socket, undefined);
167
+ socket.once("close", () => {
168
+ sockets.delete(socket);
169
+ draining.responses.delete(socket);
170
+ });
171
+ });
172
+ await new Promise((resolve, reject) => {
173
+ target.once("error", reject);
174
+ const onListening = () => {
175
+ target.off("error", reject);
176
+ resolve();
177
+ };
178
+ if (host === undefined)
179
+ target.listen(port, onListening);
180
+ else
181
+ target.listen(port, host, onListening);
182
+ });
183
+ server = target;
184
+ container.set(HttpServer, target);
185
+ await becomeReady(manageProcess);
186
+ const address = target.address();
187
+ const boundPort = typeof address === "object" && address ? address.port : port;
188
+ logger.info({ port: boundPort, routes }, `listening on http://${host ?? "localhost"}:${boundPort}`);
189
+ };
190
+ const shutdown = () => (stopping ??= (async () => {
191
+ draining.active = true;
192
+ for (const res of draining.responses.values()) {
193
+ if (res && !res.headersSent)
194
+ res.setHeader("connection", "close");
195
+ }
196
+ const closed = server?.listening ? closeServer(server, sockets, shutdownTimeoutMs) : undefined;
197
+ await container.drain(reportHookError("onDrain"));
198
+ await closed;
199
+ await container.shutdown(reportHookError("onShutdown"));
200
+ for (const signal of signals)
201
+ process.off(signal, onSignal);
202
+ process.off("unhandledRejection", onCrash);
203
+ process.off("uncaughtException", onCrash);
204
+ logger.info("shutdown complete");
205
+ })());
206
+ return {
207
+ manifest,
208
+ openapi,
209
+ express: app,
210
+ logger,
211
+ container,
212
+ get server() {
213
+ return server;
214
+ },
215
+ init,
216
+ listen,
217
+ start,
218
+ shutdown,
219
+ };
220
+ }
221
+ /** The security headers to send, computed once; `undefined` when they are turned off. */
222
+ function securityHeaders(option) {
223
+ if (option === false)
224
+ return undefined;
225
+ const merged = { ...DEFAULT_SECURITY_HEADERS, ...(typeof option === "object" ? option : {}) };
226
+ const headers = new Map();
227
+ for (const [name, value] of Object.entries(merged))
228
+ if (value !== false)
229
+ headers.set(name, value);
230
+ return headers;
231
+ }
232
+ const proceed = (next) => next();
233
+ /**
234
+ * Everything brisk does before your Express code, in one layer: security headers, the request id and context
235
+ * (with its lazy logger), the response log and `onResponse`, shutdown's `Connection: close`, and the app's
236
+ * dependency scope, so `inject()` works for the rest of the request, `configureExpress` included.
237
+ *
238
+ * The response is logged on `close`, which also fires for requests the client abandoned (`finish` would not).
239
+ * The listener exists only when something consumes it: the `info` log level or an `onResponse` hook.
240
+ */
241
+ function prelude({ logger, hooks, draining, container, headers }) {
242
+ return (req, res, next) => {
243
+ if (headers)
244
+ res.setHeaders(headers);
245
+ const incoming = req.get("x-request-id");
246
+ const requestId = incoming && SAFE_REQUEST_ID.test(incoming) ? incoming : randomUUID();
247
+ const ctx = createRequestContext(req, res, requestId, logger);
248
+ res.setHeader("x-request-id", requestId);
249
+ if (draining.active)
250
+ res.setHeader("connection", "close");
251
+ else if (draining.responses.has(req.socket))
252
+ draining.responses.set(req.socket, res);
253
+ const log = logger.isLevelEnabled("info");
254
+ if (log || hooks.onResponse) {
255
+ const started = performance.now();
256
+ res.once("close", () => {
257
+ const durationMs = Math.round(performance.now() - started);
258
+ const aborted = !res.writableFinished;
259
+ if (log) {
260
+ const line = {
261
+ method: req.method,
262
+ url: pathOf(req.originalUrl),
263
+ status: res.statusCode,
264
+ durationMs,
265
+ };
266
+ ctx.log.info(aborted ? { ...line, aborted } : line, "request");
267
+ }
268
+ hooks.onResponse?.(ctx, { status: res.statusCode, durationMs, aborted });
269
+ });
270
+ }
271
+ container.run(proceed, next);
272
+ };
273
+ }
274
+ /**
275
+ * The URL without its query string. Query strings carry secrets often enough (reset tokens, OAuth codes, API
276
+ * keys) that brisk never writes them to logs; log what you need from `ctx.req.query` in `onResponse`.
277
+ */
278
+ function pathOf(url) {
279
+ const query = url.indexOf("?");
280
+ return query === -1 ? url : url.slice(0, query);
281
+ }
282
+ function onRequest(hook) {
283
+ return async (req, _res, next) => {
284
+ await hook(getContext(req));
285
+ next();
286
+ };
287
+ }
288
+ /**
289
+ * Stops accepting connections and resolves once every in-flight request has finished. Keep-alive sockets
290
+ * close as soon as they go idle; after `timeoutMs` every remaining socket is destroyed, including upgraded
291
+ * WebSocket connections, which Node's `closeAllConnections` does not track.
292
+ */
293
+ function closeServer(server, sockets, timeoutMs) {
294
+ return new Promise((resolve) => {
295
+ const sweep = setInterval(() => server.closeIdleConnections(), 10);
296
+ const force = setTimeout(() => {
297
+ server.closeAllConnections();
298
+ for (const socket of sockets)
299
+ socket.destroy();
300
+ }, timeoutMs);
301
+ server.close(() => {
302
+ clearInterval(sweep);
303
+ clearTimeout(force);
304
+ resolve();
305
+ });
306
+ server.closeIdleConnections();
307
+ });
308
+ }
309
+ //# sourceMappingURL=instance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance.js","sourceRoot":"","sources":["../../../src/http/instance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import { type RequestContext } from "./context.ts";
2
+ /**
3
+ * Per-request logic shared by many routes, as {@link defineMiddleware} makes it. `run(ctx)` resolves to its
4
+ * output, computing it at most once per request.
5
+ */
6
+ export interface Middleware<Out extends object = object> {
7
+ readonly name: string;
8
+ run(ctx: RequestContext): Promise<Out>;
9
+ }
10
+ /**
11
+ * Names a middleware may not return: the request basics and validated input share the handler's ctx, and a
12
+ * middleware field with one of these names would be silently replaced. `log` is allowed, to enrich the logger.
13
+ */
14
+ type ReservedKey = "req" | "res" | "requestId" | "params" | "query" | "body" | "headers";
15
+ /** Rejects outputs that use a {@link ReservedKey}; the error names the offending key. */
16
+ type NoReservedKeys<Out> = {
17
+ [K in keyof Out]: K extends ReservedKey ? `"${K}" is reserved on ctx` : Out[K];
18
+ };
19
+ /** The middleware's output, or a type error naming any {@link ReservedKey} it uses. */
20
+ type Checked<Out> = Out extends object ? Out & NoReservedKeys<Out> : Out;
21
+ /**
22
+ * Declares per-request logic shared by many routes. Return an object to add typed fields to the handler's ctx,
23
+ * return nothing for a pure check, or throw an error class to stop the request. Sync or async.
24
+ *
25
+ * Each middleware runs at most once per request: another middleware, or a handler, can `await other.run(ctx)`
26
+ * and reuse its result. The names `req`, `res`, `requestId`, `params`, `query`, `body` and `headers` are
27
+ * reserved on ctx and rejected at compile time; returning `log` replaces the request logger.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * export const authenticate = defineMiddleware(async ({ req, log }) => {
32
+ * const user = await inject(Sessions).verify(req.get("authorization"));
33
+ * if (!user) throw new UnauthorizedError();
34
+ * return { user, log: log.child({ userId: user.id }) };
35
+ * }, "authenticate");
36
+ *
37
+ * export const requireRole = (role: Role) =>
38
+ * defineMiddleware(async (ctx) => {
39
+ * const { user } = await authenticate.run(ctx);
40
+ * if (user.role !== role) throw new ForbiddenError();
41
+ * });
42
+ * ```
43
+ */
44
+ export declare function defineMiddleware<Out extends object | void>(fn: (ctx: RequestContext) => Checked<Out> | Promise<Checked<Out>>, name?: string): Middleware<Out extends object ? Out : {}>;
45
+ type UnionToIntersection<U> = (U extends unknown ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
46
+ /** Merges the outputs of a middleware list into one type: `[auth, tenant]` gives `{ user } & { tenant }`. */
47
+ export type MiddlewareContext<T extends readonly Middleware[]> = [T[number]] extends [never] ? {} : UnionToIntersection<T[number] extends infer M ? (M extends Middleware<infer O> ? O : never) : never>;
48
+ /**
49
+ * Runs `list` in order against the request's context and writes each output onto `target`. Stays synchronous
50
+ * while the middlewares are, and returns a promise only from the first async one on.
51
+ */
52
+ export declare function applyMiddlewares(base: RequestContext, target: object, list: readonly Middleware[], from?: number): void | Promise<void>;
53
+ export {};
54
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../src/http/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAEhE;;;GAGG;AACH,MAAM,WAAW,UAAU,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;CACxC;AAED;;;GAGG;AACH,KAAK,WAAW,GAAG,KAAK,GAAG,KAAK,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAEzF,yFAAyF;AACzF,KAAK,cAAc,CAAC,GAAG,IAAI;KAAG,CAAC,IAAI,MAAM,GAAG,GAAG,CAAC,SAAS,WAAW,GAAG,IAAI,CAAC,sBAAsB,GAAG,GAAG,CAAC,CAAC,CAAC;CAAE,CAAC;AAE9G,uFAAuF;AACvF,KAAK,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,MAAM,GAAG,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,SAAS,MAAM,GAAG,IAAI,EACxD,EAAE,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EACjE,IAAI,GAAE,MAAgB,GACrB,UAAU,CAAC,GAAG,SAAS,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC,CAE3C;AAsDD,KAAK,mBAAmB,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,IAAI,GACnG,CAAC,GACD,KAAK,CAAC;AAEV,6GAA6G;AAC7G,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,SAAS,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACxF,EAAE,GACF,mBAAmB,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC;AAEzG;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,IAAI,SAAI,GACP,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CActB"}
@@ -0,0 +1,101 @@
1
+ import { requestMemo } from "./context.js";
2
+ /**
3
+ * Declares per-request logic shared by many routes. Return an object to add typed fields to the handler's ctx,
4
+ * return nothing for a pure check, or throw an error class to stop the request. Sync or async.
5
+ *
6
+ * Each middleware runs at most once per request: another middleware, or a handler, can `await other.run(ctx)`
7
+ * and reuse its result. The names `req`, `res`, `requestId`, `params`, `query`, `body` and `headers` are
8
+ * reserved on ctx and rejected at compile time; returning `log` replaces the request logger.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * export const authenticate = defineMiddleware(async ({ req, log }) => {
13
+ * const user = await inject(Sessions).verify(req.get("authorization"));
14
+ * if (!user) throw new UnauthorizedError();
15
+ * return { user, log: log.child({ userId: user.id }) };
16
+ * }, "authenticate");
17
+ *
18
+ * export const requireRole = (role: Role) =>
19
+ * defineMiddleware(async (ctx) => {
20
+ * const { user } = await authenticate.run(ctx);
21
+ * if (user.role !== role) throw new ForbiddenError();
22
+ * });
23
+ * ```
24
+ */
25
+ export function defineMiddleware(fn, name = fn.name) {
26
+ return new DefinedMiddleware(fn, name);
27
+ }
28
+ const EMPTY = Object.freeze({});
29
+ /**
30
+ * A middleware made by {@link defineMiddleware}. Its result is memoized in the request's own context: a plain
31
+ * value when `fn` is synchronous, so sync middlewares never allocate a promise on the request path, and a
32
+ * promise (replaced by its value once settled) when `fn` is async.
33
+ */
34
+ class DefinedMiddleware {
35
+ name;
36
+ #fn;
37
+ constructor(fn, name) {
38
+ this.name = name;
39
+ this.#fn = fn;
40
+ }
41
+ run(ctx) {
42
+ try {
43
+ const out = this.resolve(ctx);
44
+ return out instanceof Promise ? out : Promise.resolve(out);
45
+ }
46
+ catch (error) {
47
+ return Promise.reject(error);
48
+ }
49
+ }
50
+ resolve(ctx) {
51
+ const memo = requestMemo(ctx);
52
+ const cached = memo.get(this);
53
+ if (cached instanceof Promise)
54
+ return cached.then((settled) => this.#output(settled));
55
+ if (cached !== undefined)
56
+ return this.#output(cached);
57
+ const out = this.#fn(ctx);
58
+ if (!(out instanceof Promise)) {
59
+ const value = this.#output(out);
60
+ memo.set(this, value);
61
+ return value;
62
+ }
63
+ const pending = out.then((settled) => {
64
+ const value = this.#output(settled);
65
+ memo.set(this, value);
66
+ return value;
67
+ });
68
+ memo.set(this, pending);
69
+ return pending;
70
+ }
71
+ /** What `fn` returned, as the output its signature declared (`{}` for a check-only middleware). */
72
+ #output(out) {
73
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- fn is typed to return Out (or nothing)
74
+ return (typeof out === "object" && out !== null ? out : EMPTY);
75
+ }
76
+ }
77
+ /**
78
+ * Runs `list` in order against the request's context and writes each output onto `target`. Stays synchronous
79
+ * while the middlewares are, and returns a promise only from the first async one on.
80
+ */
81
+ export function applyMiddlewares(base, target, list, from = 0) {
82
+ for (let index = from; index < list.length; index++) {
83
+ const middleware = list[index];
84
+ if (!middleware)
85
+ continue;
86
+ const out = isDefined(middleware) ? middleware.resolve(base) : middleware.run(base);
87
+ if (out instanceof Promise) {
88
+ return out.then((value) => {
89
+ Object.assign(target, value);
90
+ return applyMiddlewares(base, target, list, index + 1);
91
+ });
92
+ }
93
+ Object.assign(target, out);
94
+ }
95
+ return undefined;
96
+ }
97
+ /** `instanceof` on a generic class narrows to `any` type arguments; this keeps the output typed as `object`. */
98
+ function isDefined(middleware) {
99
+ return middleware instanceof DefinedMiddleware;
100
+ }
101
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../src/http/middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { Endpoint } from "./controller.ts";
2
+ /**
3
+ * The OpenAPI document an app serves: `openapi: { title: "Shop API", version: "1.0.0" }`.
4
+ */
5
+ export interface OpenApiOptions {
6
+ title: string;
7
+ version: string;
8
+ description?: string;
9
+ /** Base URLs of the API, such as `[{ url: "https://api.example.com" }]`. Default: same origin. */
10
+ servers?: readonly {
11
+ url: string;
12
+ description?: string;
13
+ }[];
14
+ /** Where the document is served. Default `/openapi.json`; `false` to only build it with `app.openapi()`. */
15
+ path?: string | false;
16
+ }
17
+ /** An endpoint as the document lists it: the controller's name is its tag. */
18
+ export interface DocumentedEndpoint extends Endpoint {
19
+ tag: string;
20
+ }
21
+ type JsonObject = Record<string, unknown>;
22
+ /**
23
+ * Builds an OpenAPI 3.1 document from the routes' Zod schemas. Requests are documented as clients send them
24
+ * (`io: "input"`) and responses as clients receive them (`io: "output"`). A named route's dotted name is its
25
+ * `operationId`, such as `users.get`: the same name the typed client uses.
26
+ */
27
+ export declare function buildOpenApi(endpoints: readonly DocumentedEndpoint[], options: OpenApiOptions): JsonObject;
28
+ /**
29
+ * Converts an Express path: `:id` and `*path` become `{id}` and `{path}`. OpenAPI has no optional segments, so
30
+ * a `{/:slug}` group gives two paths, one with it and one without.
31
+ */
32
+ export declare function openApiPaths(path: string): string[];
33
+ export {};
34
+ //# sourceMappingURL=openapi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../../../src/http/openapi.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kGAAkG;IAClG,OAAO,CAAC,EAAE,SAAS;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3D,4GAA4G;IAC5G,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACvB;AAED,8EAA8E;AAC9E,MAAM,WAAW,kBAAmB,SAAQ,QAAQ;IAClD,GAAG,EAAE,MAAM,CAAC;CACb;AAED,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAmB1C;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,kBAAkB,EAAE,EAAE,OAAO,EAAE,cAAc,GAAG,UAAU,CAmB1G;AAsCD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAOnD"}
@@ -0,0 +1,123 @@
1
+ import { STATUS_CODES } from "node:http";
2
+ import { z } from "zod";
3
+ const ERROR_RESPONSE = {
4
+ type: "object",
5
+ required: ["error"],
6
+ properties: {
7
+ error: {
8
+ type: "object",
9
+ required: ["code", "message", "requestId"],
10
+ properties: {
11
+ code: { type: "string", examples: ["NOT_FOUND"] },
12
+ message: { type: "string" },
13
+ details: {},
14
+ requestId: { type: "string" },
15
+ },
16
+ },
17
+ },
18
+ };
19
+ /**
20
+ * Builds an OpenAPI 3.1 document from the routes' Zod schemas. Requests are documented as clients send them
21
+ * (`io: "input"`) and responses as clients receive them (`io: "output"`). A named route's dotted name is its
22
+ * `operationId`, such as `users.get`: the same name the typed client uses.
23
+ */
24
+ export function buildOpenApi(endpoints, options) {
25
+ const paths = {};
26
+ for (const endpoint of endpoints) {
27
+ for (const path of openApiPaths(endpoint.path)) {
28
+ const item = (paths[path] ??= {});
29
+ item[endpoint.method] = operation(endpoint, path);
30
+ }
31
+ }
32
+ return {
33
+ openapi: "3.1.1",
34
+ info: {
35
+ title: options.title,
36
+ version: options.version,
37
+ ...(options.description === undefined ? {} : { description: options.description }),
38
+ },
39
+ ...(options.servers ? { servers: options.servers } : {}),
40
+ paths,
41
+ components: { schemas: { ErrorResponse: ERROR_RESPONSE } },
42
+ };
43
+ }
44
+ function operation(endpoint, path) {
45
+ const { schemas } = endpoint;
46
+ const parameters = [
47
+ ...pathParameters(path, schemas.params),
48
+ ...fieldParameters(schemas.query, "query"),
49
+ ...fieldParameters(schemas.headers, "header"),
50
+ ];
51
+ const success = String(endpoint.status ?? (endpoint.method === "post" ? 201 : 200));
52
+ const responses = {
53
+ [success]: schemas.response
54
+ ? {
55
+ description: describe(success),
56
+ content: { "application/json": { schema: jsonSchema(schemas.response, "output") } },
57
+ }
58
+ : { description: describe(success) },
59
+ default: {
60
+ description: "Error",
61
+ content: { "application/json": { schema: { $ref: "#/components/schemas/ErrorResponse" } } },
62
+ },
63
+ };
64
+ return {
65
+ tags: [endpoint.tag],
66
+ operationId: endpoint.name === undefined ? undefined : `${endpoint.tag}.${endpoint.name}`,
67
+ ...(parameters.length > 0 ? { parameters } : {}),
68
+ ...(schemas.body
69
+ ? {
70
+ requestBody: {
71
+ required: true,
72
+ content: { "application/json": { schema: jsonSchema(schemas.body, "input") } },
73
+ },
74
+ }
75
+ : {}),
76
+ responses,
77
+ };
78
+ }
79
+ /**
80
+ * Converts an Express path: `:id` and `*path` become `{id}` and `{path}`. OpenAPI has no optional segments, so
81
+ * a `{/:slug}` group gives two paths, one with it and one without.
82
+ */
83
+ export function openApiPaths(path) {
84
+ const group = /\{([^{}]*)\}/.exec(path);
85
+ if (group) {
86
+ const [whole, inner = ""] = group;
87
+ return [...openApiPaths(path.replace(whole, inner)), ...openApiPaths(path.replace(whole, ""))];
88
+ }
89
+ return [path.replace(/[:*]([\w$]+)/g, "{$1}")];
90
+ }
91
+ function pathParameters(path, schema) {
92
+ const properties = schema ? objectProperties(jsonSchema(schema, "input")) : {};
93
+ return [...path.matchAll(/\{([\w$]+)\}/g)].map(([, name = ""]) => ({
94
+ name,
95
+ in: "path",
96
+ required: true,
97
+ schema: properties[name] ?? { type: "string" },
98
+ }));
99
+ }
100
+ function fieldParameters(schema, location) {
101
+ if (!schema)
102
+ return [];
103
+ const json = jsonSchema(schema, "input");
104
+ const required = new Set(Array.isArray(json["required"]) ? json["required"] : []);
105
+ return Object.entries(objectProperties(json)).map(([name, property]) => ({
106
+ name,
107
+ in: location,
108
+ required: required.has(name),
109
+ schema: property,
110
+ }));
111
+ }
112
+ function jsonSchema(schema, io) {
113
+ const { $schema: _dialect, ...json } = z.toJSONSchema(schema, { io, unrepresentable: "any" });
114
+ return json;
115
+ }
116
+ function objectProperties(json) {
117
+ const properties = json["properties"];
118
+ return typeof properties === "object" && properties !== null ? { ...properties } : {};
119
+ }
120
+ function describe(status) {
121
+ return STATUS_CODES[Number(status)] ?? "Success";
122
+ }
123
+ //# sourceMappingURL=openapi.js.map