@kiyasov/platform-hono 1.5.1 → 1.5.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/cjs/src/adapters/hono-adapter.d.ts +4 -0
- package/dist/cjs/src/adapters/hono-adapter.js +88 -71
- package/dist/cjs/src/adapters/hono-adapter.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/adapters/hono-adapter.d.ts +4 -0
- package/dist/esm/src/adapters/hono-adapter.js +88 -71
- package/dist/esm/src/adapters/hono-adapter.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +25 -18
- package/src/adapters/hono-adapter.ts +117 -107
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kiyasov/platform-hono",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"description": "Nest adapter for Hono",
|
|
5
5
|
"author": "Islam Kiiasov",
|
|
6
6
|
"repository": {
|
|
@@ -28,38 +28,43 @@
|
|
|
28
28
|
"access": "public"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@apollo/server": "^4.12.
|
|
32
|
-
"@hono/node-server": "^1.
|
|
31
|
+
"@apollo/server": "^4.12.2",
|
|
32
|
+
"@hono/node-server": "^1.17.1",
|
|
33
33
|
"@nestjs/apollo": "^13.1.0",
|
|
34
34
|
"@nestjs/graphql": "^13.1.0",
|
|
35
|
-
"hono": "^4.7
|
|
35
|
+
"hono": "^4.8.7"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@eslint/js": "^9.
|
|
38
|
+
"@eslint/js": "^9.31.0",
|
|
39
39
|
"@nestjs/cli": "^11.0.7",
|
|
40
|
-
"@nestjs/common": "^11.1.
|
|
41
|
-
"@nestjs/core": "^11.1.
|
|
40
|
+
"@nestjs/common": "^11.1.3",
|
|
41
|
+
"@nestjs/core": "^11.1.3",
|
|
42
|
+
"@nestjs/swagger": "^11.2.0",
|
|
42
43
|
"@swc/cli": "^0.7.7",
|
|
43
|
-
"@swc/core": "^1.
|
|
44
|
+
"@swc/core": "^1.12.1",
|
|
44
45
|
"@types/autocannon": "^7.12.7",
|
|
45
|
-
"@types/bun": "^1.2.
|
|
46
|
+
"@types/bun": "^1.2.16",
|
|
46
47
|
"@types/busboy": "^1.5.4",
|
|
47
48
|
"autocannon": "^8.0.0",
|
|
48
|
-
"bun": "^1.2.
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"eslint
|
|
52
|
-
"eslint-
|
|
53
|
-
"
|
|
49
|
+
"bun": "^1.2.19",
|
|
50
|
+
"class-transformer": "^0.5.1",
|
|
51
|
+
"class-validator": "^0.14.2",
|
|
52
|
+
"eslint": "^9.31.0",
|
|
53
|
+
"eslint-config-prettier": "^10.1.8",
|
|
54
|
+
"eslint-plugin-perfectionist": "^4.15.0",
|
|
55
|
+
"eslint-plugin-prettier": "^5.5.3",
|
|
56
|
+
"globals": "^16.3.0",
|
|
54
57
|
"graphql": "^16.11.0",
|
|
55
58
|
"graphql-subscriptions": "^3.0.0",
|
|
56
|
-
"prettier": "^3.
|
|
59
|
+
"prettier": "^3.6.2",
|
|
57
60
|
"reflect-metadata": "^0.2.2",
|
|
58
61
|
"rimraf": "^6.0.1",
|
|
59
62
|
"rxjs": "^7.8.2",
|
|
63
|
+
"sharp": "^0.34.3",
|
|
60
64
|
"tsc": "^2.0.4",
|
|
61
65
|
"typescript": "^5.8.3",
|
|
62
|
-
"typescript-eslint": "^8.
|
|
66
|
+
"typescript-eslint": "^8.38.0",
|
|
67
|
+
"undici": "^7.12.0"
|
|
63
68
|
},
|
|
64
69
|
"scripts": {
|
|
65
70
|
"build:esm": "bun ./node_modules/typescript/bin/tsc --p tsconfig.esm.json",
|
|
@@ -73,7 +78,9 @@
|
|
|
73
78
|
"benchmark:node:file": "bun run dev & ( sleep 5 && node ./benchmarks/benchmark.mjs)"
|
|
74
79
|
},
|
|
75
80
|
"trustedDependencies": [
|
|
81
|
+
"@apollo/protobufjs",
|
|
76
82
|
"@nestjs/core",
|
|
77
|
-
"@swc/core"
|
|
83
|
+
"@swc/core",
|
|
84
|
+
"bun"
|
|
78
85
|
]
|
|
79
86
|
}
|
|
@@ -70,10 +70,15 @@ export class HonoAdapter extends AbstractHttpAdapter<
|
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
private async
|
|
73
|
+
private async normalizeContext(ctx: Ctx): Promise<Context> {
|
|
74
74
|
if (typeof ctx === 'function') {
|
|
75
|
-
|
|
75
|
+
return await ctx();
|
|
76
76
|
}
|
|
77
|
+
return ctx;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private async getBody(ctx: Ctx, body?: Data) {
|
|
81
|
+
ctx = await this.normalizeContext(ctx);
|
|
77
82
|
|
|
78
83
|
let responseContentType = await this.getHeader(ctx, 'Content-Type');
|
|
79
84
|
|
|
@@ -99,76 +104,88 @@ export class HonoAdapter extends AbstractHttpAdapter<
|
|
|
99
104
|
return ctx.body(body);
|
|
100
105
|
}
|
|
101
106
|
|
|
102
|
-
|
|
107
|
+
private registerRoute(
|
|
108
|
+
method:
|
|
109
|
+
| 'all'
|
|
110
|
+
| 'get'
|
|
111
|
+
| 'post'
|
|
112
|
+
| 'put'
|
|
113
|
+
| 'delete'
|
|
114
|
+
| 'use'
|
|
115
|
+
| 'patch'
|
|
116
|
+
| 'options',
|
|
117
|
+
pathOrHandler: string | HonoHandler,
|
|
118
|
+
handler?: HonoHandler,
|
|
119
|
+
) {
|
|
103
120
|
const [routePath, routeHandler] = this.getRouteAndHandler(
|
|
104
121
|
pathOrHandler,
|
|
105
122
|
handler,
|
|
106
123
|
);
|
|
107
|
-
|
|
124
|
+
const routeHandler2 = this.createRouteHandler(routeHandler);
|
|
125
|
+
|
|
126
|
+
switch (method) {
|
|
127
|
+
case 'all':
|
|
128
|
+
this.instance.all(routePath, routeHandler2);
|
|
129
|
+
break;
|
|
130
|
+
case 'get':
|
|
131
|
+
this.instance.get(routePath, routeHandler2);
|
|
132
|
+
break;
|
|
133
|
+
case 'post':
|
|
134
|
+
this.instance.post(routePath, routeHandler2);
|
|
135
|
+
break;
|
|
136
|
+
case 'put':
|
|
137
|
+
this.instance.put(routePath, routeHandler2);
|
|
138
|
+
break;
|
|
139
|
+
case 'delete':
|
|
140
|
+
this.instance.delete(routePath, routeHandler2);
|
|
141
|
+
break;
|
|
142
|
+
case 'use':
|
|
143
|
+
this.instance.use(routePath, routeHandler2);
|
|
144
|
+
break;
|
|
145
|
+
case 'patch':
|
|
146
|
+
this.instance.patch(routePath, routeHandler2);
|
|
147
|
+
break;
|
|
148
|
+
case 'options':
|
|
149
|
+
this.instance.options(routePath, routeHandler2);
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
108
152
|
}
|
|
109
153
|
|
|
110
|
-
public
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
handler,
|
|
114
|
-
);
|
|
154
|
+
public all(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
|
|
155
|
+
this.registerRoute('all', pathOrHandler, handler);
|
|
156
|
+
}
|
|
115
157
|
|
|
116
|
-
|
|
158
|
+
public get(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
|
|
159
|
+
this.registerRoute('get', pathOrHandler, handler);
|
|
117
160
|
}
|
|
118
161
|
|
|
119
162
|
public post(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
|
|
120
|
-
|
|
121
|
-
pathOrHandler,
|
|
122
|
-
handler,
|
|
123
|
-
);
|
|
124
|
-
this.instance.post(routePath, this.createRouteHandler(routeHandler));
|
|
163
|
+
this.registerRoute('post', pathOrHandler, handler);
|
|
125
164
|
}
|
|
126
165
|
|
|
127
166
|
public put(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
|
|
128
|
-
|
|
129
|
-
pathOrHandler,
|
|
130
|
-
handler,
|
|
131
|
-
);
|
|
132
|
-
this.instance.put(routePath, this.createRouteHandler(routeHandler));
|
|
167
|
+
this.registerRoute('put', pathOrHandler, handler);
|
|
133
168
|
}
|
|
134
169
|
|
|
135
170
|
public delete(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
|
|
136
|
-
|
|
137
|
-
pathOrHandler,
|
|
138
|
-
handler,
|
|
139
|
-
);
|
|
140
|
-
this.instance.delete(routePath, this.createRouteHandler(routeHandler));
|
|
171
|
+
this.registerRoute('delete', pathOrHandler, handler);
|
|
141
172
|
}
|
|
142
173
|
|
|
143
174
|
public use(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
|
|
144
|
-
|
|
145
|
-
pathOrHandler,
|
|
146
|
-
handler,
|
|
147
|
-
);
|
|
148
|
-
this.instance.use(routePath, this.createRouteHandler(routeHandler));
|
|
175
|
+
this.registerRoute('use', pathOrHandler, handler);
|
|
149
176
|
}
|
|
150
177
|
|
|
151
178
|
public patch(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
|
|
152
|
-
|
|
153
|
-
pathOrHandler,
|
|
154
|
-
handler,
|
|
155
|
-
);
|
|
156
|
-
this.instance.patch(routePath, this.createRouteHandler(routeHandler));
|
|
179
|
+
this.registerRoute('patch', pathOrHandler, handler);
|
|
157
180
|
}
|
|
158
181
|
|
|
159
182
|
public options(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
|
|
160
|
-
|
|
161
|
-
pathOrHandler,
|
|
162
|
-
handler,
|
|
163
|
-
);
|
|
164
|
-
this.instance.options(routePath, this.createRouteHandler(routeHandler));
|
|
183
|
+
this.registerRoute('options', pathOrHandler, handler);
|
|
165
184
|
}
|
|
166
185
|
|
|
167
186
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
168
187
|
public async reply(ctx: Ctx, body: any, statusCode?: StatusCode) {
|
|
169
|
-
|
|
170
|
-
ctx = await ctx();
|
|
171
|
-
}
|
|
188
|
+
ctx = await this.normalizeContext(ctx);
|
|
172
189
|
|
|
173
190
|
if (statusCode) {
|
|
174
191
|
ctx.status(statusCode);
|
|
@@ -190,10 +207,7 @@ export class HonoAdapter extends AbstractHttpAdapter<
|
|
|
190
207
|
}
|
|
191
208
|
|
|
192
209
|
public async status(ctx: Ctx, statusCode: StatusCode) {
|
|
193
|
-
|
|
194
|
-
ctx = await ctx();
|
|
195
|
-
}
|
|
196
|
-
|
|
210
|
+
ctx = await this.normalizeContext(ctx);
|
|
197
211
|
return ctx.status(statusCode);
|
|
198
212
|
}
|
|
199
213
|
|
|
@@ -206,10 +220,7 @@ export class HonoAdapter extends AbstractHttpAdapter<
|
|
|
206
220
|
}
|
|
207
221
|
|
|
208
222
|
public async redirect(ctx: Ctx, statusCode: RedirectStatusCode, url: string) {
|
|
209
|
-
|
|
210
|
-
ctx = await ctx();
|
|
211
|
-
}
|
|
212
|
-
|
|
223
|
+
ctx = await this.normalizeContext(ctx);
|
|
213
224
|
ctx.res = ctx.redirect(url, statusCode);
|
|
214
225
|
}
|
|
215
226
|
|
|
@@ -224,8 +235,9 @@ export class HonoAdapter extends AbstractHttpAdapter<
|
|
|
224
235
|
public setNotFoundHandler(handler: RequestHandler) {
|
|
225
236
|
this.instance.notFound(async (ctx: Context) => {
|
|
226
237
|
await handler(ctx.req, ctx);
|
|
238
|
+
await this.status(ctx, HttpStatus.NOT_FOUND);
|
|
227
239
|
|
|
228
|
-
return this.getBody(ctx);
|
|
240
|
+
return this.getBody(ctx, 'Not Found');
|
|
229
241
|
});
|
|
230
242
|
}
|
|
231
243
|
|
|
@@ -239,42 +251,27 @@ export class HonoAdapter extends AbstractHttpAdapter<
|
|
|
239
251
|
}
|
|
240
252
|
|
|
241
253
|
public async isHeadersSent(ctx: Ctx): Promise<boolean> {
|
|
242
|
-
|
|
243
|
-
ctx = await ctx();
|
|
244
|
-
}
|
|
245
|
-
|
|
254
|
+
ctx = await this.normalizeContext(ctx);
|
|
246
255
|
return ctx.finalized;
|
|
247
256
|
}
|
|
248
257
|
|
|
249
258
|
public async getHeader(ctx: Ctx, name: string) {
|
|
250
|
-
|
|
251
|
-
ctx = await ctx();
|
|
252
|
-
}
|
|
253
|
-
|
|
259
|
+
ctx = await this.normalizeContext(ctx);
|
|
254
260
|
return ctx.res.headers.get(name);
|
|
255
261
|
}
|
|
256
262
|
|
|
257
263
|
public async setHeader(ctx: Ctx, name: string, value: string) {
|
|
258
|
-
|
|
259
|
-
ctx = await ctx();
|
|
260
|
-
}
|
|
261
|
-
|
|
264
|
+
ctx = await this.normalizeContext(ctx);
|
|
262
265
|
ctx.res.headers.set(name, value);
|
|
263
266
|
}
|
|
264
267
|
|
|
265
268
|
public async appendHeader(ctx: Ctx, name: string, value: string) {
|
|
266
|
-
|
|
267
|
-
ctx = await ctx();
|
|
268
|
-
}
|
|
269
|
-
|
|
269
|
+
ctx = await this.normalizeContext(ctx);
|
|
270
270
|
ctx.res.headers.append(name, value);
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
public async getRequestHostname(ctx: Ctx): Promise<string> {
|
|
274
|
-
|
|
275
|
-
ctx = await ctx();
|
|
276
|
-
}
|
|
277
|
-
|
|
274
|
+
ctx = await this.normalizeContext(ctx);
|
|
278
275
|
return ctx.req.header().host;
|
|
279
276
|
}
|
|
280
277
|
|
|
@@ -319,46 +316,58 @@ export class HonoAdapter extends AbstractHttpAdapter<
|
|
|
319
316
|
return new Promise((resolve) => this.httpServer.close(() => resolve()));
|
|
320
317
|
}
|
|
321
318
|
|
|
319
|
+
private extractClientIp(ctx: Context): string {
|
|
320
|
+
return (
|
|
321
|
+
ctx.req.header('cf-connecting-ip') ??
|
|
322
|
+
ctx.req.header('x-forwarded-for') ??
|
|
323
|
+
ctx.req.header('x-real-ip') ??
|
|
324
|
+
ctx.req.header('forwarded') ??
|
|
325
|
+
ctx.req.header('true-client-ip') ??
|
|
326
|
+
ctx.req.header('x-client-ip') ??
|
|
327
|
+
ctx.req.header('x-cluster-client-ip') ??
|
|
328
|
+
ctx.req.header('x-forwarded') ??
|
|
329
|
+
ctx.req.header('forwarded-for') ??
|
|
330
|
+
ctx.req.header('via')
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
private async parseRequestBody(
|
|
335
|
+
ctx: Context,
|
|
336
|
+
contentType: string,
|
|
337
|
+
rawBody: boolean,
|
|
338
|
+
): Promise<void> {
|
|
339
|
+
if (
|
|
340
|
+
contentType?.startsWith('multipart/form-data') ||
|
|
341
|
+
contentType?.startsWith('application/x-www-form-urlencoded')
|
|
342
|
+
) {
|
|
343
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
344
|
+
(ctx.req as any).body = await ctx.req
|
|
345
|
+
.parseBody({
|
|
346
|
+
all: true,
|
|
347
|
+
})
|
|
348
|
+
.catch(() => {});
|
|
349
|
+
} else if (
|
|
350
|
+
contentType?.startsWith('application/json') ||
|
|
351
|
+
contentType?.startsWith('text/plain')
|
|
352
|
+
) {
|
|
353
|
+
if (rawBody) {
|
|
354
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
355
|
+
(ctx.req as any).rawBody = Buffer.from(await ctx.req.text());
|
|
356
|
+
}
|
|
357
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
358
|
+
(ctx.req as any).body = await ctx.req.json().catch(() => {});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
322
362
|
public initHttpServer(options: NestApplicationOptions) {
|
|
323
363
|
this.instance.use(async (ctx, next) => {
|
|
324
|
-
ctx.req['ip'] =
|
|
325
|
-
ctx.req.header('cf-connecting-ip') ??
|
|
326
|
-
ctx.req.header('x-forwarded-for') ??
|
|
327
|
-
ctx.req.header('x-real-ip') ??
|
|
328
|
-
ctx.req.header('forwarded') ??
|
|
329
|
-
ctx.req.header('true-client-ip') ??
|
|
330
|
-
ctx.req.header('x-client-ip') ??
|
|
331
|
-
ctx.req.header('x-cluster-client-ip') ??
|
|
332
|
-
ctx.req.header('x-forwarded') ??
|
|
333
|
-
ctx.req.header('forwarded-for') ??
|
|
334
|
-
ctx.req.header('via');
|
|
364
|
+
ctx.req['ip'] = this.extractClientIp(ctx);
|
|
335
365
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
336
366
|
ctx.req['query'] = ctx.req.query() as any;
|
|
337
367
|
ctx.req['headers'] = Object.fromEntries(ctx.req.raw.headers);
|
|
338
368
|
|
|
339
369
|
const contentType = ctx.req.header('content-type');
|
|
340
|
-
|
|
341
|
-
if (
|
|
342
|
-
contentType?.startsWith('multipart/form-data') ||
|
|
343
|
-
contentType?.startsWith('application/x-www-form-urlencoded')
|
|
344
|
-
) {
|
|
345
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
346
|
-
(ctx.req as any).body = await ctx.req
|
|
347
|
-
.parseBody({
|
|
348
|
-
all: true,
|
|
349
|
-
})
|
|
350
|
-
.catch(() => {});
|
|
351
|
-
} else if (
|
|
352
|
-
contentType?.startsWith('application/json') ||
|
|
353
|
-
contentType?.startsWith('text/plain')
|
|
354
|
-
) {
|
|
355
|
-
if (options.rawBody) {
|
|
356
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
357
|
-
(ctx.req as any).rawBody = Buffer.from(await ctx.req.text());
|
|
358
|
-
}
|
|
359
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
360
|
-
(ctx.req as any).body = await ctx.req.json().catch(() => {});
|
|
361
|
-
}
|
|
370
|
+
await this.parseRequestBody(ctx, contentType, options.rawBody);
|
|
362
371
|
|
|
363
372
|
await next();
|
|
364
373
|
});
|
|
@@ -424,8 +433,9 @@ export class HonoAdapter extends AbstractHttpAdapter<
|
|
|
424
433
|
public bodyLimit(maxSize: number) {
|
|
425
434
|
return bodyLimit({
|
|
426
435
|
maxSize,
|
|
427
|
-
onError: () => {
|
|
428
|
-
|
|
436
|
+
onError: (ctx) => {
|
|
437
|
+
const errorMessage = `Body size exceeded: ${maxSize} bytes. Size: ${ctx.req.header('Content-Length')} bytes. Method: ${ctx.req.method}. Path: ${ctx.req.path}`;
|
|
438
|
+
throw new Error(errorMessage);
|
|
429
439
|
},
|
|
430
440
|
});
|
|
431
441
|
}
|