@kiyasov/platform-hono 1.0.0 → 1.0.2
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/.yarn/install-state.gz +0 -0
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/src/adapters/hono-adapter.d.ts +58 -0
- package/dist/cjs/src/adapters/hono-adapter.js +206 -0
- package/dist/cjs/src/adapters/hono-adapter.js.map +1 -0
- package/dist/cjs/src/adapters/index.d.ts +1 -0
- package/dist/cjs/src/adapters/index.js +18 -0
- package/dist/cjs/src/adapters/index.js.map +1 -0
- package/dist/cjs/src/drivers/graphql.driver.d.ts +16 -0
- package/dist/cjs/src/drivers/graphql.driver.js +99 -0
- package/dist/cjs/src/drivers/graphql.driver.js.map +1 -0
- package/dist/cjs/src/drivers/index.d.ts +1 -0
- package/dist/cjs/src/drivers/index.js +18 -0
- package/dist/cjs/src/drivers/index.js.map +1 -0
- package/dist/cjs/src/interfaces/index.d.ts +1 -0
- package/dist/cjs/src/interfaces/index.js +18 -0
- package/dist/cjs/src/interfaces/index.js.map +1 -0
- package/dist/cjs/src/interfaces/nest-hono-application.interface.d.ts +18 -0
- package/dist/cjs/src/interfaces/nest-hono-application.interface.js +3 -0
- package/dist/cjs/src/interfaces/nest-hono-application.interface.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/src/adapters/hono-adapter.d.ts +58 -0
- package/dist/esm/src/adapters/hono-adapter.js +202 -0
- package/dist/esm/src/adapters/hono-adapter.js.map +1 -0
- package/dist/esm/src/adapters/index.d.ts +1 -0
- package/dist/esm/src/adapters/index.js +2 -0
- package/dist/esm/src/adapters/index.js.map +1 -0
- package/dist/esm/src/drivers/graphql.driver.d.ts +16 -0
- package/dist/esm/src/drivers/graphql.driver.js +95 -0
- package/dist/esm/src/drivers/graphql.driver.js.map +1 -0
- package/dist/esm/src/drivers/index.d.ts +1 -0
- package/dist/esm/src/drivers/index.js +2 -0
- package/dist/esm/src/drivers/index.js.map +1 -0
- package/dist/esm/src/interfaces/index.d.ts +1 -0
- package/dist/esm/src/interfaces/index.js +2 -0
- package/dist/esm/src/interfaces/index.js.map +1 -0
- package/dist/esm/src/interfaces/nest-hono-application.interface.d.ts +18 -0
- package/dist/esm/src/interfaces/nest-hono-application.interface.js +2 -0
- package/dist/esm/src/interfaces/nest-hono-application.interface.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/index.ts +1 -0
- package/package.json +13 -3
- package/src/adapters/hono-adapter.ts +293 -0
- package/src/adapters/index.ts +1 -0
- package/src/drivers/graphql.driver.ts +126 -0
- package/src/drivers/index.ts +1 -0
- package/src/interfaces/index.ts +1 -0
- package/src/interfaces/nest-hono-application.interface.ts +72 -0
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kiyasov/platform-hono",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Nest adapter for Hono",
|
|
5
5
|
"author": "Islam Kiiasov",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"main": "dist/cjs/index.js",
|
|
8
|
-
"module": "dist/esm/index.js",
|
|
7
|
+
"main": "./dist/cjs/index.js",
|
|
8
|
+
"module": "./dist/esm/index.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"require": "./dist/cjs/index.js",
|
|
12
|
+
"import": "./dist/esm/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
9
15
|
"publishConfig": {
|
|
10
16
|
"access": "public"
|
|
11
17
|
},
|
|
12
18
|
"dependencies": {
|
|
19
|
+
"@hono/graphql-server": "^0.4.3",
|
|
13
20
|
"@hono/node-server": "^1.11.2",
|
|
14
21
|
"hono": "^4.4.3"
|
|
15
22
|
},
|
|
16
23
|
"devDependencies": {
|
|
24
|
+
"@apollo/server": "^4.10.4",
|
|
25
|
+
"@nestjs/apollo": "^12.1.0",
|
|
17
26
|
"@nestjs/cli": "^10.3.2",
|
|
18
27
|
"@nestjs/common": "^10.3.8",
|
|
19
28
|
"@nestjs/core": "^10.3.8",
|
|
29
|
+
"@nestjs/graphql": "^12.1.1",
|
|
20
30
|
"@swc/cli": "^0.3.12",
|
|
21
31
|
"@swc/core": "^1.5.24",
|
|
22
32
|
"reflect-metadata": "^0.2.2",
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { Server } from "node:net";
|
|
2
|
+
import { HttpBindings, createAdaptorServer } from "@hono/node-server";
|
|
3
|
+
import { RESPONSE_ALREADY_SENT } from "@hono/node-server/utils/response";
|
|
4
|
+
import { RequestMethod } from "@nestjs/common";
|
|
5
|
+
import { HttpStatus, Logger } from "@nestjs/common";
|
|
6
|
+
import { bodyLimit } from "hono/body-limit";
|
|
7
|
+
import {
|
|
8
|
+
ErrorHandler,
|
|
9
|
+
NestApplicationOptions,
|
|
10
|
+
RequestHandler,
|
|
11
|
+
} from "@nestjs/common/interfaces";
|
|
12
|
+
import {
|
|
13
|
+
ServeStaticOptions,
|
|
14
|
+
serveStatic,
|
|
15
|
+
} from "@hono/node-server/serve-static";
|
|
16
|
+
import { AbstractHttpAdapter } from "@nestjs/core/adapters/http-adapter";
|
|
17
|
+
import { Context, HonoRequest, Next } from "hono";
|
|
18
|
+
import { Hono } from "hono";
|
|
19
|
+
import { cors } from "hono/cors";
|
|
20
|
+
import { RedirectStatusCode, StatusCode } from "hono/utils/http-status";
|
|
21
|
+
import * as http from "http";
|
|
22
|
+
import { Http2SecureServer, Http2Server } from "http2";
|
|
23
|
+
import * as https from "https";
|
|
24
|
+
import { TypeBodyParser } from "../interfaces";
|
|
25
|
+
|
|
26
|
+
type HonoHandler = RequestHandler<HonoRequest, Context>;
|
|
27
|
+
|
|
28
|
+
type ServerType = Server | Http2Server | Http2SecureServer;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Adapter for using Hono with NestJS.
|
|
32
|
+
*/
|
|
33
|
+
export class HonoAdapter extends AbstractHttpAdapter<
|
|
34
|
+
ServerType,
|
|
35
|
+
HonoRequest,
|
|
36
|
+
Context
|
|
37
|
+
> {
|
|
38
|
+
protected readonly instance: Hono<{ Bindings: HttpBindings }>;
|
|
39
|
+
|
|
40
|
+
constructor() {
|
|
41
|
+
super(new Hono());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private getRouteAndHandler(
|
|
45
|
+
pathOrHandler: string | HonoHandler,
|
|
46
|
+
handler?: HonoHandler
|
|
47
|
+
): [string, HonoHandler] {
|
|
48
|
+
let path = typeof pathOrHandler === "function" ? "" : pathOrHandler;
|
|
49
|
+
handler = typeof pathOrHandler === "function" ? pathOrHandler : handler;
|
|
50
|
+
return [path, handler];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private createRouteHandler(routeHandler: HonoHandler) {
|
|
54
|
+
return async (ctx: Context, next: Next) => {
|
|
55
|
+
await routeHandler(ctx.req, ctx, next);
|
|
56
|
+
return this.send(ctx);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private send(ctx: Context) {
|
|
61
|
+
const body = ctx.get("body");
|
|
62
|
+
return typeof body === "string" ? ctx.text(body) : ctx.json(body);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public get(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
|
|
66
|
+
const [routePath, routeHandler] = this.getRouteAndHandler(
|
|
67
|
+
pathOrHandler,
|
|
68
|
+
handler
|
|
69
|
+
);
|
|
70
|
+
this.instance.get(routePath, this.createRouteHandler(routeHandler));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public post(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
|
|
74
|
+
const [routePath, routeHandler] = this.getRouteAndHandler(
|
|
75
|
+
pathOrHandler,
|
|
76
|
+
handler
|
|
77
|
+
);
|
|
78
|
+
this.instance.post(routePath, this.createRouteHandler(routeHandler));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public put(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
|
|
82
|
+
const [routePath, routeHandler] = this.getRouteAndHandler(
|
|
83
|
+
pathOrHandler,
|
|
84
|
+
handler
|
|
85
|
+
);
|
|
86
|
+
this.instance.put(routePath, this.createRouteHandler(routeHandler));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public delete(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
|
|
90
|
+
const [routePath, routeHandler] = this.getRouteAndHandler(
|
|
91
|
+
pathOrHandler,
|
|
92
|
+
handler
|
|
93
|
+
);
|
|
94
|
+
this.instance.delete(routePath, this.createRouteHandler(routeHandler));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public use(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
|
|
98
|
+
const [routePath, routeHandler] = this.getRouteAndHandler(
|
|
99
|
+
pathOrHandler,
|
|
100
|
+
handler
|
|
101
|
+
);
|
|
102
|
+
this.instance.use(routePath, this.createRouteHandler(routeHandler));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public patch(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
|
|
106
|
+
const [routePath, routeHandler] = this.getRouteAndHandler(
|
|
107
|
+
pathOrHandler,
|
|
108
|
+
handler
|
|
109
|
+
);
|
|
110
|
+
this.instance.patch(routePath, this.createRouteHandler(routeHandler));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public options(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
|
|
114
|
+
const [routePath, routeHandler] = this.getRouteAndHandler(
|
|
115
|
+
pathOrHandler,
|
|
116
|
+
handler
|
|
117
|
+
);
|
|
118
|
+
this.instance.options(routePath, this.createRouteHandler(routeHandler));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public async reply(ctx: Context, body: any, statusCode?: StatusCode) {
|
|
122
|
+
if (statusCode) ctx.status(statusCode);
|
|
123
|
+
const responseContentType = this.getHeader(ctx, "Content-Type");
|
|
124
|
+
if (
|
|
125
|
+
!responseContentType?.startsWith("application/json") &&
|
|
126
|
+
body?.statusCode >= HttpStatus.BAD_REQUEST
|
|
127
|
+
) {
|
|
128
|
+
Logger.warn(
|
|
129
|
+
"Content-Type doesn't match Reply body, you might need a custom ExceptionFilter for non-JSON responses"
|
|
130
|
+
);
|
|
131
|
+
this.setHeader(ctx, "Content-Type", "application/json");
|
|
132
|
+
}
|
|
133
|
+
ctx.set("body", body);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public status(ctx: Context, statusCode: StatusCode) {
|
|
137
|
+
ctx.status(statusCode);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
public async end() {
|
|
141
|
+
return RESPONSE_ALREADY_SENT;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
public render(response: any, view: string, options: any) {
|
|
145
|
+
throw new Error("Method not implemented.");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
public redirect(ctx: Context, statusCode: RedirectStatusCode, url: string) {
|
|
149
|
+
ctx.redirect(url, statusCode);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public setErrorHandler(handler: ErrorHandler) {
|
|
153
|
+
this.instance.onError(async (err: Error, ctx: Context) => {
|
|
154
|
+
await handler(err, ctx.req, ctx);
|
|
155
|
+
return this.send(ctx);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public setNotFoundHandler(handler: RequestHandler) {
|
|
160
|
+
this.instance.notFound(async (ctx: Context) => {
|
|
161
|
+
await handler(ctx.req, ctx);
|
|
162
|
+
return this.send(ctx);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
public useStaticAssets(path: string, options: ServeStaticOptions) {
|
|
167
|
+
Logger.log("Registering static assets middleware");
|
|
168
|
+
this.instance.use(path, serveStatic(options));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public setViewEngine(options: any | string) {
|
|
172
|
+
throw new Error("Method not implemented.");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
public isHeadersSent(ctx: Context): boolean {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
public getHeader?(ctx: Context, name: string) {
|
|
180
|
+
return ctx.req.header(name);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
public setHeader(ctx: Context, name: string, value: string) {
|
|
184
|
+
ctx.header(name, value);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
public appendHeader?(ctx: Context, name: string, value: string) {
|
|
188
|
+
ctx.res.headers.append(name, value);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
public getRequestHostname(ctx: Context): string {
|
|
192
|
+
return ctx.req.header().host;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
public getRequestMethod(request: HonoRequest): string {
|
|
196
|
+
return request.method;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
public getRequestUrl(request: HonoRequest): string {
|
|
200
|
+
return request.url;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
public enableCors(options: any) {
|
|
204
|
+
this.instance.use(cors(options));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
public useBodyParser(type: TypeBodyParser, bodyLimit: number = 1e6) {
|
|
208
|
+
Logger.log("Registering body parser middleware");
|
|
209
|
+
this.instance.use(this.bodyLimit(bodyLimit), async (ctx, next) => {
|
|
210
|
+
const contentType = ctx.req.header("content-type");
|
|
211
|
+
switch (type) {
|
|
212
|
+
case "application/json":
|
|
213
|
+
if (contentType === "application/json")
|
|
214
|
+
(ctx.req as any).body = await ctx.req.json();
|
|
215
|
+
break;
|
|
216
|
+
case "text/plain":
|
|
217
|
+
if (contentType === "text/plain") {
|
|
218
|
+
(ctx.req as any).rawBody = Buffer.from(await ctx.req.text());
|
|
219
|
+
(ctx.req as any).body = await ctx.req.json();
|
|
220
|
+
}
|
|
221
|
+
break;
|
|
222
|
+
default:
|
|
223
|
+
(ctx.req as any).body = await ctx.req.parseBody({ all: true });
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
await next();
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
public close(): Promise<void> {
|
|
231
|
+
return new Promise((resolve) => this.httpServer.close(() => resolve()));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
public initHttpServer(options: NestApplicationOptions) {
|
|
235
|
+
const isHttpsEnabled = options?.httpsOptions;
|
|
236
|
+
const createServer = isHttpsEnabled
|
|
237
|
+
? https.createServer
|
|
238
|
+
: http.createServer;
|
|
239
|
+
this.httpServer = createAdaptorServer({
|
|
240
|
+
fetch: this.instance.fetch,
|
|
241
|
+
createServer,
|
|
242
|
+
overrideGlobalObjects: false,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
public getType(): string {
|
|
247
|
+
return "hono";
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
public registerParserMiddleware(prefix?: string, rawBody?: boolean) {
|
|
251
|
+
Logger.log("Registering parser middleware");
|
|
252
|
+
this.useBodyParser("application/x-www-form-urlencoded");
|
|
253
|
+
this.useBodyParser("application/json");
|
|
254
|
+
this.useBodyParser("text/plain");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
public async createMiddlewareFactory(requestMethod: RequestMethod) {
|
|
258
|
+
return (path: string, callback: Function) => {
|
|
259
|
+
const routeMethodsMap = {
|
|
260
|
+
[RequestMethod.ALL]: this.instance.all,
|
|
261
|
+
[RequestMethod.DELETE]: this.instance.delete,
|
|
262
|
+
[RequestMethod.GET]: this.instance.get,
|
|
263
|
+
[RequestMethod.OPTIONS]: this.instance.options,
|
|
264
|
+
[RequestMethod.PATCH]: this.instance.patch,
|
|
265
|
+
[RequestMethod.POST]: this.instance.post,
|
|
266
|
+
[RequestMethod.PUT]: this.instance.put,
|
|
267
|
+
};
|
|
268
|
+
const routeMethod = (
|
|
269
|
+
routeMethodsMap[requestMethod] || this.instance.get
|
|
270
|
+
).bind(this.instance);
|
|
271
|
+
routeMethod(path, async (ctx: Context, next: Function) => {
|
|
272
|
+
await callback(ctx.req, ctx, next);
|
|
273
|
+
});
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
public applyVersionFilter(): () => () => any {
|
|
278
|
+
throw new Error("Versioning not yet supported in Hono");
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
public listen(port: string | number, ...args: any[]): ServerType {
|
|
282
|
+
return this.httpServer.listen(port, ...args);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
public bodyLimit(maxSize: number) {
|
|
286
|
+
return bodyLimit({
|
|
287
|
+
maxSize,
|
|
288
|
+
onError: () => {
|
|
289
|
+
throw new Error("Body too large");
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./hono-adapter";
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { ApolloServer, BaseContext, HeaderMap } from '@apollo/server';
|
|
2
|
+
import { AbstractGraphQLDriver } from '@nestjs/graphql';
|
|
3
|
+
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
|
|
4
|
+
import { ApolloDriverConfig } from '@nestjs/apollo';
|
|
5
|
+
import { Context, HonoRequest } from 'hono';
|
|
6
|
+
import { StatusCode } from 'hono/utils/http-status';
|
|
7
|
+
import { Logger } from '@nestjs/common';
|
|
8
|
+
|
|
9
|
+
export class HonoGraphQLDriver<
|
|
10
|
+
T extends Record<string, any> = ApolloDriverConfig,
|
|
11
|
+
> extends AbstractGraphQLDriver {
|
|
12
|
+
protected apolloServer: ApolloServer<BaseContext>;
|
|
13
|
+
|
|
14
|
+
get instance(): ApolloServer<BaseContext> {
|
|
15
|
+
return this.apolloServer;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async start(options: T): Promise<void> {
|
|
19
|
+
const { httpAdapter } = this.httpAdapterHost;
|
|
20
|
+
const platformName = httpAdapter.getType();
|
|
21
|
+
|
|
22
|
+
if (platformName !== 'hono') {
|
|
23
|
+
throw new Error('This driver is only compatible with the Hono platform');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return this.registerHono(options);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
protected async registerHono(
|
|
30
|
+
options: T,
|
|
31
|
+
{ preStartHook }: { preStartHook?: () => void } = {},
|
|
32
|
+
) {
|
|
33
|
+
const { path, typeDefs, resolvers, schema } = options;
|
|
34
|
+
const { httpAdapter } = this.httpAdapterHost;
|
|
35
|
+
const app = httpAdapter.getInstance();
|
|
36
|
+
const drainHttpServerPlugin = ApolloServerPluginDrainHttpServer({
|
|
37
|
+
httpServer: httpAdapter.getHttpServer(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
preStartHook?.();
|
|
41
|
+
|
|
42
|
+
const server = new ApolloServer({
|
|
43
|
+
typeDefs,
|
|
44
|
+
resolvers,
|
|
45
|
+
schema,
|
|
46
|
+
...options,
|
|
47
|
+
plugins: (options.plugins || []).concat(drainHttpServerPlugin),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
await server.start();
|
|
51
|
+
|
|
52
|
+
app.use(path, async (ctx: Context) => {
|
|
53
|
+
const bodyData = await this.parseBody(ctx.req);
|
|
54
|
+
|
|
55
|
+
const httpGraphQLResponse = await server.executeHTTPGraphQLRequest({
|
|
56
|
+
httpGraphQLRequest: {
|
|
57
|
+
body: bodyData,
|
|
58
|
+
method: ctx.req.method,
|
|
59
|
+
headers: this.httpHeadersToMap(ctx.req.raw.headers),
|
|
60
|
+
search: new URL(ctx.req.url).search,
|
|
61
|
+
},
|
|
62
|
+
context: options?.context ?? (() => Promise.resolve({} as BaseContext)),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const { headers, body, status } = httpGraphQLResponse;
|
|
66
|
+
|
|
67
|
+
for (const [headerKey, headerValue] of headers) {
|
|
68
|
+
ctx.header(headerKey, headerValue);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
ctx.status(status === undefined ? 200 : (status as StatusCode));
|
|
72
|
+
|
|
73
|
+
if (body.kind === 'complete') {
|
|
74
|
+
return ctx.body(body.string);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const readableStream = new ReadableStream({
|
|
78
|
+
async start(controller) {
|
|
79
|
+
for await (const chunk of body.asyncIterator) {
|
|
80
|
+
controller.enqueue(new TextEncoder().encode(chunk));
|
|
81
|
+
}
|
|
82
|
+
controller.close();
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return new Response(readableStream, {
|
|
87
|
+
headers: { 'Content-Type': 'application/octet-stream' },
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
this.apolloServer = server;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public stop() {
|
|
95
|
+
return this.apolloServer?.stop();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private httpHeadersToMap(headers: Headers) {
|
|
99
|
+
const map = new HeaderMap();
|
|
100
|
+
headers.forEach((value, key) => map.set(key, value));
|
|
101
|
+
return map;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private async parseBody(req: HonoRequest): Promise<Record<string, unknown>> {
|
|
105
|
+
const contentType = req.header('content-type');
|
|
106
|
+
if (contentType === 'application/graphql')
|
|
107
|
+
return { query: await req.text() };
|
|
108
|
+
if (contentType === 'application/json')
|
|
109
|
+
return req.json().catch(this.logError);
|
|
110
|
+
if (contentType === 'application/x-www-form-urlencoded')
|
|
111
|
+
return this.parseFormURL(req);
|
|
112
|
+
return {};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private logError(e: unknown): void {
|
|
116
|
+
if (e instanceof Error) {
|
|
117
|
+
Logger.error(e.stack || e.message);
|
|
118
|
+
}
|
|
119
|
+
throw new Error(`POST body sent invalid JSON: ${e}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private async parseFormURL(req: HonoRequest) {
|
|
123
|
+
const searchParams = new URLSearchParams(await req.text());
|
|
124
|
+
return Object.fromEntries(searchParams.entries());
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./graphql.driver";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './nest-hono-application.interface';
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { ServeStaticOptions } from "@hono/node-server/serve-static";
|
|
2
|
+
import { HttpServer, INestApplication } from "@nestjs/common";
|
|
3
|
+
import { Context, Hono, MiddlewareHandler } from "hono";
|
|
4
|
+
|
|
5
|
+
export type TypeBodyParser =
|
|
6
|
+
| "application/json"
|
|
7
|
+
| "text/plain"
|
|
8
|
+
| "application/x-www-form-urlencoded";
|
|
9
|
+
|
|
10
|
+
interface HonoViewOptions {
|
|
11
|
+
engine: string;
|
|
12
|
+
templates: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @publicApi
|
|
17
|
+
*/
|
|
18
|
+
export interface NestHonoApplication<TServer extends Hono = Hono>
|
|
19
|
+
extends INestApplication<TServer> {
|
|
20
|
+
/**
|
|
21
|
+
* Returns the underlying HTTP adapter bounded to a Hono app.
|
|
22
|
+
*
|
|
23
|
+
* @returns {HttpServer}
|
|
24
|
+
*/
|
|
25
|
+
getHttpAdapter(): HttpServer<Context, MiddlewareHandler, Hono>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Register Hono body parsers on the fly.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* // enable the json parser with a parser limit of 50mb
|
|
32
|
+
* app.useBodyParser('application/json', 50 * 1024 * 1024);
|
|
33
|
+
*
|
|
34
|
+
* @returns {this}
|
|
35
|
+
*/
|
|
36
|
+
useBodyParser(type: TypeBodyParser, bodyLimit?: number): this;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Sets a base directory for public assets.
|
|
40
|
+
* Example `app.useStaticAssets('public', { root: '/' })`
|
|
41
|
+
* @returns {this}
|
|
42
|
+
*/
|
|
43
|
+
useStaticAssets(path: string, options: ServeStaticOptions): this;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Sets a view engine for templates (views), for example: `pug`, `handlebars`, or `ejs`.
|
|
47
|
+
*
|
|
48
|
+
* Don't pass in a string. The string type in the argument is for compatibility reason and will cause an exception.
|
|
49
|
+
* @returns {this}
|
|
50
|
+
*/
|
|
51
|
+
setViewEngine(options: HonoViewOptions | string): this;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Starts the application.
|
|
55
|
+
* @returns A Promise that, when resolved, is a reference to the underlying HttpServer.
|
|
56
|
+
*/
|
|
57
|
+
listen(
|
|
58
|
+
port: number | string,
|
|
59
|
+
callback?: (err: Error, address: string) => void
|
|
60
|
+
): Promise<TServer>;
|
|
61
|
+
listen(
|
|
62
|
+
port: number | string,
|
|
63
|
+
address: string,
|
|
64
|
+
callback?: (err: Error, address: string) => void
|
|
65
|
+
): Promise<TServer>;
|
|
66
|
+
listen(
|
|
67
|
+
port: number | string,
|
|
68
|
+
address: string,
|
|
69
|
+
backlog: number,
|
|
70
|
+
callback?: (err: Error, address: string) => void
|
|
71
|
+
): Promise<TServer>;
|
|
72
|
+
}
|