Files

94 lines
2.9 KiB
TypeScript

export type DeeplinkTarget = {
landing: string;
prefix: string;
};
function page(name: string): DeeplinkTarget {
return { landing: name, prefix: name };
}
export const ENV_PAGE_TARGETS: ReadonlyMap<string, DeeplinkTarget> = new Map([
["agents", page("agents")],
["alerts", page("alerts")],
["apikeys", page("apikeys")],
["batches", page("batches")],
["branches", page("branches")],
["bulk-actions", page("bulk-actions")],
["concurrency", page("concurrency")],
["dashboards", page("dashboards")],
["deployments", page("deployments")],
["dev-branches", page("dev-branches")],
["environment-variables", page("environment-variables")],
["errors", page("errors")],
["limits", page("limits")],
["logs", page("logs")],
["models", page("models")],
["playground", page("playground")],
["prompts", page("prompts")],
["query", page("query")],
["queues", page("queues")],
["regions", page("regions")],
["runs", page("runs")],
["schedules", page("schedules")],
["sessions", page("sessions")],
["settings", page("settings")],
["tasks", { landing: "", prefix: "tasks" }],
["test", page("test")],
["waitpoints", { landing: "waitpoints/tokens", prefix: "waitpoints/tokens" }],
["webhooks", page("webhooks")],
]);
export const ORG_PAGE_TARGETS: ReadonlyMap<string, DeeplinkTarget> = new Map([
["projects", page("projects")],
]);
export const DEEPLINK_PATH_PREFIX = "/_";
export function deeplinkSuffix(pathname: string): string {
const withSlash = `${DEEPLINK_PATH_PREFIX}/`;
if (!pathname.startsWith(withSlash)) return "";
return pathname.slice(withSlash.length);
}
//`.` and `..`, plain or escaped as `%2e%2e`, would climb out of the environment path.
function isSafeSegment(segment: string): boolean {
if (segment.length === 0 || segment === "." || segment === "..") return false;
let decoded: string;
try {
decoded = decodeURIComponent(segment);
} catch {
return false;
}
return decoded !== "." && decoded !== "..";
}
function resolveDeeplinkTarget(
targets: ReadonlyMap<string, DeeplinkTarget>,
suffix: string
): string | undefined {
const segments = suffix.split("/").filter(isSafeSegment);
const [first = "", ...rest] = segments;
const target = targets.get(first.toLowerCase());
if (target === undefined) return undefined;
if (rest.length === 0) return target.landing;
const prefixDepth = target.prefix.split("/").length;
const writesPrefix = segments.slice(0, prefixDepth).join("/").toLowerCase() === target.prefix;
const beyondPrefix = writesPrefix ? segments.slice(prefixDepth) : rest;
return [target.prefix, ...beyondPrefix].join("/");
}
export function resolveDeeplinkPage(suffix: string): string | undefined {
return resolveDeeplinkTarget(ENV_PAGE_TARGETS, suffix);
}
export function resolveOrganizationDeeplinkPage(suffix: string): string | undefined {
return resolveDeeplinkTarget(ORG_PAGE_TARGETS, suffix);
}