OXYGENHUB
# b55e0d00545e oxygen-vscode / src / handoff.ts
2.7 KiB · last changed Apply swimlane plans from OxygenHub (0.12.0) 1 month ago source blame history raw
f532c426 Andy McCall ↶ 1
// "Resolve in VS Code" (OxygenHub's conflict resolver hands a conflict to
2
// the extension): vscode://oxygen.oxygen-vscode/resolve?url=…&bookmark=…&path=…
3
// Parsing and matching live here, apart from VS Code, so they are tested.
4
5
export interface ResolveRequest {
6
  /** The repository's clone URL on the hub. */
7
  url: string;
8
  bookmark: string;
9
  /** Relative to the repository root. */
10
  path: string;
11
}
12
13
/** Read a resolve request's query string, or say what is wrong with it. */
14
export function parseResolveQuery(query: string): ResolveRequest | string {
15
  const q = new URLSearchParams(query);
16
  const url = (q.get("url") ?? "").trim();
17
  const bookmark = (q.get("bookmark") ?? "").trim();
18
  const path = (q.get("path") ?? "").trim().replace(/^\/+/, "");
19
  if (!/^https?:\/\/[^\s]+$/.test(url)) {
20
    return "the link names no repository URL";
21
  }
22
  if (!bookmark || /[\s~^:?*[\\]/.test(bookmark) || bookmark.includes("..")) {
23
    return "the link names no valid bookmark";
24
  }
25
  if (!path || path.split("/").some((c) => c === "" || c === "." || c === ".." || c === ".oxy")) {
26
    return "the link names no valid path";
27
  }
28
  return { url, bookmark, path };
29
}
30
31
/** Whether two remote URLs name the same repository (case of the host and a trailing slash aside). */
32
export function sameRemote(a: string, b: string): boolean {
33
  const norm = (u: string) => {
34
    try {
35
      const x = new URL(u.trim());
36
      return `${x.protocol}//${x.host.toLowerCase()}${x.pathname.replace(/\/+$/, "").replace(/\.oxy$/, "")}`;
37
    } catch {
38
      return u.trim().replace(/\/+$/, "");
39
    }
40
  };
41
  return norm(a) === norm(b);
42
}
19639205 Andy McCall ↶ 43
44
export interface PlanRequest {
45
  url: string;
46
  bookmark: string;
47
  /** `squash` (change into target) or `rebase` (change onto target). */
48
  kind: "squash" | "rebase";
49
  change: string;
50
  target: string;
51
}
52
53
/**
54
 * A rewrite planned on OxygenHub's swimlanes:
55
 * vscode://oxygen.oxygen-vscode/plan?url=…&bookmark=…&kind=…&change=…&target=…
56
 */
57
export function parsePlanQuery(query: string): PlanRequest | string {
58
  const q = new URLSearchParams(query);
59
  const base = parseResolveQuery(new URLSearchParams({ url: q.get("url") ?? "", bookmark: q.get("bookmark") ?? "", path: "x" }).toString());
60
  if (typeof base === "string") {
61
    return base;
62
  }
63
  const kind = q.get("kind");
64
  if (kind !== "squash" && kind !== "rebase") {
65
    return "the link names no squash or rebase";
66
  }
67
  const hex = /^[0-9a-f]{64}$/;
68
  const change = q.get("change") ?? "";
69
  const target = q.get("target") ?? "";
70
  if (!hex.test(change) || !hex.test(target) || change === target) {
71
    return "the link names no two changes";
72
  }
73
  return { url: base.url, bookmark: base.bookmark, kind, change, target };
74
}