@krisanalfa/bunest-adapter 0.0.1 → 0.3.0

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 CHANGED
@@ -2,6 +2,47 @@
2
2
 
3
3
  This project provides a native Bun adapter for NestJS, allowing developers to leverage the performance benefits of the Bun runtime while using the powerful features of the NestJS framework.
4
4
 
5
+ ## Table of Contents
6
+
7
+ - [Features](#features)
8
+ - [Native Bun adapter for NestJS](#native-bun-adapter-for-nestjs)
9
+ - [Full NestJS Feature Support](#full-nestjs-feature-support)
10
+ - [Controllers & HTTP Methods](#controllers--http-methods)
11
+ - [Middleware](#middleware)
12
+ - [Guards](#guards)
13
+ - [Interceptors](#interceptors)
14
+ - [Exception Filters](#exception-filters)
15
+ - [Validation](#validation)
16
+ - [File Uploads](#file-uploads)
17
+ - [Streaming Responses](#streaming-responses)
18
+ - [Versioning](#versioning)
19
+ - [CORS](#cors)
20
+ - [Cookies](#cookies)
21
+ - [Popular Express Middleware](#popular-express-middleware)
22
+ - [Bun File API Support](#bun-file-api-support)
23
+ - [BunFileInterceptor](#bunfileinterceptor)
24
+ - [WebSocket Support](#websocket-support)
25
+ - [Basic WebSocket Gateway](#basic-websocket-gateway)
26
+ - [WebSocket with Guards](#websocket-with-guards)
27
+ - [WebSocket with Pipes](#websocket-with-pipes)
28
+ - [WebSocket with Exception Filters](#websocket-with-exception-filters)
29
+ - [Broadcasting Messages](#broadcasting-messages)
30
+ - [Secure WebSocket (WSS)](#secure-websocket-wss)
31
+ - [Limitations](#limitations)
32
+ - [HTTPS](#https)
33
+ - [Code Quality](#code-quality)
34
+ - [Request / Response Objects](#request--response-objects)
35
+ - [BunRequest](#bunrequest)
36
+ - [BunResponse](#bunresponse)
37
+ - [Benchmark Results](#benchmark-results)
38
+ - [HTTP Benchmark](#http-benchmark)
39
+ - [WebSocket Benchmark](#websocket-benchmark)
40
+ - [Running HTTP Benchmark](#running-http-benchmark)
41
+ - [Running WebSocket Benchmark](#running-websocket-benchmark)
42
+ - [Contributing](#contributing)
43
+ - [Future Plans](#future-plans)
44
+ - [License](#license)
45
+
5
46
  ## Features
6
47
 
7
48
  ### Native Bun adapter for NestJS
@@ -442,6 +483,343 @@ Tested and working with:
442
483
  - `cors` - CORS handling
443
484
  - And most other Express-compatible middleware
444
485
 
486
+ ### WebSocket Support
487
+
488
+ The Bun adapter provides full WebSocket support using Bun's native WebSocket implementation. The `BunWsAdapter` enables real-time, bidirectional communication between clients and servers with excellent performance.
489
+
490
+ #### Basic WebSocket Gateway
491
+
492
+ Create WebSocket gateways using NestJS decorators:
493
+
494
+ ```ts
495
+ import { BunWsAdapter, BunAdapter } from "@krisanalfa/bunest-adapter";
496
+ import {
497
+ WebSocketGateway,
498
+ SubscribeMessage,
499
+ MessageBody,
500
+ OnGatewayConnection,
501
+ OnGatewayDisconnect,
502
+ ConnectedSocket,
503
+ } from "@nestjs/websockets";
504
+ import { ServerWebSocket } from "bun";
505
+
506
+ @WebSocketGateway({ cors: true })
507
+ class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
508
+ handleConnection(client: ServerWebSocket) {
509
+ client.send(JSON.stringify({ event: "welcome", data: "Welcome!" }));
510
+ }
511
+
512
+ handleDisconnect(client: ServerWebSocket) {
513
+ console.log("Client disconnected");
514
+ }
515
+
516
+ @SubscribeMessage("message")
517
+ handleMessage(@MessageBody() data: string) {
518
+ return {
519
+ event: "message",
520
+ data: `Received: ${data}`,
521
+ };
522
+ }
523
+ }
524
+
525
+ // Enable WebSocket support in your application
526
+ const app = await NestFactory.create(AppModule, new BunAdapter());
527
+ app.useWebSocketAdapter(new BunWsAdapter(app));
528
+ await app.listen(3000);
529
+ ```
530
+
531
+ Connect from the client:
532
+
533
+ ```ts
534
+ const socket = new WebSocket("ws://localhost:3000");
535
+
536
+ socket.onopen = () => {
537
+ socket.send(JSON.stringify({ event: "message", data: "Hello!" }));
538
+ };
539
+
540
+ socket.onmessage = (event) => {
541
+ const data = JSON.parse(event.data);
542
+ console.log(data); // { event: 'message', data: 'Received: Hello!' }
543
+ };
544
+ ```
545
+
546
+ #### WebSocket with Guards
547
+
548
+ Protect WebSocket endpoints with guards:
549
+
550
+ ```ts
551
+ import { CanActivate, ExecutionContext, Injectable } from "@nestjs/common";
552
+ import { WsException } from "@nestjs/websockets";
553
+
554
+ @Injectable()
555
+ class WsAuthGuard implements CanActivate {
556
+ canActivate(context: ExecutionContext): boolean {
557
+ const data = context.switchToWs().getData<{ token?: string }>();
558
+
559
+ if (data.token !== "valid-token") {
560
+ throw new WsException("Unauthorized");
561
+ }
562
+
563
+ return true;
564
+ }
565
+ }
566
+
567
+ @WebSocketGateway()
568
+ @UseGuards(WsAuthGuard)
569
+ class ProtectedGateway {
570
+ @SubscribeMessage("protected")
571
+ handleProtected(@MessageBody() data: { message: string }) {
572
+ return {
573
+ event: "protected",
574
+ data: `Protected message: ${data.message}`,
575
+ };
576
+ }
577
+ }
578
+ ```
579
+
580
+ #### WebSocket with Pipes
581
+
582
+ Validate WebSocket messages using pipes:
583
+
584
+ ```ts
585
+ import { UsePipes, ValidationPipe } from "@nestjs/common";
586
+ import { IsString, IsNotEmpty, MinLength } from "class-validator";
587
+
588
+ class CreateMessageDto {
589
+ @IsString()
590
+ @IsNotEmpty()
591
+ @MinLength(3)
592
+ text!: string;
593
+ }
594
+
595
+ @WebSocketGateway()
596
+ @UsePipes(
597
+ new ValidationPipe({
598
+ exceptionFactory: (errors) => new WsException(errors),
599
+ }),
600
+ )
601
+ class ValidationGateway {
602
+ @SubscribeMessage("createMessage")
603
+ handleCreateMessage(@MessageBody() dto: CreateMessageDto) {
604
+ return {
605
+ event: "messageCreated",
606
+ data: dto.text,
607
+ };
608
+ }
609
+ }
610
+ ```
611
+
612
+ #### WebSocket with Exception Filters
613
+
614
+ Handle WebSocket exceptions with custom filters:
615
+
616
+ ```ts
617
+ import { Catch, ArgumentsHost, WsExceptionFilter } from "@nestjs/common";
618
+ import { WsException } from "@nestjs/websockets";
619
+ import { ServerWebSocket } from "bun";
620
+
621
+ @Catch(WsException)
622
+ class WsExceptionsFilter implements WsExceptionFilter {
623
+ catch(exception: WsException, host: ArgumentsHost) {
624
+ const client = host.switchToWs().getClient<ServerWebSocket>();
625
+ const error = exception.getError();
626
+ const details = typeof error === "object" ? error : { message: error };
627
+
628
+ client.send(
629
+ JSON.stringify({
630
+ event: "error",
631
+ data: {
632
+ message: "An error occurred",
633
+ details,
634
+ },
635
+ }),
636
+ );
637
+ }
638
+ }
639
+
640
+ @WebSocketGateway()
641
+ @UseFilters(WsExceptionsFilter)
642
+ class ErrorHandlingGateway {
643
+ @SubscribeMessage("risky")
644
+ handleRisky(@MessageBody() data: { shouldFail: boolean }) {
645
+ if (data.shouldFail) {
646
+ throw new WsException("Something went wrong");
647
+ }
648
+ return { event: "success", data: "OK" };
649
+ }
650
+ }
651
+ ```
652
+
653
+ #### Broadcasting Messages
654
+
655
+ Broadcast messages to all connected clients using Bun's publish/subscribe system:
656
+
657
+ ```ts
658
+ import {
659
+ WebSocketGateway,
660
+ WebSocketServer,
661
+ SubscribeMessage,
662
+ MessageBody,
663
+ ConnectedSocket,
664
+ OnGatewayConnection,
665
+ } from "@nestjs/websockets";
666
+ import { ServerWebSocket } from "bun";
667
+ import { BunPreflightHttpServer } from "@krisanalfa/bunest-adapter";
668
+
669
+ @Injectable() // Mandatory to be able to inject `BunPreflightHttpServer`
670
+ @WebSocketGateway()
671
+ class BroadcastGateway implements OnGatewayConnection {
672
+ @WebSocketServer()
673
+ server!: BunPreflightHttpServer; // Inject BunPreflightHttpServer
674
+
675
+ private readonly roomName = "global-room";
676
+
677
+ handleConnection(client: ServerWebSocket) {
678
+ // Subscribe client to room
679
+ client.subscribe(this.roomName);
680
+ }
681
+
682
+ @SubscribeMessage("broadcast")
683
+ handleBroadcast(
684
+ @MessageBody() message: string,
685
+ @ConnectedSocket() socket: ServerWebSocket,
686
+ ) {
687
+ // Get subscriber count
688
+ const count = this.server.getBunServer().subscriberCount(this.roomName);
689
+
690
+ // Publish to all subscribers in the room
691
+ socket.publishText(
692
+ this.roomName,
693
+ JSON.stringify({
694
+ event: "broadcast",
695
+ data: message,
696
+ subscribers: count,
697
+ }),
698
+ );
699
+ }
700
+ }
701
+ ```
702
+
703
+ **Key Features:**
704
+
705
+ - **Native Performance** - Uses Bun's native WebSocket implementation for maximum speed
706
+ - **NestJS Integration** - Full support for decorators, guards, pipes, and exception filters
707
+ - **Pub/Sub Support** - Built-in support for broadcasting messages to multiple clients
708
+ - **CORS Configuration** - Easy CORS setup for WebSocket connections
709
+ - **HTTP + WebSocket** - Run both HTTP and WebSocket servers on the same port
710
+
711
+ #### Secure WebSocket (WSS)
712
+
713
+ The Bun adapter supports secure WebSocket connections (WSS) using TLS/SSL certificates. You can configure WSS in two ways:
714
+
715
+ **Using BunAdapter constructor options:**
716
+
717
+ ```ts
718
+ import { BunAdapter, BunWsAdapter } from "@krisanalfa/bunest-adapter";
719
+ import { NestFactory } from "@nestjs/core";
720
+
721
+ const app = await NestFactory.create(
722
+ AppModule,
723
+ new BunAdapter({
724
+ tls: {
725
+ cert: Bun.file("/path/to/cert.pem"),
726
+ key: Bun.file("/path/to/key.pem"),
727
+ },
728
+ }),
729
+ );
730
+
731
+ app.useWebSocketAdapter(new BunWsAdapter(app));
732
+ await app.listen(3000);
733
+
734
+ // Clients connect using wss:// protocol
735
+ // const ws = new WebSocket('wss://localhost:3000');
736
+ ```
737
+
738
+ **Using NestFactory.create httpsOptions:**
739
+
740
+ ```ts
741
+ import { BunAdapter, BunWsAdapter } from "@krisanalfa/bunest-adapter";
742
+ import { NestFactory } from "@nestjs/core";
743
+
744
+ const app = await NestFactory.create(AppModule, new BunAdapter(), {
745
+ httpsOptions: {
746
+ cert: Bun.file("/path/to/cert.pem"),
747
+ key: Bun.file("/path/to/key.pem"),
748
+ },
749
+ });
750
+
751
+ app.useWebSocketAdapter(new BunWsAdapter(app));
752
+ await app.listen(3000);
753
+ ```
754
+
755
+ **Unix Socket Support with WSS:**
756
+
757
+ You can also run secure WebSocket servers over Unix sockets:
758
+
759
+ ```ts
760
+ import { BunAdapter, BunWsAdapter } from "@krisanalfa/bunest-adapter";
761
+ import { NestFactory } from "@nestjs/core";
762
+
763
+ const app = await NestFactory.create(
764
+ AppModule,
765
+ new BunAdapter({
766
+ tls: {
767
+ cert: Bun.file("/path/to/cert.pem"),
768
+ key: Bun.file("/path/to/key.pem"),
769
+ },
770
+ }),
771
+ );
772
+
773
+ app.useWebSocketAdapter(new BunWsAdapter(app));
774
+ await app.listen("/tmp/secure-nestjs.sock");
775
+
776
+ // Or use abstract namespace socket on Linux
777
+ await app.listen("\0secure-nestjs-socket");
778
+ ```
779
+
780
+ **Client Connection Example:**
781
+
782
+ ```ts
783
+ // For development with self-signed certificates
784
+ const ws = new WebSocket("wss://localhost:3000", {
785
+ tls: { rejectUnauthorized: false },
786
+ });
787
+
788
+ ws.onopen = () => {
789
+ console.log("Connected to secure WebSocket");
790
+ ws.send(JSON.stringify({ event: "message", data: "Hello WSS!" }));
791
+ };
792
+
793
+ ws.onmessage = (event) => {
794
+ const data = JSON.parse(event.data);
795
+ console.log("Received:", data);
796
+ };
797
+ ```
798
+
799
+ **Important Notes:**
800
+
801
+ - WSS automatically uses the same port as your HTTPS server
802
+ - The `wss://` protocol is used instead of `ws://` for secure connections
803
+ - For production, use properly signed certificates from a trusted Certificate Authority
804
+ - For development, you can use self-signed certificates with `rejectUnauthorized: false` on the client
805
+
806
+ #### Limitations
807
+
808
+ **Port Configuration:** The WebSocket server port will always be the same as the Bun HTTP server port. In standard NestJS, you can configure different ports via the `@WebSocketGateway` decorator's `port` option (see [NestJS WebSocket documentation](https://docs.nestjs.com/websockets/gateways#overview)), but with the Bun adapter, WebSocket connections must use the same port as your HTTP server. This is due to Bun's unified server architecture where HTTP and WebSocket upgrades are handled by the same server instance.
809
+
810
+ ```ts
811
+ // This port option is ignored with BunWsAdapter
812
+ @WebSocketGateway({ port: 8080 }) // ⚠️ Port option has no effect
813
+ class ChatGateway {
814
+ // Gateway will use the same port as app.listen()
815
+ }
816
+
817
+ // WebSocket will be available on the same port as HTTP
818
+ const app = await NestFactory.create(AppModule, new BunAdapter());
819
+ app.useWebSocketAdapter(new BunWsAdapter(app));
820
+ await app.listen(3000); // Both HTTP and WebSocket use port 3000
821
+ ```
822
+
445
823
  ### Bun File API Support
446
824
 
447
825
  This package provides first-class support for Bun's native [`BunFile`](https://bun.com/docs/runtime/file-io) API, enabling seamless file uploads and downloads using Bun's efficient file handling capabilities.
@@ -550,7 +928,7 @@ The Bun adapter is developed with high code quality standards, including:
550
928
  - Strict TypeScript typings
551
929
  - Strict linting rules (ESLint)
552
930
  - Comprehensive unit and integration tests
553
- - Coverage reports (>98% line coverage, >85% function coverage)
931
+ - Coverage reports (>97% line coverage, >85% function coverage)
554
932
 
555
933
  ## Request / Response Objects
556
934
 
@@ -562,10 +940,10 @@ A high-performance request wrapper that provides lazy parsing and caching for op
562
940
 
563
941
  #### Properties
564
942
 
565
- - **`url`** - The complete request URL
943
+ - **`url`** - The URL path and query string (e.g., "/api/users?page=1"). For the full URL including protocol and host, use `original().url`
566
944
  - **`method`** - HTTP method (GET, POST, etc.)
567
945
  - **`pathname`** - URL path (lazily parsed)
568
- - **`hostname`** - Host name without port (lazily parsed)
946
+ - **`hostname`** - Host name without port (lazily parsed, respects X-Forwarded-Host header)
569
947
  - **`query`** - Parsed query parameters (lazily parsed)
570
948
  - **`headers`** - Request headers object with `.get()` method (lazily materialized)
571
949
  - **`params`** - Route parameters from URL patterns
@@ -575,9 +953,11 @@ A high-performance request wrapper that provides lazy parsing and caching for op
575
953
  - **`files`** - Multiple uploaded files (for multi-file uploads)
576
954
  - **`signal`** - AbortSignal for request cancellation
577
955
  - **`cookies`** - Cookie map for accessing request cookies
956
+ - **`socket`** - Mock socket object for Node.js compatibility (contains `encrypted` property)
578
957
 
579
958
  #### Methods
580
959
 
960
+ - **`original()`** - Get the underlying native Bun request object
581
961
  - **`get(key)`** - Get custom property stored in request
582
962
  - **`set(key, value)`** - Set custom property in request (useful for middleware)
583
963
  - **`json()`** - Parse body as JSON
@@ -603,7 +983,10 @@ class UsersController {
603
983
  const auth = req.headers.get("authorization");
604
984
  const contentType = req.headers["content-type"];
605
985
 
606
- return { page, limit, auth };
986
+ // Check if connection is secure
987
+ const isSecure = req.socket.encrypted;
988
+
989
+ return { page, limit, auth, isSecure };
607
990
  }
608
991
 
609
992
  @Post()
@@ -612,10 +995,14 @@ class UsersController {
612
995
  console.log(req.body); // Same as dto
613
996
 
614
997
  // Access request info
998
+ console.log(req.url); // "/users" (pathname + search)
615
999
  console.log(req.pathname); // "/users"
616
1000
  console.log(req.hostname); // "localhost"
617
1001
  console.log(req.method); // "POST"
618
1002
 
1003
+ // Access native request for full URL
1004
+ console.log(req.original().url); // "http://localhost:3000/users"
1005
+
619
1006
  return { created: dto };
620
1007
  }
621
1008
 
@@ -658,7 +1045,7 @@ class ApiController {
658
1045
 
659
1046
  ### BunResponse
660
1047
 
661
- A high-performance response builder that provides methods to construct responses with headers, cookies, and various body types. Uses lazy initialization and optimized response building for maximum performance.
1048
+ A high-performance response builder that provides methods to construct responses with headers, cookies, and various body types. Uses lazy initialization and optimized response building for maximum performance. Includes Node.js compatibility methods for seamless integration with existing middleware.
662
1049
 
663
1050
  #### Methods
664
1051
 
@@ -673,10 +1060,22 @@ A high-performance response builder that provides methods to construct responses
673
1060
  - **`deleteCookie(name)`** - Delete a cookie by name
674
1061
  - **`deleteCookie(options)`** - Delete a cookie with path/domain options
675
1062
  - **`redirect(url, statusCode?)`** - Send redirect response (default 302)
676
- - **`end(body?)`** - End response and send body (auto-handles JSON, streams, binary, or even [`BunFile`](https://bun.com/docs/runtime/file-io))
1063
+ - **`write(chunk)`** - Write data to response stream (Node.js compatibility). Chunks are accumulated until `end()` is called
1064
+ - **`writeHead(statusCode, headers?)`** - Write status and headers (Node.js compatibility)
1065
+ - **`end(body?)`** - End response and send body. Auto-handles JSON, streams, binary, [`BunFile`](https://bun.com/docs/runtime/file-io), and accumulated chunks from `write()` calls
677
1066
  - **`res()`** - Get the native Response promise
678
1067
  - **`isEnded()`** - Check if response has been ended
679
1068
 
1069
+ #### Node.js Compatibility Properties
1070
+
1071
+ For compatibility with Node.js HTTP response objects and EventEmitter-based middleware:
1072
+
1073
+ - **`destroyed`** - Always returns `false` (read-only property)
1074
+ - **`on(event, listener)`** - No-op stub for EventEmitter compatibility
1075
+ - **`off(event, listener)`** - No-op stub for EventEmitter compatibility
1076
+ - **`once(event, listener)`** - No-op stub for EventEmitter compatibility
1077
+ - **`destroy(error?)`** - No-op stub for Node.js compatibility
1078
+
680
1079
  #### Usage Examples
681
1080
 
682
1081
  **Setting status and headers:**
@@ -773,6 +1172,23 @@ class FilesController {
773
1172
  sendBinary(@Res() res: BunResponse) {
774
1173
  const buffer = new Uint8Array([1, 2, 3, 4, 5]);
775
1174
  res.setHeader("Content-Type", "application/octet-stream");
1175
+
1176
+ @Get("chunked")
1177
+ sendChunked(@Res() res: BunResponse) {
1178
+ // Node.js-style chunked response
1179
+ res.writeHead(200, { "Content-Type": "text/plain" });
1180
+ res.write("Hello ");
1181
+ res.write("World");
1182
+ res.end("!"); // Sends "Hello World!"
1183
+ }
1184
+
1185
+ @Get("mixed-chunks")
1186
+ sendMixedChunks(@Res() res: BunResponse) {
1187
+ // Mix of strings and binary data
1188
+ res.write("Status: ");
1189
+ res.write(new Uint8Array([50, 48, 48])); // "200" in bytes
1190
+ res.end(); // Chunks are combined and sent
1191
+ }
776
1192
  res.end(buffer);
777
1193
  }
778
1194
 
@@ -815,38 +1231,85 @@ class HybridController {
815
1231
 
816
1232
  ## Benchmark Results
817
1233
 
818
- Tested on MacOS Sequoia (15.6.1), Apple M1 Max (64GB RAM), Bun 1.3.5, Node.js 20.10.0
1234
+ Tested on MacOS Sequoia (15.6.1), Apple M1 Max (64GB RAM), Bun 1.3.5, Node.js 20.10.0.
1235
+
1236
+ ### HTTP Benchmark
1237
+
1238
+ HTTP benchmarks run using [`oha`](https://github.com/hatoo/oha) tool with the following command:
1239
+
1240
+ ```
1241
+ oha -c 125 -n 1000000 --no-tui "http://127.0.0.1:3000/"
1242
+ ```
819
1243
 
820
1244
  | Configuration | Requests/sec | Compared to Pure Bun |
821
1245
  | --------------------------------------------------------------------------------- | -----------: | -------------------: |
822
1246
  | Pure Bun | 80,742.72 | 100.00% |
823
- | Nest + Bun + Native Bun Adapter | 69,665.59 | 86.32% |
1247
+ | Nest + Bun + Native Bun Adapter | 70,234.76 | 86.98% |
824
1248
  | Nest + Bun + Express Adapter | 43,375.97 | 53.72% |
825
1249
  | Nest + Bun + [Hono Adapter](https://www.npmjs.com/package/@kiyasov/platform-hono) | 19,194.78 | 23.77% |
826
1250
  | Nest + Node + Express | 14,019.88 | 17.36% |
827
1251
 
828
1252
  > **Pure Bun** is the fastest at **80,743 req/s**. **Nest + Bun + Native Bun Adapter** achieves **~86%** of Pure Bun's performance while providing full NestJS features, and is **~5x faster** than Nest + Node + Express. Compared to Bun with Express adapter, the native Bun adapter is **~1.6x faster**.
829
1253
 
830
- ### Running Benchmarks
1254
+ ### WebSocket Benchmark
1255
+
1256
+ WebSocket benchmarks run using the custom benchmark script in `benchmarks/ws.benchmark.ts`.
1257
+
1258
+ | Configuration | Messages/sec | Compared to Pure Bun |
1259
+ | ---------------------------------- | -----------: | -------------------: |
1260
+ | Pure Bun WebSocket | 817,594.60 | 100.00% |
1261
+ | Nest + Bun + BunWsAdapter | 764,962.70 | 93.56% |
1262
+ | Nest + Bun + WebSocketAdapter (ws) | 299,161.50 | 36.59% |
1263
+
1264
+ > **Pure Bun WebSocket** achieves **817,595 msg/s**. **Nest + Bun + BunWsAdapter** achieves **~94%** of Pure Bun's performance, and is **~2.6x faster** than using the standard WebSocketAdapter with `ws` library.
1265
+
1266
+ ### Running HTTP Benchmark
831
1267
 
832
1268
  This project includes benchmark configurations in the `benchmarks` directory.
833
- To run the specific server benchmark, you can use predefined scripts in `package.json`. For example:
1269
+ To run the specific HTTP server benchmark, you can use predefined scripts in `package.json`:
834
1270
 
835
1271
  ```bash
836
1272
  # Running native bun server benchmark
837
- bun run native
1273
+ bun run http:native
838
1274
 
839
1275
  # Running NestJS with Bun adapter benchmark
840
- bun run bun
1276
+ bun run http:bun
841
1277
 
842
1278
  # Running NestJS with Hono adapter benchmark
843
- bun run hono
1279
+ bun run http:hono
844
1280
 
845
1281
  # Running NestJS with Express adapter benchmark
846
- bun run express
1282
+ bun run http:express
847
1283
 
848
1284
  # Running NestJS with Node and Express benchmark
849
- bun run node
1285
+ bun run http:node
1286
+ ```
1287
+
1288
+ Then run the benchmark using [`oha`](https://github.com/hatoo/oha):
1289
+
1290
+ ```bash
1291
+ oha -c 125 -n 1000000 --no-tui "http://127.0.0.1:3000/"
1292
+ ```
1293
+
1294
+ ### Running WebSocket Benchmark
1295
+
1296
+ To run WebSocket benchmarks, first start the WebSocket server:
1297
+
1298
+ ```bash
1299
+ # Running native bun websocket benchmark
1300
+ bun run ws:native
1301
+
1302
+ # Running NestJS with BunWsAdapter websocket benchmark
1303
+ bun run ws:bun
1304
+
1305
+ # Running NestJS with WebSocketAdapter (ws) websocket benchmark
1306
+ bun run ws:ws
1307
+ ```
1308
+
1309
+ Then run the benchmark script:
1310
+
1311
+ ```bash
1312
+ bun benchmarks/ws.benchmark.ts
850
1313
  ```
851
1314
 
852
1315
  All benchmarks use port `3000` by default. You can adjust the port in the respective benchmark files if needed.
@@ -857,9 +1320,9 @@ Contributions are welcome! Please open issues or submit pull requests for bug fi
857
1320
 
858
1321
  ## Future Plans
859
1322
 
860
- - Support for WebSocket integration with Bun
861
1323
  - Enhanced trusted proxy configuration for host header handling
862
1324
  - Additional performance optimizations and benchmarks
1325
+ - Release automation via CI/CD pipelines
863
1326
 
864
1327
  ## License
865
1328
 
@@ -1,21 +1,30 @@
1
1
  import { CorsOptions, CorsOptionsDelegate } from '@nestjs/common/interfaces/external/cors-options.interface.js';
2
2
  import { ErrorHandler, RequestHandler } from '@nestjs/common/interfaces/index.js';
3
+ import { Server } from 'bun';
3
4
  import { NestApplicationOptions, RequestMethod, VersioningOptions } from '@nestjs/common';
4
- import { Serve, Server } from 'bun';
5
5
  import { AbstractHttpAdapter } from '@nestjs/core';
6
6
  import { VersionValue } from '@nestjs/common/interfaces/version-options.interface.js';
7
+ import { ServerOptions } from './internal.types.js';
8
+ import { BunPreflightHttpServer } from './bun.preflight-http-server.js';
7
9
  import { BunRequest } from './bun.request.js';
8
10
  import { BunResponse } from './bun.response.js';
9
11
  export declare class BunAdapter extends AbstractHttpAdapter<Server<unknown>, BunRequest, BunResponse> {
10
- private bunServeOptions;
12
+ protected bunServeOptions: ServerOptions;
11
13
  private readonly logger;
12
14
  private readonly middlewareEngine;
13
15
  private useVersioning;
14
16
  private readonly routes;
15
17
  private readonly routeHandlers;
16
18
  private notFoundHandler;
17
- constructor(bunServeOptions?: Pick<Serve.Options<unknown>, 'development' | 'maxRequestBodySize' | 'idleTimeout' | 'id' | 'tls'>);
19
+ private readonly wsHandlers;
20
+ private readonly wsMiddlewareEngine;
21
+ private wsOptions;
22
+ private useWs;
23
+ private useWsCors;
24
+ private wsCorsHeaders?;
25
+ constructor(bunServeOptions?: ServerOptions);
18
26
  use(middleware: RequestHandler<BunRequest, BunResponse>): void;
27
+ private createHttpMethodHandler;
19
28
  get(handler: RequestHandler<BunRequest, BunResponse>): void;
20
29
  get(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
21
30
  post(handler: RequestHandler<BunRequest, BunResponse>): void;
@@ -30,6 +39,7 @@ export declare class BunAdapter extends AbstractHttpAdapter<Server<unknown>, Bun
30
39
  head(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
31
40
  options(handler: RequestHandler<BunRequest, BunResponse>): void;
32
41
  options(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
42
+ private createUnsupportedMethod;
33
43
  all(handler: RequestHandler<BunRequest, BunResponse>): void;
34
44
  all(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
35
45
  propfind(handler: RequestHandler<BunRequest, BunResponse>): void;
@@ -52,7 +62,7 @@ export declare class BunAdapter extends AbstractHttpAdapter<Server<unknown>, Bun
52
62
  setViewEngine(engine: string): void;
53
63
  render(response: unknown, view: string, options: unknown): void;
54
64
  close(): Promise<void>;
55
- initHttpServer(options: NestApplicationOptions): void;
65
+ initHttpServer(options: NestApplicationOptions): BunPreflightHttpServer;
56
66
  getRequestHostname(request: BunRequest): string;
57
67
  getRequestMethod(request: BunRequest): string;
58
68
  getRequestUrl(request: BunRequest): string;
@@ -84,7 +94,17 @@ export declare class BunAdapter extends AbstractHttpAdapter<Server<unknown>, Bun
84
94
  * @param callback Optional callback to invoke once the server is listening.
85
95
  */
86
96
  listen(port: string | number, hostname: string, callback?: () => void): void;
97
+ private static isNumericPort;
98
+ private static omitKeys;
99
+ private isWebSocketUpgradeRequest;
100
+ private handleWebSocketCors;
101
+ private upgradeWebSocket;
102
+ private setupWebSocketIfNeeded;
103
+ private createServer;
87
104
  private delegateRouteHandler;
105
+ private ensureRouteExists;
106
+ private prepareRequestHandler;
107
+ private createRouteFetchHandler;
88
108
  private createVersioningHandlers;
89
109
  private executeHandlerChain;
90
110
  private createChainedHandlerForVersioningResolution;