@okay-e9g/hono-config 0.0.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 +47 -0
- package/src/handlers/not-found.ts +25 -0
- package/src/handlers/on-error.ts +47 -0
- package/src/helpers/factory.openapi.ts +72 -0
- package/src/helpers/factory.ts +68 -0
- package/src/helpers/index.ts +5 -0
- package/src/index.ts +7 -0
- package/src/middlewares/ensure-rt.ts +37 -0
- package/src/middlewares/index.ts +5 -0
- package/src/middlewares/response.ts +159 -0
- package/src/middlewares/superjson.ts +25 -0
- package/src/schemas/api.ts +41 -0
- package/src/schemas/http-status.ts +209 -0
- package/src/schemas/index.ts +7 -0
- package/src/schemas/rpc.ts +88 -0
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"dependencies": {},
|
|
3
|
+
"description": "Shared Hono config for Okay Engineering",
|
|
4
|
+
"devDependencies": {
|
|
5
|
+
"@cloudflare/workers-types": "5.20260705.1",
|
|
6
|
+
"@hono/zod-openapi": "1.4.0",
|
|
7
|
+
"@okay-e9g/biome-config": "workspace:*",
|
|
8
|
+
"@okay-e9g/tsconfig": "workspace:*",
|
|
9
|
+
"@types/bun": "1.3.14",
|
|
10
|
+
"@types/node": "26.1.0",
|
|
11
|
+
"hono": "4.12.27",
|
|
12
|
+
"superjson": "2.2.6",
|
|
13
|
+
"typescript": "6.0.3",
|
|
14
|
+
"zod": "4.4.3"
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
".": "./src/index.ts",
|
|
18
|
+
"./openapi-factory": "./src/helpers/factory.openapi.ts"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"./src"
|
|
22
|
+
],
|
|
23
|
+
"homepage": "https://okay.engineering",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"name": "@okay-e9g/hono-config",
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"@hono/zod-openapi": "^1.4.0",
|
|
28
|
+
"hono": "^4.12.27",
|
|
29
|
+
"superjson": "^2.2.6",
|
|
30
|
+
"zod": "^4.4.3"
|
|
31
|
+
},
|
|
32
|
+
"peerDependenciesMeta": {
|
|
33
|
+
"@hono/zod-openapi": {
|
|
34
|
+
"optional": true
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public",
|
|
39
|
+
"provenance": false,
|
|
40
|
+
"registry": "https://registry.npmjs.org"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"prepublishOnly": "bun run typecheck && bun test",
|
|
44
|
+
"typecheck": "tsc --noEmit"
|
|
45
|
+
},
|
|
46
|
+
"version": "0.0.0"
|
|
47
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-License-Identifier: MIT
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Env, NotFoundHandler } from 'hono/types';
|
|
6
|
+
|
|
7
|
+
import type { ResponseEnv } from '../middlewares/ensure-rt';
|
|
8
|
+
import { statusText } from '../schemas';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates the shared 404 handler used by the configured Hono factories.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export const notFound = <T extends Env>(): NotFoundHandler<T> => {
|
|
16
|
+
return (c) => {
|
|
17
|
+
const { response } = c.var as ResponseEnv['Variables'];
|
|
18
|
+
|
|
19
|
+
if (response.type === 'api') {
|
|
20
|
+
return c.json(response.error(404), 404);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return c.json(response.error(statusText[404]));
|
|
24
|
+
};
|
|
25
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-License-Identifier: MIT
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { HTTPException } from 'hono/http-exception';
|
|
6
|
+
import type { Env, ErrorHandler } from 'hono/types';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
|
|
9
|
+
import type { ResponseEnv } from '../middlewares/ensure-rt';
|
|
10
|
+
import { type ClientErrorStatusCode, type ServerErrorStatusCode, type StatusCode, statusText } from '../schemas';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates the shared error handler used by the configured Hono factories.
|
|
14
|
+
*
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export const onError = <T extends Env>(): ErrorHandler<T> => {
|
|
18
|
+
return (err, c) => {
|
|
19
|
+
const { response } = c.var as ResponseEnv['Variables'];
|
|
20
|
+
|
|
21
|
+
if (err instanceof z.ZodError) {
|
|
22
|
+
if (response.type === 'api') {
|
|
23
|
+
return c.json(response.error(400, z.flattenError(err)), 400);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return c.json(response.error(z.flattenError(err)));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (err instanceof HTTPException) {
|
|
30
|
+
const { message, status } = err;
|
|
31
|
+
|
|
32
|
+
if (response.type === 'api') {
|
|
33
|
+
return c.json(response.error(status as ClientErrorStatusCode | ServerErrorStatusCode, message), status);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return c.json(response.error(message === '' ? statusText[status as StatusCode] : message));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const errMessage = err instanceof Error ? err.message : err;
|
|
40
|
+
|
|
41
|
+
if (response.type === 'api') {
|
|
42
|
+
return c.json(response.error(500, errMessage), 500);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return c.json(response.error(errMessage === '' ? statusText[500] : errMessage));
|
|
46
|
+
};
|
|
47
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-License-Identifier: MIT
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { OpenAPIHono } from '@hono/zod-openapi';
|
|
6
|
+
import type { Factory } from 'hono/factory';
|
|
7
|
+
import type { HonoOptions } from 'hono/hono-base';
|
|
8
|
+
import type { Env } from 'hono/types';
|
|
9
|
+
|
|
10
|
+
import { notFound } from '../handlers/not-found';
|
|
11
|
+
import { onError } from '../handlers/on-error';
|
|
12
|
+
import { ensureResponseType } from '../middlewares/ensure-rt';
|
|
13
|
+
import { superjson } from '../middlewares/superjson';
|
|
14
|
+
import { type CreateHonoFactoryOpts, createHonoFactory } from './factory';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Hono factory whose apps expose OpenAPI route helpers.
|
|
18
|
+
*
|
|
19
|
+
* @typeParam T - Hono environment type for bindings and variables.
|
|
20
|
+
*/
|
|
21
|
+
export type OpenAPIHonoFactory<T extends Env> = Omit<Factory<T>, 'createApp'> & {
|
|
22
|
+
/**
|
|
23
|
+
* Creates an `OpenAPIHono` app configured with the shared defaults.
|
|
24
|
+
*/
|
|
25
|
+
createApp: (opts?: HonoOptions<T>) => OpenAPIHono<T>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Creates a Hono factory that produces `OpenAPIHono` apps with shared defaults.
|
|
30
|
+
*
|
|
31
|
+
* The generated apps use non-strict routing, throw validation failures from the
|
|
32
|
+
* OpenAPI default hook, install the shared not-found and error handlers, enable
|
|
33
|
+
* SuperJSON response serialization, apply any provided application middleware,
|
|
34
|
+
* and finish with the response-type guard.
|
|
35
|
+
*
|
|
36
|
+
* Include exactly one response-type middleware, such as `apiResponseType()` or
|
|
37
|
+
* `rpcResponseType()`, in `appMiddlewares` so the shared handlers can build the
|
|
38
|
+
* correct response envelope.
|
|
39
|
+
*
|
|
40
|
+
* @typeParam T - Hono environment type for bindings and variables.
|
|
41
|
+
* @param opts - Optional overrides for middleware and default handlers.
|
|
42
|
+
* @returns A Hono factory whose `createApp` method returns an `OpenAPIHono`.
|
|
43
|
+
*/
|
|
44
|
+
export const createOpenAPIHonoFactory = <T extends Env>({
|
|
45
|
+
appMiddlewares = [],
|
|
46
|
+
notFoundHandler = notFound,
|
|
47
|
+
onErrorHandler = onError,
|
|
48
|
+
}: Partial<CreateHonoFactoryOpts<T>> = {}): OpenAPIHonoFactory<T> => {
|
|
49
|
+
return {
|
|
50
|
+
...createHonoFactory<T>(),
|
|
51
|
+
createApp: (opts) => {
|
|
52
|
+
const app = new OpenAPIHono<T>({
|
|
53
|
+
...(opts as HonoOptions<Env>),
|
|
54
|
+
defaultHook: (result) => {
|
|
55
|
+
if (!result.success) {
|
|
56
|
+
throw result.error;
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
strict: false,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
app.notFound(notFoundHandler());
|
|
63
|
+
app.onError(onErrorHandler());
|
|
64
|
+
|
|
65
|
+
app.use(superjson());
|
|
66
|
+
app.use(...appMiddlewares);
|
|
67
|
+
app.use(ensureResponseType());
|
|
68
|
+
|
|
69
|
+
return app;
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-License-Identifier: MIT
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createFactory, type Factory } from 'hono/factory';
|
|
6
|
+
import type { Env, ErrorHandler, MiddlewareHandler, NotFoundHandler } from 'hono/types';
|
|
7
|
+
|
|
8
|
+
import { notFound } from '../handlers/not-found';
|
|
9
|
+
import { onError } from '../handlers/on-error';
|
|
10
|
+
import { ensureResponseType } from '../middlewares/ensure-rt';
|
|
11
|
+
import { superjson } from '../middlewares/superjson';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Options used to customize the shared Hono application factory.
|
|
15
|
+
*
|
|
16
|
+
* @typeParam T - Hono environment type for bindings and variables.
|
|
17
|
+
*/
|
|
18
|
+
export type CreateHonoFactoryOpts<T extends Env> = {
|
|
19
|
+
/**
|
|
20
|
+
* Application-level middleware installed between SuperJSON serialization and
|
|
21
|
+
* the final response-type guard.
|
|
22
|
+
*/
|
|
23
|
+
appMiddlewares: MiddlewareHandler<T>[];
|
|
24
|
+
/**
|
|
25
|
+
* Factory for the default 404 handler.
|
|
26
|
+
*/
|
|
27
|
+
notFoundHandler: () => NotFoundHandler<T>;
|
|
28
|
+
/**
|
|
29
|
+
* Factory for the default error handler.
|
|
30
|
+
*/
|
|
31
|
+
onErrorHandler: () => ErrorHandler<T>;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Creates a Hono factory configured with Okay Engineering defaults.
|
|
36
|
+
*
|
|
37
|
+
* The generated apps use non-strict routing, the shared not-found and error
|
|
38
|
+
* handlers, SuperJSON response serialization, any provided application
|
|
39
|
+
* middleware, and the response-type guard used by `apiResponseType` and
|
|
40
|
+
* `rpcResponseType`.
|
|
41
|
+
*
|
|
42
|
+
* Include exactly one response-type middleware, such as `apiResponseType()` or
|
|
43
|
+
* `rpcResponseType()`, in `appMiddlewares` so the shared handlers can build the
|
|
44
|
+
* correct response envelope.
|
|
45
|
+
*
|
|
46
|
+
* @typeParam T - Hono environment type for bindings and variables.
|
|
47
|
+
* @param opts - Optional overrides for middleware and default handlers.
|
|
48
|
+
* @returns A Hono factory ready to create configured apps and middleware.
|
|
49
|
+
*/
|
|
50
|
+
export const createHonoFactory = <T extends Env>({
|
|
51
|
+
appMiddlewares = [],
|
|
52
|
+
notFoundHandler = notFound,
|
|
53
|
+
onErrorHandler = onError,
|
|
54
|
+
}: Partial<CreateHonoFactoryOpts<T>> = {}): Factory<T> => {
|
|
55
|
+
return createFactory<T>({
|
|
56
|
+
defaultAppOptions: {
|
|
57
|
+
strict: false,
|
|
58
|
+
},
|
|
59
|
+
initApp: (app) => {
|
|
60
|
+
app.notFound(notFoundHandler());
|
|
61
|
+
app.onError(onErrorHandler());
|
|
62
|
+
|
|
63
|
+
app.use(superjson());
|
|
64
|
+
app.use(...appMiddlewares);
|
|
65
|
+
app.use(ensureResponseType());
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-License-Identifier: MIT
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createMiddleware } from 'hono/factory';
|
|
6
|
+
import type { Env, MiddlewareHandler } from 'hono/types';
|
|
7
|
+
|
|
8
|
+
import type { ApiResponseEnv, RpcResponseEnv } from './response';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Hono environment contract required by the response-type guard.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export interface ResponseEnv extends Env {
|
|
16
|
+
Variables: {
|
|
17
|
+
response: ApiResponseEnv['Variables']['response'] | RpcResponseEnv['Variables']['response'];
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Verifies that an API or RPC response helper was installed before a request
|
|
23
|
+
* reaches route handlers.
|
|
24
|
+
*
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
export const ensureResponseType = (): MiddlewareHandler<ResponseEnv> => {
|
|
28
|
+
return createMiddleware(async (c, next) => {
|
|
29
|
+
const response = (c.var as Partial<ResponseEnv['Variables']>).response;
|
|
30
|
+
|
|
31
|
+
if (response?.type !== 'api' && response?.type !== 'rpc') {
|
|
32
|
+
throw new Error('Response type middleware not initialized.');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
await next();
|
|
36
|
+
});
|
|
37
|
+
};
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-License-Identifier: MIT
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createMiddleware } from 'hono/factory';
|
|
6
|
+
import type { Env, MiddlewareHandler } from 'hono/types';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
type ApiError,
|
|
10
|
+
type ApiResult,
|
|
11
|
+
apiError,
|
|
12
|
+
apiResult,
|
|
13
|
+
type ClientErrorStatusCode,
|
|
14
|
+
type JsonRpc,
|
|
15
|
+
type RpcError,
|
|
16
|
+
type RpcResult,
|
|
17
|
+
rpcError,
|
|
18
|
+
rpcResult,
|
|
19
|
+
type ServerErrorStatusCode,
|
|
20
|
+
type SuccessStatusCode,
|
|
21
|
+
statusText,
|
|
22
|
+
} from '../schemas';
|
|
23
|
+
|
|
24
|
+
interface TypedApiError<T = unknown> extends ApiError {
|
|
25
|
+
error?: T;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface TypedApiResult<T = unknown> extends ApiResult {
|
|
29
|
+
result?: T;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type ErrorStatusCode = ClientErrorStatusCode | ServerErrorStatusCode;
|
|
33
|
+
|
|
34
|
+
type ResultStatusCode = Exclude<SuccessStatusCode, 204 | 205>;
|
|
35
|
+
|
|
36
|
+
const assertResponseTypeNotInitialized = (response: unknown): void => {
|
|
37
|
+
if (response !== undefined) {
|
|
38
|
+
throw new Error('Response type middleware already initialized.');
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Hono environment contract added by `apiResponseType`.
|
|
44
|
+
*/
|
|
45
|
+
export interface ApiResponseEnv extends Env {
|
|
46
|
+
Variables: {
|
|
47
|
+
/**
|
|
48
|
+
* Helpers for building the package's standard success and error response
|
|
49
|
+
* envelopes.
|
|
50
|
+
*/
|
|
51
|
+
response: {
|
|
52
|
+
/**
|
|
53
|
+
* Builds an API error envelope and fills an empty error with the HTTP
|
|
54
|
+
* status text for the provided status code.
|
|
55
|
+
*/
|
|
56
|
+
error: <T, U extends ErrorStatusCode>(status: U, error?: T) => TypedApiError<T>;
|
|
57
|
+
/**
|
|
58
|
+
* Builds an API result envelope and fills an empty result with the HTTP
|
|
59
|
+
* status text for the provided status code.
|
|
60
|
+
*/
|
|
61
|
+
result: <T, U extends ResultStatusCode>(status: U, result?: T) => TypedApiResult<T>;
|
|
62
|
+
/**
|
|
63
|
+
* Discriminator used by shared error handlers and response guards.
|
|
64
|
+
*/
|
|
65
|
+
type: 'api';
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Installs `c.var.response` helpers for conventional HTTP API responses.
|
|
72
|
+
*
|
|
73
|
+
* The helpers produce `{ success: false, error }` and `{ success: true, result }`
|
|
74
|
+
* envelopes validated by the exported API schemas.
|
|
75
|
+
*
|
|
76
|
+
* @returns Hono middleware that adds the API response helper contract.
|
|
77
|
+
*/
|
|
78
|
+
export const apiResponseType = (): MiddlewareHandler<ApiResponseEnv> => {
|
|
79
|
+
return createMiddleware(async (c, next) => {
|
|
80
|
+
assertResponseTypeNotInitialized(c.var.response);
|
|
81
|
+
|
|
82
|
+
c.set('response', {
|
|
83
|
+
error: <T, U extends ErrorStatusCode>(status: U, error?: T) => {
|
|
84
|
+
return apiError.parse({
|
|
85
|
+
error: error === undefined || error === '' ? statusText[status] : error,
|
|
86
|
+
success: false,
|
|
87
|
+
}) as TypedApiError<T>;
|
|
88
|
+
},
|
|
89
|
+
result: <T, U extends ResultStatusCode>(status: U, result?: T) => {
|
|
90
|
+
return apiResult.parse({
|
|
91
|
+
result: result === undefined || result === '' ? statusText[status] : result,
|
|
92
|
+
success: true,
|
|
93
|
+
}) as TypedApiResult<T>;
|
|
94
|
+
},
|
|
95
|
+
type: 'api',
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
await next();
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
interface TypedRpcError<T = unknown> extends RpcError {
|
|
103
|
+
error?: T;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
interface TypedRpcResult<T = unknown> extends RpcResult {
|
|
107
|
+
result?: T;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Hono environment contract added by `rpcResponseType`.
|
|
112
|
+
*/
|
|
113
|
+
export interface RpcResponseEnv extends Env {
|
|
114
|
+
Variables: {
|
|
115
|
+
/**
|
|
116
|
+
* Helpers for building JSON-RPC 2.0 response envelopes.
|
|
117
|
+
*/
|
|
118
|
+
response: {
|
|
119
|
+
/**
|
|
120
|
+
* Builds a JSON-RPC error response envelope for the provided request id.
|
|
121
|
+
*/
|
|
122
|
+
error: <T>(error?: T, id?: JsonRpc['id']) => TypedRpcError<T>;
|
|
123
|
+
/**
|
|
124
|
+
* Builds a JSON-RPC result response envelope for the provided request id.
|
|
125
|
+
*/
|
|
126
|
+
result: <T>(result?: T, id?: JsonRpc['id']) => TypedRpcResult<T>;
|
|
127
|
+
/**
|
|
128
|
+
* Discriminator used by shared error handlers and response guards.
|
|
129
|
+
*/
|
|
130
|
+
type: 'rpc';
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Installs `c.var.response` helpers for JSON-RPC 2.0 responses.
|
|
137
|
+
*
|
|
138
|
+
* The helpers produce `error` and `result` envelopes validated by the exported
|
|
139
|
+
* JSON-RPC schemas and default missing ids to the schema default.
|
|
140
|
+
*
|
|
141
|
+
* @returns Hono middleware that adds the RPC response helper contract.
|
|
142
|
+
*/
|
|
143
|
+
export const rpcResponseType = (): MiddlewareHandler<RpcResponseEnv> => {
|
|
144
|
+
return createMiddleware(async (c, next) => {
|
|
145
|
+
assertResponseTypeNotInitialized(c.var.response);
|
|
146
|
+
|
|
147
|
+
c.set('response', {
|
|
148
|
+
error: <T>(error?: T, id?: JsonRpc['id']) => {
|
|
149
|
+
return rpcError.parse({ error, id }) as TypedRpcError<T>;
|
|
150
|
+
},
|
|
151
|
+
result: <T>(result?: T, id?: JsonRpc['id']) => {
|
|
152
|
+
return rpcResult.parse({ id, result }) as TypedRpcResult<T>;
|
|
153
|
+
},
|
|
154
|
+
type: 'rpc',
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
await next();
|
|
158
|
+
});
|
|
159
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-License-Identifier: MIT
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createMiddleware } from 'hono/factory';
|
|
6
|
+
import type { Env, MiddlewareHandler } from 'hono/types';
|
|
7
|
+
import { serialize } from 'superjson';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Serializes every `c.json` response body through SuperJSON before Hono writes
|
|
11
|
+
* the response.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export const superjson = (): MiddlewareHandler<Env> => {
|
|
16
|
+
return createMiddleware(async (c, next) => {
|
|
17
|
+
const json = c.json.bind(c);
|
|
18
|
+
|
|
19
|
+
c.json = (...[object, ...args]: Parameters<typeof json>) => {
|
|
20
|
+
return json(serialize(object).json, ...args);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
await next();
|
|
24
|
+
});
|
|
25
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-License-Identifier: MIT
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Zod schema for the standard API error envelope.
|
|
9
|
+
*/
|
|
10
|
+
export const apiError = z.object({
|
|
11
|
+
error: z.unknown().optional(),
|
|
12
|
+
success: z.literal(false),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* TypeScript type inferred from `apiError`.
|
|
17
|
+
*/
|
|
18
|
+
export type ApiError = z.infer<typeof apiError>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Zod schema for the standard API success envelope.
|
|
22
|
+
*/
|
|
23
|
+
export const apiResult = z.object({
|
|
24
|
+
result: z.unknown().optional(),
|
|
25
|
+
success: z.literal(true),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* TypeScript type inferred from `apiResult`.
|
|
30
|
+
*/
|
|
31
|
+
export type ApiResult = z.infer<typeof apiResult>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Zod union schema for either standard API response envelope.
|
|
35
|
+
*/
|
|
36
|
+
export const apiResponse = z.union([apiError, apiResult]);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* TypeScript type inferred from `apiResponse`.
|
|
40
|
+
*/
|
|
41
|
+
export type ApiResponse = z.infer<typeof apiResponse>;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-License-Identifier: MIT
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ContentlessStatusCode } from 'hono/utils/http-status';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Zod schema for informational HTTP status codes.
|
|
10
|
+
*/
|
|
11
|
+
export const infoStatusCode = z.union([z.literal(100), z.literal(101), z.literal(102), z.literal(103)]);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* TypeScript union of informational HTTP status codes.
|
|
15
|
+
*/
|
|
16
|
+
export type InfoStatusCode = z.infer<typeof infoStatusCode>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Zod schema for successful HTTP status codes.
|
|
20
|
+
*/
|
|
21
|
+
export const successStatusCode = z.union([
|
|
22
|
+
z.literal(200),
|
|
23
|
+
z.literal(201),
|
|
24
|
+
z.literal(202),
|
|
25
|
+
z.literal(203),
|
|
26
|
+
z.literal(204),
|
|
27
|
+
z.literal(205),
|
|
28
|
+
z.literal(206),
|
|
29
|
+
z.literal(207),
|
|
30
|
+
z.literal(208),
|
|
31
|
+
z.literal(226),
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* TypeScript union of successful HTTP status codes.
|
|
36
|
+
*/
|
|
37
|
+
export type SuccessStatusCode = z.infer<typeof successStatusCode>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Zod schema for redirection HTTP status codes.
|
|
41
|
+
*/
|
|
42
|
+
export const redirectStatusCode = z.union([
|
|
43
|
+
z.literal(300),
|
|
44
|
+
z.literal(301),
|
|
45
|
+
z.literal(302),
|
|
46
|
+
z.literal(303),
|
|
47
|
+
z.literal(304),
|
|
48
|
+
z.literal(305),
|
|
49
|
+
z.literal(306),
|
|
50
|
+
z.literal(307),
|
|
51
|
+
z.literal(308),
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* TypeScript union of redirection HTTP status codes.
|
|
56
|
+
*/
|
|
57
|
+
export type RedirectStatusCode = z.infer<typeof redirectStatusCode>;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Zod schema for client error HTTP status codes.
|
|
61
|
+
*/
|
|
62
|
+
export const clientErrorStatusCode = z.union([
|
|
63
|
+
z.literal(400),
|
|
64
|
+
z.literal(401),
|
|
65
|
+
z.literal(402),
|
|
66
|
+
z.literal(403),
|
|
67
|
+
z.literal(404),
|
|
68
|
+
z.literal(405),
|
|
69
|
+
z.literal(406),
|
|
70
|
+
z.literal(407),
|
|
71
|
+
z.literal(408),
|
|
72
|
+
z.literal(409),
|
|
73
|
+
z.literal(410),
|
|
74
|
+
z.literal(411),
|
|
75
|
+
z.literal(412),
|
|
76
|
+
z.literal(413),
|
|
77
|
+
z.literal(414),
|
|
78
|
+
z.literal(415),
|
|
79
|
+
z.literal(416),
|
|
80
|
+
z.literal(417),
|
|
81
|
+
z.literal(418),
|
|
82
|
+
z.literal(421),
|
|
83
|
+
z.literal(422),
|
|
84
|
+
z.literal(423),
|
|
85
|
+
z.literal(424),
|
|
86
|
+
z.literal(425),
|
|
87
|
+
z.literal(426),
|
|
88
|
+
z.literal(428),
|
|
89
|
+
z.literal(429),
|
|
90
|
+
z.literal(431),
|
|
91
|
+
z.literal(451),
|
|
92
|
+
]);
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* TypeScript union of client error HTTP status codes.
|
|
96
|
+
*/
|
|
97
|
+
export type ClientErrorStatusCode = z.infer<typeof clientErrorStatusCode>;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Zod schema for server error HTTP status codes.
|
|
101
|
+
*/
|
|
102
|
+
export const serverErrorStatusCode = z.union([
|
|
103
|
+
z.literal(500),
|
|
104
|
+
z.literal(501),
|
|
105
|
+
z.literal(502),
|
|
106
|
+
z.literal(503),
|
|
107
|
+
z.literal(504),
|
|
108
|
+
z.literal(505),
|
|
109
|
+
z.literal(506),
|
|
110
|
+
z.literal(507),
|
|
111
|
+
z.literal(508),
|
|
112
|
+
z.literal(510),
|
|
113
|
+
z.literal(511),
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* TypeScript union of server error HTTP status codes.
|
|
118
|
+
*/
|
|
119
|
+
export type ServerErrorStatusCode = z.infer<typeof serverErrorStatusCode>;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Zod schema for every HTTP status code supported by this package.
|
|
123
|
+
*/
|
|
124
|
+
export const statusCode = z.union([
|
|
125
|
+
infoStatusCode,
|
|
126
|
+
successStatusCode,
|
|
127
|
+
redirectStatusCode,
|
|
128
|
+
clientErrorStatusCode,
|
|
129
|
+
serverErrorStatusCode,
|
|
130
|
+
]);
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* TypeScript union of every HTTP status code supported by this package.
|
|
134
|
+
*/
|
|
135
|
+
export type StatusCode = z.infer<typeof statusCode>;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* HTTP status codes that can carry a response body.
|
|
139
|
+
*/
|
|
140
|
+
export type ContentfulStatusCode = Exclude<StatusCode, ContentlessStatusCode>;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Default reason phrases keyed by supported HTTP status code.
|
|
144
|
+
*/
|
|
145
|
+
export const statusText: Record<StatusCode, string> = {
|
|
146
|
+
100: 'Continue',
|
|
147
|
+
101: 'Switching Protocols',
|
|
148
|
+
102: 'Processing',
|
|
149
|
+
103: 'Early Hints',
|
|
150
|
+
200: 'OK',
|
|
151
|
+
201: 'Created',
|
|
152
|
+
202: 'Accepted',
|
|
153
|
+
203: 'Non-Authoritative Information',
|
|
154
|
+
204: 'No Content',
|
|
155
|
+
205: 'Reset Content',
|
|
156
|
+
206: 'Partial Content',
|
|
157
|
+
207: 'Multi-Status',
|
|
158
|
+
208: 'Already Reported',
|
|
159
|
+
226: 'IM Used',
|
|
160
|
+
300: 'Multiple Choices',
|
|
161
|
+
301: 'Moved Permanently',
|
|
162
|
+
302: 'Found',
|
|
163
|
+
303: 'See Other',
|
|
164
|
+
304: 'Not Modified',
|
|
165
|
+
305: 'Use Proxy',
|
|
166
|
+
306: 'Unused',
|
|
167
|
+
307: 'Temporary Redirect',
|
|
168
|
+
308: 'Permanent Redirect',
|
|
169
|
+
400: 'Bad Request',
|
|
170
|
+
401: 'Unauthorized',
|
|
171
|
+
402: 'Payment Required',
|
|
172
|
+
403: 'Forbidden',
|
|
173
|
+
404: 'Not Found',
|
|
174
|
+
405: 'Method Not Allowed',
|
|
175
|
+
406: 'Not Acceptable',
|
|
176
|
+
407: 'Proxy Authentication Required',
|
|
177
|
+
408: 'Request Timeout',
|
|
178
|
+
409: 'Conflict',
|
|
179
|
+
410: 'Gone',
|
|
180
|
+
411: 'Length Required',
|
|
181
|
+
412: 'Precondition Failed',
|
|
182
|
+
413: 'Payload Too Large',
|
|
183
|
+
414: 'URI Too Long',
|
|
184
|
+
415: 'Unsupported Media Type',
|
|
185
|
+
416: 'Range Not Satisfiable',
|
|
186
|
+
417: 'Expectation Failed',
|
|
187
|
+
418: "I'm a Teapot",
|
|
188
|
+
421: 'Misdirected Request',
|
|
189
|
+
422: 'Unprocessable Content',
|
|
190
|
+
423: 'Locked',
|
|
191
|
+
424: 'Failed Dependency',
|
|
192
|
+
425: 'Too Early',
|
|
193
|
+
426: 'Upgrade Required',
|
|
194
|
+
428: 'Precondition Required',
|
|
195
|
+
429: 'Too Many Requests',
|
|
196
|
+
431: 'Request Header Fields Too Large',
|
|
197
|
+
451: 'Unavailable For Legal Reasons',
|
|
198
|
+
500: 'Internal Server Error',
|
|
199
|
+
501: 'Not Implemented',
|
|
200
|
+
502: 'Bad Gateway',
|
|
201
|
+
503: 'Service Unavailable',
|
|
202
|
+
504: 'Gateway Timeout',
|
|
203
|
+
505: 'HTTP Version Not Supported',
|
|
204
|
+
506: 'Variant Also Negotiates',
|
|
205
|
+
507: 'Insufficient Storage',
|
|
206
|
+
508: 'Loop Detected',
|
|
207
|
+
510: 'Not Extended',
|
|
208
|
+
511: 'Network Authentication Required',
|
|
209
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-License-Identifier: MIT
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Zod schema for JSON-RPC 2.0 fields shared by requests and responses.
|
|
9
|
+
*/
|
|
10
|
+
export const jsonRpc = z.object({
|
|
11
|
+
id: z.union([z.number(), z.string()]).default(1),
|
|
12
|
+
jsonrpc: z.literal('2.0').default('2.0'),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* TypeScript type inferred from `jsonRpc`.
|
|
17
|
+
*/
|
|
18
|
+
export type JsonRpc = z.infer<typeof jsonRpc>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Zod schema for a single JSON-RPC 2.0 request.
|
|
22
|
+
*/
|
|
23
|
+
export const rpcRequest = z.object({
|
|
24
|
+
...jsonRpc.shape,
|
|
25
|
+
method: z.string(),
|
|
26
|
+
params: z.unknown().optional(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* TypeScript type inferred from `rpcRequest`.
|
|
31
|
+
*/
|
|
32
|
+
export type RpcRequest = z.infer<typeof rpcRequest>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Zod schema for a JSON-RPC 2.0 request batch.
|
|
36
|
+
*/
|
|
37
|
+
export const rpcRequests = z.array(rpcRequest);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* TypeScript type inferred from `rpcRequests`.
|
|
41
|
+
*/
|
|
42
|
+
export type RpcRequests = z.infer<typeof rpcRequests>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Zod schema for a JSON-RPC 2.0 error response.
|
|
46
|
+
*/
|
|
47
|
+
export const rpcError = z.object({
|
|
48
|
+
...jsonRpc.shape,
|
|
49
|
+
error: z.unknown().optional(),
|
|
50
|
+
}).strict();
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* TypeScript type inferred from `rpcError`.
|
|
54
|
+
*/
|
|
55
|
+
export type RpcError = z.infer<typeof rpcError>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Zod schema for a JSON-RPC 2.0 result response.
|
|
59
|
+
*/
|
|
60
|
+
export const rpcResult = z.object({
|
|
61
|
+
...jsonRpc.shape,
|
|
62
|
+
result: z.unknown().optional(),
|
|
63
|
+
}).strict();
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* TypeScript type inferred from `rpcResult`.
|
|
67
|
+
*/
|
|
68
|
+
export type RpcResult = z.infer<typeof rpcResult>;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Zod union schema for either JSON-RPC 2.0 response envelope.
|
|
72
|
+
*/
|
|
73
|
+
export const rpcResponse = z.union([rpcError, rpcResult]);
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* TypeScript type inferred from `rpcResponse`.
|
|
77
|
+
*/
|
|
78
|
+
export type RpcResponse = z.infer<typeof rpcResponse>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Zod schema for a JSON-RPC 2.0 response batch.
|
|
82
|
+
*/
|
|
83
|
+
export const rpcResponses = z.array(rpcResponse);
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* TypeScript type inferred from `rpcResponses`.
|
|
87
|
+
*/
|
|
88
|
+
export type RpcResponses = z.infer<typeof rpcResponses>;
|