@aklinker1/zeta 2.1.1 → 2.1.3
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/client.d.mts +71 -0
- package/dist/client.mjs +73 -0
- package/dist/index.d.mts +316 -0
- package/dist/index.mjs +1236 -0
- package/dist/schema-IKHh0I39.d.mts +168 -0
- package/dist/schema.d.mts +2 -0
- package/dist/schema.mjs +151 -0
- package/dist/serialization-C8e7ECQ2.mjs +56 -0
- package/dist/testing.d.mts +26 -0
- package/dist/testing.mjs +52 -0
- package/dist/transports/bun-transport.d.mts +7 -0
- package/dist/transports/bun-transport.mjs +14 -0
- package/dist/transports/deno-transport.d.mts +6 -0
- package/dist/transports/deno-transport.mjs +13 -0
- package/dist/types-D9oRVe1E.d.mts +698 -0
- package/dist/types.d.mts +2 -0
- package/dist/types.mjs +1 -0
- package/package.json +39 -17
- 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
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
|
|
3
|
+
//#region src/status.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Enum containing all HTTP status codes.
|
|
6
|
+
*/
|
|
7
|
+
declare enum HttpStatus {
|
|
8
|
+
Continue = 100,
|
|
9
|
+
SwitchingProtocols = 101,
|
|
10
|
+
ProcessingDeprecated = 102,
|
|
11
|
+
EarlyHints = 103,
|
|
12
|
+
Ok = 200,
|
|
13
|
+
Created = 201,
|
|
14
|
+
Accepted = 202,
|
|
15
|
+
NonAuthoritativeInformation = 203,
|
|
16
|
+
NoContent = 204,
|
|
17
|
+
ResetContent = 205,
|
|
18
|
+
PartialContent = 206,
|
|
19
|
+
MultiStatus = 207,
|
|
20
|
+
AlreadyReported = 208,
|
|
21
|
+
ImUsed = 226,
|
|
22
|
+
MultipleChoices = 300,
|
|
23
|
+
MovedPermanently = 301,
|
|
24
|
+
Found = 302,
|
|
25
|
+
SeeOther = 303,
|
|
26
|
+
NotModified = 304,
|
|
27
|
+
UseProxyDeprecated = 305,
|
|
28
|
+
Unused = 306,
|
|
29
|
+
TemporaryRedirect = 307,
|
|
30
|
+
PermanentRedirect = 308,
|
|
31
|
+
BadRequest = 400,
|
|
32
|
+
Unauthorized = 401,
|
|
33
|
+
PaymentRequired = 402,
|
|
34
|
+
Forbidden = 403,
|
|
35
|
+
NotFound = 404,
|
|
36
|
+
MethodNotAllowed = 405,
|
|
37
|
+
NotAcceptable = 406,
|
|
38
|
+
ProxyAuthenticationRequired = 407,
|
|
39
|
+
RequestTimeout = 408,
|
|
40
|
+
Conflict = 409,
|
|
41
|
+
Gone = 410,
|
|
42
|
+
LengthRequired = 411,
|
|
43
|
+
PreconditionFailed = 412,
|
|
44
|
+
ContentTooLarge = 413,
|
|
45
|
+
UriTooLong = 414,
|
|
46
|
+
UnsupportedMediaType = 415,
|
|
47
|
+
RangeNotSatisfiable = 416,
|
|
48
|
+
ExpectationFailed = 417,
|
|
49
|
+
ImATeapot = 418,
|
|
50
|
+
MisdirectedRequest = 421,
|
|
51
|
+
UnprocessableEntity = 422,
|
|
52
|
+
Locked = 423,
|
|
53
|
+
FailedDependency = 424,
|
|
54
|
+
TooEarly = 425,
|
|
55
|
+
UpgradeRequired = 426,
|
|
56
|
+
PreconditionRequired = 428,
|
|
57
|
+
TooManyRequests = 429,
|
|
58
|
+
RequestHeaderFieldsTooLarge = 431,
|
|
59
|
+
UnavailableForLegalReasons = 451,
|
|
60
|
+
InternalServerError = 500,
|
|
61
|
+
NotImplemented = 501,
|
|
62
|
+
BadGateway = 502,
|
|
63
|
+
ServiceUnavailable = 503,
|
|
64
|
+
GatewayTimeout = 504,
|
|
65
|
+
HttpVersionNotSupported = 505,
|
|
66
|
+
VariantAlsoNegotiates = 506,
|
|
67
|
+
InsufficientStorage = 507,
|
|
68
|
+
LoopDetected = 508,
|
|
69
|
+
NotExtended = 510,
|
|
70
|
+
NetworkAuthenticationRequired = 511
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Returns the name of the HTTP status code (200 -> "OK").
|
|
74
|
+
* @param status The HTTP status code.
|
|
75
|
+
* @returns The name of the HTTP status code or `undefined` if the status code is not recognized.
|
|
76
|
+
*/
|
|
77
|
+
declare function getHttpStatusName(status: number): string | undefined;
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/schema.d.ts
|
|
80
|
+
type ZetaSchema<Input = unknown, Output = Input> = StandardSchemaV1<Input, Output> & {
|
|
81
|
+
"~zeta": {
|
|
82
|
+
type: string;
|
|
83
|
+
meta: Record<string, any>;
|
|
84
|
+
};
|
|
85
|
+
toJsonSchema?(): any;
|
|
86
|
+
meta(meta?: Record<string, any>): ZetaSchema<Input, Output>;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* A schema for an error response. Use when defining additional status codes
|
|
90
|
+
* that an operation might return with:
|
|
91
|
+
*
|
|
92
|
+
* ```ts
|
|
93
|
+
* import { ErrorResponse } from '@aklinker/zeta';
|
|
94
|
+
*
|
|
95
|
+
* app.get(
|
|
96
|
+
* "/api/item/:itemId",
|
|
97
|
+
* {
|
|
98
|
+
* responses: {
|
|
99
|
+
* 200: Item.optional(),
|
|
100
|
+
* 404: ErrorResponse,
|
|
101
|
+
* }
|
|
102
|
+
* },
|
|
103
|
+
* () => {
|
|
104
|
+
* // ...
|
|
105
|
+
* }
|
|
106
|
+
* );
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
declare const ErrorResponse: ZetaSchema<unknown, ErrorResponse>;
|
|
110
|
+
declare function isZetaSchema(schema: any): schema is ZetaSchema;
|
|
111
|
+
/**
|
|
112
|
+
* The actual type an error response conforms to.
|
|
113
|
+
*/
|
|
114
|
+
type ErrorResponse = {
|
|
115
|
+
[additionalInfo: string]: any;
|
|
116
|
+
name: string;
|
|
117
|
+
message: string;
|
|
118
|
+
status: HttpStatus;
|
|
119
|
+
stack?: string[];
|
|
120
|
+
cause?: ErrorResponse;
|
|
121
|
+
};
|
|
122
|
+
declare const ErrorResponseJsonSchema: {
|
|
123
|
+
type: "object";
|
|
124
|
+
properties: {
|
|
125
|
+
status: {
|
|
126
|
+
type: "number";
|
|
127
|
+
description: string;
|
|
128
|
+
example: number;
|
|
129
|
+
};
|
|
130
|
+
name: {
|
|
131
|
+
type: "string";
|
|
132
|
+
description: string;
|
|
133
|
+
example: string;
|
|
134
|
+
};
|
|
135
|
+
message: {
|
|
136
|
+
type: "string";
|
|
137
|
+
description: string;
|
|
138
|
+
example: string;
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
required: string[];
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* A schema for when you want to not return a response. Use when defining
|
|
145
|
+
* additional status codes that an operation might return with:
|
|
146
|
+
*
|
|
147
|
+
* ```ts
|
|
148
|
+
* import { NoResponse } from '@aklinker/zeta';
|
|
149
|
+
*
|
|
150
|
+
* app.get(
|
|
151
|
+
* "/api/item/:itemId",
|
|
152
|
+
* {
|
|
153
|
+
* responses: {
|
|
154
|
+
* [HttpStatus.Accepted]: NoResponse,
|
|
155
|
+
* }
|
|
156
|
+
* },
|
|
157
|
+
* () => {
|
|
158
|
+
* // ...
|
|
159
|
+
* }
|
|
160
|
+
* );
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
declare const NoResponse: ZetaSchema<undefined | null | void, void>;
|
|
164
|
+
declare const FormDataBody: ZetaSchema<FormData>;
|
|
165
|
+
declare const UploadFileBody: ZetaSchema<File>;
|
|
166
|
+
declare const UploadFilesBody: ZetaSchema<FileList, File[]>;
|
|
167
|
+
//#endregion
|
|
168
|
+
export { UploadFileBody as a, isZetaSchema as c, NoResponse as i, HttpStatus as l, ErrorResponseJsonSchema as n, UploadFilesBody as o, FormDataBody as r, ZetaSchema as s, ErrorResponse as t, getHttpStatusName as u };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as UploadFileBody, c as isZetaSchema, i as NoResponse, n as ErrorResponseJsonSchema, o as UploadFilesBody, r as FormDataBody, s as ZetaSchema, t as ErrorResponse } from "./schema-IKHh0I39.mjs";
|
|
2
|
+
export { ErrorResponse, ErrorResponseJsonSchema, FormDataBody, NoResponse, UploadFileBody, UploadFilesBody, ZetaSchema, isZetaSchema };
|
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 { i as App } from "./types-D9oRVe1E.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,7 @@
|
|
|
1
|
+
import { at as Transport } from "../types-D9oRVe1E.mjs";
|
|
2
|
+
import { ServeFunctionOptions } from "@types/bun";
|
|
3
|
+
|
|
4
|
+
//#region src/transports/bun-transport.d.ts
|
|
5
|
+
declare function createBunTransport(options: Omit<ServeFunctionOptions<any, any>, "fetch" | "port">): Transport;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { createBunTransport };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/transports/bun-transport.ts
|
|
2
|
+
function createBunTransport(options) {
|
|
3
|
+
const listen = (port, fetch, cb) => {
|
|
4
|
+
Bun.serve({
|
|
5
|
+
...options,
|
|
6
|
+
port,
|
|
7
|
+
fetch
|
|
8
|
+
});
|
|
9
|
+
if (cb) setTimeout(cb, 0);
|
|
10
|
+
};
|
|
11
|
+
return { listen };
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { createBunTransport };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/transports/deno-transport.ts
|
|
2
|
+
function createDenoTransport() {
|
|
3
|
+
const listen = (port, fetch, cb) => {
|
|
4
|
+
Deno.serve({
|
|
5
|
+
port,
|
|
6
|
+
fetch
|
|
7
|
+
});
|
|
8
|
+
if (cb) setTimeout(cb, 0);
|
|
9
|
+
};
|
|
10
|
+
return { listen };
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { createDenoTransport };
|