@dunx/http 2.5.0 → 3.0.1

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.
@@ -0,0 +1,36 @@
1
+ /**
2
+ * What the framework calls on itself, and the only place it is exported from.
3
+ *
4
+ * The barrel was a semver promise 173 symbols wide, which is more than this
5
+ * package meant to make. What stays public there is the surface an app writes
6
+ * against:
7
+ * decorators, options, contracts, errors, modules and the metadata helpers a
8
+ * user's own guard reads. What is here is route-table construction, the
9
+ * middleware fold, the relay codec and the discovery readers - things
10
+ * `@dunx/dashboard`, `@dunx/mcp` and `@dunx/openapi` need and an app does not.
11
+ *
12
+ * No stability promise attaches to this subpath.
13
+ */
14
+ export { discoverRoutes, joinPath, type DiscoveredRoute, } from './route/discover.js';
15
+ export { defaultStatusFor, type DefaultStatus, type RouteMeta, } from './route/marker.js';
16
+ export { guardsOf } from './route/metadata.js';
17
+ export { gatewaysOf, routesOf, type GatewayHandler, type GatewayNode, type RouteInputs, type RouteNode, } from './inspect.js';
18
+ export { buildContext } from './server/context.js';
19
+ export { preflight, withCors } from './server/cors.js';
20
+ export { isErrorFilter, toErrorMapper } from './server/errors.js';
21
+ export { compose } from './server/middleware.js';
22
+ export { assertNoCollisions, assertNoGatewayCollisions, buildRoutes, withUpgradeRoutes, type BunRoutes, type GuardResolver, type RouteMethod, type ServeRoutes, } from './server/routes.js';
23
+ export { normalizePrefix } from './static/options.js';
24
+ export { negotiate } from './compression/negotiate.js';
25
+ export { isCompressibleType } from './compression/options.js';
26
+ export { buildWebSocket, type UpgradeHandler, type WebSocketRuntime, } from './ws/adapter.js';
27
+ export { discoverGateway, discoverGateways, normalizePath, type DiscoveredGateway, type DiscoveredHandler, type Invoke, } from './ws/discover.js';
28
+ export { decode, encode } from './ws/envelope.js';
29
+ export { composeSocket, observe } from './ws/middleware.js';
30
+ export { HandlerKind, isGateway, type HandlerMeta } from './ws/marker.js';
31
+ export { defaultRelayUrl } from './ws/redis-relay.js';
32
+ export { decodeRelay, encodeRelay, type RelayFrame, type RelayPhase, } from './ws/relay.js';
33
+ export { buildGateways, buildRuntime, type GatewayRuntime, } from './ws/runtime.js';
34
+ export { HiddenHealthController } from './health/controller.js';
35
+ export { backoffDelay, executeWithRetry, isRetryableStatus, retryAfterMs, } from './client/retry.js';
36
+ export { isJsonBody, isPlainObject, safeStringify } from './client/json.js';
@@ -0,0 +1,154 @@
1
+ // @bun
2
+ import {
3
+ HIDDEN,
4
+ HandlerKind,
5
+ HiddenHealthController,
6
+ PUBLIC,
7
+ ROLES,
8
+ assertNoCollisions,
9
+ assertNoGatewayCollisions,
10
+ buildContext,
11
+ buildGateways,
12
+ buildRoutes,
13
+ buildRuntime,
14
+ buildWebSocket,
15
+ compose,
16
+ composeSocket,
17
+ decode,
18
+ decodeRelay,
19
+ defaultRelayUrl,
20
+ defaultStatusFor,
21
+ discoverGateway,
22
+ discoverGateways,
23
+ discoverRoutes,
24
+ encode,
25
+ encodeRelay,
26
+ guardsOf,
27
+ isCompressibleType,
28
+ isErrorFilter,
29
+ isGateway,
30
+ joinPath,
31
+ negotiate,
32
+ normalizePath,
33
+ normalizePrefix,
34
+ observe,
35
+ preflight,
36
+ toErrorMapper,
37
+ withCors,
38
+ withUpgradeRoutes
39
+ } from "./chunk-f6pw36av.js";
40
+ import {
41
+ backoffDelay,
42
+ executeWithRetry,
43
+ isJsonBody,
44
+ isPlainObject,
45
+ isRetryableStatus,
46
+ retryAfterMs,
47
+ safeStringify
48
+ } from "./chunk-b699cfes.js";
49
+ import"./chunk-sz4pvqxy.js";
50
+ // src/inspect.ts
51
+ import {
52
+ collectModules,
53
+ dependenciesOf,
54
+ readControllers
55
+ } from "@dunx/core";
56
+ var vendorOf = (schema) => schema?.["~standard"]?.vendor;
57
+ var validatesIn = (options) => {
58
+ const body = vendorOf(options?.body);
59
+ const query = vendorOf(options?.query);
60
+ const params = vendorOf(options?.params);
61
+ return {
62
+ ...body === undefined ? {} : { body },
63
+ ...query === undefined ? {} : { query },
64
+ ...params === undefined ? {} : { params }
65
+ };
66
+ };
67
+ var rolesIn = (route) => {
68
+ const roles = route.meta?.get(ROLES.id);
69
+ if (roles === undefined || roles === null)
70
+ return null;
71
+ return (Array.isArray(roles) ? roles : [roles]).map(String);
72
+ };
73
+ var nodeFor = (route, module) => ({
74
+ method: route.method,
75
+ path: route.path,
76
+ controller: route.controller,
77
+ handler: route.handlerName,
78
+ module,
79
+ public: route.meta?.get(PUBLIC.id) === true,
80
+ roles: rolesIn(route),
81
+ guards: (route.guards ?? []).map((guard) => guard.name),
82
+ hidden: route.meta?.get(HIDDEN.id) === true,
83
+ validates: validatesIn(route.options),
84
+ status: route.options?.status ?? null,
85
+ responses: Object.keys(route.options?.response ?? {}).map(Number)
86
+ });
87
+ var routesOf = (root) => collectModules(root).flatMap((module) => readControllers(module).flatMap((controller) => {
88
+ const { prototype } = controller;
89
+ return discoverRoutes(Object.create(prototype)).map((route) => nodeFor(route, module.name));
90
+ }));
91
+ var gatewayFor = (ctor, module) => {
92
+ const { name, path, handlers } = discoverGateway(Object.create(ctor.prototype));
93
+ return {
94
+ name,
95
+ path,
96
+ module,
97
+ dependencies: dependenciesOf(ctor),
98
+ handlers: handlers.map((handler) => ({
99
+ kind: handler.kind,
100
+ event: handler.event ?? null,
101
+ method: handler.method
102
+ }))
103
+ };
104
+ };
105
+ var classOf = (entry) => {
106
+ if (typeof entry === "function")
107
+ return entry;
108
+ return entry.provider.kind === "class" ? entry.provider.ctor : undefined;
109
+ };
110
+ var gatewaysOf = (root) => collectModules(root).flatMap((module) => (module.options.providers ?? []).map(classOf).filter((ctor) => ctor !== undefined).filter(isGateway).map((ctor) => gatewayFor(ctor, module.name)));
111
+ export {
112
+ HandlerKind,
113
+ HiddenHealthController,
114
+ assertNoCollisions,
115
+ assertNoGatewayCollisions,
116
+ backoffDelay,
117
+ buildContext,
118
+ buildGateways,
119
+ buildRoutes,
120
+ buildRuntime,
121
+ buildWebSocket,
122
+ compose,
123
+ composeSocket,
124
+ decode,
125
+ decodeRelay,
126
+ defaultRelayUrl,
127
+ defaultStatusFor,
128
+ discoverGateway,
129
+ discoverGateways,
130
+ discoverRoutes,
131
+ encode,
132
+ encodeRelay,
133
+ executeWithRetry,
134
+ gatewaysOf,
135
+ guardsOf,
136
+ isCompressibleType,
137
+ isErrorFilter,
138
+ isGateway,
139
+ isJsonBody,
140
+ isPlainObject,
141
+ isRetryableStatus,
142
+ joinPath,
143
+ negotiate,
144
+ normalizePath,
145
+ normalizePrefix,
146
+ observe,
147
+ preflight,
148
+ retryAfterMs,
149
+ routesOf,
150
+ safeStringify,
151
+ toErrorMapper,
152
+ withCors,
153
+ withUpgradeRoutes
154
+ };
@@ -31,17 +31,14 @@ export declare const UNMATCHED: MetaKey<boolean>;
31
31
  export declare const Roles: (...roles: readonly string[]) => <F extends object>(target: F) => F;
