@krisanalfa/bunest-adapter 0.0.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +479 -16
- package/dist/bun.adapter.d.ts +24 -4
- package/dist/bun.preflight-http-server.d.ts +62 -0
- package/dist/bun.request.d.ts +26 -3
- package/dist/bun.response.d.ts +102 -2
- package/dist/bun.ws-adapter.d.ts +48 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.js +566 -72
- package/dist/index.js.map +10 -8
- package/dist/internal.types.d.ts +8 -0
- package/package.json +9 -6
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
|
-
|
|
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.
|
|
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
|
-
|
|
559
|
+
static textDecoder = new TextDecoder;
|
|
560
|
+
headers = null;
|
|
445
561
|
statusCode = 200;
|
|
446
562
|
ended = false;
|
|
447
|
-
|
|
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.
|
|
571
|
+
return this.headers ??= new Map;
|
|
455
572
|
}
|
|
456
573
|
cookie(...args) {
|
|
457
|
-
this.
|
|
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.
|
|
482
|
-
if (this.
|
|
483
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
776
|
-
this.delegateRouteHandler("GET", path, handler);
|
|
1028
|
+
this.createHttpMethodHandler("GET")(pathOrHandler, maybeHandler);
|
|
777
1029
|
}
|
|
778
1030
|
post(pathOrHandler, maybeHandler) {
|
|
779
|
-
|
|
780
|
-
this.delegateRouteHandler("POST", path, handler);
|
|
1031
|
+
this.createHttpMethodHandler("POST")(pathOrHandler, maybeHandler);
|
|
781
1032
|
}
|
|
782
1033
|
put(pathOrHandler, maybeHandler) {
|
|
783
|
-
|
|
784
|
-
this.delegateRouteHandler("PUT", path, handler);
|
|
1034
|
+
this.createHttpMethodHandler("PUT")(pathOrHandler, maybeHandler);
|
|
785
1035
|
}
|
|
786
1036
|
patch(pathOrHandler, maybeHandler) {
|
|
787
|
-
|
|
788
|
-
this.delegateRouteHandler("PATCH", path, handler);
|
|
1037
|
+
this.createHttpMethodHandler("PATCH")(pathOrHandler, maybeHandler);
|
|
789
1038
|
}
|
|
790
1039
|
delete(pathOrHandler, maybeHandler) {
|
|
791
|
-
|
|
792
|
-
this.delegateRouteHandler("DELETE", path, handler);
|
|
1040
|
+
this.createHttpMethodHandler("DELETE")(pathOrHandler, maybeHandler);
|
|
793
1041
|
}
|
|
794
1042
|
head(pathOrHandler, maybeHandler) {
|
|
795
|
-
|
|
796
|
-
this.delegateRouteHandler("HEAD", path, handler);
|
|
1043
|
+
this.createHttpMethodHandler("HEAD")(pathOrHandler, maybeHandler);
|
|
797
1044
|
}
|
|
798
1045
|
options(pathOrHandler, maybeHandler) {
|
|
799
|
-
|
|
800
|
-
|
|
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
|
-
|
|
1054
|
+
this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
|
|
804
1055
|
}
|
|
805
1056
|
propfind(pathOrHandler, maybeHandler) {
|
|
806
|
-
|
|
1057
|
+
this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
|
|
807
1058
|
}
|
|
808
1059
|
proppatch(pathOrHandler, maybeHandler) {
|
|
809
|
-
|
|
1060
|
+
this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
|
|
810
1061
|
}
|
|
811
1062
|
mkcol(pathOrHandler, maybeHandler) {
|
|
812
|
-
|
|
1063
|
+
this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
|
|
813
1064
|
}
|
|
814
1065
|
copy(pathOrHandler, maybeHandler) {
|
|
815
|
-
|
|
1066
|
+
this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
|
|
816
1067
|
}
|
|
817
1068
|
move(pathOrHandler, maybeHandler) {
|
|
818
|
-
|
|
1069
|
+
this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
|
|
819
1070
|
}
|
|
820
1071
|
lock(pathOrHandler, maybeHandler) {
|
|
821
|
-
|
|
1072
|
+
this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
|
|
822
1073
|
}
|
|
823
1074
|
unlock(pathOrHandler, maybeHandler) {
|
|
824
|
-
|
|
1075
|
+
this.createUnsupportedMethod()(pathOrHandler, maybeHandler);
|
|
825
1076
|
}
|
|
826
1077
|
search(pathOrHandler, maybeHandler) {
|
|
827
|
-
|
|
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
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
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 : "
|
|
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
|
|
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:
|
|
1208
|
+
requestHandler: routeHandler
|
|
947
1209
|
});
|
|
948
1210
|
return bunResponse.res();
|
|
949
1211
|
};
|
|
950
|
-
const
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
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
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
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
|
-
...
|
|
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
|
-
|
|
985
|
-
|
|
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=
|
|
1582
|
+
//# debugId=1BD5D75380392B3E64756E2164756E21
|
|
1089
1583
|
//# sourceMappingURL=index.js.map
|