@dunx/http 2.4.0 → 3.0.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 (40) hide show
  1. package/README.md +31 -909
  2. package/dist/chunk-25g22350.js +58 -0
  3. package/dist/chunk-ywdpxbkf.js +1573 -0
  4. package/dist/client/module.d.ts +2 -7
  5. package/dist/client/options.d.ts +12 -0
  6. package/dist/client/service.d.ts +11 -22
  7. package/dist/client.d.ts +8 -2
  8. package/dist/client.js +26 -137
  9. package/dist/compression/compression.d.ts +25 -0
  10. package/dist/compression/module.d.ts +20 -0
  11. package/dist/compression/negotiate.d.ts +12 -0
  12. package/dist/compression/options.d.ts +61 -0
  13. package/dist/health/indicators.d.ts +5 -10
  14. package/dist/index.d.ts +24 -20
  15. package/dist/index.js +325 -1366
  16. package/dist/internal.d.ts +36 -0
  17. package/dist/internal.js +89 -0
  18. package/dist/route/decorators.d.ts +6 -6
  19. package/dist/route/marker.d.ts +13 -0
  20. package/dist/route/metadata.d.ts +6 -9
  21. package/dist/route/schema.d.ts +59 -29
  22. package/dist/server/application.d.ts +33 -89
  23. package/dist/server/client-address.d.ts +6 -13
  24. package/dist/server/errors.d.ts +24 -55
  25. package/dist/server/request-id.d.ts +6 -11
  26. package/dist/server/request-logging.d.ts +33 -91
  27. package/dist/server/routes.d.ts +8 -20
  28. package/dist/server/trace-context.d.ts +47 -0
  29. package/dist/static/files.d.ts +5 -13
  30. package/dist/static/module.d.ts +10 -19
  31. package/dist/throttle/guard.d.ts +6 -9
  32. package/dist/throttle/module.d.ts +6 -8
  33. package/dist/throttle/store.d.ts +11 -20
  34. package/dist/ws/middleware.d.ts +12 -21
  35. package/dist/ws/redis-relay.d.ts +9 -19
  36. package/package.json +7 -3
  37. package/dist/chunk-sz4pvqxy.js +0 -111
  38. package/dist/chunk-sz4pvqxy.js.map +0 -10
  39. package/dist/client.js.map +0 -15
  40. package/dist/index.js.map +0 -53
package/dist/index.js CHANGED
@@ -1,249 +1,92 @@
1
1
  // @bun
2
2
  import {
3
+ TRACEPARENT_HEADER,
4
+ TRACESTATE_HEADER,
5
+ TraceContext
6
+ } from "./chunk-25g22350.js";
7
+ import {
8
+ ApiHidden,
9
+ CompressionEncoding,
10
+ CompressionOptions,
11
+ Controller,
12
+ DEFAULT_RELAY_CHANNEL,
13
+ Delete,
14
+ ErrorFilter,
15
+ Get,
16
+ HEALTH_REPORT_SCHEMA,
17
+ HIDDEN,
18
+ HandlerKind,
19
+ HealthController,
20
+ HealthOptions,
21
+ HealthRegistry,
22
+ HiddenHealthController,
23
+ HttpError,
3
24
  HttpStatusCode,
25
+ PUBLIC,
26
+ Patch,
27
+ Post,
28
+ Public,
29
+ Put,
30
+ REQUEST_ID_HEADER,
31
+ ROLES,
32
+ RawBody,
33
+ RedisRelay,
34
+ RequestIds,
35
+ Roles,
36
+ StaticOptions,
37
+ UNMATCHED,
38
+ UseGuards,
39
+ ValidationError,
4
40
  __decorateElement,
5
41
  __decoratorMetadata,
6
42
  __decoratorStart,
7
- __privateAdd,
8
- __privateGet,
9
- __runInitializers
10
- } from "./chunk-sz4pvqxy.js";
11
-
12
- // src/route/marker.ts
13
- var ROUTE = Symbol.for("dunx.route");
14
- var CONTROLLER = Symbol.for("dunx.controller");
15
- var resolvePath = (path) => typeof path === "function" ? path() : path;
16
- var markRoute = (target, meta) => {
17
- Object.defineProperty(target, ROUTE, { value: meta, configurable: true });
18
- };
19
- var routeMetaOf = (value) => typeof value === "function" ? value[ROUTE] : undefined;
20
- var markController = (target, prefix) => {
21
- Object.defineProperty(target, CONTROLLER, {
22
- value: prefix,
23
- configurable: true
24
- });
25
- };
26
- var prefixOf = (target) => target[CONTROLLER] ?? "";
27
-
28
- // src/route/decorators.ts
29
- var Controller = (prefix = "") => (target) => {
30
- markController(target, prefix);
31
- return target;
32
- };
33
- var verb = (method) => (path = "/", options) => (value, _context) => {
34
- markRoute(value, { method, path, options });
35
- return value;
36
- };
37
- var Get = verb("GET");
38
- var Post = verb("POST");
39
- var Put = verb("PUT");
40
- var Patch = verb("PATCH");
41
- var Delete = verb("DELETE");
42
- // src/route/discover.ts
43
- import { markedMethods } from "@dunx/core";
44
-
45
- // src/route/metadata.ts
46
- var META = Symbol.for("dunx.meta");
47
- var GUARDS = Symbol.for("dunx.guards");
48
- var metaKey = (name) => ({
49
- name,
50
- id: Symbol(name)
51
- });
52
- var write = (target, key, value) => {
53
- const record = new Map(target[META]);
54
- record.set(key.id, value);
55
- Object.defineProperty(target, META, { value: record, configurable: true });
56
- };
57
- var meta = (key, value) => (target) => {
58
- write(target, key, value);
59
- return target;
60
- };
61
- var ROLES = metaKey("roles");
62
- var PUBLIC = metaKey("public");
63
- var HIDDEN = metaKey("hidden");
64
- var UNMATCHED = metaKey("unmatched");
65
- var Roles = (...roles) => meta(ROLES, roles);
66
- var Public = () => meta(PUBLIC, true);
67
- var ApiHidden = () => meta(HIDDEN, true);
68
- var UseGuards = (...guards) => (target) => {
69
- const existing = target[GUARDS] ?? [];
70
- const merged = Object.hasOwn(target, GUARDS) ? [...guards, ...existing] : [...existing, ...guards];
71
- Object.defineProperty(target, GUARDS, {
72
- value: merged,
73
- configurable: true
74
- });
75
- return target;
76
- };
77
- var guardsOf = (target) => target[GUARDS] ?? [];
78
- var metaOf = (target) => target[META];
79
- var mergeMeta = (...targets) => {
80
- const merged = new Map;
81
- for (const target of targets) {
82
- const record = target[META];
83
- if (record)
84
- for (const [id, value] of record)
85
- merged.set(id, value);
86
- }
87
- return merged;
88
- };
89
-
90
- // src/route/discover.ts
91
- var joinPath = (prefix, path) => {
92
- const joined = `/${prefix}/${path}`.replace(/\/{2,}/g, "/");
93
- return joined.length > 1 ? joined.replace(/\/$/, "") : "/";
94
- };
95
- var discoverRoutes = (instance) => {
96
- const klass = instance.constructor;
97
- const prefix = prefixOf(klass);
98
- const classGuards = guardsOf(klass);
99
- const members = instance;
100
- return markedMethods(Object.getPrototypeOf(instance), routeMetaOf).map(({ name, meta: meta2, value: marked }) => ({
101
- method: meta2.method,
102
- path: joinPath(prefix, resolvePath(meta2.path)),
103
- controller: klass.name,
104
- handlerName: name,
105
- handler: members[name].bind(instance),
106
- options: meta2.options,
107
- meta: mergeMeta(klass, marked),
108
- classMeta: metaOf(klass),
109
- guards: [...classGuards, ...guardsOf(marked)]
110
- }));
111
- };
112
- // src/inspect.ts
113
- import {
114
- collectModules,
115
- dependenciesOf,
116
- readControllers
117
- } from "@dunx/core";
118
-
119
- // src/ws/discover.ts
120
- import {
121
- AppError,
122
- classOf,
123
- markedMethods as markedMethods2
124
- } from "@dunx/core";
125
-
126
- // src/ws/marker.ts
127
- var HANDLER = Symbol.for("dunx.ws.handler");
128
- var GATEWAY = Symbol.for("dunx.ws.gateway");
129
- var HandlerKind = Object.freeze({
130
- UPGRADE: "upgrade",
131
- OPEN: "open",
132
- MESSAGE: "message",
133
- CLOSE: "close",
134
- DRAIN: "drain",
135
- PING: "ping",
136
- PONG: "pong"
137
- });
138
- var markHandler = (target, meta2) => {
139
- Object.defineProperty(target, HANDLER, { value: meta2, configurable: true });
140
- };
141
- var handlerMetaOf = (value) => typeof value === "function" ? value[HANDLER] : undefined;
142
- var markGateway = (target, path) => {
143
- Object.defineProperty(target, GATEWAY, { value: path, configurable: true });
144
- };
145
- var gatewayPathOf = (target) => target[GATEWAY] ?? "/";
146
- var isGateway = (target) => target[GATEWAY] !== undefined;
147
-
148
- // src/ws/discover.ts
149
- var normalizePath = (path) => {
150
- const joined = `/${path}`.replace(/\/{2,}/g, "/");
151
- return joined.length > 1 ? joined.replace(/\/$/, "") : "/";
152
- };
153
- var eachHandler = (start) => markedMethods2(start, handlerMetaOf);
154
- var discoverGateway = (instance) => {
155
- const klass = instance.constructor;
156
- const members = instance;
157
- return {
158
- name: klass.name,
159
- path: normalizePath(gatewayPathOf(klass)),
160
- handlers: eachHandler(Object.getPrototypeOf(instance)).map(({ name, meta: meta2 }) => ({
161
- kind: meta2.kind,
162
- event: meta2.event,
163
- method: name,
164
- invoke: members[name].bind(instance)
165
- }))
166
- };
167
- };
168
- var findHandlerMethod = (ctor) => eachHandler(ctor.prototype)[0]?.name;
169
- var discoverGateways = (modules, resolve) => {
170
- const discovered = [];
171
- for (const module of modules) {
172
- for (const entry of module.options.providers ?? []) {
173
- const candidate = classOf(entry);
174
- if (!candidate)
175
- continue;
176
- if (isGateway(candidate.ctor)) {
177
- discovered.push(discoverGateway(resolve(candidate.token)));
178
- continue;
179
- }
180
- const orphan = findHandlerMethod(candidate.ctor);
181
- if (orphan !== undefined) {
182
- throw new AppError(`${candidate.ctor.name}.${orphan}() is a websocket handler, but ` + `${candidate.ctor.name} is not a gateway. Decorate the class with ` + "@Gateway(path), or drop the handler decorator.");
183
- }
184
- }
185
- }
186
- return discovered;
187
- };
188
-
189
- // src/inspect.ts
190
- var vendorOf = (schema) => schema?.["~standard"]?.vendor;
191
- var validatesIn = (options) => {
192
- const body = vendorOf(options?.body);
193
- const query = vendorOf(options?.query);
194
- const params = vendorOf(options?.params);
195
- return {
196
- ...body === undefined ? {} : { body },
197
- ...query === undefined ? {} : { query },
198
- ...params === undefined ? {} : { params }
199
- };
200
- };
201
- var rolesIn = (route) => {
202
- const roles = route.meta?.get(ROLES.id);
203
- if (roles === undefined || roles === null)
204
- return null;
205
- return (Array.isArray(roles) ? roles : [roles]).map(String);
206
- };
207
- var nodeFor = (route, module) => ({
208
- method: route.method,
209
- path: route.path,
210
- controller: route.controller,
211
- handler: route.handlerName,
212
- module,
213
- public: route.meta?.get(PUBLIC.id) === true,
214
- roles: rolesIn(route),
215
- guards: (route.guards ?? []).map((guard) => guard.name),
216
- hidden: route.meta?.get(HIDDEN.id) === true,
217
- validates: validatesIn(route.options),
218
- status: route.options?.status ?? null,
219
- responses: Object.keys(route.options?.response ?? {}).map(Number)
220
- });
221
- var routesOf = (root) => collectModules(root).flatMap((module) => readControllers(module).flatMap((controller) => {
222
- const { prototype } = controller;
223
- return discoverRoutes(Object.create(prototype)).map((route) => nodeFor(route, module.name));
224
- }));
225
- var gatewayFor = (ctor, module) => {
226
- const { name, path, handlers } = discoverGateway(Object.create(ctor.prototype));
227
- return {
228
- name,
229
- path,
230
- module,
231
- dependencies: dependenciesOf(ctor),
232
- handlers: handlers.map((handler) => ({
233
- kind: handler.kind,
234
- event: handler.event ?? null,
235
- method: handler.method
236
- }))
237
- };
238
- };
239
- var classOf2 = (entry) => {
240
- if (typeof entry === "function")
241
- return entry;
242
- return entry.provider.kind === "class" ? entry.provider.ctor : undefined;
243
- };
244
- var gatewaysOf = (root) => collectModules(root).flatMap((module) => (module.options.providers ?? []).map(classOf2).filter((ctor) => ctor !== undefined).filter(isGateway).map((ctor) => gatewayFor(ctor, module.name)));
43
+ __runInitializers,
44
+ assertNoCollisions,
45
+ assertNoGatewayCollisions,
46
+ buildContext,
47
+ buildFallback,
48
+ buildGateways,
49
+ buildRoutes,
50
+ buildRuntime,
51
+ buildWebSocket,
52
+ compose,
53
+ composeSocket,
54
+ decode,
55
+ decodeRelay,
56
+ defaultErrorMapper,
57
+ defaultRelayError,
58
+ defaultRelayUrl,
59
+ defaultStatusFor,
60
+ discoverGateway,
61
+ discoverGateways,
62
+ discoverRoutes,
63
+ encode,
64
+ encodeRelay,
65
+ errorMapper,
66
+ gatewaysOf,
67
+ guardsOf,
68
+ isCompressibleType,
69
+ isErrorFilter,
70
+ isGateway,
71
+ joinPath,
72
+ markGateway,
73
+ markHandler,
74
+ mergeMeta,
75
+ meta,
76
+ metaKey,
77
+ metaOf,
78
+ negotiate,
79
+ normalizePath,
80
+ normalizePrefix,
81
+ observe,
82
+ preflight,
83
+ routesOf,
84
+ toErrorMapper,
85
+ withCors,
86
+ withUpgradeRoutes
87
+ } from "./chunk-ywdpxbkf.js";
245
88
  // src/server/client-address.ts
