blaizejs 0.3.2 → 0.3.4

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 (30) hide show
  1. package/dist/chunk-DTDGIBMA.js +11 -0
  2. package/dist/{chunk-IFP53BNM.js.map → chunk-DTDGIBMA.js.map} +1 -1
  3. package/dist/{unsupported-media-type-error-VVHRDTUH.js → chunk-EE2VJ6JY.js} +3 -9
  4. package/dist/{chunk-3VK325MM.js.map → chunk-EE2VJ6JY.js.map} +1 -1
  5. package/dist/chunk-HSLLYUVO.js +11 -0
  6. package/dist/{chunk-CQKM74J4.js.map → chunk-HSLLYUVO.js.map} +1 -1
  7. package/dist/chunk-TL4GIFTB.js +11 -0
  8. package/dist/{chunk-HB6MRTGD.js.map → chunk-TL4GIFTB.js.map} +1 -1
  9. package/dist/chunk-VLVWNGUO.js +11 -0
  10. package/dist/{chunk-7IM52S7P.js.map → chunk-VLVWNGUO.js.map} +1 -1
  11. package/dist/index.cjs +12 -3486
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +2 -2
  14. package/dist/index.d.ts +2 -2
  15. package/dist/index.js +12 -3153
  16. package/dist/index.js.map +1 -1
  17. package/dist/{validation-error-TXMSFWZL.js → internal-server-error-GWBNT3OO.js} +3 -9
  18. package/dist/{internal-server-error-PVME2DGN.js → payload-too-large-error-EBM5BNWG.js} +3 -9
  19. package/dist/{payload-too-large-error-QQG7MKGT.js → unsupported-media-type-error-YQ7GCZ32.js} +3 -9
  20. package/dist/validation-error-6JDCGV2S.js +11 -0
  21. package/package.json +5 -5
  22. package/dist/chunk-3VK325MM.js +0 -31
  23. package/dist/chunk-7IM52S7P.js +0 -31
  24. package/dist/chunk-CQKM74J4.js +0 -39
  25. package/dist/chunk-HB6MRTGD.js +0 -39
  26. package/dist/chunk-IFP53BNM.js +0 -149
  27. /package/dist/{internal-server-error-PVME2DGN.js.map → internal-server-error-GWBNT3OO.js.map} +0 -0
  28. /package/dist/{payload-too-large-error-QQG7MKGT.js.map → payload-too-large-error-EBM5BNWG.js.map} +0 -0
  29. /package/dist/{unsupported-media-type-error-VVHRDTUH.js.map → unsupported-media-type-error-YQ7GCZ32.js.map} +0 -0
  30. /package/dist/{validation-error-TXMSFWZL.js.map → validation-error-6JDCGV2S.js.map} +0 -0
package/dist/index.js CHANGED
@@ -1,3172 +1,31 @@
1
1
 
2
2
  /**
3
- * blaizejs v0.3.2
3
+ * blaizejs v0.3.4
4
4
  * A blazing-fast, TypeScript-first Node.js framework with HTTP/2 support, file-based routing, powerful middleware system, and end-to-end type safety for building modern APIs.
5
5
  *
6
6
  * Copyright (c) 2025 BlaizeJS Contributors
7
7
  * @license MIT
8
8
  */
9
9
 
