@krisanalfa/bunest-adapter 0.3.0 → 0.5.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 +156 -18
- package/dist/bun.adapter.d.ts +8 -67
- package/dist/bun.internal.types.d.ts +53 -0
- package/dist/bun.preflight-http-server.d.ts +8 -26
- package/dist/bun.request.d.ts +56 -7
- package/dist/bun.response.d.ts +35 -0
- package/dist/bun.server-instance.d.ts +137 -0
- package/dist/bun.ws-adapter.d.ts +1 -11
- package/dist/index.d.ts +2 -1
- package/dist/index.js +517 -324
- package/dist/index.js.map +11 -10
- package/package.json +17 -7
- package/dist/internal.types.d.ts +0 -8
package/README.md
CHANGED
|
@@ -15,10 +15,12 @@ This project provides a native Bun adapter for NestJS, allowing developers to le
|
|
|
15
15
|
- [Validation](#validation)
|
|
16
16
|
- [File Uploads](#file-uploads)
|
|
17
17
|
- [Streaming Responses](#streaming-responses)
|
|
18
|
+
- [Server-Sent Events (SSE)](#server-sent-events-sse)
|
|
18
19
|
- [Versioning](#versioning)
|
|
19
20
|
- [CORS](#cors)
|
|
20
21
|
- [Cookies](#cookies)
|
|
21
22
|
- [Popular Express Middleware](#popular-express-middleware)
|
|
23
|
+
- [Static Assets](#static-assets)
|
|
22
24
|
- [Bun File API Support](#bun-file-api-support)
|
|
23
25
|
- [BunFileInterceptor](#bunfileinterceptor)
|
|
24
26
|
- [WebSocket Support](#websocket-support)
|
|
@@ -50,7 +52,7 @@ This project provides a native Bun adapter for NestJS, allowing developers to le
|
|
|
50
52
|
Easy to set up and use Bun as the underlying HTTP server for your NestJS applications.
|
|
51
53
|
|
|
52
54
|
```ts
|
|
53
|
-
import { BunAdapter } from "@krisanalfa/bunest-adapter";
|
|
55
|
+
import { BunAdapter, NestBunApplication } from "@krisanalfa/bunest-adapter";
|
|
54
56
|
import { Logger } from "@nestjs/common";
|
|
55
57
|
import { NestFactory } from "@nestjs/core";
|
|
56
58
|
import { Server } from "bun";
|
|
@@ -58,10 +60,10 @@ import { Server } from "bun";
|
|
|
58
60
|
import { AppModule } from "./app.module.js";
|
|
59
61
|
|
|
60
62
|
async function main() {
|
|
61
|
-
const app = await NestFactory.create(AppModule, new BunAdapter());
|
|
63
|
+
const app = await NestFactory.create<NestBunApplication>(AppModule, new BunAdapter());
|
|
62
64
|
await app.listen(3000);
|
|
63
|
-
const server = app.
|
|
64
|
-
Logger.log(`Server started on ${server
|
|
65
|
+
const server = app.getHttpServer().getBunServer()
|
|
66
|
+
Logger.log(`Server started on ${server?.url.toString() ?? 'http://localhost:3000'}`, 'NestApplication')
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
await main();
|
|
@@ -375,6 +377,67 @@ class FilesController {
|
|
|
375
377
|
}
|
|
376
378
|
```
|
|
377
379
|
|
|
380
|
+
#### Server-Sent Events (SSE)
|
|
381
|
+
|
|
382
|
+
Full support for [Server-Sent Events](https://docs.nestjs.com/techniques/server-sent-events) using the `@Sse()` decorator. SSE allows servers to push real-time updates to clients over HTTP:
|
|
383
|
+
|
|
384
|
+
```ts
|
|
385
|
+
import { Controller, Sse, MessageEvent } from '@nestjs/common';
|
|
386
|
+
import { Observable, interval, map } from 'rxjs';
|
|
387
|
+
|
|
388
|
+
@Controller()
|
|
389
|
+
class EventsController {
|
|
390
|
+
@Sse('/sse')
|
|
391
|
+
sendEvents(): Observable<MessageEvent> {
|
|
392
|
+
return interval(1000).pipe(
|
|
393
|
+
map(num => ({
|
|
394
|
+
data: `SSE message ${num.toString()}`,
|
|
395
|
+
})),
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
**Client Connection Example:**
|
|
402
|
+
|
|
403
|
+
```ts
|
|
404
|
+
const eventSource = new EventSource('http://localhost:3000/sse');
|
|
405
|
+
|
|
406
|
+
eventSource.onopen = () => {
|
|
407
|
+
console.log('SSE connection opened');
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
eventSource.onmessage = (event) => {
|
|
411
|
+
console.log('Received:', event.data); // "SSE message 0", "SSE message 1", etc.
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
eventSource.onerror = (error) => {
|
|
415
|
+
console.error('SSE error:', error);
|
|
416
|
+
eventSource.close();
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// Close the connection when done
|
|
420
|
+
// eventSource.close();
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**For HTTPS/Secure Connections:**
|
|
424
|
+
|
|
425
|
+
```ts
|
|
426
|
+
import { EventSource } from 'eventsource'; // npm package for Node.js
|
|
427
|
+
|
|
428
|
+
const eventSource = new EventSource('https://localhost:3000/sse', {
|
|
429
|
+
fetch: (url, init) => fetch(url, {
|
|
430
|
+
...init,
|
|
431
|
+
tls: { rejectUnauthorized: false }, // For self-signed certificates
|
|
432
|
+
}),
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
eventSource.onmessage = (event) => {
|
|
436
|
+
console.log('Received:', event.data);
|
|
437
|
+
};
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
|
|
378
441
|
#### Versioning
|
|
379
442
|
|
|
380
443
|
Full API [versioning](https://docs.nestjs.com/techniques/versioning) support (URI, Header, Media Type, Custom):
|
|
@@ -433,7 +496,7 @@ app.enableCors((req: BunRequest, callback) => {
|
|
|
433
496
|
You can also use NestJS's `CorsOptions` type for static configuration.
|
|
434
497
|
|
|
435
498
|
```ts
|
|
436
|
-
const app = await NestFactory.create(AppModule, new BunAdapter(), {
|
|
499
|
+
const app = await NestFactory.create<NestBunApplication>(AppModule, new BunAdapter(), {
|
|
437
500
|
cors: {
|
|
438
501
|
origin: "https://example.com",
|
|
439
502
|
methods: ["GET", "POST", "PUT"],
|
|
@@ -473,7 +536,7 @@ Compatible with popular Express middleware:
|
|
|
473
536
|
```ts
|
|
474
537
|
import helmet from "helmet";
|
|
475
538
|
|
|
476
|
-
const app = await NestFactory.create(AppModule, new BunAdapter());
|
|
539
|
+
const app = await NestFactory.create<NestBunApplication>(AppModule, new BunAdapter());
|
|
477
540
|
app.use(helmet());
|
|
478
541
|
```
|
|
479
542
|
|
|
@@ -481,7 +544,65 @@ Tested and working with:
|
|
|
481
544
|
|
|
482
545
|
- `helmet` - Security headers
|
|
483
546
|
- `cors` - CORS handling
|
|
484
|
-
-
|
|
547
|
+
- `@thallesp/nestjs-better-auth` - `better-auth` middleware
|
|
548
|
+
- `express-session` - Session management
|
|
549
|
+
|
|
550
|
+
#### Static Assets
|
|
551
|
+
|
|
552
|
+
Serve static files from your NestJS application using Bun's native file serving capabilities. The adapter supports two distinct modes:
|
|
553
|
+
|
|
554
|
+
**File Routes (Default)** - Reads files from the filesystem on each request, supports range requests, respects middlewares, and provides full HTTP feature compatibility:
|
|
555
|
+
|
|
556
|
+
```ts
|
|
557
|
+
import { join } from 'path';
|
|
558
|
+
|
|
559
|
+
const app = await NestFactory.create<NestBunApplication>(AppModule, new BunAdapter());
|
|
560
|
+
|
|
561
|
+
// Serve static assets using file routes (default)
|
|
562
|
+
app.useStaticAssets(join(__dirname, 'public'));
|
|
563
|
+
|
|
564
|
+
// Or explicitly set useStatic to false
|
|
565
|
+
app.useStaticAssets(join(__dirname, 'public'), { useStatic: false });
|
|
566
|
+
|
|
567
|
+
await app.listen(3000);
|
|
568
|
+
|
|
569
|
+
// Files in 'public' directory are now accessible:
|
|
570
|
+
// public/index.html -> http://localhost:3000/index.html
|
|
571
|
+
// public/css/style.css -> http://localhost:3000/css/style.css
|
|
572
|
+
// public/images/logo.png -> http://localhost:3000/images/logo.png
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
> Note: Even if the file routes read the files from the filesystem on each request, you still need to make sure that Bun has access to the files right before you call `app.listen()`. The adapter reads the directory structure and prepares the routes at that time. If you need different behavior (e.g., dynamic files), consider using a custom controller to serve those files.
|
|
576
|
+
|
|
577
|
+
**Static Routes** - Serves files directly from memory for maximum performance, but with some limitations (no range requests, doesn't respect middlewares):
|
|
578
|
+
|
|
579
|
+
```ts
|
|
580
|
+
import { join } from 'path';
|
|
581
|
+
|
|
582
|
+
const app = await NestFactory.create<NestBunApplication>(AppModule, new BunAdapter());
|
|
583
|
+
|
|
584
|
+
// Serve static assets using static routes (faster, but with limitations)
|
|
585
|
+
app.useStaticAssets(join(__dirname, 'public'), { useStatic: true });
|
|
586
|
+
|
|
587
|
+
await app.listen(3000);
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
**With CORS Support:**
|
|
591
|
+
|
|
592
|
+
```ts
|
|
593
|
+
const app = await NestFactory.create<NestBunApplication>(
|
|
594
|
+
AppModule,
|
|
595
|
+
new BunAdapter(),
|
|
596
|
+
{ cors: true }
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
// Static assets will respect CORS settings when using file routes
|
|
600
|
+
app.useStaticAssets(join(__dirname, 'public'), { useStatic: false });
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
**Choosing Between Modes:**
|
|
604
|
+
|
|
605
|
+
For more details, see [Bun's documentation on file responses vs static responses](https://bun.sh/docs/runtime/http/routing#file-responses-vs-static-responses).
|
|
485
606
|
|
|
486
607
|
### WebSocket Support
|
|
487
608
|
|
|
@@ -523,7 +644,7 @@ class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
|
|
|
523
644
|
}
|
|
524
645
|
|
|
525
646
|
// Enable WebSocket support in your application
|
|
526
|
-
const app = await NestFactory.create(AppModule, new BunAdapter());
|
|
647
|
+
const app = await NestFactory.create<NestBunApplication>(AppModule, new BunAdapter());
|
|
527
648
|
app.useWebSocketAdapter(new BunWsAdapter(app));
|
|
528
649
|
await app.listen(3000);
|
|
529
650
|
```
|
|
@@ -715,10 +836,10 @@ The Bun adapter supports secure WebSocket connections (WSS) using TLS/SSL certif
|
|
|
715
836
|
**Using BunAdapter constructor options:**
|
|
716
837
|
|
|
717
838
|
```ts
|
|
718
|
-
import { BunAdapter, BunWsAdapter } from "@krisanalfa/bunest-adapter";
|
|
839
|
+
import { BunAdapter, BunWsAdapter, NestBunApplication } from "@krisanalfa/bunest-adapter";
|
|
719
840
|
import { NestFactory } from "@nestjs/core";
|
|
720
841
|
|
|
721
|
-
const app = await NestFactory.create(
|
|
842
|
+
const app = await NestFactory.create<NestBunApplication>(
|
|
722
843
|
AppModule,
|
|
723
844
|
new BunAdapter({
|
|
724
845
|
tls: {
|
|
@@ -738,10 +859,10 @@ await app.listen(3000);
|
|
|
738
859
|
**Using NestFactory.create httpsOptions:**
|
|
739
860
|
|
|
740
861
|
```ts
|
|
741
|
-
import { BunAdapter, BunWsAdapter } from "@krisanalfa/bunest-adapter";
|
|
862
|
+
import { BunAdapter, BunWsAdapter, NestBunApplication } from "@krisanalfa/bunest-adapter";
|
|
742
863
|
import { NestFactory } from "@nestjs/core";
|
|
743
864
|
|
|
744
|
-
const app = await NestFactory.create(AppModule, new BunAdapter(), {
|
|
865
|
+
const app = await NestFactory.create<NestBunApplication>(AppModule, new BunAdapter(), {
|
|
745
866
|
httpsOptions: {
|
|
746
867
|
cert: Bun.file("/path/to/cert.pem"),
|
|
747
868
|
key: Bun.file("/path/to/key.pem"),
|
|
@@ -757,10 +878,10 @@ await app.listen(3000);
|
|
|
757
878
|
You can also run secure WebSocket servers over Unix sockets:
|
|
758
879
|
|
|
759
880
|
```ts
|
|
760
|
-
import { BunAdapter, BunWsAdapter } from "@krisanalfa/bunest-adapter";
|
|
881
|
+
import { BunAdapter, BunWsAdapter, NestBunApplication } from "@krisanalfa/bunest-adapter";
|
|
761
882
|
import { NestFactory } from "@nestjs/core";
|
|
762
883
|
|
|
763
|
-
const app = await NestFactory.create(
|
|
884
|
+
const app = await NestFactory.create<NestBunApplication>(
|
|
764
885
|
AppModule,
|
|
765
886
|
new BunAdapter({
|
|
766
887
|
tls: {
|
|
@@ -815,7 +936,7 @@ class ChatGateway {
|
|
|
815
936
|
}
|
|
816
937
|
|
|
817
938
|
// WebSocket will be available on the same port as HTTP
|
|
818
|
-
const app = await NestFactory.create(AppModule, new BunAdapter());
|
|
939
|
+
const app = await NestFactory.create<NestBunApplication>(AppModule, new BunAdapter());
|
|
819
940
|
app.useWebSocketAdapter(new BunWsAdapter(app));
|
|
820
941
|
await app.listen(3000); // Both HTTP and WebSocket use port 3000
|
|
821
942
|
```
|
|
@@ -881,7 +1002,7 @@ You can run your NestJS application with HTTPS using two approaches:
|
|
|
881
1002
|
#### Using Bun's built-in HTTPS support (recommended)
|
|
882
1003
|
|
|
883
1004
|
```ts
|
|
884
|
-
const app = await NestFactory.create(
|
|
1005
|
+
const app = await NestFactory.create<NestBunApplication>(
|
|
885
1006
|
AppModule,
|
|
886
1007
|
new BunAdapter({
|
|
887
1008
|
tls: {
|
|
@@ -895,7 +1016,7 @@ const app = await NestFactory.create(
|
|
|
895
1016
|
#### Using NestJS App Factory HTTPS options
|
|
896
1017
|
|
|
897
1018
|
```ts
|
|
898
|
-
const app = await NestFactory.create(
|
|
1019
|
+
const app = await NestFactory.create<NestBunApplication>(
|
|
899
1020
|
AppModule,
|
|
900
1021
|
new BunAdapter(/* leave it empty */),
|
|
901
1022
|
{
|
|
@@ -1251,6 +1372,23 @@ oha -c 125 -n 1000000 --no-tui "http://127.0.0.1:3000/"
|
|
|
1251
1372
|
|
|
1252
1373
|
> **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**.
|
|
1253
1374
|
|
|
1375
|
+
Bonus if you use Unix sockets:
|
|
1376
|
+
```
|
|
1377
|
+
Summary:
|
|
1378
|
+
Success rate: 100.00%
|
|
1379
|
+
Total: 8298.2243 ms
|
|
1380
|
+
Slowest: 5.2326 ms
|
|
1381
|
+
Fastest: 0.2857 ms
|
|
1382
|
+
Average: 1.0361 ms
|
|
1383
|
+
Requests/sec: 120507.7092
|
|
1384
|
+
|
|
1385
|
+
Total data: 21.93 MiB
|
|
1386
|
+
Size/request: 23 B
|
|
1387
|
+
Size/sec: 2.64 MiB
|
|
1388
|
+
```
|
|
1389
|
+
|
|
1390
|
+
As you can see, using Unix sockets boosts the performance further to **120,508 req/s**, which is **~1.5x faster** than TCP. Since Bun `fetch` supports Unix sockets, you can leverage this for inter-process communication on the same machine.
|
|
1391
|
+
|
|
1254
1392
|
### WebSocket Benchmark
|
|
1255
1393
|
|
|
1256
1394
|
WebSocket benchmarks run using the custom benchmark script in `benchmarks/ws.benchmark.ts`.
|
|
@@ -1321,7 +1459,7 @@ Contributions are welcome! Please open issues or submit pull requests for bug fi
|
|
|
1321
1459
|
## Future Plans
|
|
1322
1460
|
|
|
1323
1461
|
- Enhanced trusted proxy configuration for host header handling
|
|
1324
|
-
-
|
|
1462
|
+
- Improved documentation and examples
|
|
1325
1463
|
- Release automation via CI/CD pipelines
|
|
1326
1464
|
|
|
1327
1465
|
## License
|
package/dist/bun.adapter.d.ts
CHANGED
|
@@ -1,64 +1,20 @@
|
|
|
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';
|
|
4
3
|
import { NestApplicationOptions, RequestMethod, VersioningOptions } from '@nestjs/common';
|
|
4
|
+
import { 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';
|
|
7
|
+
import { BunStaticAssetsOptions, BunWsClientData, ServerOptions } from './bun.internal.types.js';
|
|
8
8
|
import { BunPreflightHttpServer } from './bun.preflight-http-server.js';
|
|
9
9
|
import { BunRequest } from './bun.request.js';
|
|
10
10
|
import { BunResponse } from './bun.response.js';
|
|
11
|
+
import { BunServerInstance } from './bun.server-instance.js';
|
|
11
12
|
export declare class BunAdapter extends AbstractHttpAdapter<Server<unknown>, BunRequest, BunResponse> {
|
|
12
|
-
protected bunServeOptions: ServerOptions
|
|
13
|
+
protected bunServeOptions: ServerOptions<BunWsClientData>;
|
|
13
14
|
private readonly logger;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
private readonly routeHandlers;
|
|
18
|
-
private notFoundHandler;
|
|
19
|
-
private readonly wsHandlers;
|
|
20
|
-
private readonly wsMiddlewareEngine;
|
|
21
|
-
private wsOptions;
|
|
22
|
-
private useWs;
|
|
23
|
-
private useWsCors;
|
|
24
|
-
private wsCorsHeaders?;
|
|
25
|
-
constructor(bunServeOptions?: ServerOptions);
|
|
26
|
-
use(middleware: RequestHandler<BunRequest, BunResponse>): void;
|
|
27
|
-
private createHttpMethodHandler;
|
|
28
|
-
get(handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
29
|
-
get(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
30
|
-
post(handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
31
|
-
post(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
32
|
-
put(handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
33
|
-
put(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
34
|
-
patch(handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
35
|
-
patch(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
36
|
-
delete(handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
37
|
-
delete(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
38
|
-
head(handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
39
|
-
head(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
40
|
-
options(handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
41
|
-
options(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
42
|
-
private createUnsupportedMethod;
|
|
43
|
-
all(handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
44
|
-
all(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
45
|
-
propfind(handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
46
|
-
propfind(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
47
|
-
proppatch(handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
48
|
-
proppatch(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
49
|
-
mkcol(handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
50
|
-
mkcol(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
51
|
-
copy(handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
52
|
-
copy(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
53
|
-
move(handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
54
|
-
move(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
55
|
-
lock(handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
56
|
-
lock(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
57
|
-
unlock(handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
58
|
-
unlock(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
59
|
-
search(handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
60
|
-
search(path: unknown, handler: RequestHandler<BunRequest, BunResponse>): void;
|
|
61
|
-
useStaticAssets(...args: unknown[]): void;
|
|
15
|
+
protected instance: BunServerInstance;
|
|
16
|
+
constructor(bunServeOptions?: ServerOptions<BunWsClientData>);
|
|
17
|
+
useStaticAssets(path: string, options?: BunStaticAssetsOptions): void;
|
|
62
18
|
setViewEngine(engine: string): void;
|
|
63
19
|
render(response: unknown, view: string, options: unknown): void;
|
|
64
20
|
close(): Promise<void>;
|
|
@@ -94,20 +50,5 @@ export declare class BunAdapter extends AbstractHttpAdapter<Server<unknown>, Bun
|
|
|
94
50
|
* @param callback Optional callback to invoke once the server is listening.
|
|
95
51
|
*/
|
|
96
52
|
listen(port: string | number, hostname: string, callback?: () => void): void;
|
|
97
|
-
private
|
|
98
|
-
private static omitKeys;
|
|
99
|
-
private isWebSocketUpgradeRequest;
|
|
100
|
-
private handleWebSocketCors;
|
|
101
|
-
private upgradeWebSocket;
|
|
102
|
-
private setupWebSocketIfNeeded;
|
|
103
|
-
private createServer;
|
|
104
|
-
private delegateRouteHandler;
|
|
105
|
-
private ensureRouteExists;
|
|
106
|
-
private prepareRequestHandler;
|
|
107
|
-
private createRouteFetchHandler;
|
|
108
|
-
private createVersioningHandlers;
|
|
109
|
-
private executeHandlerChain;
|
|
110
|
-
private createChainedHandlerForVersioningResolution;
|
|
111
|
-
private mapRequestMethodToString;
|
|
112
|
-
private parseRouteHandler;
|
|
53
|
+
private configureTls;
|
|
113
54
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { CorsOptions, CorsOptionsDelegate } from '@nestjs/common/interfaces/external/cors-options.interface.js';
|
|
2
|
+
import { Serve, Server, ServerWebSocket, WebSocketHandler } from 'bun';
|
|
3
|
+
import { INestApplication } from '@nestjs/common';
|
|
4
|
+
import { BunPreflightHttpServer } from './bun.preflight-http-server.js';
|
|
5
|
+
import { BunRequest } from './bun.request.js';
|
|
6
|
+
export interface WsOptions extends Pick<WebSocketHandler<unknown>, 'maxPayloadLength' | 'idleTimeout' | 'backpressureLimit' | 'closeOnBackpressureLimit' | 'sendPings' | 'publishToSelf' | 'perMessageDeflate'> {
|
|
7
|
+
cors?: true | CorsOptions | CorsOptionsDelegate<BunRequest>;
|
|
8
|
+
clientDataFactory?: (req: BunRequest) => unknown;
|
|
9
|
+
}
|
|
10
|
+
export type ServerOptions<TWebSocketData = unknown> = Pick<Serve.Options<TWebSocketData>, 'development' | 'maxRequestBodySize' | 'idleTimeout' | 'id' | 'tls' | 'websocket' | 'port' | 'hostname'>;
|
|
11
|
+
export type WsData = string | Buffer | ArrayBuffer | Buffer[];
|
|
12
|
+
export interface WsHandlers {
|
|
13
|
+
onOpen: ((ws: ServerWebSocket<unknown>) => void) | undefined;
|
|
14
|
+
onMessage: ((ws: ServerWebSocket<unknown>, message: WsData, server: Server<unknown>) => void) | undefined;
|
|
15
|
+
onClose: ((ws: ServerWebSocket<unknown>, code: number, reason: string) => void) | undefined;
|
|
16
|
+
}
|
|
17
|
+
export 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
|
+
export interface BunStaticAssetsOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Enable static assets serving.
|
|
28
|
+
*
|
|
29
|
+
* Bun has two distict modes for serving static assets:
|
|
30
|
+
* 1. Static routes
|
|
31
|
+
* 2. File routes
|
|
32
|
+
*
|
|
33
|
+
* If you set `useStatic: true`, Bun will use static routes for serving assets.
|
|
34
|
+
* This approach is generally faster for serving static files, as it serves
|
|
35
|
+
* files directly from memory. However, it comes with some limitations, such as
|
|
36
|
+
* lack of support for certain features like range requests and directory indexing.
|
|
37
|
+
* On top of that, static routes didn't respect middlewares due to Bun's internal design.
|
|
38
|
+
*
|
|
39
|
+
* On the other hand, if you set `useStatic: false` (the default behavior),
|
|
40
|
+
* Bun will use file routes, which read files from the filesystem on each request.
|
|
41
|
+
* This method supports a wider range of features, including range requests, and respects
|
|
42
|
+
* middlewares. However, it may be slightly slower than static routes due to
|
|
43
|
+
* filesystem access on each request.
|
|
44
|
+
*
|
|
45
|
+
* @see https://bun.com/docs/runtime/http/routing#file-responses-vs-static-responses
|
|
46
|
+
* @defaults false Use file routes by default.
|
|
47
|
+
*/
|
|
48
|
+
useStatic?: boolean;
|
|
49
|
+
}
|
|
50
|
+
export interface NestBunApplication extends INestApplication<BunPreflightHttpServer> {
|
|
51
|
+
useStaticAssets(path: string, options?: BunStaticAssetsOptions): void;
|
|
52
|
+
enableCors(options?: CorsOptions | CorsOptionsDelegate<BunRequest>): void;
|
|
53
|
+
}
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
import { Server, ServerWebSocket } from 'bun';
|
|
2
2
|
import { BaseWsInstance } from '@nestjs/websockets';
|
|
3
|
-
import {
|
|
4
|
-
|
|
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
|
-
}
|
|
3
|
+
import { WsData, WsOptions } from './bun.internal.types.js';
|
|
4
|
+
import { BunServerInstance } from './bun.server-instance.js';
|
|
14
5
|
/**
|
|
15
6
|
* Bun HTTP server placeholder used before the actual server instance is created.
|
|
16
7
|
* This class provides compatibility methods expected by NestJS framework.
|
|
@@ -20,8 +11,8 @@ interface BunHttpAdapter {
|
|
|
20
11
|
* the server creation until NestJS calls the listen method.
|
|
21
12
|
*/
|
|
22
13
|
export declare class BunPreflightHttpServer implements BaseWsInstance {
|
|
23
|
-
private readonly
|
|
24
|
-
constructor(
|
|
14
|
+
private readonly serverInstance;
|
|
15
|
+
constructor(serverInstance: BunServerInstance);
|
|
25
16
|
on(event: string, callback: Function): void;
|
|
26
17
|
/**
|
|
27
18
|
* NestJS compatibility methods
|
|
@@ -38,25 +29,16 @@ export declare class BunPreflightHttpServer implements BaseWsInstance {
|
|
|
38
29
|
*/
|
|
39
30
|
stop(force?: boolean): Promise<void>;
|
|
40
31
|
address(): {
|
|
41
|
-
address:
|
|
42
|
-
port:
|
|
43
|
-
} | {
|
|
44
|
-
address: string | undefined;
|
|
45
|
-
port: number | undefined;
|
|
32
|
+
address: string;
|
|
33
|
+
port: number;
|
|
46
34
|
};
|
|
47
35
|
setWsOptions(options: WsOptions): void;
|
|
48
36
|
registerWsOpenHandler(handler: (ws: ServerWebSocket<unknown>) => void): void;
|
|
49
|
-
registerWsMessageHandler(handler: (ws: ServerWebSocket<unknown>, message:
|
|
37
|
+
registerWsMessageHandler(handler: (ws: ServerWebSocket<unknown>, message: WsData, server: Server<unknown>) => void): void;
|
|
50
38
|
registerWsCloseHandler(handler: (ws: ServerWebSocket<unknown>, code: number, reason: string) => void): void;
|
|
51
|
-
|
|
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>;
|
|
39
|
+
getBunServer(): Server<unknown> | null;
|
|
57
40
|
/**
|
|
58
41
|
* Proxy method for WebSocket server close
|
|
59
42
|
*/
|
|
60
43
|
close(): Promise<void>;
|
|
61
44
|
}
|
|
62
|
-
export {};
|
package/dist/bun.request.d.ts
CHANGED
|
@@ -3,6 +3,15 @@ import { ParsedQs } from 'qs';
|
|
|
3
3
|
type HeadersProxy = Record<string, string> & {
|
|
4
4
|
get: (key: string) => string | null;
|
|
5
5
|
};
|
|
6
|
+
interface Connection {
|
|
7
|
+
encrypted: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface TcpSocket {
|
|
10
|
+
encrypted: boolean;
|
|
11
|
+
setKeepAlive: (enable?: boolean, initialDelay?: number) => void;
|
|
12
|
+
setNoDelay: (noDelay?: boolean) => void;
|
|
13
|
+
setTimeout: (timeout: number, callback?: () => void) => void;
|
|
14
|
+
}
|
|
6
15
|
/**
|
|
7
16
|
* A high-performance request wrapper for Bun's native request object.
|
|
8
17
|
* Provides lazy parsing and caching for optimal performance in NestJS applications.
|
|
@@ -26,20 +35,23 @@ export declare class BunRequest {
|
|
|
26
35
|
private _file;
|
|
27
36
|
private _files;
|
|
28
37
|
private _settings;
|
|
29
|
-
private
|
|
38
|
+
private _connection;
|
|
39
|
+
private _socket;
|
|
40
|
+
private _url;
|
|
30
41
|
private readonly _parsedUrl;
|
|
31
42
|
readonly method: string;
|
|
32
43
|
readonly params: Record<string, string>;
|
|
33
44
|
constructor(nativeRequest: NativeRequest);
|
|
45
|
+
/**
|
|
46
|
+
* Gets a mock connection object for compatibility with Node.js middleware.
|
|
47
|
+
* Some middleware (like express-session) check req.connection.encrypted to determine if the connection is HTTPS.
|
|
48
|
+
*/
|
|
49
|
+
get connection(): Connection;
|
|
34
50
|
/**
|
|
35
51
|
* Gets a mock socket object for compatibility with Node.js middleware.
|
|
36
52
|
* Some middleware (like Better Auth) check req.socket.encrypted to determine if the connection is HTTPS.
|
|
37
|
-
*
|
|
38
|
-
* @returns A mock socket object with encrypted property
|
|
39
53
|
*/
|
|
40
|
-
get socket():
|
|
41
|
-
encrypted: boolean;
|
|
42
|
-
};
|
|
54
|
+
get socket(): TcpSocket;
|
|
43
55
|
/**
|
|
44
56
|
* Gets the URL path and query string of the request.
|
|
45
57
|
* Returns the pathname + search params for Node.js/Express compatibility.
|
|
@@ -235,7 +247,7 @@ export declare class BunRequest {
|
|
|
235
247
|
* console.log(user); // { id: 1, name: 'John' }
|
|
236
248
|
* ```
|
|
237
249
|
*/
|
|
238
|
-
get(key: string):
|
|
250
|
+
get<T>(key: string): T | undefined;
|
|
239
251
|
/**
|
|
240
252
|
* Sets a custom setting/property in the request.
|
|
241
253
|
* Useful for passing data between middleware and handlers.
|
|
@@ -358,5 +370,42 @@ export declare class BunRequest {
|
|
|
358
370
|
* ```
|
|
359
371
|
*/
|
|
360
372
|
clone(): BunRequest;
|
|
373
|
+
/**
|
|
374
|
+
* Stub method for Node.js EventEmitter compatibility.
|
|
375
|
+
* This is a no-op method provided for compatibility with Node.js HTTP request objects.
|
|
376
|
+
* Required for SSE and other streaming scenarios where NestJS listens for request events.
|
|
377
|
+
*
|
|
378
|
+
* @param event - The event name
|
|
379
|
+
* @param listener - The event listener function
|
|
380
|
+
* @returns This request object for chaining
|
|
381
|
+
*/
|
|
382
|
+
on(event: string, listener: (...args: unknown[]) => void): this;
|
|
383
|
+
/**
|
|
384
|
+
* Stub method for Node.js EventEmitter compatibility.
|
|
385
|
+
* This is a no-op method provided for compatibility with Node.js HTTP request objects.
|
|
386
|
+
*
|
|
387
|
+
* @param event - The event name
|
|
388
|
+
* @param listener - The event listener function
|
|
389
|
+
* @returns This request object for chaining
|
|
390
|
+
*/
|
|
391
|
+
once(event: string, listener: (...args: unknown[]) => void): this;
|
|
392
|
+
/**
|
|
393
|
+
* Stub method for Node.js EventEmitter compatibility.
|
|
394
|
+
* This is a no-op method provided for compatibility with Node.js HTTP request objects.
|
|
395
|
+
*
|
|
396
|
+
* @param event - The event name
|
|
397
|
+
* @param listener - The event listener function
|
|
398
|
+
* @returns This request object for chaining
|
|
399
|
+
*/
|
|
400
|
+
off(event: string, listener: (...args: unknown[]) => void): this;
|
|
401
|
+
/**
|
|
402
|
+
* Stub method for Node.js EventEmitter compatibility.
|
|
403
|
+
* This is a no-op method provided for compatibility with Node.js HTTP request objects.
|
|
404
|
+
*
|
|
405
|
+
* @param event - The event name
|
|
406
|
+
* @param args - Event arguments
|
|
407
|
+
* @returns True to indicate the event was handled
|
|
408
|
+
*/
|
|
409
|
+
emit(event: string, ...args: unknown[]): boolean;
|
|
361
410
|
}
|
|
362
411
|
export {};
|
package/dist/bun.response.d.ts
CHANGED
|
@@ -18,11 +18,18 @@ export declare class BunResponse {
|
|
|
18
18
|
private readonly response;
|
|
19
19
|
private readonly cookieMap;
|
|
20
20
|
private static readonly textDecoder;
|
|
21
|
+
/**
|
|
22
|
+
* Property for Node.js Writable stream compatibility.
|
|
23
|
+
* Indicates this object can be written to.
|
|
24
|
+
*/
|
|
25
|
+
readonly writable = true;
|
|
21
26
|
private headers;
|
|
22
27
|
private statusCode;
|
|
23
28
|
private ended;
|
|
24
29
|
private cookieHeaders;
|
|
25
30
|
private chunks;
|
|
31
|
+
private streamWriter;
|
|
32
|
+
private textEncoder;
|
|
26
33
|
constructor();
|
|
27
34
|
private get headersMap();
|
|
28
35
|
/**
|
|
@@ -205,6 +212,21 @@ export declare class BunResponse {
|
|
|
205
212
|
* @returns This response object for chaining
|
|
206
213
|
*/
|
|
207
214
|
once(event: string, listener: (...args: unknown[]) => void): this;
|
|
215
|
+
/**
|
|
216
|
+
* Stub method for Node.js EventEmitter compatibility.
|
|
217
|
+
* This is a no-op method provided for compatibility with Node.js streams and HTTP response objects.
|
|
218
|
+
* Required when streams are piped to the response object (e.g., for SSE).
|
|
219
|
+
*
|
|
220
|
+
* @param event - The event name
|
|
221
|
+
* @param args - Event arguments
|
|
222
|
+
* @returns True to indicate the event was handled
|
|
223
|
+
*/
|
|
224
|
+
emit(event: string, ...args: unknown[]): boolean;
|
|
225
|
+
/**
|
|
226
|
+
* Property for Node.js Writable stream compatibility.
|
|
227
|
+
* Indicates whether the stream has ended.
|
|
228
|
+
*/
|
|
229
|
+
get writableEnded(): boolean;
|
|
208
230
|
/**
|
|
209
231
|
* Stub method for Node.js HTTP response compatibility.
|
|
210
232
|
* This method writes data to the response stream.
|
|
@@ -234,6 +256,12 @@ export declare class BunResponse {
|
|
|
234
256
|
* ```
|
|
235
257
|
*/
|
|
236
258
|
write(chunk: unknown): boolean;
|
|
259
|
+
/**
|
|
260
|
+
* Initializes streaming mode by creating a TransformStream and resolving the response.
|
|
261
|
+
* This is used for SSE and other streaming scenarios where data needs to be sent
|
|
262
|
+
* before end() is called.
|
|
263
|
+
*/
|
|
264
|
+
private initializeStreamingMode;
|
|
237
265
|
/**
|
|
238
266
|
* Gets the value of a response header.
|
|
239
267
|
* Header lookup is case-insensitive.
|
|
@@ -345,6 +373,13 @@ export declare class BunResponse {
|
|
|
345
373
|
* ```
|
|
346
374
|
*/
|
|
347
375
|
isEnded(): boolean;
|
|
376
|
+
/**
|
|
377
|
+
* Stub method for Node.js HTTP response compatibility.
|
|
378
|
+
*
|
|
379
|
+
* The primary purpose of _implicitHeader() is to automatically generate and send the HTTP headers if a write operation (like `response.write()` or `response.end()`)
|
|
380
|
+
* is called without explicitly calling `response.writeHead()` first.
|
|
381
|
+
*/
|
|
382
|
+
_implicitHeader(): void;
|
|
348
383
|
private buildStreamableResponse;
|
|
349
384
|
private buildJsonResponse;
|
|
350
385
|
private createResponse;
|