File size: 1,392 Bytes
fc93158
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import { canonicalizePathVariant } from "../gateway/security-path.js";
import type { OpenClawPluginHttpRouteMatch } from "./types.js";

type PluginHttpRouteLike = {
  path: string;
  match: OpenClawPluginHttpRouteMatch;
};

function prefixMatchPath(pathname: string, prefix: string): boolean {
  return (
    pathname === prefix || pathname.startsWith(`${prefix}/`) || pathname.startsWith(`${prefix}%`)
  );
}

export function doPluginHttpRoutesOverlap(
  a: Pick<PluginHttpRouteLike, "path" | "match">,
  b: Pick<PluginHttpRouteLike, "path" | "match">,
): boolean {
  const aPath = canonicalizePathVariant(a.path);
  const bPath = canonicalizePathVariant(b.path);

  if (a.match === "exact" && b.match === "exact") {
    return aPath === bPath;
  }
  if (a.match === "prefix" && b.match === "prefix") {
    return prefixMatchPath(aPath, bPath) || prefixMatchPath(bPath, aPath);
  }

  const prefixRoute = a.match === "prefix" ? a : b;
  const exactRoute = a.match === "exact" ? a : b;
  return prefixMatchPath(
    canonicalizePathVariant(exactRoute.path),
    canonicalizePathVariant(prefixRoute.path),
  );
}

export function findOverlappingPluginHttpRoute<
  T extends {
    path: string;
    match: OpenClawPluginHttpRouteMatch;
  },
>(routes: readonly T[], candidate: PluginHttpRouteLike): T | undefined {
  return routes.find((route) => doPluginHttpRoutesOverlap(route, candidate));
}