shokupan 0.7.0 → 0.9.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 +53 -0
- package/dist/context.d.ts +50 -15
- package/dist/{http-server-DFhwlK8e.cjs → http-server-BEMPIs33.cjs} +4 -2
- package/dist/http-server-BEMPIs33.cjs.map +1 -0
- package/dist/{http-server-0xH174zz.js → http-server-CCeagTyU.js} +4 -2
- package/dist/http-server-CCeagTyU.js.map +1 -0
- package/dist/index.cjs +998 -136
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +996 -135
- package/dist/index.js.map +1 -1
- package/dist/plugins/application/dashboard/metrics-collector.d.ts +12 -0
- package/dist/plugins/application/dashboard/plugin.d.ts +14 -8
- package/dist/plugins/application/dashboard/static/charts.js +328 -0
- package/dist/plugins/application/dashboard/static/failures.js +85 -0
- package/dist/plugins/application/dashboard/static/graph.mjs +523 -0
- package/dist/plugins/application/dashboard/static/poll.js +146 -0
- package/dist/plugins/application/dashboard/static/reactflow.css +18 -0
- package/dist/plugins/application/dashboard/static/registry.css +131 -0
- package/dist/plugins/application/dashboard/static/registry.js +269 -0
- package/dist/plugins/application/dashboard/static/requests.js +118 -0
- package/dist/plugins/application/dashboard/static/scrollbar.css +24 -0
- package/dist/plugins/application/dashboard/static/styles.css +175 -0
- package/dist/plugins/application/dashboard/static/tables.js +92 -0
- package/dist/plugins/application/dashboard/static/tabs.js +113 -0
- package/dist/plugins/application/dashboard/static/tabulator.css +66 -0
- package/dist/plugins/application/dashboard/template.eta +246 -0
- package/dist/plugins/application/socket-io.d.ts +14 -0
- package/dist/router.d.ts +12 -0
- package/dist/shokupan.d.ts +21 -1
- package/dist/util/datastore.d.ts +4 -3
- package/dist/util/decorators.d.ts +5 -0
- package/dist/util/http-error.d.ts +38 -0
- package/dist/util/http-status.d.ts +30 -0
- package/dist/util/request.d.ts +1 -1
- package/dist/util/symbol.d.ts +19 -0
- package/dist/util/types.d.ts +30 -1
- package/package.json +6 -3
- package/dist/http-server-0xH174zz.js.map +0 -1
- package/dist/http-server-DFhwlK8e.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -243,6 +243,59 @@ app.mount('/api', UserController);
|
|
|
243
243
|
- `@Ctx()` - Access full context
|
|
244
244
|
- `@Req()` - Access request object
|
|
245
245
|
|
|
246
|
+
### WebSockets
|
|
247
|
+
|
|
248
|
+
> Notice: The WebSocket API is currently in an experimental stage and subject to change.
|
|
249
|
+
|
|
250
|
+
Shokupan provides native WebSocket handling and an HTTP Bridge feature.
|
|
251
|
+
|
|
252
|
+
#### Event Decorator
|
|
253
|
+
|
|
254
|
+
Handle WebSocket events directly in your controllers:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
import { Controller, Event, ShokupanContext } from 'shokupan';
|
|
258
|
+
|
|
259
|
+
@Controller('/chat')
|
|
260
|
+
export class ChatController {
|
|
261
|
+
|
|
262
|
+
@Event('message')
|
|
263
|
+
async onMessage(ctx: ShokupanContext) {
|
|
264
|
+
const payload = await ctx.body();
|
|
265
|
+
console.log('Received:', payload);
|
|
266
|
+
|
|
267
|
+
// Reply using underlying socket
|
|
268
|
+
// Native Bun: ctx.socket is ServerWebSocket
|
|
269
|
+
// Socket.IO: ctx.socket is Socket
|
|
270
|
+
ctx.emit('ack', 'received');
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
#### Socket.IO Support
|
|
276
|
+
|
|
277
|
+
Easily integrate Socket.IO and wire up your event decorators via the built-in helper:
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
import { Shokupan } from 'shokupan';
|
|
281
|
+
import { Server } from 'socket.io';
|
|
282
|
+
import { attachSocketIOBridge } from 'shokupan';
|
|
283
|
+
|
|
284
|
+
const app = new Shokupan({ enableHttpBridge: false });
|
|
285
|
+
const server = await app.listen(3000);
|
|
286
|
+
|
|
287
|
+
// Attach Socket.IO
|
|
288
|
+
const io = new Server(server.nodeServer); // For Node.js
|
|
289
|
+
attachSocketIOBridge(io, app);
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
#### HTTP Bridge
|
|
293
|
+
|
|
294
|
+
Expose your HTTP API over WebSockets (set `enableHttpBridge: true` in config).
|
|
295
|
+
|
|
296
|
+
Client sends: `{ type: "HTTP", method: "GET", path: "/api/users", ... }`
|
|
297
|
+
Server responds: `{ type: "RESPONSE", status: 200, body: ... }`
|
|
298
|
+
|
|
246
299
|
### Middleware
|
|
247
300
|
|
|
248
301
|
Middleware functions have access to the context and can control request flow:
|
package/dist/context.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { BodyInit, Server } from 'bun';
|
|
1
|
+
import { BodyInit, Server, ServerWebSocket } from 'bun';
|
|
2
|
+
import { Socket, Server as SocketServer } from 'socket.io';
|
|
2
3
|
import { Shokupan } from './shokupan';
|
|
3
4
|
import { ShokupanRequest } from './util/request';
|
|
4
5
|
import { ShokupanResponse } from './util/response';
|
|
6
|
+
import { $bodyParsed, $bodyParseError, $bodyType, $cachedBody, $cachedHost, $cachedHostname, $cachedOrigin, $cachedProtocol, $cachedQuery, $debug, $finalResponse, $io, $rawBody, $requestId, $routeMatched, $socket, $url, $ws } from './util/symbol';
|
|
5
7
|
import { CookieOptions, HeadersInit, JSXRenderer } from './util/types';
|
|
6
8
|
export interface HandlerStackItem {
|
|
7
9
|
name: string;
|
|
@@ -87,25 +89,30 @@ export declare class ShokupanContext<State extends Record<string, any> = Record<
|
|
|
87
89
|
state: State;
|
|
88
90
|
handlerStack: HandlerStackItem[];
|
|
89
91
|
readonly response: ShokupanResponse;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
private
|
|
94
|
-
private
|
|
95
|
-
private
|
|
96
|
-
private
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
private
|
|
100
|
-
private
|
|
101
|
-
private
|
|
102
|
-
private
|
|
103
|
-
private
|
|
92
|
+
[$debug]?: DebugCollector;
|
|
93
|
+
[$finalResponse]?: Response;
|
|
94
|
+
[$rawBody]?: string | ArrayBuffer | Uint8Array;
|
|
95
|
+
private [$url]?;
|
|
96
|
+
private [$cachedBody]?;
|
|
97
|
+
private [$bodyType]?;
|
|
98
|
+
private [$bodyParsed];
|
|
99
|
+
private [$bodyParseError]?;
|
|
100
|
+
[$routeMatched]: boolean;
|
|
101
|
+
private [$cachedHostname]?;
|
|
102
|
+
private [$cachedProtocol]?;
|
|
103
|
+
private [$cachedHost]?;
|
|
104
|
+
private [$cachedOrigin]?;
|
|
105
|
+
private [$cachedQuery]?;
|
|
106
|
+
private [$ws]?;
|
|
107
|
+
private [$socket]?;
|
|
108
|
+
private [$io]?;
|
|
104
109
|
/**
|
|
105
110
|
* JSX Rendering Function
|
|
106
111
|
*/
|
|
107
112
|
private renderer?;
|
|
108
113
|
setRenderer(renderer: JSXRenderer): void;
|
|
114
|
+
private [$requestId];
|
|
115
|
+
get requestId(): string;
|
|
109
116
|
constructor(request: ShokupanRequest<any>, server?: Server, state?: State, app?: Shokupan, signal?: AbortSignal, // Optional as it might not be provided in tests or simple creates
|
|
110
117
|
enableMiddlewareTracking?: boolean);
|
|
111
118
|
get url(): URL;
|
|
@@ -162,12 +169,34 @@ export declare class ShokupanContext<State extends Record<string, any> = Record<
|
|
|
162
169
|
* Base response object
|
|
163
170
|
*/
|
|
164
171
|
get res(): ShokupanResponse;
|
|
172
|
+
/**
|
|
173
|
+
* Raw WebSocket connection
|
|
174
|
+
*/
|
|
175
|
+
get ws(): ServerWebSocket<undefined>;
|
|
176
|
+
/**
|
|
177
|
+
* Socket.io socket
|
|
178
|
+
*/
|
|
179
|
+
get socket(): Socket<import('socket.io').DefaultEventsMap, import('socket.io').DefaultEventsMap, import('socket.io').DefaultEventsMap, any>;
|
|
180
|
+
/**
|
|
181
|
+
* Socket.io server
|
|
182
|
+
*/
|
|
183
|
+
get io(): SocketServer<import('socket.io').DefaultEventsMap, import('socket.io').DefaultEventsMap, import('socket.io').DefaultEventsMap, any>;
|
|
165
184
|
/**
|
|
166
185
|
* Helper to set a header on the response
|
|
167
186
|
* @param key Header key
|
|
168
187
|
* @param value Header value
|
|
169
188
|
*/
|
|
170
189
|
set(key: string, value: string): this;
|
|
190
|
+
isUpgraded: boolean;
|
|
191
|
+
/**
|
|
192
|
+
* Upgrades the request to a WebSocket connection.
|
|
193
|
+
* @param options Upgrade options
|
|
194
|
+
* @returns true if upgraded, false otherwise
|
|
195
|
+
*/
|
|
196
|
+
upgrade(options?: {
|
|
197
|
+
data?: any;
|
|
198
|
+
headers?: HeadersInit;
|
|
199
|
+
}): boolean;
|
|
171
200
|
/**
|
|
172
201
|
* Set a cookie
|
|
173
202
|
* @param name Cookie name
|
|
@@ -200,6 +229,12 @@ export declare class ShokupanContext<State extends Record<string, any> = Record<
|
|
|
200
229
|
* @returns Response
|
|
201
230
|
*/
|
|
202
231
|
send(body?: BodyInit, options?: ResponseInit): Response;
|
|
232
|
+
/**
|
|
233
|
+
* Emit an event to the client (WebSocket only)
|
|
234
|
+
* @param event Event name
|
|
235
|
+
* @param data Event data (Must be JSON serializable)
|
|
236
|
+
*/
|
|
237
|
+
emit(event: string, data?: any): void;
|
|
203
238
|
/**
|
|
204
239
|
* Respond with a JSON object
|
|
205
240
|
*/
|
|
@@ -70,7 +70,9 @@ function createHttpServer() {
|
|
|
70
70
|
requestIP: (req) => null,
|
|
71
71
|
publish: () => 0,
|
|
72
72
|
subscriberCount: () => 0,
|
|
73
|
-
url: new URL(`http://${options.hostname}:${options.port}`)
|
|
73
|
+
url: new URL(`http://${options.hostname}:${options.port}`),
|
|
74
|
+
// Expose the raw Node.js server for generic socket/websocket support (e.g. Socket.IO)
|
|
75
|
+
nodeServer: server
|
|
74
76
|
};
|
|
75
77
|
return new Promise((resolve) => {
|
|
76
78
|
server.listen(options.port, options.hostname, () => {
|
|
@@ -80,4 +82,4 @@ function createHttpServer() {
|
|
|
80
82
|
};
|
|
81
83
|
}
|
|
82
84
|
exports.createHttpServer = createHttpServer;
|
|
83
|
-
//# sourceMappingURL=http-server-
|
|
85
|
+
//# sourceMappingURL=http-server-BEMPIs33.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-server-BEMPIs33.cjs","sources":["../src/plugins/application/http-server.ts"],"sourcesContent":["import type { Server } from \"bun\";\nimport * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport type { ServerFactory } from \"../../util/types\";\n\n/**\n * Creates a server factory that uses the standard Node.js `http` module.\n * @returns A ServerFactory compatible with Shokupan.\n */\nexport function createHttpServer(): ServerFactory {\n return async (options: any): Promise<Server> => {\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url!, `http://${req.headers.host}`);\n const request = new Request(url.toString(), {\n method: req.method,\n headers: req.headers as any,\n body: ['GET', 'HEAD'].includes(req.method!) ? undefined : new ReadableStream({\n start(controller) {\n req.on('data', chunk => controller.enqueue(chunk));\n req.on('end', () => controller.close());\n req.on('error', err => controller.error(err));\n }\n }) as any,\n // Required for Node.js undici when sending a body\n duplex: 'half'\n } as any);\n\n const response = await options.fetch(request, fauxServer);\n\n res.statusCode = response.status;\n response.headers.forEach((v, k) => res.setHeader(k, v));\n\n if (response.body) {\n // Optimize: Use arrayBuffer for direct conversion instead of async iteration\n const buffer = await response.arrayBuffer();\n res.end(Buffer.from(buffer));\n } else {\n res.end();\n }\n });\n\n const fauxServer: Server = {\n stop: () => {\n server.close();\n return Promise.resolve(); // Bun.Server stop usually returns void but in type definition it might vary.\n },\n upgrade(req, options) {\n return false;\n },\n reload(options) {\n return fauxServer as any;\n },\n get port() {\n const addr = server.address();\n if (typeof addr === 'object' && addr !== null) {\n return addr.port;\n }\n return options.port;\n },\n hostname: options.hostname,\n development: options.development,\n pendingRequests: 0,\n requestIP: (req) => null,\n publish: () => 0,\n subscriberCount: () => 0,\n url: new URL(`http://${options.hostname}:${options.port}`),\n // Expose the raw Node.js server for generic socket/websocket support (e.g. Socket.IO)\n nodeServer: server\n } as unknown as Server;\n\n return new Promise((resolve) => {\n server.listen(options.port, options.hostname, () => {\n resolve(fauxServer);\n });\n });\n };\n}\n\n/**\n * Creates a server factory that uses the standard Node.js `https` module.\n * @param sslOptions - Node.js HTTPS options (key, cert, etc.)\n * @returns A ServerFactory compatible with Shokupan.\n */\nexport function createHttpsServer(sslOptions: https.ServerOptions): ServerFactory {\n return async (options: any): Promise<Server> => {\n const server = https.createServer(sslOptions, async (req, res) => {\n const url = new URL(req.url!, `https://${req.headers.host}`);\n const request = new Request(url.toString(), {\n method: req.method,\n headers: req.headers as any,\n body: ['GET', 'HEAD'].includes(req.method!) ? undefined : new ReadableStream({\n start(controller) {\n req.on('data', chunk => controller.enqueue(chunk));\n req.on('end', () => controller.close());\n req.on('error', err => controller.error(err));\n }\n }) as any,\n // Required for Node.js undici when sending a body\n duplex: 'half'\n } as any);\n\n const response = await options.fetch(request, fauxServer);\n\n res.statusCode = response.status;\n response.headers.forEach((v, k) => res.setHeader(k, v));\n\n if (response.body) {\n // Optimize: Use arrayBuffer for direct conversion instead of async iteration\n const buffer = await response.arrayBuffer();\n res.end(Buffer.from(buffer));\n } else {\n res.end();\n }\n });\n\n const fauxServer: Server = {\n stop: () => {\n server.close();\n },\n upgrade(req, options) {\n return false;\n },\n reload(options) {\n return fauxServer as any;\n },\n get port() {\n const addr = server.address();\n if (typeof addr === 'object' && addr !== null) {\n return addr.port;\n }\n return options.port;\n },\n hostname: options.hostname,\n development: options.development,\n pendingRequests: 0,\n requestIP: (req) => null,\n publish: () => 0,\n subscriberCount: () => 0,\n url: new URL(`https://${options.hostname}:${options.port}`),\n // Expose the raw Node.js server for generic socket/websocket support\n nodeServer: server\n } as unknown as Server;\n\n return new Promise((resolve) => {\n server.listen(options.port, options.hostname, () => {\n resolve(fauxServer);\n });\n });\n };\n}\n"],"names":["http","options"],"mappings":";;;;;;;;;;;;;;;;;;;;;AASO,SAAS,mBAAkC;AAC9C,SAAO,OAAO,YAAkC;AAC5C,UAAM,SAASA,gBAAK,aAAa,OAAO,KAAK,QAAQ;AACjD,YAAM,MAAM,IAAI,IAAI,IAAI,KAAM,UAAU,IAAI,QAAQ,IAAI,EAAE;AAC1D,YAAM,UAAU,IAAI,QAAQ,IAAI,YAAY;AAAA,QACxC,QAAQ,IAAI;AAAA,QACZ,SAAS,IAAI;AAAA,QACb,MAAM,CAAC,OAAO,MAAM,EAAE,SAAS,IAAI,MAAO,IAAI,SAAY,IAAI,eAAe;AAAA,UACzE,MAAM,YAAY;AACd,gBAAI,GAAG,QAAQ,CAAA,UAAS,WAAW,QAAQ,KAAK,CAAC;AACjD,gBAAI,GAAG,OAAO,MAAM,WAAW,OAAO;AACtC,gBAAI,GAAG,SAAS,CAAA,QAAO,WAAW,MAAM,GAAG,CAAC;AAAA,UAChD;AAAA,QAAA,CACH;AAAA;AAAA,QAED,QAAQ;AAAA,MAAA,CACJ;AAER,YAAM,WAAW,MAAM,QAAQ,MAAM,SAAS,UAAU;AAExD,UAAI,aAAa,SAAS;AAC1B,eAAS,QAAQ,QAAQ,CAAC,GAAG,MAAM,IAAI,UAAU,GAAG,CAAC,CAAC;AAEtD,UAAI,SAAS,MAAM;AAEf,cAAM,SAAS,MAAM,SAAS,YAAA;AAC9B,YAAI,IAAI,OAAO,KAAK,MAAM,CAAC;AAAA,MAC/B,OAAO;AACH,YAAI,IAAA;AAAA,MACR;AAAA,IACJ,CAAC;AAED,UAAM,aAAqB;AAAA,MACvB,MAAM,MAAM;AACR,eAAO,MAAA;AACP,eAAO,QAAQ,QAAA;AAAA,MACnB;AAAA,MACA,QAAQ,KAAKC,UAAS;AAClB,eAAO;AAAA,MACX;AAAA,MACA,OAAOA,UAAS;AACZ,eAAO;AAAA,MACX;AAAA,MACA,IAAI,OAAO;AACP,cAAM,OAAO,OAAO,QAAA;AACpB,YAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC3C,iBAAO,KAAK;AAAA,QAChB;AACA,eAAO,QAAQ;AAAA,MACnB;AAAA,MACA,UAAU,QAAQ;AAAA,MAClB,aAAa,QAAQ;AAAA,MACrB,iBAAiB;AAAA,MACjB,WAAW,CAAC,QAAQ;AAAA,MACpB,SAAS,MAAM;AAAA,MACf,iBAAiB,MAAM;AAAA,MACvB,KAAK,IAAI,IAAI,UAAU,QAAQ,QAAQ,IAAI,QAAQ,IAAI,EAAE;AAAA;AAAA,MAEzD,YAAY;AAAA,IAAA;AAGhB,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC5B,aAAO,OAAO,QAAQ,MAAM,QAAQ,UAAU,MAAM;AAChD,gBAAQ,UAAU;AAAA,MACtB,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AACJ;;"}
|
|
@@ -51,7 +51,9 @@ function createHttpServer() {
|
|
|
51
51
|
requestIP: (req) => null,
|
|
52
52
|
publish: () => 0,
|
|
53
53
|
subscriberCount: () => 0,
|
|
54
|
-
url: new URL(`http://${options.hostname}:${options.port}`)
|
|
54
|
+
url: new URL(`http://${options.hostname}:${options.port}`),
|
|
55
|
+
// Expose the raw Node.js server for generic socket/websocket support (e.g. Socket.IO)
|
|
56
|
+
nodeServer: server
|
|
55
57
|
};
|
|
56
58
|
return new Promise((resolve) => {
|
|
57
59
|
server.listen(options.port, options.hostname, () => {
|
|
@@ -63,4 +65,4 @@ function createHttpServer() {
|
|
|
63
65
|
export {
|
|
64
66
|
createHttpServer
|
|
65
67
|
};
|
|
66
|
-
//# sourceMappingURL=http-server-
|
|
68
|
+
//# sourceMappingURL=http-server-CCeagTyU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-server-CCeagTyU.js","sources":["../src/plugins/application/http-server.ts"],"sourcesContent":["import type { Server } from \"bun\";\nimport * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport type { ServerFactory } from \"../../util/types\";\n\n/**\n * Creates a server factory that uses the standard Node.js `http` module.\n * @returns A ServerFactory compatible with Shokupan.\n */\nexport function createHttpServer(): ServerFactory {\n return async (options: any): Promise<Server> => {\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url!, `http://${req.headers.host}`);\n const request = new Request(url.toString(), {\n method: req.method,\n headers: req.headers as any,\n body: ['GET', 'HEAD'].includes(req.method!) ? undefined : new ReadableStream({\n start(controller) {\n req.on('data', chunk => controller.enqueue(chunk));\n req.on('end', () => controller.close());\n req.on('error', err => controller.error(err));\n }\n }) as any,\n // Required for Node.js undici when sending a body\n duplex: 'half'\n } as any);\n\n const response = await options.fetch(request, fauxServer);\n\n res.statusCode = response.status;\n response.headers.forEach((v, k) => res.setHeader(k, v));\n\n if (response.body) {\n // Optimize: Use arrayBuffer for direct conversion instead of async iteration\n const buffer = await response.arrayBuffer();\n res.end(Buffer.from(buffer));\n } else {\n res.end();\n }\n });\n\n const fauxServer: Server = {\n stop: () => {\n server.close();\n return Promise.resolve(); // Bun.Server stop usually returns void but in type definition it might vary.\n },\n upgrade(req, options) {\n return false;\n },\n reload(options) {\n return fauxServer as any;\n },\n get port() {\n const addr = server.address();\n if (typeof addr === 'object' && addr !== null) {\n return addr.port;\n }\n return options.port;\n },\n hostname: options.hostname,\n development: options.development,\n pendingRequests: 0,\n requestIP: (req) => null,\n publish: () => 0,\n subscriberCount: () => 0,\n url: new URL(`http://${options.hostname}:${options.port}`),\n // Expose the raw Node.js server for generic socket/websocket support (e.g. Socket.IO)\n nodeServer: server\n } as unknown as Server;\n\n return new Promise((resolve) => {\n server.listen(options.port, options.hostname, () => {\n resolve(fauxServer);\n });\n });\n };\n}\n\n/**\n * Creates a server factory that uses the standard Node.js `https` module.\n * @param sslOptions - Node.js HTTPS options (key, cert, etc.)\n * @returns A ServerFactory compatible with Shokupan.\n */\nexport function createHttpsServer(sslOptions: https.ServerOptions): ServerFactory {\n return async (options: any): Promise<Server> => {\n const server = https.createServer(sslOptions, async (req, res) => {\n const url = new URL(req.url!, `https://${req.headers.host}`);\n const request = new Request(url.toString(), {\n method: req.method,\n headers: req.headers as any,\n body: ['GET', 'HEAD'].includes(req.method!) ? undefined : new ReadableStream({\n start(controller) {\n req.on('data', chunk => controller.enqueue(chunk));\n req.on('end', () => controller.close());\n req.on('error', err => controller.error(err));\n }\n }) as any,\n // Required for Node.js undici when sending a body\n duplex: 'half'\n } as any);\n\n const response = await options.fetch(request, fauxServer);\n\n res.statusCode = response.status;\n response.headers.forEach((v, k) => res.setHeader(k, v));\n\n if (response.body) {\n // Optimize: Use arrayBuffer for direct conversion instead of async iteration\n const buffer = await response.arrayBuffer();\n res.end(Buffer.from(buffer));\n } else {\n res.end();\n }\n });\n\n const fauxServer: Server = {\n stop: () => {\n server.close();\n },\n upgrade(req, options) {\n return false;\n },\n reload(options) {\n return fauxServer as any;\n },\n get port() {\n const addr = server.address();\n if (typeof addr === 'object' && addr !== null) {\n return addr.port;\n }\n return options.port;\n },\n hostname: options.hostname,\n development: options.development,\n pendingRequests: 0,\n requestIP: (req) => null,\n publish: () => 0,\n subscriberCount: () => 0,\n url: new URL(`https://${options.hostname}:${options.port}`),\n // Expose the raw Node.js server for generic socket/websocket support\n nodeServer: server\n } as unknown as Server;\n\n return new Promise((resolve) => {\n server.listen(options.port, options.hostname, () => {\n resolve(fauxServer);\n });\n });\n };\n}\n"],"names":["options"],"mappings":";;AASO,SAAS,mBAAkC;AAC9C,SAAO,OAAO,YAAkC;AAC5C,UAAM,SAAS,KAAK,aAAa,OAAO,KAAK,QAAQ;AACjD,YAAM,MAAM,IAAI,IAAI,IAAI,KAAM,UAAU,IAAI,QAAQ,IAAI,EAAE;AAC1D,YAAM,UAAU,IAAI,QAAQ,IAAI,YAAY;AAAA,QACxC,QAAQ,IAAI;AAAA,QACZ,SAAS,IAAI;AAAA,QACb,MAAM,CAAC,OAAO,MAAM,EAAE,SAAS,IAAI,MAAO,IAAI,SAAY,IAAI,eAAe;AAAA,UACzE,MAAM,YAAY;AACd,gBAAI,GAAG,QAAQ,CAAA,UAAS,WAAW,QAAQ,KAAK,CAAC;AACjD,gBAAI,GAAG,OAAO,MAAM,WAAW,OAAO;AACtC,gBAAI,GAAG,SAAS,CAAA,QAAO,WAAW,MAAM,GAAG,CAAC;AAAA,UAChD;AAAA,QAAA,CACH;AAAA;AAAA,QAED,QAAQ;AAAA,MAAA,CACJ;AAER,YAAM,WAAW,MAAM,QAAQ,MAAM,SAAS,UAAU;AAExD,UAAI,aAAa,SAAS;AAC1B,eAAS,QAAQ,QAAQ,CAAC,GAAG,MAAM,IAAI,UAAU,GAAG,CAAC,CAAC;AAEtD,UAAI,SAAS,MAAM;AAEf,cAAM,SAAS,MAAM,SAAS,YAAA;AAC9B,YAAI,IAAI,OAAO,KAAK,MAAM,CAAC;AAAA,MAC/B,OAAO;AACH,YAAI,IAAA;AAAA,MACR;AAAA,IACJ,CAAC;AAED,UAAM,aAAqB;AAAA,MACvB,MAAM,MAAM;AACR,eAAO,MAAA;AACP,eAAO,QAAQ,QAAA;AAAA,MACnB;AAAA,MACA,QAAQ,KAAKA,UAAS;AAClB,eAAO;AAAA,MACX;AAAA,MACA,OAAOA,UAAS;AACZ,eAAO;AAAA,MACX;AAAA,MACA,IAAI,OAAO;AACP,cAAM,OAAO,OAAO,QAAA;AACpB,YAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC3C,iBAAO,KAAK;AAAA,QAChB;AACA,eAAO,QAAQ;AAAA,MACnB;AAAA,MACA,UAAU,QAAQ;AAAA,MAClB,aAAa,QAAQ;AAAA,MACrB,iBAAiB;AAAA,MACjB,WAAW,CAAC,QAAQ;AAAA,MACpB,SAAS,MAAM;AAAA,MACf,iBAAiB,MAAM;AAAA,MACvB,KAAK,IAAI,IAAI,UAAU,QAAQ,QAAQ,IAAI,QAAQ,IAAI,EAAE;AAAA;AAAA,MAEzD,YAAY;AAAA,IAAA;AAGhB,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC5B,aAAO,OAAO,QAAQ,MAAM,QAAQ,UAAU,MAAM;AAChD,gBAAQ,UAAU;AAAA,MACtB,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AACJ;"}
|