@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.cjs CHANGED
@@ -499,6 +499,9 @@ var defaultSend = (res, data) => {
499
499
  res.json(data);
500
500
  };
501
501
  var REGISTERED_ROUTES_SYMBOL = /* @__PURE__ */ Symbol.for("routesV3.registeredRoutes");
502
+ var REGISTERED_ROUTE_ENTRIES_SYMBOL = /* @__PURE__ */ Symbol.for(
503
+ "routesV3.registeredRouteEntries"
504
+ );
502
505
  function getRegisteredRouteStore(router) {
503
506
  const existing = router[REGISTERED_ROUTES_SYMBOL];
504
507
  if (existing) return existing;
@@ -506,6 +509,88 @@ function getRegisteredRouteStore(router) {
506
509
  router[REGISTERED_ROUTES_SYMBOL] = store;
507
510
  return store;
508
511
  }
512
+ function parseRegisteredRouteKey(key) {
513
+ const firstSpace = key.indexOf(" ");
514
+ if (firstSpace <= 0) return void 0;
515
+ const method = key.slice(0, firstSpace).toUpperCase();
516
+ const path = key.slice(firstSpace + 1);
517
+ if (!path) return void 0;
518
+ return { method, path, key, origin: "unknown (pre-existing router stack)" };
519
+ }
520
+ function getRegistrationOrigin() {
521
+ const stack = new Error().stack;
522
+ if (!stack) return "unknown";
523
+ const lines = stack.split("\n").map((line) => line.trim()).filter(Boolean);
524
+ for (const line of lines) {
525
+ if (/^Error:?$/.test(line)) continue;
526
+ 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/")) {
527
+ continue;
528
+ }
529
+ return line.replace(/^at\s+/, "");
530
+ }
531
+ return lines.find((line) => !/^Error:?$/.test(line))?.replace(/^at\s+/, "") ?? "unknown";
532
+ }
533
+ function getRegisteredRouteEntriesStore(router) {
534
+ const existing = router[REGISTERED_ROUTE_ENTRIES_SYMBOL];
535
+ if (existing) return existing;
536
+ const seeded = collectRoutesFromStack(router).map(parseRegisteredRouteKey).filter((entry) => Boolean(entry));
537
+ router[REGISTERED_ROUTE_ENTRIES_SYMBOL] = seeded;
538
+ return seeded;
539
+ }
540
+ function toParsedRouteSegments(path) {
541
+ const segments = path.split("/").filter(Boolean);
542
+ return segments.map((segment) => {
543
+ if (segment.startsWith("*") && segment.length > 1) return { kind: "wildcard" };
544
+ if (segment.startsWith(":") && segment.length > 1) return { kind: "param" };
545
+ return { kind: "static", value: segment };
546
+ });
547
+ }
548
+ function buildProbePathForRoute(path) {
549
+ const parsed = toParsedRouteSegments(path);
550
+ const segments = [];
551
+ for (const segment of parsed) {
552
+ if (segment.kind === "static") {
553
+ segments.push(segment.value);
554
+ continue;
555
+ }
556
+ if (segment.kind === "param") {
557
+ segments.push("__rrroutes_param__");
558
+ continue;
559
+ }
560
+ segments.push("__rrroutes_splat__", "__rrroutes_tail__");
561
+ }
562
+ return `/${segments.join("/")}`.replace(/\/+/g, "/");
563
+ }
564
+ function routePatternMatchesConcretePath(routePattern, concretePath) {
565
+ const patternSegments = toParsedRouteSegments(routePattern);
566
+ const pathSegments = concretePath.split("/").filter(Boolean);
567
+ let i = 0;
568
+ let j = 0;
569
+ while (i < patternSegments.length) {
570
+ const pattern = patternSegments[i];
571
+ if (pattern.kind === "wildcard") {
572
+ return j < pathSegments.length;
573
+ }
574
+ if (j >= pathSegments.length) return false;
575
+ if (pattern.kind === "param") {
576
+ i += 1;
577
+ j += 1;
578
+ continue;
579
+ }
580
+ if (pattern.value !== pathSegments[j]) return false;
581
+ i += 1;
582
+ j += 1;
583
+ }
584
+ return j === pathSegments.length;
585
+ }
586
+ function findShadowingPriorRoute(entries, method, path) {
587
+ const probePath = buildProbePathForRoute(path);
588
+ return entries.find((entry) => {
589
+ if (entry.method !== method) return false;
590
+ if (entry.path === path) return true;
591
+ return routePatternMatchesConcretePath(entry.path, probePath);
592
+ });
593
+ }
509
594
  function collectRoutesFromStack(appOrRouter) {
510
595
  const result = [];
511
596
  const stack = appOrRouter.stack ?? (appOrRouter._router ? appOrRouter._router.stack : void 0) ?? [];
@@ -542,6 +627,7 @@ function createRRRoute(router, config) {
542
627
  const preCtxMws = [...middlewareConfig.preCtx ?? []];
543
628
  const sanitizerMw = middlewareConfig.sanitizer === void 0 ? void 0 : typeof middlewareConfig.sanitizer === "function" ? middlewareConfig.sanitizer : createRequestSanitizationMiddleware(middlewareConfig.sanitizer);
544
629
  const registered = getRegisteredRouteStore(router);
630
+ const registeredEntries = getRegisteredRouteEntriesStore(router);
545
631
  const getMulterOptions = (fields) => {
546
632
  if (!fields || fields.length === 0) return void 0;
547
633
  const resolved = typeof config.multerOptions === "function" ? config.multerOptions(fields) : config.multerOptions;
@@ -584,6 +670,13 @@ function createRRRoute(router, config) {
584
670
  const activeDebugMode = routeDebugEmitter?.mode ?? defaultDebugMode;
585
671
  const emit = (event) => activeEmit(event, debugName);
586
672
  const isVerboseDebug = activeDebugMode === "complete";
673
+ const origin = getRegistrationOrigin();
674
+ const shadowingEntry = findShadowingPriorRoute(registeredEntries, methodUpper, path);
675
+ if (shadowingEntry) {
676
+ console.warn(
677
+ `[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}.`
678
+ );
679
+ }
587
680
  emit({ type: "register", method: methodUpper, path });
588
681
  const routeSpecific = (def?.before ?? []).map(
589
682
  (mw) => adaptRouteBeforeMw(mw)
@@ -832,6 +925,7 @@ function createRRRoute(router, config) {
832
925
  };
833
926
  router[method](path, ...before, wrapped);
834
927
  registered.add(key);
928
+ registeredEntries.push({ method: methodUpper, path, key, origin });
835
929
  registeredDefs.set(key, {
836
930
  leaf,
837
931
  def