blaizejs 0.2.2 → 0.2.3

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.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * blaizejs v0.2.2
2
+ * blaizejs v0.2.3
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
@@ -159,6 +159,9 @@ function create2(name, version, setup, defaultOptions = {}) {
159
159
  };
160
160
  }
161
161
 
162
+ // src/router/create.ts
163
+ var import_node_url = require("url");
164
+
162
165
  // src/config.ts
163
166
  var config = {};
164
167
  function setRuntimeConfig(newConfig) {
@@ -230,73 +233,75 @@ function getCallerFilePath() {
230
233
  if (!fileName) {
231
234
  throw new Error("Unable to determine caller file name");
232
235
  }
236
+ if (fileName.startsWith("file://")) {
237
+ return (0, import_node_url.fileURLToPath)(fileName);
238
+ }
233
239
  return fileName;
234
240
  } finally {
235
241
  Error.prepareStackTrace = originalPrepareStackTrace;
236
242
  }
237
243
  }
238
244
  function getRoutePath() {
239
- console.log("getRoutePath called");
240
245
  const callerPath = getCallerFilePath();
241
246
  const routesDir = getRoutesDir();
242
247
  const parsedRoute = parseRoutePath(callerPath, routesDir);
243
- console.log(`Parsed route path: ${parsedRoute.routePath} from file: ${callerPath}`);
248
+ console.log(`\u{1F50E} Parsed route path: ${parsedRoute.routePath} from file: ${callerPath}`);
244
249
  return parsedRoute.routePath;
245
250
  }
246
251
  var createGetRoute = (config2) => {
247
252
  validateMethodConfig("GET", config2);
248
- const path5 = getRoutePath();
253
+ const path6 = getRoutePath();
249
254
  return {
250
255
  GET: config2,
251
- path: path5
256
+ path: path6
252
257
  };
253
258
  };
254
259
  var createPostRoute = (config2) => {
255
260
  validateMethodConfig("POST", config2);
256
- const path5 = getRoutePath();
261
+ const path6 = getRoutePath();
257
262
  return {
258
263
  POST: config2,
259
- path: path5
264
+ path: path6
260
265
  };
261
266
  };
262
267
  var createPutRoute = (config2) => {
263
268
  validateMethodConfig("PUT", config2);
264
- const path5 = getRoutePath();
269
+ const path6 = getRoutePath();
265
270
  return {
266
271
  PUT: config2,
267
- path: path5
272
+ path: path6
268
273
  };
269
274
  };
270
275
  var createDeleteRoute = (config2) => {
271
276
  validateMethodConfig("DELETE", config2);
272
- const path5 = getRoutePath();
277
+ const path6 = getRoutePath();
273
278
  return {
274
279
  DELETE: config2,
275
- path: path5
280
+ path: path6
276
281
  };
277
282
  };
278
283
  var createPatchRoute = (config2) => {
279
284
  validateMethodConfig("PATCH", config2);
280
- const path5 = getRoutePath();
285
+ const path6 = getRoutePath();
281
286
  return {
282
287
  PATCH: config2,
283
- path: path5
288
+ path: path6
284
289
  };
285
290
  };
286
291
  var createHeadRoute = (config2) => {
287
292
  validateMethodConfig("HEAD", config2);
288
- const path5 = getRoutePath();
293
+ const path6 = getRoutePath();
289
294
  return {
290
295
  HEAD: config2,
291
- path: path5
296
+ path: path6
292
297
  };
293
298
  };
294
299
  var createOptionsRoute = (config2) => {
295
300
  validateMethodConfig("OPTIONS", config2);
296
- const path5 = getRoutePath();
301
+ const path6 = getRoutePath();
297
302
  return {
298
303
  OPTIONS: config2,
299
- path: path5
304
+ path: path6
300
305
  };
301
306
  };
