pr_01m47d15m3e54sn21z27rpy5n9/apps/web/app/lib/compute.ts
| 1 | /** |
| 2 | * How the site shows what the compute gate says (`@g1t/contracts` |
| 3 | * compute.ts): its messages name the page to act on, such as |
| 4 | * `/acme/-/billing`, which is shown as a link. Pure, so it can be tested. |
| 5 | */ |
| 6 | |
| 7 | /** A message in pieces: plain text, and g1t paths to link. */ |
| 8 | export type Piece = { text: string; href?: string }; |
| 9 | |
| 10 | /** A workspace page path, such as `/acme/-/billing` or `/acme/-/settings`. */ |
| 11 | const PATH = /(?<![\w/])(\/[a-z0-9][a-z0-9-]*\/-\/[a-z0-9][a-z0-9/#-]*)/gi; |
| 12 | |
| 13 | /** Splits a message so the g1t pages it names can be links. */ |
| 14 | export function linkPaths(message: string): Piece[] { |
| 15 | const pieces: Piece[] = []; |
| 16 | let at = 0; |
| 17 | for (const match of message.matchAll(PATH)) { |
| 18 | // A sentence's full stop is not part of the path. |
| 19 | const path = match[1].replace(/[.#/-]+$/, ""); |
| 20 | const start = match.index ?? 0; |
| 21 | if (start > at) pieces.push({ text: message.slice(at, start) }); |
| 22 | pieces.push({ text: path, href: path }); |
| 23 | at = start + path.length; |
| 24 | } |
| 25 | if (at < message.length) pieces.push({ text: message.slice(at) }); |
| 26 | return pieces; |
| 27 | } |
| 28 | |
| 29 | /** How a run waiting for a free agent slot says so (`WAITING_PREFIX`). */ |
| 30 | export function isWaitingMessage(message: string): boolean { |
| 31 | return message.startsWith("Waiting for a free slot"); |
| 32 | } |