@dunx/http 3.2.1 → 3.3.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.
- package/dist/{chunk-3nbj06q8.js → chunk-8939brh2.js} +32 -32
- package/dist/chunk-bg0dr54z.js +37 -0
- package/dist/{chunk-3j2n1n11.js → chunk-gmtwad7f.js} +8 -8
- package/dist/client.js +9 -9
- package/dist/index.js +302 -121
- package/dist/internal.js +20 -20
- package/dist/server/application.d.ts +6 -97
- package/dist/server/binding.d.ts +67 -0
- package/dist/server/factory.d.ts +4 -2
- package/dist/server/metrics.d.ts +8 -3
- package/dist/server/options-provider.d.ts +14 -1
- package/dist/server/options.d.ts +141 -0
- package/dist/ws/socket.d.ts +16 -0
- package/package.json +4 -4
- package/dist/chunk-sz4pvqxy.js +0 -108
package/dist/internal.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
import"./chunk-bg0dr54z.js";
|
|
2
3
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
defaultStatusFor2,
|
|
5
|
+
ROLES2,
|
|
6
|
+
PUBLIC2,
|
|
7
|
+
HIDDEN2,
|
|
8
|
+
joinPath2,
|
|
9
|
+
discoverRoutes2,
|
|
10
|
+
isGateway2,
|
|
8
11
|
discoverGateway,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
joinPath
|
|
12
|
-
} from "./chunk-3nbj06q8.js";
|
|
13
|
-
import"./chunk-sz4pvqxy.js";
|
|
12
|
+
buildContext2
|
|
13
|
+
} from "./chunk-8939brh2.js";
|
|
14
14
|
// src/inspect.ts
|
|
15
15
|
import {
|
|
16
16
|
classOf,
|
|
@@ -31,7 +31,7 @@ var validatesIn = (options) => {
|
|
|
31
31
|
};
|
|
32
32
|
};
|
|
33
33
|
var rolesIn = (route) => {
|
|
34
|
-
const roles = route.meta?.get(
|
|
34
|
+
const roles = route.meta?.get(ROLES2.id);
|
|
35
35
|
if (roles === undefined || roles === null)
|
|
36
36
|
return null;
|
|
37
37
|
return (Array.isArray(roles) ? roles : [roles]).map(String);
|
|
@@ -42,16 +42,16 @@ var nodeFor = (route, module) => ({
|
|
|
42
42
|
controller: route.controller,
|
|
43
43
|
handler: route.handlerName,
|
|
44
44
|
module,
|
|
45
|
-
public: route.meta?.get(
|
|
45
|
+
public: route.meta?.get(PUBLIC2.id) === true,
|
|
46
46
|
roles: rolesIn(route),
|
|
47
47
|
guards: (route.guards ?? []).map((guard) => guard.name),
|
|
48
|
-
hidden: route.meta?.get(
|
|
48
|
+
hidden: route.meta?.get(HIDDEN2.id) === true,
|
|
49
49
|
validates: validatesIn(route.options),
|
|
50
50
|
status: route.options?.status ?? null,
|
|
51
51
|
responses: Object.keys(route.options?.response ?? {}).map(Number)
|
|
52
52
|
});
|
|
53
53
|
var routesOf = (root) => collectModules(root).flatMap((module) => readControllers(module).flatMap((controller) => {
|
|
54
|
-
return
|
|
54
|
+
return discoverRoutes2(inertInstance(controller)).map((route) => nodeFor(route, module.name));
|
|
55
55
|
}));
|
|
56
56
|
var gatewayFor = (ctor, module) => {
|
|
57
57
|
const { name, path, handlers } = discoverGateway(inertInstance(ctor));
|
|
@@ -67,16 +67,16 @@ var gatewayFor = (ctor, module) => {
|
|
|
67
67
|
}))
|
|
68
68
|
};
|
|
69
69
|
};
|
|
70
|
-
var gatewaysOf = (root) => collectModules(root).flatMap((module) => (module.options.providers ?? []).map((entry) => classOf(entry)?.ctor).filter((ctor) => ctor !== undefined).filter(
|
|
70
|
+
var gatewaysOf = (root) => collectModules(root).flatMap((module) => (module.options.providers ?? []).map((entry) => classOf(entry)?.ctor).filter((ctor) => ctor !== undefined).filter(isGateway2).map((ctor) => gatewayFor(ctor, module.name)));
|
|
71
71
|
// src/server/html.ts
|
|
72
72
|
var embedJson = (value) => JSON.stringify(value).replaceAll("<", "\\u003c");
|
|
73
73
|
export {
|
|
74
|
-
buildContext,
|
|
75
|
-
defaultStatusFor,
|
|
76
|
-
discoverRoutes,
|
|
74
|
+
buildContext2 as buildContext,
|
|
75
|
+
defaultStatusFor2 as defaultStatusFor,
|
|
76
|
+
discoverRoutes2 as discoverRoutes,
|
|
77
77
|
embedJson,
|
|
78
78
|
gatewaysOf,
|
|
79
|
-
isGateway,
|
|
80
|
-
joinPath,
|
|
79
|
+
isGateway2 as isGateway,
|
|
80
|
+
joinPath2 as joinPath,
|
|
81
81
|
routesOf
|
|
82
82
|
};
|
|
@@ -1,106 +1,11 @@
|
|
|
1
1
|
import type { BunRequest } from 'bun';
|
|
2
|
-
import { ShutdownAware, type App, type
|
|
2
|
+
import { ShutdownAware, type App, type Ctor, type InjectionToken, type ModuleRef } from '@dunx/core';
|
|
3
3
|
import { type DiscoveredRoute } from '../route/discover.js';
|
|
4
4
|
import type { WebSocketRuntime } from '../ws/adapter.js';
|
|
5
|
-
import type { SocketLoggingOptions } from '../ws/logging.js';
|
|
6
|
-
import type { SocketMiddleware } from '../ws/middleware.js';
|
|
7
|
-
import type { PubSubRelay, RelayOptions } from '../ws/relay.js';
|
|
8
|
-
import type { SocketOptions } from '../ws/socket.js';
|
|
9
5
|
import type { CorsOptions } from './cors.js';
|
|
10
|
-
import { type ErrorHandler } from './errors.js';
|
|
11
6
|
import type { Middleware } from './middleware.js';
|
|
12
|
-
import { type RequestLoggingOptions } from './request-logging.js';
|
|
13
7
|
import { type AppSettings } from './settings.js';
|
|
14
|
-
|
|
15
|
-
readonly port?: number;
|
|
16
|
-
/**
|
|
17
|
-
* Prefixes every discovered route, the same thing {@link HttpApp.setGlobalPrefix}
|
|
18
|
-
* does. Both exist: the method is what NestJS offers and what a script reaches
|
|
19
|
-
* for, the field is what an `HttpOptionsProvider` can answer from validated
|
|
20
|
-
* config. A later `setGlobalPrefix` call still wins, because it happens after.
|
|
21
|
-
*
|
|
22
|
-
* Explicitly `| undefined`, unlike the rest: a suite running one fixture both
|
|
23
|
-
* prefixed and unprefixed passes a variable here, and under
|
|
24
|
-
* `exactOptionalPropertyTypes` that would otherwise need a conditional spread.
|
|
25
|
-
* "No prefix" and "absent" mean the same thing. `@dunx/testing` relies on it.
|
|
26
|
-
*/
|
|
27
|
-
readonly prefix?: string | undefined;
|
|
28
|
-
/** Mounts an `OPTIONS` preflight per path, as {@link HttpApp.enableCors} does. */
|
|
29
|
-
readonly cors?: CorsOptions;
|
|
30
|
-
/** `app.set('trust proxy', ...)` as a field. */
|
|
31
|
-
readonly trustProxy?: boolean;
|
|
32
|
-
/**
|
|
33
|
-
* Calls `enableShutdownHooks` at construction. `true` takes the default signals;
|
|
34
|
-
* an object names them and tunes the force-exit.
|
|
35
|
-
*/
|
|
36
|
-
readonly shutdownHooks?: boolean | {
|
|
37
|
-
readonly signals?: readonly ShutdownSignal[];
|
|
38
|
-
readonly options?: ShutdownHookOptions;
|
|
39
|
-
};
|
|
40
|
-
/** Resolved from the container, so middleware can inject(). */
|
|
41
|
-
readonly middleware?: readonly Ctor<Middleware>[];
|
|
42
|
-
/**
|
|
43
|
-
* Replaces the default mapper. Prefer an `ErrorFilter` class over a bare
|
|
44
|
-
* `ErrorMapper`: a class is resolved from the container and can inject.
|
|
45
|
-
*/
|
|
46
|
-
readonly onError?: ErrorHandler;
|
|
47
|
-
/**
|
|
48
|
-
* One structured entry per request, on by default and outermost, so a request
|
|
49
|
-
* a guard rejected is still logged with the status it got.
|
|
50
|
-
* See {@link RequestLoggingMiddleware}.
|
|
51
|
-
*/
|
|
52
|
-
readonly requestLogging?: boolean | RequestLoggingOptions;
|
|
53
|
-
/**
|
|
54
|
-
* Count requests and time them per route, readable through
|
|
55
|
-
* {@link RequestMetrics}. Off by default; `+35.2 ns` per request when
|
|
56
|
-
* `requestLogging` is on, because the entry it already builds shares the
|
|
57
|
-
* timing. With `requestLogging: false` a `MetricsMiddleware` pays for its own
|
|
58
|
-
* `.then` instead, at +175.9 ns.
|
|
59
|
-
*/
|
|
60
|
-
readonly metrics?: boolean;
|
|
61
|
-
/**
|
|
62
|
-
* One entry at `listen()` naming every route and gateway served. On by default,
|
|
63
|
-
* and switched separately from `requestLogging`: one is per process, the other
|
|
64
|
-
* per request. `@dunx/testing` defaults it off.
|
|
65
|
-
*/
|
|
66
|
-
readonly bootLogging?: boolean;
|
|
67
|
-
/** Bun's `websocket` options, plus where a throwing handler goes. Server-wide;
|
|
68
|
-
* gateways themselves are declared in `@Module({ providers })`. */
|
|
69
|
-
readonly websocket?: SocketOptions;
|
|
70
|
-
/**
|
|
71
|
-
* The socket half of `middleware`. Each entry wraps every dispatched gateway
|
|
72
|
-
* handler; `socketLogging`'s runs outermost, ahead of anything here.
|
|
73
|
-
*/
|
|
74
|
-
readonly socketMiddleware?: readonly Ctor<SocketMiddleware>[];
|
|
75
|
-
/**
|
|
76
|
-
* One structured entry per socket frame, on by default at `debug` - a gateway
|
|
77
|
-
* can take a frame per connection per tick, so it writes nothing until an app
|
|
78
|
-
* lowers its level. See {@link SocketLoggingMiddleware}.
|
|
79
|
-
*/
|
|
80
|
-
readonly socketLogging?: boolean | SocketLoggingOptions;
|
|
81
|
-
/**
|
|
82
|
-
* Multi-node websocket fan-out. Absent means `PubSub` publishes to this process
|
|
83
|
-
* only. Anything with `publish` and `subscribe` fits; one that has to come out
|
|
84
|
-
* of the container goes through `app.get(PubSub).relayThrough(...)` instead.
|
|
85
|
-
*/
|
|
86
|
-
readonly relay?: PubSubRelay;
|
|
87
|
-
/** The broker channel the relay carries frames on. @default 'dunx:ws' */
|
|
88
|
-
readonly relayChannel?: string;
|
|
89
|
-
/**
|
|
90
|
-
* How hard to retry a failed subscribe. Bounded, doubling, on an unref'd timer,
|
|
91
|
-
* so a broker that never returns cannot hold the process open.
|
|
92
|
-
*/
|
|
93
|
-
readonly relayResubscribe?: RelayOptions['resubscribe'];
|
|
94
|
-
/**
|
|
95
|
-
* What an unmatched path looks like to global middleware. `'guarded'` gives the
|
|
96
|
-
* miss no route metadata, so a global guard refuses it and a prober cannot tell
|
|
97
|
-
* a 404 from a 401. `'public'` reports it as `@Public()` for a conventional 404.
|
|
98
|
-
* Either way `UNMATCHED` is set, which no real route sets.
|
|
99
|
-
*
|
|
100
|
-
* @default 'guarded'
|
|
101
|
-
*/
|
|
102
|
-
readonly notFound?: 'guarded' | 'public';
|
|
103
|
-
}
|
|
8
|
+
import type { HttpOptions } from './options.js';
|
|
104
9
|
/**
|
|
105
10
|
* Everything below `listen()` configures the route table, built once when the
|
|
106
11
|
* server binds. Calling any of them afterwards throws.
|
|
@@ -118,6 +23,8 @@ export interface HttpApp extends App {
|
|
|
118
23
|
clientIp(req: BunRequest): string | undefined;
|
|
119
24
|
/** Every gateway path this app upgrades on, exactly as mounted. */
|
|
120
25
|
readonly gatewayPaths: readonly string[];
|
|
26
|
+
/** The gateway port's own url, `undefined` unless `gatewayPort` split them. */
|
|
27
|
+
readonly gatewayUrl: string | undefined;
|
|
121
28
|
listen(port?: number): Promise<string>;
|
|
122
29
|
}
|
|
123
30
|
export declare class HttpApplication extends ShutdownAware implements HttpApp {
|
|
@@ -137,6 +44,8 @@ export declare class HttpApplication extends ShutdownAware implements HttpApp {
|
|
|
137
44
|
/** The one `Bun.serve` call. A gateway's upgrade is a native `GET` route in the
|
|
138
45
|
* same table, so Bun's router matches it and no `fetch` handler is needed. */
|
|
139
46
|
listen(port?: number): Promise<string>;
|
|
47
|
+
/** The gateway port's own url, when `gatewayPort` split them. */
|
|
48
|
+
get gatewayUrl(): string | undefined;
|
|
140
49
|
/** Public so an operator can start draining without committing to a shutdown,
|
|
141
50
|
* which is what a readiness probe wants during a rolling deploy. */
|
|
142
51
|
drain(): Promise<void>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Server } from 'bun';
|
|
2
|
+
import type { SocketData } from '../ws/socket.js';
|
|
3
|
+
import type { WebSocketRuntime } from '../ws/adapter.js';
|
|
4
|
+
import type { RouteHandler } from './middleware.js';
|
|
5
|
+
import { type BunRoutes } from './routes.js';
|
|
6
|
+
/** What `listen()` computes and hands the binding, once the table is final. */
|
|
7
|
+
export interface BindingPlan {
|
|
8
|
+
readonly port: number;
|
|
9
|
+
readonly routes: BunRoutes;
|
|
10
|
+
readonly fetch: RouteHandler;
|
|
11
|
+
readonly websocket: WebSocketRuntime | undefined;
|
|
12
|
+
}
|
|
13
|
+
/** The protocol settings, fixed at construction. `undefined` leaves Bun's own
|
|
14
|
+
* default in place rather than restating it. */
|
|
15
|
+
export interface BindingProtocols {
|
|
16
|
+
readonly http2?: boolean | undefined;
|
|
17
|
+
readonly http1?: boolean | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* A port of its own for the gateways. When set, the upgrade routes are not
|
|
20
|
+
* merged into the main table and a second `Bun.serve` takes them.
|
|
21
|
+
*/
|
|
22
|
+
readonly gatewayPort?: number | undefined;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The two per-server counters a metrics reader wants, summed across however
|
|
26
|
+
* many servers are bound. `Server` satisfies it on its own, which is what an
|
|
27
|
+
* unsplit app hands over.
|
|
28
|
+
*/
|
|
29
|
+
export interface ServerGauges {
|
|
30
|
+
readonly pendingRequests: number;
|
|
31
|
+
readonly pendingWebSockets: number;
|
|
32
|
+
}
|
|
33
|
+
export interface Bound {
|
|
34
|
+
readonly main: Server<SocketData>;
|
|
35
|
+
/** The server that owns the sockets, which is what `PubSub` publishes on. */
|
|
36
|
+
readonly sockets: Server<SocketData>;
|
|
37
|
+
readonly gauges: ServerGauges;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The one or two `Bun.serve` instances an app binds.
|
|
41
|
+
*
|
|
42
|
+
* One is the normal shape: a gateway's upgrade is a native `GET` route in the
|
|
43
|
+
* same table, so Bun's router matches it and nothing else is needed.
|
|
44
|
+
*
|
|
45
|
+
* Two exist for `gatewayPort`. A websocket upgrade is an HTTP/1.1 request, so a
|
|
46
|
+
* server with `http1: false` can serve HTTP/2 routes or gateways and never both;
|
|
47
|
+
* splitting the ports is what lets one app do both. The gateway server takes no
|
|
48
|
+
* protocol overrides, because refusing HTTP/1.x there is the thing being avoided.
|
|
49
|
+
*
|
|
50
|
+
* Both servers come from one container, which is the reason this is a second
|
|
51
|
+
* `Bun.serve` rather than a second `HttpFactory.create`: a second app would build
|
|
52
|
+
* a second container, and the gateways would inject different singletons than the
|
|
53
|
+
* controllers.
|
|
54
|
+
*/
|
|
55
|
+
export declare class ServerBinding {
|
|
56
|
+
#private;
|
|
57
|
+
constructor(protocols: BindingProtocols);
|
|
58
|
+
bind(plan: BindingPlan): Bound;
|
|
59
|
+
get gatewayUrl(): string | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* `force` is for the server that owns the sockets: a graceful stop waits for
|
|
62
|
+
* open connections, and a WebSocket never closes itself, so those clients see
|
|
63
|
+
* a 1006. Under a split the routes server holds none and stops gracefully, so
|
|
64
|
+
* an in-flight request there finishes rather than being cut off.
|
|
65
|
+
*/
|
|
66
|
+
stop(force: boolean): Promise<void>;
|
|
67
|
+
}
|
package/dist/server/factory.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type ModuleRef } from '@dunx/core';
|
|
2
|
-
import { type HttpApp
|
|
3
|
-
|
|
2
|
+
import { type HttpApp } from './application.js';
|
|
3
|
+
import type { HttpOptions } from './options.js';
|
|
4
|
+
export type { HttpApp } from './application.js';
|
|
5
|
+
export type { HttpOptions } from './options.js';
|
|
4
6
|
export declare class HttpFactory {
|
|
5
7
|
#private;
|
|
6
8
|
/**
|
package/dist/server/metrics.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type HistogramSnapshot } from '@dunx/core';
|
|
2
|
-
import type { BunRequest
|
|
2
|
+
import type { BunRequest } from 'bun';
|
|
3
|
+
import type { ServerGauges } from './binding.js';
|
|
3
4
|
import type { RouteContext } from './context.js';
|
|
4
5
|
import type { Middleware, Next } from './middleware.js';
|
|
5
6
|
/** Every path Bun matched nothing for, collapsed into one series. */
|
|
@@ -53,8 +54,12 @@ export declare class RequestMetrics {
|
|
|
53
54
|
* reflecting a deploy three days ago; who calls this is the app's decision.
|
|
54
55
|
*/
|
|
55
56
|
reset(): void;
|
|
56
|
-
/**
|
|
57
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Internal: `listen()` hands the bound server's counters to the resolved
|
|
59
|
+
* singleton. Structural, so an app serving from two ports can hand over the
|
|
60
|
+
* sum rather than one server's half of it.
|
|
61
|
+
*/
|
|
62
|
+
attach(server: ServerGauges): void;
|
|
58
63
|
}
|
|
59
64
|
/**
|
|
60
65
|
* Whether `MetricsMiddleware` is the thing doing the observing.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Ctor } from '@dunx/core';
|
|
2
|
-
import type { HttpOptions } from './
|
|
2
|
+
import type { HttpOptions } from './options.js';
|
|
3
3
|
import type { SocketLoggingOptions } from '../ws/logging.js';
|
|
4
4
|
import type { SocketMiddleware } from '../ws/middleware.js';
|
|
5
5
|
import type { PubSubRelay, RelayOptions } from '../ws/relay.js';
|
|
@@ -86,6 +86,19 @@ export declare abstract class HttpOptionsProvider {
|
|
|
86
86
|
/** Multi-node websocket fan-out. Absent publishes to this process only. */
|
|
87
87
|
get relay(): PubSubRelay | undefined;
|
|
88
88
|
get relayResubscribe(): RelayOptions['resubscribe'];
|
|
89
|
+
/**
|
|
90
|
+
* Serve HTTP/2 alongside HTTP/1.1 on the same port. `undefined` rather than
|
|
91
|
+
* `false` so an absent answer leaves Bun's own default in place instead of
|
|
92
|
+
* restating it. See {@link HttpOptions.http2}.
|
|
93
|
+
*/
|
|
94
|
+
get http2(): boolean | undefined;
|
|
95
|
+
/** See {@link HttpOptions.http1}. `false` disables every gateway. */
|
|
96
|
+
get http1(): boolean | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* A port of its own for the gateways, which is what makes `http1: false`
|
|
99
|
+
* usable with one. See {@link HttpOptions.gatewayPort}.
|
|
100
|
+
*/
|
|
101
|
+
get gatewayPort(): number | undefined;
|
|
89
102
|
}
|
|
90
103
|
/**
|
|
91
104
|
* The base itself, bound when no module bound a subclass. Concrete because the
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { AppOptions, Ctor, ShutdownHookOptions, ShutdownSignal } from '@dunx/core';
|
|
2
|
+
import type { SocketLoggingOptions } from '../ws/logging.js';
|
|
3
|
+
import type { SocketMiddleware } from '../ws/middleware.js';
|
|
4
|
+
import type { PubSubRelay, RelayOptions } from '../ws/relay.js';
|
|
5
|
+
import type { SocketOptions } from '../ws/socket.js';
|
|
6
|
+
import type { CorsOptions } from './cors.js';
|
|
7
|
+
import type { ErrorHandler } from './errors.js';
|
|
8
|
+
import type { Middleware } from './middleware.js';
|
|
9
|
+
import type { RequestLoggingOptions } from './request-logging.js';
|
|
10
|
+
/**
|
|
11
|
+
* Every setting `HttpFactory.create` takes. Here rather than in
|
|
12
|
+
* `application.ts` so that file is the class it is named for.
|
|
13
|
+
*
|
|
14
|
+
* Each field has a twin on {@link HttpOptionsProvider}, which is the same
|
|
15
|
+
* settings answered from the container instead. The argument wins field by
|
|
16
|
+
* field; see `resolveHttpOptions`.
|
|
17
|
+
*/
|
|
18
|
+
export interface HttpOptions extends AppOptions {
|
|
19
|
+
readonly port?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Prefixes every discovered route, the same thing {@link HttpApp.setGlobalPrefix}
|
|
22
|
+
* does. Both exist: the method is what NestJS offers and what a script reaches
|
|
23
|
+
* for, the field is what an `HttpOptionsProvider` can answer from validated
|
|
24
|
+
* config. A later `setGlobalPrefix` call still wins, because it happens after.
|
|
25
|
+
*
|
|
26
|
+
* Explicitly `| undefined`, unlike the rest: a suite running one fixture both
|
|
27
|
+
* prefixed and unprefixed passes a variable here, and under
|
|
28
|
+
* `exactOptionalPropertyTypes` that would otherwise need a conditional spread.
|
|
29
|
+
* "No prefix" and "absent" mean the same thing. `@dunx/testing` relies on it.
|
|
30
|
+
*/
|
|
31
|
+
readonly prefix?: string | undefined;
|
|
32
|
+
/** Mounts an `OPTIONS` preflight per path, as {@link HttpApp.enableCors} does. */
|
|
33
|
+
readonly cors?: CorsOptions;
|
|
34
|
+
/** `app.set('trust proxy', ...)` as a field. */
|
|
35
|
+
readonly trustProxy?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Calls `enableShutdownHooks` at construction. `true` takes the default signals;
|
|
38
|
+
* an object names them and tunes the force-exit.
|
|
39
|
+
*/
|
|
40
|
+
readonly shutdownHooks?: boolean | {
|
|
41
|
+
readonly signals?: readonly ShutdownSignal[];
|
|
42
|
+
readonly options?: ShutdownHookOptions;
|
|
43
|
+
};
|
|
44
|
+
/** Resolved from the container, so middleware can inject(). */
|
|
45
|
+
readonly middleware?: readonly Ctor<Middleware>[];
|
|
46
|
+
/**
|
|
47
|
+
* Replaces the default mapper. Prefer an `ErrorFilter` class over a bare
|
|
48
|
+
* `ErrorMapper`: a class is resolved from the container and can inject.
|
|
49
|
+
*/
|
|
50
|
+
readonly onError?: ErrorHandler;
|
|
51
|
+
/**
|
|
52
|
+
* One structured entry per request, on by default and outermost, so a request
|
|
53
|
+
* a guard rejected is still logged with the status it got.
|
|
54
|
+
* See {@link RequestLoggingMiddleware}.
|
|
55
|
+
*/
|
|
56
|
+
readonly requestLogging?: boolean | RequestLoggingOptions;
|
|
57
|
+
/**
|
|
58
|
+
* Count requests and time them per route, readable through
|
|
59
|
+
* {@link RequestMetrics}. Off by default; `+35.2 ns` per request when
|
|
60
|
+
* `requestLogging` is on, because the entry it already builds shares the
|
|
61
|
+
* timing. With `requestLogging: false` a `MetricsMiddleware` pays for its own
|
|
62
|
+
* `.then` instead, at +175.9 ns.
|
|
63
|
+
*/
|
|
64
|
+
readonly metrics?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* One entry at `listen()` naming every route and gateway served. On by default,
|
|
67
|
+
* and switched separately from `requestLogging`: one is per process, the other
|
|
68
|
+
* per request. `@dunx/testing` defaults it off.
|
|
69
|
+
*/
|
|
70
|
+
readonly bootLogging?: boolean;
|
|
71
|
+
/** Bun's `websocket` options, plus where a throwing handler goes. Server-wide;
|
|
72
|
+
* gateways themselves are declared in `@Module({ providers })`. */
|
|
73
|
+
readonly websocket?: SocketOptions;
|
|
74
|
+
/**
|
|
75
|
+
* The socket half of `middleware`. Each entry wraps every dispatched gateway
|
|
76
|
+
* handler; `socketLogging`'s runs outermost, ahead of anything here.
|
|
77
|
+
*/
|
|
78
|
+
readonly socketMiddleware?: readonly Ctor<SocketMiddleware>[];
|
|
79
|
+
/**
|
|
80
|
+
* One structured entry per socket frame, on by default at `debug` - a gateway
|
|
81
|
+
* can take a frame per connection per tick, so it writes nothing until an app
|
|
82
|
+
* lowers its level. See {@link SocketLoggingMiddleware}.
|
|
83
|
+
*/
|
|
84
|
+
readonly socketLogging?: boolean | SocketLoggingOptions;
|
|
85
|
+
/**
|
|
86
|
+
* Multi-node websocket fan-out. Absent means `PubSub` publishes to this process
|
|
87
|
+
* only. Anything with `publish` and `subscribe` fits; one that has to come out
|
|
88
|
+
* of the container goes through `app.get(PubSub).relayThrough(...)` instead.
|
|
89
|
+
*/
|
|
90
|
+
readonly relay?: PubSubRelay;
|
|
91
|
+
/** The broker channel the relay carries frames on. @default 'dunx:ws' */
|
|
92
|
+
readonly relayChannel?: string;
|
|
93
|
+
/**
|
|
94
|
+
* How hard to retry a failed subscribe. Bounded, doubling, on an unref'd timer,
|
|
95
|
+
* so a broker that never returns cannot hold the process open.
|
|
96
|
+
*/
|
|
97
|
+
readonly relayResubscribe?: RelayOptions['resubscribe'];
|
|
98
|
+
/**
|
|
99
|
+
* What an unmatched path looks like to global middleware. `'guarded'` gives the
|
|
100
|
+
* miss no route metadata, so a global guard refuses it and a prober cannot tell
|
|
101
|
+
* a 404 from a 401. `'public'` reports it as `@Public()` for a conventional 404.
|
|
102
|
+
* Either way `UNMATCHED` is set, which no real route sets.
|
|
103
|
+
*
|
|
104
|
+
* @default 'public'
|
|
105
|
+
*/
|
|
106
|
+
readonly notFound?: 'guarded' | 'public';
|
|
107
|
+
/**
|
|
108
|
+
* Serve HTTP/2 on the same port, through the same routes and the same
|
|
109
|
+
* `fetch` fallback. Without TLS that is h2c: a client opening with the HTTP/2
|
|
110
|
+
* preface gets HTTP/2 and everyone else gets HTTP/1.1, which is what a proxy
|
|
111
|
+
* in front of the app speaks. Bun marks the option experimental.
|
|
112
|
+
*
|
|
113
|
+
* Gateways are unaffected and keep working, because a websocket upgrade is an
|
|
114
|
+
* HTTP/1.1 request and Bun serves both protocols on the one socket. There is
|
|
115
|
+
* no websocket over HTTP/2.
|
|
116
|
+
*
|
|
117
|
+
* @default false
|
|
118
|
+
*/
|
|
119
|
+
readonly http2?: boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Serve HTTP/1.1. `false` alongside `http2` refuses HTTP/1.x with a 505, which
|
|
122
|
+
* **disables every gateway**: a websocket upgrade is an HTTP/1.1 request, so
|
|
123
|
+
* nothing can connect to one. Only for a port that is HTTP/2 or nothing.
|
|
124
|
+
*
|
|
125
|
+
* @default true
|
|
126
|
+
*/
|
|
127
|
+
readonly http1?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* A port of its own for the gateways. The routes keep {@link port} and the
|
|
130
|
+
* upgrades move here, on a second `Bun.serve` that takes no protocol
|
|
131
|
+
* overrides, so it speaks HTTP/1.1 whatever the main port is set to.
|
|
132
|
+
*
|
|
133
|
+
* This is what makes `http2` with `http1: false` usable: a websocket upgrade
|
|
134
|
+
* is an HTTP/1.1 request, so one port serves HTTP/2-only routes or gateways
|
|
135
|
+
* and never both. The pair without a `gatewayPort` is a boot error.
|
|
136
|
+
*
|
|
137
|
+
* `0` takes any free port and is the one value that does not warn when no
|
|
138
|
+
* gateway is declared. See docs/guide/19-deployment.md.
|
|
139
|
+
*/
|
|
140
|
+
readonly gatewayPort?: number;
|
|
141
|
+
}
|
package/dist/ws/socket.d.ts
CHANGED
|
@@ -42,4 +42,20 @@ export type SocketOptions = Readonly<Pick<WebSocketHandler<SocketData>, 'backpre
|
|
|
42
42
|
* nothing replaced it.
|
|
43
43
|
*/
|
|
44
44
|
readonly onError?: SocketErrorHandler;
|
|
45
|
+
/**
|
|
46
|
+
* What a binary frame arrives as: `'nodebuffer'` (Bun's default),
|
|
47
|
+
* `'arraybuffer'`, `'uint8array'` or `'blob'`. Bun 1.4.1 added `'blob'` and
|
|
48
|
+
* accepts all four on a server socket.
|
|
49
|
+
*
|
|
50
|
+
* Not one of the `Pick`ed keys above, because Bun does not take it as a handler
|
|
51
|
+
* option - it is a mutable property on each `ServerWebSocket`. Declaring it
|
|
52
|
+
* here and assigning it as each connection opens is what makes it a
|
|
53
|
+
* server-wide setting rather than a line in every gateway's `@OnOpen`. The type
|
|
54
|
+
* is read off the socket, so the accepted values cannot drift from the runtime.
|
|
55
|
+
*
|
|
56
|
+
* It changes what `message` and the ping/pong payloads are at run time. Bun
|
|
57
|
+
* types them as the default's, so a gateway that sets this narrows the value
|
|
58
|
+
* itself.
|
|
59
|
+
*/
|
|
60
|
+
readonly binaryType?: Socket['binaryType'];
|
|
45
61
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dunx/http",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Bun.serve adapter for the dunx framework: controllers, middleware and WebSocket gateways",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"@opentelemetry/sdk-trace-node": "2.11.0"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
|
-
"@dunx/core": "^3.
|
|
69
|
-
"@types/bun": ">=1.
|
|
68
|
+
"@dunx/core": "^3.3.0",
|
|
69
|
+
"@types/bun": ">=1.4.1"
|
|
70
70
|
},
|
|
71
71
|
"peerDependenciesMeta": {
|
|
72
72
|
"@types/bun": {
|
|
@@ -74,6 +74,6 @@
|
|
|
74
74
|
}
|
|
75
75
|
},
|
|
76
76
|
"engines": {
|
|
77
|
-
"bun": ">=1.4.
|
|
77
|
+
"bun": ">=1.4.1"
|
|
78
78
|
}
|
|
79
79
|
}
|
package/dist/chunk-sz4pvqxy.js
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __name = (target, name) => {
|
|
6
|
-
Object.defineProperty(target, "name", {
|
|
7
|
-
value: name,
|
|
8
|
-
enumerable: false,
|
|
9
|
-
configurable: true
|
|
10
|
-
});
|
|
11
|
-
return target;
|
|
12
|
-
};
|
|
13
|
-
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
|
|
14
|
-
var __typeError = (msg) => {
|
|
15
|
-
throw TypeError(msg);
|
|
16
|
-
};
|
|
17
|
-
var __defNormalProp = (obj, key, value) => (key in obj) ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
18
|
-
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
19
|
-
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
|
|
20
|
-
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
21
|
-
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
22
|
-
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
23
|
-
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
24
|
-
var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
|
|
25
|
-
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
26
|
-
var __expectFn = (fn) => fn !== undefined && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
27
|
-
var __decoratorContext = (kind, name, done, metadata, fns) => ({
|
|
28
|
-
kind: __decoratorStrings[kind],
|
|
29
|
-
name,
|
|
30
|
-
metadata,
|
|
31
|
-
addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null))
|
|
32
|
-
});
|
|
33
|
-
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
34
|
-
var __runInitializers = (array, flags, self, value) => {
|
|
35
|
-
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length;i < n; i++)
|
|
36
|
-
flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
|
37
|
-
return value;
|
|
38
|
-
};
|
|
39
|
-
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
40
|
-
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
|
41
|
-
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
|
42
|
-
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
|
43
|
-
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : {
|
|
44
|
-
get [name]() {
|
|
45
|
-
return __privateGet(this, extra);
|
|
46
|
-
},
|
|
47
|
-
set [name](x) {
|
|
48
|
-
__privateSet(this, extra, x);
|
|
49
|
-
}
|
|
50
|
-
}, name));
|
|
51
|
-
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
|
52
|
-
for (var i = decorators.length - 1;i >= 0; i--) {
|
|
53
|
-
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
54
|
-
if (k) {
|
|
55
|
-
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => (name in x) };
|
|
56
|
-
if (k ^ 3)
|
|
57
|
-
access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
|
58
|
-
if (k > 2)
|
|
59
|
-
access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
|
60
|
-
}
|
|
61
|
-
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? undefined : { get: desc.get, set: desc.set } : target, ctx);
|
|
62
|
-
done._ = 1;
|
|
63
|
-
if (k ^ 4 || it === undefined)
|
|
64
|
-
__expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
|
65
|
-
else if (typeof it !== "object" || it === null)
|
|
66
|
-
__typeError("Object expected");
|
|
67
|
-
else
|
|
68
|
-
__expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
|
69
|
-
}
|
|
70
|
-
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
// src/server/status.ts
|
|
74
|
-
var HttpStatusCode = Object.freeze({
|
|
75
|
-
OK: 200,
|
|
76
|
-
CREATED: 201,
|
|
77
|
-
ACCEPTED: 202,
|
|
78
|
-
NO_CONTENT: 204,
|
|
79
|
-
MOVED_PERMANENTLY: 301,
|
|
80
|
-
FOUND: 302,
|
|
81
|
-
NOT_MODIFIED: 304,
|
|
82
|
-
TEMPORARY_REDIRECT: 307,
|
|
83
|
-
PERMANENT_REDIRECT: 308,
|
|
84
|
-
BAD_REQUEST: 400,
|
|
85
|
-
UNAUTHORIZED: 401,
|
|
86
|
-
PAYMENT_REQUIRED: 402,
|
|
87
|
-
FORBIDDEN: 403,
|
|
88
|
-
NOT_FOUND: 404,
|
|
89
|
-
METHOD_NOT_ALLOWED: 405,
|
|
90
|
-
NOT_ACCEPTABLE: 406,
|
|
91
|
-
REQUEST_TIMEOUT: 408,
|
|
92
|
-
CONFLICT: 409,
|
|
93
|
-
GONE: 410,
|
|
94
|
-
PRECONDITION_FAILED: 412,
|
|
95
|
-
PAYLOAD_TOO_LARGE: 413,
|
|
96
|
-
URI_TOO_LONG: 414,
|
|
97
|
-
UNSUPPORTED_MEDIA_TYPE: 415,
|
|
98
|
-
IM_A_TEAPOT: 418,
|
|
99
|
-
UNPROCESSABLE_ENTITY: 422,
|
|
100
|
-
TOO_MANY_REQUESTS: 429,
|
|
101
|
-
INTERNAL_SERVER_ERROR: 500,
|
|
102
|
-
NOT_IMPLEMENTED: 501,
|
|
103
|
-
BAD_GATEWAY: 502,
|
|
104
|
-
SERVICE_UNAVAILABLE: 503,
|
|
105
|
-
GATEWAY_TIMEOUT: 504
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
export { __privateGet, __privateAdd, __decoratorStart, __decoratorMetadata, __runInitializers, __decorateElement, HttpStatusCode };
|