@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/dist/index.js CHANGED
@@ -15,10 +15,10 @@ var __legacyMetadataTS = (k, v) => {
15
15
  };
16
16
 
17
17
  // lib/bun.adapter.ts
18
+ var {randomUUIDv7 } = globalThis.Bun;
18
19
  import {
19
20
  Logger
20
21
  } from "@nestjs/common";
21
- var {randomUUIDv7 } = globalThis.Bun;
22
22
  import { AbstractHttpAdapter } from "@nestjs/core";
23
23
 
24
24
  // lib/bun.body-parser.middleware.ts
@@ -207,6 +207,57 @@ class BunMiddlewareEngine {
207
207
  useNotFoundHandler(handler) {
208
208
  this.notFoundHandler = handler;
209
209
  }
210
+ findRouteHandler(method, path) {
211
+ const exactHandler = this.findExactRouteHandler(method, path);
212
+ if (exactHandler)
213
+ return exactHandler;
214
+ return this.findPrefixRouteHandler(method, path);
215
+ }
216
+ findExactRouteHandler(method, path) {
217
+ const exactKey = `${method}:${path}`;
218
+ const exactMiddleware = this.routeMiddleware.get(exactKey);
219
+ if (exactMiddleware && exactMiddleware.length > 0) {
220
+ return exactMiddleware[exactMiddleware.length - 1];
221
+ }
222
+ const allExactKey = `ALL:${path}`;
223
+ const allExactMiddleware = this.routeMiddleware.get(allExactKey);
224
+ if (allExactMiddleware && allExactMiddleware.length > 0) {
225
+ return allExactMiddleware[allExactMiddleware.length - 1];
226
+ }
227
+ return null;
228
+ }
229
+ findPrefixRouteHandler(method, path) {
230
+ const methodMap = this.routeMiddlewareByMethod.get(method);
231
+ const allMethodMap = this.routeMiddlewareByMethod.get("ALL");
232
+ if (!methodMap && !allMethodMap)
233
+ return null;
234
+ const mapsToCheck = [];
235
+ if (methodMap)
236
+ mapsToCheck.push(methodMap);
237
+ if (allMethodMap)
238
+ mapsToCheck.push(allMethodMap);
239
+ return this.findBestMatchInMaps(mapsToCheck, path);
240
+ }
241
+ findBestMatchInMaps(maps, path) {
242
+ let bestMatch = null;
243
+ let bestMatchLength = 0;
244
+ const pathLen = path.length;
245
+ for (const map of maps) {
246
+ for (const [keyPath, middleware] of map) {
247
+ if (middleware.length === 0 || keyPath.length <= bestMatchLength)
248
+ continue;
249
+ if (this.isPrefixMatch(path, pathLen, keyPath)) {
250
+ bestMatch = middleware[middleware.length - 1];
251
+ bestMatchLength = keyPath.length;
252
+ }
253
+ }
254
+ }
255
+ return bestMatch;
256
+ }
257
+ isPrefixMatch(path, pathLen, keyPath) {
258
+ const keyPathLen = keyPath.length;
259
+ return path === keyPath || pathLen > keyPathLen && path.charCodeAt(keyPathLen) === 47 && path.startsWith(keyPath);
260
+ }
210
261
  async run(options) {
211
262
  try {
212
263
  const middlewares = this.getMiddlewareChain(options.method, options.path);
@@ -290,14 +341,17 @@ class BunMiddlewareEngine {
290
341
  }
291
342
  if (index === chainLength) {
292
343
  index++;
293
- const result = requestHandler(req, res, next);
344
+ if (!res.isEnded()) {
345
+ const result = requestHandler(req, res, next);
346
+ if (result instanceof Promise)
347
+ await result;
348
+ }
349
+ return;
350
+ }
351
+ if (index > chainLength && !res.isEnded()) {
352
+ const result = this.notFoundHandler?.(req, res, noop);
294
353
  if (result instanceof Promise)
295
354
  await result;
296
- if (index > chainLength) {
297
- const result2 = this.notFoundHandler?.(req, res, noop);
298
- if (result2 instanceof Promise)
299
- await result2;
300
- }
301
355
  }
302
356
  };
303
357
  await next();
@@ -313,6 +367,59 @@ class BunMiddlewareEngine {
313
367
  }
314
368
  }
315
369
 
370
+ // lib/bun.preflight-http-server.ts
371
+ class BunPreflightHttpServer {
372
+ adapter;
373
+ constructor(adapter) {
374
+ this.adapter = adapter;
375
+ }
376
+ on(event, callback) {}
377
+ once() {}
378
+ removeListener() {}
379
+ async stop(force) {
380
+ const server = this.adapter.getBunHttpServerInstance();
381
+ if (server instanceof BunPreflightHttpServer) {
382
+ return;
383
+ }
384
+ await server.stop(force);
385
+ }
386
+ address() {
387
+ const server = this.adapter.getBunHttpServerInstance();
388
+ if (server instanceof BunPreflightHttpServer) {
389
+ const options = this.adapter.getBunServerOptions();
390
+ return {
391
+ address: options.hostname ?? "127.0.0.1",
392
+ port: options.port ?? 3000
393
+ };
394
+ }
395
+ return {
396
+ address: server.hostname,
397
+ port: server.port
398
+ };
399
+ }
400
+ setWsOptions(options) {
401
+ this.adapter.setWsOptions(options);
402
+ }
403
+ registerWsOpenHandler(handler) {
404
+ this.adapter.getWsHandlers().onOpen = handler;
405
+ }
406
+ registerWsMessageHandler(handler) {
407
+ this.adapter.getWsHandlers().onMessage = handler;
408
+ }
409
+ registerWsCloseHandler(handler) {
410
+ this.adapter.getWsHandlers().onClose = handler;
411
+ }
412
+ getWsHandlers() {
413
+ return this.adapter.getWsHandlers();
414
+ }
415
+ getBunServer() {
416
+ return this.adapter.getBunHttpServerInstance();
417
+ }
418
+ async close() {
419
+ await this.stop(true);
420
+ }
421
+ }
422
+
316
423
  // lib/bun.request.ts
317
424
  import { parse } from "qs";
318
425
  var NULL_PROTO = Object.getPrototypeOf(Object.create(null));
@@ -341,8 +448,16 @@ class BunRequest {
341
448
  this.method = nativeRequest.method;
342
449
  this.params = nativeRequest.params;
343
450
  }
451
+ get socket() {
452
+ return {
453
+ encrypted: this._parsedUrl.protocol === "https:"
454
+ };
455
+ }
344
456
  get url() {
345
- return this._url;
457
+ return this._parsedUrl.pathname + this._parsedUrl.search;
458
+ }
459
+ original() {
460
+ return this.nativeRequest;
346
461
  }
347
462
  get pathname() {
348
463
  return this._pathname ??= this._parsedUrl.pathname;
@@ -441,20 +556,22 @@ class BunResponse {
441
556
  resolve;
442
557
  response;
443
558
  cookieMap = new CookieMap;
444
- _headers = null;
559
+ static textDecoder = new TextDecoder;
560
+ headers = null;
445
561
  statusCode = 200;
446
562
  ended = false;
447
- _cookieHeaderCache = null;
563
+ cookieHeaders = null;
564
+ chunks = [];
448
565
  constructor() {
449
566
  this.response = new Promise((r) => {
450
567
  this.resolve = r;
451
568
  });
452
569
  }
453
570
  get headersMap() {
454
- return this._headers ??= new Map;
571
+ return this.headers ??= new Map;
455
572
  }
456
573
  cookie(...args) {
457
- this._cookieHeaderCache = null;
574
+ this.cookieHeaders = null;
458
575
  if (args.length === 1) {
459
576
  this.cookieMap.set(args[0]);
460
577
  } else {
@@ -478,10 +595,22 @@ class BunResponse {
478
595
  if (this.ended)
479
596
  return;
480
597
  this.ended = true;
481
- this._cookieHeaderCache ??= this.cookieMap.toSetCookieHeaders().join("; ");
482
- if (this._cookieHeaderCache.length > 0) {
483
- this.setHeader("set-cookie", this._cookieHeaderCache);
598
+ this.applyCookieHeaders();
599
+ if (this.chunks.length > 0) {
600
+ const finalBody = this.combineChunks(body);
601
+ this.resolve(this.createResponse(finalBody));
602
+ return;
603
+ }
604
+ this.sendResponse(body);
605
+ }
606
+ applyCookieHeaders() {
607
+ this.cookieHeaders ??= this.cookieMap.toSetCookieHeaders();
608
+ if (this.cookieHeaders.length > 0) {
609
+ this.setHeader("set-cookie", this.cookieHeaders.join(`
610
+ `));
484
611
  }
612
+ }
613
+ sendResponse(body) {
485
614
  if (body !== null && typeof body === "object") {
486
615
  if (body instanceof Uint8Array || body instanceof Blob) {
487
616
  this.resolve(this.createResponse(body));
@@ -500,11 +629,100 @@ class BunResponse {
500
629
  }
501
630
  this.resolve(this.buildJsonResponse(body));
502
631
  }
632
+ combineChunks(finalChunk) {
633
+ const hasStrings = this.chunks.some((chunk) => typeof chunk === "string");
634
+ const finalIsString = typeof finalChunk === "string";
635
+ if (hasStrings || finalIsString) {
636
+ return this.combineAsString(finalChunk);
637
+ }
638
+ return this.combineAsBinary(finalChunk);
639
+ }
640
+ combineAsString(finalChunk) {
641
+ const decoder = BunResponse.textDecoder;
642
+ const parts = this.chunks.map((chunk) => typeof chunk === "string" ? chunk : decoder.decode(chunk));
643
+ if (finalChunk !== undefined && finalChunk !== null) {
644
+ if (typeof finalChunk === "string") {
645
+ parts.push(finalChunk);
646
+ } else if (finalChunk instanceof Uint8Array) {
647
+ parts.push(decoder.decode(finalChunk));
648
+ } else if (typeof finalChunk === "object") {
649
+ parts.push(JSON.stringify(finalChunk));
650
+ } else {
651
+ parts.push(String(finalChunk));
652
+ }
653
+ }
654
+ return parts.join("");
655
+ }
656
+ combineAsBinary(finalChunk) {
657
+ const binaryChunks = this.chunks;
658
+ let totalLength = 0;
659
+ for (const chunk of binaryChunks) {
660
+ totalLength += chunk.length;
661
+ }
662
+ if (finalChunk instanceof Uint8Array) {
663
+ totalLength += finalChunk.length;
664
+ }
665
+ const result = new Uint8Array(totalLength);
666
+ let offset = 0;
667
+ for (const chunk of binaryChunks) {
668
+ result.set(chunk, offset);
669
+ offset += chunk.length;
670
+ }
671
+ if (finalChunk instanceof Uint8Array) {
672
+ result.set(finalChunk, offset);
673
+ }
674
+ return result;
675
+ }
503
676
  setHeader(name, value) {
504
677
  this.headersMap.set(name.toLowerCase(), value);
505
678
  }
679
+ writeHead(statusCode, headers) {
680
+ this.statusCode = statusCode;
681
+ if (headers) {
682
+ for (const [name, value] of Object.entries(headers)) {
683
+ this.setHeader(name, value);
684
+ }
685
+ }
686
+ }
687
+ on(event, listener) {
688
+ return this;
689
+ }
690
+ off(event, listener) {
691
+ return this;
692
+ }
693
+ destroyed = false;
694
+ destroy(error) {}
695
+ once(event, listener) {
696
+ return this;
697
+ }
698
+ write(chunk) {
699
+ if (this.ended) {
700
+ return false;
701
+ }
702
+ if (typeof chunk === "string") {
703
+ this.chunks.push(chunk);
704
+ return true;
705
+ }
706
+ if (chunk instanceof Uint8Array) {
707
+ this.chunks.push(chunk);
708
+ return true;
709
+ }
710
+ if (chunk instanceof Buffer) {
711
+ this.chunks.push(new Uint8Array(chunk));
712
+ return true;
713
+ }
714
+ if (chunk != null) {
715
+ if (typeof chunk === "object") {
716
+ this.chunks.push(JSON.stringify(chunk));
717
+ } else {
718
+ this.chunks.push(String(chunk));
719
+ }
720
+ return true;
721
+ }
722
+ return true;
723
+ }
506
724
  getHeader(name) {
507
- return this._headers?.get(name.toLowerCase()) ?? null;
725
+ return this.headers?.get(name.toLowerCase()) ?? null;
508
726
  }
509
727
  appendHeader(name, value) {
510
728
  const key = name.toLowerCase();
@@ -513,7 +731,7 @@ class BunResponse {
513
731
  headers.set(key, existing ? `${existing}, ${value}` : value);
514
732
  }
515
733
  removeHeader(name) {
516
- this._headers?.delete(name.toLowerCase());
734
+ this.headers?.delete(name.toLowerCase());
517
735
  }
518
736
  setStatus(code) {
519
737
  this.statusCode = code;
@@ -542,7 +760,7 @@ class BunResponse {
542
760
  return this.createResponse(body.getStream());
543
761
  }
544
762
  buildJsonResponse(body) {
545
- const headers = this._headers;
763
+ const headers = this.headers;
546
764
  if (headers === null || headers.size === 0) {
547
765
  return Response.json(body, { status: this.statusCode });
548
766
  }
@@ -555,7 +773,7 @@ class BunResponse {
555
773
  });
556
774
  }
557
775
  createResponse(body) {
558
- const headers = this._headers;
776
+ const headers = this.headers;
559
777
  if (headers === null || headers.size === 0) {
560
778
  return new Response(body, { status: this.statusCode });
561
779
  }
@@ -761,70 +979,103 @@ class BunAdapter extends AbstractHttpAdapter {
761
979
  res.setStatus(404);
762
980
  res.end({ message: "Not Found" });
763
981
  };
982
+ wsHandlers = {
983
+ onOpen: undefined,
984
+ onMessage: undefined,
985
+ onClose: undefined
986
+ };
987
+ wsMiddlewareEngine = new BunMiddlewareEngine;
988
+ wsOptions = {};
989
+ useWs = false;
990
+ useWsCors = false;
991
+ wsCorsHeaders;
764
992
  constructor(bunServeOptions = {
765
993
  development: false,
766
994
  id: randomUUIDv7()
767
995
  }) {
768
996
  super();
769
997
  this.bunServeOptions = bunServeOptions;
998
+ this.setInstance({
999
+ use: (maybePath, maybeHandler) => {
1000
+ if (typeof maybePath === "string") {
1001
+ let path = maybePath;
1002
+ const handler = maybeHandler;
1003
+ if (!handler) {
1004
+ throw new Error("Handler must be provided when path is a string.");
1005
+ }
1006
+ if (path.includes("/*")) {
1007
+ path = path.substring(0, path.indexOf("/*"));
1008
+ }
1009
+ this.logger.log(`Registering middleware for path: ${path}`);
1010
+ this.middlewareEngine.useRoute("ALL", path, handler);
1011
+ } else {
1012
+ const handler = maybePath;
1013
+ this.middlewareEngine.useGlobal(handler);
1014
+ }
1015
+ }
1016
+ });
770
1017
  }
771
1018
  use(middleware) {
772
1019
  this.middlewareEngine.useGlobal(middleware);
773
1020
  }
1021
+ createHttpMethodHandler(httpMethod) {
1022
+ return (pathOrHandler, maybeHandler) => {
1023
+ const { path, handler } = this.parseRouteHandler(pathOrHandler, maybeHandler);
1024
+ this.delegateRouteHandler(httpMethod, path, handler);
1025
+ };
1026
+ }
774
1027
  get(pathOrHandler, maybeHandler) {
775
- const { path, handler } = this.parseRouteHandler(pathOrHandler, maybeHandler);
776
- this.delegateRouteHandler("GET", path, handler);
1028
+ this.createHttpMethodHandler("GET")(pathOrHandler, maybeHandler);
777
1029
  }
778
1030
  post(pathOrHandler, maybeHandler) {
779
- const { path, handler } = this.parseRouteHandler(pathOrHandler, maybeHandler);
780
- this.delegateRouteHandler("POST", path, handler);
1031
+ this.createHttpMethodHandler("POST")(pathOrHandler, maybeHandler);
781
1032
  }
782
1033
  put(pathOrHandler, maybeHandler) {
783
- const { path, handler } = this.parseRouteHandler(pathOrHandler, maybeHandler);
784
- this.delegateRouteHandler("PUT", path, handler);
1034
+ this.createHttpMethodHandler("PUT")(pathOrHandler, maybeHandler);
785
1035
  }
786
1036
  patch(pathOrHandler, maybeHandler) {
787
- const { path, handler } = this.parseRouteHandler(pathOrHandler, maybeHandler);
788
- this.delegateRouteHandler("PATCH", path, handler);
1037
+ this.createHttpMethodHandler("PATCH")(pathOrHandler, maybeHandler);
789
1038
  }
790
1039
  delete(pathOrHandler, maybeHandler) {
791
- const { path, handler } = this.parseRouteHandler(pathOrHandler, maybeHandler);
792
- this.delegateRouteHandler("DELETE", path, handler);
1040
+ this.createHttpMethodHandler("DELETE")(pathOrHandler, maybeHandler);
793
1041
  }
794
1042
  head(pathOrHandler, maybeHandler) {
795
- const { path, handler } = this.parseRouteHandler(pathOrHandler, maybeHandler);
796
- this.delegateRouteHandler("HEAD", path, handler);
1043
+ this.createHttpMethodHandler("HEAD")(pathOrHandler, maybeHandler);
797
1044
  }
798
1045
  options(pathOrHandler, maybeHandler) {
799
- const { path, handler } = this.parseRouteHandler(pathOrHandler, maybeHandler);
800
- this.delegateRouteHandler("OPTIONS", path, handler);
1046
+ this.createHttpMethodHandler("OPTIONS")(pathOrHandler, maybeHandler);
1047
+ }
1048
+ createUnsupportedMethod() {
1049
+ return (pathOrHandler, maybeHandler) => {
1050
+ throw new Error("Not supported.");
1051
+ };
801
1052
  }
802
1053
  all(pathOrHandler, maybeHandler) {
803
- throw new Error("Not supported.");
1054
+ this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
804
1055
  }
805
1056
  propfind(pathOrHandler, maybeHandler) {
806
- throw new Error("Not supported.");
1057
+ this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
807
1058
  }
808
1059
  proppatch(pathOrHandler, maybeHandler) {
809
- throw new Error("Not supported.");
1060
+ this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
810
1061
  }
811
1062
  mkcol(pathOrHandler, maybeHandler) {
812
- throw new Error("Not supported.");
1063
+ this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
813
1064
  }
814
1065
  copy(pathOrHandler, maybeHandler) {
815
- throw new Error("Not supported.");
1066
+ this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
816
1067
  }
817
1068
  move(pathOrHandler, maybeHandler) {
818
- throw new Error("Not supported.");
1069
+ this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
819
1070
  }
820
1071
  lock(pathOrHandler, maybeHandler) {
821
- throw new Error("Not supported.");
1072
+ this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
822
1073
  }
823
1074
  unlock(pathOrHandler, maybeHandler) {
824
- throw new Error("Not supported.");
1075
+ this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
825
1076
  }
826
1077
  search(pathOrHandler, maybeHandler) {
827
- throw new Error("Not supported.");
1078
+ this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
828
1079
  }
829
1080
  useStaticAssets(...args) {
830
1081
  throw new Error("Not supported.");
@@ -839,12 +1090,15 @@ class BunAdapter extends AbstractHttpAdapter {
839
1090
  await this.httpServer.stop();
840
1091
  }
841
1092
  initHttpServer(options) {
842
- this.setHttpServer({
843
- once: () => {},
844
- address: () => ({ address: "0.0.0.0", port: 0 }),
845
- removeListener: () => {},
846
- stop: () => {}
1093
+ const preflightServer = new BunPreflightHttpServer({
1094
+ getBunHttpServerInstance: () => this.getHttpServer(),
1095
+ getBunServerOptions: () => this.bunServeOptions,
1096
+ getWsHandlers: () => this.wsHandlers,
1097
+ setWsOptions: (wsOptions) => {
1098
+ this.wsOptions = wsOptions;
1099
+ }
847
1100
  });
1101
+ this.setHttpServer(preflightServer);
848
1102
  if (options.httpsOptions) {
849
1103
  this.bunServeOptions.tls = {
850
1104
  key: options.httpsOptions.key,
@@ -857,6 +1111,7 @@ class BunAdapter extends AbstractHttpAdapter {
857
1111
  requestCert: options.httpsOptions.requestCert
858
1112
  };
859
1113
  }
1114
+ return preflightServer;
860
1115
  }
861
1116
  getRequestHostname(request) {
862
1117
  return request.hostname;
@@ -931,58 +1186,150 @@ class BunAdapter extends AbstractHttpAdapter {
931
1186
  return BunVersionFilterMiddleware.createFilter(handler, version, versioningOptions);
932
1187
  }
933
1188
  listen(port, hostnameOrCallback, maybeCallback) {
934
- const hostname = typeof hostnameOrCallback === "string" ? hostnameOrCallback : "localhost";
1189
+ const hostname = typeof hostnameOrCallback === "string" ? hostnameOrCallback : this.bunServeOptions.hostname ?? "127.0.0.1";
935
1190
  const callback = typeof hostnameOrCallback === "function" ? hostnameOrCallback : maybeCallback;
936
1191
  const middlewareEngine = this.middlewareEngine;
937
1192
  const notFoundHandler = this.notFoundHandler;
938
- const fetch = async (request) => {
1193
+ const wsHandlers = this.wsHandlers;
1194
+ const bunServeOptions = this.bunServeOptions;
1195
+ this.setupWebSocketIfNeeded(wsHandlers, bunServeOptions);
1196
+ const fetch = async (request, server2) => {
1197
+ if (await this.upgradeWebSocket(request, server2)) {
1198
+ return;
1199
+ }
939
1200
  const bunRequest = new BunRequest(request);
940
1201
  const bunResponse = new BunResponse;
1202
+ const routeHandler = middlewareEngine.findRouteHandler(bunRequest.method, bunRequest.pathname) ?? notFoundHandler;
941
1203
  await middlewareEngine.run({
942
1204
  req: bunRequest,
943
1205
  res: bunResponse,
944
1206
  method: bunRequest.method,
945
1207
  path: bunRequest.pathname,
946
- requestHandler: notFoundHandler
1208
+ requestHandler: routeHandler
947
1209
  });
948
1210
  return bunResponse.res();
949
1211
  };
950
- const omit = (obj, ...keys) => {
951
- const result = { ...obj };
952
- for (const key of keys) {
953
- Reflect.deleteProperty(result, key);
1212
+ const server = this.createServer(port, hostname, bunServeOptions, fetch);
1213
+ callback?.();
1214
+ this.setHttpServer(server);
1215
+ }
1216
+ static isNumericPort(value) {
1217
+ return typeof value === "number" || !isNaN(Number(value));
1218
+ }
1219
+ static omitKeys(obj, ...keys) {
1220
+ const result = { ...obj };
1221
+ for (const key of keys) {
1222
+ Reflect.deleteProperty(result, key);
1223
+ }
1224
+ return result;
1225
+ }
1226
+ isWebSocketUpgradeRequest(request) {
1227
+ const upgradeHeader = request.headers.get("upgrade");
1228
+ const connectionHeader = request.headers.get("connection");
1229
+ return !!(upgradeHeader?.toLowerCase() === "websocket" && connectionHeader?.toLowerCase().includes("upgrade"));
1230
+ }
1231
+ async handleWebSocketCors(request) {
1232
+ if (this.wsCorsHeaders) {
1233
+ return this.wsCorsHeaders;
1234
+ }
1235
+ const bunRequest = new BunRequest(request);
1236
+ const bunResponse = new BunResponse;
1237
+ await this.wsMiddlewareEngine.run({
1238
+ req: bunRequest,
1239
+ res: bunResponse,
1240
+ method: bunRequest.method,
1241
+ path: bunRequest.pathname,
1242
+ requestHandler: (req, res) => {
1243
+ res.end();
954
1244
  }
955
- return result;
956
- };
957
- const server = typeof port === "number" || !isNaN(Number(port)) ? Bun.serve({
958
- ...this.bunServeOptions,
1245
+ });
1246
+ const response = await bunResponse.res();
1247
+ this.wsCorsHeaders = response.headers;
1248
+ return this.wsCorsHeaders;
1249
+ }
1250
+ async upgradeWebSocket(request, server) {
1251
+ if (!this.useWs || !this.isWebSocketUpgradeRequest(request)) {
1252
+ return false;
1253
+ }
1254
+ if (!this.useWsCors) {
1255
+ return server.upgrade(request, {
1256
+ data: this.wsOptions.clientDataFactory ? this.wsOptions.clientDataFactory(new BunRequest(request)) : {}
1257
+ });
1258
+ }
1259
+ const headers = await this.handleWebSocketCors(request);
1260
+ return server.upgrade(request, {
1261
+ headers,
1262
+ data: this.wsOptions.clientDataFactory ? this.wsOptions.clientDataFactory(new BunRequest(request)) : {}
1263
+ });
1264
+ }
1265
+ setupWebSocketIfNeeded(wsHandlers, bunServeOptions) {
1266
+ const useWs = !!wsHandlers.onOpen && !!wsHandlers.onMessage && !!wsHandlers.onClose;
1267
+ this.useWs = useWs;
1268
+ if (useWs) {
1269
+ const getServer = this.getHttpServer.bind(this);
1270
+ const onMessage = wsHandlers.onMessage;
1271
+ const onOpen = wsHandlers.onOpen;
1272
+ const onClose = wsHandlers.onClose;
1273
+ bunServeOptions.websocket = {
1274
+ ...this.wsOptions,
1275
+ message: (ws, message) => {
1276
+ ws.data.onMessageInternal?.(message);
1277
+ onMessage?.(ws, message, getServer());
1278
+ },
1279
+ open: (ws) => {
1280
+ onOpen?.(ws);
1281
+ },
1282
+ close: (ws, code, reason) => {
1283
+ ws.data.onCloseInternal?.();
1284
+ ws.data.onDisconnect?.(ws);
1285
+ onClose?.(ws, code, reason);
1286
+ }
1287
+ };
1288
+ }
1289
+ const useWsCors = typeof this.wsOptions.cors !== "undefined";
1290
+ this.useWsCors = useWsCors;
1291
+ if (useWsCors) {
1292
+ const corsMiddleware = new BunCorsMiddleware({
1293
+ corsOptions: this.wsOptions.cors === true ? undefined : this.wsOptions.cors
1294
+ });
1295
+ this.wsMiddlewareEngine.useGlobal(corsMiddleware.run.bind(corsMiddleware));
1296
+ }
1297
+ }
1298
+ createServer(port, hostname, bunServeOptions, fetch) {
1299
+ return BunAdapter.isNumericPort(port) ? Bun.serve({
1300
+ ...bunServeOptions,
959
1301
  hostname,
960
1302
  port,
961
1303
  routes: this.routes,
962
1304
  fetch
963
1305
  }) : Bun.serve({
964
- ...omit(this.bunServeOptions, "idleTimeout"),
1306
+ ...BunAdapter.omitKeys(bunServeOptions, "idleTimeout", "port", "hostname"),
965
1307
  unix: port,
966
1308
  routes: this.routes,
967
1309
  fetch
968
1310
  });
969
- if (typeof port === "string" && isNaN(Number(port))) {
970
- this.logger.log(`Bun server listening on unix socket: ${port}`);
971
- }
972
- callback?.();
973
- Object.defineProperty(server, "address", {
974
- configurable: true,
975
- enumerable: true,
976
- get: () => ({ address: server.hostname, port: server.port })
977
- });
978
- this.setHttpServer(server);
979
1311
  }
980
1312
  delegateRouteHandler(method, path, handler) {
1313
+ this.ensureRouteExists(path);
1314
+ const requestHandler = this.prepareRequestHandler(method, path, handler);
1315
+ this.routes[path][method] = this.createRouteFetchHandler(path, method, requestHandler);
1316
+ }
1317
+ ensureRouteExists(path) {
981
1318
  if (!(path in this.routes)) {
982
1319
  this.routes[path] = Object.create(null);
983
1320
  }
984
- const requestHandler = !this.useVersioning ? handler : this.createChainedHandlerForVersioningResolution(this.createVersioningHandlers(method, path, handler), this.notFoundHandler);
985
- this.routes[path][method] = async (request) => {
1321
+ }
1322
+ prepareRequestHandler(method, path, handler) {
1323
+ if (!this.useVersioning) {
1324
+ return handler;
1325
+ }
1326
+ return this.createChainedHandlerForVersioningResolution(this.createVersioningHandlers(method, path, handler), this.notFoundHandler);
1327
+ }
1328
+ createRouteFetchHandler(path, method, requestHandler) {
1329
+ return async (request, server) => {
1330
+ if (path === "/" && await this.upgradeWebSocket(request, server)) {
1331
+ return;
1332
+ }
986
1333
  const bunRequest = new BunRequest(request);
987
1334
  const bunResponse = new BunResponse;
988
1335
  await this.middlewareEngine.run({
@@ -1078,12 +1425,159 @@ BunFileInterceptor = __legacyDecorateClassTS([
1078
1425
  typeof HttpAdapterHost === "undefined" ? Object : HttpAdapterHost
1079
1426
  ])
1080
1427
  ], BunFileInterceptor);
1428
+ // lib/bun.ws-adapter.ts
1429
+ import {
1430
+ AbstractWsAdapter
1431
+ } from "@nestjs/websockets";
1432
+ import { EMPTY, Subject, mergeMap } from "rxjs";
1433
+ import { Logger as Logger2 } from "@nestjs/common";
1434
+ import { isNil } from "@nestjs/common/utils/shared.utils.js";
1435
+ var WS_READY_STATE_OPEN = 1;
1436
+ var defaultMessageParser = (data) => {
1437
+ if (typeof data === "string") {
1438
+ return JSON.parse(data);
1439
+ }
1440
+ if (data instanceof ArrayBuffer) {
1441
+ return JSON.parse(new TextDecoder().decode(data));
1442
+ }
1443
+ if (Buffer.isBuffer(data)) {
1444
+ return JSON.parse(data.toString("utf8"));
1445
+ }
1446
+ return JSON.parse(Buffer.concat(data).toString("utf8"));
1447
+ };
1448
+
1449
+ class BunWsAdapter extends AbstractWsAdapter {
1450
+ logger = new Logger2(BunWsAdapter.name);
1451
+ nestApp;
1452
+ messageParser = defaultMessageParser;
1453
+ onOpenHandler;
1454
+ globalHandlersInitialized = false;
1455
+ constructor(appOrHttpServer) {
1456
+ super(appOrHttpServer);
1457
+ if (!appOrHttpServer || !("getHttpAdapter" in appOrHttpServer)) {
1458
+ throw new Error("BunWsAdapter requires a NestApplication instance in the constructor.");
1459
+ }
1460
+ this.nestApp = appOrHttpServer;
1461
+ }
1462
+ create(_port, options) {
1463
+ if (options?.messageParser) {
1464
+ this.messageParser = options.messageParser;
1465
+ }
1466
+ const server = this.nestApp.getHttpAdapter().getHttpServer();
1467
+ const wsOptions = this.extractWsOptions(options);
1468
+ server.setWsOptions(wsOptions);
1469
+ this.initializeGlobalHandlers(server);
1470
+ return server;
1471
+ }
1472
+ extractWsOptions(options) {
1473
+ if (!options) {
1474
+ return {};
1475
+ }
1476
+ const wsOptions = { ...options };
1477
+ delete wsOptions.messageParser;
1478
+ return wsOptions;
1479
+ }
1480
+ async close(server) {
1481
+ await server.close();
1482
+ }
1483
+ bindClientConnect(server, callback) {
1484
+ this.onOpenHandler = callback;
1485
+ }
1486
+ bindClientDisconnect(client, callback) {
1487
+ const existingHandler = client.data.onDisconnect;
1488
+ client.data.onDisconnect = (ws) => {
1489
+ existingHandler?.(ws);
1490
+ callback(client);
1491
+ };
1492
+ }
1493
+ bindMessageHandlers(client, handlers, transform) {
1494
+ const handlerMap = this.buildHandlerMap(handlers);
1495
+ const message$ = new Subject;
1496
+ let isActive = true;
1497
+ const existingOnMessage = client.data.onMessageInternal;
1498
+ client.data.onMessageInternal = (data) => {
1499
+ existingOnMessage?.(data);
1500
+ if (isActive) {
1501
+ message$.next(data);
1502
+ }
1503
+ };
1504
+ const subscription = message$.pipe(mergeMap((data) => this.processMessage(data, handlerMap, transform))).subscribe({
1505
+ next: (response) => {
1506
+ this.sendResponse(client, response, isActive);
1507
+ },
1508
+ error: (err) => {
1509
+ this.logger.error("Message processing error", err instanceof Error ? err.stack : err);
1510
+ }
1511
+ });
1512
+ const existingOnClose = client.data.onCloseInternal;
1513
+ client.data.onCloseInternal = () => {
1514
+ existingOnClose?.();
1515
+ isActive = false;
1516
+ this.cleanupClient(message$, subscription);
1517
+ };
1518
+ }
1519
+ initializeGlobalHandlers(server) {
1520
+ if (this.globalHandlersInitialized) {
1521
+ return;
1522
+ }
1523
+ this.globalHandlersInitialized = true;
1524
+ server.registerWsOpenHandler((ws) => {
1525
+ this.onOpenHandler?.(ws);
1526
+ });
1527
+ server.registerWsMessageHandler(() => {});
1528
+ server.registerWsCloseHandler(() => {});
1529
+ }
1530
+ buildHandlerMap(handlers) {
1531
+ const map = new Map;
1532
+ for (const handler of handlers) {
1533
+ map.set(handler.message, handler);
1534
+ }
1535
+ return map;
1536
+ }
1537
+ processMessage(data, handlerMap, transform) {
1538
+ try {
1539
+ const parsed = this.messageParser(data);
1540
+ if (typeof parsed.event !== "string") {
1541
+ return EMPTY;
1542
+ }
1543
+ const handler = handlerMap.get(parsed.event);
1544
+ if (!handler) {
1545
+ return EMPTY;
1546
+ }
1547
+ const result = handler.callback(parsed.data, parsed.event);
1548
+ return transform(result).pipe(mergeMap((value) => isNil(value) ? EMPTY : [value]));
1549
+ } catch (error) {
1550
+ this.logger.warn("Failed to parse WebSocket message", error instanceof Error ? error.message : String(error));
1551
+ return EMPTY;
1552
+ }
1553
+ }
1554
+ sendResponse(client, response, isActive) {
1555
+ if (!isActive || client.readyState !== WS_READY_STATE_OPEN) {
1556
+ return;
1557
+ }
1558
+ if (response instanceof ArrayBuffer) {
1559
+ client.send(response);
1560
+ return;
1561
+ }
1562
+ if (ArrayBuffer.isView(response)) {
1563
+ client.send(response.buffer);
1564
+ return;
1565
+ }
1566
+ client.send(JSON.stringify(response));
1567
+ }
1568
+ cleanupClient(message$, subscription) {
1569
+ message$.complete();
1570
+ subscription.unsubscribe();
1571
+ }
1572
+ }
1081
1573
  export {
1574
+ BunWsAdapter,
1082
1575
  BunResponse,
1083
1576
  BunRequest,
1577
+ BunPreflightHttpServer,
1084
1578
  BunFileInterceptor,
1085
1579
  BunAdapter
1086
1580
  };
1087
1581
 
1088
- //# debugId=E15070B5DB09928264756E2164756E21
1582
+ //# debugId=1BD5D75380392B3E64756E2164756E21
1089
1583
  //# sourceMappingURL=index.js.map