@emeryld/rrroutes-server 2.6.5 → 2.6.7
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 +112 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +112 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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,101 @@ 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
|
+
}
|
|
594
|
+
function formatRouteShadowWarning(args) {
|
|
595
|
+
const { method, path, currentOrigin, prior } = args;
|
|
596
|
+
const newRoute = `${method} ${path}`;
|
|
597
|
+
const priorRoute = `${prior.method} ${prior.path}`;
|
|
598
|
+
return [
|
|
599
|
+
"[rrroutes-server][route-shadow-warning]",
|
|
600
|
+
` New route: ${newRoute}`,
|
|
601
|
+
` Existing route: ${priorRoute}`,
|
|
602
|
+
` Precedence: ${priorRoute} takes precedence (registered first)`,
|
|
603
|
+
` New origin: ${currentOrigin}`,
|
|
604
|
+
` Existing origin: ${prior.origin}`
|
|
605
|
+
].join("\n");
|
|
606
|
+
}
|
|
509
607
|
function collectRoutesFromStack(appOrRouter) {
|
|
510
608
|
const result = [];
|
|
511
609
|
const stack = appOrRouter.stack ?? (appOrRouter._router ? appOrRouter._router.stack : void 0) ?? [];
|
|
@@ -542,6 +640,7 @@ function createRRRoute(router, config) {
|
|
|
542
640
|
const preCtxMws = [...middlewareConfig.preCtx ?? []];
|
|
543
641
|
const sanitizerMw = middlewareConfig.sanitizer === void 0 ? void 0 : typeof middlewareConfig.sanitizer === "function" ? middlewareConfig.sanitizer : createRequestSanitizationMiddleware(middlewareConfig.sanitizer);
|
|
544
642
|
const registered = getRegisteredRouteStore(router);
|
|
643
|
+
const registeredEntries = getRegisteredRouteEntriesStore(router);
|
|
545
644
|
const getMulterOptions = (fields) => {
|
|
546
645
|
if (!fields || fields.length === 0) return void 0;
|
|
547
646
|
const resolved = typeof config.multerOptions === "function" ? config.multerOptions(fields) : config.multerOptions;
|
|
@@ -584,6 +683,18 @@ function createRRRoute(router, config) {
|
|
|
584
683
|
const activeDebugMode = routeDebugEmitter?.mode ?? defaultDebugMode;
|
|
585
684
|
const emit = (event) => activeEmit(event, debugName);
|
|
586
685
|
const isVerboseDebug = activeDebugMode === "complete";
|
|
686
|
+
const origin = getRegistrationOrigin();
|
|
687
|
+
const shadowingEntry = findShadowingPriorRoute(registeredEntries, methodUpper, path);
|
|
688
|
+
if (shadowingEntry) {
|
|
689
|
+
console.warn(
|
|
690
|
+
formatRouteShadowWarning({
|
|
691
|
+
method: methodUpper,
|
|
692
|
+
path,
|
|
693
|
+
currentOrigin: origin,
|
|
694
|
+
prior: shadowingEntry
|
|
695
|
+
})
|
|
696
|
+
);
|
|
697
|
+
}
|
|
587
698
|
emit({ type: "register", method: methodUpper, path });
|
|
588
699
|
const routeSpecific = (def?.before ?? []).map(
|
|
589
700
|
(mw) => adaptRouteBeforeMw(mw)
|
|
@@ -832,6 +943,7 @@ function createRRRoute(router, config) {
|
|
|
832
943
|
};
|
|
833
944
|
router[method](path, ...before, wrapped);
|
|
834
945
|
registered.add(key);
|
|
946
|
+
registeredEntries.push({ method: methodUpper, path, key, origin });
|
|
835
947
|
registeredDefs.set(key, {
|
|
836
948
|
leaf,
|
|
837
949
|
def
|