Skip to content

Commit 52c01ec

Browse files
committed
feat: Abstract transport layer and allow passing custom options
1 parent 9119952 commit 52c01ec

5 files changed

Lines changed: 86 additions & 12 deletions

File tree

src/app.ts

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,14 @@ import type {
1313
DefaultAppData,
1414
BasePrefix,
1515
SchemaAdapter,
16+
Transport,
1617
} from "./types";
1718
import { addRoute, createRouter, findRoute } from "rou3";
18-
import { callCtxModifierHooks, serializeErrorResponse } from "./internal/utils";
19+
import {
20+
callCtxModifierHooks,
21+
detectTransport,
22+
serializeErrorResponse,
23+
} from "./internal/utils";
1924
import type { OpenAPIV3_1 } from "openapi-types";
2025
import { buildOpenApiDocs, buildScalarHtml } from "./open-api";
2126

@@ -227,17 +232,8 @@ export function createApp<TPrefix extends BasePrefix = "">(
227232
},
228233

229234
listen: (port, cb) => {
230-
if (typeof Bun !== "undefined") {
231-
Bun.serve({ port, fetch: app.build() });
232-
if (cb) setTimeout(cb, 0);
233-
} else if (
234-
// @ts-expect-error: Deno types not installed.
235-
typeof Deno !== "undefined"
236-
) {
237-
// @ts-expect-error: Deno types not installed.
238-
Deno.serve({ port, fetch: app.build() });
239-
if (cb) setTimeout(cb, 0);
240-
}
235+
const transport = options?.transport ?? detectTransport();
236+
transport.listen(port, app.build(), cb);
241237
return app;
242238
},
243239

@@ -428,6 +424,24 @@ export type CreateAppOptions<TPrefix extends BasePrefix = ""> = {
428424
*/
429425
schemaAdapter?: SchemaAdapter;
430426

427+
/**
428+
* Tell Zeta how to serve your app over a port. By default, Zeta will detect
429+
* if you're runtime is Bun or Deno, and use the appropriate transport.
430+
*
431+
* If you need to customize the transport, like adding an `idleTimeout` to
432+
* bun, you can do so by passing options into the transport's factory function.
433+
*
434+
* @example
435+
* ```ts
436+
* import { createBunTransport } from "@aklinker1/zeta/transports/bun-transport"
437+
*
438+
* const app = createApp({
439+
* transport: createBunTransport(),
440+
* });
441+
* ```
442+
*/
443+
transport?: Transport;
444+
431445
/**
432446
* Where the OpenAPI JSON docs is hosted.
433447
* @default "/openapi.json"

src/internal/utils.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ import type {
77
MaybePromise,
88
RouterData,
99
StatusResult,
10+
Transport,
1011
} from "../types";
1112
import type { MatchedRoute } from "rou3";
1213
import type { ErrorResponse } from "../custom-responses";
14+
import { createBunTransport } from "../transports/bun-transport";
15+
import { createDenoTransport } from "../transports/deno-transport";
1316

1417
export function validateSchema<T>(
1518
schema: StandardSchemaV1<T, T>,
@@ -136,3 +139,22 @@ export const IsStatusResult = Symbol("IsStatusResult");
136139
export function isStatusResult(result: any): result is StatusResult {
137140
return IsStatusResult in result;
138141
}
142+
143+
export function detectTransport(): Transport {
144+
// @ts-ignore: Bun types may not be available
145+
if (typeof Bun !== "undefined") return createBunTransport();
146+
// @ts-ignore: Deno types may not be available
147+
if (typeof Deno !== "undefined") return createDenoTransport();
148+
149+
throw Error(`Cannot automatically detect which transport to use. You must specify a transport in your top-level app:
150+
151+
---
152+
import { createBunTransport } from '@aklinker1/zeta/transports/bun-transport';
153+
154+
const app = createApp({
155+
transport: createBunTransport(),
156+
})
157+
158+
app.listen();
159+
---`);
160+
}

src/transports/bun-transport.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type { Transport } from "../types";
2+
// @ts-ignore: Bun types may not be available
3+
import type { ServeFunctionOptions } from "bun";
4+
5+
export function createBunTransport(
6+
options: Omit<ServeFunctionOptions<any, any>, "fetch" | "port">,
7+
): Transport {
8+
const listen: Transport["listen"] = (port, fetch, cb) => {
9+
// @ts-ignore: Bun types may not be available
10+
Bun.serve({ ...options, port, fetch });
11+
if (cb) setTimeout(cb, 0);
12+
};
13+
14+
return {
15+
listen,
16+
};
17+
}

src/transports/deno-transport.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { Transport } from "../types";
2+
3+
export function createDenoTransport(): Transport {
4+
const listen: Transport["listen"] = (port, fetch, cb) => {
5+
// @ts-ignore: Deno types may not be available
6+
Deno.serve({ port, fetch });
7+
if (cb) setTimeout(cb, 0);
8+
};
9+
10+
return {
11+
listen,
12+
};
13+
}

src/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,14 @@ export interface SchemaAdapter {
10421042
getMeta: (schema: StandardSchemaV1) => Record<string, any> | undefined;
10431043
}
10441044

1045+
//
1046+
// TRANSPORTS
1047+
//
1048+
1049+
export interface Transport {
1050+
listen: (port: number, fetch: ServerSideFetch, cb?: () => void) => void;
1051+
}
1052+
10451053
//
10461054
// SETTER
10471055
//

0 commit comments

Comments
 (0)