@aklinker1/zeta 2.1.2 → 2.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/dist/adapters/zod-schema-adapter.d.mts +17 -0
- package/dist/adapters/zod-schema-adapter.mjs +726 -0
- package/dist/app-Bc9Kn3KA.mjs +1225 -0
- package/dist/client.d.mts +71 -0
- package/dist/client.mjs +73 -0
- package/dist/index.d.mts +317 -0
- package/dist/index.mjs +3 -0
- package/dist/schema-DKqL09oQ.d.mts +168 -0
- package/dist/schema.d.mts +2 -0
- package/dist/schema.mjs +151 -0
- package/dist/serialization-0dai2wUm.mjs +56 -0
- package/dist/testing.d.mts +26 -0
- package/dist/testing.mjs +52 -0
- package/dist/transports/bun-transport.d.mts +47 -0
- package/dist/transports/bun-transport.mjs +58 -0
- package/dist/transports/deno-transport.d.mts +48 -0
- package/dist/transports/deno-transport.mjs +57 -0
- package/dist/transports/fetch-transport.d.mts +6 -0
- package/dist/transports/fetch-transport.mjs +25 -0
- package/dist/types-BvjPE9EM.d.mts +712 -0
- package/dist/types.d.mts +2 -0
- package/dist/types.mjs +1 -0
- package/package.json +51 -19
- package/src/adapters/zod-schema-adapter.ts +0 -29
- package/src/app.ts +0 -479
- package/src/client.ts +0 -184
- package/src/errors.ts +0 -529
- package/src/index.ts +0 -5
- package/src/internal/compile-fetch-function.ts +0 -166
- package/src/internal/compile-route-handler.ts +0 -194
- package/src/internal/context.ts +0 -65
- package/src/internal/serialization.ts +0 -91
- package/src/internal/utils.ts +0 -191
- package/src/meta.ts +0 -14
- package/src/open-api.ts +0 -273
- package/src/schema.ts +0 -271
- package/src/status.ts +0 -143
- package/src/testing.ts +0 -62
- package/src/transports/bun-transport.ts +0 -17
- package/src/transports/deno-transport.ts +0 -13
- package/src/types.ts +0 -1102
package/dist/schema.mjs
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
//#region src/schema.ts
|
|
2
|
+
function createZetaSchema(name, validate, toJsonSchema, meta = {}) {
|
|
3
|
+
const parentMeta = meta;
|
|
4
|
+
return {
|
|
5
|
+
"~zeta": {
|
|
6
|
+
type: name,
|
|
7
|
+
meta
|
|
8
|
+
},
|
|
9
|
+
"~standard": {
|
|
10
|
+
vendor: "@aklinker/zeta",
|
|
11
|
+
version: 1,
|
|
12
|
+
validate
|
|
13
|
+
},
|
|
14
|
+
meta(meta) {
|
|
15
|
+
return createZetaSchema(name, validate, void 0, {
|
|
16
|
+
...parentMeta,
|
|
17
|
+
...meta
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
toJsonSchema
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A schema for an error response. Use when defining additional status codes
|
|
25
|
+
* that an operation might return with:
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { ErrorResponse } from '@aklinker/zeta';
|
|
29
|
+
*
|
|
30
|
+
* app.get(
|
|
31
|
+
* "/api/item/:itemId",
|
|
32
|
+
* {
|
|
33
|
+
* responses: {
|
|
34
|
+
* 200: Item.optional(),
|
|
35
|
+
* 404: ErrorResponse,
|
|
36
|
+
* }
|
|
37
|
+
* },
|
|
38
|
+
* () => {
|
|
39
|
+
* // ...
|
|
40
|
+
* }
|
|
41
|
+
* );
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
const ErrorResponse = createZetaSchema("ErrorResponse", (value) => {
|
|
45
|
+
if (value == null) return { issues: [{ message: `Expected an object, received ${value}` }] };
|
|
46
|
+
if (typeof value !== "object") return { issues: [{ message: `Expected an object, received ${typeof value}` }] };
|
|
47
|
+
const issues = [];
|
|
48
|
+
if (typeof value.name !== "string") issues.push({
|
|
49
|
+
message: `Expected a string, received ${typeof value.name}`,
|
|
50
|
+
path: ["name"]
|
|
51
|
+
});
|
|
52
|
+
if (typeof value.message !== "string") issues.push({
|
|
53
|
+
message: `Expected a string, received ${typeof value.message}`,
|
|
54
|
+
path: ["message"]
|
|
55
|
+
});
|
|
56
|
+
if (typeof value.status !== "number") issues.push({
|
|
57
|
+
message: `Expected a number, received ${typeof value.status}`,
|
|
58
|
+
path: ["status"]
|
|
59
|
+
});
|
|
60
|
+
if (issues.length > 0) return { issues };
|
|
61
|
+
return { value };
|
|
62
|
+
});
|
|
63
|
+
function isZetaSchema(schema) {
|
|
64
|
+
return schema?.["~standard"]?.vendor === "@aklinker/zeta";
|
|
65
|
+
}
|
|
66
|
+
const ErrorResponseJsonSchema = {
|
|
67
|
+
type: "object",
|
|
68
|
+
properties: {
|
|
69
|
+
status: {
|
|
70
|
+
type: "number",
|
|
71
|
+
description: "HTTP status code",
|
|
72
|
+
example: 400
|
|
73
|
+
},
|
|
74
|
+
name: {
|
|
75
|
+
type: "string",
|
|
76
|
+
description: "The error's name",
|
|
77
|
+
example: "Bad Request"
|
|
78
|
+
},
|
|
79
|
+
message: {
|
|
80
|
+
type: "string",
|
|
81
|
+
description: "User-facing error message",
|
|
82
|
+
example: "Property 'name' is required"
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
required: [
|
|
86
|
+
"status",
|
|
87
|
+
"name",
|
|
88
|
+
"message"
|
|
89
|
+
]
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* A schema for when you want to not return a response. Use when defining
|
|
93
|
+
* additional status codes that an operation might return with:
|
|
94
|
+
*
|
|
95
|
+
* ```ts
|
|
96
|
+
* import { NoResponse } from '@aklinker/zeta';
|
|
97
|
+
*
|
|
98
|
+
* app.get(
|
|
99
|
+
* "/api/item/:itemId",
|
|
100
|
+
* {
|
|
101
|
+
* responses: {
|
|
102
|
+
* [HttpStatus.Accepted]: NoResponse,
|
|
103
|
+
* }
|
|
104
|
+
* },
|
|
105
|
+
* () => {
|
|
106
|
+
* // ...
|
|
107
|
+
* }
|
|
108
|
+
* );
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
const NoResponse = createZetaSchema("NoResponse", (value) => {
|
|
112
|
+
return value != null ? { issues: [{ message: `Expected undefined or null, got ${typeof value}` }] } : { value: void 0 };
|
|
113
|
+
});
|
|
114
|
+
const FormDataBody = createZetaSchema("FormDataBody", (value) => {
|
|
115
|
+
return value instanceof FormData ? { value } : { issues: [{ message: `Expected FormData, got ${typeof value}` }] };
|
|
116
|
+
}, () => ({
|
|
117
|
+
type: "object",
|
|
118
|
+
additionalProperties: true
|
|
119
|
+
}), { contentType: "multipart/form-data" });
|
|
120
|
+
const UploadFileBody = createZetaSchema("UploadFileBody", (value) => {
|
|
121
|
+
if (!(value instanceof FormData)) return { issues: [{ message: `Expected FormData, got ${typeof value}` }] };
|
|
122
|
+
const file = value.get("file");
|
|
123
|
+
if (!(file instanceof File)) return { issues: [{ message: `Expected File, got ${typeof file}` }] };
|
|
124
|
+
return { value: file };
|
|
125
|
+
}, () => ({
|
|
126
|
+
type: "object",
|
|
127
|
+
properties: { file: {
|
|
128
|
+
type: "string",
|
|
129
|
+
format: "binary"
|
|
130
|
+
} }
|
|
131
|
+
}), { contentType: "multipart/form-data" });
|
|
132
|
+
const UploadFilesBody = createZetaSchema("UploadFilesBody", (value) => {
|
|
133
|
+
if (!(value instanceof FormData)) return { issues: [{ message: `Expected FormData, got ${typeof value}` }] };
|
|
134
|
+
const files = value.getAll("files");
|
|
135
|
+
if (!Array.isArray(files)) return { issues: [{ message: `Expected array of Files, got ${typeof files}` }] };
|
|
136
|
+
const issues = [];
|
|
137
|
+
for (const file of files) if (!(file instanceof File)) issues.push(`Expected File, got ${typeof file}`);
|
|
138
|
+
if (issues.length > 0) return { issues: issues.map((message) => ({ message })) };
|
|
139
|
+
return { value: files };
|
|
140
|
+
}, () => ({
|
|
141
|
+
type: "object",
|
|
142
|
+
properties: { files: {
|
|
143
|
+
type: "array",
|
|
144
|
+
items: {
|
|
145
|
+
type: "string",
|
|
146
|
+
format: "binary"
|
|
147
|
+
}
|
|
148
|
+
} }
|
|
149
|
+
}), { contentType: "multipart/form-data" });
|
|
150
|
+
//#endregion
|
|
151
|
+
export { ErrorResponse, ErrorResponseJsonSchema, FormDataBody, NoResponse, UploadFileBody, UploadFilesBody, isZetaSchema };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//#region src/internal/serialization.ts
|
|
2
|
+
function smartSerialize(value) {
|
|
3
|
+
if (value == null) return void 0;
|
|
4
|
+
switch (typeof value) {
|
|
5
|
+
case "string": return {
|
|
6
|
+
contentType: "text/plain",
|
|
7
|
+
value
|
|
8
|
+
};
|
|
9
|
+
case "number":
|
|
10
|
+
case "boolean":
|
|
11
|
+
case "bigint": return {
|
|
12
|
+
contentType: "text/plain",
|
|
13
|
+
value: String(value)
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
if (value instanceof FormData) return {
|
|
17
|
+
contentType: void 0,
|
|
18
|
+
value
|
|
19
|
+
};
|
|
20
|
+
if (value instanceof File) {
|
|
21
|
+
const form = new FormData();
|
|
22
|
+
form.append("file", value);
|
|
23
|
+
return {
|
|
24
|
+
contentType: void 0,
|
|
25
|
+
value: form
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (typeof FileList !== "undefined" && value instanceof FileList) {
|
|
29
|
+
const form = new FormData();
|
|
30
|
+
for (let i = 0; i < value.length; i++) form.append("files", value.item(i));
|
|
31
|
+
return {
|
|
32
|
+
contentType: void 0,
|
|
33
|
+
value: form
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (value instanceof Blob) return {
|
|
37
|
+
contentType: value.type,
|
|
38
|
+
value
|
|
39
|
+
};
|
|
40
|
+
return {
|
|
41
|
+
contentType: "application/json",
|
|
42
|
+
value: JSON.stringify(value)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function smartDeserialize(arg) {
|
|
46
|
+
if (arg instanceof Request && arg.method === "GET") return;
|
|
47
|
+
const contentType = arg.headers.get("content-type");
|
|
48
|
+
if (contentType == null) return;
|
|
49
|
+
if (contentType.startsWith("application/json")) return arg.json();
|
|
50
|
+
if (contentType.startsWith("application/x-www-form-urlencoded") || contentType.startsWith("multipart/form-data")) return arg.formData();
|
|
51
|
+
if (contentType.startsWith("text/")) return arg.text();
|
|
52
|
+
if (contentType.startsWith("application/octet-stream")) return arg.arrayBuffer();
|
|
53
|
+
throw Error(`Unknown content type: "${contentType}"`);
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { smartSerialize as n, smartDeserialize as t };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { a as App } from "./types-BvjPE9EM.mjs";
|
|
2
|
+
import { AppClient, CreateAppClientOptions, GetClientRoutes } from "./client.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/testing.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Create a client for testing your server-side application. When `fetch` is
|
|
7
|
+
* called, the app's `fetch` function is called instead of using the global
|
|
8
|
+
* `fetch`.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const app = createApp()
|
|
13
|
+
* .get("/example", () => "Hello, world!")
|
|
14
|
+
*
|
|
15
|
+
* const client = createTestAppClient(app);
|
|
16
|
+
*
|
|
17
|
+
* expect(await client.get("/example")).toEqual("Hello, world!");
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @param app
|
|
21
|
+
* @param options
|
|
22
|
+
* @returns An app client used to test your server-side application.
|
|
23
|
+
*/
|
|
24
|
+
declare function createTestAppClient<TApp extends App>(app: TApp, options?: Omit<CreateAppClientOptions, "fetch">): AppClient<GetClientRoutes<TApp>>;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { createTestAppClient };
|
package/dist/testing.mjs
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createAppClient } from "./client.mjs";
|
|
2
|
+
//#region src/testing.ts
|
|
3
|
+
/**
|
|
4
|
+
* Utilities for testing your server-side application.
|
|
5
|
+
*
|
|
6
|
+
* You don't need to use these utils, you can call the app's `fetch` function directly.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* const app = createApp()
|
|
10
|
+
* .get("/example", () => "Hello, world!")
|
|
11
|
+
* const fetch = app.build();
|
|
12
|
+
*
|
|
13
|
+
* const res = await fetch("http://test/example");
|
|
14
|
+
* expect(res.status).toEqual(200);
|
|
15
|
+
* expect(await res.text()).toEqual("Hello, world!");
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* If you just care about the response body or error thrown, you can use the `createTestAppClient`.
|
|
19
|
+
*
|
|
20
|
+
* @see {@link createTestAppClient}
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Create a client for testing your server-side application. When `fetch` is
|
|
25
|
+
* called, the app's `fetch` function is called instead of using the global
|
|
26
|
+
* `fetch`.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const app = createApp()
|
|
31
|
+
* .get("/example", () => "Hello, world!")
|
|
32
|
+
*
|
|
33
|
+
* const client = createTestAppClient(app);
|
|
34
|
+
*
|
|
35
|
+
* expect(await client.get("/example")).toEqual("Hello, world!");
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @param app
|
|
39
|
+
* @param options
|
|
40
|
+
* @returns An app client used to test your server-side application.
|
|
41
|
+
*/
|
|
42
|
+
function createTestAppClient(app, options) {
|
|
43
|
+
const { baseUrl = "http://localhost" } = options ?? {};
|
|
44
|
+
const fetch = app.build();
|
|
45
|
+
return createAppClient({
|
|
46
|
+
baseUrl,
|
|
47
|
+
...options,
|
|
48
|
+
fetch: (...args) => fetch(new Request(...args))
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
export { createTestAppClient };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Z as RequestContext, a as App, st as Transport } from "../types-BvjPE9EM.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/transports/bun-transport.d.ts
|
|
4
|
+
type BunTransport = Transport<[request: Request, server: Bun.Server]>;
|
|
5
|
+
declare function createBunTransport(options?: Omit<Bun.ServeFunctionOptions<any, any>, "fetch" | "port">): BunTransport;
|
|
6
|
+
/**
|
|
7
|
+
* Given the request context, return Bun's `server` object. Throws an error if the bun transport is not provided on the top-level app.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const app = createApp({
|
|
12
|
+
* transport: createBunTransport(),
|
|
13
|
+
* }).get("/", (ctx) => {
|
|
14
|
+
* const server = getBunServer(ctx);
|
|
15
|
+
* })
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @see `bunServerPlugin` to add the `server` object to request context directly.
|
|
19
|
+
*/
|
|
20
|
+
declare function getBunServer(ctx: RequestContext): Bun.Server;
|
|
21
|
+
/**
|
|
22
|
+
* Plugin that decorates Bun's `server` object in the request context.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const app = createApp({
|
|
27
|
+
* transport: createBunTransport(),
|
|
28
|
+
* })
|
|
29
|
+
* .use(bunServerPlugin)
|
|
30
|
+
* .get("/", ({ server }) => {
|
|
31
|
+
* // ...
|
|
32
|
+
* })
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @see `getBunServer` for a simple function to return the server
|
|
36
|
+
*/
|
|
37
|
+
declare const bunServerPlugin: App<{
|
|
38
|
+
prefix: "";
|
|
39
|
+
ctx: {
|
|
40
|
+
server: Bun.Server;
|
|
41
|
+
};
|
|
42
|
+
exported: true;
|
|
43
|
+
routes: {};
|
|
44
|
+
transport: BunTransport;
|
|
45
|
+
}>;
|
|
46
|
+
//#endregion
|
|
47
|
+
export { BunTransport, bunServerPlugin, createBunTransport, getBunServer };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { t as createApp } from "../app-Bc9Kn3KA.mjs";
|
|
2
|
+
//#region src/transports/bun-transport.ts
|
|
3
|
+
const SERVER_KEY = Symbol("bun-transport.server");
|
|
4
|
+
function createBunTransport(options) {
|
|
5
|
+
const listen = (port, fetch, cb) => {
|
|
6
|
+
Bun.serve({
|
|
7
|
+
...options,
|
|
8
|
+
port,
|
|
9
|
+
fetch
|
|
10
|
+
});
|
|
11
|
+
if (cb) setTimeout(cb, 0);
|
|
12
|
+
};
|
|
13
|
+
const decorate = (ctx, _request, server) => {
|
|
14
|
+
ctx[SERVER_KEY] = server;
|
|
15
|
+
};
|
|
16
|
+
return {
|
|
17
|
+
listen,
|
|
18
|
+
decorate
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Given the request context, return Bun's `server` object. Throws an error if the bun transport is not provided on the top-level app.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const app = createApp({
|
|
27
|
+
* transport: createBunTransport(),
|
|
28
|
+
* }).get("/", (ctx) => {
|
|
29
|
+
* const server = getBunServer(ctx);
|
|
30
|
+
* })
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @see `bunServerPlugin` to add the `server` object to request context directly.
|
|
34
|
+
*/
|
|
35
|
+
function getBunServer(ctx) {
|
|
36
|
+
const server = ctx[SERVER_KEY];
|
|
37
|
+
if (!server) throw Error("Bun server not found. Did you forget to provide the bun transport?");
|
|
38
|
+
return server;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Plugin that decorates Bun's `server` object in the request context.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* const app = createApp({
|
|
46
|
+
* transport: createBunTransport(),
|
|
47
|
+
* })
|
|
48
|
+
* .use(bunServerPlugin)
|
|
49
|
+
* .get("/", ({ server }) => {
|
|
50
|
+
* // ...
|
|
51
|
+
* })
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @see `getBunServer` for a simple function to return the server
|
|
55
|
+
*/
|
|
56
|
+
const bunServerPlugin = createApp().onTransform((ctx) => ({ server: getBunServer(ctx) })).export();
|
|
57
|
+
//#endregion
|
|
58
|
+
export { bunServerPlugin, createBunTransport, getBunServer };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Z as RequestContext, a as App, st as Transport } from "../types-BvjPE9EM.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/transports/deno-transport.d.ts
|
|
4
|
+
type DenoTransport = Transport<[request: Request, server: Deno.HttpServer]>;
|
|
5
|
+
type ServeOptions = Parameters<typeof Deno.serve>[0];
|
|
6
|
+
declare function createDenoTransport(options?: Omit<ServeOptions, "port">): DenoTransport;
|
|
7
|
+
/**
|
|
8
|
+
* Given the request context, return Deno's `server` object. Throws an error if the Deno transport is not provided on the top-level app.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const app = createApp({
|
|
13
|
+
* transport: createDenoTransport(),
|
|
14
|
+
* }).get("/", (ctx) => {
|
|
15
|
+
* const server = getDenoServer(ctx);
|
|
16
|
+
* })
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @see `denoServerPlugin` to add the `server` object to request context directly.
|
|
20
|
+
*/
|
|
21
|
+
declare function getDenoServer(ctx: RequestContext): Deno.HttpServer;
|
|
22
|
+
/**
|
|
23
|
+
* Plugin that decorates Deno's `server` object in the request context.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const app = createApp({
|
|
28
|
+
* transport: createDenoTransport(),
|
|
29
|
+
* })
|
|
30
|
+
* .use(denoServerPlugin)
|
|
31
|
+
* .get("/", ({ server }) => {
|
|
32
|
+
* // ...
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @see `getDenoServer` for a simple function to return the server
|
|
37
|
+
*/
|
|
38
|
+
declare const denoServerPlugin: App<{
|
|
39
|
+
prefix: "";
|
|
40
|
+
ctx: {
|
|
41
|
+
server: Deno.HttpServer;
|
|
42
|
+
};
|
|
43
|
+
exported: true;
|
|
44
|
+
routes: {};
|
|
45
|
+
transport: DenoTransport;
|
|
46
|
+
}>;
|
|
47
|
+
//#endregion
|
|
48
|
+
export { DenoTransport, createDenoTransport, denoServerPlugin, getDenoServer };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { t as createApp } from "../app-Bc9Kn3KA.mjs";
|
|
2
|
+
//#region src/transports/deno-transport.ts
|
|
3
|
+
const SERVER_KEY = Symbol("deno-transport.server");
|
|
4
|
+
function createDenoTransport(options) {
|
|
5
|
+
const listen = (port, fetch, cb) => {
|
|
6
|
+
Deno.serve({
|
|
7
|
+
...options,
|
|
8
|
+
port
|
|
9
|
+
}, fetch);
|
|
10
|
+
if (cb) setTimeout(cb, 0);
|
|
11
|
+
};
|
|
12
|
+
const decorate = (ctx, _request, server) => {
|
|
13
|
+
ctx[SERVER_KEY] = server;
|
|
14
|
+
};
|
|
15
|
+
return {
|
|
16
|
+
listen,
|
|
17
|
+
decorate
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Given the request context, return Deno's `server` object. Throws an error if the Deno transport is not provided on the top-level app.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const app = createApp({
|
|
26
|
+
* transport: createDenoTransport(),
|
|
27
|
+
* }).get("/", (ctx) => {
|
|
28
|
+
* const server = getDenoServer(ctx);
|
|
29
|
+
* })
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @see `denoServerPlugin` to add the `server` object to request context directly.
|
|
33
|
+
*/
|
|
34
|
+
function getDenoServer(ctx) {
|
|
35
|
+
const server = ctx[SERVER_KEY];
|
|
36
|
+
if (!server) throw Error("Deno server not found. Did you forget to provide the deno transport?");
|
|
37
|
+
return server;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Plugin that decorates Deno's `server` object in the request context.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* const app = createApp({
|
|
45
|
+
* transport: createDenoTransport(),
|
|
46
|
+
* })
|
|
47
|
+
* .use(denoServerPlugin)
|
|
48
|
+
* .get("/", ({ server }) => {
|
|
49
|
+
* // ...
|
|
50
|
+
* })
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @see `getDenoServer` for a simple function to return the server
|
|
54
|
+
*/
|
|
55
|
+
const denoServerPlugin = createApp().onTransform((ctx) => ({ server: getDenoServer(ctx) })).export();
|
|
56
|
+
//#endregion
|
|
57
|
+
export { createDenoTransport, denoServerPlugin, getDenoServer };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/transports/fetch-transport.ts
|
|
2
|
+
function createFetchTransport() {
|
|
3
|
+
const listen = (port, fetch, cb) => {
|
|
4
|
+
if (globalThis.Bun) Bun.serve({
|
|
5
|
+
port,
|
|
6
|
+
fetch
|
|
7
|
+
});
|
|
8
|
+
else if (globalThis.Deno) Deno.serve({ port }, fetch);
|
|
9
|
+
else throw Error(`Cannot automatically detect which transport to use. You must specify a transport in your top-level app:
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
import { createBunTransport } from '@aklinker1/zeta/transports/bun-transport';
|
|
13
|
+
|
|
14
|
+
const app = createApp({
|
|
15
|
+
transport: createBunTransport(),
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
app.listen();
|
|
19
|
+
---`);
|
|
20
|
+
if (cb) setTimeout(cb, 0);
|
|
21
|
+
};
|
|
22
|
+
return { listen };
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { createFetchTransport };
|