lacis 0.2.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,2075 @@
1
+ import path2 from 'path';
2
+ import fs from 'fs/promises';
3
+ import cluster3 from 'cluster';
4
+ import http2, { IncomingMessage, ServerResponse } from 'http';
5
+ import https from 'https';
6
+ import * as os from 'os';
7
+ import os__default from 'os';
8
+ import { EventEmitter } from 'events';
9
+ import { Socket } from 'net';
10
+
11
+ // src/core/middleware.ts
12
+ var globalMiddlewares = {
13
+ beforeRequest: [],
14
+ afterRequest: [],
15
+ onError: []
16
+ };
17
+ var pathMiddlewares = /* @__PURE__ */ new Map();
18
+ function addMiddleware(middlewareName, callback) {
19
+ globalMiddlewares[middlewareName].push(callback);
20
+ return {
21
+ remove: () => {
22
+ const index = globalMiddlewares[middlewareName].indexOf(callback);
23
+ if (index !== -1) {
24
+ globalMiddlewares[middlewareName].splice(index, 1);
25
+ }
26
+ }
27
+ };
28
+ }
29
+ function addPathMiddleware(path3, type, callback) {
30
+ const normalizedPath = path3 === "/" ? "/" : path3.replace(/\/+$/, "");
31
+ if (!pathMiddlewares.has(normalizedPath)) {
32
+ pathMiddlewares.set(normalizedPath, {
33
+ beforeRequest: [],
34
+ afterRequest: [],
35
+ onError: []
36
+ });
37
+ }
38
+ const middlewares = pathMiddlewares.get(normalizedPath);
39
+ middlewares[type].push(callback);
40
+ return {
41
+ remove: () => {
42
+ if (pathMiddlewares.has(normalizedPath)) {
43
+ const middlewares2 = pathMiddlewares.get(normalizedPath);
44
+ const index = middlewares2[type].indexOf(callback);
45
+ if (index !== -1) {
46
+ middlewares2[type].splice(index, 1);
47
+ }
48
+ }
49
+ }
50
+ };
51
+ }
52
+ function collectMiddleware(url) {
53
+ const normalizedUrl = url === "/" ? "/" : url.replace(/\/+$/, "");
54
+ const segments = normalizedUrl.split("/").filter(Boolean);
55
+ const result = {
56
+ beforeRequest: [...globalMiddlewares.beforeRequest],
57
+ afterRequest: [...globalMiddlewares.afterRequest],
58
+ onError: [...globalMiddlewares.onError]
59
+ };
60
+ let currentPath = "";
61
+ if (pathMiddlewares.has("/")) {
62
+ const rootMiddleware = pathMiddlewares.get("/");
63
+ result.beforeRequest.push(...rootMiddleware.beforeRequest);
64
+ result.afterRequest.push(...rootMiddleware.afterRequest);
65
+ result.onError.push(...rootMiddleware.onError);
66
+ }
67
+ for (const segment of segments) {
68
+ currentPath += "/" + segment;
69
+ if (pathMiddlewares.has(currentPath)) {
70
+ const middleware = pathMiddlewares.get(currentPath);
71
+ result.beforeRequest.push(...middleware.beforeRequest);
72
+ result.afterRequest.push(...middleware.afterRequest);
73
+ result.onError.push(...middleware.onError);
74
+ }
75
+ }
76
+ return result;
77
+ }
78
+ function hasMiddlewares() {
79
+ if (globalMiddlewares.beforeRequest.length > 0 || globalMiddlewares.afterRequest.length > 0 || globalMiddlewares.onError.length > 0) return true;
80
+ return pathMiddlewares.size > 0;
81
+ }
82
+ async function runMiddlewares(middlewareName, req, res, context) {
83
+ const url = req.url?.split("?")[0] || "/";
84
+ const middleware = collectMiddleware(url);
85
+ if (middleware[middlewareName].length === 0) {
86
+ return true;
87
+ }
88
+ for (const handler of middleware[middlewareName]) {
89
+ try {
90
+ const result = await handler(req, res, context);
91
+ if (result === false) return false;
92
+ } catch (error) {
93
+ if (middlewareName !== "onError") {
94
+ try {
95
+ for (const errorHandler of middleware.onError) {
96
+ await errorHandler(req, res, { error, phase: middlewareName });
97
+ }
98
+ } catch (e) {
99
+ console.error("Error in error handler:", e);
100
+ }
101
+ }
102
+ return false;
103
+ }
104
+ }
105
+ return true;
106
+ }
107
+ async function loadMiddlewares(routesDir) {
108
+ pathMiddlewares.clear();
109
+ async function scanDir(dir, prefix = "") {
110
+ try {
111
+ const entries = await fs.readdir(dir, { withFileTypes: true });
112
+ const middlewareFile = entries.find(
113
+ (entry) => entry.name === "+middleware.ts" || entry.name === "+middleware.js"
114
+ );
115
+ if (middlewareFile) {
116
+ const fullPath = path2.join(dir, middlewareFile.name);
117
+ try {
118
+ const absolutePath = path2.resolve(fullPath);
119
+ const middlewareModule = await import(`${absolutePath}?update=${Date.now()}`);
120
+ if (!pathMiddlewares.has(prefix)) {
121
+ pathMiddlewares.set(prefix, {
122
+ beforeRequest: [],
123
+ afterRequest: [],
124
+ onError: []
125
+ });
126
+ }
127
+ if (middlewareModule.beforeRequest) {
128
+ const middlewares = Array.isArray(middlewareModule.beforeRequest) ? middlewareModule.beforeRequest : [middlewareModule.beforeRequest];
129
+ pathMiddlewares.get(prefix).beforeRequest.push(...middlewares);
130
+ }
131
+ if (middlewareModule.afterRequest) {
132
+ const middlewares = Array.isArray(middlewareModule.afterRequest) ? middlewareModule.afterRequest : [middlewareModule.afterRequest];
133
+ pathMiddlewares.get(prefix).afterRequest.push(...middlewares);
134
+ }
135
+ if (middlewareModule.onError) {
136
+ const middlewares = Array.isArray(middlewareModule.onError) ? middlewareModule.onError : [middlewareModule.onError];
137
+ pathMiddlewares.get(prefix).onError.push(...middlewares);
138
+ }
139
+ } catch (error) {
140
+ console.error(`Error loading middleware for ${prefix}:`, error);
141
+ }
142
+ }
143
+ for (const entry of entries) {
144
+ if (entry.isDirectory()) {
145
+ await scanDir(
146
+ path2.join(dir, entry.name),
147
+ `${prefix === "/" ? "" : prefix}/${entry.name}`
148
+ );
149
+ }
150
+ }
151
+ } catch (error) {
152
+ console.error(`Error scanning directory ${dir}:`, error);
153
+ }
154
+ }
155
+ await scanDir(routesDir, "/");
156
+ }
157
+ function getPathMiddlewares() {
158
+ return pathMiddlewares;
159
+ }
160
+ function resetMiddlewares() {
161
+ globalMiddlewares.beforeRequest = [];
162
+ globalMiddlewares.afterRequest = [];
163
+ globalMiddlewares.onError = [];
164
+ pathMiddlewares.clear();
165
+ }
166
+ function registerMiddlewareConfig(config) {
167
+ if (!config) return;
168
+ for (const type of ["beforeRequest", "afterRequest", "onError"]) {
169
+ const handlers = config[type];
170
+ if (handlers) {
171
+ const arr = Array.isArray(handlers) ? handlers : [handlers];
172
+ for (const h of arr) addMiddleware(type, h);
173
+ }
174
+ }
175
+ }
176
+
177
+ // src/core/cors.ts
178
+ function isOriginAllowed(origin, allowed) {
179
+ if (!allowed || allowed === "*") return true;
180
+ if (typeof allowed === "string") return origin === allowed;
181
+ if (Array.isArray(allowed)) return allowed.includes(origin);
182
+ if (allowed instanceof RegExp) return allowed.test(origin);
183
+ if (typeof allowed === "function") return allowed(origin);
184
+ return false;
185
+ }
186
+ function createCorsMiddleware(config) {
187
+ const methods = (config.methods ?? ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"]).join(", ");
188
+ const allowedHeaders = (config.allowedHeaders ?? ["Content-Type", "Authorization"]).join(", ");
189
+ const exposedHeaders = config.exposedHeaders?.join(", ");
190
+ const maxAge = config.maxAge != null ? String(config.maxAge) : null;
191
+ const isWildcard = !config.origin || config.origin === "*";
192
+ return async (req, res) => {
193
+ const origin = req.getHeader("origin");
194
+ if (!origin) return;
195
+ if (!isOriginAllowed(origin, config.origin)) return;
196
+ const useWildcard = isWildcard && !config.credentials;
197
+ res.setHeader("Access-Control-Allow-Origin", useWildcard ? "*" : origin);
198
+ if (!useWildcard) res.setHeader("Vary", "Origin");
199
+ if (config.credentials) res.setHeader("Access-Control-Allow-Credentials", "true");
200
+ if (exposedHeaders) res.setHeader("Access-Control-Expose-Headers", exposedHeaders);
201
+ if (req.method === "OPTIONS") {
202
+ res.setHeader("Access-Control-Allow-Methods", methods);
203
+ res.setHeader("Access-Control-Allow-Headers", allowedHeaders);
204
+ if (maxAge) res.setHeader("Access-Control-Max-Age", maxAge);
205
+ res.status(204);
206
+ res.end();
207
+ return false;
208
+ }
209
+ };
210
+ }
211
+ function registerCorsConfig(cors) {
212
+ if (!cors) return;
213
+ addMiddleware("beforeRequest", createCorsMiddleware(cors));
214
+ }
215
+ function primaryLog(...args) {
216
+ if (cluster3.isPrimary) {
217
+ console.log(...args);
218
+ }
219
+ }
220
+
221
+ // src/core/router.ts
222
+ function parsePattern(pattern) {
223
+ const match = pattern.match(/^\[(\w+)(\??)]/);
224
+ if (match) return { name: match[1], isParam: true, isOptional: match[2] === "?" };
225
+ return { name: pattern, isParam: false, isOptional: false };
226
+ }
227
+ var CACHE_MAX = 1e3;
228
+ var CACHE_EVICT = 100;
229
+ var Router = class {
230
+ rootNode;
231
+ cachedRoutes;
232
+ routeCount;
233
+ lastLoaded;
234
+ verbose;
235
+ constructor() {
236
+ this.rootNode = this.createNode();
237
+ this.cachedRoutes = /* @__PURE__ */ new Map();
238
+ this.routeCount = 0;
239
+ this.lastLoaded = 0;
240
+ this.verbose = false;
241
+ }
242
+ createNode() {
243
+ return {
244
+ handlers: /* @__PURE__ */ Object.create(null),
245
+ staticChildren: /* @__PURE__ */ new Map(),
246
+ paramChild: null,
247
+ wildcardHandler: null,
248
+ isEndpoint: false
249
+ };
250
+ }
251
+ addRoute(method, routePath, handler) {
252
+ const segments = routePath.split("/").filter(Boolean);
253
+ let node = this.rootNode;
254
+ for (const segment of segments) {
255
+ const parsed = parsePattern(segment);
256
+ if (parsed.isParam) {
257
+ if (!node.paramChild) {
258
+ node.paramChild = { name: parsed.name, node: this.createNode(), isOptional: parsed.isOptional };
259
+ } else if (node.paramChild.name !== parsed.name) {
260
+ throw new Error(
261
+ `Route conflict: param name "[${parsed.name}]" conflicts with existing "[${node.paramChild.name}]" at the same path position. Use the same param name for all methods at this level.`
262
+ );
263
+ }
264
+ node = node.paramChild.node;
265
+ } else if (segment === "*") {
266
+ if (!node.wildcardHandler) node.wildcardHandler = /* @__PURE__ */ Object.create(null);
267
+ node.wildcardHandler[method] = handler;
268
+ this.cachedRoutes.clear();
269
+ return this;
270
+ } else {
271
+ if (!node.staticChildren.has(segment)) node.staticChildren.set(segment, this.createNode());
272
+ node = node.staticChildren.get(segment);
273
+ }
274
+ }
275
+ node.isEndpoint = true;
276
+ if (!node.handlers[method]) this.routeCount++;
277
+ node.handlers[method] = handler;
278
+ this.cachedRoutes.clear();
279
+ return this;
280
+ }
281
+ findRoute(method, url) {
282
+ const normalizedUrl = url === "/" ? "/" : url.replace(/\/+$/, "");
283
+ const cacheKey = method + ":" + normalizedUrl;
284
+ const cached = this.cachedRoutes.get(cacheKey);
285
+ if (cached) return cached;
286
+ const segments = normalizedUrl === "/" ? [] : normalizedUrl.split("/").filter(Boolean);
287
+ const params = /* @__PURE__ */ Object.create(null);
288
+ const result = this.traverse(this.rootNode, segments, method, params, 0) ?? { handler: null, params: /* @__PURE__ */ Object.create(null) };
289
+ if (this.cachedRoutes.size >= CACHE_MAX) {
290
+ let evicted = 0;
291
+ for (const key of this.cachedRoutes.keys()) {
292
+ this.cachedRoutes.delete(key);
293
+ if (++evicted >= CACHE_EVICT) break;
294
+ }
295
+ }
296
+ this.cachedRoutes.set(cacheKey, result);
297
+ return result;
298
+ }
299
+ traverse(node, segments, method, params, index) {
300
+ if (index === segments.length) {
301
+ if (node.isEndpoint) {
302
+ const handler = node.handlers[method] ?? (method === "HEAD" ? node.handlers["GET"] : void 0) ?? node.handlers[""];
303
+ if (handler) return { handler, params: { ...params } };
304
+ const allowed = Object.keys(node.handlers).filter((m) => m !== "");
305
+ return { handler: null, params: {}, allowedMethods: allowed };
306
+ }
307
+ const oc = node.paramChild;
308
+ if (oc?.isOptional && oc.node.isEndpoint) {
309
+ const handler = oc.node.handlers[method] ?? (method === "HEAD" ? oc.node.handlers["GET"] : void 0) ?? oc.node.handlers[""];
310
+ if (handler) return { handler, params: { ...params } };
311
+ }
312
+ return null;
313
+ }
314
+ const segment = segments[index];
315
+ const staticChild = node.staticChildren.get(segment);
316
+ if (staticChild) {
317
+ const found = this.traverse(staticChild, segments, method, params, index + 1);
318
+ if (found) return found;
319
+ }
320
+ if (node.paramChild) {
321
+ const { name, node: child } = node.paramChild;
322
+ params[name] = segment;
323
+ const found = this.traverse(child, segments, method, params, index + 1);
324
+ if (found) return found;
325
+ delete params[name];
326
+ }
327
+ if (node.wildcardHandler) {
328
+ const handler = node.wildcardHandler[method] ?? node.wildcardHandler[""];
329
+ const rest = segments.slice(index).join("/");
330
+ if (handler) return { handler, params: { ...params, "*": rest } };
331
+ const allowed = Object.keys(node.wildcardHandler).filter((m) => m !== "");
332
+ if (allowed.length > 0) return { handler: null, params: { ...params, "*": rest }, allowedMethods: allowed };
333
+ }
334
+ return null;
335
+ }
336
+ async loadRoutes(routesDir) {
337
+ this.rootNode = this.createNode();
338
+ this.cachedRoutes.clear();
339
+ this.routeCount = 0;
340
+ await loadMiddlewares(routesDir);
341
+ const self = this;
342
+ async function scanDir(dir, currentPath = []) {
343
+ try {
344
+ const entries = await fs.readdir(dir, { withFileTypes: true });
345
+ const indexFile = entries.find(
346
+ (e) => !e.isDirectory() && (e.name === "index.ts" || e.name === "index.js")
347
+ );
348
+ if (indexFile) {
349
+ try {
350
+ const absolutePath = path2.resolve(path2.join(dir, indexFile.name));
351
+ const module = await import(`${absolutePath}?update=${Date.now()}`);
352
+ const methods = ["GET", "POST", "PUT", "DELETE", "PATCH"];
353
+ const routePath = "/" + currentPath.join("/");
354
+ let registered = false;
355
+ for (const method of methods) {
356
+ if (typeof module[method] === "function") {
357
+ self.addRoute(method, routePath, module[method]);
358
+ registered = true;
359
+ }
360
+ }
361
+ if (!registered && typeof module.default === "function") {
362
+ self.addRoute("GET", routePath, module.default);
363
+ }
364
+ if (self.verbose) primaryLog(`Route loaded: ${routePath}`);
365
+ } catch (error) {
366
+ console.error(`Error loading index in ${dir}:`, error);
367
+ }
368
+ }
369
+ for (const entry of entries) {
370
+ if (entry.isDirectory()) {
371
+ const paramMatch = entry.name.match(/^\[(\w+)(\??)]/);
372
+ const segmentName = paramMatch ? `[${paramMatch[1]}${paramMatch[2]}]` : entry.name;
373
+ await scanDir(path2.join(dir, entry.name), [...currentPath, segmentName]);
374
+ }
375
+ }
376
+ } catch (error) {
377
+ console.error(`Error scanning directory ${dir}:`, error);
378
+ }
379
+ }
380
+ await scanDir(routesDir);
381
+ this.lastLoaded = Date.now();
382
+ if (this.verbose) primaryLog(`Loading completed: ${this.routeCount} routes`);
383
+ return true;
384
+ }
385
+ getRoutes() {
386
+ const result = [];
387
+ this.collectRoutes(this.rootNode, [], result);
388
+ return result;
389
+ }
390
+ collectRoutes(node, segments, result) {
391
+ if (node.isEndpoint) {
392
+ const path3 = segments.length === 0 ? "/" : "/" + segments.join("/");
393
+ for (const [method, handler] of Object.entries(node.handlers)) {
394
+ if (method !== "") result.push({ method, path: path3, handler });
395
+ }
396
+ }
397
+ for (const [segment, child] of node.staticChildren) {
398
+ this.collectRoutes(child, [...segments, segment], result);
399
+ }
400
+ if (node.paramChild) {
401
+ const { name, isOptional, node: child } = node.paramChild;
402
+ this.collectRoutes(child, [...segments, isOptional ? `:${name}?` : `:${name}`], result);
403
+ }
404
+ if (node.wildcardHandler) {
405
+ const path3 = "/" + [...segments, "*"].join("/");
406
+ for (const [method, handler] of Object.entries(node.wildcardHandler)) {
407
+ if (method !== "") result.push({ method, path: path3, handler });
408
+ }
409
+ }
410
+ }
411
+ getStats() {
412
+ return {
413
+ routeCount: this.routeCount,
414
+ lastLoaded: this.lastLoaded,
415
+ uptime: Date.now() - this.lastLoaded,
416
+ cacheSize: this.cachedRoutes.size
417
+ };
418
+ }
419
+ setVerbose(verbose) {
420
+ this.verbose = verbose;
421
+ return this;
422
+ }
423
+ reset() {
424
+ this.rootNode = this.createNode();
425
+ this.cachedRoutes = /* @__PURE__ */ new Map();
426
+ this.routeCount = 0;
427
+ }
428
+ };
429
+ var router = new Router();
430
+ function isRouteError(obj) {
431
+ return obj && typeof obj === "object" && "error" in obj;
432
+ }
433
+ function registerRoutes(routes) {
434
+ for (const { path: path3, handlers } of routes) {
435
+ const normalizedPath = path3.replace(
436
+ /:(\w+\??)/g,
437
+ (_, name) => name.endsWith("?") ? `[${name.slice(0, -1)}?]` : `[${name}]`
438
+ );
439
+ for (const [method, handler] of Object.entries(handlers)) {
440
+ if (typeof handler === "function") router.addRoute(method, normalizedPath, handler);
441
+ }
442
+ }
443
+ }
444
+ async function loadRoutes(routesDir) {
445
+ return router.loadRoutes(routesDir);
446
+ }
447
+ function findRoute(url, method = "GET") {
448
+ const result = router.findRoute(method, url);
449
+ if (!result.handler) {
450
+ if (result.allowedMethods?.length) {
451
+ return { error: "Method Not Allowed", status: 405, allowedMethods: result.allowedMethods };
452
+ }
453
+ return null;
454
+ }
455
+ return { handler: result.handler, params: result.params };
456
+ }
457
+ function getRoutesDir(customDir) {
458
+ return path2.resolve(process.cwd(), customDir || process.env.ROUTES_DIR || "routes");
459
+ }
460
+ function getRouterStats() {
461
+ return router.getStats();
462
+ }
463
+ function setVerboseLogging(verbose) {
464
+ router.setVerbose(verbose);
465
+ }
466
+ function resetRouter() {
467
+ router.reset();
468
+ }
469
+ function createSSEClient(urlOrReq, options = {}, handlers = {}, req) {
470
+ let isConnected = false;
471
+ let retryCount = 0;
472
+ const eventHandlers = {};
473
+ const messageHandlers = [];
474
+ const closeHandlers = [];
475
+ const errorHandlers = [];
476
+ let request = null;
477
+ if (handlers.onMessage) messageHandlers.push(handlers.onMessage);
478
+ if (handlers.onClose) closeHandlers.push(handlers.onClose);
479
+ if (handlers.onError) errorHandlers.push(handlers.onError);
480
+ if (handlers.onEvent) {
481
+ Object.entries(handlers.onEvent).forEach(([event, handler]) => {
482
+ if (!eventHandlers[event]) eventHandlers[event] = [];
483
+ eventHandlers[event].push(handler);
484
+ });
485
+ }
486
+ const reconnectInterval = options.reconnectInterval || 3e3;
487
+ const maxRetries = options.maxRetries || 3;
488
+ const disableReconnect = options.disableReconnect || false;
489
+ const requestBody = options.body;
490
+ const contentType = options.contentType || (requestBody ? "application/json" : void 0);
491
+ const method = options.method || (requestBody ? "POST" : "GET");
492
+ if (typeof urlOrReq !== "string") {
493
+ const existingReq = urlOrReq;
494
+ isConnected = true;
495
+ existingReq.on("data", (chunk) => {
496
+ handleSSEData(chunk.toString());
497
+ });
498
+ existingReq.on("end", () => {
499
+ isConnected = false;
500
+ });
501
+ existingReq.on("error", (error) => {
502
+ isConnected = false;
503
+ console.error("SSE Error:", error);
504
+ });
505
+ return createClientInterface();
506
+ }
507
+ if (typeof urlOrReq === "string") {
508
+ return new Promise((resolve, reject) => {
509
+ startListening().then(() => resolve(createClientInterface())).catch(reject);
510
+ });
511
+ }
512
+ function handleSSEData(chunk) {
513
+ let buffer = chunk;
514
+ const messages = buffer.split("\n\n");
515
+ buffer = messages.pop() || "";
516
+ for (const message of messages) {
517
+ const lines = message.split("\n");
518
+ let data = "";
519
+ let currentEvent = "";
520
+ for (const line of lines) {
521
+ if (line.startsWith("event:")) {
522
+ currentEvent = line.slice(6).trim();
523
+ } else if (line.startsWith("data:")) {
524
+ data = line.slice(5).trim();
525
+ }
526
+ }
527
+ if (data) {
528
+ try {
529
+ const parsedData = JSON.parse(data);
530
+ if (currentEvent && eventHandlers[currentEvent]) {
531
+ eventHandlers[currentEvent].forEach(
532
+ (handler) => handler(parsedData)
533
+ );
534
+ } else {
535
+ messageHandlers.forEach((handler) => handler(parsedData));
536
+ }
537
+ } catch {
538
+ if (currentEvent && eventHandlers[currentEvent]) {
539
+ eventHandlers[currentEvent].forEach((handler) => handler(data));
540
+ } else {
541
+ messageHandlers.forEach((handler) => handler(data));
542
+ }
543
+ }
544
+ }
545
+ }
546
+ }
547
+ function createClientInterface() {
548
+ return {
549
+ // Register handler for all messages
550
+ onMessage(callback) {
551
+ messageHandlers.push(callback);
552
+ return this;
553
+ },
554
+ // Register handler for specific named events
555
+ onEvent(eventName, callback) {
556
+ if (!eventHandlers[eventName]) {
557
+ eventHandlers[eventName] = [];
558
+ }
559
+ eventHandlers[eventName].push(callback);
560
+ return this;
561
+ },
562
+ // Register handler for connection close
563
+ onClose(callback) {
564
+ closeHandlers.push(callback);
565
+ return this;
566
+ },
567
+ // Manually close the connection
568
+ close() {
569
+ if (request) {
570
+ request.destroy();
571
+ request = null;
572
+ }
573
+ isConnected = false;
574
+ closeHandlers.forEach((handler) => handler());
575
+ }
576
+ };
577
+ }
578
+ async function startListening() {
579
+ if (isConnected) return;
580
+ isConnected = true;
581
+ try {
582
+ await connect();
583
+ } catch (error) {
584
+ if (!disableReconnect && retryCount < maxRetries) {
585
+ retryCount++;
586
+ setTimeout(() => {
587
+ startListening();
588
+ }, reconnectInterval);
589
+ } else {
590
+ errorHandlers.forEach((handler) => handler(error));
591
+ }
592
+ }
593
+ }
594
+ async function connect() {
595
+ return new Promise((resolve, reject) => {
596
+ const parsedUrl = new URL(urlOrReq);
597
+ if (options.params) {
598
+ if (typeof options.params === "object") {
599
+ Object.entries(options.params).forEach(([key, value]) => {
600
+ parsedUrl.searchParams.append(key, String(value));
601
+ });
602
+ } else if (typeof options.params === "string") {
603
+ const searchParams = new URLSearchParams(options.params);
604
+ searchParams.forEach((value, key) => {
605
+ parsedUrl.searchParams.append(key, value);
606
+ });
607
+ }
608
+ }
609
+ const requestOptions = {
610
+ hostname: parsedUrl.hostname,
611
+ port: parsedUrl.port || (parsedUrl.protocol === "https:" ? 443 : 80),
612
+ path: parsedUrl.pathname + parsedUrl.search,
613
+ method,
614
+ // Utiliser la méthode définie plus haut
615
+ headers: {
616
+ Accept: "text/event-stream",
617
+ ...req?.headers || {},
618
+ "Cache-Control": "no-cache",
619
+ Connection: "keep-alive",
620
+ ...contentType && { "Content-Type": contentType },
621
+ ...requestBody && typeof requestBody === "string" && { "Content-Length": Buffer.byteLength(requestBody).toString() },
622
+ ...requestBody && typeof requestBody !== "string" && { "Content-Length": Buffer.byteLength(JSON.stringify(requestBody)).toString() }
623
+ }
624
+ };
625
+ const client = parsedUrl.protocol === "https:" ? https : http2;
626
+ request = client.request(requestOptions, (response) => {
627
+ if (response.statusCode !== 200) {
628
+ isConnected = false;
629
+ reject(
630
+ new Error(`Server responded with status: ${response.statusCode}`)
631
+ );
632
+ return;
633
+ }
634
+ response.setEncoding("utf8");
635
+ response.on("data", handleSSEData);
636
+ response.on("end", () => {
637
+ isConnected = false;
638
+ request?.destroy();
639
+ request = null;
640
+ closeHandlers.forEach((handler) => handler());
641
+ resolve(null);
642
+ });
643
+ response.on("close", () => {
644
+ isConnected = false;
645
+ request?.destroy();
646
+ request = null;
647
+ closeHandlers.forEach((handler) => handler());
648
+ resolve(null);
649
+ });
650
+ response.on("error", (error) => {
651
+ isConnected = false;
652
+ request?.destroy();
653
+ request = null;
654
+ closeHandlers.forEach((handler) => handler());
655
+ reject(error);
656
+ });
657
+ });
658
+ request.on("error", (error) => {
659
+ isConnected = false;
660
+ request?.destroy();
661
+ request = null;
662
+ reject(error);
663
+ });
664
+ if (requestBody) {
665
+ if (typeof requestBody === "string") {
666
+ request.write(requestBody);
667
+ } else {
668
+ request.write(JSON.stringify(requestBody));
669
+ }
670
+ }
671
+ request.end();
672
+ });
673
+ }
674
+ return createClientInterface();
675
+ }
676
+
677
+ // src/sse/server.ts
678
+ function initSSE(res, options) {
679
+ const cacheControl = options?.headers?.["Cache-Control"] || "no-cache";
680
+ const connection = options?.headers?.["Connection"] || "keep-alive";
681
+ const timeout = options?.timeout || 3e5;
682
+ res.writeHead(200, {
683
+ "Content-Type": "text/event-stream",
684
+ "Cache-Control": cacheControl,
685
+ Connection: connection,
686
+ ...options?.headers || {}
687
+ });
688
+ const timeoutId = setTimeout(() => {
689
+ res.end();
690
+ }, timeout);
691
+ res.on("close", () => {
692
+ clearTimeout(timeoutId);
693
+ });
694
+ return timeoutId;
695
+ }
696
+ function send(res, data) {
697
+ if (res.writableEnded) return false;
698
+ return res.write(`data: ${data}
699
+
700
+ `);
701
+ }
702
+ function sendJson(res, data) {
703
+ if (res.writableEnded) return false;
704
+ return res.write(`data: ${JSON.stringify(data)}
705
+
706
+ `);
707
+ }
708
+ function sendEvent(res, event, data) {
709
+ if (res.writableEnded) return false;
710
+ res.write(`event: ${event}
711
+ `);
712
+ return res.write(`data: ${JSON.stringify(data)}
713
+
714
+ `);
715
+ }
716
+ function sseComment(res, comment) {
717
+ if (res.writableEnded) return false;
718
+ return res.write(`: ${comment}
719
+
720
+ `);
721
+ }
722
+ function sseId(res, id) {
723
+ if (res.writableEnded) return false;
724
+ return res.write(`id: ${id}
725
+
726
+ `);
727
+ }
728
+ function sseRetry(res, ms) {
729
+ if (res.writableEnded) return false;
730
+ return res.write(`retry: ${ms}
731
+
732
+ `);
733
+ }
734
+ function sseClose(res, comment = "Connection closed") {
735
+ res.write(`: ${comment}
736
+
737
+ `);
738
+ res.end();
739
+ }
740
+ function sseEventError(res, event, error, code = 500, details) {
741
+ res.writeHead(code, { "Content-Type": "text/event-stream" });
742
+ res.write(`event: ${event}
743
+ `);
744
+ const errorData = {
745
+ message: error,
746
+ code,
747
+ details: details || null
748
+ };
749
+ res.write(`data: ${JSON.stringify(errorData)}
750
+
751
+ `);
752
+ res.end();
753
+ }
754
+
755
+ // src/utils/adapter-base.ts
756
+ var MAX_BODY_SIZE = 10485760;
757
+ var RequestCookiesImpl = class {
758
+ _parsed = null;
759
+ _raw;
760
+ constructor(raw) {
761
+ this._raw = raw;
762
+ }
763
+ _parse() {
764
+ if (this._parsed !== null) return this._parsed;
765
+ this._parsed = {};
766
+ if (!this._raw) return this._parsed;
767
+ for (const part of this._raw.split(";")) {
768
+ const idx = part.indexOf("=");
769
+ if (idx === -1) continue;
770
+ const k = part.slice(0, idx).trim();
771
+ const v = part.slice(idx + 1).trim();
772
+ if (k) {
773
+ const unquoted = v.startsWith('"') && v.endsWith('"') ? v.slice(1, -1) : v;
774
+ try {
775
+ this._parsed[k] = decodeURIComponent(unquoted);
776
+ } catch {
777
+ this._parsed[k] = unquoted;
778
+ }
779
+ }
780
+ }
781
+ return this._parsed;
782
+ }
783
+ get(name) {
784
+ return this._parse()[name];
785
+ }
786
+ all() {
787
+ return { ...this._parse() };
788
+ }
789
+ };
790
+ var ResponseCookiesImpl = class {
791
+ _pending = [];
792
+ set(name, value, options = {}) {
793
+ this._pending.push({ name, value, opts: options });
794
+ return this;
795
+ }
796
+ delete(name, options = {}) {
797
+ return this.set(name, "", { ...options, maxAge: 0, expires: /* @__PURE__ */ new Date(0) });
798
+ }
799
+ serialize() {
800
+ return this._pending.map(({ name, value, opts }) => {
801
+ let str = `${name}=${encodeURIComponent(value)}`;
802
+ const path3 = opts.path !== void 0 ? opts.path : "/";
803
+ if (path3) str += `; Path=${path3}`;
804
+ if (opts.domain) str += `; Domain=${opts.domain}`;
805
+ if (opts.maxAge != null) str += `; Max-Age=${opts.maxAge}`;
806
+ if (opts.expires) str += `; Expires=${opts.expires.toUTCString()}`;
807
+ if (opts.httpOnly) str += "; HttpOnly";
808
+ if (opts.secure) str += "; Secure";
809
+ if (opts.sameSite) str += `; SameSite=${opts.sameSite}`;
810
+ return str;
811
+ });
812
+ }
813
+ };
814
+ function parseCookieHeader(headers) {
815
+ if (typeof headers.get === "function") {
816
+ return headers.get("cookie") ?? void 0;
817
+ }
818
+ const raw = headers["cookie"];
819
+ if (Array.isArray(raw)) return raw.join("; ");
820
+ return raw;
821
+ }
822
+ function withRequestMethods(Base) {
823
+ return class extends Base {
824
+ _zreqCookies;
825
+ getHeader(name) {
826
+ const h = this.headers;
827
+ if (typeof h.get === "function") {
828
+ return h.get(name) ?? void 0;
829
+ }
830
+ const val = h[name.toLowerCase()];
831
+ return Array.isArray(val) ? val[0] : val;
832
+ }
833
+ get cookies() {
834
+ if (!this._zreqCookies)
835
+ this._zreqCookies = new RequestCookiesImpl(parseCookieHeader(this.headers));
836
+ return this._zreqCookies;
837
+ }
838
+ json() {
839
+ return this.body().then((b) => JSON.parse(b.toString()));
840
+ }
841
+ form() {
842
+ return new Promise((resolve, reject) => {
843
+ const headers = this.headers;
844
+ const contentType = typeof headers.get === "function" ? headers.get("content-type") ?? "" : headers["content-type"] ?? "";
845
+ if (!contentType.startsWith("multipart/form-data")) {
846
+ reject(new Error("Content-Type is not multipart/form-data"));
847
+ return;
848
+ }
849
+ const boundaryMatch = contentType.match(/boundary=(.+)$/);
850
+ if (!boundaryMatch) {
851
+ reject(new Error("Boundary not found"));
852
+ return;
853
+ }
854
+ const boundary = boundaryMatch[1].trim();
855
+ this.body().then((buffer) => {
856
+ const result = {};
857
+ const delimiter = Buffer.from(`--${boundary}`);
858
+ const parts = [];
859
+ let offset = 0, idx;
860
+ while ((idx = buffer.indexOf(delimiter, offset)) !== -1) {
861
+ parts.push(buffer.subarray(offset, idx));
862
+ offset = idx + delimiter.length;
863
+ }
864
+ parts.push(buffer.subarray(offset));
865
+ for (let i = 1; i < parts.length - 1; i++) {
866
+ const content = parts[i].subarray(2);
867
+ const sepIdx = content.indexOf("\r\n\r\n");
868
+ if (sepIdx === -1) continue;
869
+ const hdr = content.subarray(0, sepIdx).toString();
870
+ const body = content.subarray(sepIdx + 4, content.length - 2);
871
+ const d = hdr.match(
872
+ /Content-Disposition: form-data; name="([^"]+)"(?:; filename="([^"]+)")?/i
873
+ );
874
+ if (!d) continue;
875
+ if (d[2]) {
876
+ const m = hdr.match(/Content-Type:\s*([^\r\n]+)/i);
877
+ result[d[1]] = {
878
+ filename: d[2],
879
+ mimetype: m ? m[1].trim() : "application/octet-stream",
880
+ data: body,
881
+ size: body.length
882
+ };
883
+ } else {
884
+ result[d[1]] = body.toString("utf-8");
885
+ }
886
+ }
887
+ resolve(result);
888
+ }).catch(reject);
889
+ });
890
+ }
891
+ createSSEClient(options, handlers) {
892
+ return createSSEClient(this, options, handlers);
893
+ }
894
+ };
895
+ }
896
+ function nodeBody() {
897
+ const chunks = [];
898
+ let size = 0, settled = false;
899
+ return new Promise((resolve, reject) => {
900
+ this.on("data", (chunk) => {
901
+ size += chunk.length;
902
+ if (size > MAX_BODY_SIZE) {
903
+ settled = true;
904
+ this.destroy();
905
+ reject(Object.assign(new Error("Payload Too Large"), { code: 413 }));
906
+ return;
907
+ }
908
+ chunks.push(chunk);
909
+ }).on("end", () => {
910
+ if (!settled) {
911
+ settled = true;
912
+ resolve(Buffer.concat(chunks));
913
+ }
914
+ }).on("error", (err) => {
915
+ if (!settled) {
916
+ settled = true;
917
+ reject(err);
918
+ }
919
+ });
920
+ });
921
+ }
922
+ function flushCookies(jar, res) {
923
+ const serialized = jar.serialize();
924
+ if (serialized.length > 0 && !res.headersSent)
925
+ res.setHeader("Set-Cookie", serialized);
926
+ }
927
+ function withResponseMethods(Base) {
928
+ return class extends Base {
929
+ _zresCookies;
930
+ get cookies() {
931
+ if (!this._zresCookies) this._zresCookies = new ResponseCookiesImpl();
932
+ return this._zresCookies;
933
+ }
934
+ end(data) {
935
+ if (this._zresCookies) flushCookies(this._zresCookies, this);
936
+ return super.end(data);
937
+ }
938
+ status(code) {
939
+ this.statusCode = code;
940
+ return this;
941
+ }
942
+ send(data) {
943
+ if (typeof data === "string") {
944
+ this.setHeader("Content-Type", "text/plain");
945
+ this.end(data);
946
+ } else {
947
+ this.json(data);
948
+ }
949
+ return this;
950
+ }
951
+ json(data) {
952
+ this.setHeader("Content-Type", "application/json");
953
+ this.end(JSON.stringify(data));
954
+ return this;
955
+ }
956
+ initSSE(options) {
957
+ return initSSE(this, options);
958
+ }
959
+ sseSend(data) {
960
+ send(this, data);
961
+ }
962
+ sseJson(data) {
963
+ sendJson(this, data);
964
+ }
965
+ sseEvent(event, data) {
966
+ sendEvent(this, event, data);
967
+ }
968
+ sseComment(comment) {
969
+ sseComment(this, comment);
970
+ }
971
+ sseId(id) {
972
+ sseId(this, id);
973
+ }
974
+ sseRetry(ms) {
975
+ sseRetry(this, ms);
976
+ }
977
+ sseClose(comment) {
978
+ sseClose(this, comment);
979
+ }
980
+ sseError(event, error, code = 500, details) {
981
+ sseEventError(this, event, error, code, details);
982
+ }
983
+ };
984
+ }
985
+ var _ReqBase = class {
986
+ params = {};
987
+ headers = {};
988
+ body() {
989
+ return nodeBody.call(this);
990
+ }
991
+ };
992
+ var _reqProto = withRequestMethods(_ReqBase).prototype;
993
+ var _ResBase = class {
994
+ statusCode = 200;
995
+ headersSent = false;
996
+ setHeader(_n, _v) {
997
+ }
998
+ end(_d) {
999
+ }
1000
+ write(_c) {
1001
+ }
1002
+ };
1003
+ var _resProto = withResponseMethods(_ResBase).prototype;
1004
+ function applyRequestMethods(req) {
1005
+ req.body = nodeBody;
1006
+ req.json = _reqProto.json;
1007
+ req.form = _reqProto.form;
1008
+ req.getHeader = _reqProto.getHeader;
1009
+ req.createSSEClient = _reqProto.createSSEClient;
1010
+ req.cookies = new RequestCookiesImpl(parseCookieHeader(req.headers));
1011
+ }
1012
+ function applyResponseMethods(res) {
1013
+ const p = _resProto;
1014
+ res.status = p.status;
1015
+ res.send = p.send;
1016
+ res.json = p.json;
1017
+ res.initSSE = p.initSSE;
1018
+ res.sseSend = p.sseSend;
1019
+ res.sseJson = p.sseJson;
1020
+ res.sseEvent = p.sseEvent;
1021
+ res.sseComment = p.sseComment;
1022
+ res.sseId = p.sseId;
1023
+ res.sseRetry = p.sseRetry;
1024
+ res.sseClose = p.sseClose;
1025
+ res.sseError = p.sseError;
1026
+ const cookieJar = new ResponseCookiesImpl();
1027
+ res.cookies = cookieJar;
1028
+ const origEnd = res.end.bind(res);
1029
+ res.end = function(...args) {
1030
+ flushCookies(cookieJar, this);
1031
+ return origEnd(...args);
1032
+ };
1033
+ }
1034
+ var monitorInstance = null;
1035
+ function createMonitor(options = {}) {
1036
+ const emitter = new EventEmitter();
1037
+ const config = {
1038
+ sampleInterval: options.sampleInterval || 5e3,
1039
+ reportInterval: options.reportInterval || 6e4,
1040
+ resetInterval: options.resetInterval || 24 * 60 * 60 * 1e3,
1041
+ enableHistogram: options.enableHistogram !== void 0 ? options.enableHistogram : true,
1042
+ logToConsole: options.logToConsole !== void 0 ? options.logToConsole : true,
1043
+ thresholds: {
1044
+ cpu: options.thresholds?.cpu || 80,
1045
+ memory: options.thresholds?.memory || 80,
1046
+ responseTime: options.thresholds?.responseTime || 1e3,
1047
+ errorRate: options.thresholds?.errorRate || 5
1048
+ }
1049
+ };
1050
+ const now = Date.now();
1051
+ const data = {
1052
+ requestCount: 0,
1053
+ activeRequests: 0,
1054
+ errorCount: 0,
1055
+ responseTimes: [],
1056
+ responseTimesBucket: Array(100).fill(0),
1057
+ statusCodes: {},
1058
+ lastReport: now,
1059
+ lastReset: now
1060
+ };
1061
+ let metrics = getInitialMetrics();
1062
+ let metricsHistory = [];
1063
+ let lastCpuUsage = process.cpuUsage();
1064
+ const alarmStatus = {
1065
+ cpu: false,
1066
+ memory: false,
1067
+ responseTime: false,
1068
+ errorRate: false
1069
+ };
1070
+ function getInitialMetrics() {
1071
+ return {
1072
+ timestamp: Date.now(),
1073
+ uptime: process.uptime(),
1074
+ requestCount: 0,
1075
+ activeRequests: 0,
1076
+ errorCount: 0,
1077
+ responseTimes: {
1078
+ min: 0,
1079
+ max: 0,
1080
+ avg: 0,
1081
+ count: 0,
1082
+ sum: 0
1083
+ },
1084
+ statusCodes: {},
1085
+ memory: process.memoryUsage(),
1086
+ cpu: {
1087
+ usage: 0,
1088
+ system: 0,
1089
+ user: 0
1090
+ },
1091
+ systemLoad: os.loadavg(),
1092
+ systemMemory: {
1093
+ total: os.totalmem(),
1094
+ free: os.freemem(),
1095
+ used: os.totalmem() - os.freemem()
1096
+ }
1097
+ };
1098
+ }
1099
+ function calculateResponseTimeMetrics() {
1100
+ const responseTimes = data.responseTimes;
1101
+ if (responseTimes.length === 0) {
1102
+ return {
1103
+ min: 0,
1104
+ max: 0,
1105
+ avg: 0,
1106
+ count: 0,
1107
+ sum: 0
1108
+ };
1109
+ }
1110
+ let min = Infinity;
1111
+ let max = -Infinity;
1112
+ let sum = 0;
1113
+ for (const time of responseTimes) {
1114
+ min = Math.min(min, time);
1115
+ max = Math.max(max, time);
1116
+ sum += time;
1117
+ }
1118
+ const avg = sum / responseTimes.length;
1119
+ const result = {
1120
+ min,
1121
+ max,
1122
+ avg,
1123
+ count: responseTimes.length,
1124
+ sum
1125
+ };
1126
+ if (config.enableHistogram) {
1127
+ const sorted = [...responseTimes].sort((a, b) => a - b);
1128
+ result.p50 = sorted[Math.floor(sorted.length * 0.5)];
1129
+ result.p90 = sorted[Math.floor(sorted.length * 0.9)];
1130
+ result.p99 = sorted[Math.floor(sorted.length * 0.99)];
1131
+ }
1132
+ return result;
1133
+ }
1134
+ function sampleMetrics() {
1135
+ const now2 = Date.now();
1136
+ const uptime = process.uptime();
1137
+ const memoryUsage = process.memoryUsage();
1138
+ const cpuUsage = process.cpuUsage(lastCpuUsage);
1139
+ lastCpuUsage = process.cpuUsage();
1140
+ const cpuTotal = cpuUsage.user + cpuUsage.system;
1141
+ const cpuPercentage = cpuTotal / 1e3 / config.sampleInterval * 100;
1142
+ metrics = {
1143
+ timestamp: now2,
1144
+ uptime,
1145
+ requestCount: data.requestCount,
1146
+ activeRequests: data.activeRequests,
1147
+ errorCount: data.errorCount,
1148
+ responseTimes: calculateResponseTimeMetrics(),
1149
+ statusCodes: { ...data.statusCodes },
1150
+ memory: memoryUsage,
1151
+ cpu: {
1152
+ usage: cpuPercentage,
1153
+ system: cpuUsage.system,
1154
+ user: cpuUsage.user
1155
+ },
1156
+ systemLoad: os.loadavg(),
1157
+ systemMemory: {
1158
+ total: os.totalmem(),
1159
+ free: os.freemem(),
1160
+ used: os.totalmem() - os.freemem()
1161
+ }
1162
+ };
1163
+ metricsHistory.push({ ...metrics });
1164
+ if (metricsHistory.length > 100) {
1165
+ metricsHistory.shift();
1166
+ }
1167
+ checkThresholds();
1168
+ if (cluster3.isWorker && process.send) {
1169
+ process.send({ type: "metrics", metrics });
1170
+ }
1171
+ }
1172
+ function reportMetrics() {
1173
+ if (!config.logToConsole) return;
1174
+ if (cluster3.isPrimary === false) return;
1175
+ const memoryMB = {
1176
+ rss: (metrics.memory.rss / 1024 / 1024).toFixed(2),
1177
+ heapTotal: (metrics.memory.heapTotal / 1024 / 1024).toFixed(2),
1178
+ heapUsed: (metrics.memory.heapUsed / 1024 / 1024).toFixed(2)
1179
+ };
1180
+ const elapsedMs = Date.now() - data.lastReport;
1181
+ const requestsPerSecond = (metrics.requestCount / (elapsedMs / 1e3)).toFixed(2);
1182
+ const errorRate = metrics.requestCount ? (metrics.errorCount / metrics.requestCount * 100).toFixed(2) : "0.00";
1183
+ primaryLog("\n\u{1F4CA} Server Performance Metrics \u{1F4CA}");
1184
+ primaryLog(`Uptime: ${formatUptime(metrics.uptime)}`);
1185
+ primaryLog(`Load: ${metrics.systemLoad[0].toFixed(2)}, ${metrics.systemLoad[1].toFixed(2)}, ${metrics.systemLoad[2].toFixed(2)}`);
1186
+ primaryLog(`Requests: ${metrics.requestCount} total, ${requestsPerSecond} req/sec`);
1187
+ primaryLog(`Active Requests: ${metrics.activeRequests}`);
1188
+ primaryLog(`Errors: ${metrics.errorCount} (${errorRate}%)`);
1189
+ if (metrics.responseTimes.count > 0) {
1190
+ primaryLog(`Response Times: avg ${metrics.responseTimes.avg.toFixed(2)}ms, min ${metrics.responseTimes.min}ms, max ${metrics.responseTimes.max}ms`);
1191
+ if (metrics.responseTimes.p50) {
1192
+ primaryLog(`Response Time Percentiles: p50 ${metrics.responseTimes.p50}ms, p90 ${metrics.responseTimes.p90}ms, p99 ${metrics.responseTimes.p99}ms`);
1193
+ }
1194
+ }
1195
+ primaryLog(`Memory: ${memoryMB.rss}MB (RSS), ${memoryMB.heapUsed}MB / ${memoryMB.heapTotal}MB (Heap)`);
1196
+ primaryLog(`CPU Usage: ${metrics.cpu.usage.toFixed(2)}%`);
1197
+ if (Object.keys(metrics.statusCodes).length > 0) {
1198
+ primaryLog("Status Codes:");
1199
+ Object.entries(metrics.statusCodes).sort(([a], [b]) => parseInt(a) - parseInt(b)).forEach(([code, count]) => {
1200
+ primaryLog(` ${code}: ${count}`);
1201
+ });
1202
+ }
1203
+ primaryLog("");
1204
+ data.lastReport = Date.now();
1205
+ }
1206
+ function resetMetrics() {
1207
+ const now2 = Date.now();
1208
+ data.requestCount = 0;
1209
+ data.activeRequests = 0;
1210
+ data.errorCount = 0;
1211
+ data.responseTimes = [];
1212
+ data.responseTimesBucket = Array(100).fill(0);
1213
+ data.statusCodes = {};
1214
+ data.lastReport = now2;
1215
+ data.lastReset = now2;
1216
+ primaryLog("\u{1F504} Performance metrics have been reset");
1217
+ }
1218
+ function checkThresholds() {
1219
+ const memUsedPercent = metrics.memory.heapUsed / metrics.memory.heapTotal * 100;
1220
+ const errorRate = metrics.requestCount ? metrics.errorCount / metrics.requestCount * 100 : 0;
1221
+ const avgResponseTime = metrics.responseTimes.avg;
1222
+ if (metrics.cpu.usage > config.thresholds.cpu && !alarmStatus.cpu) {
1223
+ alarmStatus.cpu = true;
1224
+ emitter.emit("alarm", "cpu", `High CPU usage: ${metrics.cpu.usage.toFixed(2)}%`);
1225
+ } else if (metrics.cpu.usage <= config.thresholds.cpu && alarmStatus.cpu) {
1226
+ alarmStatus.cpu = false;
1227
+ emitter.emit("alarm-clear", "cpu", `CPU usage returned to normal: ${metrics.cpu.usage.toFixed(2)}%`);
1228
+ }
1229
+ if (memUsedPercent > config.thresholds.memory && !alarmStatus.memory) {
1230
+ alarmStatus.memory = true;
1231
+ emitter.emit("alarm", "memory", `High memory usage: ${memUsedPercent.toFixed(2)}%`);
1232
+ } else if (memUsedPercent <= config.thresholds.memory && alarmStatus.memory) {
1233
+ alarmStatus.memory = false;
1234
+ emitter.emit("alarm-clear", "memory", `Memory usage returned to normal: ${memUsedPercent.toFixed(2)}%`);
1235
+ }
1236
+ if (avgResponseTime > config.thresholds.responseTime && !alarmStatus.responseTime) {
1237
+ alarmStatus.responseTime = true;
1238
+ emitter.emit("alarm", "responseTime", `High response time: ${avgResponseTime.toFixed(2)}ms`);
1239
+ } else if (avgResponseTime <= config.thresholds.responseTime && alarmStatus.responseTime) {
1240
+ alarmStatus.responseTime = false;
1241
+ emitter.emit("alarm-clear", "responseTime", `Response time returned to normal: ${avgResponseTime.toFixed(2)}ms`);
1242
+ }
1243
+ if (errorRate > config.thresholds.errorRate && !alarmStatus.errorRate) {
1244
+ alarmStatus.errorRate = true;
1245
+ emitter.emit("alarm", "errorRate", `High error rate: ${errorRate.toFixed(2)}%`);
1246
+ } else if (errorRate <= config.thresholds.errorRate && alarmStatus.errorRate) {
1247
+ alarmStatus.errorRate = false;
1248
+ emitter.emit("alarm-clear", "errorRate", `Error rate returned to normal: ${errorRate.toFixed(2)}%`);
1249
+ }
1250
+ }
1251
+ function formatUptime(uptime) {
1252
+ const days = Math.floor(uptime / 86400);
1253
+ const hours = Math.floor(uptime % 86400 / 3600);
1254
+ const minutes = Math.floor(uptime % 3600 / 60);
1255
+ const seconds = Math.floor(uptime % 60);
1256
+ const parts = [];
1257
+ if (days > 0) parts.push(`${days}d`);
1258
+ if (hours > 0) parts.push(`${hours}h`);
1259
+ if (minutes > 0) parts.push(`${minutes}m`);
1260
+ if (seconds > 0 || parts.length === 0) parts.push(`${seconds}s`);
1261
+ return parts.join(" ");
1262
+ }
1263
+ function start() {
1264
+ const sampleInterval = setInterval(sampleMetrics, config.sampleInterval);
1265
+ const reportInterval = setInterval(reportMetrics, config.reportInterval);
1266
+ const resetInterval = setInterval(resetMetrics, config.resetInterval);
1267
+ return {
1268
+ stop: () => {
1269
+ clearInterval(sampleInterval);
1270
+ clearInterval(reportInterval);
1271
+ clearInterval(resetInterval);
1272
+ }
1273
+ };
1274
+ }
1275
+ function trackRequest() {
1276
+ const startTime = Date.now();
1277
+ data.activeRequests++;
1278
+ return {
1279
+ end: (statusCode, error = false) => {
1280
+ const responseTime = Date.now() - startTime;
1281
+ data.activeRequests--;
1282
+ data.requestCount++;
1283
+ data.responseTimes.push(responseTime);
1284
+ if (data.responseTimes.length > 1e3) {
1285
+ data.responseTimes.shift();
1286
+ }
1287
+ data.statusCodes[statusCode] = (data.statusCodes[statusCode] || 0) + 1;
1288
+ if (error || statusCode >= 500) {
1289
+ data.errorCount++;
1290
+ }
1291
+ return responseTime;
1292
+ }
1293
+ };
1294
+ }
1295
+ function isHealthy() {
1296
+ return !Object.values(alarmStatus).some((status) => status);
1297
+ }
1298
+ function getHealthMetrics() {
1299
+ const memUsedPercent = metrics.memory.heapUsed / metrics.memory.heapTotal * 100;
1300
+ const errorRate = metrics.requestCount ? metrics.errorCount / metrics.requestCount * 100 : 0;
1301
+ return {
1302
+ status: isHealthy() ? "healthy" : "unhealthy",
1303
+ uptime: metrics.uptime,
1304
+ responseTimes: {
1305
+ avg: metrics.responseTimes.avg,
1306
+ p90: metrics.responseTimes.p90 || null,
1307
+ p99: metrics.responseTimes.p99 || null
1308
+ },
1309
+ memory: {
1310
+ usedMB: Math.round(metrics.memory.heapUsed / 1024 / 1024),
1311
+ totalMB: Math.round(metrics.memory.heapTotal / 1024 / 1024),
1312
+ percent: memUsedPercent.toFixed(2)
1313
+ },
1314
+ cpu: metrics.cpu.usage.toFixed(2),
1315
+ requests: {
1316
+ total: metrics.requestCount,
1317
+ active: metrics.activeRequests,
1318
+ errors: metrics.errorCount,
1319
+ errorRate: errorRate.toFixed(2)
1320
+ },
1321
+ alerts: Object.entries(alarmStatus).filter(([_, status]) => status).map(([type]) => type)
1322
+ };
1323
+ }
1324
+ const intervals = start();
1325
+ return {
1326
+ trackRequest,
1327
+ getMetrics: () => ({ ...metrics }),
1328
+ getMetricsHistory: () => [...metricsHistory],
1329
+ getHealthMetrics,
1330
+ isHealthy,
1331
+ stop: intervals.stop,
1332
+ on: emitter.on.bind(emitter),
1333
+ once: emitter.once.bind(emitter),
1334
+ off: emitter.off.bind(emitter)
1335
+ };
1336
+ }
1337
+ function getMonitor(options) {
1338
+ if (!monitorInstance) {
1339
+ const instance = createMonitor(options);
1340
+ const originalStop = instance.stop;
1341
+ instance.stop = () => {
1342
+ originalStop();
1343
+ monitorInstance = null;
1344
+ };
1345
+ monitorInstance = instance;
1346
+ }
1347
+ return monitorInstance;
1348
+ }
1349
+ function createLoadBalancer(options = {}) {
1350
+ const reportInterval = options.reportInterval ?? 5e3;
1351
+ const state = {
1352
+ workers: /* @__PURE__ */ new Map(),
1353
+ workerIds: []
1354
+ };
1355
+ function startWorker() {
1356
+ if (!cluster3.isWorker) return;
1357
+ let prevCpu = process.cpuUsage();
1358
+ const reportStats = () => {
1359
+ if (!process.send) return;
1360
+ const memoryUsage = process.memoryUsage();
1361
+ const cpuDelta = process.cpuUsage(prevCpu);
1362
+ prevCpu = process.cpuUsage();
1363
+ const load = (cpuDelta.user + cpuDelta.system) / (reportInterval * 1e3);
1364
+ const msg = {
1365
+ type: "stats",
1366
+ stats: { pid: process.pid, load, lastUsed: Date.now(), memoryUsage }
1367
+ };
1368
+ process.send(msg);
1369
+ };
1370
+ reportStats();
1371
+ setInterval(reportStats, reportInterval).unref();
1372
+ }
1373
+ function forkWorker() {
1374
+ const worker = cluster3.fork();
1375
+ state.workers.set(worker.id, {
1376
+ pid: worker.process.pid,
1377
+ load: 0,
1378
+ lastUsed: Date.now(),
1379
+ memoryUsage: { rss: 0, heapTotal: 0, heapUsed: 0, external: 0, arrayBuffers: 0 }
1380
+ });
1381
+ state.workerIds.push(worker.id);
1382
+ primaryLog(`Worker ${worker.process.pid} started`);
1383
+ }
1384
+ let shuttingDown = false;
1385
+ const exitCallbacks = [];
1386
+ function start(numWorkers = os.cpus().length) {
1387
+ if (!cluster3.isPrimary) {
1388
+ return startWorker();
1389
+ }
1390
+ primaryLog(`\u{1F9F5} Launching ${numWorkers} workers...`);
1391
+ for (let i = 0; i < numWorkers; i++) forkWorker();
1392
+ cluster3.on("message", (worker, message) => {
1393
+ if (message.type === "stats" && "stats" in message) {
1394
+ const s = state.workers.get(worker.id);
1395
+ if (s) Object.assign(s, message.stats);
1396
+ }
1397
+ });
1398
+ cluster3.on("exit", (worker, code, signal) => {
1399
+ state.workers.delete(worker.id);
1400
+ state.workerIds = state.workerIds.filter((id) => id !== worker.id);
1401
+ if (shuttingDown) {
1402
+ exitCallbacks.forEach((cb) => cb());
1403
+ return;
1404
+ }
1405
+ primaryLog(`Worker ${worker.process.pid} died (${signal || code}). Restarting...`);
1406
+ setTimeout(() => forkWorker(), 1e3);
1407
+ });
1408
+ }
1409
+ function shutdown(callback) {
1410
+ shuttingDown = true;
1411
+ const workerIds = Object.keys(cluster3.workers ?? {});
1412
+ if (workerIds.length === 0) {
1413
+ callback?.();
1414
+ return;
1415
+ }
1416
+ let remaining = workerIds.length;
1417
+ exitCallbacks.push(() => {
1418
+ remaining--;
1419
+ if (remaining === 0) callback?.();
1420
+ });
1421
+ for (const id of workerIds) cluster3.workers?.[id]?.kill();
1422
+ }
1423
+ return {
1424
+ start,
1425
+ shutdown,
1426
+ getWorkerStats: () => Array.from(state.workers.entries()),
1427
+ getActiveWorkerCount: () => state.workerIds.length
1428
+ };
1429
+ }
1430
+ var _ZenoRequestBase = class extends http2.IncomingMessage {
1431
+ params = {};
1432
+ body = nodeBody;
1433
+ };
1434
+ var ZenoRequest = class extends withRequestMethods(_ZenoRequestBase) {
1435
+ };
1436
+ var ZenoResponse = class extends withResponseMethods(
1437
+ http2.ServerResponse
1438
+ ) {
1439
+ };
1440
+ var nodeAdapter = {
1441
+ name: "node",
1442
+ createHandler: (config) => {
1443
+ if (typeof config !== "string") {
1444
+ throw new Error(
1445
+ "nodeAdapter requires a routesDir string, not a ServerlessConfig."
1446
+ );
1447
+ }
1448
+ const routesDir = config;
1449
+ return async (config2 = {}) => {
1450
+ const {
1451
+ port = 3e3,
1452
+ defaultHeaders,
1453
+ isDev,
1454
+ cluster: clusterConfig,
1455
+ monitoring = { enabled: false }
1456
+ } = config2;
1457
+ let performanceMonitor = null;
1458
+ if (isDev && monitoring.enabled) {
1459
+ performanceMonitor = getMonitor({
1460
+ sampleInterval: monitoring.sampleInterval || 5e3,
1461
+ reportInterval: monitoring.reportInterval || 6e4,
1462
+ thresholds: monitoring.thresholds,
1463
+ logToConsole: true
1464
+ });
1465
+ if (cluster3.isPrimary) {
1466
+ primaryLog("\u{1F4CA} Development performance monitoring enabled");
1467
+ performanceMonitor.on("alarm", (metricType, message) => {
1468
+ primaryLog(`\u26A0\uFE0F ALERT: ${message}`);
1469
+ });
1470
+ performanceMonitor.on("alarm-clear", (metricType, message) => {
1471
+ primaryLog(`\u2705 RESOLVED: ${message}`);
1472
+ });
1473
+ }
1474
+ }
1475
+ const defaultHeadersArr = defaultHeaders ? Object.entries(defaultHeaders) : [];
1476
+ if (clusterConfig?.enabled && cluster3.isPrimary) {
1477
+ const numWorkers = clusterConfig.workers ?? os__default.cpus().length;
1478
+ const protocol = config2.httpsOptions ? "https" : "http";
1479
+ primaryLog(`\u{1F9F5} Starting server with ${numWorkers} workers`);
1480
+ if (cluster3.schedulingPolicy !== void 0) {
1481
+ try {
1482
+ cluster3.schedulingPolicy = cluster3.SCHED_RR;
1483
+ } catch {
1484
+ }
1485
+ }
1486
+ const lb = createLoadBalancer();
1487
+ lb.start(numWorkers);
1488
+ primaryLog(`\u{1F680} Server running at ${protocol}://localhost:${port}/` + (isDev ? " (dev)" : ""));
1489
+ if (isDev && performanceMonitor) {
1490
+ primaryLog(`\u{1F4CA} Performance monitoring available at http://localhost:${port}/health`);
1491
+ }
1492
+ return {
1493
+ close: (callback) => {
1494
+ if (performanceMonitor) performanceMonitor.stop();
1495
+ lb.shutdown(callback);
1496
+ }
1497
+ };
1498
+ }
1499
+ if (cluster3.isWorker || !clusterConfig?.enabled) {
1500
+ await loadRoutes(routesDir);
1501
+ registerCorsConfig(config2.cors);
1502
+ registerMiddlewareConfig(config2.middleware);
1503
+ const handleRequest = async (req, res, requestTracker) => {
1504
+ try {
1505
+ if (isDev && performanceMonitor && req.url === "/health") {
1506
+ res.setHeader("Content-Type", "application/json");
1507
+ res.end(JSON.stringify(performanceMonitor.getHealthMetrics()));
1508
+ requestTracker?.end(200);
1509
+ return;
1510
+ }
1511
+ const rawUrl = req.url || "/";
1512
+ const qIdx = rawUrl.indexOf("?");
1513
+ const pathname = qIdx === -1 ? rawUrl : rawUrl.slice(0, qIdx);
1514
+ if (hasMiddlewares()) {
1515
+ const ok = await runMiddlewares(
1516
+ "beforeRequest",
1517
+ req,
1518
+ res
1519
+ );
1520
+ if (ok === false || res.headersSent) {
1521
+ requestTracker?.end(res.statusCode || 204);
1522
+ return;
1523
+ }
1524
+ }
1525
+ const route = findRoute(pathname, req.method || "GET");
1526
+ if (!route) {
1527
+ if (hasMiddlewares())
1528
+ await runMiddlewares("onError", req, res);
1529
+ res.status(404).json({ error: "Route not found" });
1530
+ requestTracker?.end(404);
1531
+ return;
1532
+ }
1533
+ if ("error" in route) {
1534
+ const status = route.status || 500;
1535
+ res.status(status).json({ error: route.error });
1536
+ requestTracker?.end(status, true);
1537
+ return;
1538
+ }
1539
+ req.params = route.params;
1540
+ await route.handler(req, res);
1541
+ if (hasMiddlewares()) {
1542
+ await runMiddlewares("afterRequest", req, res);
1543
+ }
1544
+ if (!res.headersSent) res.end();
1545
+ requestTracker?.end(res.statusCode || 200);
1546
+ } catch (error) {
1547
+ if (isDev) console.error("Error:", error);
1548
+ if (!res.headersSent)
1549
+ res.status(500).json({ error: "Internal Server Error" });
1550
+ if (hasMiddlewares())
1551
+ await runMiddlewares("onError", req, res);
1552
+ requestTracker?.end(res.statusCode || 500, true);
1553
+ }
1554
+ };
1555
+ const requestListener = (req, res) => {
1556
+ const requestTracker = isDev && performanceMonitor ? performanceMonitor.trackRequest() : null;
1557
+ if (defaultHeadersArr.length > 0) {
1558
+ for (let i = 0; i < defaultHeadersArr.length; i++) {
1559
+ res.setHeader(defaultHeadersArr[i][0], defaultHeadersArr[i][1]);
1560
+ }
1561
+ }
1562
+ handleRequest(
1563
+ req,
1564
+ res,
1565
+ requestTracker
1566
+ ).catch((err) => {
1567
+ if (isDev) console.error("Fatal error:", err);
1568
+ if (!res.headersSent) {
1569
+ res.statusCode = 500;
1570
+ res.end("Server Error");
1571
+ }
1572
+ requestTracker?.end(500, true);
1573
+ });
1574
+ };
1575
+ const serverOptions = {
1576
+ IncomingMessage: ZenoRequest,
1577
+ ServerResponse: ZenoResponse
1578
+ };
1579
+ const server = config2.httpsOptions ? https.createServer(
1580
+ { ...serverOptions, ...config2.httpsOptions },
1581
+ requestListener
1582
+ ) : http2.createServer(serverOptions, requestListener);
1583
+ server.on("clientError", (_err, socket) => {
1584
+ if (!socket.destroyed) socket.destroy();
1585
+ });
1586
+ const protocol = config2.httpsOptions ? "https" : "http";
1587
+ if (cluster3.isWorker) {
1588
+ createLoadBalancer().start();
1589
+ }
1590
+ server.listen(port, () => {
1591
+ if (!clusterConfig?.enabled) {
1592
+ primaryLog(
1593
+ `\u{1F680} Server running at ${protocol}://localhost:${port}/` + (isDev ? " (dev)" : "")
1594
+ );
1595
+ if (isDev && performanceMonitor) {
1596
+ primaryLog(
1597
+ `\u{1F4CA} Performance monitoring available at http://localhost:${port}/health`
1598
+ );
1599
+ }
1600
+ } else if (isDev) {
1601
+ primaryLog(`Worker ${process.pid} is listening on port ${port}`);
1602
+ }
1603
+ });
1604
+ return server;
1605
+ }
1606
+ return null;
1607
+ };
1608
+ }
1609
+ };
1610
+
1611
+ // src/adapters/vercel.ts
1612
+ var vercelAdapter = {
1613
+ name: "vercel",
1614
+ createHandler: (config) => {
1615
+ if (typeof config === "string") {
1616
+ throw new Error(
1617
+ "vercelAdapter.createHandler() requires a ServerlessConfig object, not a routesDir string. Import your routes manifest and pass { routes } instead."
1618
+ );
1619
+ }
1620
+ let initPromise = null;
1621
+ const init = () => {
1622
+ if (initPromise) return initPromise;
1623
+ initPromise = (async () => {
1624
+ registerRoutes(config.routes);
1625
+ registerCorsConfig(config.cors);
1626
+ registerMiddlewareConfig(config.middleware);
1627
+ })();
1628
+ return initPromise;
1629
+ };
1630
+ return async (vercelReq, vercelRes) => {
1631
+ await init();
1632
+ applyRequestMethods(vercelReq);
1633
+ applyResponseMethods(vercelRes);
1634
+ const req = vercelReq;
1635
+ const res = vercelRes;
1636
+ try {
1637
+ if (hasMiddlewares()) {
1638
+ const shouldContinue = await runMiddlewares("beforeRequest", req, res);
1639
+ if (shouldContinue === false || res.headersSent) return;
1640
+ }
1641
+ const route = findRoute(req.url ?? "/", req.method ?? "GET");
1642
+ if (!route) {
1643
+ res.status(404).json({ error: "Route not found" });
1644
+ return;
1645
+ }
1646
+ if (isRouteError(route)) {
1647
+ res.status(route.status ?? 500).json({ error: route.error });
1648
+ return;
1649
+ }
1650
+ req.params = route.params;
1651
+ await route.handler(req, res);
1652
+ if (hasMiddlewares()) await runMiddlewares("afterRequest", req, res);
1653
+ } catch (error) {
1654
+ console.error("[zeno/vercel] Handler error:", error);
1655
+ if (hasMiddlewares()) await runMiddlewares("onError", req, res, { error });
1656
+ if (!res.headersSent) {
1657
+ res.status(500).json({ error: "Internal server error" });
1658
+ }
1659
+ }
1660
+ };
1661
+ }
1662
+ };
1663
+ function netlifyResponse(statusCode, headers, multiValueHeaders, body) {
1664
+ const hasMulti = Object.keys(multiValueHeaders).length > 0;
1665
+ return { statusCode, headers, ...hasMulti ? { multiValueHeaders } : {}, body };
1666
+ }
1667
+ var netlifyAdapter = {
1668
+ name: "netlify",
1669
+ createHandler: (config) => {
1670
+ if (typeof config === "string") {
1671
+ throw new Error(
1672
+ "netlifyAdapter.createHandler() requires a ServerlessConfig object. Run `zeno build` to generate routes/_manifest.ts and pass { routes } instead."
1673
+ );
1674
+ }
1675
+ let initPromise = null;
1676
+ const init = () => {
1677
+ if (initPromise) return initPromise;
1678
+ initPromise = (async () => {
1679
+ registerRoutes(config.routes);
1680
+ registerCorsConfig(config.cors);
1681
+ registerMiddlewareConfig(config.middleware);
1682
+ })();
1683
+ return initPromise;
1684
+ };
1685
+ return async (event, _context) => {
1686
+ await init();
1687
+ const qs = event.queryStringParameters ? "?" + new URLSearchParams(event.queryStringParameters).toString() : "";
1688
+ const url = event.path + qs;
1689
+ const rawReq = new IncomingMessage(new Socket());
1690
+ rawReq.url = url;
1691
+ rawReq.method = event.httpMethod;
1692
+ rawReq.headers = event.headers;
1693
+ if (event.body) {
1694
+ rawReq.push(Buffer.from(event.body, "utf-8"));
1695
+ }
1696
+ rawReq.push(null);
1697
+ let responseBody = "";
1698
+ let responseHeaders = {};
1699
+ let multiValueResponseHeaders = {};
1700
+ let headersSent = false;
1701
+ const rawRes = new ServerResponse(rawReq);
1702
+ rawRes.writeHead = function(status, headers) {
1703
+ rawRes.statusCode = status;
1704
+ if (headers) responseHeaders = { ...responseHeaders, ...headers };
1705
+ return this;
1706
+ };
1707
+ rawRes.setHeader = function(name, value) {
1708
+ const key = name.toLowerCase();
1709
+ if (Array.isArray(value)) {
1710
+ multiValueResponseHeaders[key] = value.map(String);
1711
+ responseHeaders[key] = String(value[0]);
1712
+ } else {
1713
+ responseHeaders[key] = String(value);
1714
+ }
1715
+ return this;
1716
+ };
1717
+ rawRes.getHeader = function(name) {
1718
+ return responseHeaders[name.toLowerCase()];
1719
+ };
1720
+ rawRes.end = function(body) {
1721
+ headersSent = true;
1722
+ if (body !== void 0) responseBody = typeof body === "string" ? body : body.toString();
1723
+ return this;
1724
+ };
1725
+ Object.defineProperty(rawRes, "headersSent", { get: () => headersSent });
1726
+ applyRequestMethods(rawReq);
1727
+ applyResponseMethods(rawRes);
1728
+ const req = rawReq;
1729
+ const res = rawRes;
1730
+ const route = findRoute(event.path, event.httpMethod);
1731
+ if (!route) {
1732
+ return { statusCode: 404, body: JSON.stringify({ error: "Route not found" }) };
1733
+ }
1734
+ if (isRouteError(route)) {
1735
+ return {
1736
+ statusCode: route.status ?? 500,
1737
+ body: JSON.stringify({ error: route.error })
1738
+ };
1739
+ }
1740
+ try {
1741
+ if (hasMiddlewares()) {
1742
+ const shouldContinue = await runMiddlewares("beforeRequest", req, res);
1743
+ if (shouldContinue === false || headersSent) {
1744
+ return netlifyResponse(res.statusCode, responseHeaders, multiValueResponseHeaders, responseBody);
1745
+ }
1746
+ }
1747
+ req.params = route.params;
1748
+ await route.handler(req, res);
1749
+ if (hasMiddlewares()) await runMiddlewares("afterRequest", req, res);
1750
+ return netlifyResponse(res.statusCode, responseHeaders, multiValueResponseHeaders, responseBody);
1751
+ } catch (error) {
1752
+ console.error("[zeno/netlify] Handler error:", error);
1753
+ if (hasMiddlewares()) await runMiddlewares("onError", req, res, { error });
1754
+ if (!headersSent) {
1755
+ return { statusCode: 500, body: JSON.stringify({ error: "Internal server error" }) };
1756
+ }
1757
+ return netlifyResponse(res.statusCode, responseHeaders, multiValueResponseHeaders, responseBody);
1758
+ }
1759
+ };
1760
+ }
1761
+ };
1762
+ var MAX_BODY_SIZE2 = 10485760;
1763
+ var _encoder = new TextEncoder();
1764
+ var _BunRequestBase = class {
1765
+ params = {};
1766
+ url;
1767
+ method;
1768
+ headers;
1769
+ socket = { setTimeout: (_) => {
1770
+ } };
1771
+ connection;
1772
+ _req;
1773
+ constructor(req, pathname, search) {
1774
+ this._req = req;
1775
+ this.url = pathname + search;
1776
+ this.method = req.method;
1777
+ this.headers = req.headers;
1778
+ this.connection = {
1779
+ remoteAddress: req.headers.get("x-forwarded-for") ?? "127.0.0.1"
1780
+ };
1781
+ }
1782
+ setTimeout(_) {
1783
+ }
1784
+ text() {
1785
+ return this._req.text();
1786
+ }
1787
+ body() {
1788
+ return this._req.arrayBuffer().then((b) => {
1789
+ if (b.byteLength > MAX_BODY_SIZE2)
1790
+ throw Object.assign(new Error("Payload Too Large"), { code: 413 });
1791
+ return Buffer.from(b);
1792
+ });
1793
+ }
1794
+ };
1795
+ var BunRequest = class extends withRequestMethods(_BunRequestBase) {
1796
+ // Uses Bun native JSON parser directly, skipping the body() to Buffer conversion
1797
+ json() {
1798
+ return this._req.json();
1799
+ }
1800
+ };
1801
+ var _BunResponseBase = class {
1802
+ statusCode = 200;
1803
+ headersSent = false;
1804
+ get finished() {
1805
+ return this.headersSent;
1806
+ }
1807
+ get writableEnded() {
1808
+ return this.headersSent;
1809
+ }
1810
+ _body = null;
1811
+ _headers = null;
1812
+ _sseReadable = null;
1813
+ _sseWriter = null;
1814
+ _sseWindowClosed = false;
1815
+ _listeners = null;
1816
+ on(event, listener) {
1817
+ if (event === "finish" || event === "close") {
1818
+ if (!this._listeners) this._listeners = [];
1819
+ this._listeners.push(listener);
1820
+ }
1821
+ return this;
1822
+ }
1823
+ once(event, listener) {
1824
+ return this.on(event, listener);
1825
+ }
1826
+ emit(event) {
1827
+ if ((event === "finish" || event === "close") && this._listeners)
1828
+ for (let i = 0; i < this._listeners.length; i++) this._listeners[i]();
1829
+ return true;
1830
+ }
1831
+ setHeader(name, value) {
1832
+ if (!this._headers) this._headers = [];
1833
+ if (Array.isArray(value)) {
1834
+ for (const v of value) this._headers.push(name, v);
1835
+ } else {
1836
+ this._headers.push(name, value);
1837
+ }
1838
+ return this;
1839
+ }
1840
+ getHeader(name) {
1841
+ if (!this._headers) return void 0;
1842
+ const lo = name.toLowerCase();
1843
+ for (let i = 0; i < this._headers.length; i += 2)
1844
+ if (this._headers[i].toLowerCase() === lo) return this._headers[i + 1];
1845
+ }
1846
+ removeHeader(name) {
1847
+ if (!this._headers) return this;
1848
+ const lo = name.toLowerCase();
1849
+ for (let i = 0; i < this._headers.length; i += 2)
1850
+ if (this._headers[i].toLowerCase() === lo) {
1851
+ this._headers.splice(i, 2);
1852
+ break;
1853
+ }
1854
+ return this;
1855
+ }
1856
+ hasHeader(name) {
1857
+ if (!this._headers) return false;
1858
+ const lo = name.toLowerCase();
1859
+ for (let i = 0; i < this._headers.length; i += 2)
1860
+ if (this._headers[i].toLowerCase() === lo) return true;
1861
+ return false;
1862
+ }
1863
+ writeHead(statusCode, headers) {
1864
+ this.statusCode = statusCode;
1865
+ if (headers)
1866
+ for (const [k, v] of Object.entries(headers)) this.setHeader(k, v);
1867
+ return this;
1868
+ }
1869
+ write(chunk) {
1870
+ if (this._sseWriter) {
1871
+ this._sseWriter.write(_encoder.encode(String(chunk)));
1872
+ return true;
1873
+ }
1874
+ this._body = (this._body ?? "") + chunk;
1875
+ return true;
1876
+ }
1877
+ end(data) {
1878
+ if (data !== void 0) this.write(data);
1879
+ if (this._sseWriter) this._sseWriter.close();
1880
+ this.headersSent = true;
1881
+ if (this._listeners)
1882
+ for (let i = 0; i < this._listeners.length; i++) this._listeners[i]();
1883
+ return this;
1884
+ }
1885
+ _initSseStream() {
1886
+ if (this._sseWindowClosed)
1887
+ throw new Error("[zeno/bun] initSSE() must be called synchronously before any `await` in your handler.");
1888
+ const { readable, writable } = new TransformStream();
1889
+ this._sseReadable = readable;
1890
+ this._sseWriter = writable.getWriter();
1891
+ }
1892
+ _closeSseWindow() {
1893
+ this._sseWindowClosed = true;
1894
+ }
1895
+ };
1896
+ var BunResponse = class extends withResponseMethods(_BunResponseBase) {
1897
+ // Override initSSE to set up a TransformStream before calling writeHead
1898
+ initSSE(options) {
1899
+ this._initSseStream();
1900
+ return super.initSSE(options);
1901
+ }
1902
+ };
1903
+ var bunAdapter = {
1904
+ name: "bun",
1905
+ createHandler: (config) => {
1906
+ if (typeof config !== "string") {
1907
+ throw new Error(
1908
+ "bunAdapter requires a routesDir string, not a ServerlessConfig."
1909
+ );
1910
+ }
1911
+ const routesDir = config;
1912
+ return async (config2 = {}) => {
1913
+ const { isDev, port = 3e3, defaultHeaders, cluster: clusterConfig } = config2;
1914
+ const primaryPid = parseInt(process.env.ZENO_BUN_WORKER ?? "0");
1915
+ const isWorker = primaryPid > 0;
1916
+ if (clusterConfig?.enabled && !isWorker) {
1917
+ const numWorkers = clusterConfig.workers ?? os__default.cpus().length;
1918
+ primaryLog(`\u{1F9F5} Starting Bun server with ${numWorkers} workers (reusePort)`);
1919
+ const procs = Array.from(
1920
+ { length: numWorkers },
1921
+ () => Bun.spawn(process.argv, {
1922
+ env: { ...process.env, ZENO_BUN_WORKER: String(process.pid) },
1923
+ stdout: "ignore",
1924
+ stderr: "inherit"
1925
+ })
1926
+ );
1927
+ primaryLog(`\u{1F680} Server running at http://localhost:${port}/`);
1928
+ return {
1929
+ close: (callback) => {
1930
+ for (const p of procs) p.kill();
1931
+ callback?.();
1932
+ }
1933
+ };
1934
+ }
1935
+ if (isWorker) {
1936
+ const interval = setInterval(() => {
1937
+ try {
1938
+ process.kill(primaryPid, 0);
1939
+ } catch {
1940
+ clearInterval(interval);
1941
+ process.exit(0);
1942
+ }
1943
+ }, 2e3);
1944
+ interval.unref();
1945
+ }
1946
+ primaryLog("\u{1F680} Bun high-performance mode enabled");
1947
+ await loadRoutes(routesDir);
1948
+ registerCorsConfig(config2.cors);
1949
+ registerMiddlewareConfig(config2.middleware);
1950
+ const defaultHeadersEntries = defaultHeaders ? Object.entries(defaultHeaders) : [];
1951
+ const server = Bun.serve({
1952
+ port,
1953
+ reusePort: isWorker,
1954
+ async fetch(request) {
1955
+ const url = new URL(request.url);
1956
+ const pathname = url.pathname;
1957
+ const req = new BunRequest(request, pathname, url.search);
1958
+ const res = new BunResponse();
1959
+ try {
1960
+ for (let i = 0; i < defaultHeadersEntries.length; i++) {
1961
+ res.setHeader(
1962
+ defaultHeadersEntries[i][0],
1963
+ defaultHeadersEntries[i][1]
1964
+ );
1965
+ }
1966
+ if (hasMiddlewares()) {
1967
+ const ok = await runMiddlewares(
1968
+ "beforeRequest",
1969
+ req,
1970
+ res
1971
+ );
1972
+ if (!ok || res.headersSent) return buildResponse(res);
1973
+ }
1974
+ const route = findRoute(pathname, request.method);
1975
+ if (!route) {
1976
+ if (hasMiddlewares())
1977
+ await runMiddlewares("onError", req, res);
1978
+ return new Response(
1979
+ JSON.stringify({ error: "Route not found" }),
1980
+ {
1981
+ status: 404,
1982
+ headers: { "Content-Type": "application/json" }
1983
+ }
1984
+ );
1985
+ }
1986
+ if ("error" in route) {
1987
+ if (hasMiddlewares())
1988
+ await runMiddlewares("onError", req, res);
1989
+ return new Response(JSON.stringify({ error: route.error }), {
1990
+ status: route.status || 500,
1991
+ headers: { "Content-Type": "application/json" }
1992
+ });
1993
+ }
1994
+ req.params = route.params;
1995
+ let handlerError = null;
1996
+ const handlerDone = (async () => {
1997
+ await route.handler(req, res);
1998
+ if (hasMiddlewares())
1999
+ await runMiddlewares("afterRequest", req, res);
2000
+ if (!res.headersSent) res.end();
2001
+ })().catch((err) => {
2002
+ handlerError = err;
2003
+ if (isDev) console.error("Server error:", err);
2004
+ if (res._sseReadable && !res.headersSent) res.end();
2005
+ });
2006
+ res._closeSseWindow();
2007
+ if (res._sseReadable) {
2008
+ return buildResponse(res, res._sseReadable);
2009
+ }
2010
+ await handlerDone;
2011
+ if (handlerError && !res.headersSent) {
2012
+ return new Response(
2013
+ JSON.stringify({ error: "Internal Server Error" }),
2014
+ {
2015
+ status: 500,
2016
+ headers: { "Content-Type": "application/json" }
2017
+ }
2018
+ );
2019
+ }
2020
+ return buildResponse(res);
2021
+ } catch (error) {
2022
+ if (isDev) console.error("Server error:", error);
2023
+ return new Response(
2024
+ JSON.stringify({ error: "Internal Server Error" }),
2025
+ {
2026
+ status: 500,
2027
+ headers: { "Content-Type": "application/json" }
2028
+ }
2029
+ );
2030
+ }
2031
+ }
2032
+ });
2033
+ primaryLog(
2034
+ `\u{1F680} Server started on http://localhost:${port}${isDev ? " (dev)" : ""}`
2035
+ );
2036
+ return {
2037
+ close: () => {
2038
+ server.stop();
2039
+ }
2040
+ };
2041
+ };
2042
+ }
2043
+ };
2044
+ function buildResponse(res, body) {
2045
+ const responseBody = body ?? res._body;
2046
+ if (!res._headers) return new Response(responseBody, { status: res.statusCode });
2047
+ const headers = new Headers();
2048
+ for (let i = 0; i < res._headers.length; i += 2) {
2049
+ const name = res._headers[i];
2050
+ const value = res._headers[i + 1];
2051
+ if (name.toLowerCase() === "set-cookie") {
2052
+ headers.append(name, value);
2053
+ } else {
2054
+ headers.set(name, value);
2055
+ }
2056
+ }
2057
+ return new Response(responseBody, { status: res.statusCode, headers });
2058
+ }
2059
+
2060
+ // src/adapters/index.ts
2061
+ var adapters = {
2062
+ node: nodeAdapter,
2063
+ vercel: vercelAdapter,
2064
+ netlify: netlifyAdapter,
2065
+ bun: bunAdapter
2066
+ };
2067
+ function getAdapter(platform = "node") {
2068
+ const adapter = adapters[platform];
2069
+ if (!adapter) {
2070
+ throw new Error(`Platform "${platform}" not supported`);
2071
+ }
2072
+ return adapter;
2073
+ }
2074
+
2075
+ export { addMiddleware, addPathMiddleware, bunAdapter, collectMiddleware, createCorsMiddleware, createSSEClient, findRoute, getAdapter, getPathMiddlewares, getRouterStats, getRoutesDir, hasMiddlewares, initSSE, isRouteError, loadMiddlewares, loadRoutes, netlifyAdapter, nodeAdapter, primaryLog, registerCorsConfig, registerMiddlewareConfig, registerRoutes, resetMiddlewares, resetRouter, router, runMiddlewares, send, sendEvent, sendJson, setVerboseLogging, sseClose, sseComment, sseEventError, sseId, sseRetry, vercelAdapter };