azurajs 3.0.1 → 3.0.2

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.
Files changed (106) hide show
  1. package/dist/config/index.js +128 -6
  2. package/dist/config/index.js.map +1 -1
  3. package/dist/config/index.mjs +130 -1
  4. package/dist/config/index.mjs.map +1 -1
  5. package/dist/core/index.js +1100 -11
  6. package/dist/core/index.js.map +1 -1
  7. package/dist/core/index.mjs +1102 -3
  8. package/dist/core/index.mjs.map +1 -1
  9. package/dist/decorators/index.js +117 -87
  10. package/dist/decorators/index.js.map +1 -1
  11. package/dist/decorators/index.mjs +98 -1
  12. package/dist/decorators/index.mjs.map +1 -1
  13. package/dist/index.js +2592 -236
  14. package/dist/index.js.map +1 -1
  15. package/dist/index.mjs +2537 -9
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/middleware/index.js +16 -7
  18. package/dist/middleware/index.js.map +1 -1
  19. package/dist/middleware/index.mjs +17 -1
  20. package/dist/middleware/index.mjs.map +1 -1
  21. package/dist/plugins/index.js +1056 -73
  22. package/dist/plugins/index.js.map +1 -1
  23. package/dist/plugins/index.mjs +1042 -1
  24. package/dist/plugins/index.mjs.map +1 -1
  25. package/dist/types/index.js +49 -12
  26. package/dist/types/index.js.map +1 -1
  27. package/dist/types/index.mjs +49 -2
  28. package/dist/types/index.mjs.map +1 -1
  29. package/dist/utils/index.js +551 -50
  30. package/dist/utils/index.js.map +1 -1
  31. package/dist/utils/index.mjs +541 -3
  32. package/dist/utils/index.mjs.map +1 -1
  33. package/package.json +35 -17
  34. package/{dist/chunk-DR254CWJ.mjs → src/config/ConfigModule.ts} +169 -132
  35. package/src/config/index.ts +1 -0
  36. package/src/core/index.ts +2 -0
  37. package/src/core/router.ts +284 -0
  38. package/{dist/chunk-EYAHUNC7.mjs → src/core/server.ts} +590 -699
  39. package/src/decorators/Route.ts +110 -0
  40. package/src/decorators/index.ts +23 -0
  41. package/src/index.ts +12 -0
  42. package/src/middleware/LoggingMiddleware.ts +20 -0
  43. package/src/middleware/index.ts +1 -0
  44. package/src/plugins/CORSPlugin.ts +56 -0
  45. package/src/plugins/CircuitBreakerPlugin.ts +84 -0
  46. package/src/plugins/CompressionPlugin.ts +80 -0
  47. package/src/plugins/ETagPlugin.ts +31 -0
  48. package/src/plugins/HealthCheckPlugin.ts +57 -0
  49. package/src/plugins/HelmetPlugin.ts +89 -0
  50. package/src/plugins/JWTPlugin.ts +132 -0
  51. package/src/plugins/MultipartPlugin.ts +168 -0
  52. package/src/plugins/ProxyPlugin.ts +89 -0
  53. package/src/plugins/RateLimitPlugin.ts +96 -0
  54. package/src/plugins/RequestIdPlugin.ts +21 -0
  55. package/src/plugins/SSEPlugin.ts +114 -0
  56. package/src/plugins/SessionPlugin.ts +98 -0
  57. package/src/plugins/StaticPlugin.ts +152 -0
  58. package/src/plugins/TimeoutPlugin.ts +33 -0
  59. package/src/plugins/index.ts +18 -0
  60. package/src/types/common.type.ts +82 -0
  61. package/src/types/config.type.ts +57 -0
  62. package/{dist/chunk-OWUGAI5V.mjs → src/types/http/status.ts} +49 -51
  63. package/src/types/index.ts +55 -0
  64. package/src/types/plugins/plugin.type.ts +170 -0
  65. package/src/types/reflect.d.ts +14 -0
  66. package/src/types/routes.type.ts +70 -0
  67. package/src/utils/HttpError.ts +62 -0
  68. package/src/utils/IpResolver.ts +30 -0
  69. package/src/utils/Logger.ts +144 -0
  70. package/src/utils/Parser.ts +182 -0
  71. package/src/utils/cookies/CookieManager.ts +48 -0
  72. package/src/utils/index.ts +9 -0
  73. package/{dist/chunk-UWIFSGSQ.mjs → src/utils/validators/DTOValidator.ts} +145 -141
  74. package/src/utils/validators/SchemaValidator.ts +45 -0
  75. package/dist/chunk-3UFAWS2V.js +0 -392
  76. package/dist/chunk-3UFAWS2V.js.map +0 -1
  77. package/dist/chunk-4LSFAAZW.js +0 -4
  78. package/dist/chunk-4LSFAAZW.js.map +0 -1
  79. package/dist/chunk-7NSRIVZM.js +0 -54
  80. package/dist/chunk-7NSRIVZM.js.map +0 -1
  81. package/dist/chunk-AOG6NYAM.js +0 -144
  82. package/dist/chunk-AOG6NYAM.js.map +0 -1
  83. package/dist/chunk-DR254CWJ.mjs.map +0 -1
  84. package/dist/chunk-EYAHUNC7.mjs.map +0 -1
  85. package/dist/chunk-HHDQPIJN.mjs +0 -19
  86. package/dist/chunk-HHDQPIJN.mjs.map +0 -1
  87. package/dist/chunk-HHZNAGGI.js +0 -702
  88. package/dist/chunk-HHZNAGGI.js.map +0 -1
  89. package/dist/chunk-KJM5XCAY.js +0 -21
  90. package/dist/chunk-KJM5XCAY.js.map +0 -1
  91. package/dist/chunk-NLSZKAPA.mjs +0 -1044
  92. package/dist/chunk-NLSZKAPA.mjs.map +0 -1
  93. package/dist/chunk-OWUGAI5V.mjs.map +0 -1
  94. package/dist/chunk-POPNQEOK.js +0 -1063
  95. package/dist/chunk-POPNQEOK.js.map +0 -1
  96. package/dist/chunk-QPRW4YU4.js +0 -134
  97. package/dist/chunk-QPRW4YU4.js.map +0 -1
  98. package/dist/chunk-REJDZUZ5.mjs +0 -382
  99. package/dist/chunk-REJDZUZ5.mjs.map +0 -1
  100. package/dist/chunk-TC6N6TJZ.mjs +0 -100
  101. package/dist/chunk-TC6N6TJZ.mjs.map +0 -1
  102. package/dist/chunk-TEUXKMXP.js +0 -122
  103. package/dist/chunk-TEUXKMXP.js.map +0 -1
  104. package/dist/chunk-UWIFSGSQ.mjs.map +0 -1
  105. package/dist/chunk-YPBKY4KY.mjs +0 -3
  106. package/dist/chunk-YPBKY4KY.mjs.map +0 -1
