site-panzai/node_modules/nitropack/dist/runtime/internal/route-rules-utils.mjs

103 lines
2.8 KiB
JavaScript
Raw Permalink Normal View History

2026-11-13 13:57:04 +01:00
import { getQuery, joinURL, parseURL, withQuery, withoutBase } from "ufo";
const MAX_DECODE_PASSES = 8;
const PERCENT_ESCAPE_RE = /%([\da-f]{2})/gi;
const HAS_PERCENT_ESCAPE_RE = /%[\da-f]{2}/i;
export function canonicalizePath(pathname) {
let path = pathname;
for (let pass = 0; HAS_PERCENT_ESCAPE_RE.test(path); pass++) {
if (pass >= MAX_DECODE_PASSES) {
return void 0;
}
path = path.replace(
PERCENT_ESCAPE_RE,
(_, hex) => String.fromCharCode(Number.parseInt(hex, 16))
);
}
const segments = [];
for (const segment of path.replace(/\\/g, "/").split("/")) {
if (segment === "" || segment === ".") {
continue;
}
if (segment === "..") {
segments.pop();
continue;
}
segments.push(segment);
}
return "/" + segments.join("/");
}
function whatwgPathname(pathname) {
try {
return new URL(pathname, "http://_").pathname;
} catch {
return void 0;
}
}
const READINGS = [
(p) => canonicalizePath(p),
(p) => {
const whatwg = whatwgPathname(p);
return whatwg === void 0 ? void 0 : canonicalizePath(whatwg);
}
];
export function isPathInScope(pathname, base) {
if (!base || base === "/") {
return true;
}
for (const read of READINGS) {
const canonicalBase = read(base);
const canonical = read(pathname);
if (canonicalBase === void 0 || canonical === void 0) {
return false;
}
if (canonicalBase === "/") {
continue;
}
if (canonical !== canonicalBase && !canonical.startsWith(canonicalBase + "/")) {
return false;
}
}
return true;
}
export function isTargetInScope(target, targetBase) {
return isPathInScope(
parseURL(target).pathname || "/",
parseURL(targetBase).pathname || ""
);
}
const LEADING_SEPARATORS_RE = (
// eslint-disable-next-line no-control-regex
/^(?:[\u0000-\u0020/\\]|%(?:2f|5c|[01][\da-f]|20))+/i
);
export function normalizeRedirectPath(path) {
return path.replace(LEADING_SEPARATORS_RE, "/");
}
export function resolveRedirectTarget(path, to, stripBase) {
if (!to.endsWith("/**")) {
return path.includes("?") ? withQuery(to, getQuery(path)) : to;
}
let targetPath = path;
if (stripBase) {
if (!isPathInScope(path.split("?")[0], stripBase)) {
return void 0;
}
targetPath = withoutBase(targetPath, stripBase);
}
targetPath = normalizeRedirectPath(targetPath);
const targetBase = to.slice(0, -3);
if (targetBase && targetBase !== "/" && !isPathInScope(joinURL("/_", targetPath.split("?")[0]), "/_")) {
return void 0;
}
let target = joinURL(targetBase, targetPath);
if (!targetBase || targetBase === "/") {
target = normalizeRedirectPath(target);
if (!target.startsWith("/")) {
target = "/" + target;
}
}
if (!isTargetInScope(target, targetBase)) {
return void 0;
}
return target;
}