@emeryld/rrroutes-server 2.6.4 → 2.6.6

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
@@ -456,6 +456,9 @@ var defaultSend = (res, data) => {
456
456
  res.json(data);
457
457
  };
458
458
  var REGISTERED_ROUTES_SYMBOL = /* @__PURE__ */ Symbol.for("routesV3.registeredRoutes");
459
+ var REGISTERED_ROUTE_ENTRIES_SYMBOL = /* @__PURE__ */ Symbol.for(
460
+ "routesV3.registeredRouteEntries"
461
+ );
459
462
  function getRegisteredRouteStore(router) {
460
463
  const existing = router[REGISTERED_ROUTES_SYMBOL];
461
464
  if (existing) return existing;
@@ -463,6 +466,88 @@ function getRegisteredRouteStore(router) {
463
466
  router[REGISTERED_ROUTES_SYMBOL] = store;
464
467
  return store;
465
468
  }
469
+ function parseRegisteredRouteKey(key) {
470
+ const firstSpace = key.indexOf(" ");
471
+ if (firstSpace <= 0) return void 0;
472
+ const method = key.slice(0, firstSpace).toUpperCase();
473
+ const path = key.slice(firstSpace + 1);
474
+ if (!path) return void 0;
475
+ return { method, path, key, origin: "unknown (pre-existing router stack)" };
476
+ }
477
+ function getRegistrationOrigin() {
478
+ const stack = new Error().stack;
479
+ if (!stack) return "unknown";
480
+ const lines = stack.split("\n").map((line) => line.trim()).filter(Boolean);
481
+ for (const line of lines) {
482
+ if (/^Error:?$/.test(line)) continue;
483
+ if (line.includes("getRegistrationOrigin") || line.includes("findShadowingPriorRoute") || line.includes("createRRRoute") || line.includes("register (") || line.includes("routesV3.server.ts") || line.includes("<anonymous>") || line.includes("node_modules") || line.includes("internal/")) {
484
+ continue;
485
+ }
486
+ return line.replace(/^at\s+/, "");
487
+ }
488
+ return lines.find((line) => !/^Error:?$/.test(line))?.replace(/^at\s+/, "") ?? "unknown";
489
+ }
490
+ function getRegisteredRouteEntriesStore(router) {
491
+ const existing = router[REGISTERED_ROUTE_ENTRIES_SYMBOL];
492
+ if (existing) return existing;
493
+ const seeded = collectRoutesFromStack(router).map(parseRegisteredRouteKey).filter((entry) => Boolean(entry));
494
+ router[REGISTERED_ROUTE_ENTRIES_SYMBOL] = seeded;
495
+ return seeded;
496
+ }
497
+ function toParsedRouteSegments(path) {
498
+ const segments = path.split("/").filter(Boolean);
499
+ return segments.map((segment) => {
500
+ if (segment.startsWith("*") && segment.length > 1) return { kind: "wildcard" };
501
+ if (segment.startsWith(":") && segment.length > 1) return { kind: "param" };
502
+ return { kind: "static", value: segment };
503
+ });
504
+ }
505
+ function buildProbePathForRoute(path) {
506
+ const parsed = toParsedRouteSegments(path);
507
+ const segments = [];
508
+ for (const segment of parsed) {
509
+ if (segment.kind === "static") {
510
+ segments.push(segment.value);
511
+ continue;
512
+ }
513
+ if (segment.kind === "param") {
514
+ segments.push("__rrroutes_param__");
515
+ continue;
516
+ }
517
+ segments.push("__rrroutes_splat__", "__rrroutes_tail__");
518
+ }
519
+ return `/${segments.join("/")}`.replace(/\/+/g, "/");
520
+ }
521
+ function routePatternMatchesConcretePath(routePattern, concretePath) {
522
+ const patternSegments = toParsedRouteSegments(routePattern);
523
+ const pathSegments = concretePath.split("/").filter(Boolean);
524
+ let i = 0;
525
+ let j = 0;
526
+ while (i < patternSegments.length) {
527
+ const pattern = patternSegments[i];
528
+ if (pattern.kind === "wildcard") {
529
+ return j < pathSegments.length;
530
+ }
531
+ if (j >= pathSegments.length) return false;
532
+ if (pattern.kind === "param") {
533
+ i += 1;
534
+ j += 1;
535
+ continue;
536
+ }
537
+ if (pattern.value !== pathSegments[j]) return false;
538
+ i += 1;
539
+ j += 1;
540
+ }
541
+ return j === pathSegments.length;
542
+ }
543
+ function findShadowingPriorRoute(entries, method, path) {
544
+ const probePath = buildProbePathForRoute(path);
545
+ return entries.find((entry) => {
546
+ if (entry.method !== method) return false;
547
+ if (entry.path === path) return true;
548
+ return routePatternMatchesConcretePath(entry.path, probePath);
549
+ });
550
+ }
466
551
  function collectRoutesFromStack(appOrRouter) {
467
552
  const result = [];
468
553
  const stack = appOrRouter.stack ?? (appOrRouter._router ? appOrRouter._router.stack : void 0) ?? [];
@@ -499,6 +584,7 @@ function createRRRoute(router, config) {
499
584
  const preCtxMws = [...middlewareConfig.preCtx ?? []];
500
585
  const sanitizerMw = middlewareConfig.sanitizer === void 0 ? void 0 : typeof middlewareConfig.sanitizer === "function" ? middlewareConfig.sanitizer : createRequestSanitizationMiddleware(middlewareConfig.sanitizer);
501
586
  const registered = getRegisteredRouteStore(router);
587
+ const registeredEntries = getRegisteredRouteEntriesStore(router);
502
588
  const getMulterOptions = (fields) => {
503
589
  if (!fields || fields.length === 0) return void 0;
504
590
  const resolved = typeof config.multerOptions === "function" ? config.multerOptions(fields) : config.multerOptions;
@@ -541,6 +627,13 @@ function createRRRoute(router, config) {
541
627
  const activeDebugMode = routeDebugEmitter?.mode ?? defaultDebugMode;
542
628
  const emit = (event) => activeEmit(event, debugName);
543
629
  const isVerboseDebug = activeDebugMode === "complete";
630
+ const origin = getRegistrationOrigin();
631
+ const shadowingEntry = findShadowingPriorRoute(registeredEntries, methodUpper, path);
632
+ if (shadowingEntry) {
633
+ console.warn(
634
+ `[rrroutes-server] Registering ${methodUpper} ${path} may be shadowed by previously registered ${shadowingEntry.method} ${shadowingEntry.path}. Precedence: ${shadowingEntry.method} ${shadowingEntry.path} takes precedence (registered first). Current declaration origin: ${origin}. Prior declaration origin: ${shadowingEntry.origin}.`
635
+ );
636
+ }
544
637
  emit({ type: "register", method: methodUpper, path });
545
638
  const routeSpecific = (def?.before ?? []).map(
546
639
  (mw) => adaptRouteBeforeMw(mw)
@@ -789,6 +882,7 @@ function createRRRoute(router, config) {
789
882
  };
790
883
  router[method](path, ...before, wrapped);
791
884
  registered.add(key);
885
+ registeredEntries.push({ method: methodUpper, path, key, origin });
792
886
  registeredDefs.set(key, {
793
887
  leaf,
794
888
  def