@@ -1,5 +1,1104 @@
1
- export { AzuraServer, Router } from '../chunk-EYAHUNC7.mjs';
2
- import '../chunk-REJDZUZ5.mjs';
3
- import '../chunk-OWUGAI5V.mjs';
1
+ import { createServer as createServer$1 } from 'http';
2
+ import { createServer } from 'https';
3
+ import { readFileSync } from 'fs';
4
+
5
+ // src/core/server.ts
6
+
7
+ // src/core/router.ts
8
+ var PARAM_PREFIX = 58;
9
+ function createNode(segment = "") {
10
+ return {
11
+ segment,
12
+ children: /* @__PURE__ */ new Map(),
13
+ paramChild: null,
14
+ paramName: "",
15
+ handlers: /* @__PURE__ */ new Map(),
16
+ wildcardHandler: null
17
+ };
18
+ }
19
+ var LRUCache = class {
20
+ capacity;
21
+ map;
22
+ head;
23
+ tail;
24
+ constructor(capacity) {
25
+ this.capacity = capacity;
26
+ this.map = /* @__PURE__ */ new Map();
27
+ this.head = { prev: null, next: null };
28
+ this.tail = { prev: null, next: null };
29
+ this.head.next = this.tail;
30
+ this.tail.prev = this.head;
31
+ }
32
+ get(key) {
33
+ const node = this.map.get(key);
34
+ if (!node) return void 0;
35
+ this.moveToHead(node);
36
+ return node.value;
37
+ }
38
+ set(key, value) {
39
+ const existing = this.map.get(key);
40
+ if (existing) {
41
+ existing.value = value;
42
+ this.moveToHead(existing);
43
+ return;
44
+ }
45
+ const node = { value, key, prev: null, next: null };
46
+ this.map.set(key, node);
47
+ this.addToHead(node);
48
+ if (this.map.size > this.capacity) {
49
+ const removed = this.removeTail();
50
+ if (removed) this.map.delete(removed.key);
51
+ }
52
+ }
53
+ addToHead(node) {
54
+ node.prev = this.head;
55
+ node.next = this.head.next;
56
+ this.head.next.prev = node;
57
+ this.head.next = node;
58
+ }
59
+ removeNode(node) {
60
+ node.prev.next = node.next;
61
+ node.next.prev = node.prev;
62
+ }
63
+ moveToHead(node) {
64
+ this.removeNode(node);
65
+ this.addToHead(node);
66
+ }
67
+ removeTail() {
68
+ const node = this.tail.prev;
69
+ if (node === this.head) return null;
70
+ this.removeNode(node);
71
+ return node;
72
+ }
73
+ clear() {
74
+ this.map.clear();
75
+ this.head.next = this.tail;
76
+ this.tail.prev = this.head;
77
+ }
78
+ };
79
+ var Router = class {
80
+ root = createNode();
81
+ cache;
82
+ staticRoutes = /* @__PURE__ */ new Map();
83
+ constructor(cacheSize = 1024) {
84
+ this.cache = new LRUCache(cacheSize);
85
+ }
86
+ add(method, path, handler, middlewares = [], meta) {
87
+ this.cache.clear();
88
+ const normalizedPath = this.normalizePath(path);
89
+ const stored = { handler, middlewares, meta };
90
+ if (!normalizedPath.includes(":") && !normalizedPath.includes("*")) {
91
+ const key = `${method}:${normalizedPath}`;
92
+ this.staticRoutes.set(key, stored);
93
+ return;
94
+ }
95
+ const segments = normalizedPath.split("/").filter(Boolean);
96
+ let node = this.root;
97
+ for (let i = 0; i < segments.length; i++) {
98
+ const seg = segments[i];
99
+ if (seg === "*") {
100
+ if (!node.wildcardHandler) {
101
+ node.wildcardHandler = /* @__PURE__ */ new Map();
102
+ }
103
+ node.wildcardHandler.set(method, stored);
104
+ return;
105
+ }
106
+ if (seg.charCodeAt(0) === PARAM_PREFIX) {
107
+ if (!node.paramChild) {
108
+ node.paramChild = createNode(seg);
109
+ node.paramChild.paramName = seg.slice(1);
110
+ }
111
+ node = node.paramChild;
112
+ continue;
113
+ }
114
+ let child = node.children.get(seg);
115
+ if (!child) {
116
+ child = createNode(seg);
117
+ node.children.set(seg, child);
118
+ }
119
+ node = child;
120
+ }
121
+ node.handlers.set(method, stored);
122
+ }
123
+ find(method, path) {
124
+ const normalizedPath = this.normalizePath(path);
125
+ const staticKey = `${method}:${normalizedPath}`;
126
+ const staticRoute = this.staticRoutes.get(staticKey);
127
+ if (staticRoute) {
128
+ return {
129
+ handler: staticRoute.handler,
130
+ params: {},
131
+ middlewares: staticRoute.middlewares,
132
+ meta: staticRoute.meta
133
+ };
134
+ }
135
+ const cacheKey = staticKey;
136
+ const cached = this.cache.get(cacheKey);
137
+ if (cached !== void 0) {
138
+ return cached ? { ...cached, params: { ...cached.params } } : null;
139
+ }
140
+ const segments = normalizedPath.split("/").filter(Boolean);
141
+ const params = {};
142
+ const result = this.matchNode(this.root, segments, 0, params, method);
143
+ if (result) {
144
+ const match = {
145
+ handler: result.handler,
146
+ params,
147
+ middlewares: result.middlewares,
148
+ meta: result.meta
149
+ };
150
+ this.cache.set(cacheKey, match);
151
+ return { ...match, params: { ...params } };
152
+ }
153
+ this.cache.set(cacheKey, null);
154
+ return null;
155
+ }
156
+ matchNode(node, segments, idx, params, method) {
157
+ if (idx === segments.length) {
158
+ return node.handlers.get(method) ?? null;
159
+ }
160
+ const seg = segments[idx];
161
+ const child = node.children.get(seg);
162
+ if (child) {
163
+ const result = this.matchNode(child, segments, idx + 1, params, method);
164
+ if (result) return result;
165
+ }
166
+ if (node.paramChild) {
167
+ params[node.paramChild.paramName] = seg;
168
+ const result = this.matchNode(node.paramChild, segments, idx + 1, params, method);
169
+ if (result) return result;
170
+ delete params[node.paramChild.paramName];
171
+ }
172
+ if (node.wildcardHandler) {
173
+ const route = node.wildcardHandler.get(method);
174
+ if (route) return route;
175
+ }
176
+ return null;
177
+ }
178
+ normalizePath(path) {
179
+ if (path === "/") return "/";
180
+ if (path.endsWith("/") && path.length > 1) return path.slice(0, -1);
181
+ return path;
182
+ }
183
+ getAllRoutes() {
184
+ const routes = [];
185
+ for (const [key] of this.staticRoutes) {
186
+ const [method, path] = key.split(":", 2);
187
+ routes.push({ method, path });
188
+ }
189
+ this.collectRoutes(this.root, "", routes);
190
+ return routes;
191
+ }
192
+ collectRoutes(node, prefix, routes) {
193
+ for (const [method] of node.handlers) {
194
+ routes.push({ method, path: prefix || "/" });
195
+ }
196
+ for (const [seg, child] of node.children) {
197
+ this.collectRoutes(child, `${prefix}/${seg}`, routes);
198
+ }
199
+ if (node.paramChild) {
200
+ this.collectRoutes(
201
+ node.paramChild,
202
+ `${prefix}/:${node.paramChild.paramName}`,
203
+ routes
204
+ );
205
+ }
206
+ if (node.wildcardHandler) {
207
+ for (const [method] of node.wildcardHandler) {
208
+ routes.push({ method, path: `${prefix}/*` });
209
+ }
210
+ }
211
+ }
212
+ };
213
+
214
+ // src/utils/Logger.ts
215
+ var LEVEL_PRIORITY = {
216
+ debug: 0,
217
+ info: 1,
218
+ warn: 2,
219
+ error: 3,
220
+ silent: 4
221
+ };
222
+ var COLORS = {
223
+ reset: "\x1B[0m",
224
+ bold: "\x1B[1m",
225
+ dim: "\x1B[2m",
226
+ red: "\x1B[31m",
227
+ green: "\x1B[32m",
228
+ yellow: "\x1B[33m",
229
+ blue: "\x1B[34m",
230
+ magenta: "\x1B[35m",
231
+ cyan: "\x1B[36m",
232
+ white: "\x1B[37m",
233
+ gray: "\x1B[90m"};
234
+ var METHOD_COLORS = {
235
+ GET: COLORS.green,
236
+ POST: COLORS.blue,
237
+ PUT: COLORS.yellow,
238
+ DELETE: COLORS.red,
239
+ PATCH: COLORS.magenta,
240
+ HEAD: COLORS.cyan,
241
+ OPTIONS: COLORS.gray
242
+ };
243
+ function statusColor(code) {
244
+ if (code < 200) return COLORS.gray;
245
+ if (code < 300) return COLORS.green;
246
+ if (code < 400) return COLORS.cyan;
247
+ if (code < 500) return COLORS.yellow;
248
+ return COLORS.red;
249
+ }
250
+ function formatDuration(ns) {
251
+ const us = Number(ns) / 1e3;
252
+ if (us < 1e3) return `${us.toFixed(0)}\xB5s`;
253
+ const ms = us / 1e3;
254
+ if (ms < 1e3) return `${ms.toFixed(1)}ms`;
255
+ return `${(ms / 1e3).toFixed(2)}s`;
256
+ }
257
+ var Logger = class {
258
+ level;
259
+ useColors;
260
+ showTimestamp;
261
+ prefix;
262
+ constructor(options = {}) {
263
+ this.level = options.level ?? "info";
264
+ this.useColors = options.colors ?? process.stdout.isTTY !== false;
265
+ this.showTimestamp = options.timestamp ?? true;
266
+ this.prefix = options.prefix ?? "azura";
267
+ }
268
+ shouldLog(level) {
269
+ return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[this.level];
270
+ }
271
+ timestamp() {
272
+ if (!this.showTimestamp) return "";
273
+ const now = /* @__PURE__ */ new Date();
274
+ return `${COLORS.gray}${now.toISOString().slice(11, 23)}${COLORS.reset} `;
275
+ }
276
+ tag(level) {
277
+ if (!this.useColors) return `[${level.toUpperCase()}]`;
278
+ const colorMap = {
279
+ debug: COLORS.gray,
280
+ info: COLORS.blue,
281
+ warn: COLORS.yellow,
282
+ error: COLORS.red
283
+ };
284
+ return `${colorMap[level] ?? ""}[${level.toUpperCase()}]${COLORS.reset}`;
285
+ }
286
+ debug(message, ...args) {
287
+ if (!this.shouldLog("debug")) return;
288
+ console.debug(`${this.timestamp()}${this.tag("debug")} ${message}`, ...args);
289
+ }
290
+ info(message, ...args) {
291
+ if (!this.shouldLog("info")) return;
292
+ console.info(`${this.timestamp()}${this.tag("info")} ${message}`, ...args);
293
+ }
294
+ warn(message, ...args) {
295
+ if (!this.shouldLog("warn")) return;
296
+ console.warn(`${this.timestamp()}${this.tag("warn")} ${message}`, ...args);
297
+ }
298
+ error(message, ...args) {
299
+ if (!this.shouldLog("error")) return;
300
+ console.error(`${this.timestamp()}${this.tag("error")} ${message}`, ...args);
301
+ }
302
+ request(method, path, statusCode, duration) {
303
+ if (!this.shouldLog("info")) return;
304
+ const mc = this.useColors ? METHOD_COLORS[method] ?? COLORS.white : "";
305
+ const sc = this.useColors ? statusColor(statusCode) : "";
306
+ const r = this.useColors ? COLORS.reset : "";
307
+ const dur = formatDuration(duration);
308
+ console.info(
309
+ `${this.timestamp()}${mc}${method.padEnd(7)}${r} ${path} ${sc}${statusCode}${r} ${COLORS.dim}${dur}${r}`
310
+ );
311
+ }
312
+ banner(port, host) {
313
+ if (!this.shouldLog("info")) return;
314
+ const c = this.useColors;
315
+ const lines = [
316
+ "",
317
+ `${c ? COLORS.bold + COLORS.cyan : ""} \u26A1 AzuraJS v3.0.0${c ? COLORS.reset : ""}`,
318
+ "",
319
+ `${c ? COLORS.green : ""} \u279C Local: ${c ? COLORS.bold : ""}http://${host}:${port}/${c ? COLORS.reset : ""}`,
320
+ `${c ? COLORS.dim : ""} \u279C Press Ctrl+C to stop${c ? COLORS.reset : ""}`,
321
+ ""
322
+ ];
323
+ console.info(lines.join("\n"));
324
+ }
325
+ setLevel(level) {
326
+ this.level = level;
327
+ }
328
+ };
329
+ new Logger();
330
+
331
+ // src/types/http/status.ts
332
+ var HttpStatusText = {
333
+ 200: "OK",
334
+ 201: "Created",
335
+ 202: "Accepted",
336
+ 204: "No Content",
337
+ 301: "Moved Permanently",
338
+ 302: "Found",
339
+ 304: "Not Modified",
340
+ 400: "Bad Request",
341
+ 401: "Unauthorized",
342
+ 403: "Forbidden",
343
+ 404: "Not Found",
344
+ 405: "Method Not Allowed",
345
+ 409: "Conflict",
346
+ 410: "Gone",
347
+ 422: "Unprocessable Entity",
348
+ 429: "Too Many Requests",
349
+ 500: "Internal Server Error",
350
+ 501: "Not Implemented",
351
+ 502: "Bad Gateway",
352
+ 503: "Service Unavailable",
353
+ 504: "Gateway Timeout"
354
+ };
355
+
356
+ // src/utils/HttpError.ts
357
+ var HttpError = class _HttpError extends Error {
358
+ statusCode;
359
+ details;
360
+ isOperational;
361
+ constructor(statusCode, message, details) {
362
+ super(message ?? HttpStatusText[statusCode] ?? "Unknown Error");
363
+ this.statusCode = statusCode;
364
+ this.details = details;
365
+ this.isOperational = true;
366
+ Object.setPrototypeOf(this, _HttpError.prototype);
367
+ }
368
+ toJSON() {
369
+ const obj = {
370
+ error: {
371
+ statusCode: this.statusCode,
372
+ message: this.message
373
+ }
374
+ };
375
+ if (this.details) obj.error.details = this.details;
376
+ return obj;
377
+ }
378
+ static badRequest(message, details) {
379
+ return new _HttpError(400, message ?? "Bad Request", details);
380
+ }
381
+ static unauthorized(message) {
382
+ return new _HttpError(401, message ?? "Unauthorized");
383
+ }
384
+ static forbidden(message) {
385
+ return new _HttpError(403, message ?? "Forbidden");
386
+ }
387
+ static notFound(message) {
388
+ return new _HttpError(404, message ?? "Not Found");
389
+ }
390
+ static methodNotAllowed(message) {
391
+ return new _HttpError(405, message ?? "Method Not Allowed");
392
+ }
393
+ static conflict(message, details) {
394
+ return new _HttpError(409, message ?? "Conflict", details);
395
+ }
396
+ static unprocessableEntity(message, details) {
397
+ return new _HttpError(422, message ?? "Unprocessable Entity", details);
398
+ }
399
+ static tooManyRequests(message) {
400
+ return new _HttpError(429, message ?? "Too Many Requests");
401
+ }
402
+ static internal(message) {
403
+ return new _HttpError(500, message ?? "Internal Server Error");
404
+ }
405
+ };
406
+
407
+ // src/utils/Parser.ts
408
+ function parseQueryString(qs) {
409
+ const result = /* @__PURE__ */ Object.create(null);
410
+ if (!qs || qs.length === 0) return result;
411
+ let key = "";
412
+ let value = "";
413
+ let startingKey = true;
414
+ let i = qs.charCodeAt(0) === 63 ? 1 : 0;
415
+ for (; i < qs.length; i++) {
416
+ const ch = qs.charCodeAt(i);
417
+ if (ch === 61 && startingKey) {
418
+ startingKey = false;
419
+ continue;
420
+ }
421
+ if (ch === 38) {
422
+ if (key.length > 0) {
423
+ const decodedKey = fastDecode(key);
424
+ const decodedVal = fastDecode(value);
425
+ const existing = result[decodedKey];
426
+ if (existing === void 0) {
427
+ result[decodedKey] = decodedVal;
428
+ } else if (typeof existing === "string") {
429
+ result[decodedKey] = [existing, decodedVal];
430
+ } else {
431
+ existing.push(decodedVal);
432
+ }
433
+ }
434
+ key = "";
435
+ value = "";
436
+ startingKey = true;
437
+ continue;
438
+ }
439
+ if (startingKey) {
440
+ key += qs[i];
441
+ } else {
442
+ value += qs[i];
443
+ }
444
+ }
445
+ if (key.length > 0) {
446
+ const decodedKey = fastDecode(key);
447
+ const decodedVal = fastDecode(value);
448
+ const existing = result[decodedKey];
449
+ if (existing === void 0) {
450
+ result[decodedKey] = decodedVal;
451
+ } else if (typeof existing === "string") {
452
+ result[decodedKey] = [existing, decodedVal];
453
+ } else {
454
+ existing.push(decodedVal);
455
+ }
456
+ }
457
+ return result;
458
+ }
459
+ function fastDecode(str) {
460
+ if (str.indexOf("%") === -1 && str.indexOf("+") === -1) return str;
461
+ try {
462
+ return decodeURIComponent(str.replace(/\+/g, " "));
463
+ } catch {
464
+ return str;
465
+ }
466
+ }
467
+ function parseCookies(header) {
468
+ const cookies = /* @__PURE__ */ Object.create(null);
469
+ if (!header) return cookies;
470
+ let i = 0;
471
+ const len = header.length;
472
+ while (i < len) {
473
+ while (i < len && header.charCodeAt(i) === 32) i++;
474
+ let eqIdx = -1;
475
+ let semiIdx = -1;
476
+ for (let j = i; j < len; j++) {
477
+ const ch = header.charCodeAt(j);
478
+ if (ch === 61 && eqIdx === -1) eqIdx = j;
479
+ if (ch === 59) {
480
+ semiIdx = j;
481
+ break;
482
+ }
483
+ }
484
+ if (eqIdx === -1) {
485
+ i = semiIdx === -1 ? len : semiIdx + 1;
486
+ continue;
487
+ }
488
+ const end = semiIdx === -1 ? len : semiIdx;
489
+ const name = header.slice(i, eqIdx).trim();
490
+ let val = header.slice(eqIdx + 1, end).trim();
491
+ if (val.charCodeAt(0) === 34 && val.charCodeAt(val.length - 1) === 34) {
492
+ val = val.slice(1, -1);
493
+ }
494
+ if (cookies[name] === void 0) {
495
+ cookies[name] = fastDecode(val);
496
+ }
497
+ i = end + 1;
498
+ }
499
+ return cookies;
500
+ }
501
+ var CONTENT_TYPE_JSON = "application/json";
502
+ var CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";
503
+ async function parseBody(req) {
504
+ const contentType = req.headers["content-type"] ?? "";
505
+ const contentLength = req.headers["content-length"];
506
+ if (req.method === "GET" || req.method === "HEAD" || req.method === "OPTIONS" || contentLength !== void 0 && contentLength === "0") {
507
+ return void 0;
508
+ }
509
+ const chunks = [];
510
+ let totalSize = 0;
511
+ const maxSize = 10 * 1024 * 1024;
512
+ return new Promise((resolve, reject) => {
513
+ req.on("data", (chunk) => {
514
+ totalSize += chunk.length;
515
+ if (totalSize > maxSize) {
516
+ req.destroy();
517
+ reject(new Error("Request body too large"));
518
+ return;
519
+ }
520
+ chunks.push(chunk);
521
+ });
522
+ req.on("end", () => {
523
+ if (chunks.length === 0) {
524
+ resolve(void 0);
525
+ return;
526
+ }
527
+ const raw = Buffer.concat(chunks).toString("utf-8");
528
+ if (contentType.startsWith(CONTENT_TYPE_JSON)) {
529
+ try {
530
+ resolve(JSON.parse(raw));
531
+ } catch {
532
+ resolve(raw);
533
+ }
534
+ } else if (contentType.startsWith(CONTENT_TYPE_FORM)) {
535
+ resolve(parseQueryString(raw));
536
+ } else {
537
+ resolve(raw);
538
+ }
539
+ });
540
+ req.on("error", reject);
541
+ });
542
+ }
543
+ function parseUrl(url) {
544
+ let pathname = "";
545
+ let search = "";
546
+ let i = 0;
547
+ for (; i < url.length; i++) {
548
+ if (url.charCodeAt(i) === 63) {
549
+ pathname = url.slice(0, i);
550
+ search = url.slice(i);
551
+ return { pathname, search };
552
+ }
553
+ }
554
+ return { pathname: url, search: "" };
555
+ }
556
+
557
+ // src/utils/IpResolver.ts
558
+ var PROXY_HEADERS = [
559
+ "x-forwarded-for",
560
+ "x-real-ip",
561
+ "cf-connecting-ip",
562
+ "x-client-ip",
563
+ "x-cluster-client-ip",
564
+ "fastly-client-ip",
565
+ "true-client-ip"
566
+ ];
567
+ function resolveIp(req) {
568
+ for (const header of PROXY_HEADERS) {
569
+ const val = req.headers[header];
570
+ if (val) {
571
+ const ip = typeof val === "string" ? val.split(",")[0].trim() : val[0];
572
+ if (ip && ip.length > 0) return normalizeIp(ip);
573
+ }
574
+ }
575
+ const remoteAddr = req.socket?.remoteAddress ?? "127.0.0.1";
576
+ return normalizeIp(remoteAddr);
577
+ }
578
+ function normalizeIp(ip) {
579
+ if (ip === "::1" || ip === "::ffff:127.0.0.1") return "127.0.0.1";
580
+ if (ip.startsWith("::ffff:")) return ip.slice(7);
581
+ return ip;
582
+ }
583
+
584
+ // src/utils/cookies/CookieManager.ts
585
+ function serializeCookie(name, value, options = {}) {
586
+ let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
587
+ if (options.maxAge != null) {
588
+ cookie += `; Max-Age=${Math.floor(options.maxAge)}`;
589
+ }
590
+ if (options.expires) {
591
+ cookie += `; Expires=${options.expires.toUTCString()}`;
592
+ }
593
+ if (options.domain) {
594
+ cookie += `; Domain=${options.domain}`;
595
+ }
596
+ cookie += `; Path=${options.path ?? "/"}`;
597
+ if (options.secure) {
598
+ cookie += "; Secure";
599
+ }
600
+ if (options.httpOnly !== false) {
601
+ cookie += "; HttpOnly";
602
+ }
603
+ if (options.sameSite) {
604
+ cookie += `; SameSite=${options.sameSite}`;
605
+ }
606
+ return cookie;
607
+ }
608
+ function clearCookieHeader(name, options = {}) {
609
+ return serializeCookie(name, "", {
610
+ ...options,
611
+ maxAge: 0,
612
+ expires: /* @__PURE__ */ new Date(0)
613
+ });
614
+ }
615
+
616
+ // src/core/server.ts
617
+ var CONTROLLER_META_KEY = "azura:controller";
618
+ var ROUTES_META_KEY = "azura:routes";
619
+ var PARAMS_META_KEY = "azura:params";
620
+ var AzuraServer = class {
621
+ server = null;
622
+ router;
623
+ logger;
624
+ middlewares = [];
625
+ plugins = [];
626
+ errorHandler = null;
627
+ config;
628
+ compiledChain = null;
629
+ constructor(config = {}) {
630
+ this.config = {
631
+ environment: config.environment ?? process.env.NODE_ENV ?? "development",
632
+ server: {
633
+ port: config.server?.port ?? 3e3,
634
+ host: config.server?.host ?? "0.0.0.0",
635
+ keepAliveTimeout: config.server?.keepAliveTimeout ?? 72e3,
636
+ headersTimeout: config.server?.headersTimeout ?? 75e3,
637
+ requestTimeout: config.server?.requestTimeout ?? 3e4,
638
+ ...config.server
639
+ },
640
+ logging: {
641
+ enabled: config.logging?.enabled ?? true,
642
+ level: config.logging?.level ?? "info",
643
+ timestamp: config.logging?.timestamp ?? true,
644
+ colors: config.logging?.colors ?? true,
645
+ requestLogging: config.logging?.requestLogging ?? true,
646
+ ...config.logging
647
+ },
648
+ plugins: config.plugins ?? {},
649
+ debug: config.debug ?? false
650
+ };
651
+ this.router = new Router();
652
+ this.logger = new Logger({
653
+ level: this.config.logging?.enabled === false ? "silent" : this.config.logging?.level,
654
+ colors: this.config.logging?.colors,
655
+ timestamp: this.config.logging?.timestamp
656
+ });
657
+ }
658
+ use(handler) {
659
+ this.compiledChain = null;
660
+ if (handler.length <= 2) {
661
+ this.plugins.push(handler);
662
+ } else {
663
+ this.middlewares.push(handler);
664
+ }
665
+ return this;
666
+ }
667
+ plugin(handler) {
668
+ this.compiledChain = null;
669
+ this.plugins.push(handler);
670
+ return this;
671
+ }
672
+ onError(handler) {
673
+ this.errorHandler = handler;
674
+ return this;
675
+ }
676
+ get(path, ...handlers) {
677
+ return this.route("GET", path, handlers);
678
+ }
679
+ post(path, ...handlers) {
680
+ return this.route("POST", path, handlers);
681
+ }
682
+ put(path, ...handlers) {
683
+ return this.route("PUT", path, handlers);
684
+ }
685
+ delete(path, ...handlers) {
686
+ return this.route("DELETE", path, handlers);
687
+ }
688
+ patch(path, ...handlers) {
689
+ return this.route("PATCH", path, handlers);
690
+ }
691
+ head(path, ...handlers) {
692
+ return this.route("HEAD", path, handlers);
693
+ }
694
+ options(path, ...handlers) {
695
+ return this.route("OPTIONS", path, handlers);
696
+ }
697
+ all(path, ...handlers) {
698
+ const methods = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
699
+ for (const method of methods) {
700
+ this.route(method, path, handlers);
701
+ }
702
+ return this;
703
+ }
704
+ route(method, path, handlers) {
705
+ const routeHandler = handlers[handlers.length - 1];
706
+ const middlewares = handlers.slice(0, -1);
707
+ this.router.add(method, path, routeHandler, middlewares);
708
+ return this;
709
+ }
710
+ register(...controllers) {
711
+ for (const Controller of controllers) {
712
+ this.registerController(Controller);
713
+ }
714
+ return this;
715
+ }
716
+ registerController(Controller) {
717
+ const instance = new Controller();
718
+ const prefix = Reflect.getMetadata?.(CONTROLLER_META_KEY, Controller) ?? "";
719
+ const controllerMiddlewares = Reflect.getMetadata?.("azura:middlewares", Controller) ?? [];
720
+ const prototype = Controller.prototype;
721
+ const propertyNames = Object.getOwnPropertyNames(prototype).filter(
722
+ (p) => p !== "constructor"
723
+ );
724
+ for (const propertyKey of propertyNames) {
725
+ const routeMeta = Reflect.getMetadata?.(
726
+ ROUTES_META_KEY,
727
+ prototype,
728
+ propertyKey
729
+ );
730
+ if (!routeMeta) continue;
731
+ const fullPath = this.joinPaths(prefix, routeMeta.path);
732
+ const paramsMeta = Reflect.getMetadata?.(PARAMS_META_KEY, prototype, propertyKey) ?? [];
733
+ const routeMiddlewares = Reflect.getMetadata?.("azura:middlewares", prototype, propertyKey) ?? [];
734
+ const allMiddlewares = [...controllerMiddlewares, ...routeMiddlewares];
735
+ const handler = async (req, res) => {
736
+ const args = this.resolveParams(paramsMeta, req, res);
737
+ const result = await instance[propertyKey](...args);
738
+ if (result !== void 0 && !res.sent) {
739
+ if (typeof result === "object") {
740
+ res.json(result);
741
+ } else {
742
+ res.send(String(result));
743
+ }
744
+ }
745
+ };
746
+ this.router.add(routeMeta.method, fullPath, handler, allMiddlewares, routeMeta.meta);
747
+ }
748
+ }
749
+ resolveParams(paramsMeta, req, res) {
750
+ if (!paramsMeta || paramsMeta.length === 0) return [req, res];
751
+ const sorted = [...paramsMeta].sort((a, b) => a.index - b.index);
752
+ const args = new Array(sorted.length);
753
+ for (const meta of sorted) {
754
+ let value;
755
+ switch (meta.type) {
756
+ case "body":
757
+ value = meta.name ? req.body?.[meta.name] : req.body;
758
+ break;
759
+ case "query":
760
+ value = meta.name ? req.query[meta.name] : req.query;
761
+ break;
762
+ case "param":
763
+ value = meta.name ? req.params[meta.name] : req.params;
764
+ break;
765
+ case "header":
766
+ value = meta.name ? req.headers[meta.name.toLowerCase()] : req.headers;
767
+ break;
768
+ case "cookie":
769
+ value = meta.name ? req.cookies[meta.name] : req.cookies;
770
+ break;
771
+ case "req":
772
+ value = req;
773
+ break;
774
+ case "res":
775
+ value = res;
776
+ break;
777
+ case "next":
778
+ value = () => {
779
+ };
780
+ break;
781
+ case "ip":
782
+ value = req.ip;
783
+ break;
784
+ case "session":
785
+ value = req.session;
786
+ break;
787
+ default:
788
+ value = void 0;
789
+ }
790
+ if (meta.pipes) {
791
+ for (const pipe of meta.pipes) {
792
+ value = pipe(value);
793
+ }
794
+ }
795
+ args[meta.index] = value;
796
+ }
797
+ return args;
798
+ }
799
+ joinPaths(prefix, path) {
800
+ const p = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
801
+ const s = path.startsWith("/") ? path : `/${path}`;
802
+ return `${p}${s}`;
803
+ }
804
+ extendRequest(req, pathname, query) {
805
+ const azReq = req;
806
+ azReq.pathname = pathname;
807
+ azReq.query = query;
808
+ azReq.params = {};
809
+ azReq.cookies = parseCookies(req.headers.cookie);
810
+ azReq.ip = resolveIp(req);
811
+ azReq.protocol = req.socket.encrypted ? "https" : "http";
812
+ azReq.secure = azReq.protocol === "https";
813
+ azReq.hostname = (req.headers.host ?? "localhost").split(":")[0];
814
+ azReq.startTime = process.hrtime.bigint();
815
+ azReq.raw = req;
816
+ return azReq;
817
+ }
818
+ extendResponse(res) {
819
+ const azRes = res;
820
+ azRes.locals = {};
821
+ azRes.sent = false;
822
+ azRes.status = function(code) {
823
+ this.statusCode = code;
824
+ return this;
825
+ };
826
+ azRes.json = function(data) {
827
+ if (this.sent) return;
828
+ this.sent = true;
829
+ const body = JSON.stringify(data);
830
+ this.setHeader("Content-Type", "application/json; charset=utf-8");
831
+ this.setHeader("Content-Length", Buffer.byteLength(body));
832
+ this.end(body);
833
+ };
834
+ azRes.send = function(body) {
835
+ if (this.sent) return;
836
+ this.sent = true;
837
+ if (!this.getHeader("Content-Type")) {
838
+ this.setHeader("Content-Type", typeof body === "string" ? "text/plain; charset=utf-8" : "application/octet-stream");
839
+ }
840
+ const buf = typeof body === "string" ? Buffer.from(body) : body;
841
+ this.setHeader("Content-Length", buf.length);
842
+ this.end(buf);
843
+ };
844
+ azRes.html = function(body) {
845
+ if (this.sent) return;
846
+ this.sent = true;
847
+ this.setHeader("Content-Type", "text/html; charset=utf-8");
848
+ this.setHeader("Content-Length", Buffer.byteLength(body));
849
+ this.end(body);
850
+ };
851
+ azRes.redirect = function(url, code = 302) {
852
+ if (this.sent) return;
853
+ this.sent = true;
854
+ this.statusCode = code;
855
+ this.setHeader("Location", url);
856
+ this.end();
857
+ };
858
+ azRes.cookie = function(name, value, options) {
859
+ const header = serializeCookie(name, value, options);
860
+ const existing = this.getHeader("Set-Cookie");
861
+ if (existing) {
862
+ const arr = Array.isArray(existing) ? existing : [String(existing)];
863
+ arr.push(header);
864
+ this.setHeader("Set-Cookie", arr);
865
+ } else {
866
+ this.setHeader("Set-Cookie", header);
867
+ }
868
+ return this;
869
+ };
870
+ azRes.clearCookie = function(name, options) {
871
+ const header = clearCookieHeader(name, options);
872
+ const existing = this.getHeader("Set-Cookie");
873
+ if (existing) {
874
+ const arr = Array.isArray(existing) ? existing : [String(existing)];
875
+ arr.push(header);
876
+ this.setHeader("Set-Cookie", arr);
877
+ } else {
878
+ this.setHeader("Set-Cookie", header);
879
+ }
880
+ return this;
881
+ };
882
+ azRes.header = function(name, value) {
883
+ this.setHeader(name, value);
884
+ return this;
885
+ };
886
+ azRes.type = function(contentType) {
887
+ this.setHeader("Content-Type", contentType);
888
+ return this;
889
+ };
890
+ azRes.attachment = function(filename) {
891
+ if (filename) {
892
+ this.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
893
+ } else {
894
+ this.setHeader("Content-Disposition", "attachment");
895
+ }
896
+ return this;
897
+ };
898
+ azRes.download = function(_path, _filename) {
899
+ };
900
+ return azRes;
901
+ }
902
+ compileMiddlewareChain() {
903
+ if (this.compiledChain) return this.compiledChain;
904
+ const self = this;
905
+ const globalMiddlewares = [...this.middlewares];
906
+ this.compiledChain = async (req, res) => {
907
+ for (const plugin of self.plugins) {
908
+ if (res.sent) return;
909
+ await new Promise((resolve, reject) => {
910
+ try {
911
+ const result = plugin({ req, res }, (err) => {
912
+ if (err) reject(err);
913
+ else resolve();
914
+ });
915
+ if (result && typeof result.then === "function") {
916
+ result.then(resolve, reject);
917
+ }
918
+ } catch (e) {
919
+ reject(e);
920
+ }
921
+ });
922
+ }
923
+ if (res.sent) return;
924
+ let idx = 0;
925
+ const runMiddleware = async () => {
926
+ if (idx >= globalMiddlewares.length || res.sent) return;
927
+ const mw = globalMiddlewares[idx++];
928
+ await new Promise((resolve, reject) => {
929
+ try {
930
+ const result = mw(req, res, (err) => {
931
+ if (err) reject(err);
932
+ else resolve();
933
+ });
934
+ if (result && typeof result.then === "function") {
935
+ result.then(resolve, reject);
936
+ }
937
+ } catch (e) {
938
+ reject(e);
939
+ }
940
+ });
941
+ await runMiddleware();
942
+ };
943
+ await runMiddleware();
944
+ };
945
+ return this.compiledChain;
946
+ }
947
+ async handleRequest(req, res) {
948
+ const { pathname, search } = parseUrl(req.url ?? "/");
949
+ const query = parseQueryString(search);
950
+ const azReq = this.extendRequest(req, pathname, query);
951
+ const azRes = this.extendResponse(res);
952
+ try {
953
+ const chain = this.compileMiddlewareChain();
954
+ await chain(azReq, azRes);
955
+ if (azRes.sent) return;
956
+ const method = (req.method ?? "GET").toUpperCase();
957
+ const match = this.router.find(method, pathname);
958
+ if (!match) {
959
+ if (pathname === "/favicon.ico") {
960
+ azRes.status(204).send("");
961
+ return;
962
+ }
963
+ throw HttpError.notFound(`Route ${method} ${pathname} not found`);
964
+ }
965
+ azReq.params = match.params;
966
+ if (method !== "GET" && method !== "HEAD" && method !== "OPTIONS") {
967
+ azReq.body = await parseBody(req);
968
+ }
969
+ for (const mw of match.middlewares) {
970
+ if (azRes.sent) return;
971
+ await new Promise((resolve, reject) => {
972
+ try {
973
+ const result2 = mw(azReq, azRes, (err) => {
974
+ if (err) reject(err);
975
+ else resolve();
976
+ });
977
+ if (result2 && typeof result2.then === "function") {
978
+ result2.then(resolve, reject);
979
+ }
980
+ } catch (e) {
981
+ reject(e);
982
+ }
983
+ });
984
+ }
985
+ if (azRes.sent) return;
986
+ const result = await match.handler(azReq, azRes);
987
+ if (result !== void 0 && !azRes.sent) {
988
+ if (typeof result === "object" && result !== null) {
989
+ azRes.json(result);
990
+ } else {
991
+ azRes.send(String(result));
992
+ }
993
+ }
994
+ } catch (err) {
995
+ this.handleError(err, azReq, azRes);
996
+ } finally {
997
+ if (this.config.logging?.requestLogging) {
998
+ const duration = process.hrtime.bigint() - azReq.startTime;
999
+ this.logger.request(
1000
+ req.method ?? "GET",
1001
+ pathname,
1002
+ res.statusCode,
1003
+ duration
1004
+ );
1005
+ }
1006
+ }
1007
+ }
1008
+ handleError(err, req, res) {
1009
+ if (res.sent) return;
1010
+ if (this.errorHandler) {
1011
+ try {
1012
+ this.errorHandler(err, req, res, () => {
1013
+ });
1014
+ return;
1015
+ } catch (handlerErr) {
1016
+ this.logger.error("Error handler threw:", handlerErr);
1017
+ }
1018
+ }
1019
+ if (err instanceof HttpError) {
1020
+ res.status(err.statusCode).json(err.toJSON());
1021
+ return;
1022
+ }
1023
+ const statusCode = err?.statusCode ?? err?.status ?? 500;
1024
+ const message = this.config.environment === "production" ? "Internal Server Error" : err?.message ?? "Internal Server Error";
1025
+ this.logger.error(`Unhandled error: ${err?.message ?? err}`);
1026
+ if (this.config.debug && err?.stack) {
1027
+ this.logger.error(err.stack);
1028
+ }
1029
+ res.status(statusCode).json({
1030
+ error: {
1031
+ statusCode,
1032
+ message,
1033
+ ...this.config.debug ? { stack: err?.stack } : {}
1034
+ }
1035
+ });
1036
+ }
1037
+ async listen(port, callback) {
1038
+ const serverPort = port ?? this.config.server?.port ?? 3e3;
1039
+ const host = this.config.server?.host ?? "0.0.0.0";
1040
+ const handler = (req, res) => {
1041
+ this.handleRequest(req, res).catch((err) => {
1042
+ this.logger.error("Fatal request error:", err);
1043
+ if (!res.headersSent) {
1044
+ res.statusCode = 500;
1045
+ res.end('{"error":{"statusCode":500,"message":"Internal Server Error"}}');
1046
+ }
1047
+ });
1048
+ };
1049
+ if (this.config.server?.https) {
1050
+ const httpsConfig = this.config.server.https;
1051
+ this.server = createServer(
1052
+ {
1053
+ key: readFileSync(httpsConfig.key),
1054
+ cert: readFileSync(httpsConfig.cert),
1055
+ ...httpsConfig.ca ? { ca: readFileSync(httpsConfig.ca) } : {}
1056
+ },
1057
+ handler
1058
+ );
1059
+ } else {
1060
+ this.server = createServer$1(handler);
1061
+ }
1062
+ if (this.config.server?.keepAliveTimeout) {
1063
+ this.server.keepAliveTimeout = this.config.server.keepAliveTimeout;
1064
+ }
1065
+ if (this.config.server?.headersTimeout) {
1066
+ this.server.headersTimeout = this.config.server.headersTimeout;
1067
+ }
1068
+ return new Promise((resolve) => {
1069
+ this.server.listen(serverPort, host, () => {
1070
+ this.logger.banner(serverPort, host);
1071
+ callback?.();
1072
+ resolve(this.server);
1073
+ });
1074
+ });
1075
+ }
1076
+ async close() {
1077
+ return new Promise((resolve, reject) => {
1078
+ if (!this.server) {
1079
+ resolve();
1080
+ return;
1081
+ }
1082
+ this.server.close((err) => {
1083
+ if (err) reject(err);
1084
+ else resolve();
1085
+ });
1086
+ });
1087
+ }
1088
+ getRouter() {
1089
+ return this.router;
1090
+ }
1091
+ getLogger() {
1092
+ return this.logger;
1093
+ }
1094
+ getConfig() {
1095
+ return this.config;
1096
+ }
1097
+ getRoutes() {
1098
+ return this.router.getAllRoutes();
1099
+ }
1100
+ };
1101
+
1102
+ export { AzuraServer, Router };
4
1103
  //# sourceMappingURL=index.mjs.map
5
1104
  //# sourceMappingURL=index.mjs.map