@rhythmjs/router 0.0.10 → 0.0.11

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/README.md CHANGED
@@ -39,21 +39,34 @@ A fuller runnable version, including nested prefixes and a fallback route, is at
39
39
  - `.get/.post/.put/.patch/.delete(path, ...handlers)` — register a route; `path` may contain `:param` segments.
40
40
  - `.use(fn)` — plain middleware; it takes only functions, so a nested router mounts as `.use(child.middleware())`.
41
41
  - `.middleware()` — this router compiled to a plain middleware: the one form that mounts anywhere, into a `Rhythm` app or into another router. Because the compiled form is opaque, the mounting router's prefix is not applied to it — give the child its full prefix.
42
- - `ctx.json/.text/.html(body, status?)`, `ctx.error(status, message?)`, `ctx.redirect(url, status?)` — response helpers built into the context by the adapters (`createHttpContext` in `@rhythmjs/router/context`).
42
+ - `ctx.json/.text/.html(body, status?)`, `ctx.error(status, message?)`, `ctx.redirect(url, status?)` — response helpers built into the context (`createHttpContext` in `@rhythmjs/router/context`).
43
43
  - `toFetchHandler(app)` — bridges a `Rhythm` app to a Web-standard `(Request) => Promise<Response>` handler.
44
44
 
45
45
  ## Serving
46
46
 