32
32
  export declare const Public: () => <F extends object>(target: F) => F;
33
33
  /**
34
- * Route, but not documented. Valid on a method or on a class.
34
+ * Route, but not documented. Valid on a method or a class.
35
35
  *
36
- * The motivating case is a handler mounted on a wildcard: `@dunx/auth` routes
37
- * `<basePath>/*` to Better Auth's own handler, which is real and has to be
38
- * routed, but `*` is not an OpenAPI path template - so documenting it produced an
39
- * invalid entry named after an internal class, next to the 45 paths
40
- * `betterAuthDocument` describes properly.
36
+ * The motivating case is a wildcard mount: `@dunx/auth` routes `<basePath>/*` to
37
+ * better-auth's handler, and `*` is not an OpenAPI path template, so documenting
38
+ * it produced an invalid entry named after an internal class.
41
39
  *
42
- * It lives here rather than in `@dunx/openapi` because `@dunx/auth` must not
43
- * depend on the documentation package to say a route is undocumented, and this is
44
- * where the rest of the route metadata already is.
40
+ * Here rather than in `@dunx/openapi` so `@dunx/auth` need not depend on the
41
+ * documentation package to say a route is undocumented.
45
42
  */
46
43
  export declare const ApiHidden: () => <F extends object>(target: F) => F;
