@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kiyasov/platform-hono",
3
- "version": "1.5.2",
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.14.4",
32
+ "@hono/node-server": "^1.19.4",
33
33
  "@nestjs/apollo": "^13.1.0",
34
34
  "@nestjs/graphql": "^13.1.0",
35
- "hono": "^4.7.11"
35
+ "hono": "^4.9.9"
36
36
  },
37
37
  "devDependencies": {
38
- "@eslint/js": "^9.29.0",
39
- "@nestjs/cli": "^11.0.7",
40
- "@nestjs/common": "^11.1.3",
41
- "@nestjs/core": "^11.1.3",
42
- "@swc/cli": "^0.7.7",
43
- "@swc/core": "^1.12.1",
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.16",
46
+ "@types/bun": "^1.2.22",
46
47
  "@types/busboy": "^1.5.4",
47
48
  "autocannon": "^8.0.0",
48
- "bun": "^1.2.16",
49
- "eslint": "^9.29.0",
50
- "eslint-config-prettier": "^10.1.5",
51
- "eslint-plugin-perfectionist": "^4.14.0",
52
- "eslint-plugin-prettier": "^5.4.1",
53
- "globals": "^16.2.0",
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.5.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.8.3",
62
- "typescript-eslint": "^8.34.1"
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 && bun nest start -w --copy-files",
69
- "dev:bun": "cd example && nest start -w --copy-files --exec \"bun run --inspect\"",
70
- "benchmark": "bun dev & ( sleep 5 && autocannon -c 200 -d 5 -p 10 http://localhost:3000 )",
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 getBody(ctx: Ctx, body?: Data) {
73
+ private async normalizeContext(ctx: Ctx): Promise<Context> {
74
74
  if (typeof ctx === 'function') {
75
- ctx = await ctx();
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
- public all(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
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
- this.instance.all(routePath, this.createRouteHandler(routeHandler));
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 get(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
111
- const [routePath, routeHandler] = this.getRouteAndHandler(
112
- pathOrHandler,
113
- handler,
114
- );
154
+ public all(pathOrHandler: string | HonoHandler, handler?: HonoHandler) {
155
+ this.registerRoute('all', pathOrHandler, handler);
156
+ }
115
157
 
116
- this.instance.get(routePath, this.createRouteHandler(routeHandler));
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
- const [routePath, routeHandler] = this.getRouteAndHandler(
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
- const [routePath, routeHandler] = this.getRouteAndHandler(
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
- const [routePath, routeHandler] = this.getRouteAndHandler(
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
- const [routePath, routeHandler] = this.getRouteAndHandler(
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
- const [routePath, routeHandler] = this.getRouteAndHandler(
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
- const [routePath, routeHandler] = this.getRouteAndHandler(
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
- if (typeof ctx === 'function') {
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
- if (typeof ctx === 'function') {
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
- if (typeof ctx === 'function') {
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
- if (typeof ctx === 'function') {
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
- if (typeof ctx === 'function') {
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
- if (typeof ctx === 'function') {
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
- if (typeof ctx === 'function') {
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
- if (typeof ctx === 'function') {
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
- throw new Error('Body too large');
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
  }