@kiyasov/platform-hono 1.5.2 → 1.5.4
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/Readme.md +80 -24
- package/benchmarks/benchmark.mjs +12 -12
- package/dist/cjs/src/adapters/hono-adapter.d.ts +4 -0
- package/dist/cjs/src/adapters/hono-adapter.js +86 -70
- package/dist/cjs/src/adapters/hono-adapter.js.map +1 -1
- package/dist/cjs/src/multer/storage/memory-storage.d.ts +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 +86 -70
- package/dist/esm/src/adapters/hono-adapter.js.map +1 -1
- package/dist/esm/src/multer/storage/memory-storage.d.ts +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +27 -25
- package/src/adapters/hono-adapter.ts +115 -106
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kiyasov/platform-hono",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.4",
|
|
4
4
|
"description": "Nest adapter for Hono",
|
|
5
5
|
"author": "Islam Kiiasov",
|
|
6
6
|
"repository": {
|
|
@@ -29,48 +29,50 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@apollo/server": "^4.12.2",
|
|
32
|
-
"@hono/node-server": "^1.
|
|
32
|
+
"@hono/node-server": "^1.19.4",
|
|
33
33
|
"@nestjs/apollo": "^13.1.0",
|
|
34
34
|
"@nestjs/graphql": "^13.1.0",
|
|
35
|
-
"hono": "^4.
|
|
35
|
+
"hono": "^4.9.9"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@eslint/js": "^9.
|
|
39
|
-
"@nestjs/cli": "^11.0.
|
|
40
|
-
"@nestjs/common": "^11.1.
|
|
41
|
-
"@nestjs/core": "^11.1.
|
|
42
|
-
"@
|
|
43
|
-
"@swc/
|
|
38
|
+
"@eslint/js": "^9.36.0",
|
|
39
|
+
"@nestjs/cli": "^11.0.10",
|
|
40
|
+
"@nestjs/common": "^11.1.6",
|
|
41
|
+
"@nestjs/core": "^11.1.6",
|
|
42
|
+
"@nestjs/swagger": "^11.2.0",
|
|
43
|
+
"@swc/cli": "^0.7.8",
|
|
44
|
+
"@swc/core": "^1.13.20",
|
|
44
45
|
"@types/autocannon": "^7.12.7",
|
|
45
|
-
"@types/bun": "^1.2.
|
|
46
|
+
"@types/bun": "^1.2.22",
|
|
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.22",
|
|
50
|
+
"class-transformer": "^0.5.1",
|
|
51
|
+
"class-validator": "^0.14.2",
|
|
52
|
+
"eslint": "^9.36.0",
|
|
53
|
+
"eslint-config-prettier": "^10.1.8",
|
|
54
|
+
"eslint-plugin-perfectionist": "^4.15.0",
|
|
55
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
56
|
+
"globals": "^16.4.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.4",
|
|
60
64
|
"tsc": "^2.0.4",
|
|
61
|
-
"typescript": "^5.
|
|
62
|
-
"typescript-eslint": "^8.
|
|
65
|
+
"typescript": "^5.9.2",
|
|
66
|
+
"typescript-eslint": "^8.44.1",
|
|
67
|
+
"undici": "^7.16.0"
|
|
63
68
|
},
|
|
64
69
|
"scripts": {
|
|
65
70
|
"build:esm": "bun ./node_modules/typescript/bin/tsc --p tsconfig.esm.json",
|
|
66
71
|
"build:cjs": "bun ./node_modules/typescript/bin/tsc --p tsconfig.cjs.json",
|
|
67
72
|
"build": "bun run build:esm && bun run build:cjs",
|
|
68
|
-
"dev": "cd example &&
|
|
69
|
-
"
|
|
70
|
-
"benchmark": "bun dev & ( sleep 5 &&
|
|
71
|
-
"benchmark:bun": "bun dev:bun & ( sleep 5 && autocannon -c 200 -d 5 -p 10 http://localhost:3000 )",
|
|
72
|
-
"benchmark:bun:file": "bun run dev:bun & ( sleep 5 && bun run ./benchmarks/benchmark.mjs)",
|
|
73
|
-
"benchmark:node:file": "bun run dev & ( sleep 5 && node ./benchmarks/benchmark.mjs)"
|
|
73
|
+
"dev": "cd example && nest start -w --copy-files --exec \"bun run --inspect\"",
|
|
74
|
+
"benchmark": "bun dev:bun & ( sleep 5 && autocannon -c 200 -d 5 -p 10 http://localhost:3000 )",
|
|
75
|
+
"benchmark:file": "bun run dev:bun & ( sleep 5 && bun run ./benchmarks/benchmark.mjs)"
|
|
74
76
|
},
|
|
75
77
|
"trustedDependencies": [
|
|
76
78
|
"@apollo/protobufjs",
|
|
@@ -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
|
|
|
@@ -240,42 +251,27 @@ export class HonoAdapter extends AbstractHttpAdapter<
|
|
|
240
251
|
}
|
|
241
252
|
|
|
242
253
|
public async isHeadersSent(ctx: Ctx): Promise<boolean> {
|
|
243
|
-
|
|
244
|
-
ctx = await ctx();
|
|
245
|
-
}
|
|
246
|
-
|
|
254
|
+
ctx = await this.normalizeContext(ctx);
|
|
247
255
|
return ctx.finalized;
|
|
248
256
|
}
|
|
249
257
|
|
|
250
258
|
public async getHeader(ctx: Ctx, name: string) {
|
|
251
|
-
|
|
252
|
-
ctx = await ctx();
|
|
253
|
-
}
|
|
254
|
-
|
|
259
|
+
ctx = await this.normalizeContext(ctx);
|
|
255
260
|
return ctx.res.headers.get(name);
|
|
256
261
|
}
|
|
257
262
|
|
|
258
263
|
public async setHeader(ctx: Ctx, name: string, value: string) {
|
|
259
|
-
|
|
260
|
-
ctx = await ctx();
|
|
261
|
-
}
|
|
262
|
-
|
|
264
|
+
ctx = await this.normalizeContext(ctx);
|
|
263
265
|
ctx.res.headers.set(name, value);
|
|
264
266
|
}
|
|
265
267
|
|
|
266
268
|
public async appendHeader(ctx: Ctx, name: string, value: string) {
|
|
267
|
-
|
|
268
|
-
ctx = await ctx();
|
|
269
|
-
}
|
|
270
|
-
|
|
269
|
+
ctx = await this.normalizeContext(ctx);
|
|
271
270
|
ctx.res.headers.append(name, value);
|
|
272
271
|
}
|
|
273
272
|
|
|
274
273
|
public async getRequestHostname(ctx: Ctx): Promise<string> {
|
|
275
|
-
|
|
276
|
-
ctx = await ctx();
|
|
277
|
-
}
|
|
278
|
-
|
|
274
|
+
ctx = await this.normalizeContext(ctx);
|
|
279
275
|
return ctx.req.header().host;
|
|
280
276
|
}
|
|
281
277
|
|
|
@@ -320,46 +316,58 @@ export class HonoAdapter extends AbstractHttpAdapter<
|
|
|
320
316
|
return new Promise((resolve) => this.httpServer.close(() => resolve()));
|
|
321
317
|
}
|
|
322
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
|
+
|
|
323
362
|
public initHttpServer(options: NestApplicationOptions) {
|
|
324
363
|
this.instance.use(async (ctx, next) => {
|
|
325
|
-
ctx.req['ip'] =
|
|
326
|
-
ctx.req.header('cf-connecting-ip') ??
|
|
327
|
-
ctx.req.header('x-forwarded-for') ??
|
|
328
|
-
ctx.req.header('x-real-ip') ??
|
|
329
|
-
ctx.req.header('forwarded') ??
|
|
330
|
-
ctx.req.header('true-client-ip') ??
|
|
331
|
-
ctx.req.header('x-client-ip') ??
|
|
332
|
-
ctx.req.header('x-cluster-client-ip') ??
|
|
333
|
-
ctx.req.header('x-forwarded') ??
|
|
334
|
-
ctx.req.header('forwarded-for') ??
|
|
335
|
-
ctx.req.header('via');
|
|
364
|
+
ctx.req['ip'] = this.extractClientIp(ctx);
|
|
336
365
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
337
366
|
ctx.req['query'] = ctx.req.query() as any;
|
|
338
367
|
ctx.req['headers'] = Object.fromEntries(ctx.req.raw.headers);
|
|
339
368
|
|
|
340
369
|
const contentType = ctx.req.header('content-type');
|
|
341
|
-
|
|
342
|
-
if (
|
|
343
|
-
contentType?.startsWith('multipart/form-data') ||
|
|
344
|
-
contentType?.startsWith('application/x-www-form-urlencoded')
|
|
345
|
-
) {
|
|
346
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
347
|
-
(ctx.req as any).body = await ctx.req
|
|
348
|
-
.parseBody({
|
|
349
|
-
all: true,
|
|
350
|
-
})
|
|
351
|
-
.catch(() => {});
|
|
352
|
-
} else if (
|
|
353
|
-
contentType?.startsWith('application/json') ||
|
|
354
|
-
contentType?.startsWith('text/plain')
|
|
355
|
-
) {
|
|
356
|
-
if (options.rawBody) {
|
|
357
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
358
|
-
(ctx.req as any).rawBody = Buffer.from(await ctx.req.text());
|
|
359
|
-
}
|
|
360
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
361
|
-
(ctx.req as any).body = await ctx.req.json().catch(() => {});
|
|
362
|
-
}
|
|
370
|
+
await this.parseRequestBody(ctx, contentType, options.rawBody);
|
|
363
371
|
|
|
364
372
|
await next();
|
|
365
373
|
});
|
|
@@ -425,8 +433,9 @@ export class HonoAdapter extends AbstractHttpAdapter<
|
|
|
425
433
|
public bodyLimit(maxSize: number) {
|
|
426
434
|
return bodyLimit({
|
|
427
435
|
maxSize,
|
|
428
|
-
onError: () => {
|
|
429
|
-
|
|
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);
|
|
430
439
|
},
|
|
431
440
|
});
|
|
432
441
|
}
|