blaizejs 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,11 +1,26 @@
1
1
  /**
2
- * blaizejs v0.2.2
2
+ * blaizejs v0.3.0
3
3
  * 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.
4
4
  *
5
5
  * Copyright (c) 2025 BlaizeJS Contributors
6
6
  * @license MIT
7
7
  */
8
8
 
9
+ import {
10
+ InternalServerError
11
+ } from "./chunk-QQCQRHXJ.js";
12
+ import {
13
+ ValidationError
14
+ } from "./chunk-SRD3AB6T.js";
15
+ import {
16
+ BlaizeError,
17
+ ErrorSeverity,
18
+ ErrorType,
19
+ __require,
20
+ generateCorrelationId,
21
+ getCurrentCorrelationId,
22
+ isBodyParseError
23
+ } from "./chunk-TCPQMZ23.js";
9
24
 
10
25
  // src/middleware/execute.ts
11
26
  function execute(middleware, ctx, next) {
@@ -107,6 +122,9 @@ function create2(name, version, setup, defaultOptions = {}) {
107
122
  };
108
123
  }
109
124
 
125
+ // src/router/create.ts
126
+ import { fileURLToPath } from "node:url";
127
+
110
128
  // src/config.ts
111
129
  var config = {};
112
130
  function setRuntimeConfig(newConfig) {
@@ -178,73 +196,75 @@ function getCallerFilePath() {
178
196
  if (!fileName) {
179
197
  throw new Error("Unable to determine caller file name");
180
198
  }
199
+ if (fileName.startsWith("file://")) {
200
+ return fileURLToPath(fileName);
201
+ }
181
202
  return fileName;
182
203
  } finally {
183
204
  Error.prepareStackTrace = originalPrepareStackTrace;
184
205
  }
185
206
  }
186
207
  function getRoutePath() {
187
- console.log("getRoutePath called");
188
208
  const callerPath = getCallerFilePath();
189
209
  const routesDir = getRoutesDir();
190
210
  const parsedRoute = parseRoutePath(callerPath, routesDir);
191
- console.log(`Parsed route path: ${parsedRoute.routePath} from file: ${callerPath}`);
211
+ console.log(`\u{1F50E} Parsed route path: ${parsedRoute.routePath} from file: ${callerPath}`);
192
212
  return parsedRoute.routePath;
193
213
  }
194
214
  var createGetRoute = (config2) => {
195
215
  validateMethodConfig("GET", config2);
196
- const path5 = getRoutePath();
216
+ const path6 = getRoutePath();
197
217
  return {
198
218
  GET: config2,
199
- path: path5
219
+ path: path6
200
220
  };
201
221
  };
202
222
  var createPostRoute = (config2) => {
203
223
  validateMethodConfig("POST", config2);
204
- const path5 = getRoutePath();
224
+ const path6 = getRoutePath();
205
225
  return {
206
226
  POST: config2,
207
- path: path5
227
+ path: path6
208
228
  };
209
229
  };
210
230
  var createPutRoute = (config2) => {
211
231
  validateMethodConfig("PUT", config2);
212
- const path5 = getRoutePath();
232
+ const path6 = getRoutePath();
213
233
  return {
214
234
  PUT: config2,
215
- path: path5
235
+ path: path6
216
236
  };
217
237
  };
218
238
  var createDeleteRoute = (config2) => {
219
239
  validateMethodConfig("DELETE", config2);
220
- const path5 = getRoutePath();
240
+ const path6 = getRoutePath();
221
241
  return {
222
242
  DELETE: config2,
223
- path: path5
243
+ path: path6
224
244
  };
225
245
  };
226
246
  var createPatchRoute = (config2) => {
227
247
  validateMethodConfig("PATCH", config2);
228
- const path5 = getRoutePath();
248
+ const path6 = getRoutePath();
229
249
  return {
230
250
  PATCH: config2,
231
- path: path5
251
+ path: path6
232
252
  };
233
253
  };
234
254
  var createHeadRoute = (config2) => {
235
255
  validateMethodConfig("HEAD", config2);
236
- const path5 = getRoutePath();
256
+ const path6 = getRoutePath();
237
257
  return {
238
258
  HEAD: config2,
239
- path: path5
259
+ path: path6
240
260
  };
241
261
  };
242
262
  var createOptionsRoute = (config2) => {
243
263
  validateMethodConfig("OPTIONS", config2);
244
- const path5 = getRoutePath();
264
+ const path6 = getRoutePath();
245
265
  return {
246
266
  OPTIONS: config2,
247
- path: path5
267
+ path: path6
248
268
  };
249
269
  };
