@aklinker1/zeta 1.1.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@aklinker1/zeta",
3
3
  "description": "Composable, testable, OpenAPI-first backend framework with validation built-in",
4
- "version": "1.1.3",
4
+ "version": "1.2.0",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "packageManager": "bun@1.3.2",
@@ -42,12 +42,13 @@
42
42
  "docs:preview": "vitepress preview docs"
43
43
  },
44
44
  "dependencies": {
45
- "openapi-types": "^12.1.3",
46
45
  "@standard-schema/spec": "^1.0.0",
46
+ "openapi-types": "^12.1.3",
47
47
  "rou3": "^0.7.1"
48
48
  },
49
49
  "devDependencies": {
50
- "@aklinker1/check": "^2.1.0",
50
+ "@aklinker1/check": "^2.2.0",
51
+ "@typescript/native-preview": "^7.0.0-dev.20251114.1",
51
52
  "@types/bun": "latest",
52
53
  "changelogen": "^0.6.2",
53
54
  "elysia": "^1.3.5",
@@ -59,7 +60,6 @@
59
60
  "prettier": "^3.5.3",
60
61
  "publint": "^0.3.12",
61
62
  "tinybench": "^4.0.1",
62
- "typescript": "^5.8.3",
63
63
  "vitepress": "^2.0.0-alpha.12",
64
64
  "vitepress-plugin-mermaid": "^2.0.17",
65
65
  "zod": "^4.1.11"
package/src/app.ts CHANGED
@@ -13,9 +13,14 @@ import type {
13
13
  DefaultAppData,
14
14
  BasePrefix,
15
15
  SchemaAdapter,
16
+ Transport,
16
17
  } from "./types";
17
18
  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";
19
24
  import type { OpenAPIV3_1 } from "openapi-types";
20
25
  import { buildOpenApiDocs, buildScalarHtml } from "./open-api";
21
26
 
@@ -227,17 +232,8 @@ export function createApp<TPrefix extends BasePrefix = "">(
227
232
  },
228
233
 
229
234
  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);
241
237
  return app;
242
238
  },
243
239
 
@@ -428,6 +424,24 @@ export type CreateAppOptions<TPrefix extends BasePrefix = ""> = {
428
424
  */
429
425
  schemaAdapter?: SchemaAdapter;
430
426
 
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
+
431
445
  /**
432
446
  * Where the OpenAPI JSON docs is hosted.
433
447
  * @default "/openapi.json"
@@ -7,9 +7,12 @@ import type {
7
7
  MaybePromise,
8
8
  RouterData,
9
9
  StatusResult,
10
+ Transport,
10
11
  } from "../types";
11
12
  import type { MatchedRoute } from "rou3";
12
13
  import type { ErrorResponse } from "../custom-responses";
14
+ import { createBunTransport } from "../transports/bun-transport";
15
+ import { createDenoTransport } from "../transports/deno-transport";
13
16
 
14
17
  export function validateSchema<T>(
15
18
  schema: StandardSchemaV1<T, T>,
@@ -136,3 +139,22 @@ export const IsStatusResult = Symbol("IsStatusResult");
136
139
  export function isStatusResult(result: any): result is StatusResult {
137
140
  return IsStatusResult in result;
138
141
  }
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
+ }
package/src/open-api.ts CHANGED
@@ -193,6 +193,7 @@ function optimizeSpec(spec: OpenAPI.Document): OpenAPI.Document {
193
193
 
194
194
  // Optimizations
195
195
  addModelRefs(optimized);
196
+ sortComponentSchemas(optimized);
196
197
 
197
198
  return optimized;
198
199
  }
@@ -234,3 +235,13 @@ function addModelRefs(spec: any): void {
234
235
  // Process the "paths" object
235
236
  recurse(spec.paths);
236
237
  }
238
+
239
+ function sortComponentSchemas(spec: any): void {
240
+ if (!spec?.components?.schemas) return;
241
+
242
+ spec.components.schemas = Object.fromEntries(
243
+ Object.entries(spec.components.schemas).sort((a, b) =>
244
+ a[0].toLowerCase().localeCompare(b[0].toLowerCase()),
245
+ ),
246
+ );
247
+ }
package/src/types.ts CHANGED
@@ -1042,6 +1042,14 @@ export interface SchemaAdapter {
1042
1042
  getMeta: (schema: StandardSchemaV1) => Record<string, any> | undefined;
1043
1043
  }
1044
1044
 
1045
+ //
1046
+ // TRANSPORTS
1047
+ //
1048
+
1049
+ export interface Transport {
1050
+ listen: (port: number, fetch: ServerSideFetch, cb?: () => void) => void;
1051
+ }
1052
+
1045
1053
  //
1046
1054
  // SETTER
1047
1055
  //