246
- import { AppError as AppError2 } from "@dunx/core";
89
+ import { AppError } from "@dunx/core";
247
90
  var trustedHops = (setting) => {
248
91
  if (setting === true)
249
92
  return 1;
@@ -257,7 +100,7 @@ class ClientAddress {
257
100
  of(req) {
258
101
  const source = sources.get(this);
259
102
  if (!source) {
260
- throw new AppError2("ClientAddress has no server yet. The address comes from the live Bun " + "server, so it is only available once listen() has run.");
103
+ throw new AppError("ClientAddress has no server yet. The address comes from the live Bun " + "server, so it is only available once listen() has run.");
261
104
  }
262
105
  const hops = trustedHops(source.trustProxy);
263
106
  if (hops > 0) {
@@ -272,432 +115,17 @@ class ClientAddress {
272
115
  var attachAddressSource = (target, source) => {
273
116
  sources.set(target, source);
274
117
  };
275
- // src/server/context.ts
276
- var EMPTY = new Map;
277
- var buildContext = (route) => {
278
- const record = route.meta ?? EMPTY;
279
- return Object.freeze({
280
- controller: route.controller,
281
- handler: route.handlerName,
282
- method: route.method,
283
- path: route.path,
284
- parsesBody: route.options?.body !== undefined,
285
- get: (key) => record.get(key.id)
286
- });
287
- };
288
- // src/server/cors.ts
289
- var ORIGIN = "access-control-allow-origin";
290
- var allowedOrigin = (options, requested) => {
291
- const origin = options.origin ?? "*";
292
- if (typeof origin === "string") {
293
- if (origin !== "*")
294
- return origin === requested ? origin : undefined;
295
- if (!options.credentials)
296
- return "*";
297
- return requested ?? undefined;
298
- }
299
- if (requested === null)
300
- return;
301
- const allowed = typeof origin === "function" ? origin(requested) : origin.includes(requested);
302
- return allowed ? requested : undefined;
303
- };
304
- var applyCors = (options, req, response) => {
305
- const origin = allowedOrigin(options, req.headers.get("origin"));
306
- if (origin === undefined)
307
- return response;
308
- response.headers.set(ORIGIN, origin);
309
- if (origin !== "*")
310
- response.headers.append("vary", "Origin");
311
- if (options.credentials) {
312
- response.headers.set("access-control-allow-credentials", "true");
313
- }
314
- if (options.exposedHeaders?.length) {
315
- response.headers.set("access-control-expose-headers", options.exposedHeaders.join(", "));
316
- }
317
- return response;
318
- };
319
- var withCors = (options, handler) => {
320
- return async (req) => applyCors(options, req, await handler(req));
321
- };
322
- var preflight = (options, methods) => {
323
- const allowMethods = (options.methods ?? methods).join(", ");
324
- return async (req) => {
325
- const response = applyCors(options, req, new Response(null, { status: HttpStatusCode.NO_CONTENT }));
326
- if (!response.headers.has(ORIGIN))
327
- return response;
328
- response.headers.set("access-control-allow-methods", allowMethods);
329
- const allowHeaders = options.allowedHeaders ?? (req.headers.get("access-control-request-headers") ?? "").split(",").map((header) => header.trim()).filter((header) => header.length > 0);
330
- if (allowHeaders.length > 0) {
331
- response.headers.set("access-control-allow-headers", allowHeaders.join(", "));
332
- }
333
- if (options.maxAge !== undefined) {
334
- response.headers.set("access-control-max-age", String(options.maxAge));
335
- }
336
- return response;
337
- };
338
- };
339
- // src/server/errors.ts
340
- import { AppError as AppError3, ConsoleLogger } from "@dunx/core";
341
- class HttpError extends AppError3 {
342
- status;
343
- name = "HttpError";
344
- headers;
345
- constructor(status, message, options) {
346
- super(message, options);
347
- this.status = status;
348
- this.headers = options?.headers;
349
- }
350
- }
351
- Object.defineProperty(HttpError, Symbol.for("dunx.deps"), {
352
- value: () => [{ unresolved: "readonly status: number" }, { unresolved: "message: string" }, { unresolved: "options?: HttpErrorOptions" }]
353
- });
354
-
355
- class ValidationError extends HttpError {
356
- source;
357
- issues;
358
- name = "ValidationError";
359
- constructor(source, issues) {
360
- super(HttpStatusCode.BAD_REQUEST, `Invalid ${source}`);
361
- this.source = source;
362
- this.issues = issues;
363
- }
364
- }
365
- Object.defineProperty(ValidationError, Symbol.for("dunx.deps"), {
366
- value: () => [{ unresolved: "readonly source: InputSource" }, { unresolved: "readonly issues: readonly ValidationIssue[]" }]
367
- });
368
-
369
- class ErrorFilter {
370
- }
371
- var isErrorFilter = (handler) => typeof handler === "function" && typeof handler.prototype?.catch === "function";
372
- var toErrorMapper = (handler, resolve) => isErrorFilter(handler) ? (error, req) => resolve(handler).catch(error, req) : handler;
373
- var errorMapper = (logger) => (error) => {
374
- if (error instanceof ValidationError) {
375
- return Response.json({ error: error.message, status: error.status, issues: error.issues }, {
376
- status: error.status,
377
- ...error.headers && { headers: error.headers }
378
- });
379
- }
380
- if (error instanceof HttpError) {
381
- return Response.json({ error: error.message, status: error.status }, {
382
- status: error.status,
383
- ...error.headers && { headers: error.headers }
384
- });
385
- }
386
- logger.error("Unhandled error", error);
387
- return Response.json({
388
- error: "Internal Server Error",
389
- status: HttpStatusCode.INTERNAL_SERVER_ERROR
390
- }, { status: HttpStatusCode.INTERNAL_SERVER_ERROR });
391
- };
392
- var defaultErrorMapper = errorMapper(new ConsoleLogger);
393
118
  // src/server/factory.ts
394
119
  import {
395
- collectModules as collectModules2,
396
- AppError as AppError8,
120
+ collectModules,
121
+ AppError as AppError4,
397
122
  AppFactory,
398
123
  Logger as Logger4,
399
124
  provide,
400
- readControllers as readControllers2,
125
+ readControllers,
401
126
  RequestContext as RequestContext3
402
127
  } from "@dunx/core";
403
128
 
404
- // src/ws/envelope.ts
405
- var encode = (event, data) => JSON.stringify({ event, data });
406
- var decode = (message) => {
407
- if (typeof message !== "string")
408
- return;
409
- let parsed;
410
- try {
411
- parsed = JSON.parse(message);
412
- } catch {
413
- return;
414
- }
415
- if (typeof parsed !== "object" || parsed === null)
416
- return;
417
- const { event, data } = parsed;
418
- return typeof event === "string" ? { event, data } : undefined;
419
- };
420
-
421
- // src/ws/middleware.ts
422
- var composeSocket = (middleware, ctx) => middleware.reduceRight((next, current) => (frame, run) => current.handle(frame, ctx, () => next(frame, run)), (_frame, run) => run());
423
- var observe = (next, done) => {
424
- let result;
425
- try {
426
- result = next();
427
- } catch (error) {
428
- done(error, undefined);
429
- throw error;
430
- }
431
- if (result instanceof Promise) {
432
- return result.then((value) => {
433
- done(undefined, value);
434
- return value;
435
- }, (error) => {
436
- done(error, undefined);
437
- throw error;
438
- });
439
- }
440
- done(undefined, result);
441
- return result;
442
- };
443
-
444
- // src/ws/runtime.ts
445
- import { AppError as AppError4 } from "@dunx/core";
446
- var slotOf = (handler) => handler.kind === HandlerKind.MESSAGE && handler.event !== undefined ? `message ${JSON.stringify(handler.event)}` : handler.kind;
447
- var buildRuntime = (gateway) => {
448
- if (gateway.handlers.length === 0) {
449
- throw new AppError4(`${gateway.name} is registered as a gateway but declares no handlers. ` + "Add an @OnMessage/@OnOpen/... method, or drop the @Gateway decorator.");
450
- }
451
- const owners = new Map;
452
- const events = new Map;
453
- for (const handler of gateway.handlers) {
454
- const slot = slotOf(handler);
455
- const existing = owners.get(slot);
456
- if (existing) {
457
- throw new AppError4(`Handler collision in ${gateway.name}: ${slot} is claimed by ` + `${existing.method}() and by ${handler.method}(). One handler per event.`);
458
- }
459
- owners.set(slot, handler);
460
- if (handler.kind === HandlerKind.MESSAGE && handler.event !== undefined) {
461
- events.set(handler.event, handler.invoke);
462
- }
463
- }
464
- const at = (slot) => owners.get(slot)?.invoke;
465
- return {
466
- name: gateway.name,
467
- path: gateway.path,
468
- upgrade: at(HandlerKind.UPGRADE),
469
- open: at(HandlerKind.OPEN),
470
- close: at(HandlerKind.CLOSE),
471
- drain: at(HandlerKind.DRAIN),
472
- ping: at(HandlerKind.PING),
473
- pong: at(HandlerKind.PONG),
474
- raw: at(HandlerKind.MESSAGE),
475
- events
476
- };
477
- };
478
- var buildGateways = (discovered) => {
479
- const byPath = new Map;
480
- for (const gateway of discovered) {
481
- const existing = byPath.get(gateway.path);
482
- if (existing) {
483
- throw new AppError4(`Gateway path collision: ${gateway.path} is served by ${existing.name} ` + `and by ${gateway.name}. One gateway per path.`);
484
- }
485
- byPath.set(gateway.path, buildRuntime(gateway));
486
- }
487
- return byPath;
488
- };
489
- var someHandler = (gateways, pick) => {
490
- for (const gateway of gateways)
491
- if (pick(gateway) !== undefined)
492
- return true;
493
- return false;
494
- };
495
-
496
- // src/ws/adapter.ts
497
- var RUNTIME = Symbol.for("dunx.ws.runtime");
498
- var UNCLAIMED = Symbol.for("dunx.ws.unclaimed");
499
- var defaultOnError = (error, socket) => {
500
- console.error(`[dunx/http] ${socket.data.path} handler failed:`, error);
501
- };
502
- var reportedByMiddleware = () => {
503
- return;
504
- };
505
- var unreported = (middleware) => "Socket middleware is installed and none of it sets reportsErrors, so a " + "throwing gateway handler is reported nowhere: the console fallback is off " + "whenever middleware wraps the handler. Set reportsErrors on the one that " + "records a failure, or pass websocket.onError. Installed: " + `${middleware.map((entry) => entry.constructor.name).join(", ")}.`;
506
- var runtimeOf = (socket) => socket.data[RUNTIME];
507
- var isBinary = (value) => value instanceof ArrayBuffer || ArrayBuffer.isView(value);
508
- var replyRaw = (socket, value) => {
509
- if (value === undefined)
510
- return;
511
- socket.send(typeof value === "string" || isBinary(value) ? value : JSON.stringify(value));
512
- };
513
- var settle = (result, socket, onError, then) => {
514
- if (result instanceof Promise) {
515
- result.then((value) => {
516
- if (!then)
517
- return;
518
- try {
519
- then(value);
520
- } catch (error) {
521
- onError(error, socket);
522
- }
523
- }, (error) => onError(error, socket));
524
- return;
525
- }
526
- if (then)
527
- then(result);
528
- };
529
- var framing = (kind) => {
530
- if (kind === HandlerKind.CLOSE) {
531
- return (args) => ({
532
- socket: args[0],
533
- data: { code: args[1], reason: args[2] }
534
- });
535
- }
536
- if (kind === HandlerKind.OPEN || kind === HandlerKind.DRAIN) {
537
- return (args) => ({ socket: args[0], data: undefined });
538
- }
539
- return (args) => ({ socket: args[1], data: args[0] });
540
- };
541
- var NOTHING = () => {
542
- return;
543
- };
544
- var through = (gateway, middleware, kind, event, invoke) => {
545
- const ctx = {
546
- gateway: gateway.name,
547
- path: gateway.path,
548
- kind,
549
- event
550
- };
551
- const dispatch = composeSocket(middleware, ctx);
552
- const frameOf = framing(kind);
553
- const run = invoke ?? NOTHING;
554
- return (...args) => dispatch(frameOf(args), () => run(...args));
555
- };
556
- var withMiddleware = (gateway, middleware) => {
557
- const wrap = (kind, event, invoke) => through(gateway, middleware, kind, event, invoke);
558
- const optional = (kind, invoke) => invoke === undefined ? undefined : wrap(kind, undefined, invoke);
559
- return {
560
- ...gateway,
561
- open: wrap(HandlerKind.OPEN, undefined, gateway.open),
562
- close: wrap(HandlerKind.CLOSE, undefined, gateway.close),
563
- drain: optional(HandlerKind.DRAIN, gateway.drain),
564
- ping: optional(HandlerKind.PING, gateway.ping),
565
- pong: optional(HandlerKind.PONG, gateway.pong),
566
- raw: optional(HandlerKind.MESSAGE, gateway.raw),
567
- events: new Map([...gateway.events].map(([event, invoke]) => [
568
- event,
569
- wrap(HandlerKind.MESSAGE, event, invoke)
570
- ]))
571
- };
572
- };
573
- var unclaimedDispatch = (gateway, middleware) => (frame, event) => composeSocket(middleware, {
574
- gateway: gateway.name,
575
- path: gateway.path,
576
- kind: HandlerKind.MESSAGE,
577
- event
578
- })(frame, () => {
579
- return;
580
- });
581
- var buildWebSocket = (discovered, options = {}, middleware = []) => {
582
- const byPath = buildGateways(discovered);
583
- const wrapped = middleware.length === 0 ? byPath : new Map([...byPath].map(([path, gateway]) => [
584
- path,
585
- withMiddleware(gateway, middleware)
586
- ]));
587
- const gateways = [...wrapped.values()];
588
- const onError = options.onError ?? (middleware.length === 0 ? defaultOnError : reportedByMiddleware);
589
- const reports = options.onError !== undefined || middleware.some((entry) => entry.reportsErrors === true);
590
- const { onError: _onError, ...socketOptions } = options;
591
- const run = (invoke, args, ws, then) => {
592
- try {
593
- settle(invoke(...args), ws, onError, then);
594
- } catch (error) {
595
- onError(error, ws);
596
- }
597
- };
598
- const websocket = {
599
- ...socketOptions,
600
- message(ws, message) {
601
- const gateway = runtimeOf(ws);
602
- let event;
603
- if (gateway.events.size > 0) {
604
- const envelope = decode(message);
605
- const handler = envelope && gateway.events.get(envelope.event);
606
- if (envelope && handler) {
607
- run(handler, [envelope.data, ws], ws, (value) => {
608
- if (value !== undefined)
609
- ws.send(encode(envelope.event, value));
610
- });
611
- return;
612
- }
613
- event = envelope?.event;
614
- }
615
- if (gateway.raw) {
616
- run(gateway.raw, [message, ws], ws, (value) => replyRaw(ws, value));
617
- return;
618
- }
619
- const unclaimed2 = ws.data[UNCLAIMED];
620
- if (!unclaimed2)
621
- return;
622
- try {
623
- settle(unclaimed2({ socket: ws, data: message }, event), ws, onError, undefined);
624
- } catch (error) {
625
- onError(error, ws);
626
- }
627
- },
628
- ...someHandler(gateways, (g) => g.open) && {
629
- open(ws) {
630
- const { open } = runtimeOf(ws);
631
- if (open)
632
- run(open, [ws], ws, undefined);
633
- }
634
- },
635
- ...someHandler(gateways, (g) => g.close) && {
636
- close(ws, code, reason) {
637
- const { close } = runtimeOf(ws);
638
- if (close)
639
- run(close, [ws, code, reason], ws, undefined);
640
- }
641
- },
642
- ...someHandler(gateways, (g) => g.drain) && {
643
- drain(ws) {
644
- const { drain } = runtimeOf(ws);
645
- if (drain)
646
- run(drain, [ws], ws, undefined);
647
- }
648
- },
649
- ...someHandler(gateways, (g) => g.ping) && {
650
- ping(ws, data) {
651
- const { ping } = runtimeOf(ws);
652
- if (ping)
653
- run(ping, [data, ws], ws, undefined);
654
- }
655
- },
656
- ...someHandler(gateways, (g) => g.pong) && {
657
- pong(ws, data) {
658
- const { pong } = runtimeOf(ws);
659
- if (pong)
660
- run(pong, [data, ws], ws, undefined);
661
- }
662
- }
663
- };
664
- const unclaimed = new Map(middleware.length === 0 ? [] : gateways.map((gateway) => [
665
- gateway,
666
- unclaimedDispatch(gateway, middleware)
667
- ]));
668
- const accept = (req, server, gateway, context) => {
669
- const fallback = unclaimed.get(gateway);
670
- const data = {
671
- path: gateway.path,
672
- context,
673
- id: crypto.randomUUID(),
674
- [RUNTIME]: gateway,
675
- ...fallback === undefined ? {} : { [UNCLAIMED]: fallback }
676
- };
677
- return server.upgrade(req, { data }) ? undefined : new Response("Expected a WebSocket upgrade", { status: 426 });
678
- };
679
- const upgradeHandler = (gateway) => (req, server) => {
680
- if (!gateway.upgrade)
681
- return accept(req, server, gateway, undefined);
682
- const result = gateway.upgrade(req);
683
- if (result instanceof Promise) {
684
- return result.then((value) => value instanceof Response ? value : accept(req, server, gateway, value));
685
- }
686
- return result instanceof Response ? result : accept(req, server, gateway, result);
687
- };
688
- return {
689
- websocket,
690
- routes: new Map(gateways.map((gateway) => [gateway.path, upgradeHandler(gateway)])),
691
- paths: [...byPath.keys()],
692
- warnings: middleware.length > 0 && !reports ? [unreported(middleware)] : [],
693
- gateways: gateways.map((gateway) => ({
694
- name: gateway.name,
695
- path: gateway.path,
696
- events: [...gateway.events.keys()]
697
- }))
698
- };
699
- };
700
-
701
129
  // src/ws/logging.ts
702
130
  import { Logger, LogLevel, RequestContext } from "@dunx/core";
703
131
  var LIFECYCLE_LABEL = {
@@ -742,7 +170,7 @@ class SocketLoggingMiddleware {
742
170
  const label = ctx.event ?? LIFECYCLE_LABEL[ctx.kind] ?? ctx.kind;
743
171
  const connectionId = frame.socket.data.id;
744
172
  const started = Bun.nanoseconds();
745
- const write2 = (error, value) => {
173
+ const write = (error, value) => {
746
174
  const entry = {
747
175
  gateway: ctx.gateway,
748
176
  path: ctx.path,
@@ -756,8 +184,8 @@ class SocketLoggingMiddleware {
756
184
  this.#emit(error === undefined ? level : this.#errorLevel, line, entry);
757
185
  };
758
186
  if (!this.#correlate)
759
- return observe(next, write2);
760
- return this.context.runWithContext({ connectionId, event: label, flow: "ws", context: ctx.gateway }, () => observe(next, write2));
187
+ return observe(next, write);
188
+ return this.context.runWithContext({ connectionId, event: label, flow: "ws", context: ctx.gateway }, () => observe(next, write));
761
189
  }
762
190
  #emit(level, line, entry) {
763
191
  switch (level) {
@@ -793,42 +221,10 @@ class SocketLoggingMiddleware {
793
221
  return text.length > this.#limit ? `[${text.length} chars]` : data;
794
222
  }
795
223
  }
796
- Object.defineProperty(SocketLoggingMiddleware, Symbol.for("dunx.deps"), {
797
- value: () => [Logger, RequestContext, { unresolved: "options: SocketLoggingOptions = {}" }]
798
- });
799
-
800
- // src/ws/pubsub.ts
801
- import { AppError as AppError5 } from "@dunx/core";
802
-
803
- // src/ws/relay.ts
804
- var DEFAULT_RELAY_CHANNEL = "dunx:ws";
805
- var defaultRelayError = (error, phase) => {
806
- console.warn(`[dunx/http] the websocket relay could not ${phase}. Fan-out is local to ` + "this process until it recovers:", error);
807
- };
808
- var toBytes = (data) => ArrayBuffer.isView(data) ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength) : new Uint8Array(data);
809
- var encodeRelay = (origin, topic, data) => typeof data === "string" ? JSON.stringify({ o: origin, t: topic, d: data }) : JSON.stringify({
810
- o: origin,
811
- t: topic,
812
- d: Buffer.from(toBytes(data)).toString("base64"),
813
- b: 1
814
- });
815
- var decodeRelay = (message) => {
816
- let parsed;
817
- try {
818
- parsed = JSON.parse(message);
819
- } catch {
820
- return;
821
- }
822
- if (typeof parsed !== "object" || parsed === null)
823
- return;
824
- const { o, t, d, b } = parsed;
825
- if (typeof o !== "string" || typeof t !== "string" || typeof d !== "string") {
826
- return;
827
- }
828
- return { origin: o, topic: t, data: b ? Buffer.from(d, "base64") : d };
829
- };
224
+ Object.defineProperty(SocketLoggingMiddleware, Symbol.for("dunx.deps"), { value: () => [Logger, RequestContext, { unresolved: "options: SocketLoggingOptions = {}", optional: true }] });
830
225
 
831
226
  // src/ws/pubsub.ts
227
+ import { AppError as AppError2 } from "@dunx/core";
832
228
  class PubSub {
833
229
  #origin = Bun.randomUUIDv7();
834
230
  #server;
@@ -853,7 +249,7 @@ class PubSub {
853
249
  }
854
250
  async relayThrough(relay, options = {}) {
855
251
  if (this.#relay) {
856
- throw new AppError5("PubSub already relays. Two subscriptions on one channel would deliver " + "every relayed message twice - pass HttpOptions.relay or call " + "relayThrough(), not both.");
252
+ throw new AppError2("PubSub already relays. Two subscriptions on one channel would deliver " + "every relayed message twice - pass HttpOptions.relay or call " + "relayThrough(), not both.");
857
253
  }
858
254
  this.#relay = relay;
859
255
  this.#channel = options.channel ?? DEFAULT_RELAY_CHANNEL;
@@ -949,7 +345,7 @@ class PubSub {
949
345
  }
950
346
  #live() {
951
347
  if (!this.#server) {
952
- throw new AppError5("PubSub has no server yet. Publish once the server is listening: " + "HttpApp.listen() is what attaches it.");
348
+ throw new AppError2("PubSub has no server yet. Publish once the server is listening: " + "HttpApp.listen() is what attaches it.");
953
349
  }
954
350
  return this.#server;
955
351
  }
@@ -957,7 +353,7 @@ class PubSub {
957
353
 
958
354
  // src/server/application.ts
959
355
  import {
960
- AppError as AppError7,
356
+ AppError as AppError3,
961
357
  Logger as Logger3,
962
358
  runtimeInfo,
963
359
  ShutdownHooks,
@@ -970,47 +366,6 @@ import {
970
366
  Logger as Logger2,
971
367
  RequestContext as RequestContext2
972
368
  } from "@dunx/core";
973
-
974
- // src/server/raw-body.ts
975
- var WANTED = Symbol.for("dunx.http.rawBody.wanted");
976
- var TEXT = Symbol.for("dunx.http.rawBody.text");
977
-
978
- class RawBody {
979
- static want(req) {
980
- req[WANTED] = true;
981
- }
982
- static wanted(req) {
983
- return req[WANTED] === true;
984
- }
985
- static record(req, text) {
986
- req[TEXT] = text;
987
- }
988
- static read(req) {
989
- return req[TEXT];
990
- }
991
- }
992
-
993
- // src/server/request-id.ts
994
- var REQUEST_ID_HEADER = "x-request-id";
995
- var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
996
- var traceId = (inbound) => inbound !== null && inbound.length === 36 && UUID.test(inbound) ? inbound : crypto.randomUUID();
997
- var ID = Symbol.for("dunx.http.requestId");
998
-
999
- class RequestIds {
1000
- static assign(req) {
1001
- const id = traceId(req.headers.get(REQUEST_ID_HEADER));
1002
- req[ID] = id;
1003
- return id;
1004
- }
1005
- static stamp(response, req) {
1006
- const id = req[ID];
1007
- if (id !== undefined)
1008
- response.headers.set(REQUEST_ID_HEADER, id);
1009
- return response;
1010
- }
1011
- }
1012
-
1013
- // src/server/request-logging.ts
1014
369
  var parse = (text, limit) => {
1015
370
  if (limit === 0)
1016
371
  return;
@@ -1036,6 +391,7 @@ class RequestLoggingMiddleware {
1036
391
  #ignorePrefix;
1037
392
  #correlateIgnored;
1038
393
  #correlate;
394
+ #trace;
1039
395
  constructor(logger, context, options = {}) {
1040
396
  this.logger = logger;
1041
397
  this.context = context;
@@ -1046,6 +402,7 @@ class RequestLoggingMiddleware {
1046
402
  this.#ignorePrefix = options.ignorePrefix ?? [];
1047
403
  this.#correlateIgnored = options.correlateIgnored ?? false;
1048
404
  this.#correlate = options.correlate ?? true;
405
+ this.#trace = options.trace ?? false;
1049
406
  }
1050
407
  #ignored(path) {
1051
408
  if (this.#ignore.size > 0 && this.#ignore.has(path))
@@ -1071,6 +428,14 @@ class RequestLoggingMiddleware {
1071
428
  flow: "http",
1072
429
  context: `${ctx.controller}.${ctx.handler}`
1073
430
  };
431
+ if (this.#trace) {
432
+ const trace = TraceContext.adopt(req, requestId);
433
+ scope.traceId = trace.traceId;
434
+ scope.spanId = trace.spanId;
435
+ if (trace.parentSpanId !== undefined) {
436
+ scope.parentSpanId = trace.parentSpanId;
437
+ }
438
+ }
1074
439
  return this.#correlate ? this.context.runWithContext(scope, () => this.#begin(req, ctx, url, mark, path, requestId, started, next, undefined)) : this.#begin(req, ctx, url, mark, path, requestId, started, next, scope);
1075
440
  }
1076
441
  #begin(req, ctx, url, mark, path, requestId, started, next, scope) {
@@ -1196,248 +561,7 @@ class RequestLoggingMiddleware {
1196
561
  return response.clone().text().then((text) => parse(text, this.#limit));
1197
562
  }
1198
563
  }
1199
- Object.defineProperty(RequestLoggingMiddleware, Symbol.for("dunx.deps"), {
1200
- value: () => [Logger2, RequestContext2, { unresolved: "options: RequestLoggingOptions = {}" }]
1201
- });
1202
-
1203
- // src/server/routes.ts
1204
- import { AppError as AppError6 } from "@dunx/core";
1205
-
1206
- // src/server/input.ts
1207
- var grouped = (entries) => {
1208
- const collected = {};
1209
- entries.forEach((value, key) => {
1210
- const existing = collected[key];
1211
- if (existing === undefined)
1212
- collected[key] = value;
1213
- else if (Array.isArray(existing))
1214
- existing.push(value);
1215
- else
1216
- collected[key] = [existing, value];
1217
- });
1218
- return collected;
1219
- };
1220
- var asJson = (req) => req.json();
1221
- var asUrlEncoded = async (req) => grouped(new URLSearchParams(await req.text()));
1222
- var asMultipart = async (req) => grouped(await req.formData());
1223
- var asText = (req) => req.text();
1224
- var parserFor = (media) => {
1225
- if (media === "application/json" || media.endsWith("+json"))
1226
- return asJson;
1227
- if (media === "application/x-www-form-urlencoded")
1228
- return asUrlEncoded;
1229
- if (media === "multipart/form-data")
1230
- return asMultipart;
1231
- if (media.startsWith("text/"))
1232
- return asText;
1233
- return;
1234
- };
1235
- var JSON_MEDIA = "application/json";
1236
- var mediaTypeOf = (req) => {
1237
- const header = req.headers.get("content-type");
1238
- if (header === JSON_MEDIA || header === null)
1239
- return JSON_MEDIA;
1240
- const end = header.indexOf(";");
1241
- const media = (end === -1 ? header : header.slice(0, end)).trim();
1242
- return media === "" ? JSON_MEDIA : media.toLowerCase();
1243
- };
1244
- var flatten = (issue) => {
1245
- const path = issue.path?.map((segment) => String(typeof segment === "object" ? segment.key : segment)).join(".");
1246
- return path === undefined || path === "" ? { message: issue.message } : { message: issue.message, path };
1247
- };
1248
- var accept = (source, result) => {
1249
- if (result.issues !== undefined) {
1250
- throw new ValidationError(source, result.issues.map(flatten));
1251
- }
1252
- return result.value;
1253
- };
1254
- var fillWith = (draft, source, schema, value) => {
1255
- const result = schema["~standard"].validate(value);
1256
- if (result instanceof Promise) {
1257
- return result.then((settled) => {
1258
- draft[source] = accept(source, settled);
1259
- return draft;
1260
- });
1261
- }
1262
- draft[source] = accept(source, result);
1263
- return draft;
1264
- };
1265
- var bodyFill = (schema) => (draft) => {
1266
- const media = mediaTypeOf(draft.req);
1267
- const parse2 = parserFor(media);
1268
- if (parse2 === undefined) {
1269
- throw new HttpError(HttpStatusCode.UNSUPPORTED_MEDIA_TYPE, `Unsupported content type "${media}". Declared bodies accept ` + "application/json, application/x-www-form-urlencoded, multipart/form-data or text/*.");
1270
- }
1271
- const read = parse2 === asJson && RawBody.wanted(draft.req) ? draft.req.text().then((text) => {
1272
- RawBody.record(draft.req, text);
1273
- return JSON.parse(text);
1274
- }) : parse2(draft.req);
1275
- return read.then((value) => fillWith(draft, "body", schema, value), (error) => {
1276
- throw new HttpError(HttpStatusCode.BAD_REQUEST, `Malformed ${media} body`, { cause: error });
1277
- });
1278
- };
1279
- var searchOf = (url) => {
1280
- const start = url.indexOf("?");
1281
- if (start === -1)
1282
- return "";
1283
- const end = url.indexOf("#", start + 1);
1284
- return end === -1 ? url.slice(start + 1) : url.slice(start + 1, end);
1285
- };
1286
- var queryFill = (schema) => (draft) => {
1287
- const params = new URLSearchParams(searchOf(draft.req.url));
1288
- return fillWith(draft, "query", schema, grouped(params));
1289
- };
1290
- var paramsFill = (schema) => (draft) => fillWith(draft, "params", schema, draft.req.params);
1291
- var then = (first, second) => (draft) => {
1292
- const started = first(draft);
1293
- return started instanceof Promise ? started.then(second) : second(started);
1294
- };
1295
- var buildInputReader = (options) => {
1296
- const fills = [];
1297
- if (options?.body !== undefined)
1298
- fills.push(bodyFill(options.body));
1299
- if (options?.query !== undefined)
1300
- fills.push(queryFill(options.query));
1301
- if (options?.params !== undefined)
1302
- fills.push(paramsFill(options.params));
1303
- if (fills.length === 0)
1304
- return (req) => ({ req });
1305
- const fill = fills.reduce(then);
1306
- return (req) => fill({ req });
1307
- };
1308
-
1309
- // src/server/middleware.ts
1310
- var compose = (middleware, ctx, handler) => middleware.reduceRight((next, current) => (req) => current.handle(req, ctx, () => next(req)), handler);
1311
-
1312
- // src/server/routes.ts
1313
- var construct = (guard) => new guard;
1314
- var toResponse = (value, status) => {
1315
- if (value instanceof Response)
1316
- return value;
1317
- if (value === undefined || value === null) {
1318
- return new Response(null, { status: HttpStatusCode.NO_CONTENT });
1319
- }
1320
- return Response.json(value, { status });
1321
- };
1322
- var statusFor = (route) => route.options?.status ?? (route.method === "POST" ? HttpStatusCode.CREATED : HttpStatusCode.OK);
1323
- var assertNoCollisions = (discovered) => {
1324
- const owners = new Map;
1325
- for (const route of discovered) {
1326
- const key = `${route.method} ${route.path}`;
1327
- const owner = `${route.controller}.${route.handlerName}`;
1328
- const existing = owners.get(key);
1329
- if (existing !== undefined) {
1330
- throw new AppError6(`Route collision: ${key} is declared by ${existing} and by ${owner}. ` + "Bun would keep only one of them.");
1331
- }
1332
- owners.set(key, owner);
1333
- }
1334
- };
1335
- var assertNoGatewayCollisions = (discovered, gatewayPaths) => {
1336
- const gateways = new Set(gatewayPaths);
1337
- for (const route of discovered) {
1338
- if (gateways.has(route.path)) {
1339
- throw new AppError6(`Gateway path collision: ${route.path} is served by a gateway and by ` + `${route.controller}.${route.handlerName}(). The upgrade is a route too, ` + "so one of them would be dropped.");
1340
- }
1341
- }
1342
- };
1343
- var withUpgradeRoutes = (routes, gateways) => {
1344
- const merged = { ...routes };
1345
- for (const [path, upgrade] of gateways)
1346
- merged[path] = { GET: upgrade };
1347
- return merged;
1348
- };
1349
- var unmatchedContext = (req, isPublic) => Object.freeze({
1350
- controller: "(unmatched)",
1351
- handler: "(none)",
1352
- method: req.method,
1353
- path: new URL(req.url).pathname,
1354
- parsesBody: false,
1355
- get: (key) => {
1356
- if (key.id === UNMATCHED.id)
1357
- return true;
1358
- if (key.id === PUBLIC.id && isPublic)
1359
- return true;
1360
- return;
1361
- }
1362
- });
1363
- var buildFallback = (middleware = [], onError = defaultErrorMapper, cors, notFound = "guarded") => {
1364
- const miss = () => {
1365
- throw new HttpError(HttpStatusCode.NOT_FOUND, "NOT_FOUND");
1366
- };
1367
- const run = async (req) => {
1368
- try {
1369
- return await compose(middleware, unmatchedContext(req, notFound === "public"), miss)(req);
1370
- } catch (error) {
1371
- return RequestIds.stamp(onError(error, req), req);
1372
- }
1373
- };
1374
- return cors ? withCors(cors, run) : run;
1375
- };
1376
- var directOr = (guarded, route, read, status, onError, noMiddleware) => {
1377
- if (!noMiddleware)
1378
- return guarded;
1379
- const settle2 = (value, req) => {
1380
- try {
1381
- return toResponse(value, status);
1382
- } catch (error) {
1383
- return onError(error, req);
1384
- }
1385
- };
1386
- const invoke = (input, req) => {
1387
- try {
1388
- const value = route.handler(input);
1389
- return value instanceof Promise ? value.then((resolved) => settle2(resolved, req), (error) => onError(error, req)) : settle2(value, req);
1390
- } catch (error) {
1391
- return onError(error, req);
1392
- }
1393
- };
1394
- return (req) => {
1395
- try {
1396
- const input = read(req);
1397
- return input instanceof Promise ? input.then((resolved) => invoke(resolved, req), (error) => onError(error, req)) : invoke(input, req);
1398
- } catch (error) {
1399
- return onError(error, req);
1400
- }
1401
- };
1402
- };
1403
- var buildRoutes = (discovered, middleware = [], onError = defaultErrorMapper, cors, resolve = construct) => {
1404
- assertNoCollisions(discovered);
1405
- const routes = {};
1406
- const instances = new Map;
1407
- const guardOf = (guard, from) => {
1408
- const existing = instances.get(guard);
1409
- if (existing)
1410
- return existing;
1411
- const created = resolve(guard, from);
1412
- instances.set(guard, created);
1413
- return created;
1414
- };
1415
- for (const route of discovered) {
1416
- const read = buildInputReader(route.options);
1417
- const status = statusFor(route);
1418
- const chain = [
1419
- ...middleware,
1420
- ...(route.moduleMiddleware ?? []).map((entry) => guardOf(entry, route.module)),
1421
- ...(route.guards ?? []).map((guard) => guardOf(guard, route.module))
1422
- ];
1423
- const chained = compose(chain, buildContext(route), async (req) => toResponse(await route.handler(await read(req)), status));
1424
- const guarded = async (req) => {
1425
- try {
1426
- return await chained(req);
1427
- } catch (error) {
1428
- return RequestIds.stamp(onError(error, req), req);
1429
- }
1430
- };
1431
- const byMethod = routes[route.path] ??= {};
1432
- byMethod[route.method] = cors ? withCors(cors, guarded) : directOr(guarded, route, read, status, onError, chain.length === 0);
1433
- }
1434
- if (cors) {
1435
- for (const byMethod of Object.values(routes)) {
1436
- byMethod.OPTIONS = preflight(cors, Object.keys(byMethod));
1437
- }
1438
- }
1439
- return routes;
1440
- };
564
+ Object.defineProperty(RequestLoggingMiddleware, Symbol.for("dunx.deps"), { value: () => [Logger2, RequestContext2, { unresolved: "options: RequestLoggingOptions = {}", optional: true }] });
1441
565
 
1442
566
  // src/server/settings.ts
1443
567
  var defaultSettings = () => ({ "trust proxy": false });
@@ -1620,12 +744,10 @@ class HttpApplication {
1620
744
  #assertNotStarted(hook) {
1621
745
  if (!this.#started)
1622
746
  return;
1623
- throw new AppError7(`${hook} must be called before listen(). The route table and the middleware ` + "chain are folded into one closure per route when the server binds, so " + "this call could not take effect.");
747
+ throw new AppError3(`${hook} must be called before listen(). The route table and the middleware ` + "chain are folded into one closure per route when the server binds, so " + "this call could not take effect.");
1624
748
  }
1625
749
  }
1626
- Object.defineProperty(HttpApplication, Symbol.for("dunx.deps"), {
1627
- value: () => [{ unresolved: "app: App", typeOnly: "App" }, { unresolved: "discovered: readonly DiscoveredRoute[]" }, { unresolved: "options: HttpOptions" }, { unresolved: "root: ModuleRef", typeOnly: "ModuleRef" }, { unresolved: "websocket?: WebSocketRuntime", typeOnly: "WebSocketRuntime" }]
1628
- });
750
+ Object.defineProperty(HttpApplication, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "app: App", typeOnly: "App" }, { unresolved: "discovered: readonly DiscoveredRoute[]" }, { unresolved: "options: HttpOptions" }, { unresolved: "root: ModuleRef", typeOnly: "ModuleRef" }, { unresolved: "websocket?: WebSocketRuntime", typeOnly: "WebSocketRuntime" }] });
1629
751
 
1630
752
  // src/server/factory.ts
1631
753
  class HttpModule {
@@ -1655,14 +777,14 @@ class HttpFactory {
1655
777
  exports: providers.map((entry) => typeof entry === "function" ? entry : entry.token)
1656
778
  };
1657
779
  const app = await AppFactory.create(scope, options.overrides ? { overrides: options.overrides } : {});
1658
- const modules = collectModules2(scope);
780
+ const modules = collectModules(scope);
1659
781
  const discovered = [];
1660
782
  for (const module of modules) {
1661
783
  const moduleMiddleware = module.options.middleware ?? [];
1662
- for (const controller of readControllers2(module)) {
784
+ for (const controller of readControllers(module)) {
1663
785
  const routes = discoverRoutes(app.get(controller, module.ref));
1664
786
  if (routes.length === 0) {
1665
- throw new AppError8(`${controller.name} is registered as a controller but declares no routes. ` + "Add a @Get/@Post/... method, or move it to providers.");
787
+ throw new AppError4(`${controller.name} is registered as a controller but declares no routes. ` + "Add a @Get/@Post/... method, or move it to providers.");
1666
788
  }
1667
789
  discovered.push(...routes.map((route) => ({
1668
790
  ...route,
@@ -1689,29 +811,6 @@ class HttpFactory {
1689
811
  }
1690
812
  // src/static/files.ts
1691
813
  import { join, normalize, resolve } from "path";
1692
-
1693
- // src/static/options.ts
1694
- class StaticOptions {
1695
- root;
1696
- path;
1697
- maxAge;
1698
- immutable;
1699
- constructor(init) {
1700
- this.root = init.root;
1701
- this.path = normalizePrefix(init.path ?? "/");
1702
- this.maxAge = init.maxAge ?? 60;
1703
- this.immutable = init.immutable ?? (() => false);
1704
- }
1705
- }
1706
- Object.defineProperty(StaticOptions, Symbol.for("dunx.deps"), {
1707
- value: () => [{ unresolved: "init: StaticOptionsInit" }]
1708
- });
1709
- var normalizePrefix = (path) => {
1710
- const trimmed = path.split("/").filter(Boolean).join("/");
1711
- return trimmed === "" ? "/" : `/${trimmed}`;
1712
- };
1713
-
1714
- // src/static/files.ts
1715
814
  class StaticFiles {
1716
815
  #options;
1717
816
  #root;
@@ -1763,9 +862,7 @@ class StaticFiles {
1763
862
  });
1764
863
  }
1765
864
  }
1766
- Object.defineProperty(StaticFiles, Symbol.for("dunx.deps"), {
1767
- value: () => [StaticOptions]
1768
- });
865
+ Object.defineProperty(StaticFiles, Symbol.for("dunx.deps"), { value: () => [StaticOptions] });
1769
866
  // src/static/module.ts
1770
867
  import {
1771
868
  Module,
@@ -1810,6 +907,177 @@ StaticModule = __decorateElement(_init, 0, "StaticModule", _dec, StaticModule);
1810
907
  __runInitializers(_init, 1, StaticModule);
1811
908
  __decoratorMetadata(_init, StaticModule);
1812
909
  let _StaticModule = StaticModule;
910
+ // src/compression/compression.ts
911
+ var BODYLESS = new Set([204, 205, 304]);
912
+ var BUFFER_LIMIT = 1024 * 1024;
913
+ var buffer = async (body, limit) => {
914
+ const reader = body.getReader();
915
+ const chunks = [];
916
+ let size = 0;
917
+ for (;; ) {
918
+ const { done, value } = await reader.read();
919
+ if (done)
920
+ break;
921
+ chunks.push(value);
922
+ size += value.byteLength;
923
+ if (size > limit) {
924
+ return {
925
+ rest: new ReadableStream({
926
+ start: (controller) => {
927
+ for (const chunk of chunks)
928
+ controller.enqueue(chunk);
929
+ },
930
+ pull: async (controller) => {
931
+ const next = await reader.read();
932
+ if (next.done)
933
+ controller.close();
934
+ else
935
+ controller.enqueue(next.value);
936
+ },
937
+ cancel: (reason) => reader.cancel(reason)
938
+ })
939
+ };
940
+ }
941
+ }
942
+ const bytes = new Uint8Array(size);
943
+ let offset = 0;
944
+ for (const chunk of chunks) {
945
+ bytes.set(chunk, offset);
946
+ offset += chunk.byteLength;
947
+ }
948
+ return { bytes };
949
+ };
950
+ var declaredLength = (headers) => {
951
+ const raw = headers.get("content-length");
952
+ if (raw === null)
953
+ return;
954
+ const length = Number.parseInt(raw, 10);
955
+ return Number.isFinite(length) ? length : undefined;
956
+ };
957
+ var weakenETag = (headers) => {
958
+ const etag = headers.get("etag");
959
+ if (etag !== null && !etag.startsWith("W/"))
960
+ headers.set("etag", `W/${etag}`);
961
+ };
962
+ var varyOnEncoding = (headers) => {
963
+ const existing = headers.get("vary");
964
+ if (existing === null) {
965
+ headers.set("vary", "accept-encoding");
966
+ return;
967
+ }
968
+ if (existing.trim() === "*")
969
+ return;
970
+ const listed = existing.split(",").some((field) => field.trim().toLowerCase() === "accept-encoding");
971
+ if (!listed)
972
+ headers.set("vary", `${existing}, accept-encoding`);
973
+ };
974
+ var encodeSync = (encoding, data) => {
975
+ switch (encoding) {
976
+ case CompressionEncoding.ZSTD:
977
+ return Bun.zstdCompressSync(data);
978
+ case CompressionEncoding.GZIP:
979
+ return Bun.gzipSync(data);
980
+ }
981
+ };
982
+
983
+ class Compression {
984
+ #options;
985
+ constructor(options) {
986
+ this.#options = options;
987
+ }
988
+ #considers(res) {
989
+ if (BODYLESS.has(res.status) || res.status === 206)
990
+ return false;
991
+ if (res.headers.has("content-encoding"))
992
+ return false;
993
+ if (res.headers.get("cache-control")?.includes("no-transform") === true) {
994
+ return false;
995
+ }
996
+ return this.#options.filter(res.headers.get("content-type"));
997
+ }
998
+ async handle(req, _ctx, next) {
999
+ const res = await next();
1000
+ const body = res.body;
1001
+ if (body === null || !this.#considers(res))
1002
+ return res;
1003
+ varyOnEncoding(res.headers);
1004
+ const encoding = negotiate(req.headers.get("accept-encoding"), this.#options.encodings);
1005
+ if (encoding === undefined)
1006
+ return res;
1007
+ const declared = declaredLength(res.headers);
1008
+ if (declared !== undefined && declared < this.#options.threshold)
1009
+ return res;
1010
+ const headers = new Headers(res.headers);
1011
+ headers.set("content-encoding", encoding);
1012
+ weakenETag(headers);
1013
+ const source = declared !== undefined && declared > BUFFER_LIMIT ? { rest: body } : await buffer(body, BUFFER_LIMIT);
1014
+ if ("rest" in source) {
1015
+ headers.delete("content-length");
1016
+ return new Response(source.rest.pipeThrough(new CompressionStream(encoding)), { status: res.status, statusText: res.statusText, headers });
1017
+ }
1018
+ if (source.bytes.byteLength < this.#options.threshold) {
1019
+ const passthrough = new Headers(res.headers);
1020
+ passthrough.set("content-length", String(source.bytes.byteLength));
1021
+ return new Response(source.bytes, {
1022
+ status: res.status,
1023
+ statusText: res.statusText,
1024
+ headers: passthrough
1025
+ });
1026
+ }
1027
+ const encoded = encodeSync(encoding, source.bytes);
1028
+ headers.set("content-length", String(encoded.byteLength));
1029
+ return new Response(encoded, {
1030
+ status: res.status,
1031
+ statusText: res.statusText,
1032
+ headers
1033
+ });
1034
+ }
1035
+ }
1036
+ Object.defineProperty(Compression, Symbol.for("dunx.deps"), { value: () => [CompressionOptions] });
1037
+ // src/compression/module.ts
1038
+ import {
1039
+ Module as Module2,
1040
+ provide as provide3
1041
+ } from "@dunx/core";
1042
+ var middleware = () => provide3(Compression, {
1043
+ useFactory: (options) => new Compression(options),
1044
+ inject: [CompressionOptions]
1045
+ });
1046
+ var _dec = [
1047
+ Module2({})
1048
+ ];
1049
+ var _init = __decoratorStart(undefined);
1050
+
1051
+ class CompressionModule {
1052
+ static forRoot(init = {}) {
1053
+ return {
1054
+ module: CompressionModule,
1055
+ exports: [CompressionOptions, Compression],
1056
+ providers: [
1057
+ provide3(CompressionOptions, { useValue: new CompressionOptions(init) }),
1058
+ middleware()
1059
+ ]
1060
+ };
1061
+ }
1062
+ static forRootAsync(config) {
1063
+ return {
1064
+ module: CompressionModule,
1065
+ ...config.imports && { imports: config.imports },
1066
+ exports: [CompressionOptions, Compression],
1067
+ providers: [
1068
+ provide3(CompressionOptions, {
1069
+ useFactory: async (...deps) => new CompressionOptions(await config.useFactory(...deps)),
1070
+ inject: config.inject ?? []
1071
+ }),
1072
+ middleware()
1073
+ ]
1074
+ };
1075
+ }
1076
+ }
1077
+ CompressionModule = __decorateElement(_init, 0, "CompressionModule", _dec, CompressionModule);
1078
+ __runInitializers(_init, 1, CompressionModule);
1079
+ __decoratorMetadata(_init, CompressionModule);
1080
+ let _CompressionModule = CompressionModule;
1813
1081
  // src/throttle/decorators.ts
1814
1082
  var THROTTLE = metaKey("throttle");
1815
1083
  var SKIP_THROTTLE = metaKey("skip-throttle");
@@ -1819,7 +1087,7 @@ var SkipThrottle = () => meta(SKIP_THROTTLE, true);
1819
1087
  import { Logger as Logger5 } from "@dunx/core";
1820
1088
 
1821
1089
  // src/throttle/options.ts
1822
- import { AppError as AppError9 } from "@dunx/core";
1090
+ import { AppError as AppError5 } from "@dunx/core";
1823
1091
 
1824
1092
  class ThrottleOptions {
1825
1093
  limit;
@@ -1830,13 +1098,13 @@ class ThrottleOptions {
1830
1098
  store;
1831
1099
  constructor(init) {
1832
1100
  if (init.prefix.trim() === "") {
1833
- throw new AppError9("ThrottleModule needs a prefix naming this application, and it has no " + "default: two apps sharing one Redis with one throttle namespace each " + "spend the other's budget. Pass something like { prefix: 'orders-api' }.");
1101
+ throw new AppError5("ThrottleModule needs a prefix naming this application, and it has no " + "default: two apps sharing one Redis with one throttle namespace each " + "spend the other's budget. Pass something like { prefix: 'orders-api' }.");
1834
1102
  }
1835
1103
  if (!Number.isInteger(init.limit) || init.limit < 1) {
1836
- throw new AppError9(`ThrottleModule needs a limit of at least 1; got ${init.limit}.`);
1104
+ throw new AppError5(`ThrottleModule needs a limit of at least 1; got ${init.limit}.`);
1837
1105
  }
1838
1106
  if (!Number.isInteger(init.windowSeconds) || init.windowSeconds < 1) {
1839
- throw new AppError9("ThrottleModule needs a windowSeconds of at least 1; got " + `${init.windowSeconds}.`);
1107
+ throw new AppError5("ThrottleModule needs a windowSeconds of at least 1; got " + `${init.windowSeconds}.`);
1840
1108
  }
1841
1109
  this.limit = init.limit;
1842
1110
  this.windowSeconds = init.windowSeconds;
@@ -1846,17 +1114,15 @@ class ThrottleOptions {
1846
1114
  this.store = init.store;
1847
1115
  }
1848
1116
  }
1849
- Object.defineProperty(ThrottleOptions, Symbol.for("dunx.deps"), {
1850
- value: () => [{ unresolved: "init: ThrottleOptionsInit" }]
1851
- });
1117
+ Object.defineProperty(ThrottleOptions, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "init: ThrottleOptionsInit" }] });
1852
1118
 
1853
1119
  // src/throttle/store.ts
1854
- import { AppError as AppError10 } from "@dunx/core";
1120
+ import { AppError as AppError6 } from "@dunx/core";
1855
1121
 
1856
1122
  class ThrottleStore {
1857
1123
  constructor() {
1858
1124
  if (new.target === ThrottleStore) {
1859
- throw new AppError10("ThrottleStore is a contract, not an implementation. Bind one with " + "ThrottleModule.forRoot({ store: new RedisThrottleStore(redis) }), or " + "leave it out for the in-process MemoryThrottleStore.");
1125
+ throw new AppError6("ThrottleStore is a contract, not an implementation. Bind one with " + "ThrottleModule.forRoot({ store: new RedisThrottleStore(redis) }), or " + "leave it out for the in-process MemoryThrottleStore.");
1860
1126
  }
1861
1127
  }
1862
1128
  }
@@ -1878,9 +1144,7 @@ class RedisThrottleStore extends ThrottleStore {
1878
1144
  return left > 0 ? left : undefined;
1879
1145
  }
1880
1146
  }
1881
- Object.defineProperty(RedisThrottleStore, Symbol.for("dunx.deps"), {
1882
- value: () => [{ unresolved: "private readonly redis: ThrottleRedis" }]
1883
- });
1147
+ Object.defineProperty(RedisThrottleStore, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "private readonly redis: ThrottleRedis" }] });
1884
1148
 
1885
1149
  class MemoryThrottleStore extends ThrottleStore {
1886
1150
  #windows = new Map;
@@ -1917,9 +1181,7 @@ class MemoryThrottleStore extends ThrottleStore {
1917
1181
  this.#windows.clear();
1918
1182
  }
1919
1183
  }
1920
- Object.defineProperty(MemoryThrottleStore, Symbol.for("dunx.deps"), {
1921
- value: () => [{ unresolved: "maxKeys = 10_000" }]
1922
- });
1184
+ Object.defineProperty(MemoryThrottleStore, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "maxKeys = 10_000", optional: true }] });
1923
1185
 
1924
1186
  // src/throttle/guard.ts
1925
1187
  class ThrottleGuard {
@@ -1989,26 +1251,24 @@ class ThrottleGuard {
1989
1251
  this.logger.warn("The rate limiter is unreachable, so requests are not being counted.", { reason: error.message });
1990
1252
  }
1991
1253
  }
1992
- Object.defineProperty(ThrottleGuard, Symbol.for("dunx.deps"), {
1993
- value: () => [ThrottleOptions, ThrottleStore, ClientAddress, Logger5]
1994
- });
1254
+ Object.defineProperty(ThrottleGuard, Symbol.for("dunx.deps"), { value: () => [ThrottleOptions, ThrottleStore, ClientAddress, Logger5] });
1995
1255
  // src/throttle/module.ts
1996
1256
  import {
1997
1257
  Logger as Logger6,
1998
- Module as Module2,
1999
- provide as provide3
1258
+ Module as Module3,
1259
+ provide as provide4
2000
1260
  } from "@dunx/core";
2001
1261
  var EXPORTS = [ThrottleOptions, ThrottleStore, ThrottleGuard];
2002
- var guard = () => provide3(ThrottleGuard, {
1262
+ var guard = () => provide4(ThrottleGuard, {
2003
1263
  useFactory: (options, store, address, logger) => new ThrottleGuard(options, store, address, logger),
2004
1264
  inject: [ThrottleOptions, ThrottleStore, ClientAddress, Logger6]
2005
1265
  });
2006
- var store = () => provide3(ThrottleStore, {
1266
+ var store = () => provide4(ThrottleStore, {
2007
1267
  useFactory: (options) => options.store ?? new MemoryThrottleStore,
2008
1268
  inject: [ThrottleOptions]
2009
1269
  });
2010
1270
  var _dec = [
2011
- Module2({})
1271
+ Module3({})
2012
1272
  ];
2013
1273
  var _init = __decoratorStart(undefined);
2014
1274
 
@@ -2019,7 +1279,7 @@ class ThrottleModule {
2019
1279
  global: true,
2020
1280
  exports: EXPORTS,
2021
1281
  providers: [
2022
- provide3(ThrottleOptions, { useValue: new ThrottleOptions(init) }),
1282
+ provide4(ThrottleOptions, { useValue: new ThrottleOptions(init) }),
2023
1283
  store(),
2024
1284
  guard()
2025
1285
  ]
@@ -2032,7 +1292,7 @@ class ThrottleModule {
2032
1292
  ...config.imports && { imports: config.imports },
2033
1293
  exports: EXPORTS,
2034
1294
  providers: [
2035
- provide3(ThrottleOptions, {
1295
+ provide4(ThrottleOptions, {
2036
1296
  useFactory: async (...deps) => new ThrottleOptions(await config.useFactory(...deps)),
2037
1297
  inject: config.inject ?? []
2038
1298
  }),
@@ -2065,99 +1325,6 @@ var OnMessage = (event) => (value) => {
2065
1325
  markHandler(value, { kind: HandlerKind.MESSAGE, event });
2066
1326
  return value;
2067
1327
  };
2068
- // src/ws/redis-relay.ts
2069
- import { AppError as AppError11 } from "@dunx/core";
2070
- var PROTOCOLS = [
2071
- "redis:",
2072
- "rediss:",
2073
- "valkey:",
2074
- "valkeys:",
2075
- "redis+tls:",
2076
- "redis+unix:",
2077
- "redis+tls+unix:"
2078
- ];
2079
- var defaultRelayUrl = () => process.env["VALKEY_URL"] ?? process.env["REDIS_URL"] ?? "redis://localhost:6379";
2080
- var assertUrl = (url) => {
2081
- let parsed;
2082
- try {
2083
- parsed = new URL(url);
2084
- } catch {
2085
- throw new AppError11(`${JSON.stringify(url)} is not a valid URL for the websocket relay. ` + "Expected something like redis://localhost:6379.");
2086
- }
2087
- if (!PROTOCOLS.includes(parsed.protocol)) {
2088
- throw new AppError11(`Unsupported protocol ${JSON.stringify(parsed.protocol)} in ` + `${JSON.stringify(url)}. Expected one of ${PROTOCOLS.join(", ")}.`);
2089
- }
2090
- return url;
2091
- };
2092
-
2093
- class RedisRelay {
2094
- #url;
2095
- #options;
2096
- #pub;
2097
- #sub;
2098
- #channel;
2099
- constructor(options = {}) {
2100
- this.#url = assertUrl(options.url ?? defaultRelayUrl());
2101
- this.#options = {
2102
- maxRetries: options.maxRetries ?? 0,
2103
- ...options.connectionTimeout !== undefined && {
2104
- connectionTimeout: options.connectionTimeout
2105
- },
2106
- ...options.tls !== undefined && { tls: options.tls }
2107
- };
2108
- }
2109
- get url() {
2110
- const parsed = new URL(this.#url);
2111
- if (parsed.password)
2112
- parsed.password = "***";
2113
- return parsed.toString();
2114
- }
2115
- async publish(channel, message) {
2116
- const client = this.#pub ??= new Bun.RedisClient(this.#url, this.#options);
2117
- try {
2118
- return await client.publish(channel, message);
2119
- } catch (error) {
2120
- if (this.#pub === client) {
2121
- this.#pub = undefined;
2122
- client.close();
2123
- }
2124
- throw error;
2125
- }
2126
- }
2127
- async subscribe(channel, listener) {
2128
- const client = this.#sub ??= new Bun.RedisClient(this.#url, this.#options);
2129
- try {
2130
- await client.connect();
2131
- await client.subscribe(channel, listener);
2132
- this.#channel = channel;
2133
- } catch (error) {
2134
- if (this.#sub === client) {
2135
- this.#sub = undefined;
2136
- client.close();
2137
- }
2138
- throw error;
2139
- }
2140
- }
2141
- async close() {
2142
- const sub = this.#sub;
2143
- const channel = this.#channel;
2144
- this.#pub?.close();
2145
- this.#pub = undefined;
2146
- this.#sub = undefined;
2147
- this.#channel = undefined;
2148
- if (!sub)
2149
- return;
2150
- if (channel !== undefined) {
2151
- try {
2152
- await sub.unsubscribe(channel);
2153
- } catch {}
2154
- }
2155
- sub.close();
2156
- }
2157
- }
2158
- Object.defineProperty(RedisRelay, Symbol.for("dunx.deps"), {
2159
- value: () => [{ unresolved: "options: RedisRelayOptions = {}" }]
2160
- });
2161
1328
  // src/health/contracts.ts
2162
1329
  class HealthIndicator {
2163
1330
  critical = true;
@@ -2168,205 +1335,6 @@ class PingProbe {
2168
1335
 
2169
1336
  class QueryProbe {
2170
1337
  }
2171
- // src/health/controller.ts
2172
- import { inject } from "@dunx/core";
2173
-
2174
- // src/health/report-schema.ts
2175
- var state = {
2176
- type: "string",
2177
- enum: ["up", "down", "unknown"],
2178
- description: "`unknown` is not `down`: a probe that timed out has told you nothing."
2179
- };
2180
- var HEALTH_REPORT_SCHEMA = Object.freeze({
2181
- $id: "HealthReport",
2182
- type: "object",
2183
- description: "What the probe found. `up` answers 200 and anything else answers 503.",
2184
- properties: {
2185
- status: state,
2186
- draining: {
2187
- type: "boolean",
2188
- description: "The process is shutting down, or something holds it out."
2189
- },
2190
- uptimeMs: {
2191
- type: "integer",
2192
- description: "Measured on a monotonic clock, so it never goes backwards."
2193
- },
2194
- checks: {
2195
- type: "array",
2196
- items: {
2197
- type: "object",
2198
- properties: {
2199
- name: { type: "string" },
2200
- state,
2201
- critical: {
2202
- type: "boolean",
2203
- description: "A failure here sheds traffic. Memory and disk do not."
2204
- },
2205
- ms: { type: "integer", description: "How long the check took." },
2206
- detail: {
2207
- type: "string",
2208
- description: "A latency, a version, or a failure message."
2209
- }
2210
- },
2211
- required: ["name", "state", "critical", "ms"]
2212
- }
2213
- }
2214
- },
2215
- required: ["status", "draining", "uptimeMs", "checks"]
2216
- });
2217
-
2218
- // src/health/registry.ts
2219
- var bounded = async (indicator, timeoutMs) => {
2220
- let timer;
2221
- const timeout = new Promise((resolve2) => {
2222
- timer = setTimeout(() => resolve2({ state: "unknown", detail: `no answer in ${timeoutMs} ms` }), timeoutMs);
2223
- timer.unref?.();
2224
- });
2225
- try {
2226
- return await Promise.race([
2227
- Promise.resolve().then(() => indicator.check()).catch((error) => ({
2228
- state: "down",
2229
- detail: error instanceof Error ? error.message : String(error)
2230
- })),
2231
- timeout
2232
- ]);
2233
- } finally {
2234
- if (timer)
2235
- clearTimeout(timer);
2236
- }
2237
- };
2238
- var worst = (checks) => {
2239
- const critical = checks.filter((check) => check.critical);
2240
- if (critical.some((check) => check.state === "down"))
2241
- return "down";
2242
- if (critical.some((check) => check.state === "unknown"))
2243
- return "unknown";
2244
- return "up";
2245
- };
2246
-
2247
- class HealthOptions {
2248
- liveness;
2249
- readiness;
2250
- timeoutMs;
2251
- routes;
2252
- documented;
2253
- drainDelayMs;
2254
- constructor(init = {}) {
2255
- this.liveness = init.liveness ?? [];
2256
- this.readiness = init.readiness ?? [];
2257
- this.timeoutMs = init.timeoutMs ?? 2000;
2258
- this.routes = init.routes ?? true;
2259
- this.documented = init.documented ?? true;
2260
- this.drainDelayMs = Math.max(0, init.drainDelayMs ?? 0);
2261
- }
2262
- }
2263
- Object.defineProperty(HealthOptions, Symbol.for("dunx.deps"), {
2264
- value: () => [{ unresolved: "init: HealthOptionsInit = {}" }]
2265
- });
2266
-
2267
- class HealthRegistry {
2268
- options;
2269
- readiness_;
2270
- #startedAt = performance.now();
2271
- constructor(options, readiness_) {
2272
- this.options = options;
2273
- this.readiness_ = readiness_;
2274
- }
2275
- async report(indicators) {
2276
- const checks = await Promise.all(indicators.map(async (indicator) => {
2277
- const started = performance.now();
2278
- const result = await bounded(indicator, this.options.timeoutMs);
2279
- return {
2280
- name: indicator.name,
2281
- state: result.state,
2282
- critical: indicator.critical,
2283
- ms: Math.round(performance.now() - started),
2284
- ...result.detail === undefined ? {} : { detail: result.detail }
2285
- };
2286
- }));
2287
- return {
2288
- status: worst(checks),
2289
- draining: this.readiness_.draining,
2290
- uptimeMs: Math.round(performance.now() - this.#startedAt),
2291
- checks
2292
- };
2293
- }
2294
- liveness() {
2295
- return this.report(this.options.liveness);
2296
- }
2297
- async readiness() {
2298
- const report = await this.report(this.options.readiness);
2299
- if (!this.readiness_.draining)
2300
- return report;
2301
- return {
2302
- ...report,
2303
- status: "down",
2304
- checks: [
2305
- {
2306
- name: "readiness",
2307
- state: "down",
2308
- critical: true,
2309
- ms: 0,
2310
- detail: this.readiness_.reason ?? "not accepting traffic"
2311
- },
2312
- ...report.checks
2313
- ]
2314
- };
2315
- }
2316
- }
2317
- Object.defineProperty(HealthRegistry, Symbol.for("dunx.deps"), {
2318
- value: () => [HealthOptions, { unresolved: "private readonly readiness_: Readiness", typeOnly: "Readiness" }]
2319
- });
2320
-
2321
- // src/health/controller.ts
2322
- var probeResponses = {
2323
- response: { 200: HEALTH_REPORT_SCHEMA, 503: HEALTH_REPORT_SCHEMA }
2324
- };
2325
- var answer = (report) => Response.json(report, { status: report.status === "up" ? 200 : 503 });
2326
- var _dec = [
2327
- Controller("health")
2328
- ];
2329
- var _dec2 = [
2330
- Public(),
2331
- Get("/live", probeResponses)
2332
- ];
2333
- var _dec3 = [
2334
- Public(),
2335
- Get("/ready", probeResponses)
2336
- ];
2337
- var _health = new WeakMap;
2338
- var _init = __decoratorStart(undefined);
2339
-
2340
- class HealthController {
2341
- constructor() {
2342
- __privateAdd(this, _health, inject(HealthRegistry));
2343
- __runInitializers(_init, 5, this);
2344
- }
2345
- async live() {
2346
- return answer(await __privateGet(this, _health).liveness());
2347
- }
2348
- async ready() {
2349
- return answer(await __privateGet(this, _health).readiness());
2350
- }
2351
- }
2352
- __decorateElement(_init, 1, "live", _dec2, HealthController);
2353
- __decorateElement(_init, 1, "ready", _dec3, HealthController);
2354
- HealthController = __decorateElement(_init, 0, "HealthController", _dec, HealthController);
2355
- __runInitializers(_init, 1, HealthController);
2356
- __decoratorMetadata(_init, HealthController);
2357
- let _HealthController = HealthController;
2358
- var _dec = [
2359
- ApiHidden()
2360
- ];
2361
- var _base = HealthController;
2362
- var _init = __decoratorStart(_base);
2363
-
2364
- class HiddenHealthController extends _base {
2365
- }
2366
- HiddenHealthController = __decorateElement(_init, 0, "HiddenHealthController", _dec, HiddenHealthController);
2367
- __runInitializers(_init, 1, HiddenHealthController);
2368
- __decoratorMetadata(_init, HiddenHealthController);
2369
- let _HiddenHealthController = HiddenHealthController;
2370
1338
  // src/health/indicators.ts
2371
1339
  import { statfs } from "fs/promises";
2372
1340
  var ms = (started) => Math.round(performance.now() - started);
@@ -2384,9 +1352,7 @@ class RedisIndicator extends HealthIndicator {
2384
1352
  return { state: "up", detail: `${ms(started)} ms` };
2385
1353
  }
2386
1354
  }
2387
- Object.defineProperty(RedisIndicator, Symbol.for("dunx.deps"), {
2388
- value: () => [{ unresolved: "private readonly redis: PingProbe", typeOnly: "PingProbe" }]
2389
- });
1355
+ Object.defineProperty(RedisIndicator, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "private readonly redis: PingProbe", typeOnly: "PingProbe" }] });
2390
1356
 
2391
1357
  class DatabaseIndicator extends HealthIndicator {
2392
1358
  db;
@@ -2401,9 +1367,7 @@ class DatabaseIndicator extends HealthIndicator {
2401
1367
  return { state: "up", detail: `${ms(started)} ms` };
2402
1368
  }
2403
1369
  }
2404
- Object.defineProperty(DatabaseIndicator, Symbol.for("dunx.deps"), {
2405
- value: () => [{ unresolved: "private readonly db: QueryProbe", typeOnly: "QueryProbe" }]
2406
- });
1370
+ Object.defineProperty(DatabaseIndicator, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "private readonly db: QueryProbe", typeOnly: "QueryProbe" }] });
2407
1371
 
2408
1372
  class MemoryOptions {
2409
1373
  maxRssBytes;
@@ -2411,9 +1375,7 @@ class MemoryOptions {
2411
1375
  this.maxRssBytes = init.maxRssBytes;
2412
1376
  }
2413
1377
  }
2414
- Object.defineProperty(MemoryOptions, Symbol.for("dunx.deps"), {
2415
- value: () => [{ unresolved: "init: MemoryOptionsInit" }]
2416
- });
1378
+ Object.defineProperty(MemoryOptions, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "init: MemoryOptionsInit" }] });
2417
1379
  var MIB = 1024 * 1024;
2418
1380
  var mib = (bytes) => `${Math.round(bytes / MIB)} MiB`;
2419
1381
 
@@ -2431,9 +1393,7 @@ class MemoryIndicator extends HealthIndicator {
2431
1393
  return rss > this.options.maxRssBytes ? { state: "down", detail } : { state: "up", detail };
2432
1394
  }
2433
1395
  }
2434
- Object.defineProperty(MemoryIndicator, Symbol.for("dunx.deps"), {
2435
- value: () => [MemoryOptions]
2436
- });
1396
+ Object.defineProperty(MemoryIndicator, Symbol.for("dunx.deps"), { value: () => [MemoryOptions] });
2437
1397
 
2438
1398
  class DiskOptions {
2439
1399
  path;
@@ -2443,9 +1403,7 @@ class DiskOptions {
2443
1403
  this.maxUsedFraction = init.maxUsedFraction;
2444
1404
  }
2445
1405
  }
2446
- Object.defineProperty(DiskOptions, Symbol.for("dunx.deps"), {
2447
- value: () => [{ unresolved: "init: DiskOptionsInit" }]
2448
- });
1406
+ Object.defineProperty(DiskOptions, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "init: DiskOptionsInit" }] });
2449
1407
 
2450
1408
  class DiskIndicator extends HealthIndicator {
2451
1409
  options;
@@ -2466,13 +1424,11 @@ class DiskIndicator extends HealthIndicator {
2466
1424
  return used > this.options.maxUsedFraction ? { state: "down", detail } : { state: "up", detail };
2467
1425
  }
2468
1426
  }
2469
- Object.defineProperty(DiskIndicator, Symbol.for("dunx.deps"), {
2470
- value: () => [DiskOptions]
2471
- });
1427
+ Object.defineProperty(DiskIndicator, Symbol.for("dunx.deps"), { value: () => [DiskOptions] });
2472
1428
  // src/health/module.ts
2473
1429
  import {
2474
- Module as Module3,
2475
- provide as provide4
1430
+ Module as Module4,
1431
+ provide as provide5
2476
1432
  } from "@dunx/core";
2477
1433
 
2478
1434
  // src/health/readiness.ts
@@ -2482,9 +1438,7 @@ class ReadinessOptions {
2482
1438
  this.drainDelayMs = Math.max(0, init.drainDelayMs ?? 0);
2483
1439
  }
2484
1440
  }
2485
- Object.defineProperty(ReadinessOptions, Symbol.for("dunx.deps"), {
2486
- value: () => [{ unresolved: "init: ReadinessOptionsInit = {}" }]
2487
- });
1441
+ Object.defineProperty(ReadinessOptions, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "init: ReadinessOptionsInit = {}", optional: true }] });
2488
1442
 
2489
1443
  class Readiness {
2490
1444
  options;
@@ -2512,22 +1466,20 @@ class Readiness {
2512
1466
  }
2513
1467
  }
2514
1468
  }
2515
- Object.defineProperty(Readiness, Symbol.for("dunx.deps"), {
2516
- value: () => [ReadinessOptions]
2517
- });
1469
+ Object.defineProperty(Readiness, Symbol.for("dunx.deps"), { value: () => [ReadinessOptions] });
2518
1470
 
2519
1471
  // src/health/module.ts
2520
1472
  var wiring = (options) => [
2521
1473
  ...options,
2522
- provide4(ReadinessOptions, {
1474
+ provide5(ReadinessOptions, {
2523
1475
  useFactory: (opts) => new ReadinessOptions({ drainDelayMs: opts.drainDelayMs }),
2524
1476
  inject: [HealthOptions]
2525
1477
  }),
2526
- provide4(Readiness, {
1478
+ provide5(Readiness, {
2527
1479
  useFactory: (opts) => new Readiness(opts),
2528
1480
  inject: [ReadinessOptions]
2529
1481
  }),
2530
- provide4(HealthRegistry, {
1482
+ provide5(HealthRegistry, {
2531
1483
  useFactory: (opts, readiness) => new HealthRegistry(opts, readiness),
2532
1484
  inject: [HealthOptions, Readiness]
2533
1485
  })
@@ -2535,7 +1487,7 @@ var wiring = (options) => [
2535
1487
  var surface = [HealthOptions, HealthRegistry, Readiness];
2536
1488
  var controllerFor = (documented) => documented ? HealthController : HiddenHealthController;
2537
1489
  var _dec = [
2538
- Module3({})
1490
+ Module4({})
2539
1491
  ];
2540
1492
  var _init = __decoratorStart(undefined);
2541
1493
 
@@ -2546,7 +1498,7 @@ class HealthModule {
2546
1498
  module: HealthModule,
2547
1499
  ...options.routes ? { controllers: [controllerFor(options.documented)] } : {},
2548
1500
  exports: surface,
2549
- providers: wiring([provide4(HealthOptions, { useValue: options })])
1501
+ providers: wiring([provide5(HealthOptions, { useValue: options })])
2550
1502
  };
2551
1503
  }
2552
1504
  static forRootAsync(config) {
@@ -2556,7 +1508,7 @@ class HealthModule {
2556
1508
  ...config.routes ?? true ? { controllers: [controllerFor(config.documented ?? true)] } : {},
2557
1509
  exports: surface,
2558
1510
  providers: wiring([
2559
- provide4(HealthOptions, {
1511
+ provide5(HealthOptions, {
2560
1512
  useFactory: async (...deps) => new HealthOptions(await config.useFactory(...deps)),
2561
1513
  inject: config.inject ?? []
2562
1514
  })
@@ -2571,6 +1523,10 @@ let _HealthModule = HealthModule;
2571
1523
  export {
2572
1524
  ApiHidden,
2573
1525
  ClientAddress,
1526
+ Compression,
1527
+ CompressionEncoding,
1528
+ CompressionModule,
1529
+ CompressionOptions,
2574
1530
  Controller,
2575
1531
  DEFAULT_RELAY_CHANNEL,
2576
1532
  DatabaseIndicator,
@@ -2626,11 +1582,14 @@ export {
2626
1582
  StaticModule,
2627
1583
  StaticOptions,
2628
1584
  THROTTLE,
1585
+ TRACEPARENT_HEADER,
1586
+ TRACESTATE_HEADER,
2629
1587
  Throttle,
2630
1588
  ThrottleGuard,
2631
1589
  ThrottleModule,
2632
1590
  ThrottleOptions,
2633
1591
  ThrottleStore,
1592
+ TraceContext,
2634
1593
  UNMATCHED,
2635
1594
  UseGuards,
2636
1595
  ValidationError,
@@ -2647,6 +1606,7 @@ export {
2647
1606
  decodeRelay,
2648
1607
  defaultErrorMapper,
2649
1608
  defaultRelayUrl,
1609
+ defaultStatusFor,
2650
1610
  discoverGateway,
2651
1611
  discoverGateways,
2652
1612
  discoverRoutes,
@@ -2655,6 +1615,7 @@ export {
2655
1615
  errorMapper,
2656
1616
  gatewaysOf,
2657
1617
  guardsOf,
1618
+ isCompressibleType,
2658
1619
  isErrorFilter,
2659
1620
  isGateway,
2660
1621
  joinPath,
@@ -2662,6 +1623,7 @@ export {
2662
1623
  meta,
2663
1624
  metaKey,
2664
1625
  metaOf,
1626
+ negotiate,
2665
1627
  normalizePath,
2666
1628
  normalizePrefix,
2667
1629
  observe,
@@ -2671,6 +1633,3 @@ export {
2671
1633
  withCors,
2672
1634
  withUpgradeRoutes
2673
1635
  };
2674
-
2675
- //# debugId=1A8278039B82BC0664756E2164756E21
2676
- //# sourceMappingURL=index.js.map