@krisanalfa/bunest-adapter 0.1.0 → 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
 
@@ -853,38 +1231,85 @@ class HybridController {
853
1231
 
854
1232
  ## Benchmark Results
855
1233
 
856
- 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
+ ```
857
1243
 
858
1244
  | Configuration | Requests/sec | Compared to Pure Bun |
859
1245
  | --------------------------------------------------------------------------------- | -----------: | -------------------: |
860
1246
  | Pure Bun | 80,742.72 | 100.00% |
861
- | Nest + Bun + Native Bun Adapter | 69,665.59 | 86.32% |
1247
+ | Nest + Bun + Native Bun Adapter | 70,234.76 | 86.98% |
862
1248
  | Nest + Bun + Express Adapter | 43,375.97 | 53.72% |
863
1249
  | Nest + Bun + [Hono Adapter](https://www.npmjs.com/package/@kiyasov/platform-hono) | 19,194.78 | 23.77% |
864
1250
  | Nest + Node + Express | 14,019.88 | 17.36% |
865
1251
 
866
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**.
867
1253
 
868
- ### 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
869
1267
 
870
1268
  This project includes benchmark configurations in the `benchmarks` directory.
871
- 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`:
872
1270
 
873
1271
  ```bash
874
1272
  # Running native bun server benchmark
875
- bun run native
1273
+ bun run http:native
876
1274
 
877
1275
  # Running NestJS with Bun adapter benchmark
878
- bun run bun
1276
+ bun run http:bun
879
1277
 
880
1278
  # Running NestJS with Hono adapter benchmark
881
- bun run hono
1279
+ bun run http:hono
882
1280
 
883
1281
  # Running NestJS with Express adapter benchmark
884
- bun run express
1282
+ bun run http:express
885
1283
 
886
1284
  # Running NestJS with Node and Express benchmark
887
- 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
888
1313
  ```
889
1314
 
890
1315
  All benchmarks use port `3000` by default. You can adjust the port in the respective benchmark files if needed.
@@ -895,7 +1320,6 @@ Contributions are welcome! Please open issues or submit pull requests for bug fi
895
1320
 
896
1321
  ## Future Plans
897
1322
 
898
- - Support for WebSocket integration with Bun
899
1323
  - Enhanced trusted proxy configuration for host header handling
900
1324
  - Additional performance optimizations and benchmarks
901
1325
  - Release automation via CI/CD pipelines
@@ -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;
@@ -0,0 +1,62 @@
1
+ import { Server, ServerWebSocket } from 'bun';
2
+ import { BaseWsInstance } from '@nestjs/websockets';
3
+ import { ServerOptions, WsOptions } from './internal.types.js';
4
+ interface BunHttpAdapter {
5
+ setWsOptions(options: WsOptions): void;
6
+ getBunHttpServerInstance(): Server<unknown>;
7
+ getWsHandlers(): {
8
+ onOpen: ((ws: ServerWebSocket<unknown>) => void) | undefined;
9
+ onMessage: ((ws: ServerWebSocket<unknown>, message: string | ArrayBuffer | Buffer | Buffer[], server: Server<unknown>) => void) | undefined;
10
+ onClose: ((ws: ServerWebSocket<unknown>, code: number, reason: string) => void) | undefined;
11
+ };
12
+ getBunServerOptions(): Pick<ServerOptions, 'port' | 'hostname'>;
13
+ }
14
+ /**
15
+ * Bun HTTP server placeholder used before the actual server instance is created.
16
+ * This class provides compatibility methods expected by NestJS framework.
17
+ *
18
+ * There's limitation in Bun where we can't create the server instance without
19
+ * listening on a port right away. This placeholder allows us to defer
20
+ * the server creation until NestJS calls the listen method.
21
+ */
22
+ export declare class BunPreflightHttpServer implements BaseWsInstance {
23
+ private readonly adapter;
24
+ constructor(adapter: BunHttpAdapter);
25
+ on(event: string, callback: Function): void;
26
+ /**
27
+ * NestJS compatibility methods
28
+ * Nest use this to listen for "error" events during HTTP server initialization
29
+ */
30
+ once(): void;
31
+ /**
32
+ * NestJS compatibility methods
33
+ * Nest use this to remove "error" event listeners during HTTP server cleanup
34
+ */
35
+ removeListener(): void;
36
+ /**
37
+ * NestJS compatibility methods
38
+ */
39
+ stop(force?: boolean): Promise<void>;
40
+ address(): {
41
+ address: "0.0.0.0" | "127.0.0.1" | "localhost" | (string & {});
42
+ port: string | number;
43
+ } | {
44
+ address: string | undefined;
45
+ port: number | undefined;
46
+ };
47
+ setWsOptions(options: WsOptions): void;
48
+ registerWsOpenHandler(handler: (ws: ServerWebSocket<unknown>) => void): void;
49
+ registerWsMessageHandler(handler: (ws: ServerWebSocket<unknown>, message: string | ArrayBuffer | Buffer | Buffer[], server: Server<unknown>) => void): void;
50
+ registerWsCloseHandler(handler: (ws: ServerWebSocket<unknown>, code: number, reason: string) => void): void;
51
+ getWsHandlers(): {
52
+ onOpen: ((ws: ServerWebSocket<unknown>) => void) | undefined;
53
+ onMessage: ((ws: ServerWebSocket<unknown>, message: string | ArrayBuffer | Buffer | Buffer[], server: Server<unknown>) => void) | undefined;
54
+ onClose: ((ws: ServerWebSocket<unknown>, code: number, reason: string) => void) | undefined;
55
+ };
56
+ getBunServer(): Server<unknown>;
57
+ /**
58
+ * Proxy method for WebSocket server close
59
+ */
60
+ close(): Promise<void>;
61
+ }
62
+ export {};
@@ -0,0 +1,48 @@
1
+ import { AbstractWsAdapter, BaseWsInstance, MessageMappingProperties } from '@nestjs/websockets';
2
+ import { Observable } from 'rxjs';
3
+ import { INestApplicationContext } from '@nestjs/common';
4
+ import { ServerWebSocket } from 'bun';
5
+ import { BunPreflightHttpServer } from './bun.preflight-http-server.js';
6
+ import { WsOptions } from './internal.types.js';
7
+ export type WsData = string | Buffer | ArrayBuffer | Buffer[];
8
+ export type WsMessageParser<TData = unknown> = (data: WsData) => WsParsedData<TData>;
9
+ export interface WsParsedData<TData = unknown> {
10
+ event: string;
11
+ data: TData;
12
+ }
13
+ export interface BunWsAdapterOptions extends WsOptions {
14
+ messageParser?: WsMessageParser;
15
+ }
16
+ /** Internal data stored on each WebSocket connection - must match bun.adapter.ts */
17
+ interface BunWsClientData {
18
+ /** Called when a message is received - matches bun.adapter.ts onMessageInternal */
19
+ onMessageInternal?: (message: WsData) => void;
20
+ /** Called when the connection closes - matches bun.adapter.ts onCloseInternal */
21
+ onCloseInternal?: () => void;
22
+ /** Called by NestJS for disconnect handling */
23
+ onDisconnect?: (ws: ServerWebSocket<unknown>) => void;
24
+ }
25
+ type BunWsClient = ServerWebSocket<BunWsClientData> & BaseWsInstance;
26
+ /**
27
+ * High-performance WebSocket adapter for Bun runtime with NestJS.
28
+ */
29
+ export declare class BunWsAdapter extends AbstractWsAdapter<BunPreflightHttpServer, BunWsClient> {
30
+ private readonly logger;
31
+ private readonly nestApp;
32
+ private messageParser;
33
+ private onOpenHandler?;
34
+ private globalHandlersInitialized;
35
+ constructor(appOrHttpServer?: INestApplicationContext | object);
36
+ create(_port: number, options?: BunWsAdapterOptions): BunPreflightHttpServer;
37
+ private extractWsOptions;
38
+ close(server: BunPreflightHttpServer): Promise<void>;
39
+ bindClientConnect(server: BunPreflightHttpServer, callback: (client: BunWsClient) => void): void;
40
+ bindClientDisconnect(client: BunWsClient, callback: (client: BunWsClient) => void): void;
41
+ bindMessageHandlers(client: BunWsClient, handlers: MessageMappingProperties[], transform: (data: unknown) => Observable<unknown>): void;
42
+ private initializeGlobalHandlers;
43
+ private buildHandlerMap;
44
+ private processMessage;
45
+ private sendResponse;
46
+ private cleanupClient;
47
+ }
48
+ export {};
package/dist/index.d.ts CHANGED
@@ -1,4 +1,7 @@
1
- export { BunAdapter } from './bun.adapter.js';
2
- export { BunRequest } from './bun.request.js';
3
- export { BunResponse } from './bun.response.js';
4
- export { BunFileInterceptor } from './bun.file.interceptor.js';
1
+ export * from './bun.adapter.js';
2
+ export * from './bun.request.js';
3
+ export * from './bun.response.js';
4
+ export * from './bun.file.interceptor.js';
5
+ export * from './internal.types.js';
6
+ export * from './bun.preflight-http-server.js';
7
+ export * from './bun.ws-adapter.js';