10
- import {
11
- InternalServerError
12
- } from "./chunk-CQKM74J4.js";
13
- import {
14
- ValidationError
15
- } from "./chunk-HB6MRTGD.js";
16
- import {
17
- PayloadTooLargeError
18
- } from "./chunk-3VK325MM.js";
19
- import {
20
- UnsupportedMediaTypeError
21
- } from "./chunk-7IM52S7P.js";
22
- import {
23
- BlaizeError,
24
- ErrorSeverity,
25
- ErrorType,
26
- __require,
27
- generateCorrelationId,
28
- getCurrentCorrelationId,
29
- isBodyParseError
30
- } from "./chunk-IFP53BNM.js";
31
-
32
- // src/middleware/execute.ts
33
- function execute(middleware, ctx, next) {
34
- if (!middleware) {
35
- return Promise.resolve(next());
36
- }
37
- if (middleware.skip && middleware.skip(ctx)) {
38
- return Promise.resolve(next());
39
- }
40
- try {
41
- const result = middleware.execute(ctx, next);
42
- if (result instanceof Promise) {
43
- return result;
44
- } else {
45
- return Promise.resolve(result);
46
- }
47
- } catch (error) {
48
- return Promise.reject(error);
49
- }
50
- }
51
-
52
- // src/middleware/compose.ts
53
- function compose(middlewareStack) {
54
- if (middlewareStack.length === 0) {
55
- return async (_, next) => {
56
- await Promise.resolve(next());
57
- };
58
- }
59
- return async function(ctx, finalHandler) {
60
- const called = /* @__PURE__ */ new Set();
61
- const dispatch = async (i) => {
62
- if (i >= middlewareStack.length) {
63
- return Promise.resolve(finalHandler());
64
- }
65
- const middleware = middlewareStack[i];
66
- const nextDispatch = () => {
67
- if (called.has(i)) {
68
- throw new Error("next() called multiple times");
69
- }
70
- called.add(i);
71
- return dispatch(i + 1);
72
- };
73
- return execute(middleware, ctx, nextDispatch);
74
- };
75
- return dispatch(0);
76
- };
77
- }
78
-
79
- // src/middleware/create.ts
80
- function create(handlerOrOptions) {
81
- if (typeof handlerOrOptions === "function") {
82
- return {
83
- name: "anonymous",
84
- // Default name for function middleware
85
- execute: handlerOrOptions,
86
- debug: false
87
- };
88
- }
89
- const { name = "anonymous", handler, skip, debug = false } = handlerOrOptions;
90
- const middleware = {
91
- name,
92
- execute: handler,
93
- debug
94
- };
95
- if (skip !== void 0) {
96
- return {
97
- ...middleware,
98
- skip
99
- };
100
- }
101
- return middleware;
102
- }
103
-
104
- // src/plugins/create.ts
105
- function create2(name, version, setup, defaultOptions = {}) {
106
- if (!name || typeof name !== "string") {
107
- throw new Error("Plugin name must be a non-empty string");
108
- }
109
- if (!version || typeof version !== "string") {
110
- throw new Error("Plugin version must be a non-empty string");
111
- }
112
- if (typeof setup !== "function") {
113
- throw new Error("Plugin setup must be a function");
114
- }
115
- return function pluginFactory(userOptions) {
116
- const mergedOptions = { ...defaultOptions, ...userOptions };
117
- const plugin = {
118
- name,
119
- version,
120
- // The register hook calls the user's setup function
121
- register: async (app) => {
122
- const result = await setup(app, mergedOptions);
123
- if (result && typeof result === "object") {
124
- Object.assign(plugin, result);
125
- }
126
- }
127
- };
128
- return plugin;
129
- };
130
- }
131
-
132
- // src/router/create.ts
133
- import { fileURLToPath } from "node:url";
134
-
135
- // src/config.ts
136
- var config = {};
137
- function setRuntimeConfig(newConfig) {
138
- config = { ...config, ...newConfig };
139
- }
140
- function getRoutesDir() {
141
- if (!config.routesDir) {
142
- throw new Error("Routes directory not configured. Make sure server is properly initialized.");
143
- }
144
- return config.routesDir;
145
- }
146
-
147
- // src/router/discovery/parser.ts
148
- import * as path from "node:path";
149
- function parseRoutePath(filePath, basePath) {
150
- if (filePath.startsWith("file://")) {
151
- filePath = filePath.replace("file://", "");
152
- }
153
- if (basePath.startsWith("file://")) {
154
- basePath = basePath.replace("file://", "");
155
- }
156
- const forwardSlashFilePath = filePath.replace(/\\/g, "/");
157
- const forwardSlashBasePath = basePath.replace(/\\/g, "/");
158
- const normalizedBasePath = forwardSlashBasePath.endsWith("/") ? forwardSlashBasePath : `${forwardSlashBasePath}/`;
159
- let relativePath = forwardSlashFilePath;
160
- if (forwardSlashFilePath.startsWith(normalizedBasePath)) {
161
- relativePath = forwardSlashFilePath.substring(normalizedBasePath.length);
162
- } else if (forwardSlashFilePath.startsWith(forwardSlashBasePath)) {
163
- relativePath = forwardSlashFilePath.substring(forwardSlashBasePath.length);
164
- if (relativePath.startsWith("/")) {
165
- relativePath = relativePath.substring(1);
166
- }
167
- } else {
168
- relativePath = path.relative(forwardSlashBasePath, forwardSlashFilePath).replace(/\\/g, "/");
169
- }
170
- relativePath = relativePath.replace(/\.[^.]+$/, "");
171
- const segments = relativePath.split("/").filter(Boolean);
172
- const params = [];
173
- const routeSegments = segments.map((segment) => {
174
- if (segment.startsWith("[") && segment.endsWith("]")) {
175
- const paramName = segment.slice(1, -1);
176
- params.push(paramName);
177
- return `:${paramName}`;
178
- }
179
- return segment;
180
- });
181
- let routePath = routeSegments.length > 0 ? `/${routeSegments.join("/")}` : "/";
182
- if (routePath.endsWith("/index")) {
183
- routePath = routePath.slice(0, -6) || "/";
184
- }
185
- return {
186
- filePath,
187
- routePath,
188
- params
189
- };
190
- }
191
-
192
- // src/router/create.ts
193
- function getCallerFilePath() {
194
- const originalPrepareStackTrace = Error.prepareStackTrace;
195
- try {
196
- Error.prepareStackTrace = (_, stack2) => stack2;
197
- const stack = new Error().stack;
198
- const callerFrame = stack[3];
199
- if (!callerFrame || typeof callerFrame.getFileName !== "function") {
200
- throw new Error("Unable to determine caller file frame");
201
- }
202
- const fileName = callerFrame.getFileName();
203
- if (!fileName) {
204
- throw new Error("Unable to determine caller file name");
205
- }
206
- if (fileName.startsWith("file://")) {
207
- return fileURLToPath(fileName);
208
- }
209
- return fileName;
210
- } finally {
211
- Error.prepareStackTrace = originalPrepareStackTrace;
212
- }
213
- }
214
- function getRoutePath() {
215
- const callerPath = getCallerFilePath();
216
- const routesDir = getRoutesDir();
217
- const parsedRoute = parseRoutePath(callerPath, routesDir);
218
- console.log(`\u{1F50E} Parsed route path: ${parsedRoute.routePath} from file: ${callerPath}`);
219
- return parsedRoute.routePath;
220
- }
221
- var createGetRoute = (config2) => {
222
- validateMethodConfig("GET", config2);
223
- const path6 = getRoutePath();
224
- return {
225
- GET: config2,
226
- // Let TypeScript infer the proper types
227
- path: path6
228
- };
229
- };
230
- var createPostRoute = (config2) => {
231
- validateMethodConfig("POST", config2);
232
- const path6 = getRoutePath();
233
- return {
234
- POST: config2,
235
- // Let TypeScript infer the proper types
236
- path: path6
237
- };
238
- };
239
- var createPutRoute = (config2) => {
240
- validateMethodConfig("PUT", config2);
241
- const path6 = getRoutePath();
242
- return {
243
- PUT: config2,
244
- // Let TypeScript infer the proper types
245
- path: path6
246
- };
247
- };
248
- var createDeleteRoute = (config2) => {
249
- validateMethodConfig("DELETE", config2);
250
- const path6 = getRoutePath();
251
- return {
252
- DELETE: config2,
253
- // Let TypeScript infer the proper types
254
- path: path6
255
- };
256
- };
257
- var createPatchRoute = (config2) => {
258
- validateMethodConfig("PATCH", config2);
259
- const path6 = getRoutePath();
260
- return {
261
- PATCH: config2,
262
- // Let TypeScript infer the proper types
263
- path: path6
264
- };
265
- };
266
- var createHeadRoute = (config2) => {
267
- validateMethodConfig("HEAD", config2);
268
- const path6 = getRoutePath();
269
- return {
270
- HEAD: config2,
271
- // Let TypeScript infer the proper types
272
- path: path6
273
- };
274
- };
275
- var createOptionsRoute = (config2) => {
276
- validateMethodConfig("OPTIONS", config2);
277
- const path6 = getRoutePath();
278
- return {
279
- OPTIONS: config2,
280
- // Let TypeScript infer the proper types
281
- path: path6
282
- };
283
- };
284
- function validateMethodConfig(method, config2) {
285
- if (!config2.handler || typeof config2.handler !== "function") {
286
- throw new Error(`Handler for method ${method} must be a function`);
287
- }
288
- if (config2.middleware && !Array.isArray(config2.middleware)) {
289
- throw new Error(`Middleware for method ${method} must be an array`);
290
- }
291
- if (config2.schema) {
292
- validateSchema(method, config2.schema);
293
- }
294
- switch (method) {
295
- case "GET":
296
- case "HEAD":
297
- case "DELETE":
298
- if (config2.schema?.body) {
299
- console.warn(`Warning: ${method} requests typically don't have request bodies`);
300
- }
301
- break;
302
- }
303
- }
304
- function validateSchema(method, schema) {
305
- const { params, query, body, response } = schema;
306
- if (params && (!params._def || typeof params.parse !== "function")) {
307
- throw new Error(`Params schema for ${method} must be a valid Zod schema`);
308
- }
309
- if (query && (!query._def || typeof query.parse !== "function")) {
310
- throw new Error(`Query schema for ${method} must be a valid Zod schema`);
311
- }
312
- if (body && (!body._def || typeof body.parse !== "function")) {
313
- throw new Error(`Body schema for ${method} must be a valid Zod schema`);
314
- }
315
- if (response && (!response._def || typeof response.parse !== "function")) {
316
- throw new Error(`Response schema for ${method} must be a valid Zod schema`);
317
- }
318
- }
319
-
320
- // src/server/create.ts
321
- import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
322
- import EventEmitter from "node:events";
323
-
324
- // src/server/start.ts
325
- import * as fs2 from "node:fs";
326
- import * as http from "node:http";
327
- import * as http2 from "node:http2";
328
-
329
- // src/server/dev-certificate.ts
330
- import * as fs from "node:fs";
331
- import * as path2 from "node:path";
332
- import * as selfsigned from "selfsigned";
333
- async function generateDevCertificates() {
334
- const certDir = path2.join(process.cwd(), ".blaizejs", "certs");
335
- const keyPath = path2.join(certDir, "dev.key");
336
- const certPath = path2.join(certDir, "dev.cert");
337
- if (fs.existsSync(keyPath) && fs.existsSync(certPath)) {
338
- return {
339
- keyFile: keyPath,
340
- certFile: certPath
341
- };
342
- }
343
- if (!fs.existsSync(certDir)) {
344
- fs.mkdirSync(certDir, { recursive: true });
345
- }
346
- const attrs = [{ name: "commonName", value: "localhost" }];
347
- const options = {
348
- days: 365,
349
- algorithm: "sha256",
350
- keySize: 2048,
351
- extensions: [
352
- { name: "basicConstraints", cA: true },
353
- {
354
- name: "keyUsage",
355
- keyCertSign: true,
356
- digitalSignature: true,
357
- nonRepudiation: true,
358
- keyEncipherment: true,
359
- dataEncipherment: true
360
- },
361
- {
362
- name: "extKeyUsage",
363
- serverAuth: true,
364
- clientAuth: true
365
- },
366
- {
367
- name: "subjectAltName",
368
- altNames: [
369
- { type: 2, value: "localhost" },
370
- { type: 7, ip: "127.0.0.1" }
371
- ]
372
- }
373
- ]
374
- };
375
- const pems = selfsigned.generate(attrs, options);
376
- fs.writeFileSync(keyPath, Buffer.from(pems.private, "utf-8"));
377
- fs.writeFileSync(certPath, Buffer.from(pems.cert, "utf-8"));
378
- console.log(`
379
- \u{1F512} Generated self-signed certificates for development at ${certDir}
380
- `);
381
- return {
382
- keyFile: keyPath,
383
- certFile: certPath
384
- };
385
- }
386
-
387
- // src/context/errors.ts
388
- var ResponseSentError = class extends Error {
389
- constructor(message = "\u274C Response has already been sent") {
390
- super(message);
391
- this.name = "ResponseSentError";
392
- }
393
- };
394
- var ResponseSentHeaderError = class extends ResponseSentError {
395
- constructor(message = "Cannot set header after response has been sent") {
396
- super(message);
397
- }
398
- };
399
- var ResponseSentContentError = class extends ResponseSentError {
400
- constructor(message = "Cannot set content type after response has been sent") {
401
- super(message);
402
- }
403
- };
404
- var ParseUrlError = class extends ResponseSentError {
405
- constructor(message = "Invalide URL") {
406
- super(message);
407
- }
408
- };
409
-
410
- // src/context/store.ts
411
- import { AsyncLocalStorage } from "node:async_hooks";
412
- var contextStorage = new AsyncLocalStorage();
413
- function runWithContext(context, callback) {
414
- return contextStorage.run(context, callback);
415
- }
416
-
417
- // src/upload/multipart-parser.ts
418
- import { randomUUID } from "node:crypto";
419
- import { createWriteStream } from "node:fs";
420
- import { tmpdir } from "node:os";
421
- import { join as join2 } from "node:path";
422
- import { Readable } from "node:stream";
423
-
424
- // src/upload/utils.ts
425
- var BOUNDARY_REGEX = /boundary=([^;]+)/i;
426
- var CONTENT_DISPOSITION_REGEX = /Content-Disposition:\s*form-data;\s*name="([^"]+)"(?:;[\s\r\n]*filename="([^"]*)")?/i;
427
- var CONTENT_TYPE_REGEX = /Content-Type:\s*([^\r\n]+)/i;
428
- var MULTIPART_REGEX = /multipart\/form-data/i;
429
- function extractBoundary(contentType) {
430
- const match = contentType.match(BOUNDARY_REGEX);
431
- if (!match || !match[1]) return null;
432
- let boundary = match[1].trim();
433
- if (boundary.startsWith('"') && boundary.endsWith('"')) {
434
- boundary = boundary.slice(1, -1);
435
- }
436
- return boundary || null;
437
- }
438
- function parseContentDisposition(headers) {
439
- const match = headers.match(CONTENT_DISPOSITION_REGEX);
440
- if (!match || !match[1]) return null;
441
- return {
442
- name: match[1],
443
- filename: match[2] !== void 0 ? match[2] : void 0
444
- };
445
- }
446
- function parseContentType(headers) {
447
- const match = headers.match(CONTENT_TYPE_REGEX);
448
- return match && match[1]?.trim() ? match[1].trim() : "application/octet-stream";
449
- }
450
- function isMultipartContent(contentType) {
451
- return MULTIPART_REGEX.test(contentType);
452
- }
453
-
454
- // src/upload/multipart-parser.ts
455
- var DEFAULT_OPTIONS = {
456
- maxFileSize: 10 * 1024 * 1024,
457
- // 10MB
458
- maxFiles: 10,
459
- maxFieldSize: 1 * 1024 * 1024,
460
- // 1MB
461
- allowedMimeTypes: [],
462
- allowedExtensions: [],
463
- strategy: "stream",
464
- tempDir: tmpdir(),
465
- computeHash: false
466
- };
467
- function createParserState(boundary, options = {}) {
468
- return {
469
- boundary: Buffer.from(`--${boundary}`),
470
- options: { ...DEFAULT_OPTIONS, ...options },
471
- fields: /* @__PURE__ */ new Map(),
472
- files: /* @__PURE__ */ new Map(),
473
- buffer: Buffer.alloc(0),
474
- stage: "boundary",
475
- currentHeaders: "",
476
- currentField: null,
477
- currentFilename: void 0,
478
- currentMimetype: "application/octet-stream",
479
- currentContentLength: 0,
480
- fileCount: 0,
481
- fieldCount: 0,
482
- currentBufferChunks: [],
483
- currentStream: null,
484
- currentTempPath: null,
485
- currentWriteStream: null,
486
- streamController: null,
487
- cleanupTasks: [],
488
- // Track validation state
489
- hasFoundValidBoundary: false,
490
- hasProcessedAnyPart: false,
491
- isFinished: false
492
- };
493
- }
494
- async function processChunk(state, chunk) {
495
- const newBuffer = Buffer.concat([state.buffer, chunk]);
496
- let currentState = { ...state, buffer: newBuffer };
497
- while (currentState.buffer.length > 0 && !currentState.isFinished) {
498
- const nextState = await processCurrentStage(currentState);
499
- if (nextState === currentState) break;
500
- currentState = nextState;
501
- }
502
- return currentState;
503
- }
504
- async function processCurrentStage(state) {
505
- switch (state.stage) {
506
- case "boundary":
507
- return processBoundary(state);
508
- case "headers":
509
- return processHeaders(state);
510
- case "content":
511
- return processContent(state);
512
- default: {
513
- const { InternalServerError: InternalServerError2 } = await import("./internal-server-error-PVME2DGN.js");
514
- throw new InternalServerError2(`Invalid parser stage`, {
515
- operation: state.stage
516
- });
517
- }
518
- }
519
- }
520
- function processBoundary(state) {
521
- const boundaryIndex = state.buffer.indexOf(state.boundary);
522
- if (boundaryIndex === -1) return state;
523
- const hasFoundValidBoundary = true;
524
- let buffer = state.buffer.subarray(boundaryIndex + state.boundary.length);
525
- if (buffer.length >= 2 && buffer.subarray(0, 2).equals(Buffer.from("--"))) {
526
- return {
527
- ...state,
528
- buffer,
529
- hasFoundValidBoundary,
530
- isFinished: true,
531
- stage: "boundary"
532
- };
533
- }
534
- if (buffer.length >= 2 && buffer.subarray(0, 2).equals(Buffer.from("\r\n"))) {
535
- buffer = buffer.subarray(2);
536
- }
537
- return {
538
- ...state,
539
- buffer,
540
- hasFoundValidBoundary,
541
- stage: "headers",
542
- currentHeaders: ""
543
- };
544
- }
545
- async function processHeaders(state) {
546
- const headerEnd = state.buffer.indexOf("\r\n\r\n");
547
- if (headerEnd === -1) return state;
548
- const headers = state.buffer.subarray(0, headerEnd).toString("utf8");
549
- const buffer = state.buffer.subarray(headerEnd + 4);
550
- const disposition = parseContentDisposition(headers);
551
- if (!disposition) {
552
- const { ValidationError: ValidationError2 } = await import("./validation-error-TXMSFWZL.js");
553
- throw new ValidationError2("Missing or invalid Content-Disposition header");
554
- }
555
- const mimetype = parseContentType(headers);
556
- const isFile = disposition.filename !== void 0;
557
- if (isFile && state.fileCount >= state.options.maxFiles) {
558
- const { PayloadTooLargeError: PayloadTooLargeError2 } = await import("./payload-too-large-error-QQG7MKGT.js");
559
- throw new PayloadTooLargeError2("Too many files in upload", {
560
- fileCount: state.fileCount + 1,
561
- maxFiles: state.options.maxFiles,
562
- filename: disposition.filename
563
- });
564
- }
565
- if (isFile && state.options.allowedMimeTypes.length > 0 && !state.options.allowedMimeTypes.includes(mimetype)) {
566
- const { UnsupportedMediaTypeError: UnsupportedMediaTypeError2 } = await import("./unsupported-media-type-error-VVHRDTUH.js");
567
- throw new UnsupportedMediaTypeError2("File type not allowed", {
568
- receivedMimeType: mimetype,
569
- allowedMimeTypes: state.options.allowedMimeTypes,
570
- filename: disposition.filename
571
- });
572
- }
573
- return {
574
- ...state,
575
- buffer,
576
- stage: "content",
577
- currentHeaders: headers,
578
- currentField: disposition.name,
579
- currentFilename: disposition.filename,
580
- currentMimetype: mimetype,
581
- currentContentLength: 0,
582
- fileCount: isFile ? state.fileCount + 1 : state.fileCount,
583
- fieldCount: isFile ? state.fieldCount : state.fieldCount + 1,
584
- currentBufferChunks: []
585
- };
586
- }
587
- async function processContent(state) {
588
- const nextBoundaryIndex = state.buffer.indexOf(state.boundary);
589
- let contentChunk;
590
- let isComplete = false;
591
- let buffer = state.buffer;
592
- if (nextBoundaryIndex === -1) {
593
- const safeLength = Math.max(0, state.buffer.length - state.boundary.length);
594
- if (safeLength === 0) return state;
595
- contentChunk = state.buffer.subarray(0, safeLength);
596
- buffer = state.buffer.subarray(safeLength);
597
- } else {
598
- const contentEnd = Math.max(0, nextBoundaryIndex - 2);
599
- contentChunk = state.buffer.subarray(0, contentEnd);
600
- buffer = state.buffer.subarray(nextBoundaryIndex);
601
- isComplete = true;
602
- }
603
- let updatedState = { ...state, buffer };
604
- if (contentChunk.length > 0) {
605
- updatedState = await processContentChunk(updatedState, contentChunk);
606
- }
607
- if (isComplete) {
608
- updatedState = await finalizeCurrentPart(updatedState);
609
- updatedState = {
610
- ...updatedState,
611
- stage: "boundary",
612
- hasProcessedAnyPart: true
613
- // Mark that we've processed at least one part
614
- };
615
- }
616
- return updatedState;
617
- }
618
- async function processContentChunk(state, chunk) {
619
- const newContentLength = state.currentContentLength + chunk.length;
620
- const maxSize = state.currentFilename !== void 0 ? state.options.maxFileSize : state.options.maxFieldSize;
621
- if (newContentLength > maxSize) {
622
- const isFile = state.currentFilename !== void 0;
623
- const { PayloadTooLargeError: PayloadTooLargeError2 } = await import("./payload-too-large-error-QQG7MKGT.js");
624
- const payloadErrorDetals = state.currentField ? {
625
- contentType: isFile ? "file" : "field",
626
- currentSize: newContentLength,
627
- maxSize,
628
- field: state.currentField,
629
- filename: state.currentFilename
630
- } : {
631
- contentType: isFile ? "file" : "field",
632
- currentSize: newContentLength,
633
- maxSize,
634
- filename: state.currentFilename
635
- };
636
- throw new PayloadTooLargeError2(
637
- `${isFile ? "File" : "Field"} size exceeds limit`,
638
- payloadErrorDetals
639
- );
640
- }
641
- if (state.currentFilename !== void 0) {
642
- return processFileChunk(state, chunk, newContentLength);
643
- } else {
644
- return {
645
- ...state,
646
- currentContentLength: newContentLength,
647
- currentBufferChunks: [...state.currentBufferChunks, chunk]
648
- };
649
- }
650
- }
651
- async function processFileChunk(state, chunk, newContentLength) {
652
- switch (state.options.strategy) {
653
- case "memory":
654
- return {
655
- ...state,
656
- currentContentLength: newContentLength,
657
- currentBufferChunks: [...state.currentBufferChunks, chunk]
658
- };
659
- case "stream":
660
- if (state.streamController) {
661
- state.streamController.enqueue(chunk);
662
- }
663
- return { ...state, currentContentLength: newContentLength };
664
- case "temp":
665
- if (state.currentWriteStream) {
666
- await writeToStream(state.currentWriteStream, chunk);
667
- }
668
- return { ...state, currentContentLength: newContentLength };
669
- default: {
670
- const { ValidationError: ValidationError2 } = await import("./validation-error-TXMSFWZL.js");
671
- throw new ValidationError2(`Invalid parsing strategy`);
672
- }
673
- }
674
- }
675
- async function initializeFileProcessing(state) {
676
- if (state.currentFilename === void 0) return state;
677
- switch (state.options.strategy) {
678
- case "memory":
679
- return { ...state, currentBufferChunks: [] };
680
- case "stream": {
681
- let streamController = null;
682
- const stream = new ReadableStream({
683
- start: (controller) => {
684
- streamController = controller;
685
- }
686
- });
687
- return {
688
- ...state,
689
- currentStream: stream,
690
- // Type cast for Node.js compatibility
691
- streamController
692
- };
693
- }
694
- case "temp": {
695
- const tempPath = join2(state.options.tempDir, `upload-${randomUUID()}`);
696
- const writeStream = createWriteStream(tempPath);
697
- const cleanupTask = async () => {
698
- try {
699
- const { unlink } = await import("node:fs/promises");
700
- await unlink(tempPath);
701
- } catch (error) {
702
- console.warn(`Failed to cleanup temp file: ${tempPath}`, error);
703
- }
704
- };
705
- return {
706
- ...state,
707
- currentTempPath: tempPath,
708
- currentWriteStream: writeStream,
709
- cleanupTasks: [...state.cleanupTasks, cleanupTask]
710
- };
711
- }
712
- default: {
713
- const { ValidationError: ValidationError2 } = await import("./validation-error-TXMSFWZL.js");
714
- throw new ValidationError2(`Invalid file processing strategy`);
715
- }
716
- }
717
- }
718
- async function finalizeCurrentPart(state) {
719
- if (!state.currentField) return resetCurrentPart(state);
720
- if (state.currentFilename !== void 0) {
721
- return finalizeFile(state);
722
- } else {
723
- return finalizeField(state);
724
- }
725
- }
726
- async function finalizeFile(state) {
727
- if (!state.currentField || state.currentFilename === void 0) {
728
- return resetCurrentPart(state);
729
- }
730
- let stream;
731
- let buffer;
732
- let tempPath;
733
- switch (state.options.strategy) {
734
- case "memory":
735
- buffer = Buffer.concat(state.currentBufferChunks);
736
- stream = Readable.from(buffer);
737
- break;
738
- case "stream":
739
- if (state.streamController) {
740
- state.streamController.close();
741
- }
742
- stream = state.currentStream;
743
- break;
744
- case "temp":
745
- if (state.currentWriteStream) {
746
- await closeStream(state.currentWriteStream);
747
- }
748
- tempPath = state.currentTempPath;
749
- stream = Readable.from(Buffer.alloc(0));
750
- break;
751
- default: {
752
- const { ValidationError: ValidationError2 } = await import("./validation-error-TXMSFWZL.js");
753
- throw new ValidationError2(`Invalid file finalization strategy`);
754
- }
755
- }
756
- const file = {
757
- filename: state.currentFilename,
758
- fieldname: state.currentField,
759
- mimetype: state.currentMimetype,
760
- size: state.currentContentLength,
761
- stream,
762
- buffer,
763
- tempPath
764
- };
765
- const updatedFiles = addToCollection(state.files, state.currentField, file);
766
- return {
767
- ...resetCurrentPart(state),
768
- files: updatedFiles
769
- };
770
- }
771
- function finalizeField(state) {
772
- if (!state.currentField) return resetCurrentPart(state);
773
- const value = Buffer.concat(state.currentBufferChunks).toString("utf8");
774
- const updatedFields = addToCollection(state.fields, state.currentField, value);
775
- return {
776
- ...resetCurrentPart(state),
777
- fields: updatedFields
778
- };
779
- }
780
- function resetCurrentPart(state) {
781
- return {
782
- ...state,
783
- currentField: null,
784
- currentFilename: void 0,
785
- currentContentLength: 0,
786
- currentBufferChunks: [],
787
- currentStream: null,
788
- streamController: null,
789
- currentTempPath: null,
790
- currentWriteStream: null
791
- };
792
- }
793
- function addToCollection(collection, key, value) {
794
- const newCollection = new Map(collection);
795
- const existing = newCollection.get(key) || [];
796
- newCollection.set(key, [...existing, value]);
797
- return newCollection;
798
- }
799
- async function finalize(state) {
800
- if (!state.hasFoundValidBoundary) {
801
- const { ValidationError: ValidationError2 } = await import("./validation-error-TXMSFWZL.js");
802
- throw new ValidationError2("No valid multipart boundary found");
803
- }
804
- if (state.hasFoundValidBoundary && !state.hasProcessedAnyPart) {
805
- const { ValidationError: ValidationError2 } = await import("./validation-error-TXMSFWZL.js");
806
- throw new ValidationError2("Empty multipart request");
807
- }
808
- const fields = {};
809
- for (const [key, values] of state.fields.entries()) {
810
- fields[key] = values.length === 1 ? values[0] : values;
811
- }
812
- const files = {};
813
- for (const [key, fileList] of state.files.entries()) {
814
- files[key] = fileList.length === 1 ? fileList[0] : fileList;
815
- }
816
- return { fields, files };
817
- }
818
- async function cleanup(state) {
819
- await Promise.allSettled(state.cleanupTasks.map((task) => task()));
820
- if (state.streamController) {
821
- state.streamController.close();
822
- }
823
- if (state.currentWriteStream) {
824
- await closeStream(state.currentWriteStream);
825
- }
826
- }
827
- async function writeToStream(stream, chunk) {
828
- return new Promise((resolve3, reject) => {
829
- stream.write(chunk, (error) => {
830
- if (error) reject(error);
831
- else resolve3();
832
- });
833
- });
834
- }
835
- async function closeStream(stream) {
836
- return new Promise((resolve3) => {
837
- stream.end(() => resolve3());
838
- });
839
- }
840
- async function parseMultipartRequest(request, options = {}) {
841
- const contentType = request.headers["content-type"] || "";
842
- const boundary = extractBoundary(contentType);
843
- if (!boundary) {
844
- const { UnsupportedMediaTypeError: UnsupportedMediaTypeError2 } = await import("./unsupported-media-type-error-VVHRDTUH.js");
845
- throw new UnsupportedMediaTypeError2("Missing boundary in multipart content-type", {
846
- receivedContentType: contentType,
847
- expectedFormat: "multipart/form-data; boundary=..."
848
- });
849
- }
850
- let state = createParserState(boundary, options);
851
- if (state.currentFilename !== void 0) {
852
- state = await initializeFileProcessing(state);
853
- }
854
- try {
855
- for await (const chunk of request) {
856
- state = await processChunk(state, chunk);
857
- }
858
- return finalize(state);
859
- } finally {
860
- await cleanup(state);
861
- }
862
- }
863
-
864
- // src/context/create.ts
865
- var CONTENT_TYPE_HEADER = "Content-Type";
866
- var DEFAULT_BODY_LIMITS = {
867
- json: 512 * 1024,
868
- // 512KB - Most APIs should be much smaller
869
- form: 1024 * 1024,
870
- // 1MB - Reasonable for form submissions
871
- text: 5 * 1024 * 1024,
872
- // 5MB - Documents, logs, code files
873
- multipart: {
874
- maxFileSize: 50 * 1024 * 1024,
875
- // 50MB per file
876
- maxTotalSize: 100 * 1024 * 1024,
877
- // 100MB total request
878
- maxFiles: 10,
879
- maxFieldSize: 1024 * 1024
880
- // 1MB for form fields
881
- },
882
- raw: 10 * 1024 * 1024
883
- // 10MB for unknown content types
884
- };
885
- function parseRequestUrl(req) {
886
- const originalUrl = req.url || "/";
887
- const host = req.headers.host || "localhost";
888
- const protocol = req.socket && req.socket.encrypted ? "https" : "http";
889
- const fullUrl = `${protocol}://${host}${originalUrl.startsWith("/") ? "" : "/"}${originalUrl}`;
890
- try {
891
- const url = new URL(fullUrl);
892
- const path6 = url.pathname;
893
- const query = {};
894
- url.searchParams.forEach((value, key) => {
895
- if (query[key] !== void 0) {
896
- if (Array.isArray(query[key])) {
897
- query[key].push(value);
898
- } else {
899
- query[key] = [query[key], value];
900
- }
901
- } else {
902
- query[key] = value;
903
- }
904
- });
905
- return { path: path6, url, query };
906
- } catch (error) {
907
- console.warn(`Invalid URL: ${fullUrl}`, error);
908
- throw new ParseUrlError(`Invalid URL: ${fullUrl}`);
909
- }
910
- }
911
- function isHttp2Request(req) {
912
- return "stream" in req || "httpVersionMajor" in req && req.httpVersionMajor === 2;
913
- }
914
- function getProtocol(req) {
915
- const encrypted = req.socket && req.socket.encrypted;
916
- const forwardedProto = req.headers["x-forwarded-proto"];
917
- if (forwardedProto) {
918
- if (Array.isArray(forwardedProto)) {
919
- return forwardedProto[0]?.split(",")[0]?.trim() || "http";
920
- } else {
921
- return forwardedProto.split(",")[0]?.trim() || "http";
922
- }
923
- }
924
- return encrypted ? "https" : "http";
925
- }
926
- async function createContext(req, res, options = {}) {
927
- const { path: path6, url, query } = parseRequestUrl(req);
928
- const method = req.method || "GET";
929
- const isHttp2 = isHttp2Request(req);
930
- const protocol = getProtocol(req);
931
- const params = {};
932
- const state = { ...options.initialState || {} };
933
- const responseState = { sent: false };
934
- const ctx = {
935
- request: createRequestObject(req, {
936
- path: path6,
937
- url,
938
- query,
939
- params,
940
- method,
941
- isHttp2,
942
- protocol
943
- }),
944
- response: {},
945
- state
946
- };
947
- ctx.response = createResponseObject(res, responseState, ctx);
948
- if (options.parseBody) {
949
- await parseBodyIfNeeded(req, ctx, options);
950
- }
951
- return ctx;
952
- }
953
- function createRequestObject(req, info) {
954
- return {
955
- raw: req,
956
- ...info,
957
- header: createRequestHeaderGetter(req),
958
- headers: createRequestHeadersGetter(req),
959
- body: void 0
960
- };
961
- }
962
- function createRequestHeaderGetter(req) {
963
- return (name) => {
964
- const value = req.headers[name.toLowerCase()];
965
- if (Array.isArray(value)) {
966
- return value.join(", ");
967
- }
968
- return value || void 0;
969
- };
970
- }
971
- function createRequestHeadersGetter(req) {
972
- const headerGetter = createRequestHeaderGetter(req);
973
- return (names) => {
974
- if (names && Array.isArray(names) && names.length > 0) {
975
- return names.reduce((acc, name) => {
976
- acc[name] = headerGetter(name);
977
- return acc;
978
- }, {});
979
- } else {
980
- return Object.entries(req.headers).reduce(
981
- (acc, [key, value]) => {
982
- acc[key] = Array.isArray(value) ? value.join(", ") : value || void 0;
983
- return acc;
984
- },
985
- {}
986
- );
987
- }
988
- };
989
- }
990
- function createResponseObject(res, responseState, ctx) {
991
- return {
992
- raw: res,
993
- get sent() {
994
- return responseState.sent;
995
- },
996
- status: createStatusSetter(res, responseState, ctx),
997
- header: createHeaderSetter(res, responseState, ctx),
998
- headers: createHeadersSetter(res, responseState, ctx),
999
- type: createContentTypeSetter(res, responseState, ctx),
1000
- json: createJsonResponder(res, responseState),
1001
- text: createTextResponder(res, responseState),
1002
- html: createHtmlResponder(res, responseState),
1003
- redirect: createRedirectResponder(res, responseState),
1004
- stream: createStreamResponder(res, responseState)
1005
- };
1006
- }
1007
- function createStatusSetter(res, responseState, ctx) {
1008
- return function statusSetter(code) {
1009
- if (responseState.sent) {
1010
- throw new ResponseSentError();
1011
- }
1012
- res.statusCode = code;
1013
- return ctx.response;
1014
- };
1015
- }
1016
- function createHeaderSetter(res, responseState, ctx) {
1017
- return function headerSetter(name, value) {
1018
- if (responseState.sent) {
1019
- throw new ResponseSentHeaderError();
1020
- }
1021
- res.setHeader(name, value);
1022
- return ctx.response;
1023
- };
1024
- }
1025
- function createHeadersSetter(res, responseState, ctx) {
1026
- return function headersSetter(headers) {
1027
- if (responseState.sent) {
1028
- throw new ResponseSentHeaderError();
1029
- }
1030
- for (const [name, value] of Object.entries(headers)) {
1031
- res.setHeader(name, value);
1032
- }
1033
- return ctx.response;
1034
- };
1035
- }
1036
- function createContentTypeSetter(res, responseState, ctx) {
1037
- return function typeSetter(type) {
1038
- if (responseState.sent) {
1039
- throw new ResponseSentContentError();
1040
- }
1041
- res.setHeader(CONTENT_TYPE_HEADER, type);
1042
- return ctx.response;
1043
- };
1044
- }
1045
- function createJsonResponder(res, responseState) {
1046
- return function jsonResponder(body, status) {
1047
- if (responseState.sent) {
1048
- throw new ResponseSentError();
1049
- }
1050
- if (status !== void 0) {
1051
- res.statusCode = status;
1052
- }
1053
- res.setHeader(CONTENT_TYPE_HEADER, "application/json");
1054
- res.end(JSON.stringify(body));
1055
- responseState.sent = true;
1056
- };
1057
- }
1058
- function createTextResponder(res, responseState) {
1059
- return function textResponder(body, status) {
1060
- if (responseState.sent) {
1061
- throw new ResponseSentError();
1062
- }
1063
- if (status !== void 0) {
1064
- res.statusCode = status;
1065
- }
1066
- res.setHeader(CONTENT_TYPE_HEADER, "text/plain");
1067
- res.end(body);
1068
- responseState.sent = true;
1069
- };
1070
- }
1071
- function createHtmlResponder(res, responseState) {
1072
- return function htmlResponder(body, status) {
1073
- if (responseState.sent) {
1074
- throw new ResponseSentError();
1075
- }
1076
- if (status !== void 0) {
1077
- res.statusCode = status;
1078
- }
1079
- res.setHeader(CONTENT_TYPE_HEADER, "text/html");
1080
- res.end(body);
1081
- responseState.sent = true;
1082
- };
1083
- }
1084
- function createRedirectResponder(res, responseState) {
1085
- return function redirectResponder(url, status = 302) {
1086
- if (responseState.sent) {
1087
- throw new ResponseSentError();
1088
- }
1089
- res.statusCode = status;
1090
- res.setHeader("Location", url);
1091
- res.end();
1092
- responseState.sent = true;
1093
- };
1094
- }
1095
- function createStreamResponder(res, responseState) {
1096
- return function streamResponder(readable, options = {}) {
1097
- if (responseState.sent) {
1098
- throw new ResponseSentError();
1099
- }
1100
- if (options.status !== void 0) {
1101
- res.statusCode = options.status;
1102
- }
1103
- if (options.contentType) {
1104
- res.setHeader(CONTENT_TYPE_HEADER, options.contentType);
1105
- }
1106
- if (options.headers) {
1107
- for (const [name, value] of Object.entries(options.headers)) {
1108
- res.setHeader(name, value);
1109
- }
1110
- }
1111
- readable.pipe(res);
1112
- readable.on("end", () => {
1113
- responseState.sent = true;
1114
- });
1115
- readable.on("error", (err) => {
1116
- console.error("Stream error:", err);
1117
- if (!responseState.sent) {
1118
- res.statusCode = 500;
1119
- res.end("Stream error");
1120
- responseState.sent = true;
1121
- }
1122
- });
1123
- };
1124
- }
1125
- async function parseBodyIfNeeded(req, ctx, options = {}) {
1126
- if (shouldSkipParsing(req.method)) {
1127
- return;
1128
- }
1129
- const contentType = req.headers["content-type"] || "";
1130
- const contentLength = parseInt(req.headers["content-length"] || "0", 10);
1131
- if (contentLength === 0) {
1132
- return;
1133
- }
1134
- const limits = {
1135
- json: options.bodyLimits?.json ?? DEFAULT_BODY_LIMITS.json,
1136
- form: options.bodyLimits?.form ?? DEFAULT_BODY_LIMITS.form,
1137
- text: options.bodyLimits?.text ?? DEFAULT_BODY_LIMITS.text,
1138
- raw: options.bodyLimits?.raw ?? DEFAULT_BODY_LIMITS.raw,
1139
- multipart: {
1140
- maxFileSize: options.bodyLimits?.multipart?.maxFileSize ?? DEFAULT_BODY_LIMITS.multipart.maxFileSize,
1141
- maxFiles: options.bodyLimits?.multipart?.maxFiles ?? DEFAULT_BODY_LIMITS.multipart.maxFiles,
1142
- maxFieldSize: options.bodyLimits?.multipart?.maxFieldSize ?? DEFAULT_BODY_LIMITS.multipart.maxFieldSize,
1143
- maxTotalSize: options.bodyLimits?.multipart?.maxTotalSize ?? DEFAULT_BODY_LIMITS.multipart.maxTotalSize
1144
- }
1145
- };
1146
- try {
1147
- if (contentType.includes("application/json")) {
1148
- if (contentLength > limits.json) {
1149
- throw new Error(`JSON body too large: ${contentLength} > ${limits.json} bytes`);
1150
- }
1151
- await parseJsonBody(req, ctx);
1152
- } else if (contentType.includes("application/x-www-form-urlencoded")) {
1153
- if (contentLength > limits.form) {
1154
- throw new Error(`Form body too large: ${contentLength} > ${limits.form} bytes`);
1155
- }
1156
- await parseFormUrlEncodedBody(req, ctx);
1157
- } else if (contentType.includes("text/")) {
1158
- if (contentLength > limits.text) {
1159
- throw new Error(`Text body too large: ${contentLength} > ${limits.text} bytes`);
1160
- }
1161
- await parseTextBody(req, ctx);
1162
- } else if (isMultipartContent(contentType)) {
1163
- await parseMultipartBody(req, ctx, limits.multipart);
1164
- } else {
1165
- if (contentLength > limits.raw) {
1166
- throw new Error(`Request body too large: ${contentLength} > ${limits.raw} bytes`);
1167
- }
1168
- return;
1169
- }
1170
- } catch (error) {
1171
- const errorType = contentType.includes("multipart") ? "multipart_parse_error" : "body_read_error";
1172
- setBodyError(ctx, errorType, "Error reading request body", error);
1173
- }
1174
- }
1175
- function shouldSkipParsing(method) {
1176
- const skipMethods = ["GET", "HEAD", "OPTIONS"];
1177
- return skipMethods.includes(method || "GET");
1178
- }
1179
- async function parseJsonBody(req, ctx) {
1180
- const body = await readRequestBody(req);
1181
- if (!body) {
1182
- console.warn("Empty body, skipping JSON parsing");
1183
- return;
1184
- }
1185
- if (body.trim() === "null") {
1186
- console.warn('Body is the string "null"');
1187
- ctx.request.body = null;
1188
- return;
1189
- }
1190
- try {
1191
- const json = JSON.parse(body);
1192
- ctx.request.body = json;
1193
- } catch (error) {
1194
- ctx.request.body = null;
1195
- setBodyError(ctx, "json_parse_error", "Invalid JSON in request body", error);
1196
- }
1197
- }
1198
- async function parseFormUrlEncodedBody(req, ctx) {
1199
- const body = await readRequestBody(req);
1200
- if (!body) return;
1201
- try {
1202
- ctx.request.body = parseUrlEncodedData(body);
1203
- } catch (error) {
1204
- ctx.request.body = null;
1205
- setBodyError(ctx, "form_parse_error", "Invalid form data in request body", error);
1206
- }
1207
- }
1208
- function parseUrlEncodedData(body) {
1209
- const params = new URLSearchParams(body);
1210
- const formData = {};
1211
- params.forEach((value, key) => {
1212
- if (formData[key] !== void 0) {
1213
- if (Array.isArray(formData[key])) {
1214
- formData[key].push(value);
1215
- } else {
1216
- formData[key] = [formData[key], value];
1217
- }
1218
- } else {
1219
- formData[key] = value;
1220
- }
1221
- });
1222
- return formData;
1223
- }
1224
- async function parseTextBody(req, ctx) {
1225
- const body = await readRequestBody(req);
1226
- if (body) {
1227
- ctx.request.body = body;
1228
- }
1229
- }
1230
- async function parseMultipartBody(req, ctx, multipartLimits) {
1231
- try {
1232
- const limits = multipartLimits || DEFAULT_BODY_LIMITS.multipart;
1233
- const multipartData = await parseMultipartRequest(req, {
1234
- strategy: "stream",
1235
- maxFileSize: limits.maxFileSize,
1236
- maxFiles: limits.maxFiles,
1237
- maxFieldSize: limits.maxFieldSize
1238
- // Could add total size validation here
1239
- });
1240
- ctx.request.multipart = multipartData;
1241
- ctx.request.files = multipartData.files;
1242
- ctx.request.body = multipartData.fields;
1243
- } catch (error) {
1244
- ctx.request.body = null;
1245
- setBodyError(ctx, "multipart_parse_error", "Failed to parse multipart data", error);
1246
- }
1247
- }
1248
- function setBodyError(ctx, type, message, error) {
1249
- const bodyError = { type, message, error };
1250
- ctx.state._bodyError = bodyError;
1251
- }
1252
- async function readRequestBody(req) {
1253
- return new Promise((resolve3, reject) => {
1254
- const chunks = [];
1255
- req.on("data", (chunk) => {
1256
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
1257
- });
1258
- req.on("end", () => {
1259
- resolve3(Buffer.concat(chunks).toString("utf8"));
1260
- });
1261
- req.on("error", (err) => {
1262
- reject(err);
1263
- });
1264
- });
1265
- }
1266
-
1267
- // src/errors/not-found-error.ts
1268
- var NotFoundError = class extends BlaizeError {
1269
- /**
1270
- * Creates a new NotFoundError instance
1271
- *
1272
- * @param title - Human-readable error message
1273
- * @param details - Optional context about the missing resource
1274
- * @param correlationId - Optional correlation ID (uses current context if not provided)
1275
- */
1276
- constructor(title, details = void 0, correlationId = void 0) {
1277
- super(
1278
- "NOT_FOUND" /* NOT_FOUND */,
1279
- title,
1280
- 404,
1281
- // HTTP 404 Not Found
1282
- correlationId ?? getCurrentCorrelationId(),
1283
- details
1284
- );
1285
- }
1286
- };
1287
-
1288
- // src/errors/boundary.ts
1289
- function isHandledError(error) {
1290
- return error instanceof BlaizeError;
1291
- }
1292
- function formatErrorResponse(error) {
1293
- if (isHandledError(error)) {
1294
- return {
1295
- type: error.type,
1296
- title: error.title,
1297
- status: error.status,
1298
- correlationId: error.correlationId,
1299
- timestamp: error.timestamp.toISOString(),
1300
- details: error.details
1301
- };
1302
- }
1303
- const correlationId = generateCorrelationId();
1304
- let originalMessage;
1305
- if (error instanceof Error) {
1306
- originalMessage = error.message;
1307
- } else if (error === null || error === void 0) {
1308
- originalMessage = "Unknown error occurred";
1309
- } else {
1310
- originalMessage = String(error);
1311
- }
1312
- const wrappedError = new InternalServerError(
1313
- "Internal Server Error",
1314
- { originalMessage },
1315
- correlationId
1316
- );
1317
- return {
1318
- type: wrappedError.type,
1319
- title: wrappedError.title,
1320
- status: wrappedError.status,
1321
- correlationId: wrappedError.correlationId,
1322
- timestamp: wrappedError.timestamp.toISOString(),
1323
- details: wrappedError.details
1324
- };
1325
- }
1326
- function extractOrGenerateCorrelationId(headerGetter) {
1327
- return headerGetter("x-correlation-id") ?? generateCorrelationId();
1328
- }
1329
- function setErrorResponseHeaders(headerSetter, correlationId) {
1330
- headerSetter("x-correlation-id", correlationId);
1331
- }
1332
-
1333
- // src/middleware/error-boundary.ts
1334
- function createErrorBoundary(options = {}) {
1335
- const { debug = false } = options;
1336
- const middlewareFn = async (ctx, next) => {
1337
- try {
1338
- await next();
1339
- } catch (error) {
1340
- if (ctx.response.sent) {
1341
- if (debug) {
1342
- console.error("Error occurred after response was sent:", error);
1343
- }
1344
- return;
1345
- }
1346
- if (debug) {
1347
- console.error("Error boundary caught error:", error);
1348
- }
1349
- const correlationId = extractOrGenerateCorrelationId(ctx.request.header);
1350
- const errorResponse = formatErrorResponse(error);
1351
- errorResponse.correlationId = correlationId;
1352
- setErrorResponseHeaders(ctx.response.header, correlationId);
1353
- ctx.response.status(errorResponse.status).json(errorResponse);
1354
- }
1355
- };
1356
- return {
1357
- name: "ErrorBoundary",
1358
- execute: middlewareFn,
1359
- debug
1360
- };
1361
- }
1362
-
1363
- // src/server/request-handler.ts
1364
- function createRequestHandler(serverInstance) {
1365
- return async (req, res) => {
1366
- try {
1367
- const context = await createContext(req, res, {
1368
- parseBody: true
1369
- // Enable automatic body parsing
1370
- });
1371
- const errorBoundary = createErrorBoundary();
1372
- const allMiddleware = [errorBoundary, ...serverInstance.middleware];
1373
- const handler = compose(allMiddleware);
1374
- await runWithContext(context, async () => {
1375
- await handler(context, async () => {
1376
- if (!context.response.sent) {
1377
- await serverInstance.router.handleRequest(context);
1378
- if (!context.response.sent) {
1379
- throw new NotFoundError(
1380
- `Route not found: ${context.request.method} ${context.request.path}`
1381
- );
1382
- }
1383
- }
1384
- });
1385
- });
1386
- } catch (error) {
1387
- console.error("Error creating context:", error);
1388
- res.writeHead(500, { "Content-Type": "application/json" });
1389
- res.end(
1390
- JSON.stringify({
1391
- error: "Internal Server Error",
1392
- message: "Failed to process request"
1393
- })
1394
- );
1395
- }
1396
- };
1397
- }
1398
-
1399
- // src/server/start.ts
1400
- async function prepareCertificates(http2Options) {
1401
- if (!http2Options.enabled) {
1402
- return {};
1403
- }
1404
- const { keyFile, certFile } = http2Options;
1405
- const isDevMode = process.env.NODE_ENV === "development";
1406
- const certificatesMissing = !keyFile || !certFile;
1407
- if (certificatesMissing && isDevMode) {
1408
- const devCerts = await generateDevCertificates();
1409
- return devCerts;
1410
- }
1411
- if (certificatesMissing) {
1412
- throw new Error(
1413
- "HTTP/2 requires SSL certificates. Provide keyFile and certFile in http2 options. In development, set NODE_ENV=development to generate them automatically."
1414
- );
1415
- }
1416
- return { keyFile, certFile };
1417
- }
1418
- function createServerInstance(isHttp2, certOptions) {
1419
- if (!isHttp2) {
1420
- return http.createServer();
1421
- }
1422
- const http2ServerOptions = {
1423
- allowHTTP1: true
1424
- // Allow fallback to HTTP/1.1
1425
- };
1426
- try {
1427
- if (certOptions.keyFile) {
1428
- http2ServerOptions.key = fs2.readFileSync(certOptions.keyFile);
1429
- }
1430
- if (certOptions.certFile) {
1431
- http2ServerOptions.cert = fs2.readFileSync(certOptions.certFile);
1432
- }
1433
- } catch (err) {
1434
- throw new Error(
1435
- `Failed to read certificate files: ${err instanceof Error ? err.message : String(err)}`
1436
- );
1437
- }
1438
- return http2.createSecureServer(http2ServerOptions);
1439
- }
1440
- function listenOnPort(server, port, host, isHttp2) {
1441
- return new Promise((resolve3, reject) => {
1442
- server.listen(port, host, () => {
1443
- const protocol = isHttp2 ? "https" : "http";
1444
- const url = `${protocol}://${host}:${port}`;
1445
- console.log(`
10
+ import{a as H}from"./chunk-HSLLYUVO.js";import{a as U}from"./chunk-TL4GIFTB.js";import{a as Et}from"./chunk-EE2VJ6JY.js";import{a as Ct}from"./chunk-VLVWNGUO.js";import{a as A,b as B,c as mo,d as S,e as go,f as oe,g as x}from"./chunk-DTDGIBMA.js";function Me(e,t,r){if(!e||e.skip&&e.skip(t))return Promise.resolve(r());try{let o=e.execute(t,r);return o instanceof Promise?o:Promise.resolve(o)}catch(o){return Promise.reject(o)}}function D(e){return e.length===0?async(t,r)=>{await Promise.resolve(r())}:async function(t,r){let o=new Set,n=async i=>{if(i>=e.length)return Promise.resolve(r());let s=e[i];return Me(s,t,()=>{if(o.has(i))throw new Error("next() called multiple times");return o.add(i),n(i+1)})};return n(0)}}function ne(e){if(typeof e=="function")return{name:"anonymous",execute:e,debug:!1};let{name:t="anonymous",handler:r,skip:o,debug:n=!1}=e,i={name:t,execute:r,debug:n};return o!==void 0?{...i,skip:o}:i}function ie(e,t,r,o={}){if(!e||typeof e!="string")throw new Error("Plugin name must be a non-empty string");if(!t||typeof t!="string")throw new Error("Plugin version must be a non-empty string");if(typeof r!="function")throw new Error("Plugin setup must be a function");return function(i){let s={...o,...i},a={name:e,version:t,register:async u=>{let c=await r(u,s);c&&typeof c=="object"&&Object.assign(a,c)}};return a}}import{fileURLToPath as Ft}from"node:url";var V={};function ze(e){V={...V,...e}}function Be(){if(!V.routesDir)throw new Error("Routes directory not configured. Make sure server is properly initialized.");return V.routesDir}import*as $e from"node:path";function W(e,t){e.startsWith("file://")&&(e=e.replace("file://","")),t.startsWith("file://")&&(t=t.replace("file://",""));let r=e.replace(/\\/g,"/"),o=t.replace(/\\/g,"/"),n=o.endsWith("/")?o:`${o}/`,i=r;r.startsWith(n)?i=r.substring(n.length):r.startsWith(o)?(i=r.substring(o.length),i.startsWith("/")&&(i=i.substring(1))):i=$e.relative(o,r).replace(/\\/g,"/"),i=i.replace(/\.[^.]+$/,"");let s=i.split("/").filter(Boolean),a=[],u=s.map(w=>{if(w.startsWith("[")&&w.endsWith("]")){let d=w.slice(1,-1);return a.push(d),`:${d}`}return w}),c=u.length>0?`/${u.join("/")}`:"/";return c.endsWith("/index")&&(c=c.slice(0,-6)||"/"),{filePath:e,routePath:c,params:a}}function Mt(){let e=Error.prepareStackTrace;try{Error.prepareStackTrace=(n,i)=>i;let r=new Error().stack[3];if(!r||typeof r.getFileName!="function")throw new Error("Unable to determine caller file frame");let o=r.getFileName();if(!o)throw new Error("Unable to determine caller file name");return o.startsWith("file://")?Ft(o):o}finally{Error.prepareStackTrace=e}}function $(){let e=Mt(),t=Be(),r=W(e,t);return console.log(`\u{1F50E} Parsed route path: ${r.routePath} from file: ${e}`),r.routePath}var Oe=e=>{O("GET",e);let t=$();return{GET:e,path:t}},ke=e=>{O("POST",e);let t=$();return{POST:e,path:t}},De=e=>{O("PUT",e);let t=$();return{PUT:e,path:t}},Ne=e=>{O("DELETE",e);let t=$();return{DELETE:e,path:t}},Ae=e=>{O("PATCH",e);let t=$();return{PATCH:e,path:t}},He=e=>{O("HEAD",e);let t=$();return{HEAD:e,path:t}},Ue=e=>{O("OPTIONS",e);let t=$();return{OPTIONS:e,path:t}};function O(e,t){if(!t.handler||typeof t.handler!="function")throw new Error(`Handler for method ${e} must be a function`);if(t.middleware&&!Array.isArray(t.middleware))throw new Error(`Middleware for method ${e} must be an array`);switch(t.schema&&zt(e,t.schema),e){case"GET":case"HEAD":case"DELETE":t.schema?.body&&console.warn(`Warning: ${e} requests typically don't have request bodies`);break}}function zt(e,t){let{params:r,query:o,body:n,response:i}=t;if(r&&(!r._def||typeof r.parse!="function"))throw new Error(`Params schema for ${e} must be a valid Zod schema`);if(o&&(!o._def||typeof o.parse!="function"))throw new Error(`Query schema for ${e} must be a valid Zod schema`);if(n&&(!n._def||typeof n.parse!="function"))throw new Error(`Body schema for ${e} must be a valid Zod schema`);if(i&&(!i._def||typeof i.parse!="function"))throw new Error(`Response schema for ${e} must be a valid Zod schema`)}import{AsyncLocalStorage as Xr}from"node:async_hooks";import Kr from"node:events";import*as ae from"node:fs";import*as it from"node:http";import*as st from"node:http2";import*as E from"node:fs";import*as G from"node:path";import*as qe from"selfsigned";async function Ie(){let e=G.join(process.cwd(),".blaizejs","certs"),t=G.join(e,"dev.key"),r=G.join(e,"dev.cert");if(E.existsSync(t)&&E.existsSync(r))return{keyFile:t,certFile:r};E.existsSync(e)||E.mkdirSync(e,{recursive:!0});let i=qe.generate([{name:"commonName",value:"localhost"}],{days:365,algorithm:"sha256",keySize:2048,extensions:[{name:"basicConstraints",cA:!0},{name:"keyUsage",keyCertSign:!0,digitalSignature:!0,nonRepudiation:!0,keyEncipherment:!0,dataEncipherment:!0},{name:"extKeyUsage",serverAuth:!0,clientAuth:!0},{name:"subjectAltName",altNames:[{type:2,value:"localhost"},{type:7,ip:"127.0.0.1"}]}]});return E.writeFileSync(t,Buffer.from(i.private,"utf-8")),E.writeFileSync(r,Buffer.from(i.cert,"utf-8")),console.log(`
11
+ \u{1F512} Generated self-signed certificates for development at ${e}
12
+ `),{keyFile:t,certFile:r}}var T=class extends Error{constructor(t="\u274C Response has already been sent"){super(t),this.name="ResponseSentError"}},q=class extends T{constructor(t="Cannot set header after response has been sent"){super(t)}},Z=class extends T{constructor(t="Cannot set content type after response has been sent"){super(t)}},J=class extends T{constructor(t="Invalide URL"){super(t)}};import{AsyncLocalStorage as Bt}from"node:async_hooks";var $t=new Bt;function Le(e,t){return $t.run(e,t)}import*as Ge from"node:crypto";import{createWriteStream as At}from"node:fs";import{tmpdir as Ht}from"node:os";import{join as Ut}from"node:path";import{Readable as We}from"node:stream";var Ot=/boundary=([^;]+)/i,kt=/Content-Disposition:\s*form-data;\s*name="([^"]+)"(?:;[\s\r\n]*filename="([^"]*)")?/i,Dt=/Content-Type:\s*([^\r\n]+)/i,Nt=/multipart\/form-data/i;function je(e){let t=e.match(Ot);if(!t||!t[1])return null;let r=t[1].trim();return r.startsWith('"')&&r.endsWith('"')&&(r=r.slice(1,-1)),r||null}function _e(e){let t=e.match(kt);return!t||!t[1]?null:{name:t[1],filename:t[2]!==void 0?t[2]:void 0}}function Qe(e){let t=e.match(Dt);return t&&t[1]?.trim()?t[1].trim():"application/octet-stream"}function Ve(e){return Nt.test(e)}var qt={maxFileSize:10*1024*1024,maxFiles:10,maxFieldSize:1*1024*1024,allowedMimeTypes:[],allowedExtensions:[],strategy:"stream",tempDir:Ht(),computeHash:!1};function It(e,t={}){return{boundary:Buffer.from(`--${e}`),options:{...qt,...t},fields:new Map,files:new Map,buffer:Buffer.alloc(0),stage:"boundary",currentHeaders:"",currentField:null,currentFilename:void 0,currentMimetype:"application/octet-stream",currentContentLength:0,fileCount:0,fieldCount:0,currentBufferChunks:[],currentStream:null,currentTempPath:null,currentWriteStream:null,streamController:null,cleanupTasks:[],hasFoundValidBoundary:!1,hasProcessedAnyPart:!1,isFinished:!1}}async function Lt(e,t){let r=Buffer.concat([e.buffer,t]),o={...e,buffer:r};for(;o.buffer.length>0&&!o.isFinished;){let n=await jt(o);if(n===o)break;o=n}return o}async function jt(e){switch(e.stage){case"boundary":return _t(e);case"headers":return Qt(e);case"content":return Vt(e);default:{let{InternalServerError:t}=await import("./internal-server-error-GWBNT3OO.js");throw new t("Invalid parser stage",{operation:e.stage})}}}function _t(e){let t=e.buffer.indexOf(e.boundary);if(t===-1)return e;let r=!0,o=e.buffer.subarray(t+e.boundary.length);return o.length>=2&&o.subarray(0,2).equals(Buffer.from("--"))?{...e,buffer:o,hasFoundValidBoundary:r,isFinished:!0,stage:"boundary"}:(o.length>=2&&o.subarray(0,2).equals(Buffer.from(`\r
13
+ `))&&(o=o.subarray(2)),{...e,buffer:o,hasFoundValidBoundary:r,stage:"headers",currentHeaders:""})}async function Qt(e){let t=e.buffer.indexOf(`\r
14
+ \r
15
+ `);if(t===-1)return e;let r=e.buffer.subarray(0,t).toString("utf8"),o=e.buffer.subarray(t+4),n=_e(r);if(!n){let{ValidationError:a}=await import("./validation-error-6JDCGV2S.js");throw new a("Missing or invalid Content-Disposition header")}let i=Qe(r),s=n.filename!==void 0;if(s&&e.fileCount>=e.options.maxFiles){let{PayloadTooLargeError:a}=await import("./payload-too-large-error-EBM5BNWG.js");throw new a("Too many files in upload",{fileCount:e.fileCount+1,maxFiles:e.options.maxFiles,filename:n.filename})}if(s&&e.options.allowedMimeTypes.length>0&&!e.options.allowedMimeTypes.includes(i)){let{UnsupportedMediaTypeError:a}=await import("./unsupported-media-type-error-YQ7GCZ32.js");throw new a("File type not allowed",{receivedMimeType:i,allowedMimeTypes:e.options.allowedMimeTypes,filename:n.filename})}return{...e,buffer:o,stage:"content",currentHeaders:r,currentField:n.name,currentFilename:n.filename,currentMimetype:i,currentContentLength:0,fileCount:s?e.fileCount+1:e.fileCount,fieldCount:s?e.fieldCount:e.fieldCount+1,currentBufferChunks:[]}}async function Vt(e){let t=e.buffer.indexOf(e.boundary),r,o=!1,n=e.buffer;if(t===-1){let s=Math.max(0,e.buffer.length-e.boundary.length);if(s===0)return e;r=e.buffer.subarray(0,s),n=e.buffer.subarray(s)}else{let s=Math.max(0,t-2);r=e.buffer.subarray(0,s),n=e.buffer.subarray(t),o=!0}let i={...e,buffer:n};return r.length>0&&(i=await Wt(i,r)),o&&(i=await Jt(i),i={...i,stage:"boundary",hasProcessedAnyPart:!0}),i}async function Wt(e,t){let r=e.currentContentLength+t.length,o=e.currentFilename!==void 0?e.options.maxFileSize:e.options.maxFieldSize;if(r>o){let n=e.currentFilename!==void 0,{PayloadTooLargeError:i}=await import("./payload-too-large-error-EBM5BNWG.js"),s=e.currentField?{contentType:n?"file":"field",currentSize:r,maxSize:o,field:e.currentField,filename:e.currentFilename}:{contentType:n?"file":"field",currentSize:r,maxSize:o,filename:e.currentFilename};throw new i(`${n?"File":"Field"} size exceeds limit`,s)}return e.currentFilename!==void 0?Gt(e,t,r):{...e,currentContentLength:r,currentBufferChunks:[...e.currentBufferChunks,t]}}async function Gt(e,t,r){switch(e.options.strategy){case"memory":return{...e,currentContentLength:r,currentBufferChunks:[...e.currentBufferChunks,t]};case"stream":return e.streamController&&e.streamController.enqueue(t),{...e,currentContentLength:r};case"temp":return e.currentWriteStream&&await tr(e.currentWriteStream,t),{...e,currentContentLength:r};default:{let{ValidationError:o}=await import("./validation-error-6JDCGV2S.js");throw new o("Invalid parsing strategy")}}}async function Zt(e){if(e.currentFilename===void 0)return e;switch(e.options.strategy){case"memory":return{...e,currentBufferChunks:[]};case"stream":{let t=null,r=new ReadableStream({start:o=>{t=o}});return{...e,currentStream:r,streamController:t}}case"temp":{let t=Ut(e.options.tempDir,`upload-${Ge.randomUUID()}`),r=At(t),o=async()=>{try{let{unlink:n}=await import("node:fs/promises");await n(t)}catch(n){console.warn(`Failed to cleanup temp file: ${t}`,n)}};return{...e,currentTempPath:t,currentWriteStream:r,cleanupTasks:[...e.cleanupTasks,o]}}default:{let{ValidationError:t}=await import("./validation-error-6JDCGV2S.js");throw new t("Invalid file processing strategy")}}}async function Jt(e){return e.currentField?e.currentFilename!==void 0?Yt(e):Xt(e):I(e)}async function Yt(e){if(!e.currentField||e.currentFilename===void 0)return I(e);let t,r,o;switch(e.options.strategy){case"memory":r=Buffer.concat(e.currentBufferChunks),t=We.from(r);break;case"stream":e.streamController&&e.streamController.close(),t=e.currentStream;break;case"temp":e.currentWriteStream&&await Je(e.currentWriteStream),o=e.currentTempPath,t=We.from(Buffer.alloc(0));break;default:{let{ValidationError:s}=await import("./validation-error-6JDCGV2S.js");throw new s("Invalid file finalization strategy")}}let n={filename:e.currentFilename,fieldname:e.currentField,mimetype:e.currentMimetype,size:e.currentContentLength,stream:t,buffer:r,tempPath:o},i=Ze(e.files,e.currentField,n);return{...I(e),files:i}}function Xt(e){if(!e.currentField)return I(e);let t=Buffer.concat(e.currentBufferChunks).toString("utf8"),r=Ze(e.fields,e.currentField,t);return{...I(e),fields:r}}function I(e){return{...e,currentField:null,currentFilename:void 0,currentContentLength:0,currentBufferChunks:[],currentStream:null,streamController:null,currentTempPath:null,currentWriteStream:null}}function Ze(e,t,r){let o=new Map(e),n=o.get(t)||[];return o.set(t,[...n,r]),o}async function Kt(e){if(!e.hasFoundValidBoundary){let{ValidationError:o}=await import("./validation-error-6JDCGV2S.js");throw new o("No valid multipart boundary found")}if(e.hasFoundValidBoundary&&!e.hasProcessedAnyPart){let{ValidationError:o}=await import("./validation-error-6JDCGV2S.js");throw new o("Empty multipart request")}let t={};for(let[o,n]of e.fields.entries())t[o]=n.length===1?n[0]:n;let r={};for(let[o,n]of e.files.entries())r[o]=n.length===1?n[0]:n;return{fields:t,files:r}}async function er(e){await Promise.allSettled(e.cleanupTasks.map(t=>t())),e.streamController&&e.streamController.close(),e.currentWriteStream&&await Je(e.currentWriteStream)}async function tr(e,t){return new Promise((r,o)=>{e.write(t,n=>{n?o(n):r()})})}async function Je(e){return new Promise(t=>{e.end(()=>t())})}async function Ye(e,t={}){let r=e.headers["content-type"]||"",o=je(r);if(!o){let{UnsupportedMediaTypeError:i}=await import("./unsupported-media-type-error-YQ7GCZ32.js");throw new i("Missing boundary in multipart content-type",{receivedContentType:r,expectedFormat:"multipart/form-data; boundary=..."})}let n=It(o,t);n.currentFilename!==void 0&&(n=await Zt(n));try{for await(let i of e)n=await Lt(n,i);return Kt(n)}finally{await er(n)}}var L="Content-Type",C={json:512*1024,form:1024*1024,text:5*1024*1024,multipart:{maxFileSize:50*1024*1024,maxTotalSize:100*1024*1024,maxFiles:10,maxFieldSize:1024*1024},raw:10*1024*1024};function rr(e){let t=e.url||"/",r=e.headers.host||"localhost",n=`${e.socket&&e.socket.encrypted?"https":"http"}://${r}${t.startsWith("/")?"":"/"}${t}`;try{let i=new URL(n),s=i.pathname,a={};return i.searchParams.forEach((u,c)=>{a[c]!==void 0?Array.isArray(a[c])?a[c].push(u):a[c]=[a[c],u]:a[c]=u}),{path:s,url:i,query:a}}catch(i){throw console.warn(`Invalid URL: ${n}`,i),new J(`Invalid URL: ${n}`)}}function or(e){return"stream"in e||"httpVersionMajor"in e&&e.httpVersionMajor===2}function nr(e){let t=e.socket&&e.socket.encrypted,r=e.headers["x-forwarded-proto"];return r?Array.isArray(r)?r[0]?.split(",")[0]?.trim()||"http":r.split(",")[0]?.trim()||"http":t?"https":"http"}async function Xe(e,t,r={}){let{path:o,url:n,query:i}=rr(e),s=e.method||"GET",a=or(e),u=nr(e),c={},w={...r.initialState||{}},d={sent:!1},g={request:ir(e,{path:o,url:n,query:i,params:c,method:s,isHttp2:a,protocol:u}),response:{},state:w};return g.response=ar(t,d,g),r.parseBody&&await hr(e,g,r),g}function ir(e,t){return{raw:e,...t,header:Ke(e),headers:sr(e),body:void 0}}function Ke(e){return t=>{let r=e.headers[t.toLowerCase()];return Array.isArray(r)?r.join(", "):r||void 0}}function sr(e){let t=Ke(e);return r=>r&&Array.isArray(r)&&r.length>0?r.reduce((o,n)=>(o[n]=t(n),o),{}):Object.entries(e.headers).reduce((o,[n,i])=>(o[n]=Array.isArray(i)?i.join(", "):i||void 0,o),{})}function ar(e,t,r){return{raw:e,get sent(){return t.sent},status:ur(e,t,r),header:lr(e,t,r),headers:cr(e,t,r),type:dr(e,t,r),json:pr(e,t),text:fr(e,t),html:mr(e,t),redirect:gr(e,t),stream:yr(e,t)}}function ur(e,t,r){return function(n){if(t.sent)throw new T;return e.statusCode=n,r.response}}function lr(e,t,r){return function(n,i){if(t.sent)throw new q;return e.setHeader(n,i),r.response}}function cr(e,t,r){return function(n){if(t.sent)throw new q;for(let[i,s]of Object.entries(n))e.setHeader(i,s);return r.response}}function dr(e,t,r){return function(n){if(t.sent)throw new Z;return e.setHeader(L,n),r.response}}function pr(e,t){return function(o,n){if(t.sent)throw new T;n!==void 0&&(e.statusCode=n),e.setHeader(L,"application/json"),e.end(JSON.stringify(o)),t.sent=!0}}function fr(e,t){return function(o,n){if(t.sent)throw new T;n!==void 0&&(e.statusCode=n),e.setHeader(L,"text/plain"),e.end(o),t.sent=!0}}function mr(e,t){return function(o,n){if(t.sent)throw new T;n!==void 0&&(e.statusCode=n),e.setHeader(L,"text/html"),e.end(o),t.sent=!0}}function gr(e,t){return function(o,n=302){if(t.sent)throw new T;e.statusCode=n,e.setHeader("Location",o),e.end(),t.sent=!0}}function yr(e,t){return function(o,n={}){if(t.sent)throw new T;if(n.status!==void 0&&(e.statusCode=n.status),n.contentType&&e.setHeader(L,n.contentType),n.headers)for(let[i,s]of Object.entries(n.headers))e.setHeader(i,s);o.pipe(e),o.on("end",()=>{t.sent=!0}),o.on("error",i=>{console.error("Stream error:",i),t.sent||(e.statusCode=500,e.end("Stream error"),t.sent=!0)})}}async function hr(e,t,r={}){if(wr(e.method))return;let o=e.headers["content-type"]||"",n=parseInt(e.headers["content-length"]||"0",10);if(n===0)return;let i={json:r.bodyLimits?.json??C.json,form:r.bodyLimits?.form??C.form,text:r.bodyLimits?.text??C.text,raw:r.bodyLimits?.raw??C.raw,multipart:{maxFileSize:r.bodyLimits?.multipart?.maxFileSize??C.multipart.maxFileSize,maxFiles:r.bodyLimits?.multipart?.maxFiles??C.multipart.maxFiles,maxFieldSize:r.bodyLimits?.multipart?.maxFieldSize??C.multipart.maxFieldSize,maxTotalSize:r.bodyLimits?.multipart?.maxTotalSize??C.multipart.maxTotalSize}};try{if(o.includes("application/json")){if(n>i.json)throw new Error(`JSON body too large: ${n} > ${i.json} bytes`);await Rr(e,t)}else if(o.includes("application/x-www-form-urlencoded")){if(n>i.form)throw new Error(`Form body too large: ${n} > ${i.form} bytes`);await Sr(e,t)}else if(o.includes("text/")){if(n>i.text)throw new Error(`Text body too large: ${n} > ${i.text} bytes`);await br(e,t)}else if(Ve(o))await Tr(e,t,i.multipart);else{if(n>i.raw)throw new Error(`Request body too large: ${n} > ${i.raw} bytes`);return}}catch(s){let a=o.includes("multipart")?"multipart_parse_error":"body_read_error";Y(t,a,"Error reading request body",s)}}function wr(e){return["GET","HEAD","OPTIONS"].includes(e||"GET")}async function Rr(e,t){let r=await se(e);if(!r){console.warn("Empty body, skipping JSON parsing");return}if(r.trim()==="null"){console.warn('Body is the string "null"'),t.request.body=null;return}try{let o=JSON.parse(r);t.request.body=o}catch(o){t.request.body=null,Y(t,"json_parse_error","Invalid JSON in request body",o)}}async function Sr(e,t){let r=await se(e);if(r)try{t.request.body=xr(r)}catch(o){t.request.body=null,Y(t,"form_parse_error","Invalid form data in request body",o)}}function xr(e){let t=new URLSearchParams(e),r={};return t.forEach((o,n)=>{r[n]!==void 0?Array.isArray(r[n])?r[n].push(o):r[n]=[r[n],o]:r[n]=o}),r}async function br(e,t){let r=await se(e);r&&(t.request.body=r)}async function Tr(e,t,r){try{let o=r||C.multipart,n=await Ye(e,{strategy:"stream",maxFileSize:o.maxFileSize,maxFiles:o.maxFiles,maxFieldSize:o.maxFieldSize});t.request.multipart=n,t.request.files=n.files,t.request.body=n.fields}catch(o){t.request.body=null,Y(t,"multipart_parse_error","Failed to parse multipart data",o)}}function Y(e,t,r,o){let n={type:t,message:r,error:o};e.state._bodyError=n}async function se(e){return new Promise((t,r)=>{let o=[];e.on("data",n=>{o.push(Buffer.isBuffer(n)?n:Buffer.from(n))}),e.on("end",()=>{t(Buffer.concat(o).toString("utf8"))}),e.on("error",n=>{r(n)})})}var k=class extends S{constructor(t,r=void 0,o=void 0){super("NOT_FOUND",t,404,o??x(),r)}};function Pr(e){return e instanceof S}function et(e){if(Pr(e))return{type:e.type,title:e.title,status:e.status,correlationId:e.correlationId,timestamp:e.timestamp.toISOString(),details:e.details};let t=oe(),r;e instanceof Error?r=e.message:e==null?r="Unknown error occurred":r=String(e);let o=new H("Internal Server Error",{originalMessage:r},t);return{type:o.type,title:o.title,status:o.status,correlationId:o.correlationId,timestamp:o.timestamp.toISOString(),details:o.details}}function tt(e){return e("x-correlation-id")??oe()}function rt(e,t){e("x-correlation-id",t)}function ot(e={}){let{debug:t=!1}=e;return{name:"ErrorBoundary",execute:async(o,n)=>{try{await n()}catch(i){if(o.response.sent){t&&console.error("Error occurred after response was sent:",i);return}t&&console.error("Error boundary caught error:",i);let s=tt(o.request.header),a=et(i);a.correlationId=s,rt(o.response.header,s),o.response.status(a.status).json(a)}},debug:t}}function nt(e){return async(t,r)=>{try{let o=await Xe(t,r,{parseBody:!0}),i=[ot(),...e.middleware],s=D(i);await Le(o,async()=>{await s(o,async()=>{if(!o.response.sent&&(await e.router.handleRequest(o),!o.response.sent))throw new k(`Route not found: ${o.request.method} ${o.request.path}`)})})}catch(o){console.error("Error creating context:",o),r.writeHead(500,{"Content-Type":"application/json"}),r.end(JSON.stringify({error:"Internal Server Error",message:"Failed to process request"}))}}}async function vr(e){if(!e.enabled)return{};let{keyFile:t,certFile:r}=e,o=process.env.NODE_ENV==="development",n=!t||!r;if(n&&o)return await Ie();if(n)throw new Error("HTTP/2 requires SSL certificates. Provide keyFile and certFile in http2 options. In development, set NODE_ENV=development to generate them automatically.");return{keyFile:t,certFile:r}}function Er(e,t){if(!e)return it.createServer();let r={allowHTTP1:!0};try{t.keyFile&&(r.key=ae.readFileSync(t.keyFile)),t.certFile&&(r.cert=ae.readFileSync(t.certFile))}catch(o){throw new Error(`Failed to read certificate files: ${o instanceof Error?o.message:String(o)}`)}return st.createSecureServer(r)}function Cr(e,t,r,o){return new Promise((n,i)=>{e.listen(t,r,()=>{let a=`${o?"https":"http"}://${r}:${t}`;console.log(`
1446
16
  \u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}
1447
17
 
1448
18
  \u26A1 BlaizeJS DEVELOPMENT SERVER HOT AND READY \u26A1
1449
19
 
1450
- \u{1F680} Server: ${url}
20
+ \u{1F680} Server: ${a}
1451
21
  \u{1F525} Hot Reload: Enabled
1452
22
  \u{1F6E0}\uFE0F Mode: Development
1453
23
 
1454
24
  Time to build something amazing! \u{1F680}
1455
25
 
1456
26
  \u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}\u{1F525}
1457
- `);
1458
- resolve3();
1459
- });
1460
- server.on("error", (err) => {
1461
- console.error("Server error:", err);
1462
- reject(err);
1463
- });
1464
- });
1465
- }
1466
- async function initializePlugins(serverInstance) {
1467
- for (const plugin of serverInstance.plugins) {
1468
- if (typeof plugin.initialize === "function") {
1469
- await plugin.initialize(serverInstance);
1470
- }
1471
- }
1472
- }
1473
- async function startServer(serverInstance, serverOptions) {
1474
- if (serverInstance.server) {
1475
- return;
1476
- }
1477
- try {
1478
- const port = serverOptions.port;
1479
- const host = serverOptions.host;
1480
- await initializePlugins(serverInstance);
1481
- const http2Options = serverOptions.http2 || { enabled: true };
1482
- const isHttp2 = !!http2Options.enabled;
1483
- const certOptions = await prepareCertificates(http2Options);
1484
- if (serverOptions.http2 && certOptions.keyFile && certOptions.certFile) {
1485
- serverOptions.http2.keyFile = certOptions.keyFile;
1486
- serverOptions.http2.certFile = certOptions.certFile;
1487
- }
1488
- const server = createServerInstance(isHttp2, certOptions);
1489
- serverInstance.server = server;
1490
- serverInstance.port = port;
1491
- serverInstance.host = host;
1492
- const requestHandler = createRequestHandler(serverInstance);
1493
- server.on("request", requestHandler);
1494
- await listenOnPort(server, port, host, isHttp2);
1495
- } catch (error) {
1496
- console.error("Failed to start server:", error);
1497
- throw error;
1498
- }
1499
- }
1500
-
1501
- // src/server/stop.ts
1502
- var isShuttingDown = false;
1503
- async function stopServer(serverInstance, options = {}) {
1504
- const server = serverInstance.server;
1505
- const events = serverInstance.events;
1506
- if (isShuttingDown) {
1507
- console.log("\u26A0\uFE0F Shutdown already in progress, ignoring duplicate shutdown request");
1508
- return;
1509
- }
1510
- if (!server) {
1511
- return;
1512
- }
1513
- isShuttingDown = true;
1514
- const timeout = options.timeout || 5e3;
1515
- try {
1516
- if (options.onStopping) {
1517
- await options.onStopping();
1518
- }
1519
- events.emit("stopping");
1520
- if (serverInstance.router && typeof serverInstance.router.close === "function") {
1521
- console.log("\u{1F50C} Closing router watchers...");
1522
- try {
1523
- await Promise.race([
1524
- serverInstance.router.close(),
1525
- new Promise(
1526
- (_, reject) => setTimeout(() => reject(new Error("Router close timeout")), 2e3)
1527
- )
1528
- ]);
1529
- console.log("\u2705 Router watchers closed");
1530
- } catch (error) {
1531
- console.error("\u274C Error closing router watchers:", error);
1532
- }
1533
- }
1534
- try {
1535
- await Promise.race([
1536
- serverInstance.pluginManager.onServerStop(serverInstance, server),
1537
- new Promise(
1538
- (_, reject) => setTimeout(() => reject(new Error("Plugin stop timeout")), 2e3)
1539
- )
1540
- ]);
1541
- } catch (error) {
1542
- console.error("\u274C Plugin stop timeout:", error);
1543
- }
1544
- const closePromise = new Promise((resolve3, reject) => {
1545
- server.close((err) => {
1546
- if (err) return reject(err);
1547
- resolve3();
1548
- });
1549
- });
1550
- const timeoutPromise = new Promise((_, reject) => {
1551
- setTimeout(() => {
1552
- reject(new Error("Server shutdown timeout"));
1553
- }, timeout);
1554
- });
1555
- await Promise.race([closePromise, timeoutPromise]);
1556
- try {
1557
- await Promise.race([
1558
- serverInstance.pluginManager.terminatePlugins(serverInstance),
1559
- new Promise(
1560
- (_, reject) => setTimeout(() => reject(new Error("Plugin terminate timeout")), 1e3)
1561
- )
1562
- ]);
1563
- } catch (error) {
1564
- console.error("\u274C Plugin terminate timeout:", error);
1565
- }
1566
- if (options.onStopped) {
1567
- await options.onStopped();
1568
- }
1569
- events.emit("stopped");
1570
- serverInstance.server = null;
1571
- console.log("\u2705 Graceful shutdown completed");
1572
- isShuttingDown = false;
1573
- } catch (error) {
1574
- isShuttingDown = false;
1575
- console.error("\u26A0\uFE0F Shutdown error (forcing exit):", error);
1576
- if (server && typeof server.close === "function") {
1577
- server.close();
1578
- }
1579
- if (process.env.NODE_ENV === "development") {
1580
- console.log("\u{1F504} Forcing exit for development restart...");
1581
- process.exit(0);
1582
- }
1583
- events.emit("error", error);
1584
- throw error;
1585
- }
1586
- }
1587
- function registerSignalHandlers(stopFn) {
1588
- const isDevelopment = process.env.NODE_ENV === "development";
1589
- if (isDevelopment) {
1590
- const sigintHandler = () => {
1591
- console.log("\u{1F4E4} SIGINT received, forcing exit for development restart...");
1592
- process.exit(0);
1593
- };
1594
- const sigtermHandler = () => {
1595
- console.log("\u{1F4E4} SIGTERM received, forcing exit for development restart...");
1596
- process.exit(0);
1597
- };
1598
- process.on("SIGINT", sigintHandler);
1599
- process.on("SIGTERM", sigtermHandler);
1600
- return {
1601
- unregister: () => {
1602
- process.removeListener("SIGINT", sigintHandler);
1603
- process.removeListener("SIGTERM", sigtermHandler);
1604
- }
1605
- };
1606
- } else {
1607
- const sigintHandler = () => {
1608
- console.log("\u{1F4E4} SIGINT received, starting graceful shutdown...");
1609
- stopFn().catch(console.error);
1610
- };
1611
- const sigtermHandler = () => {
1612
- console.log("\u{1F4E4} SIGTERM received, starting graceful shutdown...");
1613
- stopFn().catch(console.error);
1614
- };
1615
- process.on("SIGINT", sigintHandler);
1616
- process.on("SIGTERM", sigtermHandler);
1617
- return {
1618
- unregister: () => {
1619
- process.removeListener("SIGINT", sigintHandler);
1620
- process.removeListener("SIGTERM", sigtermHandler);
1621
- }
1622
- };
1623
- }
1624
- }
1625
-
1626
- // src/server/validation.ts
1627
- import { z } from "zod";
1628
- var middlewareSchema = z.custom(
1629
- (data) => data !== null && typeof data === "object" && "execute" in data && typeof data.execute === "function",
1630
- {
1631
- message: "Expected middleware to have an execute function"
1632
- }
1633
- );
1634
- var pluginSchema = z.custom(
1635
- (data) => data !== null && typeof data === "object" && "register" in data && typeof data.register === "function",
1636
- {
1637
- message: "Expected a valid plugin object with a register method"
1638
- }
1639
- );
1640
- var http2Schema = z.object({
1641
- enabled: z.boolean().optional().default(true),
1642
- keyFile: z.string().optional(),
1643
- certFile: z.string().optional()
1644
- }).refine(
1645
- (data) => {
1646
- if (data.enabled && process.env.NODE_ENV === "production") {
1647
- return data.keyFile && data.certFile;
1648
- }
1649
- return true;
1650
- },
1651
- {
1652
- message: "When HTTP/2 is enabled (outside of development mode), both keyFile and certFile must be provided"
1653
- }
1654
- );
1655
- var serverOptionsSchema = z.object({
1656
- port: z.number().int().positive().optional().default(3e3),
1657
- host: z.string().optional().default("localhost"),
1658
- routesDir: z.string().optional().default("./routes"),
1659
- http2: http2Schema.optional().default({
1660
- enabled: true
1661
- }),
1662
- middleware: z.array(middlewareSchema).optional().default([]),
1663
- plugins: z.array(pluginSchema).optional().default([])
1664
- });
1665
- function validateServerOptions(options) {
1666
- try {
1667
- return serverOptionsSchema.parse(options);
1668
- } catch (error) {
1669
- if (error instanceof z.ZodError) {
1670
- const formattedError = error.format();
1671
- throw new Error(`Invalid server options: ${JSON.stringify(formattedError, null, 2)}`);
1672
- }
1673
- throw new Error(`Invalid server options: ${String(error)}`);
1674
- }
1675
- }
1676
-
1677
- // src/plugins/lifecycle.ts
1678
- function createPluginLifecycleManager(options = {}) {
1679
- const { continueOnError = true, debug = false, onError } = options;
1680
- function log(message, ...args) {
1681
- if (debug) {
1682
- console.log(`[PluginLifecycle] ${message}`, ...args);
1683
- }
1684
- }
1685
- function handleError(plugin, phase, error) {
1686
- const errorMessage = `Plugin ${plugin.name} failed during ${phase}: ${error.message}`;
1687
- if (onError) {
1688
- onError(plugin, phase, error);
1689
- } else {
1690
- console.error(errorMessage, error);
1691
- }
1692
- if (!continueOnError) {
1693
- throw new Error(errorMessage);
1694
- }
1695
- }
1696
- return {
1697
- /**
1698
- * Initialize all plugins
1699
- */
1700
- async initializePlugins(server) {
1701
- log("Initializing plugins...");
1702
- for (const plugin of server.plugins) {
1703
- if (plugin.initialize) {
1704
- try {
1705
- log(`Initializing plugin: ${plugin.name}`);
1706
- await plugin.initialize(server);
1707
- } catch (error) {
1708
- handleError(plugin, "initialize", error);
1709
- }
1710
- }
1711
- }
1712
- log(`Initialized ${server.plugins.length} plugins`);
1713
- },
1714
- /**
1715
- * Terminate all plugins in reverse order
1716
- */
1717
- async terminatePlugins(server) {
1718
- log("Terminating plugins...");
1719
- const pluginsToTerminate = [...server.plugins].reverse();
1720
- for (const plugin of pluginsToTerminate) {
1721
- if (plugin.terminate) {
1722
- try {
1723
- log(`Terminating plugin: ${plugin.name}`);
1724
- await plugin.terminate(server);
1725
- } catch (error) {
1726
- handleError(plugin, "terminate", error);
1727
- }
1728
- }
1729
- }
1730
- log(`Terminated ${pluginsToTerminate.length} plugins`);
1731
- },
1732
- /**
1733
- * Notify plugins that the server has started
1734
- */
1735
- async onServerStart(server, httpServer) {
1736
- log("Notifying plugins of server start...");
1737
- for (const plugin of server.plugins) {
1738
- if (plugin.onServerStart) {
1739
- try {
1740
- log(`Notifying plugin of server start: ${plugin.name}`);
1741
- await plugin.onServerStart(httpServer);
1742
- } catch (error) {
1743
- handleError(plugin, "onServerStart", error);
1744
- }
1745
- }
1746
- }
1747
- },
1748
- /**
1749
- * Notify plugins that the server is stopping
1750
- */
1751
- async onServerStop(server, httpServer) {
1752
- log("Notifying plugins of server stop...");
1753
- const pluginsToNotify = [...server.plugins].reverse();
1754
- for (const plugin of pluginsToNotify) {
1755
- if (plugin.onServerStop) {
1756
- try {
1757
- log(`Notifying plugin of server stop: ${plugin.name}`);
1758
- await plugin.onServerStop(httpServer);
1759
- } catch (error) {
1760
- handleError(plugin, "onServerStop", error);
1761
- }
1762
- }
1763
- }
1764
- }
1765
- };
1766
- }
1767
-
1768
- // src/plugins/errors.ts
1769
- var PluginValidationError = class extends Error {
1770
- constructor(pluginName, message) {
1771
- super(`Plugin validation error${pluginName ? ` for "${pluginName}"` : ""}: ${message}`);
1772
- this.pluginName = pluginName;
1773
- this.name = "PluginValidationError";
1774
- }
1775
- };
1776
-
1777
- // src/plugins/validation.ts
1778
- var RESERVED_NAMES = /* @__PURE__ */ new Set([
1779
- "core",
1780
- "server",
1781
- "router",
1782
- "middleware",
1783
- "context",
1784
- "blaize",
1785
- "blaizejs"
1786
- ]);
1787
- var VALID_NAME_PATTERN = /^[a-z]([a-z0-9-]*[a-z0-9])?$/;
1788
- var VALID_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[a-zA-Z0-9-.]+)?(?:\+[a-zA-Z0-9-.]+)?$/;
1789
- function validatePlugin(plugin, options = {}) {
1790
- const { requireVersion = true, validateNameFormat = true, checkReservedNames = true } = options;
1791
- if (!plugin || typeof plugin !== "object") {
1792
- throw new PluginValidationError("", "Plugin must be an object");
1793
- }
1794
- const p = plugin;
1795
- if (!p.name || typeof p.name !== "string") {
1796
- throw new PluginValidationError("", "Plugin must have a name (string)");
1797
- }
1798
- if (validateNameFormat && !VALID_NAME_PATTERN.test(p.name)) {
1799
- throw new PluginValidationError(
1800
- p.name,
1801
- "Plugin name must be lowercase letters, numbers, and hyphens only"
1802
- );
1803
- }
1804
- if (checkReservedNames && RESERVED_NAMES.has(p.name.toLowerCase())) {
1805
- throw new PluginValidationError(p.name, `Plugin name "${p.name}" is reserved`);
1806
- }
1807
- if (requireVersion) {
1808
- if (!p.version || typeof p.version !== "string") {
1809
- throw new PluginValidationError(p.name, "Plugin must have a version (string)");
1810
- }
1811
- if (!VALID_VERSION_PATTERN.test(p.version)) {
1812
- throw new PluginValidationError(
1813
- p.name,
1814
- 'Plugin version must follow semantic versioning (e.g., "1.0.0")'
1815
- );
1816
- }
1817
- }
1818
- if (!p.register || typeof p.register !== "function") {
1819
- throw new PluginValidationError(p.name, "Plugin must have a register method (function)");
1820
- }
1821
- const lifecycleMethods = ["initialize", "terminate", "onServerStart", "onServerStop"];
1822
- for (const method of lifecycleMethods) {
1823
- if (p[method] && typeof p[method] !== "function") {
1824
- throw new PluginValidationError(p.name, `Plugin ${method} must be a function if provided`);
1825
- }
1826
- }
1827
- }
1828
-
1829
- // src/router/discovery/cache.ts
1830
- import * as crypto from "node:crypto";
1831
- import * as fs3 from "node:fs/promises";
1832
- import { createRequire } from "node:module";
1833
- import * as path3 from "node:path";
1834
-
1835
- // src/router/discovery/loader.ts
1836
- async function dynamicImport(filePath) {
1837
- const cacheBuster = `?t=${Date.now()}`;
1838
- const importPath = filePath + cacheBuster;
1839
- try {
1840
- const module = await import(importPath);
1841
- console.log(`\u2705 Successfully imported module`);
1842
- return module;
1843
- } catch (error) {
1844
- const errorMessage = error instanceof Error ? error.message : String(error);
1845
- console.log(`\u26A0\uFE0F Error importing with cache buster, trying original path:`, errorMessage);
1846
- return import(filePath);
1847
- }
1848
- }
1849
- async function loadRouteModule(filePath, basePath) {
1850
- try {
1851
- const parsedRoute = parseRoutePath(filePath, basePath);
1852
- const module = await dynamicImport(filePath);
1853
- console.log("\u{1F4E6} Module exports:", Object.keys(module));
1854
- const routes = [];
1855
- if (module.default && typeof module.default === "object") {
1856
- const route = {
1857
- ...module.default,
1858
- path: parsedRoute.routePath
1859
- };
1860
- routes.push(route);
1861
- }
1862
- Object.entries(module).forEach(([exportName, exportValue]) => {
1863
- if (exportName === "default" || !exportValue || typeof exportValue !== "object") {
1864
- return;
1865
- }
1866
- const potentialRoute = exportValue;
1867
- if (isValidRoute(potentialRoute)) {
1868
- const route = {
1869
- ...potentialRoute,
1870
- // Use the route's own path if it has one, otherwise derive from file
1871
- path: parsedRoute.routePath
1872
- };
1873
- routes.push(route);
1874
- }
1875
- });
1876
- if (routes.length === 0) {
1877
- console.warn(`Route file ${filePath} does not export any valid route definitions`);
1878
- return [];
1879
- }
1880
- console.log(`\u2705 Successfully Loaded ${routes.length} route(s)`);
1881
- return routes;
1882
- } catch (error) {
1883
- console.error(`Failed to load route module ${filePath}:`, error);
1884
- return [];
1885
- }
1886
- }
1887
- function isValidRoute(obj) {
1888
- if (!obj || typeof obj !== "object") {
1889
- return false;
1890
- }
1891
- const httpMethods = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
1892
- const hasHttpMethod = httpMethods.some(
1893
- (method) => obj[method] && typeof obj[method] === "object" && obj[method].handler
1894
- );
1895
- return hasHttpMethod;
1896
- }
1897
-
1898
- // src/router/discovery/cache.ts
1899
- var fileRouteCache = /* @__PURE__ */ new Map();
1900
- async function processChangedFile(filePath, routesDir, updateCache = true) {
1901
- const stat3 = await fs3.stat(filePath);
1902
- const lastModified = stat3.mtime.getTime();
1903
- const cachedEntry = fileRouteCache.get(filePath);
1904
- if (updateCache && cachedEntry && cachedEntry.timestamp === lastModified) {
1905
- return cachedEntry.routes;
1906
- }
1907
- invalidateModuleCache(filePath);
1908
- const routes = await loadRouteModule(filePath, routesDir);
1909
- if (updateCache) {
1910
- const hash = hashRoutes(routes);
1911
- fileRouteCache.set(filePath, {
1912
- routes,
1913
- timestamp: lastModified,
1914
- hash
1915
- });
1916
- }
1917
- return routes;
1918
- }
1919
- function hasRouteContentChanged(filePath, newRoutes) {
1920
- const cachedEntry = fileRouteCache.get(filePath);
1921
- if (!cachedEntry) {
1922
- return true;
1923
- }
1924
- const newHash = hashRoutes(newRoutes);
1925
- return cachedEntry.hash !== newHash;
1926
- }
1927
- function clearFileCache(filePath) {
1928
- if (filePath) {
1929
- fileRouteCache.delete(filePath);
1930
- } else {
1931
- fileRouteCache.clear();
1932
- }
1933
- }
1934
- function hashRoutes(routes) {
1935
- const routeData = routes.map((route) => ({
1936
- path: route.path,
1937
- methods: Object.keys(route).filter((key) => key !== "path").sort().map((method) => {
1938
- const methodDef = route[method];
1939
- const handlerString = methodDef?.handler ? methodDef.handler.toString() : null;
1940
- return {
1941
- method,
1942
- // Include handler function string for change detection
1943
- handler: handlerString,
1944
- // Include middleware if present
1945
- middleware: methodDef?.middleware ? methodDef.middleware.length : 0,
1946
- // Include schema structure (but not full serialization which can be unstable)
1947
- hasSchema: !!methodDef?.schema,
1948
- schemaKeys: methodDef?.schema ? Object.keys(methodDef.schema).sort() : []
1949
- };
1950
- })
1951
- }));
1952
- const dataString = JSON.stringify(routeData);
1953
- const hash = crypto.createHash("md5").update(dataString).digest("hex");
1954
- return hash;
1955
- }
1956
- function invalidateModuleCache(filePath) {
1957
- try {
1958
- const absolutePath = path3.resolve(filePath);
1959
- if (typeof __require !== "undefined") {
1960
- delete __require.cache[absolutePath];
1961
- try {
1962
- const resolvedPath = __require.resolve(absolutePath);
1963
- delete __require.cache[resolvedPath];
1964
- } catch (resolveError) {
1965
- const errorMessage = resolveError instanceof Error ? resolveError.message : String(resolveError);
1966
- console.log(`\u26A0\uFE0F Could not resolve path: ${errorMessage}`);
1967
- }
1968
- } else {
1969
- try {
1970
- const require2 = createRequire(import.meta.url);
1971
- delete require2.cache[absolutePath];
1972
- try {
1973
- const resolvedPath = require2.resolve(absolutePath);
1974
- delete require2.cache[resolvedPath];
1975
- } catch {
1976
- console.log(`\u26A0\uFE0F Could not resolve ESM path`);
1977
- }
1978
- } catch {
1979
- console.log(`\u26A0\uFE0F createRequire not available in pure ESM`);
1980
- }
1981
- }
1982
- } catch (error) {
1983
- console.log(`\u26A0\uFE0F Error during module cache invalidation for ${filePath}:`, error);
1984
- }
1985
- }
1986
-
1987
- // src/router/discovery/parallel.ts
1988
- import * as os from "node:os";
1989
-
1990
- // src/router/discovery/finder.ts
1991
- import * as fs4 from "node:fs/promises";
1992
- import * as path4 from "node:path";
1993
- async function findRouteFiles(routesDir, options = {}) {
1994
- const absoluteDir = path4.isAbsolute(routesDir) ? routesDir : path4.resolve(process.cwd(), routesDir);
1995
- console.log("Creating router with routes directory:", absoluteDir);
1996
- try {
1997
- const stats = await fs4.stat(absoluteDir);
1998
- if (!stats.isDirectory()) {
1999
- throw new Error(`Route directory is not a directory: ${absoluteDir}`);
2000
- }
2001
- } catch (error) {
2002
- if (error.code === "ENOENT") {
2003
- throw new Error(`Route directory not found: ${absoluteDir}`);
2004
- }
2005
- throw error;
2006
- }
2007
- const routeFiles = [];
2008
- const ignore = options.ignore || ["node_modules", ".git"];
2009
- async function scanDirectory(dir) {
2010
- const entries = await fs4.readdir(dir, { withFileTypes: true });
2011
- for (const entry of entries) {
2012
- const fullPath = path4.join(dir, entry.name);
2013
- if (entry.isDirectory() && ignore.includes(entry.name)) {
2014
- continue;
2015
- }
2016
- if (entry.isDirectory()) {
2017
- await scanDirectory(fullPath);
2018
- } else if (isRouteFile(entry.name)) {
2019
- routeFiles.push(fullPath);
2020
- }
2021
- }
2022
- }
2023
- await scanDirectory(absoluteDir);
2024
- return routeFiles;
2025
- }
2026
- function isRouteFile(filename) {
2027
- return !filename.startsWith("_") && (filename.endsWith(".ts") || filename.endsWith(".js"));
2028
- }
2029
-
2030
- // src/router/discovery/parallel.ts
2031
- async function processFilesInParallel(filePaths, processor, concurrency = Math.max(1, Math.floor(os.cpus().length / 2))) {
2032
- const chunks = chunkArray(filePaths, concurrency);
2033
- const results = [];
2034
- for (const chunk of chunks) {
2035
- const chunkResults = await Promise.allSettled(chunk.map((filePath) => processor(filePath)));
2036
- const successfulResults = chunkResults.filter((result) => result.status === "fulfilled").map((result) => result.value);
2037
- results.push(...successfulResults);
2038
- }
2039
- return results;
2040
- }
2041
- async function loadInitialRoutesParallel(routesDir) {
2042
- const files = await findRouteFiles(routesDir);
2043
- const routeArrays = await processFilesInParallel(
2044
- files,
2045
- (filePath) => processChangedFile(filePath, routesDir)
2046
- );
2047
- return routeArrays.flat();
2048
- }
2049
- function chunkArray(array, chunkSize) {
2050
- const chunks = [];
2051
- for (let i = 0; i < array.length; i += chunkSize) {
2052
- chunks.push(array.slice(i, i + chunkSize));
2053
- }
2054
- return chunks;
2055
- }
2056
-
2057
- // src/router/discovery/profiler.ts
2058
- var profilerState = {
2059
- fileChanges: 0,
2060
- totalReloadTime: 0,
2061
- averageReloadTime: 0,
2062
- slowReloads: []
2063
- };
2064
- function trackReloadPerformance(filePath, startTime) {
2065
- const duration = Date.now() - startTime;
2066
- profilerState.fileChanges++;
2067
- profilerState.totalReloadTime += duration;
2068
- profilerState.averageReloadTime = profilerState.totalReloadTime / profilerState.fileChanges;
2069
- if (duration > 100) {
2070
- profilerState.slowReloads.push({ file: filePath, time: duration });
2071
- if (profilerState.slowReloads.length > 10) {
2072
- profilerState.slowReloads.shift();
2073
- }
2074
- }
2075
- if (process.env.NODE_ENV === "development") {
2076
- const emoji = duration < 50 ? "\u26A1" : duration < 100 ? "\u{1F504}" : "\u{1F40C}";
2077
- console.log(`${emoji} Route reload: ${filePath} (${duration}ms)`);
2078
- }
2079
- }
2080
- function withPerformanceTracking(fn, filePath) {
2081
- console.log(`Tracking performance for: ${filePath}`);
2082
- return async (...args) => {
2083
- const startTime = Date.now();
2084
- try {
2085
- const result = await fn(...args);
2086
- trackReloadPerformance(filePath, startTime);
2087
- return result;
2088
- } catch (error) {
2089
- trackReloadPerformance(filePath, startTime);
2090
- throw error;
2091
- }
2092
- };
2093
- }
2094
-
2095
- // src/router/discovery/watchers.ts
2096
- import * as path5 from "node:path";
2097
- import { watch } from "chokidar";
2098
- function watchRoutes(routesDir, options = {}) {
2099
- const debounceMs = options.debounceMs || 16;
2100
- const debouncedCallbacks = /* @__PURE__ */ new Map();
2101
- function createDebouncedCallback(fn, filePath) {
2102
- return (...args) => {
2103
- const existingTimeout = debouncedCallbacks.get(filePath);
2104
- if (existingTimeout) {
2105
- clearTimeout(existingTimeout);
2106
- }
2107
- const timeoutId = setTimeout(() => {
2108
- fn(...args);
2109
- debouncedCallbacks.delete(filePath);
2110
- }, debounceMs);
2111
- debouncedCallbacks.set(filePath, timeoutId);
2112
- };
2113
- }
2114
- const routesByPath = /* @__PURE__ */ new Map();
2115
- async function loadInitialRoutes() {
2116
- try {
2117
- const files = await findRouteFiles(routesDir, {
2118
- ignore: options.ignore
2119
- });
2120
- for (const filePath of files) {
2121
- await loadAndNotify(filePath);
2122
- }
2123
- } catch (error) {
2124
- handleError(error);
2125
- }
2126
- }
2127
- async function loadAndNotify(filePath) {
2128
- try {
2129
- const existingRoutes = routesByPath.get(filePath);
2130
- const newRoutes = await processChangedFile(filePath, routesDir, false);
2131
- if (!newRoutes || newRoutes.length === 0) {
2132
- return;
2133
- }
2134
- if (existingRoutes && !hasRouteContentChanged(filePath, newRoutes)) {
2135
- return;
2136
- }
2137
- await processChangedFile(filePath, routesDir, true);
2138
- const normalizedPath = path5.normalize(filePath);
2139
- if (existingRoutes) {
2140
- routesByPath.set(filePath, newRoutes);
2141
- if (options.onRouteChanged) {
2142
- options.onRouteChanged(normalizedPath, newRoutes);
2143
- }
2144
- } else {
2145
- routesByPath.set(filePath, newRoutes);
2146
- if (options.onRouteAdded) {
2147
- options.onRouteAdded(normalizedPath, newRoutes);
2148
- }
2149
- }
2150
- } catch (error) {
2151
- console.log(`\u26A0\uFE0F Error processing file ${filePath}:`, error);
2152
- handleError(error);
2153
- }
2154
- }
2155
- function handleRemoved(filePath) {
2156
- const normalizedPath = path5.normalize(filePath);
2157
- const routes = routesByPath.get(normalizedPath);
2158
- if (routes && routes.length > 0 && options.onRouteRemoved) {
2159
- options.onRouteRemoved(normalizedPath, routes);
2160
- }
2161
- routesByPath.delete(normalizedPath);
2162
- }
2163
- function handleError(error) {
2164
- if (options.onError && error instanceof Error) {
2165
- options.onError(error);
2166
- } else {
2167
- console.error("\u26A0\uFE0F Route watcher error:", error);
2168
- }
2169
- }
2170
- const watcher = watch(routesDir, {
2171
- // Much faster response times
2172
- awaitWriteFinish: {
2173
- stabilityThreshold: 50,
2174
- // Reduced from 300ms
2175
- pollInterval: 10
2176
- // Reduced from 100ms
2177
- },
2178
- // Performance optimizations
2179
- usePolling: false,
2180
- atomic: true,
2181
- followSymlinks: false,
2182
- depth: 10,
2183
- // More aggressive ignoring
2184
- ignored: [
2185
- /(^|[/\\])\../,
2186
- /node_modules/,
2187
- /\.git/,
2188
- /\.DS_Store/,
2189
- /Thumbs\.db/,
2190
- /\.(test|spec)\.(ts|js)$/,
2191
- /\.d\.ts$/,
2192
- /\.map$/,
2193
- /~$/,
2194
- ...options.ignore || []
2195
- ]
2196
- });
2197
- watcher.on("add", (filePath) => {
2198
- const debouncedLoad = createDebouncedCallback(loadAndNotify, filePath);
2199
- debouncedLoad(filePath);
2200
- }).on("change", (filePath) => {
2201
- const debouncedLoad = createDebouncedCallback(loadAndNotify, filePath);
2202
- debouncedLoad(filePath);
2203
- }).on("unlink", (filePath) => {
2204
- const debouncedRemove = createDebouncedCallback(handleRemoved, filePath);
2205
- debouncedRemove(filePath);
2206
- }).on("error", handleError);
2207
- loadInitialRoutes().catch(handleError);
2208
- return {
2209
- close: () => {
2210
- debouncedCallbacks.forEach((timeout) => clearTimeout(timeout));
2211
- debouncedCallbacks.clear();
2212
- return watcher.close();
2213
- },
2214
- getRoutes: () => {
2215
- const allRoutes = [];
2216
- for (const routes of routesByPath.values()) {
2217
- allRoutes.push(...routes);
2218
- }
2219
- return allRoutes;
2220
- },
2221
- getRoutesByFile: () => new Map(routesByPath)
2222
- };
2223
- }
2224
-
2225
- // src/router/validation/schema.ts
2226
- import { z as z6 } from "zod";
2227
-
2228
- // src/router/validation/body.ts
2229
- import { z as z2 } from "zod";
2230
- function validateBody(body, schema) {
2231
- if (schema instanceof z2.ZodObject) {
2232
- return schema.strict().parse(body);
2233
- }
2234
- return schema.parse(body);
2235
- }
2236
-
2237
- // src/router/validation/params.ts
2238
- import { z as z3 } from "zod";
2239
- function validateParams(params, schema) {
2240
- if (schema instanceof z3.ZodObject) {
2241
- return schema.strict().parse(params);
2242
- }
2243
- return schema.parse(params);
2244
- }
2245
-
2246
- // src/router/validation/query.ts
2247
- import { z as z4 } from "zod";
2248
- function validateQuery(query, schema) {
2249
- if (schema instanceof z4.ZodObject) {
2250
- return schema.strict().parse(query);
2251
- }
2252
- return schema.parse(query);
2253
- }
2254
-
2255
- // src/router/validation/response.ts
2256
- import { z as z5 } from "zod";
2257
- function validateResponse(response, schema) {
2258
- if (schema instanceof z5.ZodObject) {
2259
- return schema.strict().parse(response);
2260
- }
2261
- return schema.parse(response);
2262
- }
2263
-
2264
- // src/router/validation/schema.ts
2265
- function createRequestValidator(schema, debug = false) {
2266
- const middlewareFn = async (ctx, next) => {
2267
- if (schema.params && ctx.request.params) {
2268
- try {
2269
- ctx.request.params = validateParams(ctx.request.params, schema.params);
2270
- } catch (error) {
2271
- const fieldErrors = extractZodFieldErrors(error);
2272
- const errorCount = fieldErrors.reduce((sum, fe) => sum + fe.messages.length, 0);
2273
- throw new ValidationError("Request validation failed", {
2274
- fields: fieldErrors,
2275
- errorCount,
2276
- section: "params"
2277
- });
2278
- }
2279
- }
2280
- if (schema.query && ctx.request.query) {
2281
- try {
2282
- ctx.request.query = validateQuery(ctx.request.query, schema.query);
2283
- } catch (error) {
2284
- const fieldErrors = extractZodFieldErrors(error);
2285
- const errorCount = fieldErrors.reduce((sum, fe) => sum + fe.messages.length, 0);
2286
- throw new ValidationError("Request validation failed", {
2287
- fields: fieldErrors,
2288
- errorCount,
2289
- section: "query"
2290
- });
2291
- }
2292
- }
2293
- if (schema.body) {
2294
- try {
2295
- ctx.request.body = validateBody(ctx.request.body, schema.body);
2296
- } catch (error) {
2297
- const fieldErrors = extractZodFieldErrors(error);
2298
- const errorCount = fieldErrors.reduce((sum, fe) => sum + fe.messages.length, 0);
2299
- throw new ValidationError("Request validation failed", {
2300
- fields: fieldErrors,
2301
- errorCount,
2302
- section: "body"
2303
- });
2304
- }
2305
- }
2306
- await next();
2307
- };
2308
- return {
2309
- name: "RequestValidator",
2310
- execute: middlewareFn,
2311
- debug
2312
- };
2313
- }
2314
- function createResponseValidator(responseSchema, debug = false) {
2315
- const middlewareFn = async (ctx, next) => {
2316
- const originalJson = ctx.response.json;
2317
- ctx.response.json = (body, status) => {
2318
- try {
2319
- const validatedBody = validateResponse(body, responseSchema);
2320
- ctx.response.json = originalJson;
2321
- return originalJson.call(ctx.response, validatedBody, status);
2322
- } catch (error) {
2323
- ctx.response.json = originalJson;
2324
- throw new InternalServerError("Response validation failed", {
2325
- responseSchema: responseSchema.description || "Unknown schema",
2326
- validationError: extractZodFieldErrors(error),
2327
- originalResponse: body
2328
- });
2329
- }
2330
- };
2331
- await next();
2332
- };
2333
- return {
2334
- name: "ResponseValidator",
2335
- execute: middlewareFn,
2336
- debug
2337
- };
2338
- }
2339
- function extractZodFieldErrors(error) {
2340
- if (error instanceof z6.ZodError) {
2341
- const fieldErrorMap = /* @__PURE__ */ new Map();
2342
- for (const issue of error.issues) {
2343
- const fieldPath = issue.path.length > 0 ? issue.path.join(".") : "root";
2344
- if (!fieldErrorMap.has(fieldPath)) {
2345
- fieldErrorMap.set(fieldPath, []);
2346
- }
2347
- fieldErrorMap.get(fieldPath).push(issue.message);
2348
- }
2349
- return Array.from(fieldErrorMap.entries()).map(([field, messages]) => ({
2350
- field,
2351
- messages
2352
- }));
2353
- }
2354
- if (error instanceof Error) {
2355
- return [{ field: "unknown", messages: [error.message] }];
2356
- }
2357
- return [{ field: "unknown", messages: [String(error)] }];
2358
- }
2359
-
2360
- // src/router/handlers/executor.ts
2361
- async function executeHandler(ctx, routeOptions, params) {
2362
- const middleware = [...routeOptions.middleware || []];
2363
- if (routeOptions.schema) {
2364
- if (routeOptions.schema.params || routeOptions.schema.query || routeOptions.schema.body) {
2365
- middleware.unshift(createRequestValidator(routeOptions.schema));
2366
- }
2367
- if (routeOptions.schema.response) {
2368
- middleware.push(createResponseValidator(routeOptions.schema.response));
2369
- }
2370
- }
2371
- const handler = compose([...middleware]);
2372
- await handler(ctx, async () => {
2373
- const result = await routeOptions.handler(ctx, params);
2374
- if (!ctx.response.sent && result !== void 0) {
2375
- ctx.response.json(result);
2376
- }
2377
- });
2378
- }
2379
-
2380
- // src/router/matching/params.ts
2381
- function extractParams(path6, pattern, paramNames) {
2382
- const match = pattern.exec(path6);
2383
- if (!match) {
2384
- return {};
2385
- }
2386
- const params = {};
2387
- for (let i = 0; i < paramNames.length; i++) {
2388
- params[paramNames[i]] = match[i + 1] || "";
2389
- }
2390
- return params;
2391
- }
2392
- function compilePathPattern(path6) {
2393
- const paramNames = [];
2394
- if (path6 === "/") {
2395
- return {
2396
- pattern: /^\/$/,
2397
- paramNames: []
2398
- };
2399
- }
2400
- let patternString = path6.replace(/([.+*?^$(){}|\\])/g, "\\$1");
2401
- patternString = patternString.replace(/\/:([^/]+)/g, (_, paramName) => {
2402
- paramNames.push(paramName);
2403
- return "/([^/]+)";
2404
- }).replace(/\/\[([^\]]+)\]/g, (_, paramName) => {
2405
- paramNames.push(paramName);
2406
- return "/([^/]+)";
2407
- });
2408
- patternString = `${patternString}(?:/)?`;
2409
- const pattern = new RegExp(`^${patternString}$`);
2410
- return {
2411
- pattern,
2412
- paramNames
2413
- };
2414
- }
2415
-
2416
- // src/router/matching/matcher.ts
2417
- function createMatcher() {
2418
- const routes = [];
2419
- return {
2420
- /**
2421
- * Add a route to the matcher
2422
- */
2423
- add(path6, method, routeOptions) {
2424
- const { pattern, paramNames } = compilePathPattern(path6);
2425
- const newRoute = {
2426
- path: path6,
2427
- method,
2428
- pattern,
2429
- paramNames,
2430
- routeOptions
2431
- };
2432
- const insertIndex = routes.findIndex((route) => paramNames.length < route.paramNames.length);
2433
- if (insertIndex === -1) {
2434
- routes.push(newRoute);
2435
- } else {
2436
- routes.splice(insertIndex, 0, newRoute);
2437
- }
2438
- },
2439
- /**
2440
- * Remove a route from the matcher by path
2441
- */
2442
- remove(path6) {
2443
- for (let i = routes.length - 1; i >= 0; i--) {
2444
- if (routes[i].path === path6) {
2445
- routes.splice(i, 1);
2446
- }
2447
- }
2448
- },
2449
- /**
2450
- * Clear all routes from the matcher
2451
- */
2452
- clear() {
2453
- routes.length = 0;
2454
- },
2455
- /**
2456
- * Match a URL path to a route
2457
- */
2458
- match(path6, method) {
2459
- const pathname = path6.split("?")[0];
2460
- if (!pathname) return null;
2461
- for (const route of routes) {
2462
- if (route.method !== method) continue;
2463
- const match = route.pattern.exec(pathname);
2464
- if (match) {
2465
- const params = extractParams(path6, route.pattern, route.paramNames);
2466
- return {
2467
- route: route.routeOptions,
2468
- params
2469
- };
2470
- }
2471
- }
2472
- const matchingPath = routes.find(
2473
- (route) => route.method !== method && route.pattern.test(path6)
2474
- );
2475
- if (matchingPath) {
2476
- return {
2477
- route: null,
2478
- params: {},
2479
- methodNotAllowed: true,
2480
- allowedMethods: routes.filter((route) => route.pattern.test(path6)).map((route) => route.method)
2481
- };
2482
- }
2483
- return null;
2484
- },
2485
- /**
2486
- * Get all registered routes
2487
- */
2488
- getRoutes() {
2489
- return routes.map((route) => ({
2490
- path: route.path,
2491
- method: route.method
2492
- }));
2493
- },
2494
- /**
2495
- * Find routes matching a specific path
2496
- */
2497
- findRoutes(path6) {
2498
- return routes.filter((route) => route.pattern.test(path6)).map((route) => ({
2499
- path: route.path,
2500
- method: route.method,
2501
- params: extractParams(path6, route.pattern, route.paramNames)
2502
- }));
2503
- }
2504
- };
2505
- }
2506
-
2507
- // src/router/registry/fast-registry.ts
2508
- function createRouteRegistry() {
2509
- return {
2510
- routesByPath: /* @__PURE__ */ new Map(),
2511
- routesByFile: /* @__PURE__ */ new Map(),
2512
- pathToFile: /* @__PURE__ */ new Map()
2513
- };
2514
- }
2515
- function updateRoutesFromFile(registry, filePath, newRoutes) {
2516
- console.log(`Updating routes from file: ${filePath}`);
2517
- const oldPaths = registry.routesByFile.get(filePath) || /* @__PURE__ */ new Set();
2518
- const newPaths = new Set(newRoutes.map((r) => r.path));
2519
- const added = newRoutes.filter((r) => !oldPaths.has(r.path));
2520
- const removed = Array.from(oldPaths).filter((p) => !newPaths.has(p));
2521
- const potentiallyChanged = newRoutes.filter((r) => oldPaths.has(r.path));
2522
- const changed = potentiallyChanged.filter((route) => {
2523
- const existingRoute = registry.routesByPath.get(route.path);
2524
- return !existingRoute || !routesEqual(existingRoute, route);
2525
- });
2526
- applyRouteUpdates(registry, filePath, { added, removed, changed });
2527
- return { added, removed, changed };
2528
- }
2529
- function getAllRoutesFromRegistry(registry) {
2530
- return Array.from(registry.routesByPath.values());
2531
- }
2532
- function applyRouteUpdates(registry, filePath, updates) {
2533
- const { added, removed, changed } = updates;
2534
- removed.forEach((path6) => {
2535
- registry.routesByPath.delete(path6);
2536
- registry.pathToFile.delete(path6);
2537
- });
2538
- [...added, ...changed].forEach((route) => {
2539
- registry.routesByPath.set(route.path, route);
2540
- registry.pathToFile.set(route.path, filePath);
2541
- });
2542
- const allPathsForFile = /* @__PURE__ */ new Set([
2543
- ...added.map((r) => r.path),
2544
- ...changed.map((r) => r.path),
2545
- ...Array.from(registry.routesByFile.get(filePath) || []).filter((p) => !removed.includes(p))
2546
- ]);
2547
- if (allPathsForFile.size > 0) {
2548
- registry.routesByFile.set(filePath, allPathsForFile);
2549
- } else {
2550
- registry.routesByFile.delete(filePath);
2551
- }
2552
- }
2553
- function routesEqual(route1, route2) {
2554
- if (route1.path !== route2.path) return false;
2555
- const methods1 = Object.keys(route1).filter((k) => k !== "path").sort();
2556
- const methods2 = Object.keys(route2).filter((k) => k !== "path").sort();
2557
- if (methods1.length !== methods2.length) return false;
2558
- return methods1.every((method) => {
2559
- const handler1 = route1[method];
2560
- const handler2 = route2[method];
2561
- return typeof handler1 === typeof handler2;
2562
- });
2563
- }
2564
-
2565
- // src/router/utils/matching-helpers.ts
2566
- function addRouteToMatcher(route, matcher) {
2567
- Object.entries(route).forEach(([method, methodOptions]) => {
2568
- if (method === "path" || !methodOptions) return;
2569
- matcher.add(route.path, method, methodOptions);
2570
- });
2571
- }
2572
- function removeRouteFromMatcher(path6, matcher) {
2573
- if ("remove" in matcher && typeof matcher.remove === "function") {
2574
- matcher.remove(path6);
2575
- } else {
2576
- console.warn("Matcher does not support selective removal, consider adding remove() method");
2577
- }
2578
- }
2579
- function updateRouteInMatcher(route, matcher) {
2580
- removeRouteFromMatcher(route.path, matcher);
2581
- addRouteToMatcher(route, matcher);
2582
- }
2583
-
2584
- // src/router/router.ts
2585
- var DEFAULT_ROUTER_OPTIONS = {
2586
- routesDir: "./routes",
2587
- basePath: "/",
2588
- watchMode: process.env.NODE_ENV === "development"
2589
- };
2590
- function createRouter(options) {
2591
- const routerOptions = {
2592
- ...DEFAULT_ROUTER_OPTIONS,
2593
- ...options
2594
- };
2595
- if (options.basePath && !options.basePath.startsWith("/")) {
2596
- console.warn("Base path does nothing");
2597
- }
2598
- const registry = createRouteRegistry();
2599
- const matcher = createMatcher();
2600
- let initialized = false;
2601
- let initializationPromise = null;
2602
- let _watchers = null;
2603
- const routeDirectories = /* @__PURE__ */ new Set([routerOptions.routesDir]);
2604
- function applyMatcherChanges(changes) {
2605
- console.log("\n\u{1F527} APPLYING MATCHER CHANGES:");
2606
- console.log(` Adding ${changes.added.length} routes`);
2607
- console.log(` Removing ${changes.removed.length} routes`);
2608
- console.log(` Updating ${changes.changed.length} routes`);
2609
- changes.removed.forEach((routePath) => {
2610
- console.log(` \u2796 Removing: ${routePath}`);
2611
- removeRouteFromMatcher(routePath, matcher);
2612
- });
2613
- changes.added.forEach((route) => {
2614
- const methods = Object.keys(route).filter((key) => key !== "path");
2615
- console.log(` \u2795 Adding: ${route.path} [${methods.join(", ")}]`);
2616
- addRouteToMatcher(route, matcher);
2617
- });
2618
- changes.changed.forEach((route) => {
2619
- const methods = Object.keys(route).filter((key) => key !== "path");
2620
- console.log(` \u{1F504} Updating: ${route.path} [${methods.join(", ")}]`);
2621
- updateRouteInMatcher(route, matcher);
2622
- });
2623
- console.log("\u2705 Matcher changes applied\n");
2624
- }
2625
- function addRoutesWithSource(routes, source) {
2626
- try {
2627
- const changes = updateRoutesFromFile(registry, source, routes);
2628
- applyMatcherChanges(changes);
2629
- return changes;
2630
- } catch (error) {
2631
- console.error(`\u26A0\uFE0F Route conflicts from ${source}:`, error);
2632
- throw error;
2633
- }
2634
- }
2635
- async function loadRoutesFromDirectory(directory, source, prefix) {
2636
- try {
2637
- const discoveredRoutes = await loadInitialRoutesParallel(directory);
2638
- const finalRoutes = discoveredRoutes.map(
2639
- (route) => prefix ? { ...route, path: `${prefix}${route.path}` } : route
2640
- );
2641
- const changes = addRoutesWithSource(finalRoutes, source);
2642
- console.log(
2643
- `Loaded ${discoveredRoutes.length} routes from ${source}${prefix ? ` with prefix ${prefix}` : ""} (${changes.added.length} added, ${changes.changed.length} changed, ${changes.removed.length} removed)`
2644
- );
2645
- } catch (error) {
2646
- console.error(`\u26A0\uFE0F Failed to load routes from ${source}:`, error);
2647
- throw error;
2648
- }
2649
- }
2650
- async function initialize() {
2651
- if (initialized || initializationPromise) {
2652
- return initializationPromise;
2653
- }
2654
- initializationPromise = (async () => {
2655
- try {
2656
- await Promise.all(
2657
- Array.from(routeDirectories).map(
2658
- (directory) => loadRoutesFromDirectory(directory, directory)
2659
- )
2660
- );
2661
- if (routerOptions.watchMode) {
2662
- setupOptimizedWatching();
2663
- }
2664
- initialized = true;
2665
- } catch (error) {
2666
- console.error("\u26A0\uFE0F Failed to initialize router:", error);
2667
- throw error;
2668
- }
2669
- })();
2670
- return initializationPromise;
2671
- }
2672
- function setupOptimizedWatching() {
2673
- if (!_watchers) {
2674
- _watchers = /* @__PURE__ */ new Map();
2675
- }
2676
- for (const directory of routeDirectories) {
2677
- if (!_watchers.has(directory)) {
2678
- const watcher = watchRoutes(directory, {
2679
- debounceMs: 16,
2680
- // ~60fps debouncing
2681
- ignore: ["node_modules", ".git"],
2682
- onRouteAdded: (filepath, addedRoutes) => {
2683
- try {
2684
- const changes = updateRoutesFromFile(registry, filepath, addedRoutes);
2685
- applyMatcherChanges(changes);
2686
- } catch (error) {
2687
- console.error(`Error adding routes from ${directory}:`, error);
2688
- }
2689
- },
2690
- onRouteChanged: withPerformanceTracking(
2691
- async (filepath, changedRoutes) => {
2692
- try {
2693
- console.log(`Processing changes for ${filepath}`);
2694
- const changes = updateRoutesFromFile(registry, filepath, changedRoutes);
2695
- console.log(
2696
- `Changes detected: ${changes.added.length} added, ${changes.changed.length} changed, ${changes.removed.length} removed`
2697
- );
2698
- applyMatcherChanges(changes);
2699
- console.log(
2700
- `Route changes applied: ${changes.added.length} added, ${changes.changed.length} changed, ${changes.removed.length} removed`
2701
- );
2702
- } catch (error) {
2703
- console.error(`\u26A0\uFE0F Error updating routes from ${directory}:`, error);
2704
- }
2705
- },
2706
- directory
2707
- ),
2708
- onRouteRemoved: (filePath, removedRoutes) => {
2709
- console.log(`File removed: ${filePath} with ${removedRoutes.length} routes`);
2710
- try {
2711
- removedRoutes.forEach((route) => {
2712
- removeRouteFromMatcher(route.path, matcher);
2713
- });
2714
- clearFileCache(filePath);
2715
- } catch (error) {
2716
- console.error(`\u26A0\uFE0F Error removing routes from ${filePath}:`, error);
2717
- }
2718
- },
2719
- onError: (error) => {
2720
- console.error(`\u26A0\uFE0F Route watcher error for ${directory}:`, error);
2721
- }
2722
- });
2723
- _watchers.set(directory, watcher);
2724
- }
2725
- }
2726
- }
2727
- function setupWatcherForNewDirectory(directory, prefix) {
2728
- if (!_watchers) {
2729
- _watchers = /* @__PURE__ */ new Map();
2730
- }
2731
- const watcher = watchRoutes(directory, {
2732
- debounceMs: 16,
2733
- ignore: ["node_modules", ".git"],
2734
- onRouteAdded: (filePath, addedRoutes) => {
2735
- try {
2736
- const finalRoutes = addedRoutes.map(
2737
- (route) => prefix ? { ...route, path: `${prefix}${route.path}` } : route
2738
- );
2739
- const changes = updateRoutesFromFile(registry, filePath, finalRoutes);
2740
- applyMatcherChanges(changes);
2741
- } catch (error) {
2742
- console.error(`\u26A0\uFE0F Error adding routes from ${directory}:`, error);
2743
- }
2744
- },
2745
- onRouteChanged: withPerformanceTracking(async (filePath, changedRoutes) => {
2746
- try {
2747
- const finalRoutes = changedRoutes.map(
2748
- (route) => prefix ? { ...route, path: `${prefix}${route.path}` } : route
2749
- );
2750
- const changes = updateRoutesFromFile(registry, filePath, finalRoutes);
2751
- applyMatcherChanges(changes);
2752
- } catch (error) {
2753
- console.error(`\u26A0\uFE0F Error updating routes from ${directory}:`, error);
2754
- }
2755
- }, directory),
2756
- onRouteRemoved: (filePath, removedRoutes) => {
2757
- try {
2758
- removedRoutes.forEach((route) => {
2759
- const finalPath = prefix ? `${prefix}${route.path}` : route.path;
2760
- removeRouteFromMatcher(finalPath, matcher);
2761
- });
2762
- clearFileCache(filePath);
2763
- } catch (error) {
2764
- console.error(`Error removing routes from ${filePath}:`, error);
2765
- }
2766
- },
2767
- onError: (error) => {
2768
- console.error(`\u26A0\uFE0F Route watcher error for ${directory}:`, error);
2769
- }
2770
- });
2771
- _watchers.set(directory, watcher);
2772
- return watcher;
2773
- }
2774
- initialize().catch((error) => {
2775
- console.error("\u26A0\uFE0F Failed to initialize router on creation:", error);
2776
- });
2777
- return {
2778
- /**
2779
- * Handle an incoming request
2780
- */
2781
- async handleRequest(ctx) {
2782
- if (!initialized) {
2783
- console.log("\u{1F504} Router not initialized, initializing...");
2784
- await initialize();
2785
- }
2786
- const { method, path: path6 } = ctx.request;
2787
- console.log(`
2788
- \u{1F4E5} Handling request: ${method} ${path6}`);
2789
- const match = matcher.match(path6, method);
2790
- if (!match) {
2791
- console.log(`\u274C No match found for: ${method} ${path6}`);
2792
- throw new NotFoundError("Not found");
2793
- }
2794
- console.log(`\u2705 Route matched: ${method} ${path6}`);
2795
- console.log(` Params: ${JSON.stringify(match.params)}`);
2796
- if (match.methodNotAllowed) {
2797
- ctx.response.status(405).json({
2798
- error: "\u274C Method Not Allowed",
2799
- allowed: match.allowedMethods
2800
- });
2801
- if (match.allowedMethods && match.allowedMethods.length > 0) {
2802
- ctx.response.header("Allow", match.allowedMethods.join(", "));
2803
- }
2804
- return;
2805
- }
2806
- ctx.request.params = match.params;
2807
- await executeHandler(ctx, match.route, match.params);
2808
- },
2809
- /**
2810
- * Get all registered routes (using optimized registry)
2811
- */
2812
- getRoutes() {
2813
- return getAllRoutesFromRegistry(registry);
2814
- },
2815
- /**
2816
- * Add a route programmatically
2817
- */
2818
- addRoute(route) {
2819
- const changes = updateRoutesFromFile(registry, "programmatic", [route]);
2820
- applyMatcherChanges(changes);
2821
- },
2822
- /**
2823
- * Add multiple routes programmatically with batch processing
2824
- */
2825
- addRoutes(routes) {
2826
- const changes = updateRoutesFromFile(registry, "programmatic", routes);
2827
- applyMatcherChanges(changes);
2828
- return changes;
2829
- },
2830
- /**
2831
- * Add a route directory (for plugins) with optimized loading
2832
- */
2833
- async addRouteDirectory(directory, options2 = {}) {
2834
- if (routeDirectories.has(directory)) {
2835
- console.warn(`Route directory ${directory} already registered`);
2836
- return;
2837
- }
2838
- routeDirectories.add(directory);
2839
- if (initialized) {
2840
- await loadRoutesFromDirectory(directory, directory, options2.prefix);
2841
- if (routerOptions.watchMode) {
2842
- setupWatcherForNewDirectory(directory, options2.prefix);
2843
- }
2844
- }
2845
- },
2846
- /**
2847
- * Get route conflicts (using registry)
2848
- */
2849
- getRouteConflicts() {
2850
- const conflicts = [];
2851
- return conflicts;
2852
- },
2853
- /**
2854
- * Close watchers and cleanup (useful for testing)
2855
- */
2856
- async close() {
2857
- if (_watchers) {
2858
- for (const watcher of _watchers.values()) {
2859
- await watcher.close();
2860
- }
2861
- _watchers.clear();
2862
- }
2863
- }
2864
- };
2865
- }
2866
-
2867
- // src/server/create.ts
2868
- var DEFAULT_OPTIONS2 = {
2869
- port: 3e3,
2870
- host: "localhost",
2871
- routesDir: "./routes",
2872
- http2: {
2873
- enabled: true
2874
- },
2875
- middleware: [],
2876
- plugins: []
2877
- };
2878
- function createServerOptions(options = {}) {
2879
- const baseOptions = { ...DEFAULT_OPTIONS2 };
2880
- setRuntimeConfig({ routesDir: options.routesDir || baseOptions.routesDir });
2881
- return {
2882
- port: options.port ?? baseOptions.port,
2883
- host: options.host ?? baseOptions.host,
2884
- routesDir: options.routesDir ?? baseOptions.routesDir,
2885
- http2: {
2886
- enabled: options.http2?.enabled ?? baseOptions.http2?.enabled,
2887
- keyFile: options.http2?.keyFile ?? baseOptions.http2?.keyFile,
2888
- certFile: options.http2?.certFile ?? baseOptions.http2?.certFile
2889
- },
2890
- middleware: [...baseOptions.middleware || [], ...options.middleware || []],
2891
- plugins: [...baseOptions.plugins || [], ...options.plugins || []]
2892
- };
2893
- }
2894
- function createListenMethod(serverInstance, validatedOptions, initialMiddleware, initialPlugins) {
2895
- return async () => {
2896
- await initializeComponents(serverInstance, initialMiddleware, initialPlugins);
2897
- await serverInstance.pluginManager.initializePlugins(serverInstance);
2898
- await startServer(serverInstance, validatedOptions);
2899
- await serverInstance.pluginManager.onServerStart(serverInstance, serverInstance.server);
2900
- setupServerLifecycle(serverInstance);
2901
- return serverInstance;
2902
- };
2903
- }
2904
- async function initializeComponents(serverInstance, initialMiddleware, initialPlugins) {
2905
- for (const mw of initialMiddleware) {
2906
- serverInstance.use(mw);
2907
- }
2908
- for (const p of initialPlugins) {
2909
- await serverInstance.register(p);
2910
- }
2911
- }
2912
- function setupServerLifecycle(serverInstance) {
2913
- const signalHandlers = registerSignalHandlers(() => serverInstance.close());
2914
- serverInstance._signalHandlers = signalHandlers;
2915
- serverInstance.events.emit("started");
2916
- }
2917
- function createCloseMethod(serverInstance) {
2918
- return async (stopOptions) => {
2919
- if (!serverInstance.server) {
2920
- return;
2921
- }
2922
- const options = { ...stopOptions };
2923
- if (serverInstance._signalHandlers) {
2924
- serverInstance._signalHandlers.unregister();
2925
- delete serverInstance._signalHandlers;
2926
- }
2927
- await stopServer(serverInstance, options);
2928
- };
2929
- }
2930
- function createUseMethod(serverInstance) {
2931
- return (middleware) => {
2932
- const middlewareArray = Array.isArray(middleware) ? middleware : [middleware];
2933
- serverInstance.middleware.push(...middlewareArray);
2934
- return serverInstance;
2935
- };
2936
- }
2937
- function createRegisterMethod(serverInstance) {
2938
- return async (plugin) => {
2939
- validatePlugin(plugin);
2940
- serverInstance.plugins.push(plugin);
2941
- await plugin.register(serverInstance);
2942
- return serverInstance;
2943
- };
2944
- }
2945
- function create3(options = {}) {
2946
- const mergedOptions = createServerOptions(options);
2947
- let validatedOptions;
2948
- try {
2949
- validatedOptions = validateServerOptions(mergedOptions);
2950
- } catch (error) {
2951
- if (error instanceof Error) {
2952
- throw new Error(`Failed to create server: ${error.message}`);
2953
- }
2954
- throw new Error(`Failed to create server: ${String(error)}`);
2955
- }
2956
- const { port, host, middleware, plugins } = validatedOptions;
2957
- const initialMiddleware = Array.isArray(middleware) ? [...middleware] : [];
2958
- const initialPlugins = Array.isArray(plugins) ? [...plugins] : [];
2959
- const contextStorage2 = new AsyncLocalStorage2();
2960
- const router = createRouter({
2961
- routesDir: validatedOptions.routesDir,
2962
- watchMode: process.env.NODE_ENV === "development"
2963
- });
2964
- const pluginManager = createPluginLifecycleManager({
2965
- debug: process.env.NODE_ENV === "development",
2966
- continueOnError: true
2967
- });
2968
- const events = new EventEmitter();
2969
- const serverInstance = {
2970
- server: null,
2971
- port,
2972
- host,
2973
- context: contextStorage2,
2974
- events,
2975
- plugins: [],
2976
- middleware: [],
2977
- _signalHandlers: { unregister: () => {
2978
- } },
2979
- use: () => serverInstance,
2980
- register: async () => serverInstance,
2981
- listen: async () => serverInstance,
2982
- close: async () => {
2983
- },
2984
- router,
2985
- pluginManager
2986
- };
2987
- serverInstance.listen = createListenMethod(
2988
- serverInstance,
2989
- validatedOptions,
2990
- initialMiddleware,
2991
- initialPlugins
2992
- );
2993
- serverInstance.close = createCloseMethod(serverInstance);
2994
- serverInstance.use = createUseMethod(serverInstance);
2995
- serverInstance.register = createRegisterMethod(serverInstance);
2996
- return serverInstance;
2997
- }
2998
-
2999
- // src/errors/unauthorized-error.ts
3000
- var UnauthorizedError = class extends BlaizeError {
3001
- /**
3002
- * Creates a new UnauthorizedError instance
3003
- *
3004
- * @param title - Human-readable error message
3005
- * @param details - Optional authentication context
3006
- * @param correlationId - Optional correlation ID (uses current context if not provided)
3007
- */
3008
- constructor(title, details = void 0, correlationId = void 0) {
3009
- super(
3010
- "UNAUTHORIZED" /* UNAUTHORIZED */,
3011
- title,
3012
- 401,
3013
- // HTTP 401 Unauthorized
3014
- correlationId ?? getCurrentCorrelationId(),
3015
- details
3016
- );
3017
- }
3018
- };
3019
-
3020
- // src/errors/forbidden-error.ts
3021
- var ForbiddenError = class extends BlaizeError {
3022
- /**
3023
- * Creates a new ForbiddenError instance
3024
- *
3025
- * @param title - Human-readable error message
3026
- * @param details - Optional permission context
3027
- * @param correlationId - Optional correlation ID (uses current context if not provided)
3028
- */
3029
- constructor(title, details = void 0, correlationId = void 0) {
3030
- super(
3031
- "FORBIDDEN" /* FORBIDDEN */,
3032
- title,
3033
- 403,
3034
- // HTTP 403 Forbidden
3035
- correlationId ?? getCurrentCorrelationId(),
3036
- details
3037
- );
3038
- }
3039
- };
3040
-
3041
- // src/errors/conflict-error.ts
3042
- var ConflictError = class extends BlaizeError {
3043
- /**
3044
- * Creates a new ConflictError instance
3045
- *
3046
- * @param title - Human-readable error message
3047
- * @param details - Optional conflict context
3048
- * @param correlationId - Optional correlation ID (uses current context if not provided)
3049
- */
3050
- constructor(title, details = void 0, correlationId = void 0) {
3051
- super(
3052
- "CONFLICT" /* CONFLICT */,
3053
- title,
3054
- 409,
3055
- // HTTP 409 Conflict
3056
- correlationId ?? getCurrentCorrelationId(),
3057
- details
3058
- );
3059
- }
3060
- };
3061
-
3062
- // src/errors/rate-limit-error.ts
3063
- var RateLimitError = class extends BlaizeError {
3064
- /**
3065
- * Creates a new RateLimitError instance
3066
- *
3067
- * @param title - Human-readable error message
3068
- * @param details - Optional rate limit context
3069
- * @param correlationId - Optional correlation ID (uses current context if not provided)
3070
- */
3071
- constructor(title, details = void 0, correlationId = void 0) {
3072
- super(
3073
- "RATE_LIMITED" /* RATE_LIMITED */,
3074
- title,
3075
- 429,
3076
- // HTTP 429 Too Many Requests
3077
- correlationId ?? getCurrentCorrelationId(),
3078
- details
3079
- );
3080
- }
3081
- };
3082
-
3083
- // src/errors/request-timeout-error.ts
3084
- var RequestTimeoutError = class extends BlaizeError {
3085
- constructor(title, details, correlationId) {
3086
- super(
3087
- "UPLOAD_TIMEOUT" /* UPLOAD_TIMEOUT */,
3088
- title,
3089
- 408,
3090
- correlationId ?? getCurrentCorrelationId(),
3091
- details
3092
- );
3093
- }
3094
- };
3095
-
3096
- // src/errors/unprocessable-entity-error.ts
3097
- var UnprocessableEntityError = class extends BlaizeError {
3098
- constructor(title, details, correlationId) {
3099
- super(
3100
- "UNPROCESSABLE_ENTITY" /* UNPROCESSABLE_ENTITY */,
3101
- title,
3102
- 422,
3103
- correlationId ?? getCurrentCorrelationId(),
3104
- details
3105
- );
3106
- }
3107
- };
3108
-
3109
- // src/index.ts
3110
- var VERSION = "0.1.0";
3111
- var ServerAPI = { createServer: create3 };
3112
- var RouterAPI = {
3113
- createDeleteRoute,
3114
- createGetRoute,
3115
- createHeadRoute,
3116
- createOptionsRoute,
3117
- createPatchRoute,
3118
- createPostRoute,
3119
- createPutRoute
3120
- };
3121
- var MiddlewareAPI = { createMiddleware: create, compose };
3122
- var PluginsAPI = { createPlugin: create2 };
3123
- var Blaize = {
3124
- // Core functions
3125
- createServer: create3,
3126
- createMiddleware: create,
3127
- createPlugin: create2,
3128
- // Namespaces (using the non-conflicting names)
3129
- Server: ServerAPI,
3130
- Router: RouterAPI,
3131
- Middleware: MiddlewareAPI,
3132
- Plugins: PluginsAPI,
3133
- // Constants
3134
- VERSION
3135
- };
3136
- var index_default = Blaize;
3137
- export {
3138
- Blaize,
3139
- BlaizeError,
3140
- ConflictError,
3141
- ErrorSeverity,
3142
- ErrorType,
3143
- ForbiddenError,
3144
- InternalServerError,
3145
- MiddlewareAPI,
3146
- NotFoundError,
3147
- PayloadTooLargeError,
3148
- PluginsAPI,
3149
- RateLimitError,
3150
- RequestTimeoutError,
3151
- RouterAPI,
3152
- ServerAPI,
3153
- UnauthorizedError,
3154
- UnprocessableEntityError,
3155
- UnsupportedMediaTypeError,
3156
- VERSION,
3157
- ValidationError,
3158
- compose,
3159
- createDeleteRoute,
3160
- createGetRoute,
3161
- createHeadRoute,
3162
- create as createMiddleware,
3163
- createOptionsRoute,
3164
- createPatchRoute,
3165
- create2 as createPlugin,
3166
- createPostRoute,
3167
- createPutRoute,
3168
- create3 as createServer,
3169
- index_default as default,
3170
- isBodyParseError
3171
- };
27
+ `),n()}),e.on("error",s=>{console.error("Server error:",s),i(s)})})}async function Fr(e){for(let t of e.plugins)typeof t.initialize=="function"&&await t.initialize(e)}async function at(e,t){if(!e.server)try{let r=t.port,o=t.host;await Fr(e);let n=t.http2||{enabled:!0},i=!!n.enabled,s=await vr(n);t.http2&&s.keyFile&&s.certFile&&(t.http2.keyFile=s.keyFile,t.http2.certFile=s.certFile);let a=Er(i,s);e.server=a,e.port=r,e.host=o;let u=nt(e);a.on("request",u),await Cr(a,r,o,i)}catch(r){throw console.error("Failed to start server:",r),r}}var X=!1;async function ut(e,t={}){let r=e.server,o=e.events;if(X){console.log("\u26A0\uFE0F Shutdown already in progress, ignoring duplicate shutdown request");return}if(!r)return;X=!0;let n=t.timeout||5e3;try{if(t.onStopping&&await t.onStopping(),o.emit("stopping"),e.router&&typeof e.router.close=="function"){console.log("\u{1F50C} Closing router watchers...");try{await Promise.race([e.router.close(),new Promise((a,u)=>setTimeout(()=>u(new Error("Router close timeout")),2e3))]),console.log("\u2705 Router watchers closed")}catch(a){console.error("\u274C Error closing router watchers:",a)}}try{await Promise.race([e.pluginManager.onServerStop(e,r),new Promise((a,u)=>setTimeout(()=>u(new Error("Plugin stop timeout")),2e3))])}catch(a){console.error("\u274C Plugin stop timeout:",a)}let i=new Promise((a,u)=>{r.close(c=>{if(c)return u(c);a()})}),s=new Promise((a,u)=>{setTimeout(()=>{u(new Error("Server shutdown timeout"))},n)});await Promise.race([i,s]);try{await Promise.race([e.pluginManager.terminatePlugins(e),new Promise((a,u)=>setTimeout(()=>u(new Error("Plugin terminate timeout")),1e3))])}catch(a){console.error("\u274C Plugin terminate timeout:",a)}t.onStopped&&await t.onStopped(),o.emit("stopped"),e.server=null,console.log("\u2705 Graceful shutdown completed"),X=!1}catch(i){throw X=!1,console.error("\u26A0\uFE0F Shutdown error (forcing exit):",i),r&&typeof r.close=="function"&&r.close(),process.env.NODE_ENV==="development"&&(console.log("\u{1F504} Forcing exit for development restart..."),process.exit(0)),o.emit("error",i),i}}function lt(e){if(process.env.NODE_ENV==="development"){let r=()=>{console.log("\u{1F4E4} SIGINT received, forcing exit for development restart..."),process.exit(0)},o=()=>{console.log("\u{1F4E4} SIGTERM received, forcing exit for development restart..."),process.exit(0)};return process.on("SIGINT",r),process.on("SIGTERM",o),{unregister:()=>{process.removeListener("SIGINT",r),process.removeListener("SIGTERM",o)}}}else{let r=()=>{console.log("\u{1F4E4} SIGINT received, starting graceful shutdown..."),e().catch(console.error)},o=()=>{console.log("\u{1F4E4} SIGTERM received, starting graceful shutdown..."),e().catch(console.error)};return process.on("SIGINT",r),process.on("SIGTERM",o),{unregister:()=>{process.removeListener("SIGINT",r),process.removeListener("SIGTERM",o)}}}}import{z as b}from"zod";var Mr=b.custom(e=>e!==null&&typeof e=="object"&&"execute"in e&&typeof e.execute=="function",{message:"Expected middleware to have an execute function"}),zr=b.custom(e=>e!==null&&typeof e=="object"&&"register"in e&&typeof e.register=="function",{message:"Expected a valid plugin object with a register method"}),Br=b.object({enabled:b.boolean().optional().default(!0),keyFile:b.string().optional(),certFile:b.string().optional()}).refine(e=>e.enabled&&process.env.NODE_ENV==="production"?e.keyFile&&e.certFile:!0,{message:"When HTTP/2 is enabled (outside of development mode), both keyFile and certFile must be provided"}),$r=b.object({port:b.number().int().positive().optional().default(3e3),host:b.string().optional().default("localhost"),routesDir:b.string().optional().default("./routes"),http2:Br.optional().default({enabled:!0}),middleware:b.array(Mr).optional().default([]),plugins:b.array(zr).optional().default([])});function ct(e){try{return $r.parse(e)}catch(t){if(t instanceof b.ZodError){let r=t.format();throw new Error(`Invalid server options: ${JSON.stringify(r,null,2)}`)}throw new Error(`Invalid server options: ${String(t)}`)}}function dt(e={}){let{continueOnError:t=!0,debug:r=!1,onError:o}=e;function n(s,...a){r&&console.log(`[PluginLifecycle] ${s}`,...a)}function i(s,a,u){let c=`Plugin ${s.name} failed during ${a}: ${u.message}`;if(o?o(s,a,u):console.error(c,u),!t)throw new Error(c)}return{async initializePlugins(s){n("Initializing plugins...");for(let a of s.plugins)if(a.initialize)try{n(`Initializing plugin: ${a.name}`),await a.initialize(s)}catch(u){i(a,"initialize",u)}n(`Initialized ${s.plugins.length} plugins`)},async terminatePlugins(s){n("Terminating plugins...");let a=[...s.plugins].reverse();for(let u of a)if(u.terminate)try{n(`Terminating plugin: ${u.name}`),await u.terminate(s)}catch(c){i(u,"terminate",c)}n(`Terminated ${a.length} plugins`)},async onServerStart(s,a){n("Notifying plugins of server start...");for(let u of s.plugins)if(u.onServerStart)try{n(`Notifying plugin of server start: ${u.name}`),await u.onServerStart(a)}catch(c){i(u,"onServerStart",c)}},async onServerStop(s,a){n("Notifying plugins of server stop...");let u=[...s.plugins].reverse();for(let c of u)if(c.onServerStop)try{n(`Notifying plugin of server stop: ${c.name}`),await c.onServerStop(a)}catch(w){i(c,"onServerStop",w)}}}}var P=class extends Error{constructor(r,o){super(`Plugin validation error${r?` for "${r}"`:""}: ${o}`);this.pluginName=r;this.name="PluginValidationError"}};var Or=new Set(["core","server","router","middleware","context","blaize","blaizejs"]),kr=/^[a-z]([a-z0-9-]*[a-z0-9])?$/,Dr=/^\d+\.\d+\.\d+(?:-[a-zA-Z0-9-.]+)?(?:\+[a-zA-Z0-9-.]+)?$/;function pt(e,t={}){let{requireVersion:r=!0,validateNameFormat:o=!0,checkReservedNames:n=!0}=t;if(!e||typeof e!="object")throw new P("","Plugin must be an object");let i=e;if(!i.name||typeof i.name!="string")throw new P("","Plugin must have a name (string)");if(o&&!kr.test(i.name))throw new P(i.name,"Plugin name must be lowercase letters, numbers, and hyphens only");if(n&&Or.has(i.name.toLowerCase()))throw new P(i.name,`Plugin name "${i.name}" is reserved`);if(r){if(!i.version||typeof i.version!="string")throw new P(i.name,"Plugin must have a version (string)");if(!Dr.test(i.version))throw new P(i.name,'Plugin version must follow semantic versioning (e.g., "1.0.0")')}if(!i.register||typeof i.register!="function")throw new P(i.name,"Plugin must have a register method (function)");let s=["initialize","terminate","onServerStart","onServerStop"];for(let a of s)if(i[a]&&typeof i[a]!="function")throw new P(i.name,`Plugin ${a} must be a function if provided`)}import*as mt from"node:crypto";import*as gt from"node:fs/promises";import{createRequire as Hr}from"node:module";import*as yt from"node:path";async function Nr(e){let t=`?t=${Date.now()}`,r=e+t;try{let o=await import(r);return console.log("\u2705 Successfully imported module"),o}catch(o){let n=o instanceof Error?o.message:String(o);return console.log("\u26A0\uFE0F Error importing with cache buster, trying original path:",n),import(e)}}async function ft(e,t){try{let r=W(e,t),o=await Nr(e);console.log("\u{1F4E6} Module exports:",Object.keys(o));let n=[];if(o.default&&typeof o.default=="object"){let i={...o.default,path:r.routePath};n.push(i)}return Object.entries(o).forEach(([i,s])=>{if(i==="default"||!s||typeof s!="object")return;let a=s;if(Ar(a)){let u={...a,path:r.routePath};n.push(u)}}),n.length===0?(console.warn(`Route file ${e} does not export any valid route definitions`),[]):(console.log(`\u2705 Successfully Loaded ${n.length} route(s)`),n)}catch(r){return console.error(`Failed to load route module ${e}:`,r),[]}}function Ar(e){return!e||typeof e!="object"?!1:["GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS"].some(o=>e[o]&&typeof e[o]=="object"&&e[o].handler)}var j=new Map;async function _(e,t,r=!0){let n=(await gt.stat(e)).mtime.getTime(),i=j.get(e);if(r&&i&&i.timestamp===n)return i.routes;Ur(e);let s=await ft(e,t);if(r){let a=wt(s);j.set(e,{routes:s,timestamp:n,hash:a})}return s}function ht(e,t){let r=j.get(e);if(!r)return!0;let o=wt(t);return r.hash!==o}function ue(e){e?j.delete(e):j.clear()}function wt(e){let t=e.map(n=>({path:n.path,methods:Object.keys(n).filter(i=>i!=="path").sort().map(i=>{let s=n[i],a=s?.handler?s.handler.toString():null;return{method:i,handler:a,middleware:s?.middleware?s.middleware.length:0,hasSchema:!!s?.schema,schemaKeys:s?.schema?Object.keys(s.schema).sort():[]}})})),r=JSON.stringify(t);return mt.createHash("md5").update(r).digest("hex")}function Ur(e){try{let t=yt.resolve(e);if(typeof A<"u"){delete A.cache[t];try{let r=A.resolve(t);delete A.cache[r]}catch(r){let o=r instanceof Error?r.message:String(r);console.log(`\u26A0\uFE0F Could not resolve path: ${o}`)}}else try{let r=Hr(import.meta.url);delete r.cache[t];try{let o=r.resolve(t);delete r.cache[o]}catch{console.log("\u26A0\uFE0F Could not resolve ESM path")}}catch{console.log("\u26A0\uFE0F createRequire not available in pure ESM")}}catch(t){console.log(`\u26A0\uFE0F Error during module cache invalidation for ${e}:`,t)}}import*as Rt from"node:os";import*as K from"node:fs/promises";import*as N from"node:path";async function ee(e,t={}){let r=N.isAbsolute(e)?e:N.resolve(process.cwd(),e);console.log("Creating router with routes directory:",r);try{if(!(await K.stat(r)).isDirectory())throw new Error(`Route directory is not a directory: ${r}`)}catch(s){throw s.code==="ENOENT"?new Error(`Route directory not found: ${r}`):s}let o=[],n=t.ignore||["node_modules",".git"];async function i(s){let a=await K.readdir(s,{withFileTypes:!0});for(let u of a){let c=N.join(s,u.name);u.isDirectory()&&n.includes(u.name)||(u.isDirectory()?await i(c):qr(u.name)&&o.push(c))}}return await i(r),o}function qr(e){return!e.startsWith("_")&&(e.endsWith(".ts")||e.endsWith(".js"))}async function Ir(e,t,r=Math.max(1,Math.floor(Rt.cpus().length/2))){let o=Lr(e,r),n=[];for(let i of o){let a=(await Promise.allSettled(i.map(u=>t(u)))).filter(u=>u.status==="fulfilled").map(u=>u.value);n.push(...a)}return n}async function St(e){let t=await ee(e);return(await Ir(t,o=>_(o,e))).flat()}function Lr(e,t){let r=[];for(let o=0;o<e.length;o+=t)r.push(e.slice(o,o+t));return r}var F={fileChanges:0,totalReloadTime:0,averageReloadTime:0,slowReloads:[]};function xt(e,t){let r=Date.now()-t;if(F.fileChanges++,F.totalReloadTime+=r,F.averageReloadTime=F.totalReloadTime/F.fileChanges,r>100&&(F.slowReloads.push({file:e,time:r}),F.slowReloads.length>10&&F.slowReloads.shift()),process.env.NODE_ENV==="development"){let o=r<50?"\u26A1":r<100?"\u{1F504}":"\u{1F40C}";console.log(`${o} Route reload: ${e} (${r}ms)`)}}function le(e,t){return console.log(`Tracking performance for: ${t}`),async(...r)=>{let o=Date.now();try{let n=await e(...r);return xt(t,o),n}catch(n){throw xt(t,o),n}}}import*as ce from"node:path";import{watch as jr}from"chokidar";function de(e,t={}){let r=t.debounceMs||16,o=new Map;function n(d,g){return(...y)=>{let l=o.get(g);l&&clearTimeout(l);let p=setTimeout(()=>{d(...y),o.delete(g)},r);o.set(g,p)}}let i=new Map;async function s(){try{let d=await ee(e,{ignore:t.ignore});for(let g of d)await a(g)}catch(d){c(d)}}async function a(d){try{let g=i.get(d),y=await _(d,e,!1);if(!y||y.length===0||g&&!ht(d,y))return;await _(d,e,!0);let l=ce.normalize(d);g?(i.set(d,y),t.onRouteChanged&&t.onRouteChanged(l,y)):(i.set(d,y),t.onRouteAdded&&t.onRouteAdded(l,y))}catch(g){console.log(`\u26A0\uFE0F Error processing file ${d}:`,g),c(g)}}function u(d){let g=ce.normalize(d),y=i.get(g);y&&y.length>0&&t.onRouteRemoved&&t.onRouteRemoved(g,y),i.delete(g)}function c(d){t.onError&&d instanceof Error?t.onError(d):console.error("\u26A0\uFE0F Route watcher error:",d)}let w=jr(e,{awaitWriteFinish:{stabilityThreshold:50,pollInterval:10},usePolling:!1,atomic:!0,followSymlinks:!1,depth:10,ignored:[/(^|[/\\])\../,/node_modules/,/\.git/,/\.DS_Store/,/Thumbs\.db/,/\.(test|spec)\.(ts|js)$/,/\.d\.ts$/,/\.map$/,/~$/,...t.ignore||[]]});return w.on("add",d=>{n(a,d)(d)}).on("change",d=>{n(a,d)(d)}).on("unlink",d=>{n(u,d)(d)}).on("error",c),s().catch(c),{close:()=>(o.forEach(d=>clearTimeout(d)),o.clear(),w.close()),getRoutes:()=>{let d=[];for(let g of i.values())d.push(...g);return d},getRoutesByFile:()=>new Map(i)}}import{z as Gr}from"zod";import{z as _r}from"zod";function pe(e,t){return t instanceof _r.ZodObject?t.strict().parse(e):t.parse(e)}import{z as Qr}from"zod";function fe(e,t){return t instanceof Qr.ZodObject?t.strict().parse(e):t.parse(e)}import{z as Vr}from"zod";function me(e,t){return t instanceof Vr.ZodObject?t.strict().parse(e):t.parse(e)}import{z as Wr}from"zod";function ge(e,t){return t instanceof Wr.ZodObject?t.strict().parse(e):t.parse(e)}function ye(e,t=!1){return{name:"RequestValidator",execute:async(o,n)=>{if(e.params&&o.request.params)try{o.request.params=fe(o.request.params,e.params)}catch(i){let s=te(i),a=s.reduce((u,c)=>u+c.messages.length,0);throw new U("Request validation failed",{fields:s,errorCount:a,section:"params"})}if(e.query&&o.request.query)try{o.request.query=me(o.request.query,e.query)}catch(i){let s=te(i),a=s.reduce((u,c)=>u+c.messages.length,0);throw new U("Request validation failed",{fields:s,errorCount:a,section:"query"})}if(e.body)try{o.request.body=pe(o.request.body,e.body)}catch(i){let s=te(i),a=s.reduce((u,c)=>u+c.messages.length,0);throw new U("Request validation failed",{fields:s,errorCount:a,section:"body"})}await n()},debug:t}}function he(e,t=!1){return{name:"ResponseValidator",execute:async(o,n)=>{let i=o.response.json;o.response.json=(s,a)=>{try{let u=ge(s,e);return o.response.json=i,i.call(o.response,u,a)}catch(u){throw o.response.json=i,new H("Response validation failed",{responseSchema:e.description||"Unknown schema",validationError:te(u),originalResponse:s})}},await n()},debug:t}}function te(e){if(e instanceof Gr.ZodError){let t=new Map;for(let r of e.issues){let o=r.path.length>0?r.path.join("."):"root";t.has(o)||t.set(o,[]),t.get(o).push(r.message)}return Array.from(t.entries()).map(([r,o])=>({field:r,messages:o}))}return e instanceof Error?[{field:"unknown",messages:[e.message]}]:[{field:"unknown",messages:[String(e)]}]}async function we(e,t,r){let o=[...t.middleware||[]];t.schema&&((t.schema.params||t.schema.query||t.schema.body)&&o.unshift(ye(t.schema)),t.schema.response&&o.push(he(t.schema.response))),await D([...o])(e,async()=>{let i=await t.handler(e,r);!e.response.sent&&i!==void 0&&e.response.json(i)})}function re(e,t,r){let o=t.exec(e);if(!o)return{};let n={};for(let i=0;i<r.length;i++)n[r[i]]=o[i+1]||"";return n}function Re(e){let t=[];if(e==="/")return{pattern:/^\/$/,paramNames:[]};let r=e.replace(/([.+*?^$(){}|\\])/g,"\\$1");return r=r.replace(/\/:([^/]+)/g,(n,i)=>(t.push(i),"/([^/]+)")).replace(/\/\[([^\]]+)\]/g,(n,i)=>(t.push(i),"/([^/]+)")),r=`${r}(?:/)?`,{pattern:new RegExp(`^${r}$`),paramNames:t}}function Se(){let e=[];return{add(t,r,o){let{pattern:n,paramNames:i}=Re(t),s={path:t,method:r,pattern:n,paramNames:i,routeOptions:o},a=e.findIndex(u=>i.length<u.paramNames.length);a===-1?e.push(s):e.splice(a,0,s)},remove(t){for(let r=e.length-1;r>=0;r--)e[r].path===t&&e.splice(r,1)},clear(){e.length=0},match(t,r){let o=t.split("?")[0];if(!o)return null;for(let i of e){if(i.method!==r)continue;if(i.pattern.exec(o)){let a=re(t,i.pattern,i.paramNames);return{route:i.routeOptions,params:a}}}return e.find(i=>i.method!==r&&i.pattern.test(t))?{route:null,params:{},methodNotAllowed:!0,allowedMethods:e.filter(i=>i.pattern.test(t)).map(i=>i.method)}:null},getRoutes(){return e.map(t=>({path:t.path,method:t.method}))},findRoutes(t){return e.filter(r=>r.pattern.test(t)).map(r=>({path:r.path,method:r.method,params:re(t,r.pattern,r.paramNames)}))}}}function bt(){return{routesByPath:new Map,routesByFile:new Map,pathToFile:new Map}}function M(e,t,r){console.log(`Updating routes from file: ${t}`);let o=e.routesByFile.get(t)||new Set,n=new Set(r.map(c=>c.path)),i=r.filter(c=>!o.has(c.path)),s=Array.from(o).filter(c=>!n.has(c)),u=r.filter(c=>o.has(c.path)).filter(c=>{let w=e.routesByPath.get(c.path);return!w||!Jr(w,c)});return Zr(e,t,{added:i,removed:s,changed:u}),{added:i,removed:s,changed:u}}function Tt(e){return Array.from(e.routesByPath.values())}function Zr(e,t,r){let{added:o,removed:n,changed:i}=r;n.forEach(a=>{e.routesByPath.delete(a),e.pathToFile.delete(a)}),[...o,...i].forEach(a=>{e.routesByPath.set(a.path,a),e.pathToFile.set(a.path,t)});let s=new Set([...o.map(a=>a.path),...i.map(a=>a.path),...Array.from(e.routesByFile.get(t)||[]).filter(a=>!n.includes(a))]);s.size>0?e.routesByFile.set(t,s):e.routesByFile.delete(t)}function Jr(e,t){if(e.path!==t.path)return!1;let r=Object.keys(e).filter(n=>n!=="path").sort(),o=Object.keys(t).filter(n=>n!=="path").sort();return r.length!==o.length?!1:r.every(n=>{let i=e[n],s=t[n];return typeof i==typeof s})}function xe(e,t){Object.entries(e).forEach(([r,o])=>{r==="path"||!o||t.add(e.path,r,o)})}function Q(e,t){"remove"in t&&typeof t.remove=="function"?t.remove(e):console.warn("Matcher does not support selective removal, consider adding remove() method")}function Pt(e,t){Q(e.path,t),xe(e,t)}var Yr={routesDir:"./routes",basePath:"/",watchMode:process.env.NODE_ENV==="development"};function vt(e){let t={...Yr,...e};e.basePath&&!e.basePath.startsWith("/")&&console.warn("Base path does nothing");let r=bt(),o=Se(),n=!1,i=null,s=null,a=new Set([t.routesDir]);function u(l){console.log(`
28
+ \u{1F527} APPLYING MATCHER CHANGES:`),console.log(` Adding ${l.added.length} routes`),console.log(` Removing ${l.removed.length} routes`),console.log(` Updating ${l.changed.length} routes`),l.removed.forEach(p=>{console.log(` \u2796 Removing: ${p}`),Q(p,o)}),l.added.forEach(p=>{let m=Object.keys(p).filter(f=>f!=="path");console.log(` \u2795 Adding: ${p.path} [${m.join(", ")}]`),xe(p,o)}),l.changed.forEach(p=>{let m=Object.keys(p).filter(f=>f!=="path");console.log(` \u{1F504} Updating: ${p.path} [${m.join(", ")}]`),Pt(p,o)}),console.log(`\u2705 Matcher changes applied
29
+ `)}function c(l,p){try{let m=M(r,p,l);return u(m),m}catch(m){throw console.error(`\u26A0\uFE0F Route conflicts from ${p}:`,m),m}}async function w(l,p,m){try{let f=await St(l),h=f.map(v=>m?{...v,path:`${m}${v.path}`}:v),R=c(h,p);console.log(`Loaded ${f.length} routes from ${p}${m?` with prefix ${m}`:""} (${R.added.length} added, ${R.changed.length} changed, ${R.removed.length} removed)`)}catch(f){throw console.error(`\u26A0\uFE0F Failed to load routes from ${p}:`,f),f}}async function d(){return n||i||(i=(async()=>{try{await Promise.all(Array.from(a).map(l=>w(l,l))),t.watchMode&&g(),n=!0}catch(l){throw console.error("\u26A0\uFE0F Failed to initialize router:",l),l}})()),i}function g(){s||(s=new Map);for(let l of a)if(!s.has(l)){let p=de(l,{debounceMs:16,ignore:["node_modules",".git"],onRouteAdded:(m,f)=>{try{let h=M(r,m,f);u(h)}catch(h){console.error(`Error adding routes from ${l}:`,h)}},onRouteChanged:le(async(m,f)=>{try{console.log(`Processing changes for ${m}`);let h=M(r,m,f);console.log(`Changes detected: ${h.added.length} added, ${h.changed.length} changed, ${h.removed.length} removed`),u(h),console.log(`Route changes applied: ${h.added.length} added, ${h.changed.length} changed, ${h.removed.length} removed`)}catch(h){console.error(`\u26A0\uFE0F Error updating routes from ${l}:`,h)}},l),onRouteRemoved:(m,f)=>{console.log(`File removed: ${m} with ${f.length} routes`);try{f.forEach(h=>{Q(h.path,o)}),ue(m)}catch(h){console.error(`\u26A0\uFE0F Error removing routes from ${m}:`,h)}},onError:m=>{console.error(`\u26A0\uFE0F Route watcher error for ${l}:`,m)}});s.set(l,p)}}function y(l,p){s||(s=new Map);let m=de(l,{debounceMs:16,ignore:["node_modules",".git"],onRouteAdded:(f,h)=>{try{let R=h.map(z=>p?{...z,path:`${p}${z.path}`}:z),v=M(r,f,R);u(v)}catch(R){console.error(`\u26A0\uFE0F Error adding routes from ${l}:`,R)}},onRouteChanged:le(async(f,h)=>{try{let R=h.map(z=>p?{...z,path:`${p}${z.path}`}:z),v=M(r,f,R);u(v)}catch(R){console.error(`\u26A0\uFE0F Error updating routes from ${l}:`,R)}},l),onRouteRemoved:(f,h)=>{try{h.forEach(R=>{let v=p?`${p}${R.path}`:R.path;Q(v,o)}),ue(f)}catch(R){console.error(`Error removing routes from ${f}:`,R)}},onError:f=>{console.error(`\u26A0\uFE0F Route watcher error for ${l}:`,f)}});return s.set(l,m),m}return d().catch(l=>{console.error("\u26A0\uFE0F Failed to initialize router on creation:",l)}),{async handleRequest(l){n||(console.log("\u{1F504} Router not initialized, initializing..."),await d());let{method:p,path:m}=l.request;console.log(`
30
+ \u{1F4E5} Handling request: ${p} ${m}`);let f=o.match(m,p);if(!f)throw console.log(`\u274C No match found for: ${p} ${m}`),new k("Not found");if(console.log(`\u2705 Route matched: ${p} ${m}`),console.log(` Params: ${JSON.stringify(f.params)}`),f.methodNotAllowed){l.response.status(405).json({error:"\u274C Method Not Allowed",allowed:f.allowedMethods}),f.allowedMethods&&f.allowedMethods.length>0&&l.response.header("Allow",f.allowedMethods.join(", "));return}l.request.params=f.params,await we(l,f.route,f.params)},getRoutes(){return Tt(r)},addRoute(l){let p=M(r,"programmatic",[l]);u(p)},addRoutes(l){let p=M(r,"programmatic",l);return u(p),p},async addRouteDirectory(l,p={}){if(a.has(l)){console.warn(`Route directory ${l} already registered`);return}a.add(l),n&&(await w(l,l,p.prefix),t.watchMode&&y(l,p.prefix))},getRouteConflicts(){return[]},async close(){if(s){for(let l of s.values())await l.close();s.clear()}}}}var eo={port:3e3,host:"localhost",routesDir:"./routes",http2:{enabled:!0},middleware:[],plugins:[]};function to(e={}){let t={...eo};return ze({routesDir:e.routesDir||t.routesDir}),{port:e.port??t.port,host:e.host??t.host,routesDir:e.routesDir??t.routesDir,http2:{enabled:e.http2?.enabled??t.http2?.enabled,keyFile:e.http2?.keyFile??t.http2?.keyFile,certFile:e.http2?.certFile??t.http2?.certFile},middleware:[...t.middleware||[],...e.middleware||[]],plugins:[...t.plugins||[],...e.plugins||[]]}}function ro(e,t,r,o){return async()=>(await oo(e,r,o),await e.pluginManager.initializePlugins(e),await at(e,t),await e.pluginManager.onServerStart(e,e.server),no(e),e)}async function oo(e,t,r){for(let o of t)e.use(o);for(let o of r)await e.register(o)}function no(e){let t=lt(()=>e.close());e._signalHandlers=t,e.events.emit("started")}function io(e){return async t=>{if(!e.server)return;let r={...t};e._signalHandlers&&(e._signalHandlers.unregister(),delete e._signalHandlers),await ut(e,r)}}function so(e){return t=>{let r=Array.isArray(t)?t:[t];return e.middleware.push(...r),e}}function ao(e){return async t=>(pt(t),e.plugins.push(t),await t.register(e),e)}function be(e={}){let t=to(e),r;try{r=ct(t)}catch(l){throw l instanceof Error?new Error(`Failed to create server: ${l.message}`):new Error(`Failed to create server: ${String(l)}`)}let{port:o,host:n,middleware:i,plugins:s}=r,a=Array.isArray(i)?[...i]:[],u=Array.isArray(s)?[...s]:[],c=new Xr,w=vt({routesDir:r.routesDir,watchMode:process.env.NODE_ENV==="development"}),d=dt({debug:process.env.NODE_ENV==="development",continueOnError:!0}),g=new Kr,y={server:null,port:o,host:n,context:c,events:g,plugins:[],middleware:[],_signalHandlers:{unregister:()=>{}},use:()=>y,register:async()=>y,listen:async()=>y,close:async()=>{},router:w,pluginManager:d};return y.listen=ro(y,r,a,u),y.close=io(y),y.use=so(y),y.register=ao(y),y}var Te=class extends S{constructor(t,r=void 0,o=void 0){super("UNAUTHORIZED",t,401,o??x(),r)}};var Pe=class extends S{constructor(t,r=void 0,o=void 0){super("FORBIDDEN",t,403,o??x(),r)}};var ve=class extends S{constructor(t,r=void 0,o=void 0){super("CONFLICT",t,409,o??x(),r)}};var Ee=class extends S{constructor(t,r=void 0,o=void 0){super("RATE_LIMITED",t,429,o??x(),r)}};var Ce=class extends S{constructor(t,r,o){super("UPLOAD_TIMEOUT",t,408,o??x(),r)}};var Fe=class extends S{constructor(t,r,o){super("UNPROCESSABLE_ENTITY",t,422,o??x(),r)}};var uo="0.1.0",lo={createServer:be},co={createDeleteRoute:Ne,createGetRoute:Oe,createHeadRoute:He,createOptionsRoute:Ue,createPatchRoute:Ae,createPostRoute:ke,createPutRoute:De},po={createMiddleware:ne,compose:D},fo={createPlugin:ie},ys={createServer:be,createMiddleware:ne,createPlugin:ie,Server:lo,Router:co,Middleware:po,Plugins:fo,VERSION:uo};export{ys as Blaize,S as BlaizeError,ve as ConflictError,mo as ErrorSeverity,B as ErrorType,Pe as ForbiddenError,H as InternalServerError,po as MiddlewareAPI,k as NotFoundError,Et as PayloadTooLargeError,fo as PluginsAPI,Ee as RateLimitError,Ce as RequestTimeoutError,co as RouterAPI,lo as ServerAPI,Te as UnauthorizedError,Fe as UnprocessableEntityError,Ct as UnsupportedMediaTypeError,uo as VERSION,U as ValidationError,D as compose,Ne as createDeleteRoute,Oe as createGetRoute,He as createHeadRoute,ne as createMiddleware,Ue as createOptionsRoute,Ae as createPatchRoute,ie as createPlugin,ke as createPostRoute,De as createPutRoute,be as createServer,go as isBodyParseError};
3172
31
  //# sourceMappingURL=index.js.map