@dunx/http 0.1.0 → 0.2.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.
@@ -23,7 +23,7 @@ export declare const PUBLIC: MetaKey<boolean>;
23
23
  export declare const Roles: (...roles: readonly string[]) => <F extends object>(target: F) => F;
24
24
  export declare const Public: () => <F extends object>(target: F) => F;
25
25
  /**
26
- * Guards are middleware, so they compose rather than override which is why they
26
+ * Guards are middleware, so they compose rather than override - which is why they
27
27
  * are not a `MetaKey`. Valid on a method or on a class.
28
28
  */
29
29
  export declare const UseGuards: (...guards: readonly Ctor<Middleware>[]) => <F extends object>(target: F) => F;
@@ -31,7 +31,7 @@ export interface StandardSchemaIssue {
31
31
  readonly key: PropertyKey;
32
32
  })[] | undefined;
33
33
  }
34
- /** The validated output of a schema `InferOutput<typeof CreateNote>` is `Note`. */
34
+ /** The validated output of a schema - `InferOutput<typeof CreateNote>` is `Note`. */
35
35
  export type InferOutput<S> = S extends StandardSchemaV1<unknown, infer Out> ? Out : never;
36
36
  /**
37
37
  * The second argument to `@Get`/`@Post`/... Declaring a schema is what makes the
@@ -47,9 +47,9 @@ export interface RouteSchemas {
47
47
  }
48
48
  /**
49
49
  * The handler's parameter type, derived from its own options object. It has to be
50
- * written out a standard method decorator can *check* a parameter's type but
50
+ * written out - a standard method decorator can *check* a parameter's type but
51
51
  * cannot contextually type an unannotated one (docs/ARCHITECTURE.md, "Verified
52
- * constraints") but every field type still comes from the schemas, so nothing
52
+ * constraints") - but every field type still comes from the schemas, so nothing
53
53
  * is declared twice:
54
54
  *
55
55
  * ```ts
@@ -29,7 +29,7 @@ export interface HttpOptions extends AppOptions {
29
29
  */
30
30
  readonly websocket?: SocketOptions;
31
31
  /**
32
- * Multi-node websocket fan-out. Absent the default means `PubSub` publishes
32
+ * Multi-node websocket fan-out. Absent - the default - means `PubSub` publishes
33
33
  * to this process only, which is exactly Bun's native pub/sub and costs nothing.
34
34
  *
35
35
  * `new RedisRelay({ url })` is the batteries-included one. Anything with a
@@ -43,7 +43,7 @@ export interface HttpOptions extends AppOptions {
43
43
  }
44
44
  /**
45
45
  * Everything below `listen()` configures the route table, which is built exactly
46
- * once when the server binds. Calling any of them afterwards throws rather than
46
+ * once - when the server binds. Calling any of them afterwards throws rather than
47
47
  * being quietly dropped.
48
48
  */
