@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kiyasov/platform-hono",
3
- "version": "1.5.1",
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.0",
32
- "@hono/node-server": "^1.14.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.9"
35
+ "hono": "^4.8.7"
36
36
  },
37
37
  "devDependencies": {
38
- "@eslint/js": "^9.26.0",
38
+ "@eslint/js": "^9.31.0",
39
39
  "@nestjs/cli": "^11.0.7",
40
- "@nestjs/common": "^11.1.1",
41
- "@nestjs/core": "^11.1.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.11.24",
44
+ "@swc/core": "^1.12.1",
44
45
  "@types/autocannon": "^7.12.7",
45
- "@types/bun": "^1.2.13",
46
+ "@types/bun": "^1.2.16",
46
47
  "@types/busboy": "^1.5.4",
47
48
  "autocannon": "^8.0.0",
48
- "bun": "^1.2.13",
49
- "eslint": "^9.26.0",
50
- "eslint-config-prettier": "^10.1.5",
51
- "eslint-plugin-perfectionist": "^4.13.0",
52
- "eslint-plugin-prettier": "^5.4.0",
53
- "globals": "^16.1.0",
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.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.3",
60
64
  "tsc": "^2.0.4",
61
65
  "typescript": "^5.8.3",
62
- "typescript-eslint": "^8.32.1"
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 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
 
@@ -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
- if (typeof ctx === 'function') {
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
- if (typeof ctx === 'function') {
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
- if (typeof ctx === 'function') {
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
- if (typeof ctx === 'function') {
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
- if (typeof ctx === 'function') {
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
- 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);
429
439
  },
430
440
  });
431
441
  }