47
- Everything above (`RhythmRouter`, `ctx.response`, etc.) is runtime-agnostic. Two primitives turn an app into a server, both built on [srvx](https://srvx.h3.dev):
47
+ Two primitives turn an app into a server, both coupled to [Bun](https://bun.com) on purpose:
48
48
 
49
- - **`serve(app, options)`** (`@rhythmjs/router/serve`) — starts a server on Node, Bun, or Deno with one identical call; srvx picks the runtime implementation via conditional exports. On Node, requests are lazy: method, url, headers, and body materialize only when middleware touches them.
50
- - **`toFetchHandler(app)`** (`@rhythmjs/router/fetch`) — the universal `(Request) => Promise<Response>` handler, for platforms that invoke you per request instead of letting you own a listener.
49
+ - **`serve(app, options)`** (`@rhythmjs/router/serve`) — starts the app on `Bun.serve` and returns Bun's `Server` (`server.port`, `server.url`, `server.publish`, `server.stop()`).
50
+ - **`toFetchHandler(app)`** (`@rhythmjs/router/fetch`) — the raw `(Request) => Promise<Response>` handler, for composing and testing without a listener.
51
51
 
52
- `serve()` accepts every srvx `ServerOptions` field except `fetch`: `port`, `hostname`, `tls` (HTTPS/HTTP2), `maxRequestBodySize` (an over-limit body read is answered with `413`), `reusePort`, `gracefulShutdown`, plus the three extension points below. Errors thrown in the middleware chain are answered with `500` (or the error's own `status`) without crashing the process; override the mapping with `options.error`.
52
+ `serve()` passes the server fields through to `Bun.serve` — `port`, `hostname`, `unix`, `tls`, `reusePort`, `idleTimeout`, `development`, `maxRequestBodySize` (an over-limit body is rejected without crashing) — plus the extension points below. Every request gets a lazy `request.ip` (from `server.requestIP()`), the field `@rhythmjs/security`'s rate limit and `@rhythmjs/http`'s proxy key off. Errors thrown in the middleware chain are answered with `500` (or the error's own `status`) without crashing the process; override the mapping with `options.error`.
53
+
54
+ ### Static assets
55
+
56
+ The `static` option serves files with `Bun.file` before the app runs; a request no folder answers falls through to your routes. It takes one folder config or an array — folders are probed in order, first match wins:
57
+
58
+ ```ts
59
+ serve(app, {
60
+ port: 3000,
61
+ static: [{ dir: "dist/client", maxAge: 31536000, immutable: true }, { dir: "public" }],
62
+ });
63
+ ```
64
+
65
+ Each entry (`StaticMiddlewareOptions`, also exported from `@rhythmjs/router/static`) takes `dir`, `prefix` (mount point, default the site root), `index` (default `index.html`, served for directory paths), `maxAge`/`immutable` cache control, and `ETag` revalidation with `304`s (`etag`, on by default). Content types come from `Bun.file`. Path traversal is normalized away. `middleware` you pass runs before and wraps the static handlers, so CORS or logging cover asset responses too.
53
66
 
54
67
  ### Extending: CORS, WebSockets, and similar
55
68
 
56
- - **`middleware`** — srvx middlewares (`(request, next) => Response`) run around the whole app, the natural place for CORS, logging, or auth gates:
69
+ - **`middleware`** — serve middlewares (`(request, next) => Response`) run around the whole app, the natural place for CORS, logging, or auth gates:
57
70
 
58
71
  ```ts
59
72
  serve(app, {
@@ -69,90 +82,14 @@ Everything above (`RhythmRouter`, `ctx.response`, etc.) is runtime-agnostic. Two
69
82
  });
70
83
  ```
71
84
 
72
- - **`plugins`** — a plugin receives the live srvx `Server`, the hook for anything below fetch, WebSockets included ([crossws](https://crossws.h3.dev) attaches here; on Node the raw server is at `server.node.server`):
85
+ - **`upgrade` + `websocket`** — the seams for Bun's native WebSockets, shaped for [`@rhythmjs/ws`](https://github.com/rhythmjs/ws). Requests with an `upgrade: websocket` header divert to `upgrade(request, server)` before the app runs (return a `Response` to reject, or `undefined` after `server.upgrade()`); `websocket` is Bun's behavior object, passed through:
73
86
 
74
87
  ```ts
75
- serve(app, { plugins: [(server) => wireWebSockets(server)] });
88
+ import { websocket } from "@rhythmjs/ws";
89
+
90
+ serve(app, { upgrade: ws.upgrade, websocket: websocket() });
76
91
  ```
77
92
 
78
93
  - **`error`** — replace the default error-to-response mapping.
79
94
 
80
- ## Deploying per runtime
81
-
82
- One app definition; each runtime has its own adapter under `@rhythmjs/router/adapters/*`, hono-style: import from your target's adapter and export what the platform expects. Runtimes that speak web-standard `Request`/`Response` natively (Bun, Deno, Vercel) can equally use `toFetchHandler(app)` from `@rhythmjs/router/fetch` — their adapters are aliases for it.
83
-
84
- Every adapter maps errors thrown in the middleware chain to a response (`500`, or the error's own `status`) instead of crashing, and accepts `{ onError }` to replace that mapping: `handle(app, { onError: (error) => new Response("down", { status: 503 }) })`. Platform metadata (Cloudflare `env`/`ctx`, the Netlify context) is readable in middleware via `getRuntime(ctx.request)` from `@rhythmjs/router/context`.
85
-
86
- Fetch-shaped adapters also accept `{ websocket }` — anything with a `handleUpgrade(request, ...extra)` method, such as a [crossws](https://crossws.h3.dev) instance or an `@rhythmjs/ws` adapter. Requests carrying an `upgrade: websocket` header divert to it (with the runtime's extra arguments — Bun's `server`, Deno's `info`, Cloudflare's `env`/`ctx` — forwarded) before the app runs; every other request is untouched.
87
-
88
- **Node** — `getRequestListener` returns a listener for your own `node:http` server:
89
-
90
- ```ts
91
- import { createServer } from "node:http";
92
- import { getRequestListener } from "@rhythmjs/router/adapters/node";
93
-
94
- createServer(getRequestListener(app, { maxRequestBodySize: 1024 * 1024 })).listen(3000);
95
- ```
96
-
97
- (Or skip the adapter entirely and use `serve(app, { port: 3000 })`.)
98
-
99
- **Bun**
100
-
101
- ```ts
102
- import { handle } from "@rhythmjs/router/adapters/bun";
103
-
104
- Bun.serve({ port: 3000, fetch: handle(app) });
105
- ```
106
-
107
- **Deno**
108
-
109
- ```ts
110
- import { handle } from "@rhythmjs/router/adapters/deno";
111
-
112
- Deno.serve({ port: 3000 }, handle(app));
113
- ```
114
-
115
- **Vercel** — in a catch-all route file:
116
-
117
- ```ts
118
- import { handle } from "@rhythmjs/router/adapters/vercel";
119
-
120
- const handler = handle(app);
121
- export const GET = handler;
122
- export const POST = handler; // …and the other methods you serve
123
- ```
124
-
125
- **Cloudflare Workers** — `env` and `ctx` are exposed to middleware as `ctx.request.runtime.cloudflare`:
126
-
127
- ```ts
128
- import { handle } from "@rhythmjs/router/adapters/cloudflare";
129
-
130
- export default { fetch: handle(app) };
131
- ```
132
-
133
- **AWS Lambda** — API Gateway v1/v2 events, translated by srvx:
134
-
135
- ```ts
136
- import { handle } from "@rhythmjs/router/adapters/aws-lambda";
137
-
138
- export const lambda = handle(app);
139
- ```
140
-
141
- **Netlify Edge Functions** — the Netlify context is exposed as `ctx.request.runtime.netlify`:
142
-
143
- ```ts
144
- import { handle } from "@rhythmjs/router/adapters/netlify";
145
-
146
- export default handle(app);
147
- export const config = { path: "/*" };
148
- ```
149
-
150
- **Service workers**
151
-
152
- ```ts
153
- import { handle } from "@rhythmjs/router/adapters/service-worker";
154
-
155
- addEventListener("fetch", handle(app));
156
- ```
157
-
158
- Any other fetch-based runtime works with the raw primitive: `toFetchHandler(app)` from `@rhythmjs/router/fetch`.
95
+ Anything else fetch-shaped composes with the raw primitive: `toFetchHandler(app)` from `@rhythmjs/router/fetch`.
package/dist/context.d.ts CHANGED
@@ -1,2 +1,18 @@
1
- import { a as createHttpContext, i as RhythmRuntime, n as RhythmResponse, o as getRuntime, r as RhythmResponseBody, s as toResponse, t as RhythmHttpContext } from "./context-ChHlG-q0.js";
2
- export { RhythmHttpContext, RhythmResponse, RhythmResponseBody, RhythmRuntime, createHttpContext, getRuntime, toResponse };
1
+ export type RhythmResponseBody = string | ArrayBuffer | Uint8Array | Blob | FormData | URLSearchParams | ReadableStream<Uint8Array> | null;
2
+ export declare class RhythmResponse {
3
+ status: number;
4
+ statusText: string | undefined;
5
+ headers: Headers;
6
+ body: RhythmResponseBody;
7
+ }
8
+ export interface RhythmHttpContext {
9
+ readonly request: Request;
10
+ readonly response: RhythmResponse;
11
+ json(data: unknown, status?: number): void;
12
+ text(body: string, status?: number): void;
13
+ html(body: string, status?: number): void;
14
+ error(status: number, message?: string): void;
15
+ redirect(url: string, status?: number): void;
16
+ }
17
+ export declare function createHttpContext(request: Request): RhythmHttpContext;
18
+ export declare function toResponse(response: RhythmResponse): Response;
package/dist/context.js CHANGED
@@ -1,69 +1,11 @@
1
- //#region src/context.ts
2
- function getRuntime(request) {
3
- return request.runtime;
4
- }
5
- var RhythmResponse = class {
6
- status = 200;
7
- statusText = void 0;
8
- headers = new Headers();
9
- body = null;
1
+ // @bun
2
+ import {
3
+ RhythmResponse2,
4
+ createHttpContext2,
5
+ toResponse2
6
+ } from "./rhythm-router-yj4ffyyg.js";
7
+ export {
8
+ RhythmResponse2 as RhythmResponse,
9
+ createHttpContext2 as createHttpContext,
10
+ toResponse2 as toResponse
10
11
  };
11
- const STATUS_TEXT = {
12
- 400: "Bad Request",
13
- 401: "Unauthorized",
14
- 403: "Forbidden",
15
- 404: "Not Found",
16
- 405: "Method Not Allowed",
17
- 409: "Conflict",
18
- 410: "Gone",
19
- 413: "Payload Too Large",
20
- 415: "Unsupported Media Type",
21
- 422: "Unprocessable Entity",
22
- 429: "Too Many Requests",
23
- 500: "Internal Server Error",
24
- 501: "Not Implemented",
25
- 502: "Bad Gateway",
26
- 503: "Service Unavailable",
27
- 504: "Gateway Timeout"
28
- };
29
- function createHttpContext(request) {
30
- const response = new RhythmResponse();
31
- return {
32
- request,
33
- response,
34
- json(data, status) {
35
- if (status !== void 0) response.status = status;
36
- response.headers.set("content-type", "application/json; charset=utf-8");
37
- response.body = JSON.stringify(data);
38
- },
39
- text(body, status) {
40
- if (status !== void 0) response.status = status;
41
- response.headers.set("content-type", "text/plain; charset=utf-8");
42
- response.body = body;
43
- },
44
- html(body, status) {
45
- if (status !== void 0) response.status = status;
46
- response.headers.set("content-type", "text/html; charset=utf-8");
47
- response.body = body;
48
- },
49
- error(status, message) {
50
- response.status = status;
51
- response.headers.set("content-type", "text/plain; charset=utf-8");
52
- response.body = message ?? STATUS_TEXT[status] ?? `Error ${status}`;
53
- },
54
- redirect(url, status = 302) {
55
- response.status = status;
56
- response.headers.set("location", url);
57
- response.body = null;
58
- }
59
- };
60
- }
61
- function toResponse(response) {
62
- return new Response(response.body, {
63
- status: response.status,
64
- statusText: response.statusText,
65
- headers: response.headers
66
- });
67
- }
68
- //#endregion
69
- export { RhythmResponse, createHttpContext, getRuntime, toResponse };
package/dist/fetch.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- import { t as RhythmHttpContext } from "./context-ChHlG-q0.js";
2
- import { Rhythm } from "@rhythmjs/rhythm";
3
- //#region src/fetch.d.ts
1
+ import type { Rhythm } from "@rhythmjs/rhythm";
2
+ import { type RhythmHttpContext } from "./context";
4
3
  export declare function toFetchHandler<TContext extends RhythmHttpContext, TProviders extends object = {}>(app: Rhythm<RhythmHttpContext, TContext, TProviders>): (request: Request) => Promise<Response>;
5
- //#endregion
package/dist/fetch.js CHANGED
@@ -1,16 +1,8 @@
1
- import { createHttpContext } from "./context.js";
2
- import { FastResponse } from "srvx";
3
- //#region src/fetch.ts
4
- function toFetchHandler(app) {
5
- const run = app.callback();
6
- return async (request) => {
7
- const response = (await run(createHttpContext(request))).response;
8
- return new FastResponse(response.body, {
9
- status: response.status,
10
- statusText: response.statusText,
11
- headers: response.headers
12
- });
13
- };
14
- }
15
- //#endregion
16
- export { toFetchHandler };
1
+ // @bun
2
+ import {
3
+ toFetchHandler2
4
+ } from "./rhythm-router-88dtq5mx.js";
5
+ import"./rhythm-router-yj4ffyyg.js";
6
+ export {
7
+ toFetchHandler2 as toFetchHandler
8
+ };
@@ -0,0 +1,20 @@
1
+ // @bun
2
+ import {
3
+ createHttpContext2
4
+ } from "./rhythm-router-yj4ffyyg.js";
5
+
6
+ // src/fetch.ts
7
+ function toFetchHandler2(app) {
8
+ const run = app.callback();
9
+ return async (request) => {
10
+ const ctx = await run(createHttpContext2(request));
11
+ const response = ctx.response;
12
+ return new Response(response.body, {
13
+ status: response.status,
14
+ statusText: response.statusText,
15
+ headers: response.headers
16
+ });
17
+ };
18
+ }
19
+
20
+ export { toFetchHandler2 };
@@ -0,0 +1,53 @@
1
+ // @bun
2
+ // src/static.ts
3
+ import { join, normalize, sep } from "path";
4
+ function normalizePrefix(prefix) {
5
+ const withLeading = prefix.startsWith("/") ? prefix : `/${prefix}`;
6
+ return withLeading.endsWith("/") ? withLeading.slice(0, -1) : withLeading;
7
+ }
8
+ function staticMiddleware2(options) {
9
+ const prefix = normalizePrefix(options.prefix ?? "/");
10
+ const index = options.index ?? "index.html";
11
+ const useEtag = options.etag ?? true;
12
+ const cacheControl = options.maxAge === undefined ? undefined : `max-age=${options.maxAge}${options.immutable ? ", immutable" : ""}`;
13
+ return async (request, next) => {
14
+ if (request.method !== "GET" && request.method !== "HEAD")
15
+ return next();
16
+ let pathname;
17
+ try {
18
+ pathname = decodeURIComponent(new URL(request.url).pathname);
19
+ } catch {
20
+ return next();
21
+ }
22
+ if (prefix !== "" && pathname !== prefix && !pathname.startsWith(`${prefix}/`))
23
+ return next();
24
+ const relative = normalize(pathname.slice(prefix.length));
25
+ if (relative.includes("\x00") || relative === ".." || relative.startsWith(`..${sep}`))
26
+ return next();
27
+ const base = join(options.dir, relative);
28
+ const candidates = pathname.endsWith("/") ? [join(base, index)] : [base, join(base, index)];
29
+ let file;
30
+ for (const candidate of candidates) {
31
+ const found = Bun.file(candidate);
32
+ if (await found.exists()) {
33
+ file = found;
34
+ break;
35
+ }
36
+ }
37
+ if (file === undefined)
38
+ return next();
39
+ const headers = new Headers;
40
+ if (cacheControl !== undefined)
41
+ headers.set("cache-control", cacheControl);
42
+ if (useEtag) {
43
+ const etag = `"${file.size.toString(16)}-${Math.floor(file.lastModified).toString(16)}"`;
44
+ headers.set("etag", etag);
45
+ if (request.headers.get("if-none-match") === etag) {
46
+ return new Response(null, { status: 304, headers });
47
+ }
48
+ }
49
+ return new Response(file, { headers });
50
+ };
51
+ }
52
+
53
+ export { staticMiddleware2 };
@@ -0,0 +1,70 @@
1
+ // @bun
2
+ // src/context.ts
3
+ class RhythmResponse2 {
4
+ status = 200;
5
+ statusText = undefined;
6
+ headers = new Headers;
7
+ body = null;
8
+ }
9
+ var STATUS_TEXT = {
10
+ 400: "Bad Request",
11
+ 401: "Unauthorized",
12
+ 403: "Forbidden",
13
+ 404: "Not Found",
14
+ 405: "Method Not Allowed",
15
+ 409: "Conflict",
16
+ 410: "Gone",
17
+ 413: "Payload Too Large",
18
+ 415: "Unsupported Media Type",
19
+ 422: "Unprocessable Entity",
20
+ 429: "Too Many Requests",
21
+ 500: "Internal Server Error",
22
+ 501: "Not Implemented",
23
+ 502: "Bad Gateway",
24
+ 503: "Service Unavailable",
25
+ 504: "Gateway Timeout"
26
+ };
27
+ function createHttpContext2(request) {
28
+ const response = new RhythmResponse2;
29
+ return {
30
+ request,
31
+ response,
32
+ json(data, status) {
33
+ if (status !== undefined)
34
+ response.status = status;
35
+ response.headers.set("content-type", "application/json; charset=utf-8");
36
+ response.body = JSON.stringify(data);
37
+ },
38
+ text(body, status) {
39
+ if (status !== undefined)
40
+ response.status = status;
41
+ response.headers.set("content-type", "text/plain; charset=utf-8");
42
+ response.body = body;
43
+ },
44
+ html(body, status) {
45
+ if (status !== undefined)
46
+ response.status = status;
47
+ response.headers.set("content-type", "text/html; charset=utf-8");
48
+ response.body = body;
49
+ },
50
+ error(status, message) {
51
+ response.status = status;
52
+ response.headers.set("content-type", "text/plain; charset=utf-8");
53
+ response.body = message ?? STATUS_TEXT[status] ?? `Error ${status}`;
54
+ },
55
+ redirect(url, status = 302) {
56
+ response.status = status;
57
+ response.headers.set("location", url);
58
+ response.body = null;
59
+ }
60
+ };
61
+ }
62
+ function toResponse2(response) {
63
+ return new Response(response.body, {
64
+ status: response.status,
65
+ statusText: response.statusText,
66
+ headers: response.headers
67
+ });
68
+ }
69
+
70
+ export { RhythmResponse2, createHttpContext2, toResponse2 };
@@ -1,40 +1,39 @@
1
- import { t as RhythmHttpContext } from "./context-ChHlG-q0.js";
2
- import { DeriveMiddleware, Middleware } from "@rhythmjs/rhythm/types";
3
- //#region src/rhythm-router.d.ts
1
+ import type { DeriveMiddleware, Middleware } from "@rhythmjs/rhythm/types";
2
+ import type { RhythmHttpContext } from "./context";
4
3
  export interface RhythmRouterContext {
5
- readonly params: Readonly<Record<string, string>>;
4
+ readonly params: Readonly<Record<string, string>>;
6
5
  }
7
6
  export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
8
7
  export interface RhythmRouterOptions {
9
- prefix?: string;
8
+ prefix?: string;
10
9
  }
11
10
  export type RouterEntry = {
12
- readonly kind: "middleware";
13
- readonly fn: Middleware<any>;
11
+ readonly kind: "middleware";
12
+ readonly fn: Middleware<any>;
14
13
  } | {
15
- readonly kind: "route";
16
- readonly method: HttpMethod;
17
- readonly path: string;
18
- readonly handlers: readonly Middleware<any>[];
14
+ readonly kind: "route";
15
+ readonly method: HttpMethod;
16
+ readonly path: string;
17
+ readonly handlers: readonly Middleware<any>[];
19
18
  };
20
19
  type RouteHandler<TContext> = Middleware<TContext & RhythmRouterContext>;
21
20
  export declare function joinPath(prefix: string, path: string): string;
22
21
  export declare class RhythmRouter<TContext extends RhythmHttpContext = RhythmHttpContext> {
23
- #private;
24
- constructor(options?: RhythmRouterOptions);
25
- get entries(): readonly RouterEntry[];
26
- use<TExtra extends object>(fn: DeriveMiddleware<TContext, TExtra>): RhythmRouter<TContext & TExtra>;
27
- use(fn: Middleware<TContext>): this;
28
- get<TExtra extends object>(path: string, middleware: DeriveMiddleware<TContext & RhythmRouterContext, TExtra>, ...handlers: RouteHandler<TContext & TExtra>[]): this;
29
- get(path: string, ...handlers: RouteHandler<TContext>[]): this;
30
- post<TExtra extends object>(path: string, middleware: DeriveMiddleware<TContext & RhythmRouterContext, TExtra>, ...handlers: RouteHandler<TContext & TExtra>[]): this;
31
- post(path: string, ...handlers: RouteHandler<TContext>[]): this;
32
- put<TExtra extends object>(path: string, middleware: DeriveMiddleware<TContext & RhythmRouterContext, TExtra>, ...handlers: RouteHandler<TContext & TExtra>[]): this;
33
- put(path: string, ...handlers: RouteHandler<TContext>[]): this;
34
- patch<TExtra extends object>(path: string, middleware: DeriveMiddleware<TContext & RhythmRouterContext, TExtra>, ...handlers: RouteHandler<TContext & TExtra>[]): this;
35
- patch(path: string, ...handlers: RouteHandler<TContext>[]): this;
36
- delete<TExtra extends object>(path: string, middleware: DeriveMiddleware<TContext & RhythmRouterContext, TExtra>, ...handlers: RouteHandler<TContext & TExtra>[]): this;
37
- delete(path: string, ...handlers: RouteHandler<TContext>[]): this;
38
- middleware(): Middleware<TContext>;
22
+ #private;
23
+ constructor(options?: RhythmRouterOptions);
24
+ get entries(): readonly RouterEntry[];
25
+ use<TExtra extends object>(fn: DeriveMiddleware<TContext, TExtra>): RhythmRouter<TContext & TExtra>;
26
+ use(fn: Middleware<TContext>): this;
27
+ get<TExtra extends object>(path: string, middleware: DeriveMiddleware<TContext & RhythmRouterContext, TExtra>, ...handlers: RouteHandler<TContext & TExtra>[]): this;
28
+ get(path: string, ...handlers: RouteHandler<TContext>[]): this;
29
+ post<TExtra extends object>(path: string, middleware: DeriveMiddleware<TContext & RhythmRouterContext, TExtra>, ...handlers: RouteHandler<TContext & TExtra>[]): this;
30
+ post(path: string, ...handlers: RouteHandler<TContext>[]): this;
31
+ put<TExtra extends object>(path: string, middleware: DeriveMiddleware<TContext & RhythmRouterContext, TExtra>, ...handlers: RouteHandler<TContext & TExtra>[]): this;
32
+ put(path: string, ...handlers: RouteHandler<TContext>[]): this;
33
+ patch<TExtra extends object>(path: string, middleware: DeriveMiddleware<TContext & RhythmRouterContext, TExtra>, ...handlers: RouteHandler<TContext & TExtra>[]): this;
34
+ patch(path: string, ...handlers: RouteHandler<TContext>[]): this;
35
+ delete<TExtra extends object>(path: string, middleware: DeriveMiddleware<TContext & RhythmRouterContext, TExtra>, ...handlers: RouteHandler<TContext & TExtra>[]): this;
36
+ delete(path: string, ...handlers: RouteHandler<TContext>[]): this;
37
+ middleware(): Middleware<TContext>;
39
38
  }
40
- //#endregion
39
+ export {};
@@ -1,94 +1,92 @@
1
+ // @bun
2
+ // src/rhythm-router.ts
1
3
  import { compose } from "@rhythmjs/rhythm/compose";
2
4
  import { addRoute, createRouter, findRoute } from "rou3";
3
- //#region src/rhythm-router.ts
4
5
  function joinPath(prefix, path) {
5
- if (!prefix) return path;
6
- return `${prefix.endsWith("/") ? prefix.slice(0, -1) : prefix}${path.startsWith("/") ? path : `/${path}`}`;
6
+ if (!prefix)
7
+ return path;
8
+ const trimmedPrefix = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
9
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
10
+ return `${trimmedPrefix}${normalizedPath}`;
7
11
  }
8
- var RhythmRouter = class {
9
- #options;
10
- #entries = [];
11
- constructor(options = {}) {
12
- this.#options = options;
13
- }
14
- get #prefix() {
15
- return this.#options.prefix ?? "";
16
- }
17
- get entries() {
18
- return [...this.#entries];
19
- }
20
- use(fn) {
21
- if (typeof fn !== "function") throw new TypeError("middleware must be a function!");
22
- this.#entries.push({
23
- kind: "middleware",
24
- fn
25
- });
26
- return this;
27
- }
28
- #route(method, path, handlers) {
29
- this.#entries.push({
30
- kind: "route",
31
- method,
32
- path: joinPath(this.#prefix, path),
33
- handlers
34
- });
35
- return this;
36
- }
37
- get(path, ...handlers) {
38
- return this.#route("GET", path, handlers);
39
- }
40
- post(path, ...handlers) {
41
- return this.#route("POST", path, handlers);
42
- }
43
- put(path, ...handlers) {
44
- return this.#route("PUT", path, handlers);
45
- }
46
- patch(path, ...handlers) {
47
- return this.#route("PATCH", path, handlers);
48
- }
49
- delete(path, ...handlers) {
50
- return this.#route("DELETE", path, handlers);
51
- }
52
- #compile() {
53
- const dispatchFor = (tree) => {
54
- return async (ctx, next) => {
55
- const match = findRoute(tree, ctx.request.method, new URL(ctx.request.url).pathname);
56
- if (!match) {
57
- await next();
58
- return;
59
- }
60
- await match.data({
61
- ...ctx,
62
- params: match.params ?? {}
63
- }, next);
64
- };
65
- };
66
- const stack = [];
67
- let i = 0;
68
- while (i < this.#entries.length) {
69
- const entry = this.#entries[i];
70
- if (entry.kind === "middleware") {
71
- stack.push(entry.fn);
72
- i++;
73
- continue;
74
- }
75
- const tree = createRouter();
76
- while (i < this.#entries.length) {
77
- const route = this.#entries[i];
78
- if (route.kind !== "route") break;
79
- addRoute(tree, route.method, route.path, compose(route.handlers));
80
- i++;
81
- }
82
- stack.push(dispatchFor(tree));
83
- }
84
- return compose(stack);
85
- }
86
- middleware() {
87
- const fn = this.#compile();
88
- return async (ctx, next) => {
89
- await fn(ctx, next);
90
- };
91
- }
12
+
13
+ class RhythmRouter {
14
+ #options;
15
+ #entries = [];
16
+ constructor(options = {}) {
17
+ this.#options = options;
18
+ }
19
+ get #prefix() {
20
+ return this.#options.prefix ?? "";
21
+ }
22
+ get entries() {
23
+ return [...this.#entries];
24
+ }
25
+ use(fn) {
26
+ if (typeof fn !== "function")
27
+ throw new TypeError("middleware must be a function!");
28
+ this.#entries.push({ kind: "middleware", fn });
29
+ return this;
30
+ }
31
+ #route(method, path, handlers) {
32
+ this.#entries.push({ kind: "route", method, path: joinPath(this.#prefix, path), handlers });
33
+ return this;
34
+ }
35
+ get(path, ...handlers) {
36
+ return this.#route("GET", path, handlers);
37
+ }
38
+ post(path, ...handlers) {
39
+ return this.#route("POST", path, handlers);
40
+ }
41
+ put(path, ...handlers) {
42
+ return this.#route("PUT", path, handlers);
43
+ }
44
+ patch(path, ...handlers) {
45
+ return this.#route("PATCH", path, handlers);
46
+ }
47
+ delete(path, ...handlers) {
48
+ return this.#route("DELETE", path, handlers);
49
+ }
50
+ #compile() {
51
+ const dispatchFor = (tree) => {
52
+ return async (ctx, next) => {
53
+ const match = findRoute(tree, ctx.request.method, new URL(ctx.request.url).pathname);
54
+ if (!match) {
55
+ await next();
56
+ return;
57
+ }
58
+ await match.data({ ...ctx, params: match.params ?? {} }, next);
59
+ };
60
+ };
61
+ const stack = [];
62
+ let i = 0;
63
+ while (i < this.#entries.length) {
64
+ const entry = this.#entries[i];
65
+ if (entry.kind === "middleware") {
66
+ stack.push(entry.fn);
67
+ i++;
68
+ continue;
69
+ }
70
+ const tree = createRouter();
71
+ while (i < this.#entries.length) {
72
+ const route = this.#entries[i];
73
+ if (route.kind !== "route")
74
+ break;
75
+ addRoute(tree, route.method, route.path, compose(route.handlers));
76
+ i++;
77
+ }
78
+ stack.push(dispatchFor(tree));
79
+ }
80
+ return compose(stack);
81
+ }
82
+ middleware() {
83
+ const fn = this.#compile();
84
+ return async (ctx, next) => {
85
+ await fn(ctx, next);
86
+ };
87
+ }
88
+ }
89
+ export {
90
+ RhythmRouter,
91
+ joinPath
92
92
  };
93
- //#endregion
94
- export { RhythmRouter, joinPath };
package/dist/serve.d.ts CHANGED
@@ -1,9 +1,31 @@
1
- import { t as RhythmHttpContext } from "./context-ChHlG-q0.js";
2
- import { ErrorHandler, Server, ServerMiddleware, ServerOptions, ServerPlugin } from "srvx";
3
- import { Rhythm } from "@rhythmjs/rhythm";
4
- //#region src/serve.d.ts
5
- export type RhythmServeOptions = Omit<ServerOptions, "fetch">;
1
+ import type { Rhythm } from "@rhythmjs/rhythm";
2
+ import type { RhythmHttpContext } from "./context";
3
+ import { type StaticMiddlewareOptions } from "./static";
4
+ export type { StaticMiddlewareOptions };
5
+ export type Server = Bun.Server<unknown>;
6
+ export type ErrorHandler = (error: unknown) => Response | Promise<Response>;
7
+ export type ServeMiddleware = (request: Request, next: () => Promise<Response>) => Response | Promise<Response>;
8
+ export type UpgradeHandler = (request: Request, server: Server) => Response | undefined | null | Promise<Response | undefined> | null;
9
+ export interface RhythmServeOptions {
10
+ port?: number;
11
+ hostname?: string;
12
+ unix?: string;
13
+ reusePort?: boolean;
14
+ idleTimeout?: number;
15
+ development?: boolean;
16
+ maxRequestBodySize?: number;
17
+ tls?: Bun.TLSOptions;
18
+ error?: ErrorHandler;
19
+ middleware?: ServeMiddleware[];
20
+ static?: StaticMiddlewareOptions | StaticMiddlewareOptions[];
21
+ /** Bun websocket behavior; pair with `upgrade` to accept connections. */
22
+ websocket?: Bun.WebSocketHandler<never>;
23
+ /**
24
+ * Called for websocket upgrade requests — `RhythmWs.upgrade` fits directly.
25
+ * A Response rejects, undefined means `server.upgrade()` succeeded, and
26
+ * null falls through to the HTTP app.
27
+ */
28
+ upgrade?: UpgradeHandler;
29
+ }
6
30
  export declare const errorToResponse: ErrorHandler;
7
31
  export declare function serve<TContext extends RhythmHttpContext, TProviders extends object = {}>(app: Rhythm<RhythmHttpContext, TContext, TProviders>, options?: RhythmServeOptions): Server;
8
- //#endregion
9
- export type { ErrorHandler, Server, ServerMiddleware, ServerPlugin };
package/dist/serve.js CHANGED
@@ -1,21 +1,67 @@
1
- import { toFetchHandler } from "./fetch.js";
2
- import { serve as serve$1 } from "srvx";
3
- //#region src/serve.ts
4
- const errorToResponse = (error) => {
5
- const status = error.status ?? error.statusCode ?? 500;
6
- const message = status >= 500 ? "Internal Server Error" : error instanceof Error ? error.message : String(error);
7
- if (status >= 500) console.error(error);
8
- return new Response(message, {
9
- status,
10
- headers: { "content-type": "text/plain; charset=utf-8" }
11
- });
1
+ // @bun
2
+ import"./rhythm-router-yj4ffyyg.js";
3
+ import {
4
+ toFetchHandler2
5
+ } from "./rhythm-router-88dtq5mx.js";
6
+ import {
7
+ staticMiddleware2
8
+ } from "./rhythm-router-n08cmxbj.js";
9
+
10
+ // src/serve.ts
11
+ var errorToResponse = (error) => {
12
+ const status = error.status ?? error.statusCode ?? 500;
13
+ const message = status >= 500 ? "Internal Server Error" : error instanceof Error ? error.message : String(error);
14
+ if (status >= 500)
15
+ console.error(error);
16
+ return new Response(message, {
17
+ status,
18
+ headers: { "content-type": "text/plain; charset=utf-8" }
19
+ });
12
20
  };
13
21
  function serve(app, options = {}) {
14
- return serve$1({
15
- error: errorToResponse,
16
- ...options,
17
- fetch: toFetchHandler(app)
18
- });
22
+ const {
23
+ static: staticOptions,
24
+ middleware = [],
25
+ error = errorToResponse,
26
+ websocket,
27
+ upgrade,
28
+ ...bunOptions
29
+ } = options;
30
+ const assets = staticOptions === undefined ? [] : [staticOptions].flat().map(staticMiddleware2);
31
+ const chain = [...middleware, ...assets];
32
+ const handler = toFetchHandler2(app);
33
+ const dispatch = (request) => {
34
+ let index = -1;
35
+ const next = async (step) => {
36
+ if (step <= index)
37
+ throw new Error("next() called multiple times");
38
+ index = step;
39
+ return step < chain.length ? chain[step](request, () => next(step + 1)) : handler(request);
40
+ };
41
+ return next(0);
42
+ };
43
+ return Bun.serve({
44
+ ...bunOptions,
45
+ ...websocket === undefined ? {} : { websocket },
46
+ async fetch(request, server) {
47
+ try {
48
+ Object.defineProperty(request, "ip", {
49
+ configurable: true,
50
+ get: () => server.requestIP(request)?.address
51
+ });
52
+ if (upgrade !== undefined && request.headers.get("upgrade")?.toLowerCase() === "websocket") {
53
+ const result = await upgrade(request, server);
54
+ if (result !== null)
55
+ return result;
56
+ }
57
+ return await dispatch(request);
58
+ } catch (thrown) {
59
+ return error(thrown);
60
+ }
61
+ }
62
+ });
19
63
  }
20
- //#endregion
21
- export { errorToResponse, serve };
64
+ export {
65
+ errorToResponse,
66
+ serve
67
+ };
@@ -0,0 +1,10 @@
1
+ import type { ServeMiddleware } from "./serve";
2
+ export interface StaticMiddlewareOptions {
3
+ dir: string;
4
+ prefix?: string;
5
+ index?: string;
6
+ maxAge?: number;
7
+ immutable?: boolean;
8
+ etag?: boolean;
9
+ }
10
+ export declare function staticMiddleware(options: StaticMiddlewareOptions): ServeMiddleware;
package/dist/static.js ADDED
@@ -0,0 +1,7 @@
1
+ // @bun
2
+ import {
3
+ staticMiddleware2
4
+ } from "./rhythm-router-n08cmxbj.js";
5
+ export {
6
+ staticMiddleware2 as staticMiddleware
7
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rhythmjs/router",
3
- "version": "0.0.10",
4
- "description": "HTTP router for the Rhythm middleware kernel, matching with rou3, with Bun, Node, and Deno adapters.",
3
+ "version": "0.0.11",
4
+ "description": "Bun-native HTTP router for the Rhythm middleware kernel, matching with rou3 and serving with Bun.serve.",
5
5
  "homepage": "https://rhythm.js.org/router",
6
6
  "license": "ISC",
7
7
  "repository": {
@@ -31,37 +31,9 @@
31
31
  "types": "./dist/serve.d.ts",
32
32
  "default": "./dist/serve.js"
33
33
  },
34
- "./adapters/node": {
35
- "types": "./dist/adapters/node.d.ts",
36
- "default": "./dist/adapters/node.js"
37
- },
38
- "./adapters/bun": {
39
- "types": "./dist/adapters/bun.d.ts",
40
- "default": "./dist/adapters/bun.js"
41
- },
42
- "./adapters/deno": {
43
- "types": "./dist/adapters/deno.d.ts",
44
- "default": "./dist/adapters/deno.js"
45
- },
46
- "./adapters/vercel": {
47
- "types": "./dist/adapters/vercel.d.ts",
48
- "default": "./dist/adapters/vercel.js"
49
- },
50
- "./adapters/netlify": {
51
- "types": "./dist/adapters/netlify.d.ts",
52
- "default": "./dist/adapters/netlify.js"
53
- },
54
- "./adapters/cloudflare": {
55
- "types": "./dist/adapters/cloudflare.d.ts",
56
- "default": "./dist/adapters/cloudflare.js"
57
- },
58
- "./adapters/aws-lambda": {
59
- "types": "./dist/adapters/aws-lambda.d.ts",
60
- "default": "./dist/adapters/aws-lambda.js"
61
- },
62
- "./adapters/service-worker": {
63
- "types": "./dist/adapters/service-worker.d.ts",
64
- "default": "./dist/adapters/service-worker.js"
34
+ "./static": {
35
+ "types": "./dist/static.d.ts",
36
+ "default": "./dist/static.js"
65
37
  },
66
38
  "./adapters/context": {
67
39
  "types": "./dist/context.d.ts",
@@ -74,15 +46,19 @@
74
46
  },
75
47
  "dependencies": {
76
48
  "rou3": "^0.11.0",
77
- "srvx": "^1.0.5",
78
- "@rhythmjs/rhythm": "0.0.10"
49
+ "@rhythmjs/rhythm": "0.0.11"
79
50
  },
80
51
  "devDependencies": {
52
+ "@types/bun": "^1.2.0",
81
53
  "@types/node": "^26.6.3",
82
- "typescript": "^7.0.2",
83
- "vite-plus": "^1.0.0"
54
+ "typescript": "^7.0.2"
84
55
  },
85
56
  "engines": {
86
- "node": ">=20.19.0"
57
+ "bun": ">=1.2.0"
58
+ },
59
+ "scripts": {
60
+ "build": "bun build src/rhythm-router.ts src/context.ts src/fetch.ts src/serve.ts src/static.ts --outdir dist --root src --format esm --target bun --packages external --splitting && tsc -p tsconfig.build.json",
61
+ "typecheck": "tsc --noEmit",
62
+ "test": "bun test"
87
63
  }
88
64
  }
@@ -1,8 +0,0 @@
1
- import { t as RhythmHttpContext } from "../context-ChHlG-q0.js";
2
- import { t as AdapterOptions } from "../base-DwJhCkwZ.js";
3
- import { AWSLambdaHandler } from "srvx/aws-lambda";
4
- import { Rhythm } from "@rhythmjs/rhythm";
5
- //#region src/adapters/aws-lambda.d.ts
6
- export declare function handle<TContext extends RhythmHttpContext, TProviders extends object = {}>(app: Rhythm<RhythmHttpContext, TContext, TProviders>, options?: AdapterOptions): AWSLambdaHandler;
7
- //#endregion
8
- export type { AWSLambdaHandler, AdapterOptions };
@@ -1,9 +0,0 @@
1
- import { t as createFetchAdapter } from "../base-BMFEzT_g.js";
2
- import { toLambdaHandler } from "srvx/aws-lambda";
3
- //#region src/adapters/aws-lambda.ts
4
- const fetchHandle = createFetchAdapter();
5
- function handle(app, options = {}) {
6
- return toLambdaHandler({ fetch: fetchHandle(app, options) });
7
- }
8
- //#endregion
9
- export { handle };
@@ -1,5 +0,0 @@
1
- import { n as FetchAdapter, t as AdapterOptions } from "../base-DwJhCkwZ.js";
2
- //#region src/adapters/bun.d.ts
3
- export declare const handle: FetchAdapter<[server?: unknown]>;
4
- //#endregion
5
- export type { AdapterOptions };
@@ -1,5 +0,0 @@
1
- import { t as createFetchAdapter } from "../base-BMFEzT_g.js";
2
- //#region src/adapters/bun.ts
3
- const handle = createFetchAdapter();
4
- //#endregion
5
- export { handle };
@@ -1,5 +0,0 @@
1
- import { n as FetchAdapter, t as AdapterOptions } from "../base-DwJhCkwZ.js";
2
- //#region src/adapters/cloudflare.d.ts
3
- export declare const handle: FetchAdapter<[env?: unknown, context?: unknown]>;
4
- //#endregion
5
- export type { AdapterOptions };
@@ -1,13 +0,0 @@
1
- import { n as setRuntime, t as createFetchAdapter } from "../base-BMFEzT_g.js";
2
- //#region src/adapters/cloudflare.ts
3
- const handle = createFetchAdapter((request, env, context) => {
4
- if (env !== void 0 || context !== void 0) setRuntime(request, {
5
- name: "cloudflare",
6
- cloudflare: {
7
- env,
8
- context
9
- }
10
- });
11
- });
12
- //#endregion
13
- export { handle };
@@ -1,5 +0,0 @@
1
- import { n as FetchAdapter, t as AdapterOptions } from "../base-DwJhCkwZ.js";
2
- //#region src/adapters/deno.d.ts
3
- export declare const handle: FetchAdapter<[info?: unknown]>;
4
- //#endregion
5
- export type { AdapterOptions };
@@ -1,5 +0,0 @@
1
- import { t as createFetchAdapter } from "../base-BMFEzT_g.js";
2
- //#region src/adapters/deno.ts
3
- const handle = createFetchAdapter();
4
- //#endregion
5
- export { handle };
@@ -1,5 +0,0 @@
1
- import { n as FetchAdapter, t as AdapterOptions } from "../base-DwJhCkwZ.js";
2
- //#region src/adapters/netlify.d.ts
3
- export declare const handle: FetchAdapter<[context?: unknown]>;
4
- //#endregion
5
- export type { AdapterOptions };
@@ -1,10 +0,0 @@
1
- import { n as setRuntime, t as createFetchAdapter } from "../base-BMFEzT_g.js";
2
- //#region src/adapters/netlify.ts
3
- const handle = createFetchAdapter((request, context) => {
4
- if (context !== void 0) setRuntime(request, {
5
- name: "netlify",
6
- netlify: { context }
7
- });
8
- });
9
- //#endregion
10
- export { handle };
@@ -1,11 +0,0 @@
1
- import { t as RhythmHttpContext } from "../context-ChHlG-q0.js";
2
- import { t as AdapterOptions } from "../base-DwJhCkwZ.js";
3
- import { NodeHttp1Handler, NodeHttp2Handler } from "srvx";
4
- import { Rhythm } from "@rhythmjs/rhythm";
5
- //#region src/adapters/node.d.ts
6
- export interface NodeHandlerOptions extends AdapterOptions {
7
- maxRequestBodySize?: number;
8
- }
9
- export type NodeHandler = NodeHttp1Handler & NodeHttp2Handler;
10
- export declare function getRequestListener<TContext extends RhythmHttpContext, TProviders extends object = {}>(app: Rhythm<RhythmHttpContext, TContext, TProviders>, options?: NodeHandlerOptions): NodeHandler;
11
- //#endregion
@@ -1,9 +0,0 @@
1
- import { t as createFetchAdapter } from "../base-BMFEzT_g.js";
2
- import { toNodeHandler } from "srvx/node";
3
- //#region src/adapters/node.ts
4
- const fetchHandle = createFetchAdapter();
5
- function getRequestListener(app, options = {}) {
6
- return toNodeHandler(fetchHandle(app, options), options);
7
- }
8
- //#endregion
9
- export { getRequestListener };
@@ -1,11 +0,0 @@
1
- import { t as RhythmHttpContext } from "../context-ChHlG-q0.js";
2
- import { t as AdapterOptions } from "../base-DwJhCkwZ.js";
3
- import { Rhythm } from "@rhythmjs/rhythm";
4
- //#region src/adapters/service-worker.d.ts
5
- interface FetchEventLike {
6
- readonly request: Request;
7
- respondWith(response: Response | Promise<Response>): void;
8
- }
9
- export declare function handle<TContext extends RhythmHttpContext, TProviders extends object = {}>(app: Rhythm<RhythmHttpContext, TContext, TProviders>, options?: AdapterOptions): (event: FetchEventLike) => void;
10
- //#endregion
11
- export type { AdapterOptions };
@@ -1,11 +0,0 @@
1
- import { t as createFetchAdapter } from "../base-BMFEzT_g.js";
2
- //#region src/adapters/service-worker.ts
3
- const fetchHandle = createFetchAdapter();
4
- function handle(app, options = {}) {
5
- const handler = fetchHandle(app, options);
6
- return (event) => {
7
- event.respondWith(handler(event.request));
8
- };
9
- }
10
- //#endregion
11
- export { handle };
@@ -1,5 +0,0 @@
1
- import { n as FetchAdapter, t as AdapterOptions } from "../base-DwJhCkwZ.js";
2
- //#region src/adapters/vercel.d.ts
3
- export declare const handle: FetchAdapter;
4
- //#endregion
5
- export type { AdapterOptions };
@@ -1,5 +0,0 @@
1
- import { t as createFetchAdapter } from "../base-BMFEzT_g.js";
2
- //#region src/adapters/vercel.ts
3
- const handle = createFetchAdapter();
4
- //#endregion
5
- export { handle };
@@ -1,26 +0,0 @@
1
- import { toFetchHandler } from "./fetch.js";
2
- import { errorToResponse } from "./serve.js";
3
- //#region src/adapters/base.ts
4
- function setRuntime(request, runtime) {
5
- Object.defineProperty(request, "runtime", {
6
- value: runtime,
7
- configurable: true
8
- });
9
- }
10
- function createFetchAdapter(enrich) {
11
- return (app, options = {}) => {
12
- const handler = toFetchHandler(app);
13
- const onError = options.onError ?? errorToResponse;
14
- return async (request, ...extra) => {
15
- if (options.websocket && request.headers.get("upgrade")?.toLowerCase() === "websocket") return options.websocket.handleUpgrade(request, ...extra);
16
- try {
17
- enrich?.(request, ...extra);
18
- return await handler(request);
19
- } catch (error) {
20
- return onError(error);
21
- }
22
- };
23
- };
24
- }
25
- //#endregion
26
- export { setRuntime as n, createFetchAdapter as t };
@@ -1,14 +0,0 @@
1
- import { t as RhythmHttpContext } from "./context-ChHlG-q0.js";
2
- import { ErrorHandler, ServerRuntimeContext } from "srvx";
3
- import { Rhythm } from "@rhythmjs/rhythm";
4
- //#region src/adapters/base.d.ts
5
- interface WebSocketUpgrader {
6
- handleUpgrade(request: Request, ...extra: unknown[]): Response | undefined | Promise<Response | undefined>;
7
- }
8
- interface AdapterOptions {
9
- onError?: ErrorHandler;
10
- websocket?: WebSocketUpgrader;
11
- }
12
- type FetchAdapter<TExtra extends unknown[] = []> = <TContext extends RhythmHttpContext, TProviders extends object = {}>(app: Rhythm<RhythmHttpContext, TContext, TProviders>, options?: AdapterOptions) => (request: Request, ...extra: TExtra) => Promise<Response>;
13
- //#endregion
14
- export { FetchAdapter as n, AdapterOptions as t };
@@ -1,24 +0,0 @@
1
- import { ServerRuntimeContext } from "srvx";
2
- //#region src/context.d.ts
3
- type RhythmRuntime = ServerRuntimeContext;
4
- declare function getRuntime(request: Request): RhythmRuntime | undefined;
5
- type RhythmResponseBody = string | ArrayBuffer | Uint8Array | Blob | FormData | URLSearchParams | ReadableStream<Uint8Array> | null;
6
- declare class RhythmResponse {
7
- status: number;
8
- statusText: string | undefined;
9
- headers: Headers;
10
- body: RhythmResponseBody;
11
- }
12
- interface RhythmHttpContext {
13
- readonly request: Request;
14
- readonly response: RhythmResponse;
15
- json(data: unknown, status?: number): void;
16
- text(body: string, status?: number): void;
17
- html(body: string, status?: number): void;
18
- error(status: number, message?: string): void;
19
- redirect(url: string, status?: number): void;
20
- }
21
- declare function createHttpContext(request: Request): RhythmHttpContext;
22
- declare function toResponse(response: RhythmResponse): Response;
23
- //#endregion
24
- export { createHttpContext as a, RhythmRuntime as i, RhythmResponse as n, getRuntime as o, RhythmResponseBody as r, toResponse as s, RhythmHttpContext as t };