49
49
  export interface HttpApp extends App {
@@ -55,7 +55,7 @@ export interface HttpApp extends App {
55
55
  setting<K extends keyof AppSettings>(key: K): AppSettings[K];
56
56
  /** Mounts an `OPTIONS` preflight per path. Last call wins. */
57
57
  enableCors(options?: CorsOptions): this;
58
- /** The same `inject(ClientAddress)` singleton honours `'trust proxy'`. */
58
+ /** The same `inject(ClientAddress)` singleton - honours `'trust proxy'`. */
59
59
  clientIp(req: BunRequest): string | undefined;
60
60
  /** Every gateway path this app upgrades on, exactly as mounted. */
61
61
  readonly gatewayPaths: readonly string[];
@@ -75,7 +75,7 @@ export declare class HttpApplication implements HttpApp {
75
75
  clientIp(req: BunRequest): string | undefined;
76
76
  /**
77
77
  * The one `Bun.serve` call. A gateway's upgrade is a native `GET` route in the
78
- * same table, so Bun's router not a hand-written `fetch` fallback is what
78
+ * same table, so Bun's router - not a hand-written `fetch` fallback - is what
79
79
  * matches an upgrade, and no `fetch` handler is needed at all.
80
80
  */
81
81
  listen(port?: number): Promise<string>;
@@ -4,7 +4,7 @@ import type { MetaKey } from '../route/metadata.js';
4
4
  /**
5
5
  * Which route the middleware is running for, and what that route's decorators
6
6
  * declared. `get` resolves the handler's metadata first and the controller class's
7
- * second the same override direction as Nest's `getAllAndOverride`.
7
+ * second - the same override direction as Nest's `getAllAndOverride`.
8
8
  */
9
9
  export interface RouteContext {
10
10
  readonly controller: string;
@@ -15,7 +15,7 @@ export interface RouteContext {
15
15
  }
16
16
  /**
17
17
  * One frozen context per route, built when the table is built and closed over by
18
- * the chain. The merge already happened at discovery, so `get` is a Map lookup
18
+ * the chain. The merge already happened at discovery, so `get` is a Map lookup -
19
19
  * not a prototype walk, and nothing is read per request.
20
20
  */
21
21
  export declare const buildContext: (route: DiscoveredRoute) => RouteContext;
@@ -3,7 +3,7 @@ export type CorsOrigin = string | readonly string[] | ((origin: string) => boole
3
3
  export interface CorsOptions {
4
4
  /**
5
5
  * `'*'` by default. A concrete string, a list, or a predicate all answer with the
6
- * caller's own origin only when it is allowed a request from anywhere else gets
6
+ * caller's own origin only when it is allowed - a request from anywhere else gets
7
7
  * no CORS headers at all, which is what makes the browser block it.
8
8
  */
9
9
  readonly origin?: CorsOrigin;
@@ -20,7 +20,7 @@ export interface CorsOptions {
20
20
  export declare const withCors: (options: CorsOptions, handler: RouteHandler) => RouteHandler;
21
21
  /**
22
22
  * `Bun.serve({ routes })` answers a method miss with 404, so a preflight cannot be
23
- * inferred every CORS-enabled path gets its own `OPTIONS` handler, built at boot
23
+ * inferred - every CORS-enabled path gets its own `OPTIONS` handler, built at boot
24
24
  * from the methods that path actually declares.
25
25
  */
26
26
  export declare const preflight: (options: CorsOptions, methods: readonly string[]) => RouteHandler;
@@ -13,7 +13,7 @@ export interface ValidationIssue {
13
13
  }
14
14
  /**
15
15
  * A declared schema rejected the input. Always a 400, and the issues survive into
16
- * the response body a caller cannot fix what it cannot see.
16
+ * the response body - a caller cannot fix what it cannot see.
17
17
  */
18
18
  export declare class ValidationError extends HttpError {
19
19
  readonly source: InputSource;
@@ -2,11 +2,11 @@ import type { BunRequest } from 'bun';
2
2
  import type { RouteInput, RouteSchemas } from '../route/schema.js';
3
3
  /**
4
4
  * Built once per route at boot. A route that declares nothing gets the identity
5
- * reader no parse, no validation, not even a promise.
5
+ * reader - no parse, no validation, not even a promise.
6
6
  *
7
7
  * A reader **returns a promise only when it has something to wait for**. A `body`
8
- * schema always does; `query` and `params` against a synchronous validator which
9
- * zod, Valibot and ArkType all are resolve without one.
8
+ * schema always does; `query` and `params` against a synchronous validator - which
9
+ * zod, Valibot and ArkType all are - resolve without one.
10
10
  */
11
11
  export type InputReader = (req: BunRequest) => RouteInput | Promise<RouteInput>;
12
12
  /**
@@ -4,7 +4,7 @@ export type Next = () => Promise<Response>;
4
4
  /**
5
5
  * The single extension point. A guard is middleware that throws, an interceptor
6
6
  * wraps `next()`, a filter is the error mapper. `ctx` names the route and carries
7
- * what its decorators declared, resolved at boot so a guard costs a Map lookup.
7
+ * what its decorators declared, resolved at boot - so a guard costs a Map lookup.
8
8
  */
9
9
  export interface Middleware {
10
10
  handle(req: BunRequest, ctx: RouteContext, next: Next): Promise<Response>;
@@ -13,8 +13,8 @@ export type RouteHandler = (req: BunRequest) => Promise<Response>;
13
13
  /**
14
14
  * What goes into the `Bun.serve` route table. Wider than `RouteHandler` because
15
15
  * Bun accepts a plain `Response`, which is what lets a route with nothing to
16
- * await skip promises altogether see `buildRoutes`.
16
+ * await skip promises altogether - see `buildRoutes`.
17
17
  */
18
18
  export type ServedHandler = (req: BunRequest) => Response | Promise<Response>;
19
- /** Folded into one closure per route at boot no per-request array iteration. */
19
+ /** Folded into one closure per route at boot - no per-request array iteration. */
20
20
  export declare const compose: (middleware: readonly Middleware[], ctx: RouteContext, handler: RouteHandler) => RouteHandler;
@@ -9,7 +9,7 @@ export interface RequestLoggingOptions {
9
9
  /**
10
10
  * Log the request body. Default **`false`**.
11
11
  *
12
- * Reading it means `req.clone().text()` a second copy of every payload,
12
+ * Reading it means `req.clone().text()` - a second copy of every payload,
13
13
  * buffered and parsed, on the hot path. Measured on the `validate` scenario in
14
14
  * `tools/bench`, turning both body options on costs roughly two thirds of the
15
15
  * throughput. It is also the field most likely to contain a password.
@@ -17,17 +17,17 @@ export interface RequestLoggingOptions {
17
17
  * Turn it on in development, where seeing the payload is the point.
18
18
  */
19
19
  readonly requestBody?: boolean;
20
- /** Log the response body. Default **`false`** same clone-and-buffer cost. */
20
+ /** Log the response body. Default **`false`** - same clone-and-buffer cost. */
21
21
  readonly responseBody?: boolean;
22
- /** Paths to skip entirely a health check polled every second, say. */
22
+ /** Paths to skip entirely - a health check polled every second, say. */
23
23
  readonly ignore?: readonly string[];
24
24
  }
25
25
  /**
26
26
  * One structured entry per request, carrying the request and its response.
27
27
  *
28
28
  * Installed by `HttpFactory.create` unless `requestLogging: false`. It injects
29
- * `Logger` and `RequestContext` both `@dunx/core` contracts, both bound by
30
- * default so it works with no logging module imported, and picks up
29
+ * `Logger` and `RequestContext` - both `@dunx/core` contracts, both bound by
30
+ * default - so it works with no logging module imported, and picks up
31
31
  * `@arkv/logger` automatically once `@dunx/infra/logger` is.
32
32
  *
33
33
  * **One entry, not two.** Nest needs a middleware for the inbound half and an
@@ -42,7 +42,7 @@ export interface RequestLoggingOptions {
42
42
  *
43
43
  * **Nothing here is `async`.** Reading the request or the response body are the
44
44
  * only steps that can ever wait, both are off by default, and both are adopted
45
- * with `.then` rather than awaited the same rule `input.ts` follows, for the
45
+ * with `.then` rather than awaited - the same rule `input.ts` follows, for the
46
46
  * same measured reason. An `async` scope callback alone cost 0.44 µs/request
47
47
  * against a synchronous one on raw `Bun.serve`.
48
48
  */
@@ -7,7 +7,7 @@ import { type ErrorMapper } from './errors.js';
7
7
  import { type Middleware, type RouteHandler, type ServedHandler } from './middleware.js';
8
8
  /** How a `@UseGuards` class becomes an instance. `listen()` passes `app.get`. */
9
9
  export type GuardResolver = (guard: Ctor<Middleware>) => Middleware;
10
- /** `OPTIONS` is never a `@Get`-style route only CORS mounts one. */
10
+ /** `OPTIONS` is never a `@Get`-style route - only CORS mounts one. */
11
11
  export type RouteMethod = HttpMethod | 'OPTIONS';
12
12
  export type BunRoutes = Record<string, Partial<Record<RouteMethod, ServedHandler>>>;
13
13
  /**
@@ -28,12 +28,12 @@ export declare const assertNoCollisions: (discovered: readonly DiscoveredRoute[]
28
28
  export declare const assertNoGatewayCollisions: (discovered: readonly DiscoveredRoute[], gatewayPaths: readonly string[]) => void;
29
29
  /**
30
30
  * The two tables in one. A gateway's `GET` is what Bun's router matches on an
31
- * upgrade the reason no `fetch` handler is needed for a socket to connect.
31
+ * upgrade - the reason no `fetch` handler is needed for a socket to connect.
32
32
  */
33
33
  export declare const withUpgradeRoutes: (routes: BunRoutes, gateways: ReadonlyMap<string, UpgradeHandler>) => ServeRoutes;
34
34
  /**
35
35
  * Bun answers an unmatched path itself, so nothing in the middleware chain ever
36
- * sees it which makes a 404 invisible to request logging, metrics and tracing.
36
+ * sees it - which makes a 404 invisible to request logging, metrics and tracing.
37
37
  *
38
38
  * This is the only `fetch` handler dunx installs, and it is not a router: Bun
39
39
  * still does all the matching, and this runs only once Bun has decided nothing
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The settings `app.set()` accepts. A key has to be declared here to be settable,
3
- * so the map is checked at compile time instead of being a string bag a typo is
3
+ * so the map is checked at compile time instead of being a string bag - a typo is
4
4
  * a type error, not a setting that silently never applies.
5
5
  */
6
6
  export interface AppSettings {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Frozen object plus an indexed-access union, not an `enum`. An enum emits a
3
- * runtime object that no other syntax can produce, which is why the repo bans it
3
+ * runtime object that no other syntax can produce, which is why the repo bans it -
4
4
  * see CLAUDE.md. This gives the same `HttpStatusCode.NOT_FOUND` ergonomics, a
5
5
  * narrower type, and erases cleanly.
6
6
  */
@@ -34,7 +34,7 @@ export declare const discoverGateway: (instance: object) => DiscoveredGateway;
34
34
  export declare const findHandlerMethod: (ctor: Ctor<unknown>) => string | undefined;
35
35
  /**
36
36
  * Gateways are declared in `@Module({ providers })` like any other injectable and
37
- * found here by their marker the same discovery-by-inspection controllers get,
37
+ * found here by their marker - the same discovery-by-inspection controllers get,
38
38
  * with no second registration key to keep in step.
39
39
  */
40
40
  export declare const discoverGateways: (modules: readonly ResolvedModule[], resolve: (token: InjectionToken<unknown>) => unknown) => readonly DiscoveredGateway[];
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The whole wire protocol: one JSON object, an event name, and a payload. It is
3
3
  * only ever read for a gateway that declares at least one `@OnMessage(event)`
4
- * handler a gateway with only a raw `@OnMessage()` never parses anything.
4
+ * handler - a gateway with only a raw `@OnMessage()` never parses anything.
5
5
  */
6
6
  export interface Envelope {
7
7
  readonly event: string;
@@ -9,7 +9,7 @@ export interface Envelope {
9
9
  }
10
10
  export declare const encode: (event: string, data: unknown) => string;
11
11
  /**
12
- * `undefined` for anything that is not an envelope binary frames, invalid JSON,
12
+ * `undefined` for anything that is not an envelope - binary frames, invalid JSON,
13
13
  * a non-object, or a missing `event`. Those fall through to the raw handler
14
14
  * instead of being rejected here.
15
15
  */
@@ -6,11 +6,11 @@ import type { SocketData } from './socket.js';
6
6
  * runtime, not in a JavaScript registry: `socket.subscribe(topic)` is what joins
7
7
  * one, and Bun does the fan-out.
8
8
  *
9
- * Injectable `HttpFactory` binds it, so a service can publish without holding a
9
+ * Injectable - `HttpFactory` binds it, so a service can publish without holding a
10
10
  * socket and without registering anything.
11
11
  *
12
12
  * With a {@link PubSubRelay} attached the same publish also reaches the other
13
- * nodes. Without one the default nothing here touches a broker and the cost is
13
+ * nodes. Without one - the default - nothing here touches a broker and the cost is
14
14
  * exactly Bun's.
15
15
  */
16
16
  export declare class PubSub {
@@ -25,12 +25,12 @@ export declare class PubSub {
25
25
  * Opt into multi-node fan-out: every `publish` from here on also goes to
26
26
  * `relay`, and everything other nodes put on the channel is fanned out locally.
27
27
  *
28
- * `HttpFactory.create(root, { relay })` is the shorthand `listen()` calls this.
28
+ * `HttpFactory.create(root, { relay })` is the shorthand - `listen()` calls this.
29
29
  * Call it directly when the relay has to come out of the container, which is the
30
30
  * case for an app reusing its own `@dunx/infra/redis` connection:
31
31
  * `app.get(PubSub).relayThrough(app.get(RedisConnection))` before `listen()`.
32
32
  *
33
- * A broker that cannot be reached is reported through `onError` and left alone
33
+ * A broker that cannot be reached is reported through `onError` and left alone -
34
34
  * local fan-out is unaffected, and the app boots either way.
35
35
  */
36
36
  relayThrough(relay: PubSubRelay, options?: RelayOptions): Promise<void>;
@@ -10,7 +10,7 @@ export interface RedisRelayOptions {
10
10
  * `0` by default, and that default is not a preference: a `Bun.RedisClient` that
11
11
  * never connects keeps an internal retry timer alive past `close()`, and the
12
12
  * process then never exits. A relay is exactly the connection most likely to be
13
- * absent a single-node deployment with `REDIS_URL` left over from staging
13
+ * absent - a single-node deployment with `REDIS_URL` left over from staging -
14
14
  * so the default has to be the one that lets the app boot, degrade, and still
15
15
  * exit. Raise it when Redis is a hard requirement and you want Bun to reconnect
16
16
  * for you.
@@ -23,7 +23,7 @@ export interface RedisRelayOptions {
23
23
  readonly tls?: boolean | Bun.TLSOptions;
24
24
  }
25
25
  /**
26
- * A {@link PubSubRelay} on `Bun.RedisClient` a Bun global, so this costs
26
+ * A {@link PubSubRelay} on `Bun.RedisClient` - a Bun global, so this costs
27
27
  * `@dunx/http` no dependency at all.
28
28
  *
29
29
  * **Two connections, not one.** A client in subscriber mode rejects every data
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * What `PubSub` needs from something that carries a message to the other nodes:
3
3
  * publish, and subscribe. Nothing else, so anything that already talks to a
4
- * broker satisfies it `@dunx/infra/redis`'s `RedisConnection` does, structurally
4
+ * broker satisfies it - `@dunx/infra/redis`'s `RedisConnection` does, structurally
5
5
  * and with no adapter, and so does a bare `Bun.RedisClient` pair.
6
6
  *
7
7
  * The return types are `unknown` rather than `Promise<void>` deliberately: Bun's
@@ -15,7 +15,7 @@ export interface PubSubRelay {
15
15
  publish(channel: string, message: string): unknown;
16
16
  /**
17
17
  * Deliver every message published to `channel` to `listener`. Called once, with
18
- * one channel pattern subscription is not used, because Bun's `psubscribe`
18
+ * one channel - pattern subscription is not used, because Bun's `psubscribe`
19
19
  * does not work (see docs/bun-apis.md).
20
20
  */
21
21
  subscribe(channel: string, listener: (message: string) => void): unknown;
@@ -34,7 +34,7 @@ export interface RelayOptions {
34
34
  * The one broker channel every topic's frames travel on.
35
35
  *
36
36
  * One channel rather than one per topic, because a node cannot know which
37
- * topics its sockets joined `socket.subscribe()` goes straight into Bun and
37
+ * topics its sockets joined - `socket.subscribe()` goes straight into Bun - and
38
38
  * `psubscribe` is unusable. The cost is that every node reads every relayed
39
39
  * frame and drops the ones for topics it has no local subscriber on, which is a
40
40
  * `server.publish` returning `0`. Two apps sharing a Redis need two channels.
@@ -50,8 +50,8 @@ export interface RelayOptions {
50
50
  */
51
51
  readonly onError?: (error: unknown, phase: RelayPhase) => void;
52
52
  /**
53
- * What to do when the **boot** subscribe fails. Publishing recovers on its own
54
- * every publish retries the broker but a failed subscribe used to be retried
53
+ * What to do when the **boot** subscribe fails. Publishing recovers on its own -
54
+ * every publish retries the broker - but a failed subscribe used to be retried
55
55
  * by nothing, so the node stayed permanently deaf to other nodes while still
56
56
  * looking healthy.
57
57
  *
@@ -69,7 +69,7 @@ export declare const DEFAULT_RELAY_CHANNEL = "dunx:ws";
69
69
  export declare const defaultRelayError: (error: unknown, phase: RelayPhase) => void;
70
70
  /**
71
71
  * One relayed publish: which process published it, which topic it belongs to, and
72
- * the frame itself. `origin` is the whole duplicate-delivery defence the broker
72
+ * the frame itself. `origin` is the whole duplicate-delivery defence - the broker
73
73
  * echoes a publish back to the publisher, and fanning that out locally a second
74
74
  * time would give every client on the originating node the message twice.
75
75
  */
@@ -1,7 +1,7 @@
1
1
  import type { DiscoveredGateway, Invoke } from './discover.js';
2
2
  /**
3
3
  * One gateway reduced to direct references, built once at boot. Dispatch reads
4
- * these fields and nothing else no lookup, no metadata, no DI per message.
4
+ * these fields and nothing else - no lookup, no metadata, no DI per message.
5
5
  */
6
6
  export interface GatewayRuntime {
7
7
  readonly name: string;
@@ -10,7 +10,7 @@ export interface SocketData<T = unknown> {
10
10
  }
11
11
  /**
12
12
  * Bun's native socket, unwrapped. `send`, `subscribe`, `unsubscribe`,
13
- * `isSubscribed`, `publish`, `cork` and `close` are its own methods nothing here
13
+ * `isSubscribed`, `publish`, `cork` and `close` are its own methods - nothing here
14
14
  * reimplements them.
15
15
  */
16
16
  export type Socket<T = unknown> = ServerWebSocket<SocketData<T>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dunx/http",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Bun.serve adapter for the dunx framework: controllers, middleware and WebSocket gateways",
5
5
  "keywords": [
6
6
  "bun",
@@ -13,7 +13,7 @@
13
13
  "websocket"
14
14
  ],
15
15
  "homepage": "https://github.com/petarzarkov/dunx/tree/main/packages/http#readme",
16
- "license": "MIT",
16
+ "license": "Apache-2.0",
17
17
  "author": {
18
18
  "name": "Petar Zarkov",
19
19
  "email": "pzarko1@gmail.com",
@@ -51,7 +51,7 @@
51
51
  "@dunx/core": "workspace:*"
52
52
  },
53
53
  "peerDependencies": {
54
- "@dunx/core": "0.1.0",
54
+ "@dunx/core": "0.2.0",
55
55
  "@types/bun": ">=1.3.0"
56
56
  },
57
57
  "peerDependenciesMeta": {