From 1aa7b44f1cd2f5dc94d0fc854029a6df6ae6e602 Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Fri, 13 Feb 2026 13:34:23 +0100 Subject: [PATCH 01/25] feat(adapters): new Bunny.net adapter --- build.config.mjs | 13 +++- docs/1.guide/1.index.md | 17 ++--- docs/1.guide/6.bundler.md | 1 + examples/bunny/package.json | 12 ++++ examples/bunny/public/index.html | 31 ++++++++++ examples/bunny/server.ts | 79 ++++++++++++++++++++++++ package.json | 2 + src/adapters/bunny.ts | 103 +++++++++++++++++++++++++++++++ src/types.ts | 10 ++- vitest.config.mjs | 1 + 10 files changed, 257 insertions(+), 12 deletions(-) create mode 100644 examples/bunny/package.json create mode 100644 examples/bunny/public/index.html create mode 100644 examples/bunny/server.ts create mode 100644 src/adapters/bunny.ts diff --git a/build.config.mjs b/build.config.mjs index 212b7803..2b7818ae 100644 --- a/build.config.mjs +++ b/build.config.mjs @@ -14,9 +14,16 @@ export default defineBuildConfig({ "src/log.ts", "src/tracing.ts", "src/loader.ts", - ...["deno", "bun", "node", "cloudflare", "generic", "service-worker", "aws-lambda"].map( - (adapter) => `src/adapters/${adapter}.ts`, - ), + ...[ + "deno", + "bun", + "bunny", + "node", + "cloudflare", + "generic", + "service-worker", + "aws-lambda", + ].map((adapter) => `src/adapters/${adapter}.ts`), ], rolldown: { external: ["bun", "@cloudflare/workers-types", "aws-lambda"], diff --git a/docs/1.guide/1.index.md b/docs/1.guide/1.index.md index d691f4b1..42ec93b6 100644 --- a/docs/1.guide/1.index.md +++ b/docs/1.guide/1.index.md @@ -89,18 +89,19 @@ bun run server.mjs | Example | Source | Try | | ---------------- | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- | -| `aws-lambda` | [examples/aws-lambda](https://github.com/h3js/srvx/tree/main/examples/aws-lambda/) | `npx giget gh:h3js/srvx/examples/aws-lambda srvx-aws-lambda` | -| `elysia` | [examples/elysia](https://github.com/h3js/srvx/tree/main/examples/elysia/) | `npx giget gh:h3js/srvx/examples/elysia srvx-elysia` | -| `express` | [examples/express](https://github.com/h3js/srvx/tree/main/examples/express/) | `npx giget gh:h3js/srvx/examples/express srvx-express` | | `fastify` | [examples/fastify](https://github.com/h3js/srvx/tree/main/examples/fastify/) | `npx giget gh:h3js/srvx/examples/fastify srvx-fastify` | -| `h3` | [examples/h3](https://github.com/h3js/srvx/tree/main/examples/h3/) | `npx giget gh:h3js/srvx/examples/h3 srvx-h3` | | `hello-world` | [examples/hello-world](https://github.com/h3js/srvx/tree/main/examples/hello-world/) | `npx giget gh:h3js/srvx/examples/hello-world srvx-hello-world` | -| `hono` | [examples/hono](https://github.com/h3js/srvx/tree/main/examples/hono/) | `npx giget gh:h3js/srvx/examples/hono srvx-hono` | -| `jsx` | [examples/jsx](https://github.com/h3js/srvx/tree/main/examples/jsx/) | `npx giget gh:h3js/srvx/examples/jsx srvx-jsx` | +| `tracing` | [examples/tracing](https://github.com/h3js/srvx/tree/main/examples/tracing/) | `npx giget gh:h3js/srvx/examples/tracing srvx-tracing` | +| `websocket` | [examples/websocket](https://github.com/h3js/srvx/tree/main/examples/websocket/) | `npx giget gh:h3js/srvx/examples/websocket srvx-websocket` | | `node-handler` | [examples/node-handler](https://github.com/h3js/srvx/tree/main/examples/node-handler/) | `npx giget gh:h3js/srvx/examples/node-handler srvx-node-handler` | +| `elysia` | [examples/elysia](https://github.com/h3js/srvx/tree/main/examples/elysia/) | `npx giget gh:h3js/srvx/examples/elysia srvx-elysia` | +| `h3` | [examples/h3](https://github.com/h3js/srvx/tree/main/examples/h3/) | `npx giget gh:h3js/srvx/examples/h3 srvx-h3` | +| `bunny` | [examples/bunny](https://github.com/h3js/srvx/tree/main/examples/bunny/) | `npx giget gh:h3js/srvx/examples/bunny srvx-bunny` | +| `jsx` | [examples/jsx](https://github.com/h3js/srvx/tree/main/examples/jsx/) | `npx giget gh:h3js/srvx/examples/jsx srvx-jsx` | | `service-worker` | [examples/service-worker](https://github.com/h3js/srvx/tree/main/examples/service-worker/) | `npx giget gh:h3js/srvx/examples/service-worker srvx-service-worker` | +| `hono` | [examples/hono](https://github.com/h3js/srvx/tree/main/examples/hono/) | `npx giget gh:h3js/srvx/examples/hono srvx-hono` | +| `aws-lambda` | [examples/aws-lambda](https://github.com/h3js/srvx/tree/main/examples/aws-lambda/) | `npx giget gh:h3js/srvx/examples/aws-lambda srvx-aws-lambda` | +| `express` | [examples/express](https://github.com/h3js/srvx/tree/main/examples/express/) | `npx giget gh:h3js/srvx/examples/express srvx-express` | | `streaming` | [examples/streaming](https://github.com/h3js/srvx/tree/main/examples/streaming/) | `npx giget gh:h3js/srvx/examples/streaming srvx-streaming` | -| `tracing` | [examples/tracing](https://github.com/h3js/srvx/tree/main/examples/tracing/) | `npx giget gh:h3js/srvx/examples/tracing srvx-tracing` | -| `websocket` | [examples/websocket](https://github.com/h3js/srvx/tree/main/examples/websocket/) | `npx giget gh:h3js/srvx/examples/websocket srvx-websocket` | diff --git a/docs/1.guide/6.bundler.md b/docs/1.guide/6.bundler.md index eea0e4d1..0ee40348 100644 --- a/docs/1.guide/6.bundler.md +++ b/docs/1.guide/6.bundler.md @@ -24,6 +24,7 @@ Instead of depending on ESM conditions, you can explicitly import `srvx` for spe import { serve } from "srvx/node"; import { serve } from "srvx/deno"; import { serve } from "srvx/bun"; +import { serve } from "srvx/bunny"; import { serve } from "srvx/cloudflare"; ``` diff --git a/examples/bunny/package.json b/examples/bunny/package.json new file mode 100644 index 00000000..f7533e9e --- /dev/null +++ b/examples/bunny/package.json @@ -0,0 +1,12 @@ +{ + "name": "srvx-examples-bunny", + "version": "0.0.0", + "type": "module", + "scripts": { + "start": "srvx --prod", + "dev": "srvx" + }, + "devDependencies": { + "srvx": "latest" + } +} diff --git a/examples/bunny/public/index.html b/examples/bunny/public/index.html new file mode 100644 index 00000000..ac820dda --- /dev/null +++ b/examples/bunny/public/index.html @@ -0,0 +1,31 @@ + + + + + + srvx on Bunny Edge + + + +

🐰 srvx on Bunny Edge

+

This is a static file served from the public directory.

+ + diff --git a/examples/bunny/server.ts b/examples/bunny/server.ts new file mode 100644 index 00000000..48295615 --- /dev/null +++ b/examples/bunny/server.ts @@ -0,0 +1,79 @@ +export default { + // https://srvx.h3.dev/guide/options + fetch(req: Request) { + const url = new URL(req.url); + + // Serve static files + if (url.pathname.startsWith("/public/")) { + return new Response(null, { status: 404 }); + } + + // API endpoint example + if (url.pathname === "/api/info") { + return Response.json({ + runtime: "bunny", + message: "Running on Bunny Edge Network!", + ip: req.ip, + headers: Object.fromEntries(req.headers.entries()), + }); + } + + // Default response + return new Response( + ` + + + + srvx on Bunny Edge + + + +

🐰 srvx on Bunny Edge

+

This example demonstrates srvx running on the Bunny Edge Network.

+ +

Try it out:

+ + +

Code Example:

+
export default {
+  fetch(req: Request) {
+    return Response.json({
+      message: "Hello from Bunny Edge!"
+    });
+  },
+};
+ + + `.trim(), + { + headers: { + "content-type": "text/html; charset=utf-8", + }, + }, + ); + }, +}; diff --git a/package.json b/package.json index 3d6f9484..eacf8b71 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "./bun": "./dist/adapters/bun.mjs", "./node": "./dist/adapters/node.mjs", "./cloudflare": "./dist/adapters/cloudflare.mjs", + "./bunny": "./dist/adapters/bunny.mjs", "./generic": "./dist/adapters/generic.mjs", "./service-worker": "./dist/adapters/service-worker.mjs", "./aws-lambda": "./dist/adapters/aws-lambda.mjs", @@ -32,6 +33,7 @@ "types": "./dist/types.d.mts", "deno": "./dist/adapters/deno.mjs", "bun": "./dist/adapters/bun.mjs", + "bunny": "./dist/adapters/bunny.mjs", "workerd": "./dist/adapters/cloudflare.mjs", "browser": "./dist/adapters/service-worker.mjs", "node": "./dist/adapters/node.mjs", diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts new file mode 100644 index 00000000..f47cac3c --- /dev/null +++ b/src/adapters/bunny.ts @@ -0,0 +1,103 @@ +import type { BunnyFetchHandler, Server, ServerOptions } from "../types.ts"; +import { wrapFetch } from "../_middleware.ts"; +import { errorPlugin } from "../_plugins.ts"; + +export const FastURL: typeof globalThis.URL = URL; +export const FastResponse: typeof globalThis.Response = Response; + +/** + * Bunny global namespace types (from @bunny.net/edgescript-sdk) + * @internal + */ +declare namespace Bunny { + type BunnySDKV1 = { + /** + * Serve function for Bunny Edge runtime + */ + serve: (handler: (request: Request) => Response | Promise) => void; + /** + * Serve PullZone function, to leverage middlewares + */ + registerMiddlewares: (middlewares: { + onOriginRequest: Array< + (ctx: { request: Request }) => Promise | Promise | undefined + >; + onOriginResponse: Array< + (ctx: { + request: Request; + response: Response; + }) => Promise | Promise | undefined + >; + }) => void; + }; + + export const v1: BunnySDKV1; +} + +export function serve(options: ServerOptions): Server { + return new BunnyServer(options); +} + +class BunnyServer implements Server { + readonly runtime = "bunny"; + readonly options: Server["options"]; + readonly fetch: BunnyFetchHandler; + + constructor(options: ServerOptions) { + this.options = { ...options, middleware: [...(options.middleware || [])] }; + + for (const plugin of options.plugins || []) plugin(this as unknown as Server); + errorPlugin(this as unknown as Server); + + const fetchHandler = wrapFetch(this as unknown as Server); + + this.fetch = (request: Request) => { + Object.defineProperties(request, { + runtime: { + enumerable: true, + value: { name: "bunny", bunny: {} }, + }, + // IP address from Bunny headers + ip: { + enumerable: true, + get() { + // Bunny uses X-Forwarded-For or similar headers + return ( + request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || + request.headers.get("x-real-ip") || + undefined + ); + }, + }, + }); + return fetchHandler(request as unknown as Request) as Response | Promise; + }; + + if (!options.manual) { + this.serve(); + } + } + + serve() { + // Check if running in Bunny runtime + if (typeof Bunny !== "undefined" && Bunny.v1?.serve) { + Bunny.v1.serve(this.fetch); + } else { + // Not in Bunny runtime - could be in development or testing + // In this case, we can't actually serve, but we won't throw an error + // to allow for testing and development scenarios + if (!this.options.silent) { + console.warn("[srvx] Bunny runtime not detected. The server will not be started."); + } + } + } + + ready(): Promise> { + return Promise.resolve(this); + } + + close() { + // Bunny runtime doesn't support closing the server + return Promise.resolve(); + } +} diff --git a/src/types.ts b/src/types.ts index 72af082c..26db448d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -187,6 +187,7 @@ export interface Server { | "node" | "deno" | "bun" + | "bunny" | "cloudflare" | "service-worker" | "aws-lambda" @@ -252,7 +253,7 @@ export interface Server { // ---------------------------------------------------------------------------- export interface ServerRuntimeContext { - name: "node" | "deno" | "bun" | "cloudflare" | "aws-lambda" | (string & {}); + name: "node" | "deno" | "bun" | "bunny" | "cloudflare" | "aws-lambda" | (string & {}); /** * Underlying Node.js server request info. @@ -276,6 +277,11 @@ export interface ServerRuntimeContext { server: Bun.Server; }; + /** + * Underlying Bunny edge runtime context. + */ + bunny?: {}; + /** * Underlying Cloudflare request context. */ @@ -356,6 +362,8 @@ export type DenoFetchHandler = ( info?: Deno.ServeHandlerInfo, ) => Response | Promise; +export type BunnyFetchHandler = (request: Request) => Response | Promise; + export type NodeServerRequest = NodeHttp.IncomingMessage | NodeHttp2.Http2ServerRequest; export type NodeServerResponse = NodeHttp.ServerResponse | NodeHttp2.Http2ServerResponse; diff --git a/vitest.config.mjs b/vitest.config.mjs index 46dc3c39..bddba2d6 100644 --- a/vitest.config.mjs +++ b/vitest.config.mjs @@ -7,6 +7,7 @@ export default defineConfig({ include: ["src/**/*.ts"], exclude: [ "src/adapters/bun.ts", + "src/adapters/bunny.ts", "src/adapters/cloudflare.ts", "src/adapters/deno.ts", "src/types.ts", From d253e05cfabd95b4b496c7416e8accda37f66a8f Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Fri, 13 Feb 2026 13:37:36 +0100 Subject: [PATCH 02/25] up --- pnpm-lock.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d6bf0f36..c18ad816 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -120,6 +120,12 @@ importers: specifier: link:../.. version: link:../.. + examples/bunny: + devDependencies: + srvx: + specifier: link:../.. + version: link:../.. + examples/elysia: devDependencies: elysia: From 93f6e447cdb029d0214714623045018d6a75a15d Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 13 Feb 2026 12:38:12 +0000 Subject: [PATCH 03/25] chore: apply automated updates --- docs/1.guide/1.index.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/1.guide/1.index.md b/docs/1.guide/1.index.md index 42ec93b6..e8eeccc2 100644 --- a/docs/1.guide/1.index.md +++ b/docs/1.guide/1.index.md @@ -89,19 +89,19 @@ bun run server.mjs | Example | Source | Try | | ---------------- | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- | -| `fastify` | [examples/fastify](https://github.com/h3js/srvx/tree/main/examples/fastify/) | `npx giget gh:h3js/srvx/examples/fastify srvx-fastify` | -| `hello-world` | [examples/hello-world](https://github.com/h3js/srvx/tree/main/examples/hello-world/) | `npx giget gh:h3js/srvx/examples/hello-world srvx-hello-world` | -| `tracing` | [examples/tracing](https://github.com/h3js/srvx/tree/main/examples/tracing/) | `npx giget gh:h3js/srvx/examples/tracing srvx-tracing` | -| `websocket` | [examples/websocket](https://github.com/h3js/srvx/tree/main/examples/websocket/) | `npx giget gh:h3js/srvx/examples/websocket srvx-websocket` | -| `node-handler` | [examples/node-handler](https://github.com/h3js/srvx/tree/main/examples/node-handler/) | `npx giget gh:h3js/srvx/examples/node-handler srvx-node-handler` | +| `aws-lambda` | [examples/aws-lambda](https://github.com/h3js/srvx/tree/main/examples/aws-lambda/) | `npx giget gh:h3js/srvx/examples/aws-lambda srvx-aws-lambda` | +| `bunny` | [examples/bunny](https://github.com/h3js/srvx/tree/main/examples/bunny/) | `npx giget gh:h3js/srvx/examples/bunny srvx-bunny` | | `elysia` | [examples/elysia](https://github.com/h3js/srvx/tree/main/examples/elysia/) | `npx giget gh:h3js/srvx/examples/elysia srvx-elysia` | +| `express` | [examples/express](https://github.com/h3js/srvx/tree/main/examples/express/) | `npx giget gh:h3js/srvx/examples/express srvx-express` | +| `fastify` | [examples/fastify](https://github.com/h3js/srvx/tree/main/examples/fastify/) | `npx giget gh:h3js/srvx/examples/fastify srvx-fastify` | | `h3` | [examples/h3](https://github.com/h3js/srvx/tree/main/examples/h3/) | `npx giget gh:h3js/srvx/examples/h3 srvx-h3` | -| `bunny` | [examples/bunny](https://github.com/h3js/srvx/tree/main/examples/bunny/) | `npx giget gh:h3js/srvx/examples/bunny srvx-bunny` | +| `hello-world` | [examples/hello-world](https://github.com/h3js/srvx/tree/main/examples/hello-world/) | `npx giget gh:h3js/srvx/examples/hello-world srvx-hello-world` | +| `hono` | [examples/hono](https://github.com/h3js/srvx/tree/main/examples/hono/) | `npx giget gh:h3js/srvx/examples/hono srvx-hono` | | `jsx` | [examples/jsx](https://github.com/h3js/srvx/tree/main/examples/jsx/) | `npx giget gh:h3js/srvx/examples/jsx srvx-jsx` | +| `node-handler` | [examples/node-handler](https://github.com/h3js/srvx/tree/main/examples/node-handler/) | `npx giget gh:h3js/srvx/examples/node-handler srvx-node-handler` | | `service-worker` | [examples/service-worker](https://github.com/h3js/srvx/tree/main/examples/service-worker/) | `npx giget gh:h3js/srvx/examples/service-worker srvx-service-worker` | -| `hono` | [examples/hono](https://github.com/h3js/srvx/tree/main/examples/hono/) | `npx giget gh:h3js/srvx/examples/hono srvx-hono` | -| `aws-lambda` | [examples/aws-lambda](https://github.com/h3js/srvx/tree/main/examples/aws-lambda/) | `npx giget gh:h3js/srvx/examples/aws-lambda srvx-aws-lambda` | -| `express` | [examples/express](https://github.com/h3js/srvx/tree/main/examples/express/) | `npx giget gh:h3js/srvx/examples/express srvx-express` | | `streaming` | [examples/streaming](https://github.com/h3js/srvx/tree/main/examples/streaming/) | `npx giget gh:h3js/srvx/examples/streaming srvx-streaming` | +| `tracing` | [examples/tracing](https://github.com/h3js/srvx/tree/main/examples/tracing/) | `npx giget gh:h3js/srvx/examples/tracing srvx-tracing` | +| `websocket` | [examples/websocket](https://github.com/h3js/srvx/tree/main/examples/websocket/) | `npx giget gh:h3js/srvx/examples/websocket srvx-websocket` | From e9bbd24ef0294e133c7ddbad1275477c12a4497f Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Fri, 13 Feb 2026 15:09:55 +0100 Subject: [PATCH 04/25] feat: try to fallback to Deno runtime for local development --- examples/bunny/public/favicon.ico | Bin 0 -> 15406 bytes examples/bunny/public/index.html | 31 ----------------------- examples/bunny/server.ts | 11 ++------ src/adapters/bunny.ts | 40 ++++++++++++++++++++++-------- 4 files changed, 32 insertions(+), 50 deletions(-) create mode 100644 examples/bunny/public/favicon.ico delete mode 100644 examples/bunny/public/index.html diff --git a/examples/bunny/public/favicon.ico b/examples/bunny/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..0fecc452eb67dacfd01b02f9d58165b4e1a6a208 GIT binary patch literal 15406 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 - - - - - srvx on Bunny Edge - - - -

🐰 srvx on Bunny Edge

-

This is a static file served from the public directory.

- - diff --git a/examples/bunny/server.ts b/examples/bunny/server.ts index 48295615..d7725691 100644 --- a/examples/bunny/server.ts +++ b/examples/bunny/server.ts @@ -3,11 +3,6 @@ export default { fetch(req: Request) { const url = new URL(req.url); - // Serve static files - if (url.pathname.startsWith("/public/")) { - return new Response(null, { status: 404 }); - } - // API endpoint example if (url.pathname === "/api/info") { return Response.json({ @@ -19,8 +14,7 @@ export default { } // Default response - return new Response( - ` + return new Response(` @@ -67,8 +61,7 @@ export default { }, }; - - `.trim(), +`.trim(), { headers: { "content-type": "text/html; charset=utf-8", diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index f47cac3c..47221737 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -1,7 +1,11 @@ +import process from "node:process"; + import type { BunnyFetchHandler, Server, ServerOptions } from "../types.ts"; import { wrapFetch } from "../_middleware.ts"; import { errorPlugin } from "../_plugins.ts"; +type MaybePromise = T | Promise; + export const FastURL: typeof globalThis.URL = URL; export const FastResponse: typeof globalThis.Response = Response; @@ -14,7 +18,7 @@ declare namespace Bunny { /** * Serve function for Bunny Edge runtime */ - serve: (handler: (request: Request) => Response | Promise) => void; + serve: (handler: (request: Request) => MaybePromise) => void; /** * Serve PullZone function, to leverage middlewares */ @@ -46,10 +50,10 @@ class BunnyServer implements Server { constructor(options: ServerOptions) { this.options = { ...options, middleware: [...(options.middleware || [])] }; - for (const plugin of options.plugins || []) plugin(this as unknown as Server); - errorPlugin(this as unknown as Server); + for (const plugin of options.plugins || []) plugin(this); + errorPlugin(this); - const fetchHandler = wrapFetch(this as unknown as Server); + const fetchHandler = wrapFetch(this); this.fetch = (request: Request) => { Object.defineProperties(request, { @@ -70,7 +74,7 @@ class BunnyServer implements Server { }, }, }); - return fetchHandler(request as unknown as Request) as Response | Promise; + return fetchHandler(request); }; if (!options.manual) { @@ -82,13 +86,29 @@ class BunnyServer implements Server { // Check if running in Bunny runtime if (typeof Bunny !== "undefined" && Bunny.v1?.serve) { Bunny.v1.serve(this.fetch); - } else { - // Not in Bunny runtime - could be in development or testing - // In this case, we can't actually serve, but we won't throw an error - // to allow for testing and development scenarios + } else if (typeof Deno !== "undefined") { + // Try to fallback to Deno's serve for local use if (!this.options.silent) { - console.warn("[srvx] Bunny runtime not detected. The server will not be started."); + console.warn("[srvx] Bunny runtime not detected. Falling back to Deno for local use."); } + const _parsedPort = + typeof this.options.port === "number" + ? this.options.port + : Number.parseInt(this.options.port ?? process.env.NITRO_PORT ?? process.env.PORT ?? ""); + const port = !Number.isNaN(_parsedPort) ? _parsedPort : 3000; + const hostname = this.options.hostname || process.env.NITRO_HOST || process.env.HOST; + + Deno.serve( + { + port, + hostname, + }, + this.fetch, + ); + } else { + throw new Error( + "[srvx] Bunny runtime not detected and Deno is not available. Unable to start server.", + ); } } From 8b0b09230f280b763d1be24fb8133c071794bd70 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 13 Feb 2026 14:10:32 +0000 Subject: [PATCH 05/25] chore: apply automated updates --- examples/bunny/server.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/bunny/server.ts b/examples/bunny/server.ts index d7725691..3a7bd57b 100644 --- a/examples/bunny/server.ts +++ b/examples/bunny/server.ts @@ -14,7 +14,8 @@ export default { } // Default response - return new Response(` + return new Response( + ` From 02440a8cde035c2c1932b4469ae366fc95f85598 Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Fri, 13 Feb 2026 15:27:17 +0100 Subject: [PATCH 06/25] up --- src/adapters/bunny.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index 47221737..551df338 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -94,9 +94,9 @@ class BunnyServer implements Server { const _parsedPort = typeof this.options.port === "number" ? this.options.port - : Number.parseInt(this.options.port ?? process.env.NITRO_PORT ?? process.env.PORT ?? ""); + : Number.parseInt(this.options.port ?? process.env.PORT ?? ""); const port = !Number.isNaN(_parsedPort) ? _parsedPort : 3000; - const hostname = this.options.hostname || process.env.NITRO_HOST || process.env.HOST; + const hostname = this.options.hostname || process.env.HOST; Deno.serve( { From e4a2efb757b5dbc15bcea14560ee2c4b187ff9fc Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Fri, 13 Feb 2026 15:32:12 +0100 Subject: [PATCH 07/25] fix(bunny): properly store and close local Deno.serve in dev/preview --- src/adapters/bunny.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index 551df338..e7a9c4b0 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -46,6 +46,7 @@ class BunnyServer implements Server { readonly runtime = "bunny"; readonly options: Server["options"]; readonly fetch: BunnyFetchHandler; + private _denoServer?: Deno.HttpServer = undefined; constructor(options: ServerOptions) { this.options = { ...options, middleware: [...(options.middleware || [])] }; @@ -98,7 +99,7 @@ class BunnyServer implements Server { const port = !Number.isNaN(_parsedPort) ? _parsedPort : 3000; const hostname = this.options.hostname || process.env.HOST; - Deno.serve( + this._denoServer = Deno.serve( { port, hostname, @@ -117,6 +118,9 @@ class BunnyServer implements Server { } close() { + if (this._denoServer) { + this._denoServer.shutdown(); + } // Bunny runtime doesn't support closing the server return Promise.resolve(); } From c86dc6f394187cef6620af4f890c54f712bfbba1 Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Fri, 13 Feb 2026 15:32:51 +0100 Subject: [PATCH 08/25] up --- src/adapters/bunny.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index e7a9c4b0..27729e4b 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -119,7 +119,7 @@ class BunnyServer implements Server { close() { if (this._denoServer) { - this._denoServer.shutdown(); + return this._denoServer.shutdown(); } // Bunny runtime doesn't support closing the server return Promise.resolve(); From ff5289ae81a64a79d072c67142713808395f9586 Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Fri, 13 Feb 2026 15:38:25 +0100 Subject: [PATCH 09/25] cleanup --- package.json | 1 - src/adapters/bunny.ts | 10 +++++----- src/types.ts | 7 ------- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index eacf8b71..c7e635f9 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,6 @@ "types": "./dist/types.d.mts", "deno": "./dist/adapters/deno.mjs", "bun": "./dist/adapters/bun.mjs", - "bunny": "./dist/adapters/bunny.mjs", "workerd": "./dist/adapters/cloudflare.mjs", "browser": "./dist/adapters/service-worker.mjs", "node": "./dist/adapters/node.mjs", diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index 27729e4b..956fb77f 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -1,6 +1,6 @@ import process from "node:process"; -import type { BunnyFetchHandler, Server, ServerOptions } from "../types.ts"; +import type { Server, ServerOptions } from "../types.ts"; import { wrapFetch } from "../_middleware.ts"; import { errorPlugin } from "../_plugins.ts"; @@ -38,14 +38,14 @@ declare namespace Bunny { export const v1: BunnySDKV1; } -export function serve(options: ServerOptions): Server { +export function serve(options: ServerOptions): Server<(request: Request) => MaybePromise> { return new BunnyServer(options); } -class BunnyServer implements Server { +class BunnyServer implements Server<(request: Request) => MaybePromise> { readonly runtime = "bunny"; readonly options: Server["options"]; - readonly fetch: BunnyFetchHandler; + readonly fetch: (request: Request) => MaybePromise; private _denoServer?: Deno.HttpServer = undefined; constructor(options: ServerOptions) { @@ -113,7 +113,7 @@ class BunnyServer implements Server { } } - ready(): Promise> { + ready(): Promise MaybePromise>> { return Promise.resolve(this); } diff --git a/src/types.ts b/src/types.ts index 26db448d..454fd544 100644 --- a/src/types.ts +++ b/src/types.ts @@ -277,11 +277,6 @@ export interface ServerRuntimeContext { server: Bun.Server; }; - /** - * Underlying Bunny edge runtime context. - */ - bunny?: {}; - /** * Underlying Cloudflare request context. */ @@ -362,8 +357,6 @@ export type DenoFetchHandler = ( info?: Deno.ServeHandlerInfo, ) => Response | Promise; -export type BunnyFetchHandler = (request: Request) => Response | Promise; - export type NodeServerRequest = NodeHttp.IncomingMessage | NodeHttp2.Http2ServerRequest; export type NodeServerResponse = NodeHttp.ServerResponse | NodeHttp2.Http2ServerResponse; From 4535696ab9f7255cf6a36ed658573af8fffea78f Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Fri, 13 Feb 2026 15:39:22 +0100 Subject: [PATCH 10/25] fix(bunny): wait for ready when using Deno fallback --- src/adapters/bunny.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index 956fb77f..1460df2e 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -114,6 +114,9 @@ class BunnyServer implements Server<(request: Request) => MaybePromise } ready(): Promise MaybePromise>> { + if (this._denoServer) { + return this._denoServer.finished.then(() => this); + } return Promise.resolve(this); } From 7ed42a22a175423887472d5a86d601fa1ea5455b Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 13 Feb 2026 14:39:56 +0000 Subject: [PATCH 11/25] chore: apply automated updates --- src/adapters/bunny.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index 1460df2e..60ea56a9 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -38,7 +38,9 @@ declare namespace Bunny { export const v1: BunnySDKV1; } -export function serve(options: ServerOptions): Server<(request: Request) => MaybePromise> { +export function serve( + options: ServerOptions, +): Server<(request: Request) => MaybePromise> { return new BunnyServer(options); } From f62fe89cd4f76b3051b9560978c63c826095833a Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Fri, 13 Feb 2026 15:47:06 +0100 Subject: [PATCH 12/25] Revert "fix(bunny): wait for ready when using Deno fallback" This reverts commit 4535696ab9f7255cf6a36ed658573af8fffea78f. --- src/adapters/bunny.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index 60ea56a9..13eb904d 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -116,9 +116,6 @@ class BunnyServer implements Server<(request: Request) => MaybePromise } ready(): Promise MaybePromise>> { - if (this._denoServer) { - return this._denoServer.finished.then(() => this); - } return Promise.resolve(this); } From 3b845100cff9587944ac8009154e71e15b4228dd Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Fri, 13 Feb 2026 15:55:34 +0100 Subject: [PATCH 13/25] feat(bunny): add waitUntil --- src/adapters/bunny.ts | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index 13eb904d..7aced018 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -1,5 +1,5 @@ import process from "node:process"; - +import { createWaitUntil } from "../_utils.ts"; import type { Server, ServerOptions } from "../types.ts"; import { wrapFetch } from "../_middleware.ts"; import { errorPlugin } from "../_plugins.ts"; @@ -40,16 +40,18 @@ declare namespace Bunny { export function serve( options: ServerOptions, -): Server<(request: Request) => MaybePromise> { +): Server { return new BunnyServer(options); } -class BunnyServer implements Server<(request: Request) => MaybePromise> { +class BunnyServer implements Server { readonly runtime = "bunny"; readonly options: Server["options"]; readonly fetch: (request: Request) => MaybePromise; private _denoServer?: Deno.HttpServer = undefined; + #wait: ReturnType | undefined; + constructor(options: ServerOptions) { this.options = { ...options, middleware: [...(options.middleware || [])] }; @@ -58,11 +60,14 @@ class BunnyServer implements Server<(request: Request) => MaybePromise const fetchHandler = wrapFetch(this); + this.#wait = createWaitUntil(); + this.fetch = (request: Request) => { Object.defineProperties(request, { + waitUntil: { value: this.#wait?.waitUntil }, runtime: { enumerable: true, - value: { name: "bunny", bunny: {} }, + value: { name: "bunny" }, }, // IP address from Bunny headers ip: { @@ -115,15 +120,16 @@ class BunnyServer implements Server<(request: Request) => MaybePromise } } - ready(): Promise MaybePromise>> { + ready(): Promise { return Promise.resolve(this); } - close() { + async close(): Promise { + // Bunny runtime doesn't support closing the server + const promises = [this.#wait?.wait()]; if (this._denoServer) { - return this._denoServer.shutdown(); + promises.push(this._denoServer.shutdown()); } - // Bunny runtime doesn't support closing the server - return Promise.resolve(); + await Promise.all(promises); } } From e466fccfbf0c73f0174abf96b198be82227fd21c Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Fri, 13 Feb 2026 16:06:33 +0100 Subject: [PATCH 14/25] feat(bunny): prevent multiple calls to serve in Edge Scripting --- src/adapters/bunny.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index 7aced018..544a6465 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -1,5 +1,11 @@ import process from "node:process"; -import { createWaitUntil } from "../_utils.ts"; +import { + fmtURL, + printListening, + resolvePortAndHost, + resolveTLSOptions, + createWaitUntil, +} from "../_utils.ts"; import type { Server, ServerOptions } from "../types.ts"; import { wrapFetch } from "../_middleware.ts"; import { errorPlugin } from "../_plugins.ts"; @@ -49,6 +55,7 @@ class BunnyServer implements Server { readonly options: Server["options"]; readonly fetch: (request: Request) => MaybePromise; private _denoServer?: Deno.HttpServer = undefined; + private _started = false; #wait: ReturnType | undefined; @@ -91,6 +98,10 @@ class BunnyServer implements Server { } serve() { + // Prevent multiple calls to serve, mostly for Bunny + if (this._started) return; + this._started = true; + // Check if running in Bunny runtime if (typeof Bunny !== "undefined" && Bunny.v1?.serve) { Bunny.v1.serve(this.fetch); From 05e845feab0caad5e883298c33fbecbbaf69e934 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 13 Feb 2026 15:07:18 +0000 Subject: [PATCH 15/25] chore: apply automated updates --- src/adapters/bunny.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index 544a6465..757576a7 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -44,9 +44,7 @@ declare namespace Bunny { export const v1: BunnySDKV1; } -export function serve( - options: ServerOptions, -): Server { +export function serve(options: ServerOptions): Server { return new BunnyServer(options); } From a348e2c12682d41e3afb28c6eee60ef8df92a708 Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Fri, 13 Feb 2026 16:33:22 +0100 Subject: [PATCH 16/25] fix(bunny): prefer resolvePortAndHost during dev --- src/adapters/bunny.ts | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index 757576a7..5ecb5f95 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -1,11 +1,4 @@ -import process from "node:process"; -import { - fmtURL, - printListening, - resolvePortAndHost, - resolveTLSOptions, - createWaitUntil, -} from "../_utils.ts"; +import { resolvePortAndHost, createWaitUntil } from "../_utils.ts"; import type { Server, ServerOptions } from "../types.ts"; import { wrapFetch } from "../_middleware.ts"; import { errorPlugin } from "../_plugins.ts"; @@ -108,12 +101,7 @@ class BunnyServer implements Server { if (!this.options.silent) { console.warn("[srvx] Bunny runtime not detected. Falling back to Deno for local use."); } - const _parsedPort = - typeof this.options.port === "number" - ? this.options.port - : Number.parseInt(this.options.port ?? process.env.PORT ?? ""); - const port = !Number.isNaN(_parsedPort) ? _parsedPort : 3000; - const hostname = this.options.hostname || process.env.HOST; + const { port, hostname } = resolvePortAndHost(this.options); this._denoServer = Deno.serve( { From 03b4e51eac8710d476218a8f3412dce9f71c0cc8 Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Sat, 14 Feb 2026 16:24:58 +0100 Subject: [PATCH 17/25] fix(bunny): properly fallback to deno adapter --- src/adapters/bunny.ts | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index 5ecb5f95..a05bde3d 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -1,7 +1,8 @@ -import { resolvePortAndHost, createWaitUntil } from "../_utils.ts"; +import { createWaitUntil } from "../_utils.ts"; import type { Server, ServerOptions } from "../types.ts"; import { wrapFetch } from "../_middleware.ts"; import { errorPlugin } from "../_plugins.ts"; +import { serve as serveDeno } from "./deno.ts"; type MaybePromise = T | Promise; @@ -38,7 +39,7 @@ declare namespace Bunny { } export function serve(options: ServerOptions): Server { - return new BunnyServer(options); + return typeof Bunny !== "undefined" ? new BunnyServer(options) : serveDeno(options); } class BunnyServer implements Server { @@ -93,28 +94,7 @@ class BunnyServer implements Server { if (this._started) return; this._started = true; - // Check if running in Bunny runtime - if (typeof Bunny !== "undefined" && Bunny.v1?.serve) { - Bunny.v1.serve(this.fetch); - } else if (typeof Deno !== "undefined") { - // Try to fallback to Deno's serve for local use - if (!this.options.silent) { - console.warn("[srvx] Bunny runtime not detected. Falling back to Deno for local use."); - } - const { port, hostname } = resolvePortAndHost(this.options); - - this._denoServer = Deno.serve( - { - port, - hostname, - }, - this.fetch, - ); - } else { - throw new Error( - "[srvx] Bunny runtime not detected and Deno is not available. Unable to start server.", - ); - } + Bunny.v1.serve(this.fetch); } ready(): Promise { From 51ba93491e52cad4031f199557d15775e0fbba90 Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Sat, 14 Feb 2026 16:42:29 +0100 Subject: [PATCH 18/25] up --- src/adapters/bunny.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index a05bde3d..409b2776 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -39,7 +39,10 @@ declare namespace Bunny { } export function serve(options: ServerOptions): Server { - return typeof Bunny !== "undefined" ? new BunnyServer(options) : serveDeno(options); + if (typeof Bunny !== "undefined" && Bunny.v1 && typeof Bunny.v1.serve === "function") { + return new BunnyServer(options); + } + return serveDeno(options); } class BunnyServer implements Server { From 5605c108507ce3ac603ebb80582af86c15f50ad8 Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Sat, 14 Feb 2026 17:13:29 +0100 Subject: [PATCH 19/25] revert(bunny): fallback to deno adapter --- src/adapters/bunny.ts | 59 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index 409b2776..bb0859d5 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -1,8 +1,13 @@ -import { createWaitUntil } from "../_utils.ts"; +import { + resolvePortAndHost, + resolveTLSOptions, + printListening, + fmtURL, + createWaitUntil, +} from "../_utils.ts"; import type { Server, ServerOptions } from "../types.ts"; import { wrapFetch } from "../_middleware.ts"; import { errorPlugin } from "../_plugins.ts"; -import { serve as serveDeno } from "./deno.ts"; type MaybePromise = T | Promise; @@ -39,10 +44,7 @@ declare namespace Bunny { } export function serve(options: ServerOptions): Server { - if (typeof Bunny !== "undefined" && Bunny.v1 && typeof Bunny.v1.serve === "function") { - return new BunnyServer(options); - } - return serveDeno(options); + return new BunnyServer(options); } class BunnyServer implements Server { @@ -52,6 +54,9 @@ class BunnyServer implements Server { private _denoServer?: Deno.HttpServer = undefined; private _started = false; + // Deno only properties for local use, not available in Bunny runtime + #listeningPromise?: Promise; + #wait: ReturnType | undefined; constructor(options: ServerOptions) { @@ -97,7 +102,47 @@ class BunnyServer implements Server { if (this._started) return; this._started = true; - Bunny.v1.serve(this.fetch); + // Check if running in Bunny runtime + if (typeof Bunny !== "undefined" && Bunny.v1?.serve) { + Bunny.v1.serve(this.fetch); + } else if (typeof Deno !== "undefined") { + // Try to fallback to Deno's serve for local use + if (!this.options.silent) { + console.warn("[srvx] Bunny runtime not detected. Falling back to Deno for local use."); + } + + if (this._denoServer) { + return Promise.resolve(this.#listeningPromise).then(() => this); + } + const onListenPromise = Promise.withResolvers(); + this.#listeningPromise = onListenPromise.promise; + const tls = resolveTLSOptions(this.options); + + const denoServeOptions = { + ...resolvePortAndHost(this.options), + reusePort: this.options.reusePort, + onError: this.options.error, + ...(tls ? { key: tls.key, cert: tls.cert, passphrase: tls.passphrase } : {}), + ...this.options.deno, + }; + this._denoServer = Deno.serve( + { + ...denoServeOptions, + onListen: (info) => { + if (this.options.deno?.onListen) { + this.options.deno.onListen(info); + } + printListening(this.options, fmtURL(info.hostname, info.port, !!denoServeOptions.cert)); + onListenPromise.resolve(); + }, + }, + this.fetch, + ); + } else { + throw new Error( + "[srvx] Bunny runtime not detected and Deno is not available. Unable to start server.", + ); + } } ready(): Promise { From 7ebe54de5ef04127291092bbda69d8ae401a5be4 Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Sat, 14 Feb 2026 17:23:27 +0100 Subject: [PATCH 20/25] fix(bunny): improve check on already started server and use remoteAddr in local dev --- src/adapters/bunny.ts | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index bb0859d5..6507f900 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -1,8 +1,6 @@ import { resolvePortAndHost, resolveTLSOptions, - printListening, - fmtURL, createWaitUntil, } from "../_utils.ts"; import type { Server, ServerOptions } from "../types.ts"; @@ -69,7 +67,7 @@ class BunnyServer implements Server { this.#wait = createWaitUntil(); - this.fetch = (request: Request) => { + this.fetch = (request: Request, denoInfo: { remoteAddr?: Deno.Addr } = {}) => { Object.defineProperties(request, { waitUntil: { value: this.#wait?.waitUntil }, runtime: { @@ -84,6 +82,7 @@ class BunnyServer implements Server { return ( request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || request.headers.get("x-real-ip") || + (denoInfo?.remoteAddr as Deno.NetAddr)?.hostname || undefined ); }, @@ -98,12 +97,12 @@ class BunnyServer implements Server { } serve() { - // Prevent multiple calls to serve, mostly for Bunny - if (this._started) return; - this._started = true; - // Check if running in Bunny runtime if (typeof Bunny !== "undefined" && Bunny.v1?.serve) { + // Prevent multiple calls to serve, mostly for Bunny + if (this._started) return; + this._started = true; + Bunny.v1.serve(this.fetch); } else if (typeof Deno !== "undefined") { // Try to fallback to Deno's serve for local use @@ -118,23 +117,14 @@ class BunnyServer implements Server { this.#listeningPromise = onListenPromise.promise; const tls = resolveTLSOptions(this.options); - const denoServeOptions = { - ...resolvePortAndHost(this.options), - reusePort: this.options.reusePort, - onError: this.options.error, - ...(tls ? { key: tls.key, cert: tls.cert, passphrase: tls.passphrase } : {}), - ...this.options.deno, - }; this._denoServer = Deno.serve( { - ...denoServeOptions, - onListen: (info) => { - if (this.options.deno?.onListen) { - this.options.deno.onListen(info); - } - printListening(this.options, fmtURL(info.hostname, info.port, !!denoServeOptions.cert)); - onListenPromise.resolve(); - }, + ...this.options.deno, + ...resolvePortAndHost(this.options), + reusePort: this.options.reusePort, + onError: this.options.error, + ...(tls ? { key: tls.key, cert: tls.cert, passphrase: tls.passphrase } : {}), + ...this.options.deno, }, this.fetch, ); From 17b903006d84321f0480197ea8cfc2483f2ad7e2 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 14 Feb 2026 16:23:56 +0000 Subject: [PATCH 21/25] chore: apply automated updates --- src/adapters/bunny.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index 6507f900..ce16ece3 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -1,8 +1,4 @@ -import { - resolvePortAndHost, - resolveTLSOptions, - createWaitUntil, -} from "../_utils.ts"; +import { resolvePortAndHost, resolveTLSOptions, createWaitUntil } from "../_utils.ts"; import type { Server, ServerOptions } from "../types.ts"; import { wrapFetch } from "../_middleware.ts"; import { errorPlugin } from "../_plugins.ts"; From 1c4195f14e015d93f3b081ec21de5f57f14963f7 Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Sat, 14 Feb 2026 17:24:43 +0100 Subject: [PATCH 22/25] up --- src/adapters/bunny.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index ce16ece3..ab965d70 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -132,7 +132,7 @@ class BunnyServer implements Server { } ready(): Promise { - return Promise.resolve(this); + return Promise.resolve(this.#listeningPromise).then(() => this); } async close(): Promise { From c1864c7eb3264d8029c1ebcb0608b20ef7ced65a Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Tue, 17 Feb 2026 09:46:48 +0100 Subject: [PATCH 23/25] update --- examples/bunny/package.json | 12 ----- examples/bunny/public/favicon.ico | Bin 15406 -> 0 bytes examples/bunny/server.ts | 73 ------------------------------ src/adapters/bunny.ts | 66 +++++---------------------- 4 files changed, 12 insertions(+), 139 deletions(-) delete mode 100644 examples/bunny/package.json delete mode 100644 examples/bunny/public/favicon.ico delete mode 100644 examples/bunny/server.ts diff --git a/examples/bunny/package.json b/examples/bunny/package.json deleted file mode 100644 index f7533e9e..00000000 --- a/examples/bunny/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "srvx-examples-bunny", - "version": "0.0.0", - "type": "module", - "scripts": { - "start": "srvx --prod", - "dev": "srvx" - }, - "devDependencies": { - "srvx": "latest" - } -} diff --git a/examples/bunny/public/favicon.ico b/examples/bunny/public/favicon.ico deleted file mode 100644 index 0fecc452eb67dacfd01b02f9d58165b4e1a6a208..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15406 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 - - - srvx on Bunny Edge - - - -

🐰 srvx on Bunny Edge

-

This example demonstrates srvx running on the Bunny Edge Network.

- -

Try it out:

-
    -
  • GET /api/info - View request information
  • -
  • Your IP: ${req.ip || "unknown"}
  • -
- -

Code Example:

-
export default {
-  fetch(req: Request) {
-    return Response.json({
-      message: "Hello from Bunny Edge!"
-    });
-  },
-};
- -`.trim(), - { - headers: { - "content-type": "text/html; charset=utf-8", - }, - }, - ); - }, -}; diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index ab965d70..076adaae 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -9,10 +9,15 @@ export const FastURL: typeof globalThis.URL = URL; export const FastResponse: typeof globalThis.Response = Response; /** - * Bunny global namespace types (from @bunny.net/edgescript-sdk) + * Bunny global namespace types + * + * Source: https://github.com/BunnyWay/edge-script-sdk/blob/main/libs/bunny-sdk/types/bunny.d.ts + * * @internal */ declare namespace Bunny { + export const v1: BunnySDKV1; + type BunnySDKV1 = { /** * Serve function for Bunny Edge runtime @@ -33,8 +38,6 @@ declare namespace Bunny { >; }) => void; }; - - export const v1: BunnySDKV1; } export function serve(options: ServerOptions): Server { @@ -45,12 +48,8 @@ class BunnyServer implements Server { readonly runtime = "bunny"; readonly options: Server["options"]; readonly fetch: (request: Request) => MaybePromise; - private _denoServer?: Deno.HttpServer = undefined; private _started = false; - // Deno only properties for local use, not available in Bunny runtime - #listeningPromise?: Promise; - #wait: ReturnType | undefined; constructor(options: ServerOptions) { @@ -63,24 +62,14 @@ class BunnyServer implements Server { this.#wait = createWaitUntil(); - this.fetch = (request: Request, denoInfo: { remoteAddr?: Deno.Addr } = {}) => { + this.fetch = (request: Request) => { Object.defineProperties(request, { waitUntil: { value: this.#wait?.waitUntil }, - runtime: { - enumerable: true, - value: { name: "bunny" }, - }, - // IP address from Bunny headers + runtime: { enumerable: true, value: { name: "bunny" } }, ip: { enumerable: true, get() { - // Bunny uses X-Forwarded-For or similar headers - return ( - request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || - request.headers.get("x-real-ip") || - (denoInfo?.remoteAddr as Deno.NetAddr)?.hostname || - undefined - ); + return request.headers.get("x-real-ip"); }, }, }); @@ -100,47 +89,16 @@ class BunnyServer implements Server { this._started = true; Bunny.v1.serve(this.fetch); - } else if (typeof Deno !== "undefined") { - // Try to fallback to Deno's serve for local use - if (!this.options.silent) { - console.warn("[srvx] Bunny runtime not detected. Falling back to Deno for local use."); - } - - if (this._denoServer) { - return Promise.resolve(this.#listeningPromise).then(() => this); - } - const onListenPromise = Promise.withResolvers(); - this.#listeningPromise = onListenPromise.promise; - const tls = resolveTLSOptions(this.options); - - this._denoServer = Deno.serve( - { - ...this.options.deno, - ...resolvePortAndHost(this.options), - reusePort: this.options.reusePort, - onError: this.options.error, - ...(tls ? { key: tls.key, cert: tls.cert, passphrase: tls.passphrase } : {}), - ...this.options.deno, - }, - this.fetch, - ); } else { - throw new Error( - "[srvx] Bunny runtime not detected and Deno is not available. Unable to start server.", - ); + throw new Error("[srvx] Bunny runtime not detected."); } } ready(): Promise { - return Promise.resolve(this.#listeningPromise).then(() => this); + return Promise.resolve(this); } async close(): Promise { - // Bunny runtime doesn't support closing the server - const promises = [this.#wait?.wait()]; - if (this._denoServer) { - promises.push(this._denoServer.shutdown()); - } - await Promise.all(promises); + await this.#wait?.wait(); } } From 99cd410530c2a5f8590d0bfb86e4c019d77f681b Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Tue, 17 Feb 2026 09:47:16 +0100 Subject: [PATCH 24/25] fmt --- docs/1.guide/1.index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/1.guide/1.index.md b/docs/1.guide/1.index.md index e8eeccc2..d691f4b1 100644 --- a/docs/1.guide/1.index.md +++ b/docs/1.guide/1.index.md @@ -90,7 +90,6 @@ bun run server.mjs | Example | Source | Try | | ---------------- | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- | | `aws-lambda` | [examples/aws-lambda](https://github.com/h3js/srvx/tree/main/examples/aws-lambda/) | `npx giget gh:h3js/srvx/examples/aws-lambda srvx-aws-lambda` | -| `bunny` | [examples/bunny](https://github.com/h3js/srvx/tree/main/examples/bunny/) | `npx giget gh:h3js/srvx/examples/bunny srvx-bunny` | | `elysia` | [examples/elysia](https://github.com/h3js/srvx/tree/main/examples/elysia/) | `npx giget gh:h3js/srvx/examples/elysia srvx-elysia` | | `express` | [examples/express](https://github.com/h3js/srvx/tree/main/examples/express/) | `npx giget gh:h3js/srvx/examples/express srvx-express` | | `fastify` | [examples/fastify](https://github.com/h3js/srvx/tree/main/examples/fastify/) | `npx giget gh:h3js/srvx/examples/fastify srvx-fastify` | From 0bfc775b4708b5e92acc001db181a61ce701d5eb Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Tue, 17 Feb 2026 09:49:30 +0100 Subject: [PATCH 25/25] up --- src/adapters/bunny.ts | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts index 076adaae..12f6227f 100644 --- a/src/adapters/bunny.ts +++ b/src/adapters/bunny.ts @@ -1,4 +1,4 @@ -import { resolvePortAndHost, resolveTLSOptions, createWaitUntil } from "../_utils.ts"; +import { createWaitUntil } from "../_utils.ts"; import type { Server, ServerOptions } from "../types.ts"; import { wrapFetch } from "../_middleware.ts"; import { errorPlugin } from "../_plugins.ts"; @@ -17,26 +17,8 @@ export const FastResponse: typeof globalThis.Response = Response; */ declare namespace Bunny { export const v1: BunnySDKV1; - type BunnySDKV1 = { - /** - * Serve function for Bunny Edge runtime - */ serve: (handler: (request: Request) => MaybePromise) => void; - /** - * Serve PullZone function, to leverage middlewares - */ - registerMiddlewares: (middlewares: { - onOriginRequest: Array< - (ctx: { request: Request }) => Promise | Promise | undefined - >; - onOriginResponse: Array< - (ctx: { - request: Request; - response: Response; - }) => Promise | Promise | undefined - >; - }) => void; }; }