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.js CHANGED
@@ -1,11 +1,17 @@
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
6
6
  * @license MIT
7
7
  */
8
8
 
9
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
10
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
11
+ }) : x)(function(x) {
12
+ if (typeof require !== "undefined") return require.apply(this, arguments);
13
+ throw Error('Dynamic require of "' + x + '" is not supported');
14
+ });
9
15
 
10
16
  // src/middleware/execute.ts
11
17
  function execute(middleware, ctx, next) {
@@ -107,6 +113,9 @@ function create2(name, version, setup, defaultOptions = {}) {
107
113
  };
108
114
  }
109
115
 
116
+ // src/router/create.ts
117
+ import { fileURLToPath } from "node:url";
118
+
110
119
  // src/config.ts
111
120
  var config = {};
112
121
  function setRuntimeConfig(newConfig) {
@@ -178,73 +187,75 @@ function getCallerFilePath() {
178
187
  if (!fileName) {
179
188
  throw new Error("Unable to determine caller file name");
180
189
  }
190
+ if (fileName.startsWith("file://")) {
191
+ return fileURLToPath(fileName);
192
+ }
181
193
  return fileName;
182
194
  } finally {
183
195
  Error.prepareStackTrace = originalPrepareStackTrace;
184
196
  }
185
197
  }
186
198
  function getRoutePath() {
187
- console.log("getRoutePath called");
188
199
  const callerPath = getCallerFilePath();
189
200
  const routesDir = getRoutesDir();
190
201
  const parsedRoute = parseRoutePath(callerPath, routesDir);
191
- console.log(`Parsed route path: ${parsedRoute.routePath} from file: ${callerPath}`);
202
+ console.log(`\u{1F50E} Parsed route path: ${parsedRoute.routePath} from file: ${callerPath}`);
192
203
  return parsedRoute.routePath;
193
204
  }
194
205
  var createGetRoute = (config2) => {
195
206
  validateMethodConfig("GET", config2);
196
- const path5 = getRoutePath();
207
+ const path6 = getRoutePath();
197
208
  return {
198
209
  GET: config2,
199
- path: path5
210
+ path: path6
200
211
  };
201
212
  };
202
213
  var createPostRoute = (config2) => {
203
214
  validateMethodConfig("POST", config2);
204
- const path5 = getRoutePath();
215
+ const path6 = getRoutePath();
205
216
  return {
206
217
  POST: config2,
207
- path: path5
218
+ path: path6
208
219
  };
209
220
  };
210
221
  var createPutRoute = (config2) => {
211
222
  validateMethodConfig("PUT", config2);
212
- const path5 = getRoutePath();
223
+ const path6 = getRoutePath();
213
224
  return {
214
225
  PUT: config2,
215
- path: path5
226
+ path: path6
216
227
  };
217
228
  };
218
229
  var createDeleteRoute = (config2) => {
219
230
  validateMethodConfig("DELETE", config2);
220
- const path5 = getRoutePath();
231
+ const path6 = getRoutePath();
221
232
  return {
222
233
  DELETE: config2,
223
- path: path5
234
+ path: path6
224
235
  };
225
236
  };
226
237
  var createPatchRoute = (config2) => {
227
238
  validateMethodConfig("PATCH", config2);
228
- const path5 = getRoutePath();
239
+ const path6 = getRoutePath();
229
240
  return {
230
241
  PATCH: config2,
231
- path: path5
242
+ path: path6
232
243
  };
233
244
  };
234
245
  var createHeadRoute = (config2) => {
235
246
  validateMethodConfig("HEAD", config2);
236
- const path5 = getRoutePath();
247
+ const path6 = getRoutePath();
237
248
  return {
238
249
  HEAD: config2,
239
- path: path5
250
+ path: path6
240
251
  };
241
252
  };
242
253
  var createOptionsRoute = (config2) => {
243
254
  validateMethodConfig("OPTIONS", config2);
244
- const path5 = getRoutePath();
255
+ const path6 = getRoutePath();
245
256
  return {
246
257
  OPTIONS: config2,
247
- path: path5
258
+ path: path6
248
259
  };
249
260
  };
