diff --git a/build.config.mjs b/build.config.mjs index 212b780..2b7818a 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/6.bundler.md b/docs/1.guide/6.bundler.md index eea0e4d..0ee4034 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/package.json b/package.json index 3d6f948..c7e635f 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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d6bf0f3..c18ad81 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: diff --git a/src/adapters/bunny.ts b/src/adapters/bunny.ts new file mode 100644 index 0000000..12f6227 --- /dev/null +++ b/src/adapters/bunny.ts @@ -0,0 +1,86 @@ +import { createWaitUntil } from "../_utils.ts"; +import type { 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; + +/** + * 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: (handler: (request: Request) => MaybePromise) => void; + }; +} + +export function serve(options: ServerOptions): Server { + return new BunnyServer(options); +} + +class BunnyServer implements Server { + readonly runtime = "bunny"; + readonly options: Server["options"]; + readonly fetch: (request: Request) => MaybePromise; + private _started = false; + + #wait: ReturnType | undefined; + + constructor(options: ServerOptions) { + this.options = { ...options, middleware: [...(options.middleware || [])] }; + + for (const plugin of options.plugins || []) plugin(this); + errorPlugin(this); + + 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" } }, + ip: { + enumerable: true, + get() { + return request.headers.get("x-real-ip"); + }, + }, + }); + return fetchHandler(request); + }; + + if (!options.manual) { + this.serve(); + } + } + + serve() { + // 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 { + throw new Error("[srvx] Bunny runtime not detected."); + } + } + + ready(): Promise { + return Promise.resolve(this); + } + + async close(): Promise { + await this.#wait?.wait(); + } +} diff --git a/src/types.ts b/src/types.ts index 72af082..454fd54 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. diff --git a/vitest.config.mjs b/vitest.config.mjs index 46dc3c3..bddba2d 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",