@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 +4 -4
- package/src/app.ts +26 -12
- package/src/internal/utils.ts +22 -0
- package/src/open-api.ts +11 -0
- package/src/types.ts +8 -0
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.
|
|
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.
|
|
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 {
|
|
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
|
-
|
|
231
|
-
|
|
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"
|
package/src/internal/utils.ts
CHANGED
|
@@ -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
|
//
|