@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.
@@ -0,0 +1,62 @@
1
+ import { Server, ServerWebSocket } from 'bun';
2
+ import { BaseWsInstance } from '@nestjs/websockets';
3
+ import { ServerOptions, WsOptions } from './internal.types.js';
4
+ interface BunHttpAdapter {
5
+ setWsOptions(options: WsOptions): void;
6
+ getBunHttpServerInstance(): Server<unknown>;
7
+ getWsHandlers(): {
8
+ onOpen: ((ws: ServerWebSocket<unknown>) => void) | undefined;
9
+ onMessage: ((ws: ServerWebSocket<unknown>, message: string | ArrayBuffer | Buffer | Buffer[], server: Server<unknown>) => void) | undefined;
10
+ onClose: ((ws: ServerWebSocket<unknown>, code: number, reason: string) => void) | undefined;
11
+ };
12
+ getBunServerOptions(): Pick<ServerOptions, 'port' | 'hostname'>;
13
+ }
14
+ /**
15
+ * Bun HTTP server placeholder used before the actual server instance is created.
16
+ * This class provides compatibility methods expected by NestJS framework.
17
+ *
18
+ * There's limitation in Bun where we can't create the server instance without
19
+ * listening on a port right away. This placeholder allows us to defer
20
+ * the server creation until NestJS calls the listen method.
21
+ */
22
+ export declare class BunPreflightHttpServer implements BaseWsInstance {
23
+ private readonly adapter;
24
+ constructor(adapter: BunHttpAdapter);
25
+ on(event: string, callback: Function): void;
26
+ /**
27
+ * NestJS compatibility methods
28
+ * Nest use this to listen for "error" events during HTTP server initialization
29
+ */
30
+ once(): void;
31
+ /**
32
+ * NestJS compatibility methods
33
+ * Nest use this to remove "error" event listeners during HTTP server cleanup
34
+ */
35
+ removeListener(): void;
36
+ /**
37
+ * NestJS compatibility methods
38
+ */
39
+ stop(force?: boolean): Promise<void>;
40
+ address(): {
41
+ address: "0.0.0.0" | "127.0.0.1" | "localhost" | (string & {});
42
+ port: string | number;
43
+ } | {
44
+ address: string | undefined;
45
+ port: number | undefined;
46
+ };
47
+ setWsOptions(options: WsOptions): void;
48
+ registerWsOpenHandler(handler: (ws: ServerWebSocket<unknown>) => void): void;
49
+ registerWsMessageHandler(handler: (ws: ServerWebSocket<unknown>, message: string | ArrayBuffer | Buffer | Buffer[], server: Server<unknown>) => void): void;
50
+ registerWsCloseHandler(handler: (ws: ServerWebSocket<unknown>, code: number, reason: string) => void): void;
51
+ getWsHandlers(): {
52
+ onOpen: ((ws: ServerWebSocket<unknown>) => void) | undefined;
53
+ onMessage: ((ws: ServerWebSocket<unknown>, message: string | ArrayBuffer | Buffer | Buffer[], server: Server<unknown>) => void) | undefined;
54
+ onClose: ((ws: ServerWebSocket<unknown>, code: number, reason: string) => void) | undefined;
55
+ };
56
+ getBunServer(): Server<unknown>;
57
+ /**
58
+ * Proxy method for WebSocket server close
59
+ */
60
+ close(): Promise<void>;
61
+ }
62
+ export {};
@@ -32,15 +32,38 @@ export declare class BunRequest {
32
32
  readonly params: Record<string, string>;
33
33
  constructor(nativeRequest: NativeRequest);
34
34
  /**
35
- * Gets the full URL of the request.
35
+ * Gets a mock socket object for compatibility with Node.js middleware.
36
+ * Some middleware (like Better Auth) check req.socket.encrypted to determine if the connection is HTTPS.
36
37
  *
37
- * @returns The complete URL string
38
+ * @returns A mock socket object with encrypted property
39
+ */
40
+ get socket(): {
41
+ encrypted: boolean;
42
+ };
43
+ /**
44
+ * Gets the URL path and query string of the request.
45
+ * Returns the pathname + search params for Node.js/Express compatibility.
46
+ * For the full URL including protocol and host, use the original() method to access the native request.
47
+ *
48
+ * @returns The URL path and query string (e.g., "/api/users?page=1")
38
49
  * @example
39
50
  * ```typescript
40
- * const url = request.url; // "http://localhost:3000/api/users?page=1"
51
+ * const url = request.url; // "/api/users?page=1"
52
+ * // For full URL: request.original().url // "http://localhost:3000/api/users?page=1"
41
53
  * ```
42
54
  */
43
55
  get url(): string;
56
+ /**
57
+ * Gets the original native Bun request object.
58
+ *
59
+ * @returns The underlying Bun request
60
+ * @example
61
+ * ```typescript
62
+ * const nativeReq = request.original();
63
+ * console.log(nativeReq.url); // Full URL with protocol and host
64
+ * ```
65
+ */
66
+ original(): NativeRequest;
44
67
  /**
45
68
  * Gets the pathname portion of the URL.
46
69
  * Uses lazy parsing for optimal performance - the pathname is only extracted when first accessed.
@@ -17,10 +17,12 @@ export declare class BunResponse {
17
17
  private resolve;
18
18
  private readonly response;
19
19
  private readonly cookieMap;
20
- private _headers;
20
+ private static readonly textDecoder;
21
+ private headers;
21
22
  private statusCode;
22
23
  private ended;
23
- private _cookieHeaderCache;
24
+ private cookieHeaders;
25
+ private chunks;
24
26
  constructor();
25
27
  private get headersMap();
26
28
  /**
@@ -99,6 +101,7 @@ export declare class BunResponse {
99
101
  * Ends the response and sends the body to the client.
100
102
  * Automatically handles JSON serialization, streams, and binary data.
101
103
  * Can only be called once per response.
104
+ * If write() was called before end(), accumulated chunks will be sent.
102
105
  *
103
106
  * @param body - The response body (JSON, string, Uint8Array, StreamableFile, or undefined)
104
107
  * @example
@@ -117,9 +120,24 @@ export declare class BunResponse {
117
120
  * // Send file stream
118
121
  * const file = new StreamableFile(stream);
119
122
  * response.end(file);
123
+ *
124
+ * // Send accumulated chunks from write() calls
125
+ * response.write('Hello ');
126
+ * response.write('World');
127
+ * response.end('!'); // Sends "Hello World!"
120
128
  * ```
121
129
  */
122
130
  end(body?: unknown): void;
131
+ /**
132
+ * Applies cookie headers to the response.
133
+ * According to RFC 6265, each cookie must be sent as a separate Set-Cookie header.
134
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
135
+ */
136
+ private applyCookieHeaders;
137
+ private sendResponse;
138
+ private combineChunks;
139
+ private combineAsString;
140
+ private combineAsBinary;
123
141
  /**
124
142
  * Sets a response header.
125
143
  * Header names are automatically normalized to lowercase.
@@ -134,6 +152,88 @@ export declare class BunResponse {
134
152
  * ```
135
153
  */
136
154
  setHeader(name: string, value: string): void;
155
+ /**
156
+ * Writes the response status and headers (Node.js compatibility method).
157
+ * This method is provided for compatibility with Node.js HTTP response objects.
158
+ *
159
+ * @param statusCode - The HTTP status code
160
+ * @param headers - Optional headers to set
161
+ * @example
162
+ * ```typescript
163
+ * response.writeHead(200, { 'Content-Type': 'application/json' });
164
+ * response.writeHead(404);
165
+ * ```
166
+ */
167
+ writeHead(statusCode: number, headers?: Record<string, string>): void;
168
+ /**
169
+ * Stub method for Node.js EventEmitter compatibility.
170
+ * This is a no-op method provided for compatibility with Node.js HTTP response objects.
171
+ * Some middleware may try to listen for events like 'close' on the response object.
172
+ *
173
+ * @param event - The event name
174
+ * @param listener - The event listener function
175
+ * @returns This response object for chaining
176
+ */
177
+ on(event: string, listener: (...args: unknown[]) => void): this;
178
+ /**
179
+ * Stub method for Node.js EventEmitter compatibility.
180
+ * This is a no-op method provided for compatibility with Node.js HTTP response objects.
181
+ *
182
+ * @param event - The event name
183
+ * @param listener - The event listener function
184
+ * @returns This response object for chaining
185
+ */
186
+ off(event: string, listener: (...args: unknown[]) => void): this;
187
+ /**
188
+ * Property for Node.js HTTP response compatibility.
189
+ * Always returns false since Bun responses don't have a destroyed state.
190
+ */
191
+ readonly destroyed = false;
192
+ /**
193
+ * Stub method for Node.js HTTP response compatibility.
194
+ * This is a no-op method provided for compatibility with Node.js HTTP response objects.
195
+ *
196
+ * @param error - Optional error
197
+ */
198
+ destroy(error?: Error): void;
199
+ /**
200
+ * Stub method for Node.js EventEmitter compatibility.
201
+ * This is a no-op method provided for compatibility with Node.js HTTP response objects.
202
+ *
203
+ * @param event - The event name
204
+ * @param listener - The event listener function
205
+ * @returns This response object for chaining
206
+ */
207
+ once(event: string, listener: (...args: unknown[]) => void): this;
208
+ /**
209
+ * Stub method for Node.js HTTP response compatibility.
210
+ * This method writes data to the response stream.
211
+ * Data is accumulated in a buffer until end() is called.
212
+ * Mimics Node.js behavior where write() can be called multiple times.
213
+ *
214
+ * @param chunk - The data to write (string, Uint8Array, or other types that can be stringified)
215
+ * @returns true if the chunk was successfully buffered, false if the response has already ended
216
+ * @example
217
+ * ```typescript
218
+ * // Write string chunks
219
+ * response.write('Hello ');
220
+ * response.write('World');
221
+ * response.end('!'); // Sends "Hello World!"
222
+ *
223
+ * // Write binary chunks
224
+ * const chunk1 = new Uint8Array([1, 2, 3]);
225
+ * const chunk2 = new Uint8Array([4, 5, 6]);
226
+ * response.write(chunk1);
227
+ * response.write(chunk2);
228
+ * response.end(); // Sends combined binary data
229
+ *
230
+ * // Mixed usage (converts to string)
231
+ * response.write('Status: ');
232
+ * response.write(200);
233
+ * response.end(); // Sends "Status: 200"
234
+ * ```
235
+ */
236
+ write(chunk: unknown): boolean;
137
237
  /**
138
238
  * Gets the value of a response header.
139
239
  * Header lookup is case-insensitive.
@@ -0,0 +1,48 @@
1
+ import { AbstractWsAdapter, BaseWsInstance, MessageMappingProperties } from '@nestjs/websockets';
2
+ import { Observable } from 'rxjs';
3
+ import { INestApplicationContext } from '@nestjs/common';
4
+ import { ServerWebSocket } from 'bun';
5
+ import { BunPreflightHttpServer } from './bun.preflight-http-server.js';
6
+ import { WsOptions } from './internal.types.js';
7
+ export type WsData = string | Buffer | ArrayBuffer | Buffer[];
8
+ export type WsMessageParser<TData = unknown> = (data: WsData) => WsParsedData<TData>;
9
+ export interface WsParsedData<TData = unknown> {
10
+ event: string;
11
+ data: TData;
12
+ }
13
+ export interface BunWsAdapterOptions extends WsOptions {
14
+ messageParser?: WsMessageParser;
15
+ }
16
+ /** Internal data stored on each WebSocket connection - must match bun.adapter.ts */
17
+ interface BunWsClientData {
18
+ /** Called when a message is received - matches bun.adapter.ts onMessageInternal */
19
+ onMessageInternal?: (message: WsData) => void;
20
+ /** Called when the connection closes - matches bun.adapter.ts onCloseInternal */
21
+ onCloseInternal?: () => void;
22
+ /** Called by NestJS for disconnect handling */
23
+ onDisconnect?: (ws: ServerWebSocket<unknown>) => void;
24
+ }
25
+ type BunWsClient = ServerWebSocket<BunWsClientData> & BaseWsInstance;
26
+ /**
27
+ * High-performance WebSocket adapter for Bun runtime with NestJS.
28
+ */
29
+ export declare class BunWsAdapter extends AbstractWsAdapter<BunPreflightHttpServer, BunWsClient> {
30
+ private readonly logger;
31
+ private readonly nestApp;
32
+ private messageParser;
33
+ private onOpenHandler?;
34
+ private globalHandlersInitialized;
35
+ constructor(appOrHttpServer?: INestApplicationContext | object);
36
+ create(_port: number, options?: BunWsAdapterOptions): BunPreflightHttpServer;
37
+ private extractWsOptions;
38
+ close(server: BunPreflightHttpServer): Promise<void>;
39
+ bindClientConnect(server: BunPreflightHttpServer, callback: (client: BunWsClient) => void): void;
40
+ bindClientDisconnect(client: BunWsClient, callback: (client: BunWsClient) => void): void;
41
+ bindMessageHandlers(client: BunWsClient, handlers: MessageMappingProperties[], transform: (data: unknown) => Observable<unknown>): void;
42
+ private initializeGlobalHandlers;
43
+ private buildHandlerMap;
44
+ private processMessage;
45
+ private sendResponse;
46
+ private cleanupClient;
47
+ }
48
+ export {};
package/dist/index.d.ts CHANGED
@@ -1,4 +1,7 @@
1
- export { BunAdapter } from './bun.adapter.js';
2
- export { BunRequest } from './bun.request.js';
3
- export { BunResponse } from './bun.response.js';
4
- export { BunFileInterceptor } from './bun.file.interceptor.js';
1
+ export * from './bun.adapter.js';
2
+ export * from './bun.request.js';
3
+ export * from './bun.response.js';
4
+ export * from './bun.file.interceptor.js';
5
+ export * from './internal.types.js';
6
+ export * from './bun.preflight-http-server.js';
7
+ export * from './bun.ws-adapter.js';