250
270
  function validateMethodConfig(method, config2) {
@@ -380,8 +400,474 @@ function runWithContext(context, callback) {
380
400
  return contextStorage.run(context, callback);
381
401
  }
382
402
 
403
+ // src/upload/multipart-parser.ts
404
+ import { randomUUID } from "node:crypto";
405
+ import { createWriteStream } from "node:fs";
406
+ import { tmpdir } from "node:os";
407
+ import { join as join2 } from "node:path";
408
+ import { Readable } from "node:stream";
409
+
410
+ // src/upload/utils.ts
411
+ var BOUNDARY_REGEX = /boundary=([^;]+)/i;
412
+ var CONTENT_DISPOSITION_REGEX = /Content-Disposition:\s*form-data;\s*name="([^"]+)"(?:;[\s\r\n]*filename="([^"]*)")?/i;
413
+ var CONTENT_TYPE_REGEX = /Content-Type:\s*([^\r\n]+)/i;
414
+ var MULTIPART_REGEX = /multipart\/form-data/i;
415
+ function extractBoundary(contentType) {
416
+ const match = contentType.match(BOUNDARY_REGEX);
417
+ if (!match || !match[1]) return null;
418
+ let boundary = match[1].trim();
419
+ if (boundary.startsWith('"') && boundary.endsWith('"')) {
420
+ boundary = boundary.slice(1, -1);
421
+ }
422
+ return boundary || null;
423
+ }
424
+ function parseContentDisposition(headers) {
425
+ const match = headers.match(CONTENT_DISPOSITION_REGEX);
426
+ if (!match || !match[1]) return null;
427
+ return {
428
+ name: match[1],
429
+ filename: match[2] !== void 0 ? match[2] : void 0
430
+ };
431
+ }
432
+ function parseContentType(headers) {
433
+ const match = headers.match(CONTENT_TYPE_REGEX);
434
+ return match && match[1]?.trim() ? match[1].trim() : "application/octet-stream";
435
+ }
436
+ function isMultipartContent(contentType) {
437
+ return MULTIPART_REGEX.test(contentType);
438
+ }
439
+
440
+ // src/upload/multipart-parser.ts
441
+ var DEFAULT_OPTIONS = {
442
+ maxFileSize: 10 * 1024 * 1024,
443
+ // 10MB
444
+ maxFiles: 10,
445
+ maxFieldSize: 1 * 1024 * 1024,
446
+ // 1MB
447
+ allowedMimeTypes: [],
448
+ allowedExtensions: [],
449
+ strategy: "stream",
450
+ tempDir: tmpdir(),
451
+ computeHash: false
452
+ };
453
+ function createParserState(boundary, options = {}) {
454
+ return {
455
+ boundary: Buffer.from(`--${boundary}`),
456
+ options: { ...DEFAULT_OPTIONS, ...options },
457
+ fields: /* @__PURE__ */ new Map(),
458
+ files: /* @__PURE__ */ new Map(),
459
+ buffer: Buffer.alloc(0),
460
+ stage: "boundary",
461
+ currentHeaders: "",
462
+ currentField: null,
463
+ currentFilename: void 0,
464
+ currentMimetype: "application/octet-stream",
465
+ currentContentLength: 0,
466
+ fileCount: 0,
467
+ fieldCount: 0,
468
+ currentBufferChunks: [],
469
+ currentStream: null,
470
+ currentTempPath: null,
471
+ currentWriteStream: null,
472
+ streamController: null,
473
+ cleanupTasks: [],
474
+ // Track validation state
475
+ hasFoundValidBoundary: false,
476
+ hasProcessedAnyPart: false,
477
+ isFinished: false
478
+ };
479
+ }
480
+ async function processChunk(state, chunk) {
481
+ const newBuffer = Buffer.concat([state.buffer, chunk]);
482
+ let currentState = { ...state, buffer: newBuffer };
483
+ while (currentState.buffer.length > 0 && !currentState.isFinished) {
484
+ const nextState = await processCurrentStage(currentState);
485
+ if (nextState === currentState) break;
486
+ currentState = nextState;
487
+ }
488
+ return currentState;
489
+ }
490
+ async function processCurrentStage(state) {
491
+ switch (state.stage) {
492
+ case "boundary":
493
+ return processBoundary(state);
494
+ case "headers":
495
+ return processHeaders(state);
496
+ case "content":
497
+ return processContent(state);
498
+ default: {
499
+ const { InternalServerError: InternalServerError2 } = await import("./internal-server-error-CVRDTBLL.js");
500
+ throw new InternalServerError2(`Invalid parser stage`, {
501
+ operation: state.stage
502
+ });
503
+ }
504
+ }
505
+ }
506
+ function processBoundary(state) {
507
+ const boundaryIndex = state.buffer.indexOf(state.boundary);
508
+ if (boundaryIndex === -1) return state;
509
+ const hasFoundValidBoundary = true;
510
+ let buffer = state.buffer.subarray(boundaryIndex + state.boundary.length);
511
+ if (buffer.length >= 2 && buffer.subarray(0, 2).equals(Buffer.from("--"))) {
512
+ return {
513
+ ...state,
514
+ buffer,
515
+ hasFoundValidBoundary,
516
+ isFinished: true,
517
+ stage: "boundary"
518
+ };
519
+ }
520
+ if (buffer.length >= 2 && buffer.subarray(0, 2).equals(Buffer.from("\r\n"))) {
521
+ buffer = buffer.subarray(2);
522
+ }
523
+ return {
524
+ ...state,
525
+ buffer,
526
+ hasFoundValidBoundary,
527
+ stage: "headers",
528
+ currentHeaders: ""
529
+ };
530
+ }
531
+ async function processHeaders(state) {
532
+ const headerEnd = state.buffer.indexOf("\r\n\r\n");
533
+ if (headerEnd === -1) return state;
534
+ const headers = state.buffer.subarray(0, headerEnd).toString("utf8");
535
+ const buffer = state.buffer.subarray(headerEnd + 4);
536
+ const disposition = parseContentDisposition(headers);
537
+ if (!disposition) {
538
+ const { ValidationError: ValidationError2 } = await import("./validation-error-CM6IKIJU.js");
539
+ throw new ValidationError2("Missing or invalid Content-Disposition header");
540
+ }
541
+ const mimetype = parseContentType(headers);
542
+ const isFile = disposition.filename !== void 0;
543
+ if (isFile && state.fileCount >= state.options.maxFiles) {
544
+ const { PayloadTooLargeError } = await import("./payload-too-large-error-PAYLDBZT.js");
545
+ throw new PayloadTooLargeError("Too many files in upload", {
546
+ fileCount: state.fileCount + 1,
547
+ maxFiles: state.options.maxFiles,
548
+ filename: disposition.filename
549
+ });
550
+ }
551
+ if (isFile && state.options.allowedMimeTypes.length > 0 && !state.options.allowedMimeTypes.includes(mimetype)) {
552
+ const { UnsupportedMediaTypeError } = await import("./unsupported-media-type-error-MQZD7YQJ.js");
553
+ throw new UnsupportedMediaTypeError("File type not allowed", {
554
+ receivedMimeType: mimetype,
555
+ allowedMimeTypes: state.options.allowedMimeTypes,
556
+ filename: disposition.filename
557
+ });
558
+ }
559
+ return {
560
+ ...state,
561
+ buffer,
562
+ stage: "content",
563
+ currentHeaders: headers,
564
+ currentField: disposition.name,
565
+ currentFilename: disposition.filename,
566
+ currentMimetype: mimetype,
567
+ currentContentLength: 0,
568
+ fileCount: isFile ? state.fileCount + 1 : state.fileCount,
569
+ fieldCount: isFile ? state.fieldCount : state.fieldCount + 1,
570
+ currentBufferChunks: []
571
+ };
572
+ }
573
+ async function processContent(state) {
574
+ const nextBoundaryIndex = state.buffer.indexOf(state.boundary);
575
+ let contentChunk;
576
+ let isComplete = false;
577
+ let buffer = state.buffer;
578
+ if (nextBoundaryIndex === -1) {
579
+ const safeLength = Math.max(0, state.buffer.length - state.boundary.length);
580
+ if (safeLength === 0) return state;
581
+ contentChunk = state.buffer.subarray(0, safeLength);
582
+ buffer = state.buffer.subarray(safeLength);
583
+ } else {
584
+ const contentEnd = Math.max(0, nextBoundaryIndex - 2);
585
+ contentChunk = state.buffer.subarray(0, contentEnd);
586
+ buffer = state.buffer.subarray(nextBoundaryIndex);
587
+ isComplete = true;
588
+ }
589
+ let updatedState = { ...state, buffer };
590
+ if (contentChunk.length > 0) {
591
+ updatedState = await processContentChunk(updatedState, contentChunk);
592
+ }
593
+ if (isComplete) {
594
+ updatedState = await finalizeCurrentPart(updatedState);
595
+ updatedState = {
596
+ ...updatedState,
597
+ stage: "boundary",
598
+ hasProcessedAnyPart: true
599
+ // Mark that we've processed at least one part
600
+ };
601
+ }
602
+ return updatedState;
603
+ }
604
+ async function processContentChunk(state, chunk) {
605
+ const newContentLength = state.currentContentLength + chunk.length;
606
+ const maxSize = state.currentFilename !== void 0 ? state.options.maxFileSize : state.options.maxFieldSize;
607
+ if (newContentLength > maxSize) {
608
+ const isFile = state.currentFilename !== void 0;
609
+ const { PayloadTooLargeError } = await import("./payload-too-large-error-PAYLDBZT.js");
610
+ const payloadErrorDetals = state.currentField ? {
611
+ contentType: isFile ? "file" : "field",
612
+ currentSize: newContentLength,
613
+ maxSize,
614
+ field: state.currentField,
615
+ filename: state.currentFilename
616
+ } : {
617
+ contentType: isFile ? "file" : "field",
618
+ currentSize: newContentLength,
619
+ maxSize,
620
+ filename: state.currentFilename
621
+ };
622
+ throw new PayloadTooLargeError(
623
+ `${isFile ? "File" : "Field"} size exceeds limit`,
624
+ payloadErrorDetals
625
+ );
626
+ }
627
+ if (state.currentFilename !== void 0) {
628
+ return processFileChunk(state, chunk, newContentLength);
629
+ } else {
630
+ return {
631
+ ...state,
632
+ currentContentLength: newContentLength,
633
+ currentBufferChunks: [...state.currentBufferChunks, chunk]
634
+ };
635
+ }
636
+ }
637
+ async function processFileChunk(state, chunk, newContentLength) {
638
+ switch (state.options.strategy) {
639
+ case "memory":
640
+ return {
641
+ ...state,
642
+ currentContentLength: newContentLength,
643
+ currentBufferChunks: [...state.currentBufferChunks, chunk]
644
+ };
645
+ case "stream":
646
+ if (state.streamController) {
647
+ state.streamController.enqueue(chunk);
648
+ }
649
+ return { ...state, currentContentLength: newContentLength };
650
+ case "temp":
651
+ if (state.currentWriteStream) {
652
+ await writeToStream(state.currentWriteStream, chunk);
653
+ }
654
+ return { ...state, currentContentLength: newContentLength };
655
+ default: {
656
+ const { ValidationError: ValidationError2 } = await import("./validation-error-CM6IKIJU.js");
657
+ throw new ValidationError2(`Invalid parsing strategy`);
658
+ }
659
+ }
660
+ }
661
+ async function initializeFileProcessing(state) {
662
+ if (state.currentFilename === void 0) return state;
663
+ switch (state.options.strategy) {
664
+ case "memory":
665
+ return { ...state, currentBufferChunks: [] };
666
+ case "stream": {
667
+ let streamController = null;
668
+ const stream = new ReadableStream({
669
+ start: (controller) => {
670
+ streamController = controller;
671
+ }
672
+ });
673
+ return {
674
+ ...state,
675
+ currentStream: stream,
676
+ // Type cast for Node.js compatibility
677
+ streamController
678
+ };
679
+ }
680
+ case "temp": {
681
+ const tempPath = join2(state.options.tempDir, `upload-${randomUUID()}`);
682
+ const writeStream = createWriteStream(tempPath);
683
+ const cleanupTask = async () => {
684
+ try {
685
+ const { unlink } = await import("node:fs/promises");
686
+ await unlink(tempPath);
687
+ } catch (error) {
688
+ console.warn(`Failed to cleanup temp file: ${tempPath}`, error);
689
+ }
690
+ };
691
+ return {
692
+ ...state,
693
+ currentTempPath: tempPath,
694
+ currentWriteStream: writeStream,
695
+ cleanupTasks: [...state.cleanupTasks, cleanupTask]
696
+ };
697
+ }
698
+ default: {
699
+ const { ValidationError: ValidationError2 } = await import("./validation-error-CM6IKIJU.js");
700
+ throw new ValidationError2(`Invalid file processing strategy`);
701
+ }
702
+ }
703
+ }
704
+ async function finalizeCurrentPart(state) {
705
+ if (!state.currentField) return resetCurrentPart(state);
706
+ if (state.currentFilename !== void 0) {
707
+ return finalizeFile(state);
708
+ } else {
709
+ return finalizeField(state);
710
+ }
711
+ }
712
+ async function finalizeFile(state) {
713
+ if (!state.currentField || state.currentFilename === void 0) {
714
+ return resetCurrentPart(state);
715
+ }
716
+ let stream;
717
+ let buffer;
718
+ let tempPath;
719
+ switch (state.options.strategy) {
720
+ case "memory":
721
+ buffer = Buffer.concat(state.currentBufferChunks);
722
+ stream = Readable.from(buffer);
723
+ break;
724
+ case "stream":
725
+ if (state.streamController) {
726
+ state.streamController.close();
727
+ }
728
+ stream = state.currentStream;
729
+ break;
730
+ case "temp":
731
+ if (state.currentWriteStream) {
732
+ await closeStream(state.currentWriteStream);
733
+ }
734
+ tempPath = state.currentTempPath;
735
+ stream = Readable.from(Buffer.alloc(0));
736
+ break;
737
+ default: {
738
+ const { ValidationError: ValidationError2 } = await import("./validation-error-CM6IKIJU.js");
739
+ throw new ValidationError2(`Invalid file finalization strategy`);
740
+ }
741
+ }
742
+ const file = {
743
+ filename: state.currentFilename,
744
+ fieldname: state.currentField,
745
+ mimetype: state.currentMimetype,
746
+ size: state.currentContentLength,
747
+ stream,
748
+ buffer,
749
+ tempPath
750
+ };
751
+ const updatedFiles = addToCollection(state.files, state.currentField, file);
752
+ return {
753
+ ...resetCurrentPart(state),
754
+ files: updatedFiles
755
+ };
756
+ }
757
+ function finalizeField(state) {
758
+ if (!state.currentField) return resetCurrentPart(state);
759
+ const value = Buffer.concat(state.currentBufferChunks).toString("utf8");
760
+ const updatedFields = addToCollection(state.fields, state.currentField, value);
761
+ return {
762
+ ...resetCurrentPart(state),
763
+ fields: updatedFields
764
+ };
765
+ }
766
+ function resetCurrentPart(state) {
767
+ return {
768
+ ...state,
769
+ currentField: null,
770
+ currentFilename: void 0,
771
+ currentContentLength: 0,
772
+ currentBufferChunks: [],
773
+ currentStream: null,
774
+ streamController: null,
775
+ currentTempPath: null,
776
+ currentWriteStream: null
777
+ };
778
+ }
779
+ function addToCollection(collection, key, value) {
780
+ const newCollection = new Map(collection);
781
+ const existing = newCollection.get(key) || [];
782
+ newCollection.set(key, [...existing, value]);
783
+ return newCollection;
784
+ }
785
+ async function finalize(state) {
786
+ if (!state.hasFoundValidBoundary) {
787
+ const { ValidationError: ValidationError2 } = await import("./validation-error-CM6IKIJU.js");
788
+ throw new ValidationError2("No valid multipart boundary found");
789
+ }
790
+ if (state.hasFoundValidBoundary && !state.hasProcessedAnyPart) {
791
+ const { ValidationError: ValidationError2 } = await import("./validation-error-CM6IKIJU.js");
792
+ throw new ValidationError2("Empty multipart request");
793
+ }
794
+ const fields = {};
795
+ for (const [key, values] of state.fields.entries()) {
796
+ fields[key] = values.length === 1 ? values[0] : values;
797
+ }
798
+ const files = {};
799
+ for (const [key, fileList] of state.files.entries()) {
800
+ files[key] = fileList.length === 1 ? fileList[0] : fileList;
801
+ }
802
+ return { fields, files };
803
+ }
804
+ async function cleanup(state) {
805
+ await Promise.allSettled(state.cleanupTasks.map((task) => task()));
806
+ if (state.streamController) {
807
+ state.streamController.close();
808
+ }
809
+ if (state.currentWriteStream) {
810
+ await closeStream(state.currentWriteStream);
811
+ }
812
+ }
813
+ async function writeToStream(stream, chunk) {
814
+ return new Promise((resolve3, reject) => {
815
+ stream.write(chunk, (error) => {
816
+ if (error) reject(error);
817
+ else resolve3();
818
+ });
819
+ });
820
+ }
821
+ async function closeStream(stream) {
822
+ return new Promise((resolve3) => {
823
+ stream.end(() => resolve3());
824
+ });
825
+ }
826
+ async function parseMultipartRequest(request, options = {}) {
827
+ const contentType = request.headers["content-type"] || "";
828
+ const boundary = extractBoundary(contentType);
829
+ if (!boundary) {
830
+ const { UnsupportedMediaTypeError } = await import("./unsupported-media-type-error-MQZD7YQJ.js");
831
+ throw new UnsupportedMediaTypeError("Missing boundary in multipart content-type", {
832
+ receivedContentType: contentType,
833
+ expectedFormat: "multipart/form-data; boundary=..."
834
+ });
835
+ }
836
+ let state = createParserState(boundary, options);
837
+ if (state.currentFilename !== void 0) {
838
+ state = await initializeFileProcessing(state);
839
+ }
840
+ try {
841
+ for await (const chunk of request) {
842
+ state = await processChunk(state, chunk);
843
+ }
844
+ return finalize(state);
845
+ } finally {
846
+ await cleanup(state);
847
+ }
848
+ }
849
+
383
850
  // src/context/create.ts
384
851
  var CONTENT_TYPE_HEADER = "Content-Type";
852
+ var DEFAULT_BODY_LIMITS = {
853
+ json: 512 * 1024,
854
+ // 512KB - Most APIs should be much smaller
855
+ form: 1024 * 1024,
856
+ // 1MB - Reasonable for form submissions
857
+ text: 5 * 1024 * 1024,
858
+ // 5MB - Documents, logs, code files
859
+ multipart: {
860
+ maxFileSize: 50 * 1024 * 1024,
861
+ // 50MB per file
862
+ maxTotalSize: 100 * 1024 * 1024,
863
+ // 100MB total request
864
+ maxFiles: 10,
865
+ maxFieldSize: 1024 * 1024
866
+ // 1MB for form fields
867
+ },
868
+ raw: 10 * 1024 * 1024
869
+ // 10MB for unknown content types
870
+ };
385
871
  function parseRequestUrl(req) {
386
872
  const originalUrl = req.url || "/";
387
873
  const host = req.headers.host || "localhost";
@@ -389,7 +875,7 @@ function parseRequestUrl(req) {
389
875
  const fullUrl = `${protocol}://${host}${originalUrl.startsWith("/") ? "" : "/"}${originalUrl}`;
390
876
  try {
391
877
  const url = new URL(fullUrl);
392
- const path5 = url.pathname;
878
+ const path6 = url.pathname;
393
879
  const query = {};
394
880
  url.searchParams.forEach((value, key) => {
395
881
  if (query[key] !== void 0) {
@@ -402,7 +888,7 @@ function parseRequestUrl(req) {
402
888
  query[key] = value;
403
889
  }
404
890
  });
405
- return { path: path5, url, query };
891
+ return { path: path6, url, query };
406
892
  } catch (error) {
407
893
  console.warn(`Invalid URL: ${fullUrl}`, error);
408
894
  throw new ParseUrlError(`Invalid URL: ${fullUrl}`);
@@ -424,7 +910,7 @@ function getProtocol(req) {
424
910
  return encrypted ? "https" : "http";
425
911
  }
426
912
  async function createContext(req, res, options = {}) {
427
- const { path: path5, url, query } = parseRequestUrl(req);
913
+ const { path: path6, url, query } = parseRequestUrl(req);
428
914
  const method = req.method || "GET";
429
915
  const isHttp2 = isHttp2Request(req);
430
916
  const protocol = getProtocol(req);
@@ -433,7 +919,7 @@ async function createContext(req, res, options = {}) {
433
919
  const responseState = { sent: false };
434
920
  const ctx = {
435
921
  request: createRequestObject(req, {
436
- path: path5,
922
+ path: path6,
437
923
  url,
438
924
  query,
439
925
  params,
@@ -446,7 +932,7 @@ async function createContext(req, res, options = {}) {
446
932
  };
447
933
  ctx.response = createResponseObject(res, responseState, ctx);
448
934
  if (options.parseBody) {
449
- await parseBodyIfNeeded(req, ctx);
935
+ await parseBodyIfNeeded(req, ctx, options);
450
936
  }
451
937
  return ctx;
452
938
  }
@@ -622,34 +1108,60 @@ function createStreamResponder(res, responseState) {
622
1108
  });
623
1109
  };
624
1110
  }
625
- async function parseBodyIfNeeded(req, ctx) {
1111
+ async function parseBodyIfNeeded(req, ctx, options = {}) {
626
1112
  if (shouldSkipParsing(req.method)) {
627
1113
  return;
628
1114
  }
629
1115
  const contentType = req.headers["content-type"] || "";
630
1116
  const contentLength = parseInt(req.headers["content-length"] || "0", 10);
631
- if (contentLength === 0 || contentLength > 1048576) {
1117
+ if (contentLength === 0) {
632
1118
  return;
633
1119
  }
1120
+ const limits = {
1121
+ json: options.bodyLimits?.json ?? DEFAULT_BODY_LIMITS.json,
1122
+ form: options.bodyLimits?.form ?? DEFAULT_BODY_LIMITS.form,
1123
+ text: options.bodyLimits?.text ?? DEFAULT_BODY_LIMITS.text,
1124
+ raw: options.bodyLimits?.raw ?? DEFAULT_BODY_LIMITS.raw,
1125
+ multipart: {
1126
+ maxFileSize: options.bodyLimits?.multipart?.maxFileSize ?? DEFAULT_BODY_LIMITS.multipart.maxFileSize,
1127
+ maxFiles: options.bodyLimits?.multipart?.maxFiles ?? DEFAULT_BODY_LIMITS.multipart.maxFiles,
1128
+ maxFieldSize: options.bodyLimits?.multipart?.maxFieldSize ?? DEFAULT_BODY_LIMITS.multipart.maxFieldSize,
1129
+ maxTotalSize: options.bodyLimits?.multipart?.maxTotalSize ?? DEFAULT_BODY_LIMITS.multipart.maxTotalSize
1130
+ }
1131
+ };
634
1132
  try {
635
- await parseBodyByContentType(req, ctx, contentType);
1133
+ if (contentType.includes("application/json")) {
1134
+ if (contentLength > limits.json) {
1135
+ throw new Error(`JSON body too large: ${contentLength} > ${limits.json} bytes`);
1136
+ }
1137
+ await parseJsonBody(req, ctx);
1138
+ } else if (contentType.includes("application/x-www-form-urlencoded")) {
1139
+ if (contentLength > limits.form) {
1140
+ throw new Error(`Form body too large: ${contentLength} > ${limits.form} bytes`);
1141
+ }
1142
+ await parseFormUrlEncodedBody(req, ctx);
1143
+ } else if (contentType.includes("text/")) {
1144
+ if (contentLength > limits.text) {
1145
+ throw new Error(`Text body too large: ${contentLength} > ${limits.text} bytes`);
1146
+ }
1147
+ await parseTextBody(req, ctx);
1148
+ } else if (isMultipartContent(contentType)) {
1149
+ await parseMultipartBody(req, ctx, limits.multipart);
1150
+ } else {
1151
+ if (contentLength > limits.raw) {
1152
+ throw new Error(`Request body too large: ${contentLength} > ${limits.raw} bytes`);
1153
+ }
1154
+ return;
1155
+ }
636
1156
  } catch (error) {
637
- setBodyError(ctx, "body_read_error", "Error reading request body", error);
1157
+ const errorType = contentType.includes("multipart") ? "multipart_parse_error" : "body_read_error";
1158
+ setBodyError(ctx, errorType, "Error reading request body", error);
638
1159
  }
639
1160
  }
640
1161
  function shouldSkipParsing(method) {
641
1162
  const skipMethods = ["GET", "HEAD", "OPTIONS"];
642
1163
  return skipMethods.includes(method || "GET");
643
1164
  }
644
- async function parseBodyByContentType(req, ctx, contentType) {
645
- if (contentType.includes("application/json")) {
646
- await parseJsonBody(req, ctx);
647
- } else if (contentType.includes("application/x-www-form-urlencoded")) {
648
- await parseFormUrlEncodedBody(req, ctx);
649
- } else if (contentType.includes("text/")) {
650
- await parseTextBody(req, ctx);
651
- }
652
- }
653
1165
  async function parseJsonBody(req, ctx) {
654
1166
  const body = await readRequestBody(req);
655
1167
  if (!body) {
@@ -701,17 +1213,36 @@ async function parseTextBody(req, ctx) {
701
1213
  ctx.request.body = body;
702
1214
  }
703
1215
  }
1216
+ async function parseMultipartBody(req, ctx, multipartLimits) {
1217
+ try {
1218
+ const limits = multipartLimits || DEFAULT_BODY_LIMITS.multipart;
1219
+ const multipartData = await parseMultipartRequest(req, {
1220
+ strategy: "stream",
1221
+ maxFileSize: limits.maxFileSize,
1222
+ maxFiles: limits.maxFiles,
1223
+ maxFieldSize: limits.maxFieldSize
1224
+ // Could add total size validation here
1225
+ });
1226
+ ctx.request.multipart = multipartData;
1227
+ ctx.request.files = multipartData.files;
1228
+ ctx.request.body = multipartData.fields;
1229
+ } catch (error) {
1230
+ ctx.request.body = null;
1231
+ setBodyError(ctx, "multipart_parse_error", "Failed to parse multipart data", error);
1232
+ }
1233
+ }
704
1234
  function setBodyError(ctx, type, message, error) {
705
- ctx.state._bodyError = { type, message, error };
1235
+ const bodyError = { type, message, error };
1236
+ ctx.state._bodyError = bodyError;
706
1237
  }
707
1238
  async function readRequestBody(req) {
708
- return new Promise((resolve2, reject) => {
1239
+ return new Promise((resolve3, reject) => {
709
1240
  const chunks = [];
710
1241
  req.on("data", (chunk) => {
711
1242
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
712
1243
  });
713
1244
  req.on("end", () => {
714
- resolve2(Buffer.concat(chunks).toString("utf8"));
1245
+ resolve3(Buffer.concat(chunks).toString("utf8"));
715
1246
  });
716
1247
  req.on("error", (err) => {
717
1248
  reject(err);
@@ -719,6 +1250,102 @@ async function readRequestBody(req) {
719
1250
  });
720
1251
  }
721
1252
 
1253
+ // src/errors/not-found-error.ts
1254
+ var NotFoundError = class extends BlaizeError {
1255
+ /**
1256
+ * Creates a new NotFoundError instance
1257
+ *
1258
+ * @param title - Human-readable error message
1259
+ * @param details - Optional context about the missing resource
1260
+ * @param correlationId - Optional correlation ID (uses current context if not provided)
1261
+ */
1262
+ constructor(title, details = void 0, correlationId = void 0) {
1263
+ super(
1264
+ "NOT_FOUND" /* NOT_FOUND */,
1265
+ title,
1266
+ 404,
1267
+ // HTTP 404 Not Found
1268
+ correlationId ?? getCurrentCorrelationId(),
1269
+ details
1270
+ );
1271
+ }
1272
+ };
1273
+
1274
+ // src/errors/boundary.ts
1275
+ function isHandledError(error) {
1276
+ return error instanceof BlaizeError;
1277
+ }
1278
+ function formatErrorResponse(error) {
1279
+ if (isHandledError(error)) {
1280
+ return {
1281
+ type: error.type,
1282
+ title: error.title,
1283
+ status: error.status,
1284
+ correlationId: error.correlationId,
1285
+ timestamp: error.timestamp.toISOString(),
1286
+ details: error.details
1287
+ };
1288
+ }
1289
+ const correlationId = generateCorrelationId();
1290
+ let originalMessage;
1291
+ if (error instanceof Error) {
1292
+ originalMessage = error.message;
1293
+ } else if (error === null || error === void 0) {
1294
+ originalMessage = "Unknown error occurred";
1295
+ } else {
1296
+ originalMessage = String(error);
1297
+ }
1298
+ const wrappedError = new InternalServerError(
1299
+ "Internal Server Error",
1300
+ { originalMessage },
1301
+ correlationId
1302
+ );
1303
+ return {
1304
+ type: wrappedError.type,
1305
+ title: wrappedError.title,
1306
+ status: wrappedError.status,
1307
+ correlationId: wrappedError.correlationId,
1308
+ timestamp: wrappedError.timestamp.toISOString(),
1309
+ details: wrappedError.details
1310
+ };
1311
+ }
1312
+ function extractOrGenerateCorrelationId(headerGetter) {
1313
+ return headerGetter("x-correlation-id") ?? generateCorrelationId();
1314
+ }
1315
+ function setErrorResponseHeaders(headerSetter, correlationId) {
1316
+ headerSetter("x-correlation-id", correlationId);
1317
+ }
1318
+
1319
+ // src/middleware/error-boundary.ts
1320
+ function createErrorBoundary(options = {}) {
1321
+ const { debug = false } = options;
1322
+ const middlewareFn = async (ctx, next) => {
1323
+ try {
1324
+ await next();
1325
+ } catch (error) {
1326
+ if (ctx.response.sent) {
1327
+ if (debug) {
1328
+ console.error("Error occurred after response was sent:", error);
1329
+ }
1330
+ return;
1331
+ }
1332
+ if (debug) {
1333
+ console.error("Error boundary caught error:", error);
1334
+ }
1335
+ const correlationId = extractOrGenerateCorrelationId(ctx.request.header);
1336
+ const errorResponse = formatErrorResponse(error);
1337
+ errorResponse.correlationId = correlationId;
1338
+ setErrorResponseHeaders(ctx.response.header, correlationId);
1339
+ ctx.response.status(errorResponse.status).json(errorResponse);
1340
+ }
1341
+ };
1342
+ return {
1343
+ name: "ErrorBoundary",
1344
+ execute: middlewareFn,
1345
+ debug
1346
+ };
1347
+ }
1348
+
722
1349
  // src/server/request-handler.ts
723
1350
  function createRequestHandler(serverInstance) {
724
1351
  return async (req, res) => {
@@ -727,32 +1354,20 @@ function createRequestHandler(serverInstance) {
727
1354
  parseBody: true
728
1355
  // Enable automatic body parsing
729
1356
  });
730
- const handler = compose(serverInstance.middleware);
1357
+ const errorBoundary = createErrorBoundary();
1358
+ const allMiddleware = [errorBoundary, ...serverInstance.middleware];
1359
+ const handler = compose(allMiddleware);
731
1360
  await runWithContext(context, async () => {
732
- try {
733
- await handler(context, async () => {
1361
+ await handler(context, async () => {
1362
+ if (!context.response.sent) {
1363
+ await serverInstance.router.handleRequest(context);
734
1364
  if (!context.response.sent) {
735
- await serverInstance.router.handleRequest(context);
736
- if (!context.response.sent) {
737
- context.response.status(404).json({
738
- error: "Not Found",
739
- message: `Route not found: ${context.request.method} ${context.request.path}`
740
- });
741
- }
1365
+ throw new NotFoundError(
1366
+ `Route not found: ${context.request.method} ${context.request.path}`
1367
+ );
742
1368
  }
743
- });
744
- } catch (error) {
745
- console.error("Error processing request:", error);
746
- if (!context.response.sent) {
747
- context.response.json(
748
- {
749
- error: "Internal Server Error",
750
- message: process.env.NODE_ENV === "development" ? error || "Unknown error" : "An error occurred processing your request"
751
- },
752
- 500
753
- );
754
1369
  }
755
- }
1370
+ });
756
1371
  });
757
1372
  } catch (error) {
758
1373
  console.error("Error creating context:", error);
@@ -809,7 +1424,7 @@ function createServerInstance(isHttp2, certOptions) {
809
1424
  return http2.createSecureServer(http2ServerOptions);
810
1425
  }
811
1426
  function listenOnPort(server, port, host, isHttp2) {
812
- return new Promise((resolve2, reject) => {
1427
+ return new Promise((resolve3, reject) => {
813
1428
  server.listen(port, host, () => {
814
1429
  const protocol = isHttp2 ? "https" : "http";
815
1430
  const url = `${protocol}://${host}:${port}`;
@@ -826,7 +1441,7 @@ function listenOnPort(server, port, host, isHttp2) {
826
1441
 
827
1442
  \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}
828
1443
  `);
829
- resolve2();
1444
+ resolve3();
830
1445
  });
831
1446
  server.on("error", (err) => {
832
1447
  console.error("Server error:", err);
@@ -870,55 +1485,128 @@ async function startServer(serverInstance, serverOptions) {
870
1485
  }
871
1486
 
872
1487
  // src/server/stop.ts
1488
+ var isShuttingDown = false;
873
1489
  async function stopServer(serverInstance, options = {}) {
874
1490
  const server = serverInstance.server;
875
1491
  const events = serverInstance.events;
1492
+ if (isShuttingDown) {
1493
+ console.log("\u26A0\uFE0F Shutdown already in progress, ignoring duplicate shutdown request");
1494
+ return;
1495
+ }
876
1496
  if (!server) {
877
1497
  return;
878
1498
  }
879
- const timeout = options.timeout || 3e4;
1499
+ isShuttingDown = true;
1500
+ const timeout = options.timeout || 5e3;
880
1501
  try {
881
1502
  if (options.onStopping) {
882
1503
  await options.onStopping();
883
1504
  }
884
1505
  events.emit("stopping");
885
- await serverInstance.pluginManager.onServerStop(serverInstance, server);
1506
+ if (serverInstance.router && typeof serverInstance.router.close === "function") {
1507
+ console.log("\u{1F50C} Closing router watchers...");
1508
+ try {
1509
+ await Promise.race([
1510
+ serverInstance.router.close(),
1511
+ new Promise(
1512
+ (_, reject) => setTimeout(() => reject(new Error("Router close timeout")), 2e3)
1513
+ )
1514
+ ]);
1515
+ console.log("\u2705 Router watchers closed");
1516
+ } catch (error) {
1517
+ console.error("\u274C Error closing router watchers:", error);
1518
+ }
1519
+ }
1520
+ try {
1521
+ await Promise.race([
1522
+ serverInstance.pluginManager.onServerStop(serverInstance, server),
1523
+ new Promise(
1524
+ (_, reject) => setTimeout(() => reject(new Error("Plugin stop timeout")), 2e3)
1525
+ )
1526
+ ]);
1527
+ } catch (error) {
1528
+ console.error("\u274C Plugin stop timeout:", error);
1529
+ }
1530
+ const closePromise = new Promise((resolve3, reject) => {
1531
+ server.close((err) => {
1532
+ if (err) return reject(err);
1533
+ resolve3();
1534
+ });
1535
+ });
886
1536
  const timeoutPromise = new Promise((_, reject) => {
887
1537
  setTimeout(() => {
888
- reject(new Error("Server shutdown timed out waiting for requests to complete"));
1538
+ reject(new Error("Server shutdown timeout"));
889
1539
  }, timeout);
890
1540
  });
891
- const closePromise = new Promise((resolve2, reject) => {
892
- server.close((err) => {
893
- if (err) {
894
- return reject(err);
895
- }
896
- resolve2();
897
- });
898
- });
899
1541
  await Promise.race([closePromise, timeoutPromise]);
900
- await serverInstance.pluginManager.terminatePlugins(serverInstance);
1542
+ try {
1543
+ await Promise.race([
1544
+ serverInstance.pluginManager.terminatePlugins(serverInstance),
1545
+ new Promise(
1546
+ (_, reject) => setTimeout(() => reject(new Error("Plugin terminate timeout")), 1e3)
1547
+ )
1548
+ ]);
1549
+ } catch (error) {
1550
+ console.error("\u274C Plugin terminate timeout:", error);
1551
+ }
901
1552
  if (options.onStopped) {
902
1553
  await options.onStopped();
903
1554
  }
904
1555
  events.emit("stopped");
905
1556
  serverInstance.server = null;
1557
+ console.log("\u2705 Graceful shutdown completed");
1558
+ isShuttingDown = false;
906
1559
  } catch (error) {
1560
+ isShuttingDown = false;
1561
+ console.error("\u26A0\uFE0F Shutdown error (forcing exit):", error);
1562
+ if (server && typeof server.close === "function") {
1563
+ server.close();
1564
+ }
1565
+ if (process.env.NODE_ENV === "development") {
1566
+ console.log("\u{1F504} Forcing exit for development restart...");
1567
+ process.exit(0);
1568
+ }
907
1569
  events.emit("error", error);
908
1570
  throw error;
909
1571
  }
910
1572
  }
911
1573
  function registerSignalHandlers(stopFn) {
912
- const sigintHandler = () => stopFn().catch(console.error);
913
- const sigtermHandler = () => stopFn().catch(console.error);
914
- process.on("SIGINT", sigintHandler);
915
- process.on("SIGTERM", sigtermHandler);
916
- return {
917
- unregister: () => {
918
- process.removeListener("SIGINT", sigintHandler);
919
- process.removeListener("SIGTERM", sigtermHandler);
920
- }
921
- };
1574
+ const isDevelopment = process.env.NODE_ENV === "development";
1575
+ if (isDevelopment) {
1576
+ const sigintHandler = () => {
1577
+ console.log("\u{1F4E4} SIGINT received, forcing exit for development restart...");
1578
+ process.exit(0);
1579
+ };
1580
+ const sigtermHandler = () => {
1581
+ console.log("\u{1F4E4} SIGTERM received, forcing exit for development restart...");
1582
+ process.exit(0);
1583
+ };
1584
+ process.on("SIGINT", sigintHandler);
1585
+ process.on("SIGTERM", sigtermHandler);
1586
+ return {
1587
+ unregister: () => {
1588
+ process.removeListener("SIGINT", sigintHandler);
1589
+ process.removeListener("SIGTERM", sigtermHandler);
1590
+ }
1591
+ };
1592
+ } else {
1593
+ const sigintHandler = () => {
1594
+ console.log("\u{1F4E4} SIGINT received, starting graceful shutdown...");
1595
+ stopFn().catch(console.error);
1596
+ };
1597
+ const sigtermHandler = () => {
1598
+ console.log("\u{1F4E4} SIGTERM received, starting graceful shutdown...");
1599
+ stopFn().catch(console.error);
1600
+ };
1601
+ process.on("SIGINT", sigintHandler);
1602
+ process.on("SIGTERM", sigtermHandler);
1603
+ return {
1604
+ unregister: () => {
1605
+ process.removeListener("SIGINT", sigintHandler);
1606
+ process.removeListener("SIGTERM", sigtermHandler);
1607
+ }
1608
+ };
1609
+ }
922
1610
  }
923
1611
 
924
1612
  // src/server/validation.ts
@@ -1124,59 +1812,33 @@ function validatePlugin(plugin, options = {}) {
1124
1812
  }
1125
1813
  }
1126
1814
 
1127
- // src/router/discovery/finder.ts
1815
+ // src/router/discovery/cache.ts
1816
+ import * as crypto from "node:crypto";
1128
1817
  import * as fs3 from "node:fs/promises";
1818
+ import { createRequire } from "node:module";
1129
1819
  import * as path3 from "node:path";
1130
- async function findRouteFiles(routesDir, options = {}) {
1131
- const absoluteDir = path3.isAbsolute(routesDir) ? routesDir : path3.resolve(process.cwd(), routesDir);
1132
- console.log("Creating router with routes directory:", absoluteDir);
1133
- try {
1134
- const stats = await fs3.stat(absoluteDir);
1135
- if (!stats.isDirectory()) {
1136
- throw new Error(`Route directory is not a directory: ${absoluteDir}`);
1137
- }
1138
- } catch (error) {
1139
- if (error.code === "ENOENT") {
1140
- throw new Error(`Route directory not found: ${absoluteDir}`);
1141
- }
1142
- throw error;
1143
- }
1144
- const routeFiles = [];
1145
- const ignore = options.ignore || ["node_modules", ".git"];
1146
- async function scanDirectory(dir) {
1147
- const entries = await fs3.readdir(dir, { withFileTypes: true });
1148
- for (const entry of entries) {
1149
- const fullPath = path3.join(dir, entry.name);
1150
- if (entry.isDirectory() && ignore.includes(entry.name)) {
1151
- continue;
1152
- }
1153
- if (entry.isDirectory()) {
1154
- await scanDirectory(fullPath);
1155
- } else if (isRouteFile(entry.name)) {
1156
- routeFiles.push(fullPath);
1157
- }
1158
- }
1159
- }
1160
- await scanDirectory(absoluteDir);
1161
- return routeFiles;
1162
- }
1163
- function isRouteFile(filename) {
1164
- return !filename.startsWith("_") && (filename.endsWith(".ts") || filename.endsWith(".js"));
1165
- }
1166
1820
 
1167
1821
  // src/router/discovery/loader.ts
1168
1822
  async function dynamicImport(filePath) {
1169
- return import(filePath);
1823
+ const cacheBuster = `?t=${Date.now()}`;
1824
+ const importPath = filePath + cacheBuster;
1825
+ try {
1826
+ const module = await import(importPath);
1827
+ console.log(`\u2705 Successfully imported module`);
1828
+ return module;
1829
+ } catch (error) {
1830
+ const errorMessage = error instanceof Error ? error.message : String(error);
1831
+ console.log(`\u26A0\uFE0F Error importing with cache buster, trying original path:`, errorMessage);
1832
+ return import(filePath);
1833
+ }
1170
1834
  }
1171
1835
  async function loadRouteModule(filePath, basePath) {
1172
1836
  try {
1173
1837
  const parsedRoute = parseRoutePath(filePath, basePath);
1174
- console.log("parsedRoute:", parsedRoute);
1175
1838
  const module = await dynamicImport(filePath);
1176
- console.log("Module exports:", Object.keys(module));
1839
+ console.log("\u{1F4E6} Module exports:", Object.keys(module));
1177
1840
  const routes = [];
1178
1841
  if (module.default && typeof module.default === "object") {
1179
- console.log("Found default export:", module.default);
1180
1842
  const route = {
1181
1843
  ...module.default,
1182
1844
  path: parsedRoute.routePath
@@ -1189,7 +1851,6 @@ async function loadRouteModule(filePath, basePath) {
1189
1851
  }
1190
1852
  const potentialRoute = exportValue;
1191
1853
  if (isValidRoute(potentialRoute)) {
1192
- console.log(`Found named route export: ${exportName}`, potentialRoute);
1193
1854
  const route = {
1194
1855
  ...potentialRoute,
1195
1856
  // Use the route's own path if it has one, otherwise derive from file
@@ -1202,7 +1863,7 @@ async function loadRouteModule(filePath, basePath) {
1202
1863
  console.warn(`Route file ${filePath} does not export any valid route definitions`);
1203
1864
  return [];
1204
1865
  }
1205
- console.log(`Loaded ${routes.length} route(s) from ${filePath}`);
1866
+ console.log(`\u2705 Successfully Loaded ${routes.length} route(s)`);
1206
1867
  return routes;
1207
1868
  } catch (error) {
1208
1869
  console.error(`Failed to load route module ${filePath}:`, error);
@@ -1220,25 +1881,222 @@ function isValidRoute(obj) {
1220
1881
  return hasHttpMethod;
1221
1882
  }
1222
1883
 
1223
- // src/router/discovery/index.ts
1224
- async function findRoutes(routesDir, options = {}) {
1225
- const routeFiles = await findRouteFiles(routesDir, {
1226
- ignore: options.ignore
1227
- });
1228
- const routes = [];
1229
- for (const filePath of routeFiles) {
1230
- const moduleRoutes = await loadRouteModule(filePath, routesDir);
1231
- if (moduleRoutes.length > 0) {
1232
- routes.push(...moduleRoutes);
1233
- }
1884
+ // src/router/discovery/cache.ts
1885
+ var fileRouteCache = /* @__PURE__ */ new Map();
1886
+ async function processChangedFile(filePath, routesDir, updateCache = true) {
1887
+ const stat3 = await fs3.stat(filePath);
1888
+ const lastModified = stat3.mtime.getTime();
1889
+ const cachedEntry = fileRouteCache.get(filePath);
1890
+ if (updateCache && cachedEntry && cachedEntry.timestamp === lastModified) {
1891
+ return cachedEntry.routes;
1892
+ }
1893
+ invalidateModuleCache(filePath);
1894
+ const routes = await loadRouteModule(filePath, routesDir);
1895
+ if (updateCache) {
1896
+ const hash = hashRoutes(routes);
1897
+ fileRouteCache.set(filePath, {
1898
+ routes,
1899
+ timestamp: lastModified,
1900
+ hash
1901
+ });
1234
1902
  }
1235
1903
  return routes;
1236
1904
  }
1905
+ function hasRouteContentChanged(filePath, newRoutes) {
1906
+ const cachedEntry = fileRouteCache.get(filePath);
1907
+ if (!cachedEntry) {
1908
+ return true;
1909
+ }
1910
+ const newHash = hashRoutes(newRoutes);
1911
+ return cachedEntry.hash !== newHash;
1912
+ }
1913
+ function clearFileCache(filePath) {
1914
+ if (filePath) {
1915
+ fileRouteCache.delete(filePath);
1916
+ } else {
1917
+ fileRouteCache.clear();
1918
+ }
1919
+ }
1920
+ function hashRoutes(routes) {
1921
+ const routeData = routes.map((route) => ({
1922
+ path: route.path,
1923
+ methods: Object.keys(route).filter((key) => key !== "path").sort().map((method) => {
1924
+ const methodDef = route[method];
1925
+ const handlerString = methodDef?.handler ? methodDef.handler.toString() : null;
1926
+ return {
1927
+ method,
1928
+ // Include handler function string for change detection
1929
+ handler: handlerString,
1930
+ // Include middleware if present
1931
+ middleware: methodDef?.middleware ? methodDef.middleware.length : 0,
1932
+ // Include schema structure (but not full serialization which can be unstable)
1933
+ hasSchema: !!methodDef?.schema,
1934
+ schemaKeys: methodDef?.schema ? Object.keys(methodDef.schema).sort() : []
1935
+ };
1936
+ })
1937
+ }));
1938
+ const dataString = JSON.stringify(routeData);
1939
+ const hash = crypto.createHash("md5").update(dataString).digest("hex");
1940
+ return hash;
1941
+ }
1942
+ function invalidateModuleCache(filePath) {
1943
+ try {
1944
+ const absolutePath = path3.resolve(filePath);
1945
+ if (typeof __require !== "undefined") {
1946
+ delete __require.cache[absolutePath];
1947
+ try {
1948
+ const resolvedPath = __require.resolve(absolutePath);
1949
+ delete __require.cache[resolvedPath];
1950
+ } catch (resolveError) {
1951
+ const errorMessage = resolveError instanceof Error ? resolveError.message : String(resolveError);
1952
+ console.log(`\u26A0\uFE0F Could not resolve path: ${errorMessage}`);
1953
+ }
1954
+ } else {
1955
+ try {
1956
+ const require2 = createRequire(import.meta.url);
1957
+ delete require2.cache[absolutePath];
1958
+ try {
1959
+ const resolvedPath = require2.resolve(absolutePath);
1960
+ delete require2.cache[resolvedPath];
1961
+ } catch {
1962
+ console.log(`\u26A0\uFE0F Could not resolve ESM path`);
1963
+ }
1964
+ } catch {
1965
+ console.log(`\u26A0\uFE0F createRequire not available in pure ESM`);
1966
+ }
1967
+ }
1968
+ } catch (error) {
1969
+ console.log(`\u26A0\uFE0F Error during module cache invalidation for ${filePath}:`, error);
1970
+ }
1971
+ }
1237
1972
 
1238
- // src/router/discovery/watchers.ts
1973
+ // src/router/discovery/parallel.ts
1974
+ import * as os from "node:os";
1975
+
1976
+ // src/router/discovery/finder.ts
1977
+ import * as fs4 from "node:fs/promises";
1239
1978
  import * as path4 from "node:path";
1979
+ async function findRouteFiles(routesDir, options = {}) {
1980
+ const absoluteDir = path4.isAbsolute(routesDir) ? routesDir : path4.resolve(process.cwd(), routesDir);
1981
+ console.log("Creating router with routes directory:", absoluteDir);
1982
+ try {
1983
+ const stats = await fs4.stat(absoluteDir);
1984
+ if (!stats.isDirectory()) {
1985
+ throw new Error(`Route directory is not a directory: ${absoluteDir}`);
1986
+ }
1987
+ } catch (error) {
1988
+ if (error.code === "ENOENT") {
1989
+ throw new Error(`Route directory not found: ${absoluteDir}`);
1990
+ }
1991
+ throw error;
1992
+ }
1993
+ const routeFiles = [];
1994
+ const ignore = options.ignore || ["node_modules", ".git"];
1995
+ async function scanDirectory(dir) {
1996
+ const entries = await fs4.readdir(dir, { withFileTypes: true });
1997
+ for (const entry of entries) {
1998
+ const fullPath = path4.join(dir, entry.name);
1999
+ if (entry.isDirectory() && ignore.includes(entry.name)) {
2000
+ continue;
2001
+ }
2002
+ if (entry.isDirectory()) {
2003
+ await scanDirectory(fullPath);
2004
+ } else if (isRouteFile(entry.name)) {
2005
+ routeFiles.push(fullPath);
2006
+ }
2007
+ }
2008
+ }
2009
+ await scanDirectory(absoluteDir);
2010
+ return routeFiles;
2011
+ }
2012
+ function isRouteFile(filename) {
2013
+ return !filename.startsWith("_") && (filename.endsWith(".ts") || filename.endsWith(".js"));
2014
+ }
2015
+
2016
+ // src/router/discovery/parallel.ts
2017
+ async function processFilesInParallel(filePaths, processor, concurrency = Math.max(1, Math.floor(os.cpus().length / 2))) {
2018
+ const chunks = chunkArray(filePaths, concurrency);
2019
+ const results = [];
2020
+ for (const chunk of chunks) {
2021
+ const chunkResults = await Promise.allSettled(chunk.map((filePath) => processor(filePath)));
2022
+ const successfulResults = chunkResults.filter((result) => result.status === "fulfilled").map((result) => result.value);
2023
+ results.push(...successfulResults);
2024
+ }
2025
+ return results;
2026
+ }
2027
+ async function loadInitialRoutesParallel(routesDir) {
2028
+ const files = await findRouteFiles(routesDir);
2029
+ const routeArrays = await processFilesInParallel(
2030
+ files,
2031
+ (filePath) => processChangedFile(filePath, routesDir)
2032
+ );
2033
+ return routeArrays.flat();
2034
+ }
2035
+ function chunkArray(array, chunkSize) {
2036
+ const chunks = [];
2037
+ for (let i = 0; i < array.length; i += chunkSize) {
2038
+ chunks.push(array.slice(i, i + chunkSize));
2039
+ }
2040
+ return chunks;
2041
+ }
2042
+
2043
+ // src/router/discovery/profiler.ts
2044
+ var profilerState = {
2045
+ fileChanges: 0,
2046
+ totalReloadTime: 0,
2047
+ averageReloadTime: 0,
2048
+ slowReloads: []
2049
+ };
2050
+ function trackReloadPerformance(filePath, startTime) {
2051
+ const duration = Date.now() - startTime;
2052
+ profilerState.fileChanges++;
2053
+ profilerState.totalReloadTime += duration;
2054
+ profilerState.averageReloadTime = profilerState.totalReloadTime / profilerState.fileChanges;
2055
+ if (duration > 100) {
2056
+ profilerState.slowReloads.push({ file: filePath, time: duration });
2057
+ if (profilerState.slowReloads.length > 10) {
2058
+ profilerState.slowReloads.shift();
2059
+ }
2060
+ }
2061
+ if (process.env.NODE_ENV === "development") {
2062
+ const emoji = duration < 50 ? "\u26A1" : duration < 100 ? "\u{1F504}" : "\u{1F40C}";
2063
+ console.log(`${emoji} Route reload: ${filePath} (${duration}ms)`);
2064
+ }
2065
+ }
2066
+ function withPerformanceTracking(fn, filePath) {
2067
+ console.log(`Tracking performance for: ${filePath}`);
2068
+ return async (...args) => {
2069
+ const startTime = Date.now();
2070
+ try {
2071
+ const result = await fn(...args);
2072
+ trackReloadPerformance(filePath, startTime);
2073
+ return result;
2074
+ } catch (error) {
2075
+ trackReloadPerformance(filePath, startTime);
2076
+ throw error;
2077
+ }
2078
+ };
2079
+ }
2080
+
2081
+ // src/router/discovery/watchers.ts
2082
+ import * as path5 from "node:path";
1240
2083
  import { watch } from "chokidar";
1241
2084
  function watchRoutes(routesDir, options = {}) {
2085
+ const debounceMs = options.debounceMs || 16;
2086
+ const debouncedCallbacks = /* @__PURE__ */ new Map();
2087
+ function createDebouncedCallback(fn, filePath) {
2088
+ return (...args) => {
2089
+ const existingTimeout = debouncedCallbacks.get(filePath);
2090
+ if (existingTimeout) {
2091
+ clearTimeout(existingTimeout);
2092
+ }
2093
+ const timeoutId = setTimeout(() => {
2094
+ fn(...args);
2095
+ debouncedCallbacks.delete(filePath);
2096
+ }, debounceMs);
2097
+ debouncedCallbacks.set(filePath, timeoutId);
2098
+ };
2099
+ }
1242
2100
  const routesByPath = /* @__PURE__ */ new Map();
1243
2101
  async function loadInitialRoutes() {
1244
2102
  try {
@@ -1254,28 +2112,34 @@ function watchRoutes(routesDir, options = {}) {
1254
2112
  }
1255
2113
  async function loadAndNotify(filePath) {
1256
2114
  try {
1257
- const routes = await loadRouteModule(filePath, routesDir);
1258
- if (!routes || routes.length === 0) {
2115
+ const existingRoutes = routesByPath.get(filePath);
2116
+ const newRoutes = await processChangedFile(filePath, routesDir, false);
2117
+ if (!newRoutes || newRoutes.length === 0) {
1259
2118
  return;
1260
2119
  }
1261
- const existingRoutes = routesByPath.get(filePath);
2120
+ if (existingRoutes && !hasRouteContentChanged(filePath, newRoutes)) {
2121
+ return;
2122
+ }
2123
+ await processChangedFile(filePath, routesDir, true);
2124
+ const normalizedPath = path5.normalize(filePath);
1262
2125
  if (existingRoutes) {
1263
- routesByPath.set(filePath, routes);
2126
+ routesByPath.set(filePath, newRoutes);
1264
2127
  if (options.onRouteChanged) {
1265
- options.onRouteChanged(routes);
2128
+ options.onRouteChanged(normalizedPath, newRoutes);
1266
2129
  }
1267
2130
  } else {
1268
- routesByPath.set(filePath, routes);
2131
+ routesByPath.set(filePath, newRoutes);
1269
2132
  if (options.onRouteAdded) {
1270
- options.onRouteAdded(routes);
2133
+ options.onRouteAdded(normalizedPath, newRoutes);
1271
2134
  }
1272
2135
  }
1273
2136
  } catch (error) {
2137
+ console.log(`\u26A0\uFE0F Error processing file ${filePath}:`, error);
1274
2138
  handleError(error);
1275
2139
  }
1276
2140
  }
1277
2141
  function handleRemoved(filePath) {
1278
- const normalizedPath = path4.normalize(filePath);
2142
+ const normalizedPath = path5.normalize(filePath);
1279
2143
  const routes = routesByPath.get(normalizedPath);
1280
2144
  if (routes && routes.length > 0 && options.onRouteRemoved) {
1281
2145
  options.onRouteRemoved(normalizedPath, routes);
@@ -1286,33 +2150,53 @@ function watchRoutes(routesDir, options = {}) {
1286
2150
  if (options.onError && error instanceof Error) {
1287
2151
  options.onError(error);
1288
2152
  } else {
1289
- console.error("Route watcher error:", error);
2153
+ console.error("\u26A0\uFE0F Route watcher error:", error);
1290
2154
  }
1291
2155
  }
1292
2156
  const watcher = watch(routesDir, {
2157
+ // Much faster response times
2158
+ awaitWriteFinish: {
2159
+ stabilityThreshold: 50,
2160
+ // Reduced from 300ms
2161
+ pollInterval: 10
2162
+ // Reduced from 100ms
2163
+ },
2164
+ // Performance optimizations
2165
+ usePolling: false,
2166
+ atomic: true,
2167
+ followSymlinks: false,
2168
+ depth: 10,
2169
+ // More aggressive ignoring
1293
2170
  ignored: [
1294
2171
  /(^|[/\\])\../,
1295
- // Ignore dot files
1296
2172
  /node_modules/,
2173
+ /\.git/,
2174
+ /\.DS_Store/,
2175
+ /Thumbs\.db/,
2176
+ /\.(test|spec)\.(ts|js)$/,
2177
+ /\.d\.ts$/,
2178
+ /\.map$/,
2179
+ /~$/,
1297
2180
  ...options.ignore || []
1298
- ],
1299
- persistent: true,
1300
- ignoreInitial: false,
1301
- awaitWriteFinish: {
1302
- stabilityThreshold: 300,
1303
- pollInterval: 100
1304
- }
2181
+ ]
1305
2182
  });
1306
- watcher.on("add", loadAndNotify).on("change", loadAndNotify).on("unlink", handleRemoved).on("error", handleError);
2183
+ watcher.on("add", (filePath) => {
2184
+ const debouncedLoad = createDebouncedCallback(loadAndNotify, filePath);
2185
+ debouncedLoad(filePath);
2186
+ }).on("change", (filePath) => {
2187
+ const debouncedLoad = createDebouncedCallback(loadAndNotify, filePath);
2188
+ debouncedLoad(filePath);
2189
+ }).on("unlink", (filePath) => {
2190
+ const debouncedRemove = createDebouncedCallback(handleRemoved, filePath);
2191
+ debouncedRemove(filePath);
2192
+ }).on("error", handleError);
1307
2193
  loadInitialRoutes().catch(handleError);
1308
2194
  return {
1309
- /**
1310
- * Close the watcher
1311
- */
1312
- close: () => watcher.close(),
1313
- /**
1314
- * Get all currently loaded routes (flattened)
1315
- */
2195
+ close: () => {
2196
+ debouncedCallbacks.forEach((timeout) => clearTimeout(timeout));
2197
+ debouncedCallbacks.clear();
2198
+ return watcher.close();
2199
+ },
1316
2200
  getRoutes: () => {
1317
2201
  const allRoutes = [];
1318
2202
  for (const routes of routesByPath.values()) {
@@ -1320,88 +2204,12 @@ function watchRoutes(routesDir, options = {}) {
1320
2204
  }
1321
2205
  return allRoutes;
1322
2206
  },
1323
- /**
1324
- * Get routes organized by file path
1325
- */
1326
2207
  getRoutesByFile: () => new Map(routesByPath)
1327
2208
  };
1328
2209
  }
1329
2210
 
1330
- // src/router/handlers/error.ts
1331
- function handleRouteError(ctx, error, options = {}) {
1332
- if (options.log) {
1333
- console.error("Route error:", error);
1334
- }
1335
- const status = getErrorStatus(error);
1336
- const response = {
1337
- error: getErrorType(error),
1338
- message: getErrorMessage(error)
1339
- };
1340
- if (options.detailed) {
1341
- if (error instanceof Error) {
1342
- response.stack = error.stack;
1343
- }
1344
- if (error && typeof error === "object" && "details" in error && error.details) {
1345
- response.details = error.details;
1346
- }
1347
- }
1348
- ctx.response.status(status).json(response);
1349
- }
1350
- function getErrorStatus(error) {
1351
- if (error && typeof error === "object") {
1352
- if ("status" in error && typeof error.status === "number") {
1353
- return error.status;
1354
- }
1355
- if ("statusCode" in error && typeof error.statusCode === "number") {
1356
- return error.statusCode;
1357
- }
1358
- if ("code" in error && typeof error.code === "string") {
1359
- return getStatusFromCode(error.code);
1360
- }
1361
- }
1362
- return 500;
1363
- }
1364
- function getStatusFromCode(code) {
1365
- switch (code) {
1366
- case "NOT_FOUND":
1367
- return 404;
1368
- case "UNAUTHORIZED":
1369
- return 401;
1370
- case "FORBIDDEN":
1371
- return 403;
1372
- case "BAD_REQUEST":
1373
- return 400;
1374
- case "CONFLICT":
1375
- return 409;
1376
- default:
1377
- return 500;
1378
- }
1379
- }
1380
- function getErrorType(error) {
1381
- if (error && typeof error === "object") {
1382
- if ("type" in error && typeof error.type === "string") {
1383
- return error.type;
1384
- }
1385
- if ("name" in error && typeof error.name === "string") {
1386
- return error.name;
1387
- }
1388
- if (error instanceof Error) {
1389
- return error.constructor.name;
1390
- }
1391
- }
1392
- return "Error";
1393
- }
1394
- function getErrorMessage(error) {
1395
- if (error instanceof Error) {
1396
- return error.message;
1397
- }
1398
- if (error && typeof error === "object") {
1399
- if ("message" in error && typeof error.message === "string") {
1400
- return error.message;
1401
- }
1402
- }
1403
- return String(error);
1404
- }
2211
+ // src/router/validation/schema.ts
2212
+ import { z as z6 } from "zod";
1405
2213
 
1406
2214
  // src/router/validation/body.ts
1407
2215
  import { z as z2 } from "zod";
@@ -1442,35 +2250,45 @@ function validateResponse(response, schema) {
1442
2250
  // src/router/validation/schema.ts
1443
2251
  function createRequestValidator(schema, debug = false) {
1444
2252
  const middlewareFn = async (ctx, next) => {
1445
- const errors = {};
1446
2253
  if (schema.params && ctx.request.params) {
1447
2254
  try {
1448
2255
  ctx.request.params = validateParams(ctx.request.params, schema.params);
1449
2256
  } catch (error) {
1450
- errors.params = formatValidationError(error);
2257
+ const fieldErrors = extractZodFieldErrors(error);
2258
+ const errorCount = fieldErrors.reduce((sum, fe) => sum + fe.messages.length, 0);
2259
+ throw new ValidationError("Request validation failed", {
2260
+ fields: fieldErrors,
2261
+ errorCount,
2262
+ section: "params"
2263
+ });
1451
2264
  }
1452
2265
  }
1453
2266
  if (schema.query && ctx.request.query) {
1454
2267
  try {
1455
2268
  ctx.request.query = validateQuery(ctx.request.query, schema.query);
1456
2269
  } catch (error) {
1457
- errors.query = formatValidationError(error);
2270
+ const fieldErrors = extractZodFieldErrors(error);
2271
+ const errorCount = fieldErrors.reduce((sum, fe) => sum + fe.messages.length, 0);
2272
+ throw new ValidationError("Request validation failed", {
2273
+ fields: fieldErrors,
2274
+ errorCount,
2275
+ section: "query"
2276
+ });
1458
2277
  }
1459
2278
  }
1460
2279
  if (schema.body) {
1461
2280
  try {
1462
2281
  ctx.request.body = validateBody(ctx.request.body, schema.body);
1463
2282
  } catch (error) {
1464
- errors.body = formatValidationError(error);
2283
+ const fieldErrors = extractZodFieldErrors(error);
2284
+ const errorCount = fieldErrors.reduce((sum, fe) => sum + fe.messages.length, 0);
2285
+ throw new ValidationError("Request validation failed", {
2286
+ fields: fieldErrors,
2287
+ errorCount,
2288
+ section: "body"
2289
+ });
1465
2290
  }
1466
2291
  }
1467
- if (Object.keys(errors).length > 0) {
1468
- ctx.response.status(400).json({
1469
- error: "Validation Error",
1470
- details: errors
1471
- });
1472
- return;
1473
- }
1474
2292
  await next();
1475
2293
  };
1476
2294
  return {
@@ -1489,12 +2307,11 @@ function createResponseValidator(responseSchema, debug = false) {
1489
2307
  return originalJson.call(ctx.response, validatedBody, status);
1490
2308
  } catch (error) {
1491
2309
  ctx.response.json = originalJson;
1492
- console.error("Response validation error:", error);
1493
- ctx.response.status(500).json({
1494
- error: "Internal Server Error",
1495
- message: "Response validation failed"
2310
+ throw new InternalServerError("Response validation failed", {
2311
+ responseSchema: responseSchema.description || "Unknown schema",
2312
+ validationError: extractZodFieldErrors(error),
2313
+ originalResponse: body
1496
2314
  });
1497
- return ctx.response;
1498
2315
  }
1499
2316
  };
1500
2317
  await next();
@@ -1505,11 +2322,25 @@ function createResponseValidator(responseSchema, debug = false) {
1505
2322
  debug
1506
2323
  };
1507
2324
  }
1508
- function formatValidationError(error) {
1509
- if (error && typeof error === "object" && "format" in error && typeof error.format === "function") {
1510
- return error.format();
2325
+ function extractZodFieldErrors(error) {
2326
+ if (error instanceof z6.ZodError) {
2327
+ const fieldErrorMap = /* @__PURE__ */ new Map();
2328
+ for (const issue of error.issues) {
2329
+ const fieldPath = issue.path.length > 0 ? issue.path.join(".") : "root";
2330
+ if (!fieldErrorMap.has(fieldPath)) {
2331
+ fieldErrorMap.set(fieldPath, []);
2332
+ }
2333
+ fieldErrorMap.get(fieldPath).push(issue.message);
2334
+ }
2335
+ return Array.from(fieldErrorMap.entries()).map(([field, messages]) => ({
2336
+ field,
2337
+ messages
2338
+ }));
2339
+ }
2340
+ if (error instanceof Error) {
2341
+ return [{ field: "unknown", messages: [error.message] }];
1511
2342
  }
1512
- return error instanceof Error ? error.message : String(error);
2343
+ return [{ field: "unknown", messages: [String(error)] }];
1513
2344
  }
1514
2345
 
1515
2346
  // src/router/handlers/executor.ts
@@ -1533,8 +2364,8 @@ async function executeHandler(ctx, routeOptions, params) {
1533
2364
  }
1534
2365
 
1535
2366
  // src/router/matching/params.ts
1536
- function extractParams(path5, pattern, paramNames) {
1537
- const match = pattern.exec(path5);
2367
+ function extractParams(path6, pattern, paramNames) {
2368
+ const match = pattern.exec(path6);
1538
2369
  if (!match) {
1539
2370
  return {};
1540
2371
  }
@@ -1544,15 +2375,15 @@ function extractParams(path5, pattern, paramNames) {
1544
2375
  }
1545
2376
  return params;
1546
2377
  }
1547
- function compilePathPattern(path5) {
2378
+ function compilePathPattern(path6) {
1548
2379
  const paramNames = [];
1549
- if (path5 === "/") {
2380
+ if (path6 === "/") {
1550
2381
  return {
1551
2382
  pattern: /^\/$/,
1552
2383
  paramNames: []
1553
2384
  };
1554
2385
  }
1555
- let patternString = path5.replace(/([.+*?^$(){}|\\])/g, "\\$1");
2386
+ let patternString = path6.replace(/([.+*?^$(){}|\\])/g, "\\$1");
1556
2387
  patternString = patternString.replace(/\/:([^/]+)/g, (_, paramName) => {
1557
2388
  paramNames.push(paramName);
1558
2389
  return "/([^/]+)";
@@ -1575,10 +2406,10 @@ function createMatcher() {
1575
2406
  /**
1576
2407
  * Add a route to the matcher
1577
2408
  */
1578
- add(path5, method, routeOptions) {
1579
- const { pattern, paramNames } = compilePathPattern(path5);
2409
+ add(path6, method, routeOptions) {
2410
+ const { pattern, paramNames } = compilePathPattern(path6);
1580
2411
  const newRoute = {
1581
- path: path5,
2412
+ path: path6,
1582
2413
  method,
1583
2414
  pattern,
1584
2415
  paramNames,
@@ -1591,17 +2422,33 @@ function createMatcher() {
1591
2422
  routes.splice(insertIndex, 0, newRoute);
1592
2423
  }
1593
2424
  },
2425
+ /**
2426
+ * Remove a route from the matcher by path
2427
+ */
2428
+ remove(path6) {
2429
+ for (let i = routes.length - 1; i >= 0; i--) {
2430
+ if (routes[i].path === path6) {
2431
+ routes.splice(i, 1);
2432
+ }
2433
+ }
2434
+ },
2435
+ /**
2436
+ * Clear all routes from the matcher
2437
+ */
2438
+ clear() {
2439
+ routes.length = 0;
2440
+ },
1594
2441
  /**
1595
2442
  * Match a URL path to a route
1596
2443
  */
1597
- match(path5, method) {
1598
- const pathname = path5.split("?")[0];
2444
+ match(path6, method) {
2445
+ const pathname = path6.split("?")[0];
1599
2446
  if (!pathname) return null;
1600
2447
  for (const route of routes) {
1601
2448
  if (route.method !== method) continue;
1602
2449
  const match = route.pattern.exec(pathname);
1603
2450
  if (match) {
1604
- const params = extractParams(path5, route.pattern, route.paramNames);
2451
+ const params = extractParams(path6, route.pattern, route.paramNames);
1605
2452
  return {
1606
2453
  route: route.routeOptions,
1607
2454
  params
@@ -1609,14 +2456,14 @@ function createMatcher() {
1609
2456
  }
1610
2457
  }
1611
2458
  const matchingPath = routes.find(
1612
- (route) => route.method !== method && route.pattern.test(path5)
2459
+ (route) => route.method !== method && route.pattern.test(path6)
1613
2460
  );
1614
2461
  if (matchingPath) {
1615
2462
  return {
1616
2463
  route: null,
1617
2464
  params: {},
1618
2465
  methodNotAllowed: true,
1619
- allowedMethods: routes.filter((route) => route.pattern.test(path5)).map((route) => route.method)
2466
+ allowedMethods: routes.filter((route) => route.pattern.test(path6)).map((route) => route.method)
1620
2467
  };
1621
2468
  }
1622
2469
  return null;
@@ -1633,16 +2480,93 @@ function createMatcher() {
1633
2480
  /**
1634
2481
  * Find routes matching a specific path
1635
2482
  */
1636
- findRoutes(path5) {
1637
- return routes.filter((route) => route.pattern.test(path5)).map((route) => ({
2483
+ findRoutes(path6) {
2484
+ return routes.filter((route) => route.pattern.test(path6)).map((route) => ({
1638
2485
  path: route.path,
1639
2486
  method: route.method,
1640
- params: extractParams(path5, route.pattern, route.paramNames)
2487
+ params: extractParams(path6, route.pattern, route.paramNames)
1641
2488
  }));
1642
2489
  }
1643
2490
  };
1644
2491
  }
1645
2492
 
2493
+ // src/router/registry/fast-registry.ts
2494
+ function createRouteRegistry() {
2495
+ return {
2496
+ routesByPath: /* @__PURE__ */ new Map(),
2497
+ routesByFile: /* @__PURE__ */ new Map(),
2498
+ pathToFile: /* @__PURE__ */ new Map()
2499
+ };
2500
+ }
2501
+ function updateRoutesFromFile(registry, filePath, newRoutes) {
2502
+ console.log(`Updating routes from file: ${filePath}`);
2503
+ const oldPaths = registry.routesByFile.get(filePath) || /* @__PURE__ */ new Set();
2504
+ const newPaths = new Set(newRoutes.map((r) => r.path));
2505
+ const added = newRoutes.filter((r) => !oldPaths.has(r.path));
2506
+ const removed = Array.from(oldPaths).filter((p) => !newPaths.has(p));
2507
+ const potentiallyChanged = newRoutes.filter((r) => oldPaths.has(r.path));
2508
+ const changed = potentiallyChanged.filter((route) => {
2509
+ const existingRoute = registry.routesByPath.get(route.path);
2510
+ return !existingRoute || !routesEqual(existingRoute, route);
2511
+ });
2512
+ applyRouteUpdates(registry, filePath, { added, removed, changed });
2513
+ return { added, removed, changed };
2514
+ }
2515
+ function getAllRoutesFromRegistry(registry) {
2516
+ return Array.from(registry.routesByPath.values());
2517
+ }
2518
+ function applyRouteUpdates(registry, filePath, updates) {
2519
+ const { added, removed, changed } = updates;
2520
+ removed.forEach((path6) => {
2521
+ registry.routesByPath.delete(path6);
2522
+ registry.pathToFile.delete(path6);
2523
+ });
2524
+ [...added, ...changed].forEach((route) => {
2525
+ registry.routesByPath.set(route.path, route);
2526
+ registry.pathToFile.set(route.path, filePath);
2527
+ });
2528
+ const allPathsForFile = /* @__PURE__ */ new Set([
2529
+ ...added.map((r) => r.path),
2530
+ ...changed.map((r) => r.path),
2531
+ ...Array.from(registry.routesByFile.get(filePath) || []).filter((p) => !removed.includes(p))
2532
+ ]);
2533
+ if (allPathsForFile.size > 0) {
2534
+ registry.routesByFile.set(filePath, allPathsForFile);
2535
+ } else {
2536
+ registry.routesByFile.delete(filePath);
2537
+ }
2538
+ }
2539
+ function routesEqual(route1, route2) {
2540
+ if (route1.path !== route2.path) return false;
2541
+ const methods1 = Object.keys(route1).filter((k) => k !== "path").sort();
2542
+ const methods2 = Object.keys(route2).filter((k) => k !== "path").sort();
2543
+ if (methods1.length !== methods2.length) return false;
2544
+ return methods1.every((method) => {
2545
+ const handler1 = route1[method];
2546
+ const handler2 = route2[method];
2547
+ return typeof handler1 === typeof handler2;
2548
+ });
2549
+ }
2550
+
2551
+ // src/router/utils/matching-helpers.ts
2552
+ function addRouteToMatcher(route, matcher) {
2553
+ Object.entries(route).forEach(([method, methodOptions]) => {
2554
+ if (method === "path" || !methodOptions) return;
2555
+ matcher.add(route.path, method, methodOptions);
2556
+ });
2557
+ }
2558
+ function removeRouteFromMatcher(path6, matcher) {
2559
+ if ("remove" in matcher && typeof matcher.remove === "function") {
2560
+ matcher.remove(path6);
2561
+ } else {
2562
+ console.warn("Matcher does not support selective removal, consider adding remove() method");
2563
+ }
2564
+ }
2565
+ function updateRouteInMatcher(route, matcher) {
2566
+ removeRouteFromMatcher(route.path, matcher);
2567
+ addRouteToMatcher(route, matcher);
2568
+ }
2569
+
1646
2570
  // src/router/router.ts
1647
2571
  var DEFAULT_ROUTER_OPTIONS = {
1648
2572
  routesDir: "./routes",
@@ -1657,46 +2581,55 @@ function createRouter(options) {
1657
2581
  if (options.basePath && !options.basePath.startsWith("/")) {
1658
2582
  console.warn("Base path does nothing");
1659
2583
  }
1660
- const routes = [];
2584
+ const registry = createRouteRegistry();
1661
2585
  const matcher = createMatcher();
1662
2586
  let initialized = false;
1663
2587
  let initializationPromise = null;
1664
2588
  let _watchers = null;
1665
- const routeSources = /* @__PURE__ */ new Map();
1666
2589
  const routeDirectories = /* @__PURE__ */ new Set([routerOptions.routesDir]);
1667
- function addRouteWithSource(route, source) {
1668
- const existingSources = routeSources.get(route.path) || [];
1669
- if (existingSources.includes(source)) {
1670
- console.warn(`Skipping duplicate route: ${route.path} from ${source}`);
1671
- return;
1672
- }
1673
- if (existingSources.length > 0) {
1674
- const conflictError = new Error(
1675
- `Route conflict for path "${route.path}": already defined in ${existingSources.join(", ")}, now being added from ${source}`
1676
- );
1677
- console.error(conflictError.message);
1678
- throw conflictError;
2590
+ function applyMatcherChanges(changes) {
2591
+ console.log("\n\u{1F527} APPLYING MATCHER CHANGES:");
2592
+ console.log(` Adding ${changes.added.length} routes`);
2593
+ console.log(` Removing ${changes.removed.length} routes`);
2594
+ console.log(` Updating ${changes.changed.length} routes`);
2595
+ changes.removed.forEach((routePath) => {
2596
+ console.log(` \u2796 Removing: ${routePath}`);
2597
+ removeRouteFromMatcher(routePath, matcher);
2598
+ });
2599
+ changes.added.forEach((route) => {
2600
+ const methods = Object.keys(route).filter((key) => key !== "path");
2601
+ console.log(` \u2795 Adding: ${route.path} [${methods.join(", ")}]`);
2602
+ addRouteToMatcher(route, matcher);
2603
+ });
2604
+ changes.changed.forEach((route) => {
2605
+ const methods = Object.keys(route).filter((key) => key !== "path");
2606
+ console.log(` \u{1F504} Updating: ${route.path} [${methods.join(", ")}]`);
2607
+ updateRouteInMatcher(route, matcher);
2608
+ });
2609
+ console.log("\u2705 Matcher changes applied\n");
2610
+ }
2611
+ function addRoutesWithSource(routes, source) {
2612
+ try {
2613
+ const changes = updateRoutesFromFile(registry, source, routes);
2614
+ applyMatcherChanges(changes);
2615
+ return changes;
2616
+ } catch (error) {
2617
+ console.error(`\u26A0\uFE0F Route conflicts from ${source}:`, error);
2618
+ throw error;
1679
2619
  }
1680
- routeSources.set(route.path, [...existingSources, source]);
1681
- addRouteInternal(route);
1682
2620
  }
1683
2621
  async function loadRoutesFromDirectory(directory, source, prefix) {
1684
2622
  try {
1685
- const discoveredRoutes = await findRoutes(directory, {
1686
- basePath: routerOptions.basePath
1687
- });
1688
- for (const route of discoveredRoutes) {
1689
- const finalRoute = prefix ? {
1690
- ...route,
1691
- path: `${prefix}${route.path}`
1692
- } : route;
1693
- addRouteWithSource(finalRoute, source);
1694
- }
2623
+ const discoveredRoutes = await loadInitialRoutesParallel(directory);
2624
+ const finalRoutes = discoveredRoutes.map(
2625
+ (route) => prefix ? { ...route, path: `${prefix}${route.path}` } : route
2626
+ );
2627
+ const changes = addRoutesWithSource(finalRoutes, source);
1695
2628
  console.log(
1696
- `Loaded ${discoveredRoutes.length} routes from ${source}${prefix ? ` with prefix ${prefix}` : ""}`
2629
+ `Loaded ${discoveredRoutes.length} routes from ${source}${prefix ? ` with prefix ${prefix}` : ""} (${changes.added.length} added, ${changes.changed.length} changed, ${changes.removed.length} removed)`
1697
2630
  );
1698
2631
  } catch (error) {
1699
- console.error(`Failed to load routes from ${source}:`, error);
2632
+ console.error(`\u26A0\uFE0F Failed to load routes from ${source}:`, error);
1700
2633
  throw error;
1701
2634
  }
1702
2635
  }
@@ -1706,105 +2639,126 @@ function createRouter(options) {
1706
2639
  }
1707
2640
  initializationPromise = (async () => {
1708
2641
  try {
1709
- for (const directory of routeDirectories) {
1710
- await loadRoutesFromDirectory(directory, directory);
1711
- }
2642
+ await Promise.all(
2643
+ Array.from(routeDirectories).map(
2644
+ (directory) => loadRoutesFromDirectory(directory, directory)
2645
+ )
2646
+ );
1712
2647
  if (routerOptions.watchMode) {
1713
- setupWatcherForAllDirectories();
2648
+ setupOptimizedWatching();
1714
2649
  }
1715
2650
  initialized = true;
1716
2651
  } catch (error) {
1717
- console.error("Failed to initialize router:", error);
2652
+ console.error("\u26A0\uFE0F Failed to initialize router:", error);
1718
2653
  throw error;
1719
2654
  }
1720
2655
  })();
1721
2656
  return initializationPromise;
1722
2657
  }
1723
- function addRouteInternal(route) {
1724
- routes.push(route);
1725
- Object.entries(route).forEach(([method, methodOptions]) => {
1726
- if (method === "path" || !methodOptions) return;
1727
- matcher.add(route.path, method, methodOptions);
1728
- });
1729
- }
1730
- function createWatcherCallbacks(directory, source, prefix) {
1731
- return {
1732
- onRouteAdded: (addedRoutes) => {
1733
- console.log(
1734
- `${addedRoutes.length} route(s) added from ${directory}:`,
1735
- addedRoutes.map((r) => r.path)
1736
- );
1737
- addedRoutes.forEach((route) => {
1738
- const finalRoute = prefix ? { ...route, path: `${prefix}${route.path}` } : route;
1739
- addRouteWithSource(finalRoute, source);
1740
- });
1741
- },
1742
- onRouteChanged: (changedRoutes) => {
1743
- console.log(
1744
- `${changedRoutes.length} route(s) changed in ${directory}:`,
1745
- changedRoutes.map((r) => r.path)
1746
- );
1747
- changedRoutes.forEach((route) => {
1748
- const finalPath = prefix ? `${prefix}${route.path}` : route.path;
1749
- const index = routes.findIndex((r) => r.path === finalPath);
1750
- if (index >= 0) {
1751
- routes.splice(index, 1);
1752
- const sources = routeSources.get(finalPath) || [];
1753
- const filteredSources = sources.filter((s) => s !== source);
1754
- if (filteredSources.length > 0) {
1755
- routeSources.set(finalPath, filteredSources);
1756
- } else {
1757
- routeSources.delete(finalPath);
2658
+ function setupOptimizedWatching() {
2659
+ if (!_watchers) {
2660
+ _watchers = /* @__PURE__ */ new Map();
2661
+ }
2662
+ for (const directory of routeDirectories) {
2663
+ if (!_watchers.has(directory)) {
2664
+ const watcher = watchRoutes(directory, {
2665
+ debounceMs: 16,
2666
+ // ~60fps debouncing
2667
+ ignore: ["node_modules", ".git"],
2668
+ onRouteAdded: (filepath, addedRoutes) => {
2669
+ try {
2670
+ const changes = updateRoutesFromFile(registry, filepath, addedRoutes);
2671
+ applyMatcherChanges(changes);
2672
+ } catch (error) {
2673
+ console.error(`Error adding routes from ${directory}:`, error);
2674
+ }
2675
+ },
2676
+ onRouteChanged: withPerformanceTracking(
2677
+ async (filepath, changedRoutes) => {
2678
+ try {
2679
+ console.log(`Processing changes for ${filepath}`);
2680
+ const changes = updateRoutesFromFile(registry, filepath, changedRoutes);
2681
+ console.log(
2682
+ `Changes detected: ${changes.added.length} added, ${changes.changed.length} changed, ${changes.removed.length} removed`
2683
+ );
2684
+ applyMatcherChanges(changes);
2685
+ console.log(
2686
+ `Route changes applied: ${changes.added.length} added, ${changes.changed.length} changed, ${changes.removed.length} removed`
2687
+ );
2688
+ } catch (error) {
2689
+ console.error(`\u26A0\uFE0F Error updating routes from ${directory}:`, error);
2690
+ }
2691
+ },
2692
+ directory
2693
+ ),
2694
+ onRouteRemoved: (filePath, removedRoutes) => {
2695
+ console.log(`File removed: ${filePath} with ${removedRoutes.length} routes`);
2696
+ try {
2697
+ removedRoutes.forEach((route) => {
2698
+ removeRouteFromMatcher(route.path, matcher);
2699
+ });
2700
+ clearFileCache(filePath);
2701
+ } catch (error) {
2702
+ console.error(`\u26A0\uFE0F Error removing routes from ${filePath}:`, error);
1758
2703
  }
2704
+ },
2705
+ onError: (error) => {
2706
+ console.error(`\u26A0\uFE0F Route watcher error for ${directory}:`, error);
1759
2707
  }
1760
- const finalRoute = prefix ? { ...route, path: finalPath } : route;
1761
- addRouteWithSource(finalRoute, source);
1762
2708
  });
2709
+ _watchers.set(directory, watcher);
2710
+ }
2711
+ }
2712
+ }
2713
+ function setupWatcherForNewDirectory(directory, prefix) {
2714
+ if (!_watchers) {
2715
+ _watchers = /* @__PURE__ */ new Map();
2716
+ }
2717
+ const watcher = watchRoutes(directory, {
2718
+ debounceMs: 16,
2719
+ ignore: ["node_modules", ".git"],
2720
+ onRouteAdded: (filePath, addedRoutes) => {
2721
+ try {
2722
+ const finalRoutes = addedRoutes.map(
2723
+ (route) => prefix ? { ...route, path: `${prefix}${route.path}` } : route
2724
+ );
2725
+ const changes = updateRoutesFromFile(registry, filePath, finalRoutes);
2726
+ applyMatcherChanges(changes);
2727
+ } catch (error) {
2728
+ console.error(`\u26A0\uFE0F Error adding routes from ${directory}:`, error);
2729
+ }
1763
2730
  },
2731
+ onRouteChanged: withPerformanceTracking(async (filePath, changedRoutes) => {
2732
+ try {
2733
+ const finalRoutes = changedRoutes.map(
2734
+ (route) => prefix ? { ...route, path: `${prefix}${route.path}` } : route
2735
+ );
2736
+ const changes = updateRoutesFromFile(registry, filePath, finalRoutes);
2737
+ applyMatcherChanges(changes);
2738
+ } catch (error) {
2739
+ console.error(`\u26A0\uFE0F Error updating routes from ${directory}:`, error);
2740
+ }
2741
+ }, directory),
1764
2742
  onRouteRemoved: (filePath, removedRoutes) => {
1765
- console.log(
1766
- `File removed from ${directory}: ${filePath} with ${removedRoutes.length} route(s):`,
1767
- removedRoutes.map((r) => r.path)
1768
- );
1769
- removedRoutes.forEach((route) => {
1770
- const finalPath = prefix ? `${prefix}${route.path}` : route.path;
1771
- const index = routes.findIndex((r) => r.path === finalPath);
1772
- if (index >= 0) {
1773
- routes.splice(index, 1);
1774
- }
1775
- const sources = routeSources.get(finalPath) || [];
1776
- const filteredSources = sources.filter((s) => s !== source);
1777
- if (filteredSources.length > 0) {
1778
- routeSources.set(finalPath, filteredSources);
1779
- } else {
1780
- routeSources.delete(finalPath);
1781
- }
1782
- });
2743
+ try {
2744
+ removedRoutes.forEach((route) => {
2745
+ const finalPath = prefix ? `${prefix}${route.path}` : route.path;
2746
+ removeRouteFromMatcher(finalPath, matcher);
2747
+ });
2748
+ clearFileCache(filePath);
2749
+ } catch (error) {
2750
+ console.error(`Error removing routes from ${filePath}:`, error);
2751
+ }
1783
2752
  },
1784
2753
  onError: (error) => {
1785
- console.error(`Route watcher error for ${directory}:`, error);
2754
+ console.error(`\u26A0\uFE0F Route watcher error for ${directory}:`, error);
1786
2755
  }
1787
- };
1788
- }
1789
- function setupWatcherForDirectory(directory, source, prefix) {
1790
- const callbacks = createWatcherCallbacks(directory, source, prefix);
1791
- const watcher = watchRoutes(directory, {
1792
- ignore: ["node_modules", ".git"],
1793
- ...callbacks
1794
2756
  });
1795
- if (!_watchers) {
1796
- _watchers = /* @__PURE__ */ new Map();
1797
- }
1798
2757
  _watchers.set(directory, watcher);
1799
2758
  return watcher;
1800
2759
  }
1801
- function setupWatcherForAllDirectories() {
1802
- for (const directory of routeDirectories) {
1803
- setupWatcherForDirectory(directory, directory);
1804
- }
1805
- }
1806
2760
  initialize().catch((error) => {
1807
- console.error("Failed to initialize router on creation:", error);
2761
+ console.error("\u26A0\uFE0F Failed to initialize router on creation:", error);
1808
2762
  });
1809
2763
  return {
1810
2764
  /**
@@ -1812,17 +2766,22 @@ function createRouter(options) {
1812
2766
  */
1813
2767
  async handleRequest(ctx) {
1814
2768
  if (!initialized) {
2769
+ console.log("\u{1F504} Router not initialized, initializing...");
1815
2770
  await initialize();
1816
2771
  }
1817
- const { method, path: path5 } = ctx.request;
1818
- const match = matcher.match(path5, method);
2772
+ const { method, path: path6 } = ctx.request;
2773
+ console.log(`
2774
+ \u{1F4E5} Handling request: ${method} ${path6}`);
2775
+ const match = matcher.match(path6, method);
1819
2776
  if (!match) {
1820
- ctx.response.status(404).json({ error: "Not Found" });
1821
- return;
2777
+ console.log(`\u274C No match found for: ${method} ${path6}`);
2778
+ throw new NotFoundError("Not found");
1822
2779
  }
2780
+ console.log(`\u2705 Route matched: ${method} ${path6}`);
2781
+ console.log(` Params: ${JSON.stringify(match.params)}`);
1823
2782
  if (match.methodNotAllowed) {
1824
2783
  ctx.response.status(405).json({
1825
- error: "Method Not Allowed",
2784
+ error: "\u274C Method Not Allowed",
1826
2785
  allowed: match.allowedMethods
1827
2786
  });
1828
2787
  if (match.allowedMethods && match.allowedMethods.length > 0) {
@@ -1831,29 +2790,31 @@ function createRouter(options) {
1831
2790
  return;
1832
2791
  }
1833
2792
  ctx.request.params = match.params;
1834
- try {
1835
- await executeHandler(ctx, match.route, match.params);
1836
- } catch (error) {
1837
- handleRouteError(ctx, error, {
1838
- detailed: process.env.NODE_ENV !== "production",
1839
- log: true
1840
- });
1841
- }
2793
+ await executeHandler(ctx, match.route, match.params);
1842
2794
  },
1843
2795
  /**
1844
- * Get all registered routes
2796
+ * Get all registered routes (using optimized registry)
1845
2797
  */
1846
2798
  getRoutes() {
1847
- return [...routes];
2799
+ return getAllRoutesFromRegistry(registry);
1848
2800
  },
1849
2801
  /**
1850
2802
  * Add a route programmatically
1851
2803
  */
1852
2804
  addRoute(route) {
1853
- addRouteInternal(route);
2805
+ const changes = updateRoutesFromFile(registry, "programmatic", [route]);
2806
+ applyMatcherChanges(changes);
2807
+ },
2808
+ /**
2809
+ * Add multiple routes programmatically with batch processing
2810
+ */
2811
+ addRoutes(routes) {
2812
+ const changes = updateRoutesFromFile(registry, "programmatic", routes);
2813
+ applyMatcherChanges(changes);
2814
+ return changes;
1854
2815
  },
1855
2816
  /**
1856
- * Add a route directory (for plugins)
2817
+ * Add a route directory (for plugins) with optimized loading
1857
2818
  */
1858
2819
  async addRouteDirectory(directory, options2 = {}) {
1859
2820
  if (routeDirectories.has(directory)) {
@@ -1864,27 +2825,33 @@ function createRouter(options) {
1864
2825
  if (initialized) {
1865
2826
  await loadRoutesFromDirectory(directory, directory, options2.prefix);
1866
2827
  if (routerOptions.watchMode) {
1867
- setupWatcherForDirectory(directory, directory, options2.prefix);
2828
+ setupWatcherForNewDirectory(directory, options2.prefix);
1868
2829
  }
1869
2830
  }
1870
2831
  },
1871
2832
  /**
1872
- * Get route conflicts
2833
+ * Get route conflicts (using registry)
1873
2834
  */
1874
2835
  getRouteConflicts() {
1875
2836
  const conflicts = [];
1876
- for (const [path5, sources] of routeSources.entries()) {
1877
- if (sources.length > 1) {
1878
- conflicts.push({ path: path5, sources });
2837
+ return conflicts;
2838
+ },
2839
+ /**
2840
+ * Close watchers and cleanup (useful for testing)
2841
+ */
2842
+ async close() {
2843
+ if (_watchers) {
2844
+ for (const watcher of _watchers.values()) {
2845
+ await watcher.close();
1879
2846
  }
2847
+ _watchers.clear();
1880
2848
  }
1881
- return conflicts;
1882
2849
  }
1883
2850
  };
1884
2851
  }
1885
2852
 
1886
2853
  // src/server/create.ts
1887
- var DEFAULT_OPTIONS = {
2854
+ var DEFAULT_OPTIONS2 = {
1888
2855
  port: 3e3,
1889
2856
  host: "localhost",
1890
2857
  routesDir: "./routes",
@@ -1895,7 +2862,7 @@ var DEFAULT_OPTIONS = {
1895
2862
  plugins: []
1896
2863
  };
1897
2864
  function createServerOptions(options = {}) {
1898
- const baseOptions = { ...DEFAULT_OPTIONS };
2865
+ const baseOptions = { ...DEFAULT_OPTIONS2 };
1899
2866
  setRuntimeConfig({ routesDir: options.routesDir || baseOptions.routesDir });
1900
2867
  return {
1901
2868
  port: options.port ?? baseOptions.port,
@@ -2015,6 +2982,90 @@ function create3(options = {}) {
2015
2982
  return serverInstance;
2016
2983
  }
2017
2984
 
2985
+ // src/errors/unauthorized-error.ts
2986
+ var UnauthorizedError = class extends BlaizeError {
2987
+ /**
2988
+ * Creates a new UnauthorizedError instance
2989
+ *
2990
+ * @param title - Human-readable error message
2991
+ * @param details - Optional authentication context
2992
+ * @param correlationId - Optional correlation ID (uses current context if not provided)
2993
+ */
2994
+ constructor(title, details = void 0, correlationId = void 0) {
2995
+ super(
2996
+ "UNAUTHORIZED" /* UNAUTHORIZED */,
2997
+ title,
2998
+ 401,
2999
+ // HTTP 401 Unauthorized
3000
+ correlationId ?? getCurrentCorrelationId(),
3001
+ details
3002
+ );
3003
+ }
3004
+ };
3005
+
3006
+ // src/errors/forbidden-error.ts
3007
+ var ForbiddenError = class extends BlaizeError {
3008
+ /**
3009
+ * Creates a new ForbiddenError instance
3010
+ *
3011
+ * @param title - Human-readable error message
3012
+ * @param details - Optional permission context
3013
+ * @param correlationId - Optional correlation ID (uses current context if not provided)
3014
+ */
3015
+ constructor(title, details = void 0, correlationId = void 0) {
3016
+ super(
3017
+ "FORBIDDEN" /* FORBIDDEN */,
3018
+ title,
3019
+ 403,
3020
+ // HTTP 403 Forbidden
3021
+ correlationId ?? getCurrentCorrelationId(),
3022
+ details
3023
+ );
3024
+ }
3025
+ };
3026
+
3027
+ // src/errors/conflict-error.ts
3028
+ var ConflictError = class extends BlaizeError {
3029
+ /**
3030
+ * Creates a new ConflictError instance
3031
+ *
3032
+ * @param title - Human-readable error message
3033
+ * @param details - Optional conflict context
3034
+ * @param correlationId - Optional correlation ID (uses current context if not provided)
3035
+ */
3036
+ constructor(title, details = void 0, correlationId = void 0) {
3037
+ super(
3038
+ "CONFLICT" /* CONFLICT */,
3039
+ title,
3040
+ 409,
3041
+ // HTTP 409 Conflict
3042
+ correlationId ?? getCurrentCorrelationId(),
3043
+ details
3044
+ );
3045
+ }
3046
+ };
3047
+
3048
+ // src/errors/rate-limit-error.ts
3049
+ var RateLimitError = class extends BlaizeError {
3050
+ /**
3051
+ * Creates a new RateLimitError instance
3052
+ *
3053
+ * @param title - Human-readable error message
3054
+ * @param details - Optional rate limit context
3055
+ * @param correlationId - Optional correlation ID (uses current context if not provided)
3056
+ */
3057
+ constructor(title, details = void 0, correlationId = void 0) {
3058
+ super(
3059
+ "RATE_LIMITED" /* RATE_LIMITED */,
3060
+ title,
3061
+ 429,
3062
+ // HTTP 429 Too Many Requests
3063
+ correlationId ?? getCurrentCorrelationId(),
3064
+ details
3065
+ );
3066
+ }
3067
+ };
3068
+
2018
3069
  // src/index.ts
2019
3070
  var VERSION = "0.1.0";
2020
3071
  var ServerAPI = { createServer: create3 };
@@ -2045,11 +3096,21 @@ var Blaize = {
2045
3096
  var index_default = Blaize;
2046
3097
  export {
2047
3098
  Blaize,
3099
+ BlaizeError,
3100
+ ConflictError,
3101
+ ErrorSeverity,
3102
+ ErrorType,
3103
+ ForbiddenError,
3104
+ InternalServerError,
2048
3105
  MiddlewareAPI,
3106
+ NotFoundError,
2049
3107
  PluginsAPI,
3108
+ RateLimitError,
2050
3109
  RouterAPI,
2051
3110
  ServerAPI,
3111
+ UnauthorizedError,
2052
3112
  VERSION,
3113
+ ValidationError,
2053
3114
  compose,
2054
3115
  createDeleteRoute,
2055
3116
  createGetRoute,
@@ -2061,6 +3122,7 @@ export {
2061
3122
  createPostRoute,
2062
3123
  createPutRoute,
2063
3124
  create3 as createServer,
2064
- index_default as default
3125
+ index_default as default,
3126
+ isBodyParseError
2065
3127
  };
2066
3128
  //# sourceMappingURL=index.js.map