47
44
  /**
@@ -1,11 +1,9 @@
1
1
  import type { BunRequest } from 'bun';
2
2
  import type { DefaultStatus, HttpMethod } from './marker.js';
3
3
  /**
4
- * Standard Schema v1, restated rather than depended on. The spec is an
5
- * *interface*, not a runtime: `@standard-schema/spec` ships nothing but these
6
- * declarations, so restating them costs one file and keeps `@dunx/http` at zero
7
- * dependencies. Zod 4, Valibot and ArkType already satisfy this shape, so any of
8
- * them drops straight into a route's options.
4
+ * Standard Schema v1, restated rather than depended on: the spec is an interface,
5
+ * so restating it keeps `@dunx/http` at zero dependencies. Zod 4, Valibot and
6
+ * ArkType all satisfy this shape already.
9
7
  */
10
8
  export interface StandardSchemaV1<In = unknown, Out = In> {
11
9
  readonly '~standard': {
@@ -35,11 +33,8 @@ export interface StandardSchemaIssue {
35
33
  /** The validated output of a schema - `InferOutput<typeof CreateNote>` is `Note`. */
36
34
  export type InferOutput<S> = S extends StandardSchemaV1<unknown, infer Out> ? Out : never;
37
35
  /**
38
- * A JSON Schema, as JSON. OpenAPI 3.1 embeds draft 2020-12 verbatim.
39
- *
40
- * Declared here rather than in `@dunx/openapi` because {@link RouteSchemas} names
41
- * it and that package depends on this one, so this is the lowest common owner.
42
- * `@dunx/openapi` re-exports it.
36
+ * A JSON Schema, as JSON. Declared here rather than in `@dunx/openapi` because
37
+ * {@link RouteSchemas} names it and that package depends on this one.
43
38
  */
44
39
  export type JsonSchema = Readonly<Record<string, unknown>>;
45
40
  /**
@@ -55,9 +50,7 @@ export interface RouteSchemas {
55
50
  readonly status?: number;
56
51
  /**
57
52
  * What the route answers with, keyed by status code, in the same Standard
58
- * Schema the request side takes - so a response schema with a `.meta({ id })`
59
- * hoists into `components/schemas` exactly as a request body does, and there is
60
- * one contract for both directions.
53
+ * Schema the request side takes.
61
54
  *
62
55
  * ```ts
63
56
  * const one = {
@@ -66,19 +59,9 @@ export interface RouteSchemas {
66
59
  * } as const satisfies RouteSchemas;
67
60
  * ```
68
61
  *
69
- * **Never validated at runtime, checked at compile time.** Running a validation
70
- * pass over every response body would be a per-request cost paid for a
71
- * documentation feature. The handler's own return type carries the check
72
- * instead: the verb decorators constrain it against the entry for the success
73
- * status, so a handler answering with a different shape is a `TS1241` naming
74
- * the mismatched property. See {@link Returns}. Nothing in the request path
75
- * reads this key.
76
- *
77
- * A plain {@link JsonSchema} is accepted here too, and only here: a JSON Schema
78
- * needs no conversion, so documenting a response costs no validator. `$id` names
79
- * it, hoisting it into `components/schemas` the way `.meta({ id })` does for a
80
- * zod schema. `body`, `query` and `params` still take a Standard Schema, because
81
- * those are parsed.
62
+ * Never validated at runtime, checked at compile time against the success
63
+ * status. A plain {@link JsonSchema} is accepted here and only here, and `$id`
64
+ * hoists it into `components/schemas`.
82
65
  *
83
66
  * ```ts
84
67
  * response: {
@@ -92,10 +75,8 @@ export interface RouteSchemas {
92
75
  export type ResponseMap = Readonly<Record<number, StandardSchemaV1 | JsonSchema>>;
93
76
  /**
94
77
  * The handler's parameter type, derived from its own options object. It has to be
95
- * written out - a standard method decorator can *check* a parameter's type but
96
- * cannot contextually type an unannotated one
97
- * (docs/architecture/constraints.md) - but every field type still comes from the schemas, so nothing
98
- * is declared twice:
78
+ * written out: a standard method decorator can check a parameter's type but not
79
+ * contextually type an unannotated one (docs/architecture/constraints.md).
99
80
  *
100
81
  * ```ts
101
82
  * const createNote = { body: CreateNote, status: HttpStatusCode.CREATED } as const;
@@ -125,8 +106,8 @@ export type Input<O extends RouteSchemas> = {
125
106
  } : unknown);
126
107
  /**
127
108
  * The status a handler's return type is held to: an explicit `options.status`,
128
- * else the verb's default. Widened to `number` without `as const`, which is what
129
- * turns the check off rather than misapplying it.
109
+ * else the verb's default. Widened to `number` without `as const`, which turns
110
+ * the check off rather than misapplying it.
130
111
  */
131
112
  type SuccessStatus<O extends RouteSchemas, M extends HttpMethod> = O extends {
132
113
  status: infer S extends number;
@@ -138,39 +119,30 @@ type SuccessStatus<O extends RouteSchemas, M extends HttpMethod> = O extends {
138
119
  */
139
120
  type Declared<S> = [InferOutput<S>] extends [never] ? unknown : Serialised<InferOutput<S>>;
140
121
  /**
141
- * The declared shape as JSON will present it, which is the same shape with every
142
- * array made readonly.
122
+ * The declared shape as JSON will present it: the same shape with every array made
123
+ * readonly. `z.array()` infers a mutable `T[]`, so a method correctly returning
124
+ * `readonly User[]` would fail against a document it satisfies, and mutability
125
+ * does not survive `Response.json` anyway.
143
126
  *
144
- * `z.array()` infers a mutable `T[]`, and `readonly T[]` is not assignable to it -
145
- * so a repository method returning `readonly User[]`, the correct signature for
146
- * something that must not be mutated, would fail against a document it satisfies.
147
- * Mutability does not survive `Response.json`, so it is not part of the contract.
148
- *
149
- * Only arrays need the rewrite; TypeScript already ignores a property's `readonly`
150
- * modifier when checking assignability. The object branch is how nested arrays are
151
- * reached, and functions are returned untouched because mapping over one would
152
- * discard its call signature.
127
+ * Only arrays need it. The object branch reaches nested ones; functions are
128
+ * returned untouched, since mapping over one discards its call signature.
153
129
  */
154
130
  type Serialised<T> = T extends readonly (infer E)[] ? readonly Serialised<E>[] : T extends (...args: never[]) => unknown ? T : T extends object ? {
155
131
  readonly [K in keyof T]: Serialised<T[K]>;
156
132
  } : T;
157
133
  /**
158
- * What a handler may return, given its own options object and its verb.
159
- *
160
- * A route decorator can *check* a handler's type but cannot *infer* it
161
- * (docs/architecture/constraints.md), and that cuts both ways: this is the return
162
- * half of the same guarantee `Input<O>` gives the parameter. Declaring
163
- * `response: { 200: User }` stops being documentation a handler can contradict.
134
+ * What a handler may return, given its options object and its verb. The return
135
+ * half of the guarantee `Input<O>` gives the parameter, so `response: { 200: User }`
136
+ * stops being documentation a handler can contradict.
164
137
  *
165
- * `Response` is always allowed - it is the escape hatch `buildRoutes` passes
166
- * through untouched. So is a promise of either. Nothing is checked when the
167
- * success status has no `response` entry.
138
+ * `Response` is always allowed, and so is a promise of either. Nothing is checked
139
+ * when the success status has no `response` entry.
168
140
  */
169
141
  export type Returns<O extends RouteSchemas, M extends HttpMethod> = SuccessBody<O, M> | Response | Promise<SuccessBody<O, M> | Response>;
170
142
  /**
171
- * `infer R extends ResponseMap` is load bearing: without the constraint the
172
- * narrowed `O` inside the branch is `{ response: R } & O`, whose `response` no
173
- * longer satisfies `RouteSchemas`, and `SuccessStatus<O, M>` fails with `TS2344`.
143
+ * `infer R extends ResponseMap` is required: without it the narrowed `O` is
144
+ * `{ response: R } & O`, whose `response` no longer satisfies `RouteSchemas`, and
145
+ * `SuccessStatus<O, M>` fails with `TS2344`.
174
146
  */
175
147
  type SuccessBody<O extends RouteSchemas, M extends HttpMethod> = O extends {
176
148
  response: infer R extends ResponseMap;
@@ -16,111 +16,62 @@ export interface HttpOptions extends AppOptions {
16
16
  /** Resolved from the container, so middleware can inject(). */
17
17
  readonly middleware?: readonly Ctor<Middleware>[];
18
18
  /**
19
- * Replaces the default mapper.
20
- *
21
- * A bare `ErrorMapper` function, or an `ErrorFilter` **class** - which is the one
22
- * to prefer, because a class is resolved from the container and can therefore
23
- * inject the `Logger` or the config a real filter needs. A mapper cannot; dunx's
24
- * own default has to be curried over its logger for exactly that reason.
25
- *
26
- * A filter with dependencies needs them bindable, the same rule `middleware`
27
- * entries follow; one with none self-binds and needs no `providers` entry.
19
+ * Replaces the default mapper. Prefer an `ErrorFilter` class over a bare
20
+ * `ErrorMapper`: a class is resolved from the container and can inject.
28
21
  */
29
22
  readonly onError?: ErrorHandler;
30
23
  /**
31
- * One structured entry per request, on by default. `false` removes it; an
32
- * options object tunes what it records. See {@link RequestLoggingMiddleware}.
33
- *
34
- * It is the **outermost** middleware, ahead of anything `middleware` declares,
35
- * so a request rejected by a guard is still logged with the status it got.
24
+ * One structured entry per request, on by default and outermost, so a request
25
+ * a guard rejected is still logged with the status it got.
26
+ * See {@link RequestLoggingMiddleware}.
36
27
  */
37
28
  readonly requestLogging?: boolean | RequestLoggingOptions;
38
29
  /**
39
- * One entry at `listen()` naming every route and gateway the process serves. On
40
- * by default, because it is the answer to "is my route registered" and a service
41
- * that logs nothing at boot cannot answer it from production.
42
- *
43
- * `false` removes it. Separate from `requestLogging` rather than sharing its
44
- * switch: one is per request and one is per process, and silencing the noisy one
45
- * is not a reason to lose the quiet one. `@dunx/testing` defaults it off, for the
46
- * same reason it defaults request logging off.
30
+ * One entry at `listen()` naming every route and gateway served. On by default,
31
+ * and switched separately from `requestLogging`: one is per process, the other
32
+ * per request. `@dunx/testing` defaults it off.
47
33
  */
48
34
  readonly bootLogging?: boolean;
49
- /**
50
- * Bun's `websocket` options, plus where a throwing handler goes. Server-wide, so
51
- * they live here next to `middleware` rather than on a module: gateways
52
- * themselves are declared in `@Module({ providers })`.
53
- */
35
+ /** Bun's `websocket` options, plus where a throwing handler goes. Server-wide;
36
+ * gateways themselves are declared in `@Module({ providers })`. */
54
37
  readonly websocket?: SocketOptions;
55
38
  /**
56
- * The socket half of `middleware`, resolved from the container the same way.
57
- *
58
- * Each entry wraps every dispatched gateway handler - open, each named message,
59
- * the catch-all, close, drain, ping and pong - the way an HTTP middleware wraps a
60
- * route. `socketLogging`'s middleware runs outermost, ahead of anything here.
39
+ * The socket half of `middleware`. Each entry wraps every dispatched gateway
40
+ * handler; `socketLogging`'s runs outermost, ahead of anything here.
61
41
  */
62
42
  readonly socketMiddleware?: readonly Ctor<SocketMiddleware>[];
63
43
  /**
64
- * One structured entry per socket frame, on by default at **`debug`**. `false`
65
- * removes it; an options object tunes the level per event. See
66
- * {@link SocketLoggingMiddleware}.
67
- *
68
- * `debug` rather than request logging's `info`, because a gateway can take a
69
- * frame per connection per tick. The default `ConsoleLogger` threshold is
70
- * `info`, so this writes nothing until an app lowers its level or names a louder
71
- * one here.
72
- *
73
- * Installing it also takes `SocketOptions.onError`'s `console.error` default out
74
- * of the way: a middleware wraps the handler, so the failure is already reported
75
- * through the `Logger` with the gateway and the event on it.
44
+ * One structured entry per socket frame, on by default at `debug` - a gateway
45
+ * can take a frame per connection per tick, so it writes nothing until an app
46
+ * lowers its level. See {@link SocketLoggingMiddleware}.
76
47
  */
77
48
  readonly socketLogging?: boolean | SocketLoggingOptions;
78
49
  /**
79
- * Multi-node websocket fan-out. Absent - the default - means `PubSub` publishes
80
- * to this process only, which is exactly Bun's native pub/sub and costs nothing.
81
- *
82
- * `new RedisRelay({ url })` is the batteries-included one. Anything with a
83
- * `publish` and a `subscribe` fits, including `@dunx/infra`'s `RedisConnection`,
84
- * which has to come out of the container and so goes through
85
- * `app.get(PubSub).relayThrough(...)` instead of this option.
50
+ * Multi-node websocket fan-out. Absent means `PubSub` publishes to this process
51
+ * only. Anything with `publish` and `subscribe` fits; one that has to come out
52
+ * of the container goes through `app.get(PubSub).relayThrough(...)` instead.
86
53
  */
87
54
  readonly relay?: PubSubRelay;
88
55
  /** The broker channel the relay carries frames on. @default 'dunx:ws' */
89
56
  readonly relayChannel?: string;
90
57
  /**
91
- * How hard to retry a subscribe that failed. Same shape as
92
- * `RelayOptions.resubscribe`: bounded, doubling, and on an unref'd timer, so a
93
- * broker that never comes back cannot hold the process open.
94
- *
95
- * Here rather than only on `relayThrough` because reaching for that to set one
96
- * option means giving up `relay` above entirely - the two conflict, and the
97
- * second to run throws `PubSub already relays`.
58
+ * How hard to retry a failed subscribe. Bounded, doubling, on an unref'd timer,
59
+ * so a broker that never returns cannot hold the process open.
98
60
  */
99
61
  readonly relayResubscribe?: RelayOptions['resubscribe'];
100
62
  /**
101
- * What an unmatched path looks like to global middleware.
102
- *
103
- * `'guarded'`, the default, gives the miss no route metadata, so a global guard
104
- * refuses it and an anonymous caller gets that guard's status rather than a 404.
105
- * That is deliberate: a 404 on a miss while every real path answers 401 tells a
106
- * prober which paths exist.
107
- *
108
- * `'public'` reports the miss as `@Public()`, so a guard honouring that flag
109
- * passes it through to the conventional 404. The request is still logged and
110
- * still gets a request id either way, which is the whole reason the fallback
111
- * runs the middleware at all.
112
- *
113
- * A guard can discriminate under either setting: `UNMATCHED` is set on the miss
114
- * and no real route ever sets it.
63
+ * What an unmatched path looks like to global middleware. `'guarded'` gives the
64
+ * miss no route metadata, so a global guard refuses it and a prober cannot tell
65
+ * a 404 from a 401. `'public'` reports it as `@Public()` for a conventional 404.
66
+ * Either way `UNMATCHED` is set, which no real route sets.
115
67
  *
116
68
  * @default 'guarded'
117
69
  */
118
70
  readonly notFound?: 'guarded' | 'public';
119
71
  }
120
72
  /**
121
- * Everything below `listen()` configures the route table, which is built exactly
122
- * once - when the server binds. Calling any of them afterwards throws rather than
123
- * being quietly dropped.
73
+ * Everything below `listen()` configures the route table, built once when the
74
+ * server binds. Calling any of them afterwards throws.
124
75
  */
125
76
  export interface HttpApp extends App {
126
77
  /** Prefixes every discovered route. Last call wins. */
@@ -151,23 +102,16 @@ export declare class HttpApplication implements HttpApp {
151
102
  setting<K extends keyof AppSettings>(key: K): AppSettings[K];
152
103
  enableCors(options?: CorsOptions): this;
153
104
  clientIp(req: BunRequest): string | undefined;
154
- /**
155
- * The one `Bun.serve` call. A gateway's upgrade is a native `GET` route in the
156
- * same table, so Bun's router - not a hand-written `fetch` fallback - is what
157
- * matches an upgrade, and no `fetch` handler is needed at all.
158
- */
105
+ /** The one `Bun.serve` call. A gateway's upgrade is a native `GET` route in the
106
+ * same table, so Bun's router matches it and no `fetch` handler is needed. */
159
107
  listen(port?: number): Promise<string>;
160
- /**
161
- * Delegated unchanged: the drain is the container's phase, and `shutdown()`
162
- * runs it. Public so an operator can start draining without committing to a
163
- * shutdown, which is what a readiness probe wants during a rolling deploy.
164
- */
108
+ /** Public so an operator can start draining without committing to a shutdown,
109
+ * which is what a readiness probe wants during a rolling deploy. */
165
110
  drain(): Promise<void>;
166
111
  /**
167
- * The four phases, in order, and **none of them is skipped because an earlier
168
- * one failed**. A drain hook that threw used to abort this before `server.stop()`
169
- * had run, so the port stayed open and `closed` never resolved; each failure is
170
- * collected now and thrown once the whole teardown is over.
112
+ * Four phases in order, none skipped because an earlier one failed. A throwing
113
+ * drain hook used to abort before `server.stop()`, leaving the port open and
114
+ * `closed` unresolved; failures are collected and thrown at the end.
171
115
  */
172
116
  shutdown(): Promise<void>;
173
117
  enableShutdownHooks(signals?: readonly ShutdownSignal[], options?: ShutdownHookOptions): this;
@@ -4,20 +4,13 @@ export interface AddressSource {
4
4
  readonly trustProxy: boolean | number;
5
5
  }
6
6
  /**
7
- * The client's address, honouring the `'trust proxy'` setting.
7
+ * The client's address, honouring `'trust proxy'`. The address is counted from the
8
+ * right of `X-Forwarded-For` by the number of trusted hops, never from the left: a
9
+ * client can send anything, and only the entries a proxy appended carry weight.
8
10
  *
9
- * With the setting on, the address is read from `X-Forwarded-For` counting from
10
- * the right by the number of trusted hops, never from the left. A client can put
11
- * anything in the header it sends; only the entries a proxy appended carry any
12
- * weight, and there are exactly as many of those as there are proxies in front of
13
- * this server.
14
- *
15
- * Bound and exported by `HttpFactory`'s global wrapper module, so injecting it in a
16
- * middleware or controller needs no registration and `app.clientIp(req)` is the same
17
- * instance. That binding is not optional under module scoping: an unbound class
18
- * self-binds into whichever scope asks first, so a second module injecting it was a
19
- * boot error naming the first, and `listen()` could attach the server to an instance
20
- * nothing else held.
11
+ * Bound by `HttpFactory`'s global wrapper, which is not optional under module
12
+ * scoping - an unbound class self-binds into whichever scope asks first, so a
13
+ * second module injecting it was a boot error.
21
14
  */
22
15
  export declare class ClientAddress {
23
16
  of(req: BunRequest): string | undefined;