@krisanalfa/bunest-adapter 0.4.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 CHANGED
@@ -20,6 +20,7 @@ This project provides a native Bun adapter for NestJS, allowing developers to le
20
20
  - [CORS](#cors)
21
21
  - [Cookies](#cookies)
22
22
  - [Popular Express Middleware](#popular-express-middleware)
23
+ - [Static Assets](#static-assets)
23
24
  - [Bun File API Support](#bun-file-api-support)
24
25
  - [BunFileInterceptor](#bunfileinterceptor)
25
26
  - [WebSocket Support](#websocket-support)
@@ -51,7 +52,7 @@ This project provides a native Bun adapter for NestJS, allowing developers to le
51
52
  Easy to set up and use Bun as the underlying HTTP server for your NestJS applications.
52
53
 
53
54
  ```ts
54
- import { BunAdapter } from "@krisanalfa/bunest-adapter";
55
+ import { BunAdapter, NestBunApplication } from "@krisanalfa/bunest-adapter";
55
56
  import { Logger } from "@nestjs/common";
56
57
  import { NestFactory } from "@nestjs/core";
57
58
  import { Server } from "bun";
@@ -59,10 +60,10 @@ import { Server } from "bun";
59
60
  import { AppModule } from "./app.module.js";
60
61
 
61
62
  async function main() {
62
- const app = await NestFactory.create(AppModule, new BunAdapter());
63
+ const app = await NestFactory.create<NestBunApplication>(AppModule, new BunAdapter());
63
64
  await app.listen(3000);
64
- const server = app.getHttpAdapter().getHttpServer() as Server<unknown>;
65
- Logger.log(`Server started on ${server.url.toString()}`, "NestApplication");
65
+ const server = app.getHttpServer().getBunServer()
66
+ Logger.log(`Server started on ${server?.url.toString() ?? 'http://localhost:3000'}`, 'NestApplication')
66
67
  }
67
68
 
68
69
  await main();
@@ -495,7 +496,7 @@ app.enableCors((req: BunRequest, callback) => {
495
496
  You can also use NestJS's `CorsOptions` type for static configuration.
496
497
 
497
498
  ```ts
498
- const app = await NestFactory.create(AppModule, new BunAdapter(), {
499
+ const app = await NestFactory.create<NestBunApplication>(AppModule, new BunAdapter(), {
499
500
  cors: {
500
501
  origin: "https://example.com",
501
502
  methods: ["GET", "POST", "PUT"],
@@ -535,7 +536,7 @@ Compatible with popular Express middleware:
535
536
  ```ts
536
537
  import helmet from "helmet";
537
538
 
538
- const app = await NestFactory.create(AppModule, new BunAdapter());
539
+ const app = await NestFactory.create<NestBunApplication>(AppModule, new BunAdapter());
539
540
  app.use(helmet());
540
541
  ```
541
542
 
@@ -543,7 +544,65 @@ Tested and working with:
543
544
 
544
545
  - `helmet` - Security headers
545
546
  - `cors` - CORS handling
546
- - And most other Express-compatible middleware
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).
547
606
 
548
607
  ### WebSocket Support
549
608
 
@@ -585,7 +644,7 @@ class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
585
644
  }
586
645
 
587
646
  // Enable WebSocket support in your application
588
- const app = await NestFactory.create(AppModule, new BunAdapter());
647
+ const app = await NestFactory.create<NestBunApplication>(AppModule, new BunAdapter());
589
648
  app.useWebSocketAdapter(new BunWsAdapter(app));
590
649
  await app.listen(3000);
591
650
  ```
@@ -777,10 +836,10 @@ The Bun adapter supports secure WebSocket connections (WSS) using TLS/SSL certif
777
836
  **Using BunAdapter constructor options:**
778
837
 
779
838
  ```ts
780
- import { BunAdapter, BunWsAdapter } from "@krisanalfa/bunest-adapter";
839
+ import { BunAdapter, BunWsAdapter, NestBunApplication } from "@krisanalfa/bunest-adapter";
781
840
  import { NestFactory } from "@nestjs/core";
782
841
 
783
- const app = await NestFactory.create(
842
+ const app = await NestFactory.create<NestBunApplication>(
784
843
  AppModule,
785
844
  new BunAdapter({
786
845
  tls: {
@@ -800,10 +859,10 @@ await app.listen(3000);
800
859
  **Using NestFactory.create httpsOptions:**
801
860
 
802
861
  ```ts
803
- import { BunAdapter, BunWsAdapter } from "@krisanalfa/bunest-adapter";
862
+ import { BunAdapter, BunWsAdapter, NestBunApplication } from "@krisanalfa/bunest-adapter";
804
863
  import { NestFactory } from "@nestjs/core";
805
864
 
806
- const app = await NestFactory.create(AppModule, new BunAdapter(), {
865
+ const app = await NestFactory.create<NestBunApplication>(AppModule, new BunAdapter(), {
807
866
  httpsOptions: {
808
867
  cert: Bun.file("/path/to/cert.pem"),
809
868
  key: Bun.file("/path/to/key.pem"),
@@ -819,10 +878,10 @@ await app.listen(3000);
819
878
  You can also run secure WebSocket servers over Unix sockets:
820
879
 
821
880
  ```ts
822
- import { BunAdapter, BunWsAdapter } from "@krisanalfa/bunest-adapter";
881
+ import { BunAdapter, BunWsAdapter, NestBunApplication } from "@krisanalfa/bunest-adapter";
823
882
  import { NestFactory } from "@nestjs/core";
824
883
 
825
- const app = await NestFactory.create(
884
+ const app = await NestFactory.create<NestBunApplication>(
826
885
  AppModule,
827
886
  new BunAdapter({
828
887
  tls: {
@@ -877,7 +936,7 @@ class ChatGateway {
877
936
  }
878
937
 
879
938
  // WebSocket will be available on the same port as HTTP
880
- const app = await NestFactory.create(AppModule, new BunAdapter());
939
+ const app = await NestFactory.create<NestBunApplication>(AppModule, new BunAdapter());
881
940
  app.useWebSocketAdapter(new BunWsAdapter(app));
882
941
  await app.listen(3000); // Both HTTP and WebSocket use port 3000
883
942
  ```
@@ -943,7 +1002,7 @@ You can run your NestJS application with HTTPS using two approaches:
943
1002
  #### Using Bun's built-in HTTPS support (recommended)
944
1003
 
945
1004
  ```ts
946
- const app = await NestFactory.create(
1005
+ const app = await NestFactory.create<NestBunApplication>(
947
1006
  AppModule,
948
1007
  new BunAdapter({
949
1008
  tls: {
@@ -957,7 +1016,7 @@ const app = await NestFactory.create(
957
1016
  #### Using NestJS App Factory HTTPS options
958
1017
 
959
1018
  ```ts
960
- const app = await NestFactory.create(
1019
+ const app = await NestFactory.create<NestBunApplication>(
961
1020
  AppModule,
962
1021
  new BunAdapter(/* leave it empty */),
963
1022
  {
@@ -4,7 +4,7 @@ import { NestApplicationOptions, RequestMethod, VersioningOptions } from '@nestj
4
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 { BunWsClientData, ServerOptions } from './bun.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';
@@ -14,7 +14,7 @@ export declare class BunAdapter extends AbstractHttpAdapter<Server<unknown>, Bun
14
14
  private readonly logger;
15
15
  protected instance: BunServerInstance;
16
16
  constructor(bunServeOptions?: ServerOptions<BunWsClientData>);
17
- useStaticAssets(...args: unknown[]): void;
17
+ useStaticAssets(path: string, options?: BunStaticAssetsOptions): void;
18
18
  setViewEngine(engine: string): void;
19
19
  render(response: unknown, view: string, options: unknown): void;
20
20
  close(): Promise<void>;
@@ -1,8 +1,10 @@
1
- import { CorsOptionsDelegate, CorsOptions as NestCorsOptions } from '@nestjs/common/interfaces/external/cors-options.interface.js';
1
+ import { CorsOptions, CorsOptionsDelegate } from '@nestjs/common/interfaces/external/cors-options.interface.js';
2
2
  import { Serve, Server, ServerWebSocket, WebSocketHandler } from 'bun';
3
+ import { INestApplication } from '@nestjs/common';
4
+ import { BunPreflightHttpServer } from './bun.preflight-http-server.js';
3
5
  import { BunRequest } from './bun.request.js';
4
6
  export interface WsOptions extends Pick<WebSocketHandler<unknown>, 'maxPayloadLength' | 'idleTimeout' | 'backpressureLimit' | 'closeOnBackpressureLimit' | 'sendPings' | 'publishToSelf' | 'perMessageDeflate'> {
5
- cors?: true | NestCorsOptions | CorsOptionsDelegate<BunRequest>;
7
+ cors?: true | CorsOptions | CorsOptionsDelegate<BunRequest>;
6
8
  clientDataFactory?: (req: BunRequest) => unknown;
7
9
  }
8
10
  export type ServerOptions<TWebSocketData = unknown> = Pick<Serve.Options<TWebSocketData>, 'development' | 'maxRequestBodySize' | 'idleTimeout' | 'id' | 'tls' | 'websocket' | 'port' | 'hostname'>;
@@ -20,3 +22,32 @@ export interface BunWsClientData {
20
22
  /** Called by NestJS for disconnect handling */
21
23
  onDisconnect?: (ws: ServerWebSocket<unknown>) => void;
22
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
+ }
@@ -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,23 +35,23 @@ export declare class BunRequest {
26
35
  private _file;
27
36
  private _files;
28
37
  private _settings;
29
- private readonly _url;
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
- setKeepAlive: () => void;
43
- setNoDelay: () => void;
44
- setTimeout: () => void;
45
- };
54
+ get socket(): TcpSocket;
46
55
  /**
47
56
  * Gets the URL path and query string of the request.
48
57
  * Returns the pathname + search params for Node.js/Express compatibility.
@@ -373,6 +373,13 @@ export declare class BunResponse {
373
373
  * ```
374
374
  */
375
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;
376
383
  private buildStreamableResponse;
377
384
  private buildJsonResponse;
378
385
  private createResponse;
@@ -2,7 +2,7 @@ import { CorsOptions, CorsOptionsDelegate } from '@nestjs/common/interfaces/exte
2
2
  import { RequestMethod } from '@nestjs/common';
3
3
  import { Server, ServerWebSocket } from 'bun';
4
4
  import { RequestHandler } from '@nestjs/common/interfaces/index.js';
5
- import { BunWsClientData, ServerOptions, WsData, WsOptions } from './bun.internal.types.js';
5
+ import { BunStaticAssetsOptions, BunWsClientData, ServerOptions, WsData, WsOptions } from './bun.internal.types.js';
6
6
  import { BunMiddlewareEngine } from './bun.middleware-engine.js';
7
7
  import { BunRequest } from './bun.request.js';
8
8
  import { BunResponse } from './bun.response.js';
@@ -25,6 +25,7 @@ export declare class BunServerInstance {
25
25
  private wsOptions;
26
26
  private useWs;
27
27
  private useWsCors;
28
+ private staticAssetsOptions;
28
29
  private httpServer;
29
30
  constructor(bunServeOptions: ServerOptions<BunWsClientData>);
30
31
  use(maybePath: string | RequestHandler<BunRequest, BunResponse>, maybeHandler?: RequestHandler<BunRequest, BunResponse>): void;
@@ -52,7 +53,7 @@ export declare class BunServerInstance {
52
53
  * @param hostnameOrCallback The hostname to bind to or the callback function.
53
54
  * @param maybeCallback Optional callback to invoke once the server is listening.
54
55
  */
55
- listen(port: string | number, hostnameOrCallback?: string | (() => void), maybeCallback?: () => void): Server<unknown>;
56
+ listen(port: string | number, hostnameOrCallback?: string | (() => void), maybeCallback?: () => void): Promise<Server<unknown>>;
56
57
  /**
57
58
  * NestJS compatibility methods - stop the server
58
59
  */
@@ -112,11 +113,16 @@ export declare class BunServerInstance {
112
113
  * Enable CORS middleware
113
114
  */
114
115
  enableCors(corsOptions?: CorsOptions | CorsOptionsDelegate<BunRequest>, prefix?: string): void;
116
+ /**
117
+ * Serve static assets
118
+ */
119
+ useStaticAssets(path: string, options?: BunStaticAssetsOptions): void;
115
120
  private static isNumericPort;
116
121
  private static omitKeys;
117
122
  private isWebSocketUpgradeRequest;
118
123
  private provideCorsHeaders;
119
124
  private upgradeWebSocket;
125
+ private setupStaticAssetsIfNeeded;
120
126
  private setupWebSocketIfNeeded;
121
127
  private createServer;
122
128
  private delegateRouteHandler;
package/dist/index.js CHANGED
@@ -58,6 +58,8 @@ class BunPreflightHttpServer {
58
58
 
59
59
  // lib/bun.server-instance.ts
60
60
  import { Logger } from "@nestjs/common";
61
+ import { join } from "path";
62
+ import { readdir } from "fs/promises";
61
63
 
62
64
  // lib/bun.body-parser.middleware.ts
63
65
  var GET_CODE = 71;
@@ -429,28 +431,34 @@ class BunRequest {
429
431
  _file = null;
430
432
  _files = null;
431
433
  _settings = null;
432
- _url;
434
+ _connection = null;
435
+ _socket = null;
436
+ _url = null;
433
437
  _parsedUrl;
434
438
  method;
435
439
  params;
436
440
  constructor(nativeRequest) {
437
441
  this.nativeRequest = nativeRequest;
438
- this._url = nativeRequest.url;
439
- this._parsedUrl = new URL(this._url);
442
+ this._parsedUrl = new URL(nativeRequest.url);
440
443
  this._nativeHeaders = nativeRequest.headers;
441
444
  this.method = nativeRequest.method;
442
445
  this.params = nativeRequest.params;
443
446
  }
447
+ get connection() {
448
+ return this._connection ??= {
449
+ encrypted: this._parsedUrl.protocol === "https:" || this.nativeRequest.url.startsWith("https://")
450
+ };
451
+ }
444
452
  get socket() {
445
- return {
446
- encrypted: this._parsedUrl.protocol === "https:",
453
+ return this._socket ??= {
454
+ encrypted: this._parsedUrl.protocol === "https:" || this.nativeRequest.url.startsWith("https://"),
447
455
  setKeepAlive: () => {},
448
456
  setNoDelay: () => {},
449
457
  setTimeout: () => {}
450
458
  };
451
459
  }
452
460
  get url() {
453
- return this._parsedUrl.pathname + this._parsedUrl.search;
461
+ return this._url ??= this._parsedUrl.pathname + this._parsedUrl.search;
454
462
  }
455
463
  original() {
456
464
  return this.nativeRequest;
@@ -531,14 +539,18 @@ class BunRequest {
531
539
  }
532
540
  clone() {
533
541
  const cloned = new BunRequest(this.nativeRequest.clone());
542
+ cloned._hostname = this._hostname;
534
543
  cloned._pathname = this._pathname;
544
+ cloned._query = this._query;
535
545
  cloned._body = this._body;
536
546
  cloned._rawBody = this._rawBody;
537
547
  cloned._file = this._file;
538
548
  cloned._files = this._files;
539
549
  cloned._headers = this._headers;
540
- cloned._query = this._query;
541
550
  cloned._settings = this._settings;
551
+ cloned._connection = this._connection;
552
+ cloned._socket = this._socket;
553
+ cloned._url = this._url;
542
554
  return cloned;
543
555
  }
544
556
  on(event, listener) {
@@ -797,6 +809,9 @@ class BunResponse {
797
809
  isEnded() {
798
810
  return this.ended;
799
811
  }
812
+ _implicitHeader() {
813
+ this.writeHead(this.statusCode, {});
814
+ }
800
815
  buildStreamableResponse(body) {
801
816
  const streamHeaders = body.getHeaders();
802
817
  const headers = this.headersMap;
@@ -1045,6 +1060,7 @@ class BunServerInstance {
1045
1060
  wsOptions = {};
1046
1061
  useWs = false;
1047
1062
  useWsCors = false;
1063
+ staticAssetsOptions = null;
1048
1064
  httpServer = null;
1049
1065
  constructor(bunServeOptions) {
1050
1066
  this.bunServeOptions = bunServeOptions;
@@ -1125,13 +1141,14 @@ class BunServerInstance {
1125
1141
  search(pathOrHandler, maybeHandler) {
1126
1142
  this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
1127
1143
  }
1128
- listen(port, hostnameOrCallback, maybeCallback) {
1144
+ async listen(port, hostnameOrCallback, maybeCallback) {
1129
1145
  const hostname = typeof hostnameOrCallback === "string" ? hostnameOrCallback : this.bunServeOptions.hostname ?? "127.0.0.1";
1130
1146
  const callback = typeof hostnameOrCallback === "function" ? hostnameOrCallback : maybeCallback;
1131
1147
  const middlewareEngine = this.middlewareEngine;
1132
1148
  const notFoundHandler = this.notFoundHandler;
1133
1149
  const wsHandlers = this.wsHandlers;
1134
1150
  const bunServeOptions = this.bunServeOptions;
1151
+ await this.setupStaticAssetsIfNeeded();
1135
1152
  this.setupWebSocketIfNeeded(wsHandlers, bunServeOptions);
1136
1153
  const fetch = async (request, server) => {
1137
1154
  const bunRequest = new BunRequest(request);
@@ -1222,6 +1239,10 @@ class BunServerInstance {
1222
1239
  const corsMiddleware = new BunCorsMiddleware({ corsOptions, prefix });
1223
1240
  this.middlewareEngine.useGlobal(corsMiddleware.run.bind(corsMiddleware));
1224
1241
  }
1242
+ useStaticAssets(path, options) {
1243
+ this.logger.log(`Configuring static assets serving from path: ${path} with options: ${JSON.stringify(options)}`);
1244
+ this.staticAssetsOptions = { path, options };
1245
+ }
1225
1246
  static isNumericPort(value) {
1226
1247
  return typeof value === "number" || !isNaN(Number(value));
1227
1248
  }
@@ -1261,6 +1282,70 @@ class BunServerInstance {
1261
1282
  data: await this.wsOptions.clientDataFactory?.(bunRequest) ?? {}
1262
1283
  });
1263
1284
  }
1285
+ async setupStaticAssetsIfNeeded() {
1286
+ if (!this.staticAssetsOptions)
1287
+ return;
1288
+ const { path, options } = this.staticAssetsOptions;
1289
+ const files = await readdir(path, { withFileTypes: true, recursive: true });
1290
+ const flattenFiles = files.flat(Infinity);
1291
+ if (flattenFiles.length === 0)
1292
+ return;
1293
+ const useStatic = options?.useStatic ?? false;
1294
+ for (const file of flattenFiles) {
1295
+ if (!file.isFile())
1296
+ continue;
1297
+ const relativePath = file.parentPath.replace(path, "");
1298
+ const routePath = relativePath.startsWith("/") ? [relativePath, file.name].join("/") : `/${file.name}`;
1299
+ if (useStatic) {
1300
+ const bunFile = Bun.file(join(file.parentPath, file.name));
1301
+ this.routes[routePath] = {
1302
+ GET: new Response(await bunFile.bytes(), {
1303
+ headers: {
1304
+ "Content-Type": bunFile.type
1305
+ }
1306
+ })
1307
+ };
1308
+ } else {
1309
+ this.delegateRouteHandler("GET", routePath, async (req, res) => {
1310
+ const bunFile = Bun.file(join(file.parentPath, file.name));
1311
+ if (!await bunFile.exists()) {
1312
+ this.notFoundHandler(req, res);
1313
+ return;
1314
+ }
1315
+ const ifModifiedSince = req.headers.get("if-modified-since");
1316
+ if (ifModifiedSince) {
1317
+ const lastModified = bunFile.lastModified;
1318
+ if (new Date(ifModifiedSince).getTime() >= lastModified) {
1319
+ res.setStatus(304);
1320
+ res.end();
1321
+ return;
1322
+ }
1323
+ }
1324
+ const range = req.headers.get("range");
1325
+ if (range) {
1326
+ const fileSize = bunFile.size;
1327
+ const match = /bytes=(\d*)-(\d*)/.exec(range);
1328
+ if (match) {
1329
+ const start = parseInt(match[1], 10) || 0;
1330
+ const end = parseInt(match[2], 10) || fileSize - 1;
1331
+ if (start >= 0 && end < fileSize && start <= end) {
1332
+ res.setStatus(206);
1333
+ res.setHeader("Content-Range", `bytes ${start.toString()}-${end.toString()}/${fileSize.toString()}`);
1334
+ res.end(bunFile.slice(start, end + 1));
1335
+ return;
1336
+ } else {
1337
+ res.setStatus(416);
1338
+ res.setHeader("Content-Range", `bytes */${fileSize.toString()}`);
1339
+ res.end();
1340
+ return;
1341
+ }
1342
+ }
1343
+ }
1344
+ res.end(bunFile);
1345
+ });
1346
+ }
1347
+ }
1348
+ }
1264
1349
  setupWebSocketIfNeeded(wsHandlers, bunServeOptions) {
1265
1350
  const useWs = !!wsHandlers.onOpen && !!wsHandlers.onMessage && !!wsHandlers.onClose;
1266
1351
  if (!useWs)
@@ -1399,8 +1484,8 @@ class BunAdapter extends AbstractHttpAdapter {
1399
1484
  super(new BunServerInstance(bunServeOptions));
1400
1485
  this.bunServeOptions = bunServeOptions;
1401
1486
  }
1402
- useStaticAssets(...args) {
1403
- throw new Error("Not supported.");
1487
+ useStaticAssets(path, options) {
1488
+ this.instance.useStaticAssets(path, options);
1404
1489
  }
1405
1490
  setViewEngine(engine) {
1406
1491
  throw new Error("Not supported.");
@@ -1478,7 +1563,9 @@ class BunAdapter extends AbstractHttpAdapter {
1478
1563
  return BunVersionFilterMiddleware.createFilter(handler, version, versioningOptions);
1479
1564
  }
1480
1565
  listen(port, hostnameOrCallback, maybeCallback) {
1481
- this.setHttpServer(this.instance.listen(port, hostnameOrCallback, maybeCallback));
1566
+ this.instance.listen(port, hostnameOrCallback, maybeCallback).then((server) => {
1567
+ this.setHttpServer(server);
1568
+ });
1482
1569
  }
1483
1570
  configureTls(options) {
1484
1571
  if (options.httpsOptions) {
@@ -1497,7 +1584,7 @@ class BunAdapter extends AbstractHttpAdapter {
1497
1584
  }
1498
1585
  // lib/bun.file.interceptor.ts
1499
1586
  import { Injectable } from "@nestjs/common";
1500
- import { basename, join } from "path";
1587
+ import { basename, join as join2 } from "path";
1501
1588
  import { HttpAdapterHost } from "@nestjs/core";
1502
1589
  var {randomUUIDv7: randomUUIDv72 } = globalThis.Bun;
1503
1590
  import { tmpdir } from "os";
@@ -1507,7 +1594,7 @@ class BunFileInterceptor {
1507
1594
  constructor(adapter) {
1508
1595
  this.adapter = adapter;
1509
1596
  const httpAdapter = this.adapter.httpAdapter;
1510
- this.uploadDir ??= join(tmpdir(), "uploads", httpAdapter.getHttpServer().id, randomUUIDv72());
1597
+ this.uploadDir ??= join2(tmpdir(), "uploads", httpAdapter.getHttpServer().id, randomUUIDv72());
1511
1598
  }
1512
1599
  async intercept(context, next) {
1513
1600
  const request = context.switchToHttp().getRequest();
@@ -1515,7 +1602,7 @@ class BunFileInterceptor {
1515
1602
  return next.handle();
1516
1603
  }
1517
1604
  const files = await Promise.all(request.files.map(async (file) => {
1518
- const destPath = join(this.uploadDir, basename(file.name));
1605
+ const destPath = join2(this.uploadDir, basename(file.name));
1519
1606
  await Bun.write(destPath, file);
1520
1607
  return Bun.file(destPath);
1521
1608
  }));
@@ -1685,5 +1772,5 @@ export {
1685
1772
  BunAdapter
1686
1773
  };
1687
1774
 
1688
- //# debugId=813882D1C572F1EC64756E2164756E21
1775
+ //# debugId=B2EFFD14F95E510364756E2164756E21
1689
1776
  //# sourceMappingURL=index.js.map