@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.
@@ -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 };
@@ -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 };
@@ -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,6 @@
1
+ import { at as Transport } from "../types-D9oRVe1E.mjs";
2
+
3
+ //#region src/transports/deno-transport.d.ts
4
+ declare function createDenoTransport(): Transport;
5
+ //#endregion
6
+ export { createDenoTransport };
@@ -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 };