250
261
  function validateMethodConfig(method, config2) {
@@ -389,7 +400,7 @@ function parseRequestUrl(req) {
389
400
  const fullUrl = `${protocol}://${host}${originalUrl.startsWith("/") ? "" : "/"}${originalUrl}`;
390
401
  try {
391
402
  const url = new URL(fullUrl);
392
- const path5 = url.pathname;
403
+ const path6 = url.pathname;
393
404
  const query = {};
394
405
  url.searchParams.forEach((value, key) => {
395
406
  if (query[key] !== void 0) {
@@ -402,7 +413,7 @@ function parseRequestUrl(req) {
402
413
  query[key] = value;
403
414
  }
404
415
  });
405
- return { path: path5, url, query };
416
+ return { path: path6, url, query };
406
417
  } catch (error) {
407
418
  console.warn(`Invalid URL: ${fullUrl}`, error);
408
419
  throw new ParseUrlError(`Invalid URL: ${fullUrl}`);
@@ -424,7 +435,7 @@ function getProtocol(req) {
424
435
  return encrypted ? "https" : "http";
425
436
  }
426
437
  async function createContext(req, res, options = {}) {
427
- const { path: path5, url, query } = parseRequestUrl(req);
438
+ const { path: path6, url, query } = parseRequestUrl(req);
428
439
  const method = req.method || "GET";
429
440
  const isHttp2 = isHttp2Request(req);
430
441
  const protocol = getProtocol(req);
@@ -433,7 +444,7 @@ async function createContext(req, res, options = {}) {
433
444
  const responseState = { sent: false };
434
445
  const ctx = {
435
446
  request: createRequestObject(req, {
436
- path: path5,
447
+ path: path6,
437
448
  url,
438
449
  query,
439
450
  params,
@@ -705,13 +716,13 @@ function setBodyError(ctx, type, message, error) {
705
716
  ctx.state._bodyError = { type, message, error };
706
717
  }
707
718
  async function readRequestBody(req) {
708
- return new Promise((resolve2, reject) => {
719
+ return new Promise((resolve3, reject) => {
709
720
  const chunks = [];
710
721
  req.on("data", (chunk) => {
711
722
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
712
723
  });
713
724
  req.on("end", () => {
714
- resolve2(Buffer.concat(chunks).toString("utf8"));
725
+ resolve3(Buffer.concat(chunks).toString("utf8"));
715
726
  });
716
727
  req.on("error", (err) => {
717
728
  reject(err);
@@ -809,7 +820,7 @@ function createServerInstance(isHttp2, certOptions) {
809
820
  return http2.createSecureServer(http2ServerOptions);
810
821
  }
811
822
  function listenOnPort(server, port, host, isHttp2) {
812
- return new Promise((resolve2, reject) => {
823
+ return new Promise((resolve3, reject) => {
813
824
  server.listen(port, host, () => {
814
825
  const protocol = isHttp2 ? "https" : "http";
815
826
  const url = `${protocol}://${host}:${port}`;
@@ -826,7 +837,7 @@ function listenOnPort(server, port, host, isHttp2) {
826
837
 
827
838
  \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
839
  `);
829
- resolve2();
840
+ resolve3();
830
841
  });
831
842
  server.on("error", (err) => {
832
843
  console.error("Server error:", err);
@@ -870,55 +881,128 @@ async function startServer(serverInstance, serverOptions) {
870
881
  }
871
882
 
872
883
  // src/server/stop.ts
884
+ var isShuttingDown = false;
873
885
  async function stopServer(serverInstance, options = {}) {
874
886
  const server = serverInstance.server;
875
887
  const events = serverInstance.events;
888
+ if (isShuttingDown) {
889
+ console.log("\u26A0\uFE0F Shutdown already in progress, ignoring duplicate shutdown request");
890
+ return;
891
+ }
876
892
  if (!server) {
877
893
  return;
878
894
  }
879
- const timeout = options.timeout || 3e4;
895
+ isShuttingDown = true;
896
+ const timeout = options.timeout || 5e3;
880
897
  try {
881
898
  if (options.onStopping) {
882
899
  await options.onStopping();
883
900
  }
884
901
  events.emit("stopping");
885
- await serverInstance.pluginManager.onServerStop(serverInstance, server);
902
+ if (serverInstance.router && typeof serverInstance.router.close === "function") {
903
+ console.log("\u{1F50C} Closing router watchers...");
904
+ try {
905
+ await Promise.race([
906
+ serverInstance.router.close(),
907
+ new Promise(
908
+ (_, reject) => setTimeout(() => reject(new Error("Router close timeout")), 2e3)
909
+ )
910
+ ]);
911
+ console.log("\u2705 Router watchers closed");
912
+ } catch (error) {
913
+ console.error("\u274C Error closing router watchers:", error);
914
+ }
915
+ }
916
+ try {
917
+ await Promise.race([
918
+ serverInstance.pluginManager.onServerStop(serverInstance, server),
919
+ new Promise(
920
+ (_, reject) => setTimeout(() => reject(new Error("Plugin stop timeout")), 2e3)
921
+ )
922
+ ]);
923
+ } catch (error) {
924
+ console.error("\u274C Plugin stop timeout:", error);
925
+ }
926
+ const closePromise = new Promise((resolve3, reject) => {
927
+ server.close((err) => {
928
+ if (err) return reject(err);
929
+ resolve3();
930
+ });
931
+ });
886
932
  const timeoutPromise = new Promise((_, reject) => {
887
933
  setTimeout(() => {
888
- reject(new Error("Server shutdown timed out waiting for requests to complete"));
934
+ reject(new Error("Server shutdown timeout"));
889
935
  }, timeout);
890
936
  });
891
- const closePromise = new Promise((resolve2, reject) => {
892
- server.close((err) => {
893
- if (err) {
894
- return reject(err);
895
- }
896
- resolve2();
897
- });
898
- });
899
937
  await Promise.race([closePromise, timeoutPromise]);
900
- await serverInstance.pluginManager.terminatePlugins(serverInstance);
938
+ try {
939
+ await Promise.race([
940
+ serverInstance.pluginManager.terminatePlugins(serverInstance),
941
+ new Promise(
942
+ (_, reject) => setTimeout(() => reject(new Error("Plugin terminate timeout")), 1e3)
943
+ )
944
+ ]);
945
+ } catch (error) {
946
+ console.error("\u274C Plugin terminate timeout:", error);
947
+ }
901
948
  if (options.onStopped) {
902
949
  await options.onStopped();
903
950
  }
904
951
  events.emit("stopped");
905
952
  serverInstance.server = null;
953
+ console.log("\u2705 Graceful shutdown completed");
954
+ isShuttingDown = false;
906
955
  } catch (error) {
956
+ isShuttingDown = false;
957
+ console.error("\u26A0\uFE0F Shutdown error (forcing exit):", error);
958
+ if (server && typeof server.close === "function") {
959
+ server.close();
960
+ }
961
+ if (process.env.NODE_ENV === "development") {
962
+ console.log("\u{1F504} Forcing exit for development restart...");
963
+ process.exit(0);
964
+ }
907
965
  events.emit("error", error);
908
966
  throw error;
909
967
  }
910
968
  }
911
969
  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
- };
970
+ const isDevelopment = process.env.NODE_ENV === "development";
971
+ if (isDevelopment) {
972
+ const sigintHandler = () => {
973
+ console.log("\u{1F4E4} SIGINT received, forcing exit for development restart...");
974
+ process.exit(0);
975
+ };
976
+ const sigtermHandler = () => {
977
+ console.log("\u{1F4E4} SIGTERM received, forcing exit for development restart...");
978
+ process.exit(0);
979
+ };
980
+ process.on("SIGINT", sigintHandler);
981
+ process.on("SIGTERM", sigtermHandler);
982
+ return {
983
+ unregister: () => {
984
+ process.removeListener("SIGINT", sigintHandler);
985
+ process.removeListener("SIGTERM", sigtermHandler);
986
+ }
987
+ };
988
+ } else {
989
+ const sigintHandler = () => {
990
+ console.log("\u{1F4E4} SIGINT received, starting graceful shutdown...");
991
+ stopFn().catch(console.error);
992
+ };
993
+ const sigtermHandler = () => {
994
+ console.log("\u{1F4E4} SIGTERM received, starting graceful shutdown...");
995
+ stopFn().catch(console.error);
996
+ };
997
+ process.on("SIGINT", sigintHandler);
998
+ process.on("SIGTERM", sigtermHandler);
999
+ return {
1000
+ unregister: () => {
1001
+ process.removeListener("SIGINT", sigintHandler);
1002
+ process.removeListener("SIGTERM", sigtermHandler);
1003
+ }
1004
+ };
1005
+ }
922
1006
  }
923
1007
 
924
1008
  // src/server/validation.ts
@@ -1124,59 +1208,33 @@ function validatePlugin(plugin, options = {}) {
1124
1208
  }
1125
1209
  }
1126
1210
 
1127
- // src/router/discovery/finder.ts
1211
+ // src/router/discovery/cache.ts
1212
+ import * as crypto from "node:crypto";
1128
1213
  import * as fs3 from "node:fs/promises";
1214
+ import { createRequire } from "node:module";
1129
1215
  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
1216
 
1167
1217
  // src/router/discovery/loader.ts
1168
1218
  async function dynamicImport(filePath) {
1169
- return import(filePath);
1219
+ const cacheBuster = `?t=${Date.now()}`;
1220
+ const importPath = filePath + cacheBuster;
1221
+ try {
1222
+ const module = await import(importPath);
1223
+ console.log(`\u2705 Successfully imported module`);
1224
+ return module;
1225
+ } catch (error) {
1226
+ const errorMessage = error instanceof Error ? error.message : String(error);
1227
+ console.log(`\u26A0\uFE0F Error importing with cache buster, trying original path:`, errorMessage);
1228
+ return import(filePath);
1229
+ }
1170
1230
  }
1171
1231
  async function loadRouteModule(filePath, basePath) {
1172
1232
  try {
1173
1233
  const parsedRoute = parseRoutePath(filePath, basePath);
1174
- console.log("parsedRoute:", parsedRoute);
1175
1234
  const module = await dynamicImport(filePath);
1176
- console.log("Module exports:", Object.keys(module));
1235
+ console.log("\u{1F4E6} Module exports:", Object.keys(module));
1177
1236
  const routes = [];
1178
1237
  if (module.default && typeof module.default === "object") {
1179
- console.log("Found default export:", module.default);
1180
1238
  const route = {
1181
1239
  ...module.default,
1182
1240
  path: parsedRoute.routePath
@@ -1189,7 +1247,6 @@ async function loadRouteModule(filePath, basePath) {
1189
1247
  }
1190
1248
  const potentialRoute = exportValue;
1191
1249
  if (isValidRoute(potentialRoute)) {
1192
- console.log(`Found named route export: ${exportName}`, potentialRoute);
1193
1250
  const route = {
1194
1251
  ...potentialRoute,
1195
1252
  // Use the route's own path if it has one, otherwise derive from file
@@ -1202,7 +1259,7 @@ async function loadRouteModule(filePath, basePath) {
1202
1259
  console.warn(`Route file ${filePath} does not export any valid route definitions`);
1203
1260
  return [];
1204
1261
  }
1205
- console.log(`Loaded ${routes.length} route(s) from ${filePath}`);
1262
+ console.log(`\u2705 Successfully Loaded ${routes.length} route(s)`);
1206
1263
  return routes;
1207
1264
  } catch (error) {
1208
1265
  console.error(`Failed to load route module ${filePath}:`, error);
@@ -1220,25 +1277,222 @@ function isValidRoute(obj) {
1220
1277
  return hasHttpMethod;
1221
1278
  }
1222
1279
 
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
- }
1280
+ // src/router/discovery/cache.ts
1281
+ var fileRouteCache = /* @__PURE__ */ new Map();
1282
+ async function processChangedFile(filePath, routesDir, updateCache = true) {
1283
+ const stat3 = await fs3.stat(filePath);
1284
+ const lastModified = stat3.mtime.getTime();
1285
+ const cachedEntry = fileRouteCache.get(filePath);
1286
+ if (updateCache && cachedEntry && cachedEntry.timestamp === lastModified) {
1287
+ return cachedEntry.routes;
1288
+ }
1289
+ invalidateModuleCache(filePath);
1290
+ const routes = await loadRouteModule(filePath, routesDir);
1291
+ if (updateCache) {
1292
+ const hash = hashRoutes(routes);
1293
+ fileRouteCache.set(filePath, {
1294
+ routes,
1295
+ timestamp: lastModified,
1296
+ hash
1297
+ });
1234
1298
  }
1235
1299
  return routes;
1236
1300
  }
1301
+ function hasRouteContentChanged(filePath, newRoutes) {
1302
+ const cachedEntry = fileRouteCache.get(filePath);
1303
+ if (!cachedEntry) {
1304
+ return true;
1305
+ }
1306
+ const newHash = hashRoutes(newRoutes);
1307
+ return cachedEntry.hash !== newHash;
1308
+ }
1309
+ function clearFileCache(filePath) {
1310
+ if (filePath) {
1311
+ fileRouteCache.delete(filePath);
1312
+ } else {
1313
+ fileRouteCache.clear();
1314
+ }
1315
+ }
1316
+ function hashRoutes(routes) {
1317
+ const routeData = routes.map((route) => ({
1318
+ path: route.path,
1319
+ methods: Object.keys(route).filter((key) => key !== "path").sort().map((method) => {
1320
+ const methodDef = route[method];
1321
+ const handlerString = methodDef?.handler ? methodDef.handler.toString() : null;
1322
+ return {
1323
+ method,
1324
+ // Include handler function string for change detection
1325
+ handler: handlerString,
1326
+ // Include middleware if present
1327
+ middleware: methodDef?.middleware ? methodDef.middleware.length : 0,
1328
+ // Include schema structure (but not full serialization which can be unstable)
1329
+ hasSchema: !!methodDef?.schema,
1330
+ schemaKeys: methodDef?.schema ? Object.keys(methodDef.schema).sort() : []
1331
+ };
1332
+ })
1333
+ }));
1334
+ const dataString = JSON.stringify(routeData);
1335
+ const hash = crypto.createHash("md5").update(dataString).digest("hex");
1336
+ return hash;
1337
+ }
1338
+ function invalidateModuleCache(filePath) {
1339
+ try {
1340
+ const absolutePath = path3.resolve(filePath);
1341
+ if (typeof __require !== "undefined") {
1342
+ delete __require.cache[absolutePath];
1343
+ try {
1344
+ const resolvedPath = __require.resolve(absolutePath);
1345
+ delete __require.cache[resolvedPath];
1346
+ } catch (resolveError) {
1347
+ const errorMessage = resolveError instanceof Error ? resolveError.message : String(resolveError);
1348
+ console.log(`\u26A0\uFE0F Could not resolve path: ${errorMessage}`);
1349
+ }
1350
+ } else {
1351
+ try {
1352
+ const require2 = createRequire(import.meta.url);
1353
+ delete require2.cache[absolutePath];
1354
+ try {
1355
+ const resolvedPath = require2.resolve(absolutePath);
1356
+ delete require2.cache[resolvedPath];
1357
+ } catch {
1358
+ console.log(`\u26A0\uFE0F Could not resolve ESM path`);
1359
+ }
1360
+ } catch {
1361
+ console.log(`\u26A0\uFE0F createRequire not available in pure ESM`);
1362
+ }
1363
+ }
1364
+ } catch (error) {
1365
+ console.log(`\u26A0\uFE0F Error during module cache invalidation for ${filePath}:`, error);
1366
+ }
1367
+ }
1237
1368
 
1238
- // src/router/discovery/watchers.ts
1369
+ // src/router/discovery/parallel.ts
1370
+ import * as os from "node:os";
1371
+
1372
+ // src/router/discovery/finder.ts
1373
+ import * as fs4 from "node:fs/promises";
1239
1374
  import * as path4 from "node:path";
1375
+ async function findRouteFiles(routesDir, options = {}) {
1376
+ const absoluteDir = path4.isAbsolute(routesDir) ? routesDir : path4.resolve(process.cwd(), routesDir);
1377
+ console.log("Creating router with routes directory:", absoluteDir);
1378
+ try {
1379
+ const stats = await fs4.stat(absoluteDir);
1380
+ if (!stats.isDirectory()) {
1381
+ throw new Error(`Route directory is not a directory: ${absoluteDir}`);
1382
+ }
1383
+ } catch (error) {
1384
+ if (error.code === "ENOENT") {
1385
+ throw new Error(`Route directory not found: ${absoluteDir}`);
1386
+ }
1387
+ throw error;
1388
+ }
1389
+ const routeFiles = [];
1390
+ const ignore = options.ignore || ["node_modules", ".git"];
1391
+ async function scanDirectory(dir) {
1392
+ const entries = await fs4.readdir(dir, { withFileTypes: true });
1393
+ for (const entry of entries) {
1394
+ const fullPath = path4.join(dir, entry.name);
1395
+ if (entry.isDirectory() && ignore.includes(entry.name)) {
1396
+ continue;
1397
+ }
1398
+ if (entry.isDirectory()) {
1399
+ await scanDirectory(fullPath);
1400
+ } else if (isRouteFile(entry.name)) {
1401
+ routeFiles.push(fullPath);
1402
+ }
1403
+ }
1404
+ }
1405
+ await scanDirectory(absoluteDir);
1406
+ return routeFiles;
1407
+ }
1408
+ function isRouteFile(filename) {
1409
+ return !filename.startsWith("_") && (filename.endsWith(".ts") || filename.endsWith(".js"));
1410
+ }
1411
+
1412
+ // src/router/discovery/parallel.ts
1413
+ async function processFilesInParallel(filePaths, processor, concurrency = Math.max(1, Math.floor(os.cpus().length / 2))) {
1414
+ const chunks = chunkArray(filePaths, concurrency);
1415
+ const results = [];
1416
+ for (const chunk of chunks) {
1417
+ const chunkResults = await Promise.allSettled(chunk.map((filePath) => processor(filePath)));
1418
+ const successfulResults = chunkResults.filter((result) => result.status === "fulfilled").map((result) => result.value);
1419
+ results.push(...successfulResults);
1420
+ }
1421
+ return results;
1422
+ }
1423
+ async function loadInitialRoutesParallel(routesDir) {
1424
+ const files = await findRouteFiles(routesDir);
1425
+ const routeArrays = await processFilesInParallel(
1426
+ files,
1427
+ (filePath) => processChangedFile(filePath, routesDir)
1428
+ );
1429
+ return routeArrays.flat();
1430
+ }
1431
+ function chunkArray(array, chunkSize) {
1432
+ const chunks = [];
1433
+ for (let i = 0; i < array.length; i += chunkSize) {
1434
+ chunks.push(array.slice(i, i + chunkSize));
1435
+ }
1436
+ return chunks;
1437
+ }
1438
+
1439
+ // src/router/discovery/profiler.ts
1440
+ var profilerState = {
1441
+ fileChanges: 0,
1442
+ totalReloadTime: 0,
1443
+ averageReloadTime: 0,
1444
+ slowReloads: []
1445
+ };
1446
+ function trackReloadPerformance(filePath, startTime) {
1447
+ const duration = Date.now() - startTime;
1448
+ profilerState.fileChanges++;
1449
+ profilerState.totalReloadTime += duration;
1450
+ profilerState.averageReloadTime = profilerState.totalReloadTime / profilerState.fileChanges;
1451
+ if (duration > 100) {
1452
+ profilerState.slowReloads.push({ file: filePath, time: duration });
1453
+ if (profilerState.slowReloads.length > 10) {
1454
+ profilerState.slowReloads.shift();
1455
+ }
1456
+ }
1457
+ if (process.env.NODE_ENV === "development") {
1458
+ const emoji = duration < 50 ? "\u26A1" : duration < 100 ? "\u{1F504}" : "\u{1F40C}";
1459
+ console.log(`${emoji} Route reload: ${filePath} (${duration}ms)`);
1460
+ }
1461
+ }
1462
+ function withPerformanceTracking(fn, filePath) {
1463
+ console.log(`Tracking performance for: ${filePath}`);
1464
+ return async (...args) => {
1465
+ const startTime = Date.now();
1466
+ try {
1467
+ const result = await fn(...args);
1468
+ trackReloadPerformance(filePath, startTime);
1469
+ return result;
1470
+ } catch (error) {
1471
+ trackReloadPerformance(filePath, startTime);
1472
+ throw error;
1473
+ }
1474
+ };
1475
+ }
1476
+
1477
+ // src/router/discovery/watchers.ts
1478
+ import * as path5 from "node:path";
1240
1479
  import { watch } from "chokidar";
1241
1480
  function watchRoutes(routesDir, options = {}) {
1481
+ const debounceMs = options.debounceMs || 16;
1482
+ const debouncedCallbacks = /* @__PURE__ */ new Map();
1483
+ function createDebouncedCallback(fn, filePath) {
1484
+ return (...args) => {
1485
+ const existingTimeout = debouncedCallbacks.get(filePath);
1486
+ if (existingTimeout) {
1487
+ clearTimeout(existingTimeout);
1488
+ }
1489
+ const timeoutId = setTimeout(() => {
1490
+ fn(...args);
1491
+ debouncedCallbacks.delete(filePath);
1492
+ }, debounceMs);
1493
+ debouncedCallbacks.set(filePath, timeoutId);
1494
+ };
1495
+ }
1242
1496
  const routesByPath = /* @__PURE__ */ new Map();
1243
1497
  async function loadInitialRoutes() {
1244
1498
  try {
@@ -1254,28 +1508,34 @@ function watchRoutes(routesDir, options = {}) {
1254
1508
  }
1255
1509
  async function loadAndNotify(filePath) {
1256
1510
  try {
1257
- const routes = await loadRouteModule(filePath, routesDir);
1258
- if (!routes || routes.length === 0) {
1511
+ const existingRoutes = routesByPath.get(filePath);
1512
+ const newRoutes = await processChangedFile(filePath, routesDir, false);
1513
+ if (!newRoutes || newRoutes.length === 0) {
1259
1514
  return;
1260
1515
  }
1261
- const existingRoutes = routesByPath.get(filePath);
1516
+ if (existingRoutes && !hasRouteContentChanged(filePath, newRoutes)) {
1517
+ return;
1518
+ }
1519
+ await processChangedFile(filePath, routesDir, true);
1520
+ const normalizedPath = path5.normalize(filePath);
1262
1521
  if (existingRoutes) {
1263
- routesByPath.set(filePath, routes);
1522
+ routesByPath.set(filePath, newRoutes);
1264
1523
  if (options.onRouteChanged) {
1265
- options.onRouteChanged(routes);
1524
+ options.onRouteChanged(normalizedPath, newRoutes);
1266
1525
  }
1267
1526
  } else {
1268
- routesByPath.set(filePath, routes);
1527
+ routesByPath.set(filePath, newRoutes);
1269
1528
  if (options.onRouteAdded) {
1270
- options.onRouteAdded(routes);
1529
+ options.onRouteAdded(normalizedPath, newRoutes);
1271
1530
  }
1272
1531
  }
1273
1532
  } catch (error) {
1533
+ console.log(`\u26A0\uFE0F Error processing file ${filePath}:`, error);
1274
1534
  handleError(error);
1275
1535
  }
1276
1536
  }
1277
1537
  function handleRemoved(filePath) {
1278
- const normalizedPath = path4.normalize(filePath);
1538
+ const normalizedPath = path5.normalize(filePath);
1279
1539
  const routes = routesByPath.get(normalizedPath);
1280
1540
  if (routes && routes.length > 0 && options.onRouteRemoved) {
1281
1541
  options.onRouteRemoved(normalizedPath, routes);
@@ -1286,33 +1546,53 @@ function watchRoutes(routesDir, options = {}) {
1286
1546
  if (options.onError && error instanceof Error) {
1287
1547
  options.onError(error);
1288
1548
  } else {
1289
- console.error("Route watcher error:", error);
1549
+ console.error("\u26A0\uFE0F Route watcher error:", error);
1290
1550
  }
1291
1551
  }
1292
1552
  const watcher = watch(routesDir, {
1553
+ // Much faster response times
1554
+ awaitWriteFinish: {
1555
+ stabilityThreshold: 50,
1556
+ // Reduced from 300ms
1557
+ pollInterval: 10
1558
+ // Reduced from 100ms
1559
+ },
1560
+ // Performance optimizations
1561
+ usePolling: false,
1562
+ atomic: true,
1563
+ followSymlinks: false,
1564
+ depth: 10,
1565
+ // More aggressive ignoring
1293
1566
  ignored: [
1294
1567
  /(^|[/\\])\../,
1295
- // Ignore dot files
1296
1568
  /node_modules/,
1569
+ /\.git/,
1570
+ /\.DS_Store/,
1571
+ /Thumbs\.db/,
1572
+ /\.(test|spec)\.(ts|js)$/,
1573
+ /\.d\.ts$/,
1574
+ /\.map$/,
1575
+ /~$/,
1297
1576
  ...options.ignore || []
1298
- ],
1299
- persistent: true,
1300
- ignoreInitial: false,
1301
- awaitWriteFinish: {
1302
- stabilityThreshold: 300,
1303
- pollInterval: 100
1304
- }
1577
+ ]
1305
1578
  });
1306
- watcher.on("add", loadAndNotify).on("change", loadAndNotify).on("unlink", handleRemoved).on("error", handleError);
1579
+ watcher.on("add", (filePath) => {
1580
+ const debouncedLoad = createDebouncedCallback(loadAndNotify, filePath);
1581
+ debouncedLoad(filePath);
1582
+ }).on("change", (filePath) => {
1583
+ const debouncedLoad = createDebouncedCallback(loadAndNotify, filePath);
1584
+ debouncedLoad(filePath);
1585
+ }).on("unlink", (filePath) => {
1586
+ const debouncedRemove = createDebouncedCallback(handleRemoved, filePath);
1587
+ debouncedRemove(filePath);
1588
+ }).on("error", handleError);
1307
1589
  loadInitialRoutes().catch(handleError);
1308
1590
  return {
1309
- /**
1310
- * Close the watcher
1311
- */
1312
- close: () => watcher.close(),
1313
- /**
1314
- * Get all currently loaded routes (flattened)
1315
- */
1591
+ close: () => {
1592
+ debouncedCallbacks.forEach((timeout) => clearTimeout(timeout));
1593
+ debouncedCallbacks.clear();
1594
+ return watcher.close();
1595
+ },
1316
1596
  getRoutes: () => {
1317
1597
  const allRoutes = [];
1318
1598
  for (const routes of routesByPath.values()) {
@@ -1320,9 +1600,6 @@ function watchRoutes(routesDir, options = {}) {
1320
1600
  }
1321
1601
  return allRoutes;
1322
1602
  },
1323
- /**
1324
- * Get routes organized by file path
1325
- */
1326
1603
  getRoutesByFile: () => new Map(routesByPath)
1327
1604
  };
1328
1605
  }
@@ -1533,8 +1810,8 @@ async function executeHandler(ctx, routeOptions, params) {
1533
1810
  }
1534
1811
 
1535
1812
  // src/router/matching/params.ts
1536
- function extractParams(path5, pattern, paramNames) {
1537
- const match = pattern.exec(path5);
1813
+ function extractParams(path6, pattern, paramNames) {
1814
+ const match = pattern.exec(path6);
1538
1815
  if (!match) {
1539
1816
  return {};
1540
1817
  }
@@ -1544,15 +1821,15 @@ function extractParams(path5, pattern, paramNames) {
1544
1821
  }
1545
1822
  return params;
1546
1823
  }
1547
- function compilePathPattern(path5) {
1824
+ function compilePathPattern(path6) {
1548
1825
  const paramNames = [];
1549
- if (path5 === "/") {
1826
+ if (path6 === "/") {
1550
1827
  return {
1551
1828
  pattern: /^\/$/,
1552
1829
  paramNames: []
1553
1830
  };
1554
1831
  }
1555
- let patternString = path5.replace(/([.+*?^$(){}|\\])/g, "\\$1");
1832
+ let patternString = path6.replace(/([.+*?^$(){}|\\])/g, "\\$1");
1556
1833
  patternString = patternString.replace(/\/:([^/]+)/g, (_, paramName) => {
1557
1834
  paramNames.push(paramName);
1558
1835
  return "/([^/]+)";
@@ -1575,10 +1852,10 @@ function createMatcher() {
1575
1852
  /**
1576
1853
  * Add a route to the matcher
1577
1854
  */
1578
- add(path5, method, routeOptions) {
1579
- const { pattern, paramNames } = compilePathPattern(path5);
1855
+ add(path6, method, routeOptions) {
1856
+ const { pattern, paramNames } = compilePathPattern(path6);
1580
1857
  const newRoute = {
1581
- path: path5,
1858
+ path: path6,
1582
1859
  method,
1583
1860
  pattern,
1584
1861
  paramNames,
@@ -1591,17 +1868,33 @@ function createMatcher() {
1591
1868
  routes.splice(insertIndex, 0, newRoute);
1592
1869
  }
1593
1870
  },
1871
+ /**
1872
+ * Remove a route from the matcher by path
1873
+ */
1874
+ remove(path6) {
1875
+ for (let i = routes.length - 1; i >= 0; i--) {
1876
+ if (routes[i].path === path6) {
1877
+ routes.splice(i, 1);
1878
+ }
1879
+ }
1880
+ },
1881
+ /**
1882
+ * Clear all routes from the matcher
1883
+ */
1884
+ clear() {
1885
+ routes.length = 0;
1886
+ },
1594
1887
  /**
1595
1888
  * Match a URL path to a route
1596
1889
  */
1597
- match(path5, method) {
1598
- const pathname = path5.split("?")[0];
1890
+ match(path6, method) {
1891
+ const pathname = path6.split("?")[0];
1599
1892
  if (!pathname) return null;
1600
1893
  for (const route of routes) {
1601
1894
  if (route.method !== method) continue;
1602
1895
  const match = route.pattern.exec(pathname);
1603
1896
  if (match) {
1604
- const params = extractParams(path5, route.pattern, route.paramNames);
1897
+ const params = extractParams(path6, route.pattern, route.paramNames);
1605
1898
  return {
1606
1899
  route: route.routeOptions,
1607
1900
  params
@@ -1609,14 +1902,14 @@ function createMatcher() {
1609
1902
  }
1610
1903
  }
1611
1904
  const matchingPath = routes.find(
1612
- (route) => route.method !== method && route.pattern.test(path5)
1905
+ (route) => route.method !== method && route.pattern.test(path6)
1613
1906
  );
1614
1907
  if (matchingPath) {
1615
1908
  return {
1616
1909
  route: null,
1617
1910
  params: {},
1618
1911
  methodNotAllowed: true,
1619
- allowedMethods: routes.filter((route) => route.pattern.test(path5)).map((route) => route.method)
1912
+ allowedMethods: routes.filter((route) => route.pattern.test(path6)).map((route) => route.method)
1620
1913
  };
1621
1914
  }
1622
1915
  return null;
@@ -1633,16 +1926,93 @@ function createMatcher() {
1633
1926
  /**
1634
1927
  * Find routes matching a specific path
1635
1928
  */
1636
- findRoutes(path5) {
1637
- return routes.filter((route) => route.pattern.test(path5)).map((route) => ({
1929
+ findRoutes(path6) {
1930
+ return routes.filter((route) => route.pattern.test(path6)).map((route) => ({
1638
1931
  path: route.path,
1639
1932
  method: route.method,
1640
- params: extractParams(path5, route.pattern, route.paramNames)
1933
+ params: extractParams(path6, route.pattern, route.paramNames)
1641
1934
  }));
1642
1935
  }
1643
1936
  };
1644
1937
  }
1645
1938
 
1939
+ // src/router/registry/fast-registry.ts
1940
+ function createRouteRegistry() {
1941
+ return {
1942
+ routesByPath: /* @__PURE__ */ new Map(),
1943
+ routesByFile: /* @__PURE__ */ new Map(),
1944
+ pathToFile: /* @__PURE__ */ new Map()
1945
+ };
1946
+ }
1947
+ function updateRoutesFromFile(registry, filePath, newRoutes) {
1948
+ console.log(`Updating routes from file: ${filePath}`);
1949
+ const oldPaths = registry.routesByFile.get(filePath) || /* @__PURE__ */ new Set();
1950
+ const newPaths = new Set(newRoutes.map((r) => r.path));
1951
+ const added = newRoutes.filter((r) => !oldPaths.has(r.path));
1952
+ const removed = Array.from(oldPaths).filter((p) => !newPaths.has(p));
1953
+ const potentiallyChanged = newRoutes.filter((r) => oldPaths.has(r.path));
1954
+ const changed = potentiallyChanged.filter((route) => {
1955
+ const existingRoute = registry.routesByPath.get(route.path);
1956
+ return !existingRoute || !routesEqual(existingRoute, route);
1957
+ });
1958
+ applyRouteUpdates(registry, filePath, { added, removed, changed });
1959
+ return { added, removed, changed };
1960
+ }
1961
+ function getAllRoutesFromRegistry(registry) {
1962
+ return Array.from(registry.routesByPath.values());
1963
+ }
1964
+ function applyRouteUpdates(registry, filePath, updates) {
1965
+ const { added, removed, changed } = updates;
1966
+ removed.forEach((path6) => {
1967
+ registry.routesByPath.delete(path6);
1968
+ registry.pathToFile.delete(path6);
1969
+ });
1970
+ [...added, ...changed].forEach((route) => {
1971
+ registry.routesByPath.set(route.path, route);
1972
+ registry.pathToFile.set(route.path, filePath);
1973
+ });
1974
+ const allPathsForFile = /* @__PURE__ */ new Set([
1975
+ ...added.map((r) => r.path),
1976
+ ...changed.map((r) => r.path),
1977
+ ...Array.from(registry.routesByFile.get(filePath) || []).filter((p) => !removed.includes(p))
1978
+ ]);
1979
+ if (allPathsForFile.size > 0) {
1980
+ registry.routesByFile.set(filePath, allPathsForFile);
1981
+ } else {
1982
+ registry.routesByFile.delete(filePath);
1983
+ }
1984
+ }
1985
+ function routesEqual(route1, route2) {
1986
+ if (route1.path !== route2.path) return false;
1987
+ const methods1 = Object.keys(route1).filter((k) => k !== "path").sort();
1988
+ const methods2 = Object.keys(route2).filter((k) => k !== "path").sort();
1989
+ if (methods1.length !== methods2.length) return false;
1990
+ return methods1.every((method) => {
1991
+ const handler1 = route1[method];
1992
+ const handler2 = route2[method];
1993
+ return typeof handler1 === typeof handler2;
1994
+ });
1995
+ }
1996
+
1997
+ // src/router/utils/matching-helpers.ts
1998
+ function addRouteToMatcher(route, matcher) {
1999
+ Object.entries(route).forEach(([method, methodOptions]) => {
2000
+ if (method === "path" || !methodOptions) return;
2001
+ matcher.add(route.path, method, methodOptions);
2002
+ });
2003
+ }
2004
+ function removeRouteFromMatcher(path6, matcher) {
2005
+ if ("remove" in matcher && typeof matcher.remove === "function") {
2006
+ matcher.remove(path6);
2007
+ } else {
2008
+ console.warn("Matcher does not support selective removal, consider adding remove() method");
2009
+ }
2010
+ }
2011
+ function updateRouteInMatcher(route, matcher) {
2012
+ removeRouteFromMatcher(route.path, matcher);
2013
+ addRouteToMatcher(route, matcher);
2014
+ }
2015
+
1646
2016
  // src/router/router.ts
1647
2017
  var DEFAULT_ROUTER_OPTIONS = {
1648
2018
  routesDir: "./routes",
@@ -1657,46 +2027,55 @@ function createRouter(options) {
1657
2027
  if (options.basePath && !options.basePath.startsWith("/")) {
1658
2028
  console.warn("Base path does nothing");
1659
2029
  }
1660
- const routes = [];
2030
+ const registry = createRouteRegistry();
1661
2031
  const matcher = createMatcher();
1662
2032
  let initialized = false;
1663
2033
  let initializationPromise = null;
1664
2034
  let _watchers = null;
1665
- const routeSources = /* @__PURE__ */ new Map();
1666
2035
  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;
2036
+ function applyMatcherChanges(changes) {
2037
+ console.log("\n\u{1F527} APPLYING MATCHER CHANGES:");
2038
+ console.log(` Adding ${changes.added.length} routes`);
2039
+ console.log(` Removing ${changes.removed.length} routes`);
2040
+ console.log(` Updating ${changes.changed.length} routes`);
2041
+ changes.removed.forEach((routePath) => {
2042
+ console.log(` \u2796 Removing: ${routePath}`);
2043
+ removeRouteFromMatcher(routePath, matcher);
2044
+ });
2045
+ changes.added.forEach((route) => {
2046
+ const methods = Object.keys(route).filter((key) => key !== "path");
2047
+ console.log(` \u2795 Adding: ${route.path} [${methods.join(", ")}]`);
2048
+ addRouteToMatcher(route, matcher);
2049
+ });
2050
+ changes.changed.forEach((route) => {
2051
+ const methods = Object.keys(route).filter((key) => key !== "path");
2052
+ console.log(` \u{1F504} Updating: ${route.path} [${methods.join(", ")}]`);
2053
+ updateRouteInMatcher(route, matcher);
2054
+ });
2055
+ console.log("\u2705 Matcher changes applied\n");
2056
+ }
2057
+ function addRoutesWithSource(routes, source) {
2058
+ try {
2059
+ const changes = updateRoutesFromFile(registry, source, routes);
2060
+ applyMatcherChanges(changes);
2061
+ return changes;
2062
+ } catch (error) {
2063
+ console.error(`\u26A0\uFE0F Route conflicts from ${source}:`, error);
2064
+ throw error;
1679
2065
  }
1680
- routeSources.set(route.path, [...existingSources, source]);
1681
- addRouteInternal(route);
1682
2066
  }
1683
2067
  async function loadRoutesFromDirectory(directory, source, prefix) {
1684
2068
  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
- }
2069
+ const discoveredRoutes = await loadInitialRoutesParallel(directory);
2070
+ const finalRoutes = discoveredRoutes.map(
2071
+ (route) => prefix ? { ...route, path: `${prefix}${route.path}` } : route
2072
+ );
2073
+ const changes = addRoutesWithSource(finalRoutes, source);
1695
2074
  console.log(
1696
- `Loaded ${discoveredRoutes.length} routes from ${source}${prefix ? ` with prefix ${prefix}` : ""}`
2075
+ `Loaded ${discoveredRoutes.length} routes from ${source}${prefix ? ` with prefix ${prefix}` : ""} (${changes.added.length} added, ${changes.changed.length} changed, ${changes.removed.length} removed)`
1697
2076
  );
1698
2077
  } catch (error) {
1699
- console.error(`Failed to load routes from ${source}:`, error);
2078
+ console.error(`\u26A0\uFE0F Failed to load routes from ${source}:`, error);
1700
2079
  throw error;
1701
2080
  }
1702
2081
  }
@@ -1706,105 +2085,126 @@ function createRouter(options) {
1706
2085
  }
1707
2086
  initializationPromise = (async () => {
1708
2087
  try {
1709
- for (const directory of routeDirectories) {
1710
- await loadRoutesFromDirectory(directory, directory);
1711
- }
2088
+ await Promise.all(
2089
+ Array.from(routeDirectories).map(
2090
+ (directory) => loadRoutesFromDirectory(directory, directory)
2091
+ )
2092
+ );
1712
2093
  if (routerOptions.watchMode) {
1713
- setupWatcherForAllDirectories();
2094
+ setupOptimizedWatching();
1714
2095
  }
1715
2096
  initialized = true;
1716
2097
  } catch (error) {
1717
- console.error("Failed to initialize router:", error);
2098
+ console.error("\u26A0\uFE0F Failed to initialize router:", error);
1718
2099
  throw error;
1719
2100
  }
1720
2101
  })();
1721
2102
  return initializationPromise;
1722
2103
  }
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);
2104
+ function setupOptimizedWatching() {
2105
+ if (!_watchers) {
2106
+ _watchers = /* @__PURE__ */ new Map();
2107
+ }
2108
+ for (const directory of routeDirectories) {
2109
+ if (!_watchers.has(directory)) {
2110
+ const watcher = watchRoutes(directory, {
2111
+ debounceMs: 16,
2112
+ // ~60fps debouncing
2113
+ ignore: ["node_modules", ".git"],
2114
+ onRouteAdded: (filepath, addedRoutes) => {
2115
+ try {
2116
+ const changes = updateRoutesFromFile(registry, filepath, addedRoutes);
2117
+ applyMatcherChanges(changes);
2118
+ } catch (error) {
2119
+ console.error(`Error adding routes from ${directory}:`, error);
2120
+ }
2121
+ },
2122
+ onRouteChanged: withPerformanceTracking(
2123
+ async (filepath, changedRoutes) => {
2124
+ try {
2125
+ console.log(`Processing changes for ${filepath}`);
2126
+ const changes = updateRoutesFromFile(registry, filepath, changedRoutes);
2127
+ console.log(
2128
+ `Changes detected: ${changes.added.length} added, ${changes.changed.length} changed, ${changes.removed.length} removed`
2129
+ );
2130
+ applyMatcherChanges(changes);
2131
+ console.log(
2132
+ `Route changes applied: ${changes.added.length} added, ${changes.changed.length} changed, ${changes.removed.length} removed`
2133
+ );
2134
+ } catch (error) {
2135
+ console.error(`\u26A0\uFE0F Error updating routes from ${directory}:`, error);
2136
+ }
2137
+ },
2138
+ directory
2139
+ ),
2140
+ onRouteRemoved: (filePath, removedRoutes) => {
2141
+ console.log(`File removed: ${filePath} with ${removedRoutes.length} routes`);
2142
+ try {
2143
+ removedRoutes.forEach((route) => {
2144
+ removeRouteFromMatcher(route.path, matcher);
2145
+ });
2146
+ clearFileCache(filePath);
2147
+ } catch (error) {
2148
+ console.error(`\u26A0\uFE0F Error removing routes from ${filePath}:`, error);
1758
2149
  }
2150
+ },
2151
+ onError: (error) => {
2152
+ console.error(`\u26A0\uFE0F Route watcher error for ${directory}:`, error);
1759
2153
  }
1760
- const finalRoute = prefix ? { ...route, path: finalPath } : route;
1761
- addRouteWithSource(finalRoute, source);
1762
2154
  });
2155
+ _watchers.set(directory, watcher);
2156
+ }
2157
+ }
2158
+ }
2159
+ function setupWatcherForNewDirectory(directory, prefix) {
2160
+ if (!_watchers) {
2161
+ _watchers = /* @__PURE__ */ new Map();
2162
+ }
2163
+ const watcher = watchRoutes(directory, {
2164
+ debounceMs: 16,
2165
+ ignore: ["node_modules", ".git"],
2166
+ onRouteAdded: (filePath, addedRoutes) => {
2167
+ try {
2168
+ const finalRoutes = addedRoutes.map(
2169
+ (route) => prefix ? { ...route, path: `${prefix}${route.path}` } : route
2170
+ );
2171
+ const changes = updateRoutesFromFile(registry, filePath, finalRoutes);
2172
+ applyMatcherChanges(changes);
2173
+ } catch (error) {
2174
+ console.error(`\u26A0\uFE0F Error adding routes from ${directory}:`, error);
2175
+ }
1763
2176
  },
2177
+ onRouteChanged: withPerformanceTracking(async (filePath, changedRoutes) => {
2178
+ try {
2179
+ const finalRoutes = changedRoutes.map(
2180
+ (route) => prefix ? { ...route, path: `${prefix}${route.path}` } : route
2181
+ );
2182
+ const changes = updateRoutesFromFile(registry, filePath, finalRoutes);
2183
+ applyMatcherChanges(changes);
2184
+ } catch (error) {
2185
+ console.error(`\u26A0\uFE0F Error updating routes from ${directory}:`, error);
2186
+ }
2187
+ }, directory),
1764
2188
  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
- });
2189
+ try {
2190
+ removedRoutes.forEach((route) => {
2191
+ const finalPath = prefix ? `${prefix}${route.path}` : route.path;
2192
+ removeRouteFromMatcher(finalPath, matcher);
2193
+ });
2194
+ clearFileCache(filePath);
2195
+ } catch (error) {
2196
+ console.error(`Error removing routes from ${filePath}:`, error);
2197
+ }
1783
2198
  },
1784
2199
  onError: (error) => {
1785
- console.error(`Route watcher error for ${directory}:`, error);
2200
+ console.error(`\u26A0\uFE0F Route watcher error for ${directory}:`, error);
1786
2201
  }
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
2202
  });
1795
- if (!_watchers) {
1796
- _watchers = /* @__PURE__ */ new Map();
1797
- }
1798
2203
  _watchers.set(directory, watcher);
1799
2204
  return watcher;
1800
2205
  }
1801
- function setupWatcherForAllDirectories() {
1802
- for (const directory of routeDirectories) {
1803
- setupWatcherForDirectory(directory, directory);
1804
- }
1805
- }
1806
2206
  initialize().catch((error) => {
1807
- console.error("Failed to initialize router on creation:", error);
2207
+ console.error("\u26A0\uFE0F Failed to initialize router on creation:", error);
1808
2208
  });
1809
2209
  return {
1810
2210
  /**
@@ -1812,17 +2212,23 @@ function createRouter(options) {
1812
2212
  */
1813
2213
  async handleRequest(ctx) {
1814
2214
  if (!initialized) {
2215
+ console.log("\u{1F504} Router not initialized, initializing...");
1815
2216
  await initialize();
1816
2217
  }
1817
- const { method, path: path5 } = ctx.request;
1818
- const match = matcher.match(path5, method);
2218
+ const { method, path: path6 } = ctx.request;
2219
+ console.log(`
2220
+ \u{1F4E5} Handling request: ${method} ${path6}`);
2221
+ const match = matcher.match(path6, method);
1819
2222
  if (!match) {
2223
+ console.log(`\u274C No match found for: ${method} ${path6}`);
1820
2224
  ctx.response.status(404).json({ error: "Not Found" });
1821
2225
  return;
1822
2226
  }
2227
+ console.log(`\u2705 Route matched: ${method} ${path6}`);
2228
+ console.log(` Params: ${JSON.stringify(match.params)}`);
1823
2229
  if (match.methodNotAllowed) {
1824
2230
  ctx.response.status(405).json({
1825
- error: "Method Not Allowed",
2231
+ error: "\u274C Method Not Allowed",
1826
2232
  allowed: match.allowedMethods
1827
2233
  });
1828
2234
  if (match.allowedMethods && match.allowedMethods.length > 0) {
@@ -1841,19 +2247,28 @@ function createRouter(options) {
1841
2247
  }
1842
2248
  },
1843
2249
  /**
1844
- * Get all registered routes
2250
+ * Get all registered routes (using optimized registry)
1845
2251
  */
1846
2252
  getRoutes() {
1847
- return [...routes];
2253
+ return getAllRoutesFromRegistry(registry);
1848
2254
  },
1849
2255
  /**
1850
2256
  * Add a route programmatically
1851
2257
  */
1852
2258
  addRoute(route) {
1853
- addRouteInternal(route);
2259
+ const changes = updateRoutesFromFile(registry, "programmatic", [route]);
2260
+ applyMatcherChanges(changes);
1854
2261
  },
1855
2262
  /**
1856
- * Add a route directory (for plugins)
2263
+ * Add multiple routes programmatically with batch processing
2264
+ */
2265
+ addRoutes(routes) {
2266
+ const changes = updateRoutesFromFile(registry, "programmatic", routes);
2267
+ applyMatcherChanges(changes);
2268
+ return changes;
2269
+ },
2270
+ /**
2271
+ * Add a route directory (for plugins) with optimized loading
1857
2272
  */
1858
2273
  async addRouteDirectory(directory, options2 = {}) {
1859
2274
  if (routeDirectories.has(directory)) {
@@ -1864,21 +2279,27 @@ function createRouter(options) {
1864
2279
  if (initialized) {
1865
2280
  await loadRoutesFromDirectory(directory, directory, options2.prefix);
1866
2281
  if (routerOptions.watchMode) {
1867
- setupWatcherForDirectory(directory, directory, options2.prefix);
2282
+ setupWatcherForNewDirectory(directory, options2.prefix);
1868
2283
  }
1869
2284
  }
1870
2285
  },
1871
2286
  /**
1872
- * Get route conflicts
2287
+ * Get route conflicts (using registry)
1873
2288
  */
1874
2289
  getRouteConflicts() {
1875
2290
  const conflicts = [];
1876
- for (const [path5, sources] of routeSources.entries()) {
1877
- if (sources.length > 1) {
1878
- conflicts.push({ path: path5, sources });
2291
+ return conflicts;
2292
+ },
2293
+ /**
2294
+ * Close watchers and cleanup (useful for testing)
2295
+ */
2296
+ async close() {
2297
+ if (_watchers) {
2298
+ for (const watcher of _watchers.values()) {
2299
+ await watcher.close();
1879
2300
  }
2301
+ _watchers.clear();
1880
2302
  }
1881
- return conflicts;
1882
2303
  }
1883
2304
  };
1884
2305
  }