302
307
  function validateMethodConfig(method, config2) {
@@ -441,7 +446,7 @@ function parseRequestUrl(req) {
441
446
  const fullUrl = `${protocol}://${host}${originalUrl.startsWith("/") ? "" : "/"}${originalUrl}`;
442
447
  try {
443
448
  const url = new URL(fullUrl);
444
- const path5 = url.pathname;
449
+ const path6 = url.pathname;
445
450
  const query = {};
446
451
  url.searchParams.forEach((value, key) => {
447
452
  if (query[key] !== void 0) {
@@ -454,7 +459,7 @@ function parseRequestUrl(req) {
454
459
  query[key] = value;
455
460
  }
456
461
  });
457
- return { path: path5, url, query };
462
+ return { path: path6, url, query };
458
463
  } catch (error) {
459
464
  console.warn(`Invalid URL: ${fullUrl}`, error);
460
465
  throw new ParseUrlError(`Invalid URL: ${fullUrl}`);
@@ -476,7 +481,7 @@ function getProtocol(req) {
476
481
  return encrypted ? "https" : "http";
477
482
  }
478
483
  async function createContext(req, res, options = {}) {
479
- const { path: path5, url, query } = parseRequestUrl(req);
484
+ const { path: path6, url, query } = parseRequestUrl(req);
480
485
  const method = req.method || "GET";
481
486
  const isHttp2 = isHttp2Request(req);
482
487
  const protocol = getProtocol(req);
@@ -485,7 +490,7 @@ async function createContext(req, res, options = {}) {
485
490
  const responseState = { sent: false };
486
491
  const ctx = {
487
492
  request: createRequestObject(req, {
488
- path: path5,
493
+ path: path6,
489
494
  url,
490
495
  query,
491
496
  params,
@@ -757,13 +762,13 @@ function setBodyError(ctx, type, message, error) {
757
762
  ctx.state._bodyError = { type, message, error };
758
763
  }
759
764
  async function readRequestBody(req) {
760
- return new Promise((resolve2, reject) => {
765
+ return new Promise((resolve3, reject) => {
761
766
  const chunks = [];
762
767
  req.on("data", (chunk) => {
763
768
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
764
769
  });
765
770
  req.on("end", () => {
766
- resolve2(Buffer.concat(chunks).toString("utf8"));
771
+ resolve3(Buffer.concat(chunks).toString("utf8"));
767
772
  });
768
773
  req.on("error", (err) => {
769
774
  reject(err);
@@ -861,7 +866,7 @@ function createServerInstance(isHttp2, certOptions) {
861
866
  return http2.createSecureServer(http2ServerOptions);
862
867
  }
863
868
  function listenOnPort(server, port, host, isHttp2) {
864
- return new Promise((resolve2, reject) => {
869
+ return new Promise((resolve3, reject) => {
865
870
  server.listen(port, host, () => {
866
871
  const protocol = isHttp2 ? "https" : "http";
867
872
  const url = `${protocol}://${host}:${port}`;
@@ -878,7 +883,7 @@ function listenOnPort(server, port, host, isHttp2) {
878
883
 
879
884
  \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}
880
885
  `);
881
- resolve2();
886
+ resolve3();
882
887
  });
883
888
  server.on("error", (err) => {
884
889
  console.error("Server error:", err);
@@ -922,55 +927,128 @@ async function startServer(serverInstance, serverOptions) {
922
927
  }
923
928
 
924
929
  // src/server/stop.ts
930
+ var isShuttingDown = false;
925
931
  async function stopServer(serverInstance, options = {}) {
926
932
  const server = serverInstance.server;
927
933
  const events = serverInstance.events;
934
+ if (isShuttingDown) {
935
+ console.log("\u26A0\uFE0F Shutdown already in progress, ignoring duplicate shutdown request");
936
+ return;
937
+ }
928
938
  if (!server) {
929
939
  return;
930
940
  }
931
- const timeout = options.timeout || 3e4;
941
+ isShuttingDown = true;
942
+ const timeout = options.timeout || 5e3;
932
943
  try {
933
944
  if (options.onStopping) {
934
945
  await options.onStopping();
935
946
  }
936
947
  events.emit("stopping");
937
- await serverInstance.pluginManager.onServerStop(serverInstance, server);
948
+ if (serverInstance.router && typeof serverInstance.router.close === "function") {
949
+ console.log("\u{1F50C} Closing router watchers...");
950
+ try {
951
+ await Promise.race([
952
+ serverInstance.router.close(),
953
+ new Promise(
954
+ (_, reject) => setTimeout(() => reject(new Error("Router close timeout")), 2e3)
955
+ )
956
+ ]);
957
+ console.log("\u2705 Router watchers closed");
958
+ } catch (error) {
959
+ console.error("\u274C Error closing router watchers:", error);
960
+ }
961
+ }
962
+ try {
963
+ await Promise.race([
964
+ serverInstance.pluginManager.onServerStop(serverInstance, server),
965
+ new Promise(
966
+ (_, reject) => setTimeout(() => reject(new Error("Plugin stop timeout")), 2e3)
967
+ )
968
+ ]);
969
+ } catch (error) {
970
+ console.error("\u274C Plugin stop timeout:", error);
971
+ }
972
+ const closePromise = new Promise((resolve3, reject) => {
973
+ server.close((err) => {
974
+ if (err) return reject(err);
975
+ resolve3();
976
+ });
977
+ });
938
978
  const timeoutPromise = new Promise((_, reject) => {
939
979
  setTimeout(() => {
940
- reject(new Error("Server shutdown timed out waiting for requests to complete"));
980
+ reject(new Error("Server shutdown timeout"));
941
981
  }, timeout);
942
982
  });
943
- const closePromise = new Promise((resolve2, reject) => {
944
- server.close((err) => {
945
- if (err) {
946
- return reject(err);
947
- }
948
- resolve2();
949
- });
950
- });
951
983
  await Promise.race([closePromise, timeoutPromise]);
952
- await serverInstance.pluginManager.terminatePlugins(serverInstance);
984
+ try {
985
+ await Promise.race([
986
+ serverInstance.pluginManager.terminatePlugins(serverInstance),
987
+ new Promise(
988
+ (_, reject) => setTimeout(() => reject(new Error("Plugin terminate timeout")), 1e3)
989
+ )
990
+ ]);
991
+ } catch (error) {
992
+ console.error("\u274C Plugin terminate timeout:", error);
993
+ }
953
994
  if (options.onStopped) {
954
995
  await options.onStopped();
955
996
  }
956
997
  events.emit("stopped");
957
998
  serverInstance.server = null;
999
+ console.log("\u2705 Graceful shutdown completed");
1000
+ isShuttingDown = false;
958
1001
  } catch (error) {
1002
+ isShuttingDown = false;
1003
+ console.error("\u26A0\uFE0F Shutdown error (forcing exit):", error);
1004
+ if (server && typeof server.close === "function") {
1005
+ server.close();
1006
+ }
1007
+ if (process.env.NODE_ENV === "development") {
1008
+ console.log("\u{1F504} Forcing exit for development restart...");
1009
+ process.exit(0);
1010
+ }
959
1011
  events.emit("error", error);
960
1012
  throw error;
961
1013
  }
962
1014
  }
963
1015
  function registerSignalHandlers(stopFn) {
964
- const sigintHandler = () => stopFn().catch(console.error);
965
- const sigtermHandler = () => stopFn().catch(console.error);
966
- process.on("SIGINT", sigintHandler);
967
- process.on("SIGTERM", sigtermHandler);
968
- return {
969
- unregister: () => {
970
- process.removeListener("SIGINT", sigintHandler);
971
- process.removeListener("SIGTERM", sigtermHandler);
972
- }
973
- };
1016
+ const isDevelopment = process.env.NODE_ENV === "development";
1017
+ if (isDevelopment) {
1018
+ const sigintHandler = () => {
1019
+ console.log("\u{1F4E4} SIGINT received, forcing exit for development restart...");
1020
+ process.exit(0);
1021
+ };
1022
+ const sigtermHandler = () => {
1023
+ console.log("\u{1F4E4} SIGTERM received, forcing exit for development restart...");
1024
+ process.exit(0);
1025
+ };
1026
+ process.on("SIGINT", sigintHandler);
1027
+ process.on("SIGTERM", sigtermHandler);
1028
+ return {
1029
+ unregister: () => {
1030
+ process.removeListener("SIGINT", sigintHandler);
1031
+ process.removeListener("SIGTERM", sigtermHandler);
1032
+ }
1033
+ };
1034
+ } else {
1035
+ const sigintHandler = () => {
1036
+ console.log("\u{1F4E4} SIGINT received, starting graceful shutdown...");
1037
+ stopFn().catch(console.error);
1038
+ };
1039
+ const sigtermHandler = () => {
1040
+ console.log("\u{1F4E4} SIGTERM received, starting graceful shutdown...");
1041
+ stopFn().catch(console.error);
1042
+ };
1043
+ process.on("SIGINT", sigintHandler);
1044
+ process.on("SIGTERM", sigtermHandler);
1045
+ return {
1046
+ unregister: () => {
1047
+ process.removeListener("SIGINT", sigintHandler);
1048
+ process.removeListener("SIGTERM", sigtermHandler);
1049
+ }
1050
+ };
1051
+ }
974
1052
  }
975
1053
 
976
1054
  // src/server/validation.ts
@@ -1176,59 +1254,33 @@ function validatePlugin(plugin, options = {}) {
1176
1254
  }
1177
1255
  }
1178
1256
 
1179
- // src/router/discovery/finder.ts
1257
+ // src/router/discovery/cache.ts
1258
+ var crypto = __toESM(require("crypto"), 1);
1180
1259
  var fs3 = __toESM(require("fs/promises"), 1);
1260
+ var import_node_module = require("module");
1181
1261
  var path3 = __toESM(require("path"), 1);
1182
- async function findRouteFiles(routesDir, options = {}) {
1183
- const absoluteDir = path3.isAbsolute(routesDir) ? routesDir : path3.resolve(process.cwd(), routesDir);
1184
- console.log("Creating router with routes directory:", absoluteDir);
1185
- try {
1186
- const stats = await fs3.stat(absoluteDir);
1187
- if (!stats.isDirectory()) {
1188
- throw new Error(`Route directory is not a directory: ${absoluteDir}`);
1189
- }
1190
- } catch (error) {
1191
- if (error.code === "ENOENT") {
1192
- throw new Error(`Route directory not found: ${absoluteDir}`);
1193
- }
1194
- throw error;
1195
- }
1196
- const routeFiles = [];
1197
- const ignore = options.ignore || ["node_modules", ".git"];
1198
- async function scanDirectory(dir) {
1199
- const entries = await fs3.readdir(dir, { withFileTypes: true });
1200
- for (const entry of entries) {
1201
- const fullPath = path3.join(dir, entry.name);
1202
- if (entry.isDirectory() && ignore.includes(entry.name)) {
1203
- continue;
1204
- }
1205
- if (entry.isDirectory()) {
1206
- await scanDirectory(fullPath);
1207
- } else if (isRouteFile(entry.name)) {
1208
- routeFiles.push(fullPath);
1209
- }
1210
- }
1211
- }
1212
- await scanDirectory(absoluteDir);
1213
- return routeFiles;
1214
- }
1215
- function isRouteFile(filename) {
1216
- return !filename.startsWith("_") && (filename.endsWith(".ts") || filename.endsWith(".js"));
1217
- }
1218
1262
 
1219
1263
  // src/router/discovery/loader.ts
1220
1264
  async function dynamicImport(filePath) {
1221
- return import(filePath);
1265
+ const cacheBuster = `?t=${Date.now()}`;
1266
+ const importPath = filePath + cacheBuster;
1267
+ try {
1268
+ const module2 = await import(importPath);
1269
+ console.log(`\u2705 Successfully imported module`);
1270
+ return module2;
1271
+ } catch (error) {
1272
+ const errorMessage = error instanceof Error ? error.message : String(error);
1273
+ console.log(`\u26A0\uFE0F Error importing with cache buster, trying original path:`, errorMessage);
1274
+ return import(filePath);
1275
+ }
1222
1276
  }
1223
1277
  async function loadRouteModule(filePath, basePath) {
1224
1278
  try {
1225
1279
  const parsedRoute = parseRoutePath(filePath, basePath);
1226
- console.log("parsedRoute:", parsedRoute);
1227
1280
  const module2 = await dynamicImport(filePath);
1228
- console.log("Module exports:", Object.keys(module2));
1281
+ console.log("\u{1F4E6} Module exports:", Object.keys(module2));
1229
1282
  const routes = [];
1230
1283
  if (module2.default && typeof module2.default === "object") {
1231
- console.log("Found default export:", module2.default);
1232
1284
  const route = {
1233
1285
  ...module2.default,
1234
1286
  path: parsedRoute.routePath
@@ -1241,7 +1293,6 @@ async function loadRouteModule(filePath, basePath) {
1241
1293
  }
1242
1294
  const potentialRoute = exportValue;
1243
1295
  if (isValidRoute(potentialRoute)) {
1244
- console.log(`Found named route export: ${exportName}`, potentialRoute);
1245
1296
  const route = {
1246
1297
  ...potentialRoute,
1247
1298
  // Use the route's own path if it has one, otherwise derive from file
@@ -1254,7 +1305,7 @@ async function loadRouteModule(filePath, basePath) {
1254
1305
  console.warn(`Route file ${filePath} does not export any valid route definitions`);
1255
1306
  return [];
1256
1307
  }
1257
- console.log(`Loaded ${routes.length} route(s) from ${filePath}`);
1308
+ console.log(`\u2705 Successfully Loaded ${routes.length} route(s)`);
1258
1309
  return routes;
1259
1310
  } catch (error) {
1260
1311
  console.error(`Failed to load route module ${filePath}:`, error);
@@ -1272,25 +1323,223 @@ function isValidRoute(obj) {
1272
1323
  return hasHttpMethod;
1273
1324
  }
1274
1325
 
1275
- // src/router/discovery/index.ts
1276
- async function findRoutes(routesDir, options = {}) {
1277
- const routeFiles = await findRouteFiles(routesDir, {
1278
- ignore: options.ignore
1279
- });
1280
- const routes = [];
1281
- for (const filePath of routeFiles) {
1282
- const moduleRoutes = await loadRouteModule(filePath, routesDir);
1283
- if (moduleRoutes.length > 0) {
1284
- routes.push(...moduleRoutes);
1285
- }
1326
+ // src/router/discovery/cache.ts
1327
+ var import_meta = {};
1328
+ var fileRouteCache = /* @__PURE__ */ new Map();
1329
+ async function processChangedFile(filePath, routesDir, updateCache = true) {
1330
+ const stat3 = await fs3.stat(filePath);
1331
+ const lastModified = stat3.mtime.getTime();
1332
+ const cachedEntry = fileRouteCache.get(filePath);
1333
+ if (updateCache && cachedEntry && cachedEntry.timestamp === lastModified) {
1334
+ return cachedEntry.routes;
1335
+ }
1336
+ invalidateModuleCache(filePath);
1337
+ const routes = await loadRouteModule(filePath, routesDir);
1338
+ if (updateCache) {
1339
+ const hash = hashRoutes(routes);
1340
+ fileRouteCache.set(filePath, {
1341
+ routes,
1342
+ timestamp: lastModified,
1343
+ hash
1344
+ });
1286
1345
  }
1287
1346
  return routes;
1288
1347
  }
1348
+ function hasRouteContentChanged(filePath, newRoutes) {
1349
+ const cachedEntry = fileRouteCache.get(filePath);
1350
+ if (!cachedEntry) {
1351
+ return true;
1352
+ }
1353
+ const newHash = hashRoutes(newRoutes);
1354
+ return cachedEntry.hash !== newHash;
1355
+ }
1356
+ function clearFileCache(filePath) {
1357
+ if (filePath) {
1358
+ fileRouteCache.delete(filePath);
1359
+ } else {
1360
+ fileRouteCache.clear();
1361
+ }
1362
+ }
1363
+ function hashRoutes(routes) {
1364
+ const routeData = routes.map((route) => ({
1365
+ path: route.path,
1366
+ methods: Object.keys(route).filter((key) => key !== "path").sort().map((method) => {
1367
+ const methodDef = route[method];
1368
+ const handlerString = methodDef?.handler ? methodDef.handler.toString() : null;
1369
+ return {
1370
+ method,
1371
+ // Include handler function string for change detection
1372
+ handler: handlerString,
1373
+ // Include middleware if present
1374
+ middleware: methodDef?.middleware ? methodDef.middleware.length : 0,
1375
+ // Include schema structure (but not full serialization which can be unstable)
1376
+ hasSchema: !!methodDef?.schema,
1377
+ schemaKeys: methodDef?.schema ? Object.keys(methodDef.schema).sort() : []
1378
+ };
1379
+ })
1380
+ }));
1381
+ const dataString = JSON.stringify(routeData);
1382
+ const hash = crypto.createHash("md5").update(dataString).digest("hex");
1383
+ return hash;
1384
+ }
1385
+ function invalidateModuleCache(filePath) {
1386
+ try {
1387
+ const absolutePath = path3.resolve(filePath);
1388
+ if (typeof require !== "undefined") {
1389
+ delete require.cache[absolutePath];
1390
+ try {
1391
+ const resolvedPath = require.resolve(absolutePath);
1392
+ delete require.cache[resolvedPath];
1393
+ } catch (resolveError) {
1394
+ const errorMessage = resolveError instanceof Error ? resolveError.message : String(resolveError);
1395
+ console.log(`\u26A0\uFE0F Could not resolve path: ${errorMessage}`);
1396
+ }
1397
+ } else {
1398
+ try {
1399
+ const require2 = (0, import_node_module.createRequire)(import_meta.url);
1400
+ delete require2.cache[absolutePath];
1401
+ try {
1402
+ const resolvedPath = require2.resolve(absolutePath);
1403
+ delete require2.cache[resolvedPath];
1404
+ } catch {
1405
+ console.log(`\u26A0\uFE0F Could not resolve ESM path`);
1406
+ }
1407
+ } catch {
1408
+ console.log(`\u26A0\uFE0F createRequire not available in pure ESM`);
1409
+ }
1410
+ }
1411
+ } catch (error) {
1412
+ console.log(`\u26A0\uFE0F Error during module cache invalidation for ${filePath}:`, error);
1413
+ }
1414
+ }
1289
1415
 
1290
- // src/router/discovery/watchers.ts
1416
+ // src/router/discovery/parallel.ts
1417
+ var os = __toESM(require("os"), 1);
1418
+
1419
+ // src/router/discovery/finder.ts
1420
+ var fs4 = __toESM(require("fs/promises"), 1);
1291
1421
  var path4 = __toESM(require("path"), 1);
1422
+ async function findRouteFiles(routesDir, options = {}) {
1423
+ const absoluteDir = path4.isAbsolute(routesDir) ? routesDir : path4.resolve(process.cwd(), routesDir);
1424
+ console.log("Creating router with routes directory:", absoluteDir);
1425
+ try {
1426
+ const stats = await fs4.stat(absoluteDir);
1427
+ if (!stats.isDirectory()) {
1428
+ throw new Error(`Route directory is not a directory: ${absoluteDir}`);
1429
+ }
1430
+ } catch (error) {
1431
+ if (error.code === "ENOENT") {
1432
+ throw new Error(`Route directory not found: ${absoluteDir}`);
1433
+ }
1434
+ throw error;
1435
+ }
1436
+ const routeFiles = [];
1437
+ const ignore = options.ignore || ["node_modules", ".git"];
1438
+ async function scanDirectory(dir) {
1439
+ const entries = await fs4.readdir(dir, { withFileTypes: true });
1440
+ for (const entry of entries) {
1441
+ const fullPath = path4.join(dir, entry.name);
1442
+ if (entry.isDirectory() && ignore.includes(entry.name)) {
1443
+ continue;
1444
+ }
1445
+ if (entry.isDirectory()) {
1446
+ await scanDirectory(fullPath);
1447
+ } else if (isRouteFile(entry.name)) {
1448
+ routeFiles.push(fullPath);
1449
+ }
1450
+ }
1451
+ }
1452
+ await scanDirectory(absoluteDir);
1453
+ return routeFiles;
1454
+ }
1455
+ function isRouteFile(filename) {
1456
+ return !filename.startsWith("_") && (filename.endsWith(".ts") || filename.endsWith(".js"));
1457
+ }
1458
+
1459
+ // src/router/discovery/parallel.ts
1460
+ async function processFilesInParallel(filePaths, processor, concurrency = Math.max(1, Math.floor(os.cpus().length / 2))) {
1461
+ const chunks = chunkArray(filePaths, concurrency);
1462
+ const results = [];
1463
+ for (const chunk of chunks) {
1464
+ const chunkResults = await Promise.allSettled(chunk.map((filePath) => processor(filePath)));
1465
+ const successfulResults = chunkResults.filter((result) => result.status === "fulfilled").map((result) => result.value);
1466
+ results.push(...successfulResults);
1467
+ }
1468
+ return results;
1469
+ }
1470
+ async function loadInitialRoutesParallel(routesDir) {
1471
+ const files = await findRouteFiles(routesDir);
1472
+ const routeArrays = await processFilesInParallel(
1473
+ files,
1474
+ (filePath) => processChangedFile(filePath, routesDir)
1475
+ );
1476
+ return routeArrays.flat();
1477
+ }
1478
+ function chunkArray(array, chunkSize) {
1479
+ const chunks = [];
1480
+ for (let i = 0; i < array.length; i += chunkSize) {
1481
+ chunks.push(array.slice(i, i + chunkSize));
1482
+ }
1483
+ return chunks;
1484
+ }
1485
+
1486
+ // src/router/discovery/profiler.ts
1487
+ var profilerState = {
1488
+ fileChanges: 0,
1489
+ totalReloadTime: 0,
1490
+ averageReloadTime: 0,
1491
+ slowReloads: []
1492
+ };
1493
+ function trackReloadPerformance(filePath, startTime) {
1494
+ const duration = Date.now() - startTime;
1495
+ profilerState.fileChanges++;
1496
+ profilerState.totalReloadTime += duration;
1497
+ profilerState.averageReloadTime = profilerState.totalReloadTime / profilerState.fileChanges;
1498
+ if (duration > 100) {
1499
+ profilerState.slowReloads.push({ file: filePath, time: duration });
1500
+ if (profilerState.slowReloads.length > 10) {
1501
+ profilerState.slowReloads.shift();
1502
+ }
1503
+ }
1504
+ if (process.env.NODE_ENV === "development") {
1505
+ const emoji = duration < 50 ? "\u26A1" : duration < 100 ? "\u{1F504}" : "\u{1F40C}";
1506
+ console.log(`${emoji} Route reload: ${filePath} (${duration}ms)`);
1507
+ }
1508
+ }
1509
+ function withPerformanceTracking(fn, filePath) {
1510
+ console.log(`Tracking performance for: ${filePath}`);
1511
+ return async (...args) => {
1512
+ const startTime = Date.now();
1513
+ try {
1514
+ const result = await fn(...args);
1515
+ trackReloadPerformance(filePath, startTime);
1516
+ return result;
1517
+ } catch (error) {
1518
+ trackReloadPerformance(filePath, startTime);
1519
+ throw error;
1520
+ }
1521
+ };
1522
+ }
1523
+
1524
+ // src/router/discovery/watchers.ts
1525
+ var path5 = __toESM(require("path"), 1);
1292
1526
  var import_chokidar = require("chokidar");
1293
1527
  function watchRoutes(routesDir, options = {}) {
1528
+ const debounceMs = options.debounceMs || 16;
1529
+ const debouncedCallbacks = /* @__PURE__ */ new Map();
1530
+ function createDebouncedCallback(fn, filePath) {
1531
+ return (...args) => {
1532
+ const existingTimeout = debouncedCallbacks.get(filePath);
1533
+ if (existingTimeout) {
1534
+ clearTimeout(existingTimeout);
1535
+ }
1536
+ const timeoutId = setTimeout(() => {
1537
+ fn(...args);
1538
+ debouncedCallbacks.delete(filePath);
1539
+ }, debounceMs);
1540
+ debouncedCallbacks.set(filePath, timeoutId);
1541
+ };
1542
+ }
1294
1543
  const routesByPath = /* @__PURE__ */ new Map();
1295
1544
  async function loadInitialRoutes() {
1296
1545
  try {
@@ -1306,28 +1555,34 @@ function watchRoutes(routesDir, options = {}) {
1306
1555
  }
1307
1556
  async function loadAndNotify(filePath) {
1308
1557
  try {
1309
- const routes = await loadRouteModule(filePath, routesDir);
1310
- if (!routes || routes.length === 0) {
1558
+ const existingRoutes = routesByPath.get(filePath);
1559
+ const newRoutes = await processChangedFile(filePath, routesDir, false);
1560
+ if (!newRoutes || newRoutes.length === 0) {
1311
1561
  return;
1312
1562
  }
1313
- const existingRoutes = routesByPath.get(filePath);
1563
+ if (existingRoutes && !hasRouteContentChanged(filePath, newRoutes)) {
1564
+ return;
1565
+ }
1566
+ await processChangedFile(filePath, routesDir, true);
1567
+ const normalizedPath = path5.normalize(filePath);
1314
1568
  if (existingRoutes) {
1315
- routesByPath.set(filePath, routes);
1569
+ routesByPath.set(filePath, newRoutes);
1316
1570
  if (options.onRouteChanged) {
1317
- options.onRouteChanged(routes);
1571
+ options.onRouteChanged(normalizedPath, newRoutes);
1318
1572
  }
1319
1573
  } else {
1320
- routesByPath.set(filePath, routes);
1574
+ routesByPath.set(filePath, newRoutes);
1321
1575
  if (options.onRouteAdded) {
1322
- options.onRouteAdded(routes);
1576
+ options.onRouteAdded(normalizedPath, newRoutes);
1323
1577
  }
1324
1578
  }
1325
1579
  } catch (error) {
1580
+ console.log(`\u26A0\uFE0F Error processing file ${filePath}:`, error);
1326
1581
  handleError(error);
1327
1582
  }
1328
1583
  }
1329
1584
  function handleRemoved(filePath) {
1330
- const normalizedPath = path4.normalize(filePath);
1585
+ const normalizedPath = path5.normalize(filePath);
1331
1586
  const routes = routesByPath.get(normalizedPath);
1332
1587
  if (routes && routes.length > 0 && options.onRouteRemoved) {
1333
1588
  options.onRouteRemoved(normalizedPath, routes);
@@ -1338,33 +1593,53 @@ function watchRoutes(routesDir, options = {}) {
1338
1593
  if (options.onError && error instanceof Error) {
1339
1594
  options.onError(error);
1340
1595
  } else {
1341
- console.error("Route watcher error:", error);
1596
+ console.error("\u26A0\uFE0F Route watcher error:", error);
1342
1597
  }
1343
1598
  }
1344
1599
  const watcher = (0, import_chokidar.watch)(routesDir, {
1600
+ // Much faster response times
1601
+ awaitWriteFinish: {
1602
+ stabilityThreshold: 50,
1603
+ // Reduced from 300ms
1604
+ pollInterval: 10
1605
+ // Reduced from 100ms
1606
+ },
1607
+ // Performance optimizations
1608
+ usePolling: false,
1609
+ atomic: true,
1610
+ followSymlinks: false,
1611
+ depth: 10,
1612
+ // More aggressive ignoring
1345
1613
  ignored: [
1346
1614
  /(^|[/\\])\../,
1347
- // Ignore dot files
1348
1615
  /node_modules/,
1616
+ /\.git/,
1617
+ /\.DS_Store/,
1618
+ /Thumbs\.db/,
1619
+ /\.(test|spec)\.(ts|js)$/,
1620
+ /\.d\.ts$/,
1621
+ /\.map$/,
1622
+ /~$/,
1349
1623
  ...options.ignore || []
1350
- ],
1351
- persistent: true,
1352
- ignoreInitial: false,
1353
- awaitWriteFinish: {
1354
- stabilityThreshold: 300,
1355
- pollInterval: 100
1356
- }
1624
+ ]
1357
1625
  });
1358
- watcher.on("add", loadAndNotify).on("change", loadAndNotify).on("unlink", handleRemoved).on("error", handleError);
1626
+ watcher.on("add", (filePath) => {
1627
+ const debouncedLoad = createDebouncedCallback(loadAndNotify, filePath);
1628
+ debouncedLoad(filePath);
1629
+ }).on("change", (filePath) => {
1630
+ const debouncedLoad = createDebouncedCallback(loadAndNotify, filePath);
1631
+ debouncedLoad(filePath);
1632
+ }).on("unlink", (filePath) => {
1633
+ const debouncedRemove = createDebouncedCallback(handleRemoved, filePath);
1634
+ debouncedRemove(filePath);
1635
+ }).on("error", handleError);
1359
1636
  loadInitialRoutes().catch(handleError);
1360
1637
  return {
1361
- /**
1362
- * Close the watcher
1363
- */
1364
- close: () => watcher.close(),
1365
- /**
1366
- * Get all currently loaded routes (flattened)
1367
- */
1638
+ close: () => {
1639
+ debouncedCallbacks.forEach((timeout) => clearTimeout(timeout));
1640
+ debouncedCallbacks.clear();
1641
+ return watcher.close();
1642
+ },
1368
1643
  getRoutes: () => {
1369
1644
  const allRoutes = [];
1370
1645
  for (const routes of routesByPath.values()) {
@@ -1372,9 +1647,6 @@ function watchRoutes(routesDir, options = {}) {
1372
1647
  }
1373
1648
  return allRoutes;
1374
1649
  },
1375
- /**
1376
- * Get routes organized by file path
1377
- */
1378
1650
  getRoutesByFile: () => new Map(routesByPath)
1379
1651
  };
1380
1652
  }
@@ -1585,8 +1857,8 @@ async function executeHandler(ctx, routeOptions, params) {
1585
1857
  }
1586
1858
 
1587
1859
  // src/router/matching/params.ts
1588
- function extractParams(path5, pattern, paramNames) {
1589
- const match = pattern.exec(path5);
1860
+ function extractParams(path6, pattern, paramNames) {
1861
+ const match = pattern.exec(path6);
1590
1862
  if (!match) {
1591
1863
  return {};
1592
1864
  }
@@ -1596,15 +1868,15 @@ function extractParams(path5, pattern, paramNames) {
1596
1868
  }
1597
1869
  return params;
1598
1870
  }
1599
- function compilePathPattern(path5) {
1871
+ function compilePathPattern(path6) {
1600
1872
  const paramNames = [];
1601
- if (path5 === "/") {
1873
+ if (path6 === "/") {
1602
1874
  return {
1603
1875
  pattern: /^\/$/,
1604
1876
  paramNames: []
1605
1877
  };
1606
1878
  }
1607
- let patternString = path5.replace(/([.+*?^$(){}|\\])/g, "\\$1");
1879
+ let patternString = path6.replace(/([.+*?^$(){}|\\])/g, "\\$1");
1608
1880
  patternString = patternString.replace(/\/:([^/]+)/g, (_, paramName) => {
1609
1881
  paramNames.push(paramName);
1610
1882
  return "/([^/]+)";
@@ -1627,10 +1899,10 @@ function createMatcher() {
1627
1899
  /**
1628
1900
  * Add a route to the matcher
1629
1901
  */
1630
- add(path5, method, routeOptions) {
1631
- const { pattern, paramNames } = compilePathPattern(path5);
1902
+ add(path6, method, routeOptions) {
1903
+ const { pattern, paramNames } = compilePathPattern(path6);
1632
1904
  const newRoute = {
1633
- path: path5,
1905
+ path: path6,
1634
1906
  method,
1635
1907
  pattern,
1636
1908
  paramNames,
@@ -1643,17 +1915,33 @@ function createMatcher() {
1643
1915
  routes.splice(insertIndex, 0, newRoute);
1644
1916
  }
1645
1917
  },
1918
+ /**
1919
+ * Remove a route from the matcher by path
1920
+ */
1921
+ remove(path6) {
1922
+ for (let i = routes.length - 1; i >= 0; i--) {
1923
+ if (routes[i].path === path6) {
1924
+ routes.splice(i, 1);
1925
+ }
1926
+ }
1927
+ },
1928
+ /**
1929
+ * Clear all routes from the matcher
1930
+ */
1931
+ clear() {
1932
+ routes.length = 0;
1933
+ },
1646
1934
  /**
1647
1935
  * Match a URL path to a route
1648
1936
  */
1649
- match(path5, method) {
1650
- const pathname = path5.split("?")[0];
1937
+ match(path6, method) {
1938
+ const pathname = path6.split("?")[0];
1651
1939
  if (!pathname) return null;
1652
1940
  for (const route of routes) {
1653
1941
  if (route.method !== method) continue;
1654
1942
  const match = route.pattern.exec(pathname);
1655
1943
  if (match) {
1656
- const params = extractParams(path5, route.pattern, route.paramNames);
1944
+ const params = extractParams(path6, route.pattern, route.paramNames);
1657
1945
  return {
1658
1946
  route: route.routeOptions,
1659
1947
  params
@@ -1661,14 +1949,14 @@ function createMatcher() {
1661
1949
  }
1662
1950
  }
1663
1951
  const matchingPath = routes.find(
1664
- (route) => route.method !== method && route.pattern.test(path5)
1952
+ (route) => route.method !== method && route.pattern.test(path6)
1665
1953
  );
1666
1954
  if (matchingPath) {
1667
1955
  return {
1668
1956
  route: null,
1669
1957
  params: {},
1670
1958
  methodNotAllowed: true,
1671
- allowedMethods: routes.filter((route) => route.pattern.test(path5)).map((route) => route.method)
1959
+ allowedMethods: routes.filter((route) => route.pattern.test(path6)).map((route) => route.method)
1672
1960
  };
1673
1961
  }
1674
1962
  return null;
@@ -1685,16 +1973,93 @@ function createMatcher() {
1685
1973
  /**
1686
1974
  * Find routes matching a specific path
1687
1975
  */
1688
- findRoutes(path5) {
1689
- return routes.filter((route) => route.pattern.test(path5)).map((route) => ({
1976
+ findRoutes(path6) {
1977
+ return routes.filter((route) => route.pattern.test(path6)).map((route) => ({
1690
1978
  path: route.path,
1691
1979
  method: route.method,
1692
- params: extractParams(path5, route.pattern, route.paramNames)
1980
+ params: extractParams(path6, route.pattern, route.paramNames)
1693
1981
  }));
1694
1982
  }
1695
1983
  };
1696
1984
  }
1697
1985
 
1986
+ // src/router/registry/fast-registry.ts
1987
+ function createRouteRegistry() {
1988
+ return {
1989
+ routesByPath: /* @__PURE__ */ new Map(),
1990
+ routesByFile: /* @__PURE__ */ new Map(),
1991
+ pathToFile: /* @__PURE__ */ new Map()
1992
+ };
1993
+ }
1994
+ function updateRoutesFromFile(registry, filePath, newRoutes) {
1995
+ console.log(`Updating routes from file: ${filePath}`);
1996
+ const oldPaths = registry.routesByFile.get(filePath) || /* @__PURE__ */ new Set();
1997
+ const newPaths = new Set(newRoutes.map((r) => r.path));
1998
+ const added = newRoutes.filter((r) => !oldPaths.has(r.path));
1999
+ const removed = Array.from(oldPaths).filter((p) => !newPaths.has(p));
2000
+ const potentiallyChanged = newRoutes.filter((r) => oldPaths.has(r.path));
2001
+ const changed = potentiallyChanged.filter((route) => {
2002
+ const existingRoute = registry.routesByPath.get(route.path);
2003
+ return !existingRoute || !routesEqual(existingRoute, route);
2004
+ });
2005
+ applyRouteUpdates(registry, filePath, { added, removed, changed });
2006
+ return { added, removed, changed };
2007
+ }
2008
+ function getAllRoutesFromRegistry(registry) {
2009
+ return Array.from(registry.routesByPath.values());
2010
+ }
2011
+ function applyRouteUpdates(registry, filePath, updates) {
2012
+ const { added, removed, changed } = updates;
2013
+ removed.forEach((path6) => {
2014
+ registry.routesByPath.delete(path6);
2015
+ registry.pathToFile.delete(path6);
2016
+ });
2017
+ [...added, ...changed].forEach((route) => {
2018
+ registry.routesByPath.set(route.path, route);
2019
+ registry.pathToFile.set(route.path, filePath);
2020
+ });
2021
+ const allPathsForFile = /* @__PURE__ */ new Set([
2022
+ ...added.map((r) => r.path),
2023
+ ...changed.map((r) => r.path),
2024
+ ...Array.from(registry.routesByFile.get(filePath) || []).filter((p) => !removed.includes(p))
2025
+ ]);
2026
+ if (allPathsForFile.size > 0) {
2027
+ registry.routesByFile.set(filePath, allPathsForFile);
2028
+ } else {
2029
+ registry.routesByFile.delete(filePath);
2030
+ }
2031
+ }
2032
+ function routesEqual(route1, route2) {
2033
+ if (route1.path !== route2.path) return false;
2034
+ const methods1 = Object.keys(route1).filter((k) => k !== "path").sort();
2035
+ const methods2 = Object.keys(route2).filter((k) => k !== "path").sort();
2036
+ if (methods1.length !== methods2.length) return false;
2037
+ return methods1.every((method) => {
2038
+ const handler1 = route1[method];
2039
+ const handler2 = route2[method];
2040
+ return typeof handler1 === typeof handler2;
2041
+ });
2042
+ }
2043
+
2044
+ // src/router/utils/matching-helpers.ts
2045
+ function addRouteToMatcher(route, matcher) {
2046
+ Object.entries(route).forEach(([method, methodOptions]) => {
2047
+ if (method === "path" || !methodOptions) return;
2048
+ matcher.add(route.path, method, methodOptions);
2049
+ });
2050
+ }
2051
+ function removeRouteFromMatcher(path6, matcher) {
2052
+ if ("remove" in matcher && typeof matcher.remove === "function") {
2053
+ matcher.remove(path6);
2054
+ } else {
2055
+ console.warn("Matcher does not support selective removal, consider adding remove() method");
2056
+ }
2057
+ }
2058
+ function updateRouteInMatcher(route, matcher) {
2059
+ removeRouteFromMatcher(route.path, matcher);
2060
+ addRouteToMatcher(route, matcher);
2061
+ }
2062
+
1698
2063
  // src/router/router.ts
1699
2064
  var DEFAULT_ROUTER_OPTIONS = {
1700
2065
  routesDir: "./routes",
@@ -1709,46 +2074,55 @@ function createRouter(options) {
1709
2074
  if (options.basePath && !options.basePath.startsWith("/")) {
1710
2075
  console.warn("Base path does nothing");
1711
2076
  }
1712
- const routes = [];
2077
+ const registry = createRouteRegistry();
1713
2078
  const matcher = createMatcher();
1714
2079
  let initialized = false;
1715
2080
  let initializationPromise = null;
1716
2081
  let _watchers = null;
1717
- const routeSources = /* @__PURE__ */ new Map();
1718
2082
  const routeDirectories = /* @__PURE__ */ new Set([routerOptions.routesDir]);
1719
- function addRouteWithSource(route, source) {
1720
- const existingSources = routeSources.get(route.path) || [];
1721
- if (existingSources.includes(source)) {
1722
- console.warn(`Skipping duplicate route: ${route.path} from ${source}`);
1723
- return;
1724
- }
1725
- if (existingSources.length > 0) {
1726
- const conflictError = new Error(
1727
- `Route conflict for path "${route.path}": already defined in ${existingSources.join(", ")}, now being added from ${source}`
1728
- );
1729
- console.error(conflictError.message);
1730
- throw conflictError;
2083
+ function applyMatcherChanges(changes) {
2084
+ console.log("\n\u{1F527} APPLYING MATCHER CHANGES:");
2085
+ console.log(` Adding ${changes.added.length} routes`);
2086
+ console.log(` Removing ${changes.removed.length} routes`);
2087
+ console.log(` Updating ${changes.changed.length} routes`);
2088
+ changes.removed.forEach((routePath) => {
2089
+ console.log(` \u2796 Removing: ${routePath}`);
2090
+ removeRouteFromMatcher(routePath, matcher);
2091
+ });
2092
+ changes.added.forEach((route) => {
2093
+ const methods = Object.keys(route).filter((key) => key !== "path");
2094
+ console.log(` \u2795 Adding: ${route.path} [${methods.join(", ")}]`);
2095
+ addRouteToMatcher(route, matcher);
2096
+ });
2097
+ changes.changed.forEach((route) => {
2098
+ const methods = Object.keys(route).filter((key) => key !== "path");
2099
+ console.log(` \u{1F504} Updating: ${route.path} [${methods.join(", ")}]`);
2100
+ updateRouteInMatcher(route, matcher);
2101
+ });
2102
+ console.log("\u2705 Matcher changes applied\n");
2103
+ }
2104
+ function addRoutesWithSource(routes, source) {
2105
+ try {
2106
+ const changes = updateRoutesFromFile(registry, source, routes);
2107
+ applyMatcherChanges(changes);
2108
+ return changes;
2109
+ } catch (error) {
2110
+ console.error(`\u26A0\uFE0F Route conflicts from ${source}:`, error);
2111
+ throw error;
1731
2112
  }
1732
- routeSources.set(route.path, [...existingSources, source]);
1733
- addRouteInternal(route);
1734
2113
  }
1735
2114
  async function loadRoutesFromDirectory(directory, source, prefix) {
1736
2115
  try {
1737
- const discoveredRoutes = await findRoutes(directory, {
1738
- basePath: routerOptions.basePath
1739
- });
1740
- for (const route of discoveredRoutes) {
1741
- const finalRoute = prefix ? {
1742
- ...route,
1743
- path: `${prefix}${route.path}`
1744
- } : route;
1745
- addRouteWithSource(finalRoute, source);
1746
- }
2116
+ const discoveredRoutes = await loadInitialRoutesParallel(directory);
2117
+ const finalRoutes = discoveredRoutes.map(
2118
+ (route) => prefix ? { ...route, path: `${prefix}${route.path}` } : route
2119
+ );
2120
+ const changes = addRoutesWithSource(finalRoutes, source);
1747
2121
  console.log(
1748
- `Loaded ${discoveredRoutes.length} routes from ${source}${prefix ? ` with prefix ${prefix}` : ""}`
2122
+ `Loaded ${discoveredRoutes.length} routes from ${source}${prefix ? ` with prefix ${prefix}` : ""} (${changes.added.length} added, ${changes.changed.length} changed, ${changes.removed.length} removed)`
1749
2123
  );
1750
2124
  } catch (error) {
1751
- console.error(`Failed to load routes from ${source}:`, error);
2125
+ console.error(`\u26A0\uFE0F Failed to load routes from ${source}:`, error);
1752
2126
  throw error;
1753
2127
  }
1754
2128
  }
@@ -1758,105 +2132,126 @@ function createRouter(options) {
1758
2132
  }
1759
2133
  initializationPromise = (async () => {
1760
2134
  try {
1761
- for (const directory of routeDirectories) {
1762
- await loadRoutesFromDirectory(directory, directory);
1763
- }
2135
+ await Promise.all(
2136
+ Array.from(routeDirectories).map(
2137
+ (directory) => loadRoutesFromDirectory(directory, directory)
2138
+ )
2139
+ );
1764
2140
  if (routerOptions.watchMode) {
1765
- setupWatcherForAllDirectories();
2141
+ setupOptimizedWatching();
1766
2142
  }
1767
2143
  initialized = true;
1768
2144
  } catch (error) {
1769
- console.error("Failed to initialize router:", error);
2145
+ console.error("\u26A0\uFE0F Failed to initialize router:", error);
1770
2146
  throw error;
1771
2147
  }
1772
2148
  })();
1773
2149
  return initializationPromise;
1774
2150
  }
1775
- function addRouteInternal(route) {
1776
- routes.push(route);
1777
- Object.entries(route).forEach(([method, methodOptions]) => {
1778
- if (method === "path" || !methodOptions) return;
1779
- matcher.add(route.path, method, methodOptions);
1780
- });
1781
- }
1782
- function createWatcherCallbacks(directory, source, prefix) {
1783
- return {
1784
- onRouteAdded: (addedRoutes) => {
1785
- console.log(
1786
- `${addedRoutes.length} route(s) added from ${directory}:`,
1787
- addedRoutes.map((r) => r.path)
1788
- );
1789
- addedRoutes.forEach((route) => {
1790
- const finalRoute = prefix ? { ...route, path: `${prefix}${route.path}` } : route;
1791
- addRouteWithSource(finalRoute, source);
1792
- });
1793
- },
1794
- onRouteChanged: (changedRoutes) => {
1795
- console.log(
1796
- `${changedRoutes.length} route(s) changed in ${directory}:`,
1797
- changedRoutes.map((r) => r.path)
1798
- );
1799
- changedRoutes.forEach((route) => {
1800
- const finalPath = prefix ? `${prefix}${route.path}` : route.path;
1801
- const index = routes.findIndex((r) => r.path === finalPath);
1802
- if (index >= 0) {
1803
- routes.splice(index, 1);
1804
- const sources = routeSources.get(finalPath) || [];
1805
- const filteredSources = sources.filter((s) => s !== source);
1806
- if (filteredSources.length > 0) {
1807
- routeSources.set(finalPath, filteredSources);
1808
- } else {
1809
- routeSources.delete(finalPath);
2151
+ function setupOptimizedWatching() {
2152
+ if (!_watchers) {
2153
+ _watchers = /* @__PURE__ */ new Map();
2154
+ }
2155
+ for (const directory of routeDirectories) {
2156
+ if (!_watchers.has(directory)) {
2157
+ const watcher = watchRoutes(directory, {
2158
+ debounceMs: 16,
2159
+ // ~60fps debouncing
2160
+ ignore: ["node_modules", ".git"],
2161
+ onRouteAdded: (filepath, addedRoutes) => {
2162
+ try {
2163
+ const changes = updateRoutesFromFile(registry, filepath, addedRoutes);
2164
+ applyMatcherChanges(changes);
2165
+ } catch (error) {
2166
+ console.error(`Error adding routes from ${directory}:`, error);
2167
+ }
2168
+ },
2169
+ onRouteChanged: withPerformanceTracking(
2170
+ async (filepath, changedRoutes) => {
2171
+ try {
2172
+ console.log(`Processing changes for ${filepath}`);
2173
+ const changes = updateRoutesFromFile(registry, filepath, changedRoutes);
2174
+ console.log(
2175
+ `Changes detected: ${changes.added.length} added, ${changes.changed.length} changed, ${changes.removed.length} removed`
2176
+ );
2177
+ applyMatcherChanges(changes);
2178
+ console.log(
2179
+ `Route changes applied: ${changes.added.length} added, ${changes.changed.length} changed, ${changes.removed.length} removed`
2180
+ );
2181
+ } catch (error) {
2182
+ console.error(`\u26A0\uFE0F Error updating routes from ${directory}:`, error);
2183
+ }
2184
+ },
2185
+ directory
2186
+ ),
2187
+ onRouteRemoved: (filePath, removedRoutes) => {
2188
+ console.log(`File removed: ${filePath} with ${removedRoutes.length} routes`);
2189
+ try {
2190
+ removedRoutes.forEach((route) => {
2191
+ removeRouteFromMatcher(route.path, matcher);
2192
+ });
2193
+ clearFileCache(filePath);
2194
+ } catch (error) {
2195
+ console.error(`\u26A0\uFE0F Error removing routes from ${filePath}:`, error);
1810
2196
  }
2197
+ },
2198
+ onError: (error) => {
2199
+ console.error(`\u26A0\uFE0F Route watcher error for ${directory}:`, error);
1811
2200
  }
1812
- const finalRoute = prefix ? { ...route, path: finalPath } : route;
1813
- addRouteWithSource(finalRoute, source);
1814
2201
  });
2202
+ _watchers.set(directory, watcher);
2203
+ }
2204
+ }
2205
+ }
2206
+ function setupWatcherForNewDirectory(directory, prefix) {
2207
+ if (!_watchers) {
2208
+ _watchers = /* @__PURE__ */ new Map();
2209
+ }
2210
+ const watcher = watchRoutes(directory, {
2211
+ debounceMs: 16,
2212
+ ignore: ["node_modules", ".git"],
2213
+ onRouteAdded: (filePath, addedRoutes) => {
2214
+ try {
2215
+ const finalRoutes = addedRoutes.map(
2216
+ (route) => prefix ? { ...route, path: `${prefix}${route.path}` } : route
2217
+ );
2218
+ const changes = updateRoutesFromFile(registry, filePath, finalRoutes);
2219
+ applyMatcherChanges(changes);
2220
+ } catch (error) {
2221
+ console.error(`\u26A0\uFE0F Error adding routes from ${directory}:`, error);
2222
+ }
1815
2223
  },
2224
+ onRouteChanged: withPerformanceTracking(async (filePath, changedRoutes) => {
2225
+ try {
2226
+ const finalRoutes = changedRoutes.map(
2227
+ (route) => prefix ? { ...route, path: `${prefix}${route.path}` } : route
2228
+ );
2229
+ const changes = updateRoutesFromFile(registry, filePath, finalRoutes);
2230
+ applyMatcherChanges(changes);
2231
+ } catch (error) {
2232
+ console.error(`\u26A0\uFE0F Error updating routes from ${directory}:`, error);
2233
+ }
2234
+ }, directory),
1816
2235
  onRouteRemoved: (filePath, removedRoutes) => {
1817
- console.log(
1818
- `File removed from ${directory}: ${filePath} with ${removedRoutes.length} route(s):`,
1819
- removedRoutes.map((r) => r.path)
1820
- );
1821
- removedRoutes.forEach((route) => {
1822
- const finalPath = prefix ? `${prefix}${route.path}` : route.path;
1823
- const index = routes.findIndex((r) => r.path === finalPath);
1824
- if (index >= 0) {
1825
- routes.splice(index, 1);
1826
- }
1827
- const sources = routeSources.get(finalPath) || [];
1828
- const filteredSources = sources.filter((s) => s !== source);
1829
- if (filteredSources.length > 0) {
1830
- routeSources.set(finalPath, filteredSources);
1831
- } else {
1832
- routeSources.delete(finalPath);
1833
- }
1834
- });
2236
+ try {
2237
+ removedRoutes.forEach((route) => {
2238
+ const finalPath = prefix ? `${prefix}${route.path}` : route.path;
2239
+ removeRouteFromMatcher(finalPath, matcher);
2240
+ });
2241
+ clearFileCache(filePath);
2242
+ } catch (error) {
2243
+ console.error(`Error removing routes from ${filePath}:`, error);
2244
+ }
1835
2245
  },
1836
2246
  onError: (error) => {
1837
- console.error(`Route watcher error for ${directory}:`, error);
2247
+ console.error(`\u26A0\uFE0F Route watcher error for ${directory}:`, error);
1838
2248
  }
1839
- };
1840
- }
1841
- function setupWatcherForDirectory(directory, source, prefix) {
1842
- const callbacks = createWatcherCallbacks(directory, source, prefix);
1843
- const watcher = watchRoutes(directory, {
1844
- ignore: ["node_modules", ".git"],
1845
- ...callbacks
1846
2249
  });
1847
- if (!_watchers) {
1848
- _watchers = /* @__PURE__ */ new Map();
1849
- }
1850
2250
  _watchers.set(directory, watcher);
1851
2251
  return watcher;
1852
2252
  }
1853
- function setupWatcherForAllDirectories() {
1854
- for (const directory of routeDirectories) {
1855
- setupWatcherForDirectory(directory, directory);
1856
- }
1857
- }
1858
2253
  initialize().catch((error) => {
1859
- console.error("Failed to initialize router on creation:", error);
2254
+ console.error("\u26A0\uFE0F Failed to initialize router on creation:", error);
1860
2255
  });
1861
2256
  return {
1862
2257
  /**
@@ -1864,17 +2259,23 @@ function createRouter(options) {
1864
2259
  */
1865
2260
  async handleRequest(ctx) {
1866
2261
  if (!initialized) {
2262
+ console.log("\u{1F504} Router not initialized, initializing...");
1867
2263
  await initialize();
1868
2264
  }
1869
- const { method, path: path5 } = ctx.request;
1870
- const match = matcher.match(path5, method);
2265
+ const { method, path: path6 } = ctx.request;
2266
+ console.log(`
2267
+ \u{1F4E5} Handling request: ${method} ${path6}`);
2268
+ const match = matcher.match(path6, method);
1871
2269
  if (!match) {
2270
+ console.log(`\u274C No match found for: ${method} ${path6}`);
1872
2271
  ctx.response.status(404).json({ error: "Not Found" });
1873
2272
  return;
1874
2273
  }
2274
+ console.log(`\u2705 Route matched: ${method} ${path6}`);
2275
+ console.log(` Params: ${JSON.stringify(match.params)}`);
1875
2276
  if (match.methodNotAllowed) {
1876
2277
  ctx.response.status(405).json({
1877
- error: "Method Not Allowed",
2278
+ error: "\u274C Method Not Allowed",
1878
2279
  allowed: match.allowedMethods
1879
2280
  });
1880
2281
  if (match.allowedMethods && match.allowedMethods.length > 0) {
@@ -1893,19 +2294,28 @@ function createRouter(options) {
1893
2294
  }
1894
2295
  },
1895
2296
  /**
1896
- * Get all registered routes
2297
+ * Get all registered routes (using optimized registry)
1897
2298
  */
1898
2299
  getRoutes() {
1899
- return [...routes];
2300
+ return getAllRoutesFromRegistry(registry);
1900
2301
  },
1901
2302
  /**
1902
2303
  * Add a route programmatically
1903
2304
  */
1904
2305
  addRoute(route) {
1905
- addRouteInternal(route);
2306
+ const changes = updateRoutesFromFile(registry, "programmatic", [route]);
2307
+ applyMatcherChanges(changes);
1906
2308
  },
1907
2309
  /**
1908
- * Add a route directory (for plugins)
2310
+ * Add multiple routes programmatically with batch processing
2311
+ */
2312
+ addRoutes(routes) {
2313
+ const changes = updateRoutesFromFile(registry, "programmatic", routes);
2314
+ applyMatcherChanges(changes);
2315
+ return changes;
2316
+ },
2317
+ /**
2318
+ * Add a route directory (for plugins) with optimized loading
1909
2319
  */
1910
2320
  async addRouteDirectory(directory, options2 = {}) {
1911
2321
  if (routeDirectories.has(directory)) {
@@ -1916,21 +2326,27 @@ function createRouter(options) {
1916
2326
  if (initialized) {
1917
2327
  await loadRoutesFromDirectory(directory, directory, options2.prefix);
1918
2328
  if (routerOptions.watchMode) {
1919
- setupWatcherForDirectory(directory, directory, options2.prefix);
2329
+ setupWatcherForNewDirectory(directory, options2.prefix);
1920
2330
  }
1921
2331
  }
1922
2332
  },
1923
2333
  /**
1924
- * Get route conflicts
2334
+ * Get route conflicts (using registry)
1925
2335
  */
1926
2336
  getRouteConflicts() {
1927
2337
  const conflicts = [];
1928
- for (const [path5, sources] of routeSources.entries()) {
1929
- if (sources.length > 1) {
1930
- conflicts.push({ path: path5, sources });
2338
+ return conflicts;
2339
+ },
2340
+ /**
2341
+ * Close watchers and cleanup (useful for testing)
2342
+ */
2343
+ async close() {
2344
+ if (_watchers) {
2345
+ for (const watcher of _watchers.values()) {
2346
+ await watcher.close();
1931
2347
  }
2348
+ _watchers.clear();
1932
2349
  }
1933
- return conflicts;
1934
2350
  }
1935
2351
  };
1936
2352
  }