@smwb/onreel-git-mcp 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -231,6 +231,15 @@ var GITHUB_PRIORITY_LABELS = {
231
231
  "priority: blocker": "blocker"
232
232
  };
233
233
  var ALL_STATUSES = ["open", "in_progress", "done", "declined"];
234
+ var GITVERSE_ISSUE_STATES = {
235
+ open: "open",
236
+ closed: "done"
237
+ };
238
+ var GITVERSE_ISSUE_STATE_NAMES = {
239
+ open: "open",
240
+ done: "closed"
241
+ };
242
+ var GITVERSE_DONE_COLUMN = /^(?:done|closed|complete|completed|resolved|готово|выполнено|завершено|закрыто|сделано)$/i;
234
243
  var GITLAB_MERGE_REQUEST_STATES = {
235
244
  opened: "open",
236
245
  locked: "open",
@@ -239,6 +248,10 @@ var GITLAB_MERGE_REQUEST_STATES = {
239
248
  };
240
249
  var GITLAB_DRAFT_PREFIX = "Draft: ";
241
250
  var GITLAB_DRAFT_PATTERN = /^\s*(?:\[draft\]|\(draft\)|draft:)\s*/i;
251
+ var GITVERSE_PULL_STATES = {
252
+ open: "open",
253
+ closed: "closed"
254
+ };
242
255
  var SOURCECRAFT_PULL_STATUSES = {
243
256
  draft: "draft",
244
257
  open: "open",
@@ -324,10 +337,35 @@ var SOURCECRAFT_RUN_EVENTS = {
324
337
  schedule: "schedule",
325
338
  repository_event: "trigger"
326
339
  };
340
+ var GITVERSE_CI_STATUSES = {
341
+ queued: "queued",
342
+ pending: "queued",
343
+ waiting: "queued",
344
+ blocked: "waiting",
345
+ running: "running",
346
+ in_progress: "running",
347
+ success: "success",
348
+ failure: "failed",
349
+ failed: "failed",
350
+ timeout: "failed",
351
+ cancelled: "canceled",
352
+ canceled: "canceled",
353
+ skipped: "skipped"
354
+ };
355
+ var GITVERSE_RUN_EVENTS = {
356
+ push: "push",
357
+ pull_request: "pull_request",
358
+ pull_request_target: "pull_request",
359
+ workflow_dispatch: "manual",
360
+ schedule: "schedule",
361
+ repository_dispatch: "trigger",
362
+ workflow_run: "trigger",
363
+ workflow_call: "trigger"
364
+ };
327
365
 
328
366
  // src/tracker/http.lib.ts
329
367
  async function requestJson(url, request) {
330
- const accept = { accept: "application/json" };
368
+ const accept = { accept: request.accept ?? "application/json" };
331
369
  const { response, url: final } = await send(url, request, AbortSignal.timeout(TRACKER_TIMEOUT_MS), accept, {
332
370
  method: request.method ?? "GET",
333
371
  headers: { ...accept, ...request.body === void 0 ? {} : { "content-type": "application/json" }, ...request.headers },
@@ -536,6 +574,9 @@ function decodeUtf8(bytes) {
536
574
  function notAnswered(url, error) {
537
575
  return new TrackerError("failed", redactSecretText(`\u0422\u0440\u0435\u043A\u0435\u0440 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B (${hostOf(url)}): ${error instanceof Error ? error.message : String(error)}`));
538
576
  }
577
+ var RATE_LIMIT_REMAINING_HEADERS = ["x-ratelimit-remaining", "gitverse-ratelimit-remaining", "gitverse-ratelimit-user-remaining"];
578
+ var RETRY_AFTER_HEADERS = ["retry-after", "gitverse-ratelimit-retry-after"];
579
+ var RATE_LIMIT_RESET_HEADERS = ["x-ratelimit-reset", "ratelimit-reset", "gitverse-ratelimit-reset"];
539
580
  function failureOf(url, response, raw, request) {
540
581
  const status = response.status;
541
582
  if (status === 404 && request.missing !== void 0) return new TrackerError(request.missing.failure, request.missing.message);
@@ -543,7 +584,8 @@ function failureOf(url, response, raw, request) {
543
584
  const detail = redactSecretText(raw.replace(/\s+/g, " ").trim()).slice(0, TRACKER_ERROR_BODY_MAX);
544
585
  const answered = `\u0422\u0440\u0435\u043A\u0435\u0440 ${hostOf(url)} \u043E\u0442\u0432\u0435\u0442\u0438\u043B ${String(status)}${detail === "" ? "" : `: ${detail}`}.`;
545
586
  if (request.conflictOn?.includes(status) === true) return new TrackerError("conflict", answered);
546
- const secondaryLimit = status === 403 && (response.headers.get("x-ratelimit-remaining")?.trim() === "0" || retryAfterOf(response.headers.get("retry-after")?.trim(), Date.now()) !== void 0);
587
+ const exhausted = RATE_LIMIT_REMAINING_HEADERS.some((name) => response.headers.get(name)?.trim() === "0");
588
+ const secondaryLimit = status === 403 && (exhausted || retryAfterHeaderOf(response.headers, Date.now()) !== void 0);
547
589
  if (status === 429 || secondaryLimit) {
548
590
  const delay = retryDelayOf(response.headers);
549
591
  return new TrackerError("rate_limited", `\u0422\u0440\u0435\u043A\u0435\u0440 ${hostOf(url)} \u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0438\u043B \u0447\u0430\u0441\u0442\u043E\u0442\u0443 \u0437\u0430\u043F\u0440\u043E\u0441\u043E\u0432${delay === void 0 ? "" : `, \u043F\u043E\u0432\u0442\u043E\u0440 \u0447\u0435\u0440\u0435\u0437 ${String(delay)} \u0441`}.`);
@@ -551,14 +593,21 @@ function failureOf(url, response, raw, request) {
551
593
  return new TrackerError("failed", answered);
552
594
  }
553
595
  function retryDelayOf(headers, now = Date.now()) {
554
- const retryAfter = retryAfterOf(headers.get("retry-after")?.trim(), now);
596
+ const retryAfter = retryAfterHeaderOf(headers, now);
555
597
  if (retryAfter !== void 0) return retryAfter;
556
- for (const name of ["x-ratelimit-reset", "ratelimit-reset"]) {
598
+ for (const name of RATE_LIMIT_RESET_HEADERS) {
557
599
  const reset = headers.get(name)?.trim();
558
600
  if (reset !== void 0 && /^\d+$/.test(reset)) return resetDelay(Number(reset), now);
559
601
  }
560
602
  return void 0;
561
603
  }
604
+ function retryAfterHeaderOf(headers, now) {
605
+ for (const name of RETRY_AFTER_HEADERS) {
606
+ const delay = retryAfterOf(headers.get(name)?.trim(), now);
607
+ if (delay !== void 0) return delay;
608
+ }
609
+ return void 0;
610
+ }
562
611
  function retryAfterOf(value, now) {
563
612
  if (value === void 0 || value === "") return void 0;
564
613
  if (/^\d+$/.test(value)) return Number(value);
@@ -586,6 +635,31 @@ function baseUrl(value, fallback) {
586
635
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new TrackerError("invalid_request", "\u0410\u0434\u0440\u0435\u0441 \u0442\u0440\u0435\u043A\u0435\u0440\u0430 \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C http \u0438\u043B\u0438 https.");
587
636
  return raw;
588
637
  }
638
+ function linkNextPage(link2) {
639
+ if (link2 === null || link2 === void 0) return void 0;
640
+ for (const [, target, params] of link2.matchAll(/<([^>]*)>([^<]*)/g)) {
641
+ const rel = /;\s*rel\s*=\s*"?([^";,]*)"?/i.exec(params ?? "")?.[1];
642
+ if (rel === void 0 || !rel.toLowerCase().split(/\s+/).includes("next")) continue;
643
+ let page;
644
+ try {
645
+ page = new URL(target ?? "", "http://link.invalid").searchParams.get("page");
646
+ } catch {
647
+ return void 0;
648
+ }
649
+ return page !== null && /^[1-9]\d{0,8}$/.test(page) ? Number(page) : void 0;
650
+ }
651
+ return void 0;
652
+ }
653
+ function nextPageCursor(input) {
654
+ const { headers, page, perPage, count } = input;
655
+ const linked = linkNextPage(headers.get("link"));
656
+ if (linked !== void 0) return linked > page ? { nextCursor: String(linked) } : {};
657
+ const rawTotal = headers.get("x-total-count")?.trim() ?? input.total;
658
+ const total = typeof rawTotal === "string" && /^\d{1,15}$/.test(rawTotal) ? Number(rawTotal) : typeof rawTotal === "number" && Number.isSafeInteger(rawTotal) ? rawTotal : void 0;
659
+ if (total !== void 0) return page * perPage < total ? { nextCursor: String(page + 1) } : {};
660
+ if (headers.has("link")) return {};
661
+ return count >= perPage ? { nextCursor: String(page + 1) } : {};
662
+ }
589
663
 
590
664
  // src/tracker/ci.schema.ts
591
665
  import { z } from "zod";
@@ -1590,6 +1664,258 @@ function concat(head, tail) {
1590
1664
  return merged;
1591
1665
  }
1592
1666
 
1667
+ // src/tracker/gitverse.const.ts
1668
+ var GITVERSE_API = "https://api.gitverse.ru";
1669
+ var GITVERSE_WEB = "https://gitverse.ru";
1670
+ var GITVERSE_ACCEPT = "application/vnd.gitverse.object+json;version=1";
1671
+ var GITVERSE_REMOTE_HOSTS = ["gitverse.ru"];
1672
+ var GITVERSE_TOKEN_ENV = "GITVERSE_TOKEN";
1673
+
1674
+ // src/tracker/gitverse-ci.lib.ts
1675
+ var TARGET4 = /^[\w.-]+\/[\w.-]+$/;
1676
+ var ID3 = /^[1-9]\d{0,19}$/;
1677
+ var SHA2 = /^[0-9a-f]{7,64}$/;
1678
+ var PAGE_SIZE_MAX = 30;
1679
+ var RUN_FILTERS4 = ciRunFilterSchema.options;
1680
+ var JOB_FILTERS4 = ciJobFilterSchema.options;
1681
+ var RUNS_SUBJECT2 = "\u041F\u0440\u043E\u0433\u043E\u043D\u044B CI GitVerse";
1682
+ var JOBS_SUBJECT2 = "\u0417\u0430\u0434\u0430\u043D\u0438\u044F CI GitVerse";
1683
+ var WORKFLOWS_SUBJECT2 = "\u0421\u043F\u0438\u0441\u043E\u043A workflow GitVerse";
1684
+ var TRIGGER_SUBJECT2 = "\u0417\u0430\u043F\u0443\u0441\u043A CI GitVerse";
1685
+ var CAPABILITIES4 = {
1686
+ workflows: true,
1687
+ runs: { filters: ["status", "ref", "sha", "event", "workflow", "actor", "pullRequest"], clientSide: ["status", "ref", "sha", "event", "pullRequest"], sorts: [] },
1688
+ jobs: { filters: ["status"], clientSide: ["status"], sorts: [] },
1689
+ log: { live: false, cursor: true },
1690
+ trigger: { supported: true, workflows: "one", inputs: true, tags: true },
1691
+ rerun: { all: true, failedOnly: false, job: true, debug: false },
1692
+ cancel: { run: true, job: false }
1693
+ };
1694
+ function gitverseCi() {
1695
+ return { capabilities: CAPABILITIES4, create: (context) => createGitverseCiAdapter(context) };
1696
+ }
1697
+ function createGitverseCiAdapter({ connection, fetch }) {
1698
+ const target = requireTarget(connection.target, "owner/repo \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F GitVerse");
1699
+ if (!TARGET4.test(target)) throw new TrackerError("invalid_request", "target GitVerse \u2014 owner/repo.");
1700
+ const api = baseUrl(connection.url, GITVERSE_API);
1701
+ const origin = new URL(api).origin;
1702
+ const repo = `${api}/repos/${target}`;
1703
+ const actions = `${repo}/actions`;
1704
+ const headers = connection.token === void 0 || connection.token === "" ? {} : { authorization: `Bearer ${connection.token}` };
1705
+ const call = (extra = {}) => ({ fetch, origin, headers, accept: GITVERSE_ACCEPT, ...extra });
1706
+ const runKey = (key) => {
1707
+ if (!ID3.test(key)) throw new TrackerError("not_found", `\u0412 GitVerse \u043D\u0435\u0442 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 ${key}.`);
1708
+ return key;
1709
+ };
1710
+ const jobKey = (key) => {
1711
+ if (!ID3.test(key)) throw new TrackerError("not_found", `\u0412 GitVerse \u043D\u0435\u0442 \u0437\u0430\u0434\u0430\u043D\u0438\u044F ${key}.`);
1712
+ return key;
1713
+ };
1714
+ const missingRun = (key) => ({ failure: "not_found", message: `\u0412 GitVerse \u043D\u0435\u0442 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 ${key}.` });
1715
+ const missingJob = (key) => ({ failure: "not_found", message: `\u0412 GitVerse \u043D\u0435\u0442 \u0437\u0430\u0434\u0430\u043D\u0438\u044F ${key}.` });
1716
+ const getRun = async (key) => runOf4((await requestJson(`${actions}/runs/${runKey(key)}`, call({ missing: missingRun(key) }))).body);
1717
+ const rawJob = async (key) => {
1718
+ const id = jobKey(key);
1719
+ const { body } = await requestJson(`${actions}/jobs/${id}`, call({ missing: missingJob(id) }));
1720
+ return isRecord(body) ? body : {};
1721
+ };
1722
+ const headShaOf = async (query) => {
1723
+ if (query.pullRequest === void 0) return query.sha?.toLowerCase();
1724
+ if (query.sha !== void 0) throw new TrackerError("invalid_request", `${RUNS_SUBJECT2}: sha \u0438 pullRequest \u0437\u0430\u0434\u0430\u044E\u0442\u0441\u044F \u043F\u043E \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u0438 \u2014 \u043F\u0440\u043E\u0433\u043E\u043D\u044B PR \u0431\u0435\u0440\u0443\u0442\u0441\u044F \u043F\u043E \u0435\u0433\u043E \u0433\u043E\u043B\u043E\u0432\u0435.`);
1725
+ const pull = query.pullRequest;
1726
+ if (!ID3.test(pull)) throw new TrackerError("not_found", `\u0412 GitVerse \u043D\u0435\u0442 PR ${pull}.`);
1727
+ const { body } = await requestJson(`${repo}/pulls/${pull}`, call({ missing: { failure: "not_found", message: `\u0412 GitVerse \u043D\u0435\u0442 PR ${pull}.` } }));
1728
+ const head = isRecord(body) && isRecord(body.head) ? text(body.head.sha) : void 0;
1729
+ if (head === void 0) throw new TrackerError("failed", "GitVerse \u043D\u0435 \u0432\u0435\u0440\u043D\u0443\u043B \u0433\u043E\u043B\u043E\u0432\u0443 PR.");
1730
+ return head.toLowerCase();
1731
+ };
1732
+ return {
1733
+ listWorkflows: async (query) => {
1734
+ assertListQuery(WORKFLOWS_SUBJECT2, { filters: [], clientSide: [], sorts: [] }, query, []);
1735
+ const page = pageOf3(query.cursor, WORKFLOWS_SUBJECT2);
1736
+ const perPage = Math.min(query.limit, PAGE_SIZE_MAX);
1737
+ const { body, headers: responseHeaders } = await requestJson(`${actions}/workflows?page=${String(page)}&per_page=${String(perPage)}`, call());
1738
+ const rows = rowsOf2(body, "workflows");
1739
+ return { items: rows.map(workflowOf2), ...nextPageCursor({ headers: responseHeaders, page, perPage, count: rows.length, total: isRecord(body) ? body.total_count : void 0 }) };
1740
+ },
1741
+ listRuns: async (query) => {
1742
+ assertListQuery(RUNS_SUBJECT2, CAPABILITIES4.runs, query, RUN_FILTERS4);
1743
+ const page = pageOf3(query.cursor, RUNS_SUBJECT2);
1744
+ const perPage = Math.min(query.limit, PAGE_SIZE_MAX);
1745
+ const sha = await headShaOf(query);
1746
+ const params = new URLSearchParams({ page: String(page), per_page: String(perPage) });
1747
+ if (query.ref !== void 0 && !query.ref.includes("/")) params.set("branch", query.ref);
1748
+ if (query.actor !== void 0) params.set("actor", query.actor);
1749
+ const path = query.workflow === void 0 ? `${actions}/runs` : `${actions}/workflows/${encodeURIComponent(query.workflow)}/runs`;
1750
+ const { body, headers: responseHeaders } = await requestJson(`${path}?${params.toString()}`, call());
1751
+ const rows = rowsOf2(body, "workflow_runs");
1752
+ const items = rows.map(runOf4).filter(
1753
+ (run) => (query.status === void 0 || query.status.length === 0 || query.status.includes(run.status.category)) && (query.event === void 0 || query.event.length === 0 || run.event !== void 0 && query.event.includes(run.event.category)) && (query.ref === void 0 || run.ref === query.ref) && (sha === void 0 || run.sha !== void 0 && run.sha.startsWith(sha))
1754
+ );
1755
+ return { items, ...nextPageCursor({ headers: responseHeaders, page, perPage, count: rows.length, total: isRecord(body) ? body.total_count : void 0 }) };
1756
+ },
1757
+ getRun,
1758
+ listJobs: async (key, query) => {
1759
+ const run = runKey(key);
1760
+ const { allAttempts, ...rest } = query;
1761
+ assertListQuery(JOBS_SUBJECT2, CAPABILITIES4.jobs, allAttempts === true ? query : rest, JOB_FILTERS4);
1762
+ const page = pageOf3(query.cursor, JOBS_SUBJECT2);
1763
+ const perPage = Math.min(query.limit, PAGE_SIZE_MAX);
1764
+ const { body, headers: responseHeaders } = await requestJson(`${actions}/runs/${run}/jobs?page=${String(page)}&per_page=${String(perPage)}`, call({ missing: missingRun(run) }));
1765
+ const rows = rowsOf2(body, "jobs");
1766
+ const items = rows.map(jobOf4).filter((job) => query.status === void 0 || query.status.length === 0 || query.status.includes(job.status.category));
1767
+ return { items, ...nextPageCursor({ headers: responseHeaders, page, perPage, count: rows.length, total: isRecord(body) ? body.total_count : void 0 }) };
1768
+ },
1769
+ getJob: async (key) => jobOf4(await rawJob(key)),
1770
+ log: async (key, query) => {
1771
+ const id = jobKey(key);
1772
+ const window = logWindow(query);
1773
+ const complete = isFinished(jobStatusOf(await rawJob(id)).category);
1774
+ if (!complete) return { text: "", truncated: false, complete: false, nextCursor: logCursor(0, 0) };
1775
+ const response = await requestText(`${actions}/jobs/${id}/logs`, {
1776
+ fetch,
1777
+ origin,
1778
+ headers,
1779
+ accept: GITVERSE_ACCEPT,
1780
+ skip: window.resume?.offset ?? 0,
1781
+ keep: { bytes: window.bytes, from: window.from },
1782
+ missing: missingJob(id)
1783
+ });
1784
+ return logOf({
1785
+ text: response.text,
1786
+ truncated: window.from === "tail" ? response.start > 0 : !response.whole,
1787
+ complete,
1788
+ page: 0,
1789
+ end: response.end,
1790
+ more: window.from === "head" && !response.whole,
1791
+ ...response.whole ? { size: response.read } : {},
1792
+ raw: query.raw ?? false
1793
+ });
1794
+ },
1795
+ trigger: async (input) => {
1796
+ const workflows = input.workflows ?? [];
1797
+ const workflow = workflows[0];
1798
+ if (workflows.length !== 1 || workflow === void 0) throw new TrackerError("invalid_request", `${TRIGGER_SUBJECT2}: \u043D\u0443\u0436\u0435\u043D \u0440\u043E\u0432\u043D\u043E \u043E\u0434\u0438\u043D workflow \u2014 \u0438\u043C\u044F \u0444\u0430\u0439\u043B\u0430.`);
1799
+ const params = new URLSearchParams({ [input.refType === "tag" ? "tag" : "branch"]: input.ref });
1800
+ await requestJson(`${actions}/workflows/${encodeURIComponent(workflow)}/dispatches?${params.toString()}`, call({ method: "POST", body: { ...input.inputs }, conflictOn: [406, 409] }));
1801
+ return {};
1802
+ },
1803
+ rerun: async (key, input) => {
1804
+ const run = runKey(key);
1805
+ if (input.failedOnly === true) throw new TrackerError("unsupported", "\u041F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A CI GitVerse: \u0442\u043E\u043B\u044C\u043A\u043E \u0443\u043F\u0430\u0432\u0448\u0438\u0445 \u0437\u0430\u0434\u0430\u043D\u0438\u0439 \u043D\u0435\u0442 \u2014 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0435\u0442\u0441\u044F \u043F\u0440\u043E\u0433\u043E\u043D \u0446\u0435\u043B\u0438\u043A\u043E\u043C.");
1806
+ if (input.debug === true) throw new TrackerError("unsupported", "\u041F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A CI GitVerse: \u043E\u0442\u043B\u0430\u0434\u043E\u0447\u043D\u043E\u0433\u043E \u043B\u043E\u0433\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u043D\u0435\u0442.");
1807
+ await requestJson(`${actions}/runs/${run}/rerun`, call({ method: "POST", conflictOn: [406, 409], missing: missingRun(run) }));
1808
+ return getRun(run);
1809
+ },
1810
+ rerunJob: async (key, input) => {
1811
+ const id = jobKey(key);
1812
+ if (input.debug === true) throw new TrackerError("unsupported", "\u041F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A CI GitVerse: \u043E\u0442\u043B\u0430\u0434\u043E\u0447\u043D\u043E\u0433\u043E \u043B\u043E\u0433\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u043D\u0435\u0442.");
1813
+ const run = idOf((await rawJob(id)).run_id);
1814
+ if (run === void 0) throw new TrackerError("failed", "GitVerse \u043D\u0435 \u0432\u0435\u0440\u043D\u0443\u043B \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u0434\u0430\u043D\u0438\u044F.");
1815
+ await requestJson(`${actions}/jobs/${id}/rerun`, call({ method: "POST", conflictOn: [406, 409], missing: missingJob(id) }));
1816
+ return getRun(run);
1817
+ },
1818
+ cancel: async (key) => {
1819
+ const run = runKey(key);
1820
+ await requestJson(`${actions}/runs/${run}/cancel`, call({ method: "POST", conflictOn: [409], missing: missingRun(run) }));
1821
+ return getRun(run);
1822
+ },
1823
+ cancelJob: async () => {
1824
+ throw new TrackerError("unsupported", "\u041E\u0442\u043C\u0435\u043D\u0430 CI GitVerse: \u0437\u0430\u0434\u0430\u043D\u0438\u044F \u043F\u043E \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u0438 \u043D\u0435 \u043E\u0442\u043C\u0435\u043D\u044F\u044E\u0442\u0441\u044F, \u043E\u0442\u043C\u0435\u043D\u044F\u0435\u0442\u0441\u044F \u043F\u0440\u043E\u0433\u043E\u043D.");
1825
+ }
1826
+ };
1827
+ }
1828
+ function pageOf3(cursor, subject) {
1829
+ if (cursor === void 0) return 1;
1830
+ if (!/^[1-9]\d{0,8}$/.test(cursor)) throw new TrackerError("invalid_request", `${subject}: \u043A\u0443\u0440\u0441\u043E\u0440 \u2014 \u043D\u043E\u043C\u0435\u0440 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B \u0438\u0437 nextCursor.`);
1831
+ return Number(cursor);
1832
+ }
1833
+ function rowsOf2(body, field) {
1834
+ const rows = isRecord(body) ? body[field] : void 0;
1835
+ return Array.isArray(rows) ? rows : [];
1836
+ }
1837
+ function keyOf2(value) {
1838
+ if (typeof value === "number" && Number.isSafeInteger(value)) return String(value);
1839
+ return typeof value === "string" && value !== "" ? value : "?";
1840
+ }
1841
+ function positiveInt2(value) {
1842
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : void 0;
1843
+ }
1844
+ function idOf(value) {
1845
+ const id = positiveInt2(value);
1846
+ return id === void 0 ? void 0 : String(id);
1847
+ }
1848
+ function statusOf2(item) {
1849
+ const raw = text(item.status) ?? "unknown";
1850
+ return { category: lookup(GITVERSE_CI_STATUSES, raw.toLowerCase(), "running"), name: raw };
1851
+ }
1852
+ function jobStatusOf(item) {
1853
+ const status = statusOf2(item);
1854
+ return item.failed_step !== void 0 && item.failed_step !== null ? { category: "failed", name: status.name } : status;
1855
+ }
1856
+ function refOf(value) {
1857
+ const ref = text(value);
1858
+ return ref === void 0 ? void 0 : text(ref.replace(/^refs\/(?:heads|tags)\//, "")) ?? ref;
1859
+ }
1860
+ function workflowOf2(raw) {
1861
+ const item = isRecord(raw) ? raw : {};
1862
+ const key = text(item.workflow) ?? text(item.path) ?? text(item.name) ?? "?";
1863
+ const path = text(item.path);
1864
+ const state = text(item.state);
1865
+ return { key, name: text(item.name) ?? key, ...path === void 0 ? {} : { path }, enabled: state === void 0 || !/disabled/i.test(state) };
1866
+ }
1867
+ function runOf4(raw) {
1868
+ const item = isRecord(raw) ? raw : {};
1869
+ const status = statusOf2(item);
1870
+ const name = text(item.name);
1871
+ const ref = refOf(item.ref);
1872
+ const sha = text(item.commit_sha)?.toLowerCase();
1873
+ const actor = isRecord(item.actor) ? text(item.actor.login) : void 0;
1874
+ const number = positiveInt2(item.index);
1875
+ const url = text(item.html_url);
1876
+ const startedAt = toTimestamp(item.started);
1877
+ const finishedAt = isFinished(status.category) ? toTimestamp(item.stopped) : void 0;
1878
+ const event = text(item.event);
1879
+ const effective = item.need_approval === true && !isFinished(status.category) ? { category: "waiting", name: status.name } : status;
1880
+ return {
1881
+ key: keyOf2(item.id),
1882
+ ...number === void 0 ? {} : { number },
1883
+ workflows: name === void 0 ? [] : [name],
1884
+ status: effective,
1885
+ ...event === void 0 ? {} : { event: { category: lookup(GITVERSE_RUN_EVENTS, event, "other"), name: event } },
1886
+ ...ref === void 0 ? {} : { ref },
1887
+ ...sha === void 0 || !SHA2.test(sha) ? {} : { sha },
1888
+ ...actor === void 0 ? {} : { actor },
1889
+ links: url === void 0 ? [] : [{ url, title: "GitVerse" }],
1890
+ ...startedAt === void 0 ? {} : { startedAt },
1891
+ ...finishedAt === void 0 ? {} : { finishedAt }
1892
+ };
1893
+ }
1894
+ function jobOf4(raw) {
1895
+ const item = isRecord(raw) ? raw : {};
1896
+ const key = keyOf2(item.id);
1897
+ const stage = text(item.workflow_name);
1898
+ const startedAt = toTimestamp(item.started_at);
1899
+ const finishedAt = toTimestamp(item.completed_at);
1900
+ const steps = Array.isArray(item.steps) ? item.steps.map(stepOf2) : void 0;
1901
+ return {
1902
+ key,
1903
+ runKey: idOf(item.run_id) ?? "?",
1904
+ name: text(item.name) ?? key,
1905
+ ...stage === void 0 ? {} : { stage },
1906
+ status: jobStatusOf(item),
1907
+ ...steps === void 0 ? {} : { steps },
1908
+ links: [],
1909
+ ...startedAt === void 0 ? {} : { startedAt },
1910
+ ...finishedAt === void 0 ? {} : { finishedAt }
1911
+ };
1912
+ }
1913
+ function stepOf2(raw) {
1914
+ const item = isRecord(raw) ? raw : {};
1915
+ const number = typeof item.number === "number" && Number.isSafeInteger(item.number) && item.number >= 0 ? item.number : 0;
1916
+ return { number, name: text(item.name) ?? String(number), status: statusOf2(item) };
1917
+ }
1918
+
1593
1919
  // src/tracker/registry.lib.ts
1594
1920
  var definitions = /* @__PURE__ */ new Map();
1595
1921
  var userDefinitions = /* @__PURE__ */ new Map();
@@ -1646,9 +1972,9 @@ var pullRequestSchema = z2.object({
1646
1972
 
1647
1973
  // src/tracker/github-pull.lib.ts
1648
1974
  var DEFAULT_API3 = "https://api.github.com";
1649
- var TARGET4 = /^[\w.-]+\/[\w.-]+$/;
1975
+ var TARGET5 = /^[\w.-]+\/[\w.-]+$/;
1650
1976
  var NUMBER = /^[1-9]\d{0,19}$/;
1651
- var SHA2 = /^[0-9a-f]{7,64}$/;
1977
+ var SHA3 = /^[0-9a-f]{7,64}$/;
1652
1978
  var LIST_SUBJECT = "PR GitHub";
1653
1979
  var COMMENTS_SUBJECT = "\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 PR GitHub";
1654
1980
  var PULL_CAPABILITIES = {
@@ -1674,7 +2000,7 @@ function githubPulls() {
1674
2000
  }
1675
2001
  function createGithubPullAdapter({ connection, fetch }) {
1676
2002
  const target = requireTarget(connection.target, "owner/repo \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F GitHub");
1677
- if (!TARGET4.test(target)) throw new TrackerError("invalid_request", "target GitHub \u2014 owner/repo.");
2003
+ if (!TARGET5.test(target)) throw new TrackerError("invalid_request", "target GitHub \u2014 owner/repo.");
1678
2004
  const api = baseUrl(connection.url, DEFAULT_API3);
1679
2005
  const origin = new URL(api).origin;
1680
2006
  const root = `${api}/repos/${target}/pulls`;
@@ -1695,7 +2021,7 @@ function createGithubPullAdapter({ connection, fetch }) {
1695
2021
  list: async (query) => {
1696
2022
  assertListQuery(LIST_SUBJECT, PULL_CAPABILITIES.list, query, pullRequestFilterSchema.options);
1697
2023
  const status = usedStatuses(query.status);
1698
- const page = pageOf3(query.cursor);
2024
+ const page = pageOf4(query.cursor);
1699
2025
  const params = new URLSearchParams({ state: stateFilter(status), per_page: String(query.limit), page: String(page) });
1700
2026
  if (query.source !== void 0) params.set("head", `${owner}:${query.source}`);
1701
2027
  if (query.target !== void 0) params.set("base", query.target);
@@ -1726,7 +2052,7 @@ function createGithubPullAdapter({ connection, fetch }) {
1726
2052
  },
1727
2053
  comments: async (key, query) => {
1728
2054
  assertCommentQuery(COMMENTS_SUBJECT, PULL_CAPABILITIES.comments, query);
1729
- const page = pageOf3(query.cursor);
2055
+ const page = pageOf4(query.cursor);
1730
2056
  const params = new URLSearchParams({ per_page: String(query.limit), page: String(page) });
1731
2057
  if (query.updatedAfter !== void 0) params.set("since", isoTime(query.updatedAfter));
1732
2058
  const { body, headers: responseHeaders } = await requestJson(`${issues}/${number(key)}/comments?${params.toString()}`, { fetch, origin, headers, missing: missing(key) });
@@ -1735,7 +2061,7 @@ function createGithubPullAdapter({ connection, fetch }) {
1735
2061
  comment: async (key, body) => commentOf((await requestJson(`${issues}/${number(key)}/comments`, { fetch, origin, headers, method: "POST", body: { body }, missing: missing(key) })).body)
1736
2062
  };
1737
2063
  }
1738
- function pageOf3(cursor) {
2064
+ function pageOf4(cursor) {
1739
2065
  const page = cursor === void 0 ? 1 : Number(cursor);
1740
2066
  return Number.isInteger(page) && page >= 1 ? page : 1;
1741
2067
  }
@@ -1814,7 +2140,7 @@ function pullOf(raw) {
1814
2140
  ...author === void 0 ? {} : { author },
1815
2141
  source: text(head.ref) ?? "?",
1816
2142
  target: text(base.ref) ?? "?",
1817
- ...sha !== void 0 && SHA2.test(sha) ? { headSha: sha } : {},
2143
+ ...sha !== void 0 && SHA3.test(sha) ? { headSha: sha } : {},
1818
2144
  labels: Array.isArray(item.labels) ? stringList(item.labels.map((label) => isRecord(label) ? label.name : label)) : [],
1819
2145
  reviewers: logins(item.requested_reviewers),
1820
2146
  links: url === void 0 ? [] : [{ url, title: "GitHub" }],
@@ -1863,19 +2189,19 @@ var branchSchema = z3.object({
1863
2189
 
1864
2190
  // src/tracker/github-repo.lib.ts
1865
2191
  var DEFAULT_API4 = "https://api.github.com";
1866
- var TARGET5 = /^[\w.-]+\/[\w.-]+$/;
1867
- var SHA3 = /^[0-9a-f]{7,64}$/;
2192
+ var TARGET6 = /^[\w.-]+\/[\w.-]+$/;
2193
+ var SHA4 = /^[0-9a-f]{7,64}$/;
1868
2194
  var BRANCHES_SUBJECT = "\u0412\u0435\u0442\u043A\u0438 GitHub";
1869
2195
  var BRANCH_FILTERS = branchFilterSchema.options;
1870
- var CAPABILITIES4 = {
2196
+ var CAPABILITIES5 = {
1871
2197
  branches: { filters: BRANCH_FILTERS, clientSide: BRANCH_FILTERS, sorts: [] }
1872
2198
  };
1873
2199
  function githubRepo() {
1874
- return { capabilities: CAPABILITIES4, create: (context) => createGithubRepoAdapter(context) };
2200
+ return { capabilities: CAPABILITIES5, create: (context) => createGithubRepoAdapter(context) };
1875
2201
  }
1876
2202
  function createGithubRepoAdapter({ connection, fetch }) {
1877
2203
  const target = requireTarget(connection.target, "owner/repo \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F GitHub");
1878
- if (!TARGET5.test(target)) throw new TrackerError("invalid_request", "target GitHub \u2014 owner/repo.");
2204
+ if (!TARGET6.test(target)) throw new TrackerError("invalid_request", "target GitHub \u2014 owner/repo.");
1879
2205
  const api = baseUrl(connection.url, DEFAULT_API4);
1880
2206
  const origin = new URL(api).origin;
1881
2207
  const root = `${api}/repos/${target}`;
@@ -1888,8 +2214,8 @@ function createGithubRepoAdapter({ connection, fetch }) {
1888
2214
  return {
1889
2215
  get: async () => repoOf((await requestJson(root, { fetch, origin, headers, missing })).body, target),
1890
2216
  branches: async (query) => {
1891
- assertListQuery(BRANCHES_SUBJECT, CAPABILITIES4.branches, query, BRANCH_FILTERS);
1892
- const page = pageOf4(query.cursor);
2217
+ assertListQuery(BRANCHES_SUBJECT, CAPABILITIES5.branches, query, BRANCH_FILTERS);
2218
+ const page = pageOf5(query.cursor);
1893
2219
  const params = new URLSearchParams({ per_page: String(query.limit), page: String(page) });
1894
2220
  const { body, headers: responseHeaders } = await requestJson(`${root}/branches?${params.toString()}`, { fetch, origin, headers, missing });
1895
2221
  const items = (Array.isArray(body) ? body : []).map(branchOf).filter((branch) => query.search === void 0 || containsText([branch.name], query.search));
@@ -1897,7 +2223,7 @@ function createGithubRepoAdapter({ connection, fetch }) {
1897
2223
  }
1898
2224
  };
1899
2225
  }
1900
- function pageOf4(cursor) {
2226
+ function pageOf5(cursor) {
1901
2227
  const page = cursor === void 0 ? 1 : Number(cursor);
1902
2228
  return Number.isInteger(page) && page >= 1 ? page : 1;
1903
2229
  }
@@ -1923,7 +2249,7 @@ function branchOf(raw) {
1923
2249
  const sha = isRecord(item.commit) ? text(item.commit.sha) : void 0;
1924
2250
  return {
1925
2251
  name: text(item.name) ?? "?",
1926
- ...sha === void 0 || !SHA3.test(sha) ? {} : { sha },
2252
+ ...sha === void 0 || !SHA4.test(sha) ? {} : { sha },
1927
2253
  ...typeof item.protected === "boolean" ? { protected: item.protected } : {}
1928
2254
  };
1929
2255
  }
@@ -1934,7 +2260,7 @@ function visibilityOf(value) {
1934
2260
 
1935
2261
  // src/tracker/github.lib.ts
1936
2262
  var DEFAULT_API5 = "https://api.github.com";
1937
- var TARGET6 = /^[\w.-]+\/[\w.-]+$/;
2263
+ var TARGET7 = /^[\w.-]+\/[\w.-]+$/;
1938
2264
  var NUMBER2 = /^[1-9]\d{0,14}$/;
1939
2265
  var LIST_SUBJECT2 = "\u0417\u0430\u0434\u0430\u0447\u0438 GitHub";
1940
2266
  var COMMENTS_SUBJECT2 = "\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 GitHub";
@@ -1964,7 +2290,7 @@ function githubTracker() {
1964
2290
  }
1965
2291
  function createGithubAdapter({ connection, fetch }, capabilities) {
1966
2292
  const target = requireTarget(connection.target, "owner/repo \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F GitHub");
1967
- if (!TARGET6.test(target)) throw new TrackerError("invalid_request", "target GitHub \u2014 owner/repo.");
2293
+ if (!TARGET7.test(target)) throw new TrackerError("invalid_request", "target GitHub \u2014 owner/repo.");
1968
2294
  const api = baseUrl(connection.url, DEFAULT_API5);
1969
2295
  const origin = new URL(api).origin;
1970
2296
  const root = `${api}/repos/${target}/issues`;
@@ -1985,11 +2311,11 @@ function createGithubAdapter({ connection, fetch }, capabilities) {
1985
2311
  list: async (query) => {
1986
2312
  assertListQuery(LIST_SUBJECT2, capabilities.list, query, ISSUE_FILTERS);
1987
2313
  assertCsvLabels(query.labels);
1988
- const page = pageOf5(query.cursor);
2314
+ const page = pageOf6(query.cursor);
1989
2315
  const search = needsSearch(query);
1990
2316
  const params = search ? searchParams(target, query, page) : issuesParams(query, page);
1991
2317
  const { body, headers: responseHeaders } = await requestJson(`${search ? `${api}/search/issues` : root}?${params.toString()}`, { fetch, origin, headers });
1992
- return issuePageOf(rowsOf2(body, search), query, responseHeaders.get("link") ?? "", page);
2318
+ return issuePageOf(rowsOf3(body, search), query, responseHeaders.get("link") ?? "", page);
1993
2319
  },
1994
2320
  get: async (key) => issueOf(await rawOf(key)),
1995
2321
  create: async (input) => {
@@ -2009,7 +2335,7 @@ function createGithubAdapter({ connection, fetch }, capabilities) {
2009
2335
  },
2010
2336
  comments: async (key, query) => {
2011
2337
  assertCommentQuery(COMMENTS_SUBJECT2, capabilities.comments, query);
2012
- const page = pageOf5(query.cursor);
2338
+ const page = pageOf6(query.cursor);
2013
2339
  const params = new URLSearchParams({ per_page: String(query.limit), page: String(page) });
2014
2340
  if (query.updatedAfter !== void 0) params.set("since", isoTime(query.updatedAfter));
2015
2341
  const { body, headers: responseHeaders } = await requestJson(`${root}/${number(key)}/comments?${params.toString()}`, { fetch, origin, headers, missingIsIssue: true });
@@ -2018,7 +2344,7 @@ function createGithubAdapter({ connection, fetch }, capabilities) {
2018
2344
  comment: async (key, body) => commentOf2((await requestJson(`${root}/${number(key)}/comments`, { fetch, origin, headers, method: "POST", body: { body }, missingIsIssue: true })).body)
2019
2345
  };
2020
2346
  }
2021
- function pageOf5(cursor) {
2347
+ function pageOf6(cursor) {
2022
2348
  const page = cursor === void 0 ? 1 : Number(cursor);
2023
2349
  return Number.isInteger(page) && page >= 1 ? page : 1;
2024
2350
  }
@@ -2073,7 +2399,7 @@ function timeRangeQualifier(field, after, before) {
2073
2399
  if (before !== void 0) return `${field}:<=${isoTime(before)}`;
2074
2400
  return void 0;
2075
2401
  }
2076
- function rowsOf2(body, search) {
2402
+ function rowsOf3(body, search) {
2077
2403
  if (!search) return Array.isArray(body) ? body : [];
2078
2404
  return isRecord(body) && Array.isArray(body.items) ? body.items : [];
2079
2405
  }
@@ -2137,12 +2463,510 @@ function commentOf2(raw) {
2137
2463
  return { id: typeof item.id === "number" ? String(item.id) : "?", ...author === void 0 ? {} : { author }, body: typeof item.body === "string" ? item.body : "", ...createdAt === void 0 ? {} : { createdAt } };
2138
2464
  }
2139
2465
 
2466
+ // src/tracker/gitverse-pull.lib.ts
2467
+ var TARGET8 = /^[\w.-]+\/[\w.-]+$/;
2468
+ var NUMBER3 = /^[1-9]\d{0,14}$/;
2469
+ var OFFSET = /^\d{1,9}$/;
2470
+ var SHA5 = /^[0-9a-f]{7,64}$/;
2471
+ var LIST_SUBJECT3 = "PR GitVerse";
2472
+ var COMMENTS_SUBJECT3 = "\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 PR GitVerse";
2473
+ var PAGE_SIZE_MAX2 = 100;
2474
+ var PULL_FILTERS = pullRequestFilterSchema.options;
2475
+ var SORT_FIELDS_UPSTREAM = { created: "created", updated: "updated" };
2476
+ var PULL_CAPABILITIES2 = {
2477
+ list: {
2478
+ filters: PULL_FILTERS,
2479
+ clientSide: ["status", "author", "source", "target", "labels", "search", "createdAfter", "createdBefore", "updatedAfter", "updatedBefore"],
2480
+ sorts: ["created", "updated"]
2481
+ },
2482
+ comments: { filters: COMMENT_FILTERS, clientSide: COMMENT_FILTERS, order: ["asc", "desc"] },
2483
+ create: true,
2484
+ createDraft: true,
2485
+ update: ["title", "description", "target"],
2486
+ statuses: ["open", "closed"],
2487
+ comment: true,
2488
+ merge: [],
2489
+ deleteSourceOnMerge: false
2490
+ };
2491
+ function gitversePulls() {
2492
+ return { capabilities: PULL_CAPABILITIES2, create: (context) => createPullAdapter(context) };
2493
+ }
2494
+ function createPullAdapter({ connection, fetch }) {
2495
+ const target = requireTarget(connection.target, "owner/repo \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F GitVerse");
2496
+ if (!TARGET8.test(target)) throw new TrackerError("invalid_request", "target GitVerse \u2014 owner/repo.");
2497
+ const api = baseUrl(connection.url, GITVERSE_API);
2498
+ const origin = new URL(api).origin;
2499
+ const repo = `${api}/repos/${target}`;
2500
+ const root = `${repo}/pulls`;
2501
+ const headers = connection.token === void 0 || connection.token === "" ? {} : { authorization: `Bearer ${connection.token}` };
2502
+ const web = connection.url === void 0 ? `${GITVERSE_WEB}/${target}` : void 0;
2503
+ const missing = (key) => ({ failure: "not_found", message: `\u0412 GitVerse \u043D\u0435\u0442 PR ${key}.` });
2504
+ const call = (key, extra = {}) => ({ fetch, origin, headers, accept: GITVERSE_ACCEPT, ...key === void 0 ? {} : { missing: missing(key) }, ...extra });
2505
+ const number = (key) => {
2506
+ if (!NUMBER3.test(key)) throw new TrackerError("not_found", `\u0412 GitVerse \u043D\u0435\u0442 PR ${key}.`);
2507
+ return key;
2508
+ };
2509
+ const toPull = (raw) => pullOf2(raw, web);
2510
+ const getPull = async (key) => toPull((await requestJson(`${root}/${number(key)}`, call(key))).body);
2511
+ return {
2512
+ list: async (query) => {
2513
+ assertListQuery(LIST_SUBJECT3, PULL_CAPABILITIES2.list, query, PULL_FILTERS);
2514
+ const page = pageOf7(query.cursor);
2515
+ const perPage = Math.min(query.limit, PAGE_SIZE_MAX2);
2516
+ const params = new URLSearchParams({ state: stateFilter3(query.status), page: String(page), per_page: String(perPage) });
2517
+ if (query.source !== void 0 && !query.source.includes("/")) params.set("head", query.source);
2518
+ if (query.target !== void 0 && !query.target.includes("/")) params.set("base", query.target);
2519
+ if (query.sort !== void 0) {
2520
+ params.set("sort", sortFieldOf(query.sort.field));
2521
+ params.set("direction", query.sort.direction ?? "desc");
2522
+ }
2523
+ const { body, headers: responseHeaders } = await requestJson(`${root}?${params.toString()}`, call(void 0));
2524
+ const rows = Array.isArray(body) ? body : [];
2525
+ return { items: rows.map(toPull).filter((pull) => matchesPage2(pull, query)), ...nextPageCursor({ headers: responseHeaders, page, perPage, count: rows.length }) };
2526
+ },
2527
+ get: (key) => getPull(key),
2528
+ create: async (input) => {
2529
+ const body = { title: input.title, head: input.source, base: input.target, ...input.description === void 0 ? {} : { body: input.description }, draft: input.draft === true };
2530
+ return toPull((await requestJson(root, call(void 0, { method: "POST", body }))).body);
2531
+ },
2532
+ update: async (key, patch) => {
2533
+ const id = number(key);
2534
+ if (patch.status === "draft") throw new TrackerError("unsupported", "PR GitVerse: \u0432\u0435\u0440\u043D\u0443\u0442\u044C PR \u0432 \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A API \u043D\u0435 \u0434\u0430\u0451\u0442.");
2535
+ let state;
2536
+ if (patch.status === "closed") state = "closed";
2537
+ if (patch.status === "open") {
2538
+ const current = (await getPull(key)).status.category;
2539
+ if (current === "draft") throw new TrackerError("unsupported", "PR GitVerse: \u0441\u043D\u044F\u0442\u044C \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A API \u043D\u0435 \u0434\u0430\u0451\u0442 \u2014 \u043E\u043F\u0443\u0431\u043B\u0438\u043A\u0443\u0439\u0442\u0435 PR \u0432 \u0432\u0435\u0431-\u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435.");
2540
+ if (current === "merged") throw new TrackerError("conflict", `PR ${key} \u0443\u0436\u0435 \u0432\u043B\u0438\u0442: \u043F\u0435\u0440\u0435\u043E\u0442\u043A\u0440\u044B\u0442\u044C \u0435\u0433\u043E \u043D\u0435\u043B\u044C\u0437\u044F.`);
2541
+ if (current === "closed") state = "open";
2542
+ }
2543
+ const body = {
2544
+ ...patch.title === void 0 ? {} : { title: patch.title },
2545
+ ...patch.description === void 0 ? {} : { body: patch.description },
2546
+ ...patch.target === void 0 ? {} : { base: patch.target },
2547
+ ...state === void 0 ? {} : { state }
2548
+ };
2549
+ if (Object.keys(body).length === 0) return getPull(key);
2550
+ const pull = toPull((await requestJson(`${root}/${id}`, call(key, { method: "PATCH", body }))).body);
2551
+ if (state !== void 0 && pull.status.category !== state) throw new TrackerError("conflict", `GitVerse \u043E\u0441\u0442\u0430\u0432\u0438\u043B PR ${key} \u0432 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0438 ${pull.status.name}.`);
2552
+ return pull;
2553
+ },
2554
+ merge: async (key) => {
2555
+ const hint = web === void 0 || !NUMBER3.test(key) ? "\u0432 \u0432\u0435\u0431-\u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 GitVerse" : `\u0432 \u0432\u0435\u0431-\u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435: ${web}/pulls/${key}`;
2556
+ throw new TrackerError("unsupported", `PR GitVerse: \u0441\u043B\u0438\u044F\u043D\u0438\u044F \u0432 API \u043D\u0435\u0442 \u2014 \u0432\u043B\u0438\u0442\u044C PR \u043C\u043E\u0436\u043D\u043E \u0442\u043E\u043B\u044C\u043A\u043E ${hint}.`);
2557
+ },
2558
+ comments: async (key, query) => {
2559
+ assertCommentQuery(COMMENTS_SUBJECT3, PULL_CAPABILITIES2.comments, query);
2560
+ const offset = offsetOf2(query.cursor);
2561
+ const { body } = await requestJson(`${repo}/issues/${number(key)}/comments`, call(key));
2562
+ const visible = (Array.isArray(body) ? body : []).map(commentOf3).filter((comment) => query.updatedAfter === void 0 || inTimeRange(comment.updatedAt, query.updatedAfter, void 0)).sort((left, right) => (left.comment.createdAt ?? 0) - (right.comment.createdAt ?? 0));
2563
+ if (query.order === "desc") visible.reverse();
2564
+ const end = offset + query.limit;
2565
+ return { items: visible.slice(offset, end).map((item) => item.comment), ...end < visible.length ? { nextCursor: String(end) } : {} };
2566
+ },
2567
+ comment: async (key, body) => commentOf3((await requestJson(`${repo}/issues/${number(key)}/comments`, call(key, { method: "POST", body: { body } }))).body).comment
2568
+ };
2569
+ }
2570
+ function pageOf7(cursor) {
2571
+ if (cursor === void 0) return 1;
2572
+ if (!/^[1-9]\d{0,8}$/.test(cursor)) throw new TrackerError("invalid_request", `${LIST_SUBJECT3}: \u043A\u0443\u0440\u0441\u043E\u0440 \u2014 \u043D\u043E\u043C\u0435\u0440 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B \u0438\u0437 nextCursor.`);
2573
+ return Number(cursor);
2574
+ }
2575
+ function offsetOf2(cursor) {
2576
+ if (cursor === void 0) return 0;
2577
+ if (!OFFSET.test(cursor)) throw new TrackerError("invalid_request", `${COMMENTS_SUBJECT3}: \u043A\u0443\u0440\u0441\u043E\u0440 \u2014 nextCursor \u043F\u0440\u043E\u0448\u043B\u043E\u0439 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B.`);
2578
+ return Number(cursor);
2579
+ }
2580
+ function stateFilter3(status) {
2581
+ if (status === void 0 || status.length === 0) return "all";
2582
+ if (status.every((item) => item === "open" || item === "draft")) return "open";
2583
+ if (status.every((item) => item === "merged" || item === "closed")) return "closed";
2584
+ return "all";
2585
+ }
2586
+ function sortFieldOf(field) {
2587
+ const upstream = SORT_FIELDS_UPSTREAM[field];
2588
+ if (upstream === void 0) throw new TrackerError("unsupported", `${LIST_SUBJECT3}: \u0441\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u043A\u0430 \u043F\u043E ${field} \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F.`);
2589
+ return upstream;
2590
+ }
2591
+ function matchesPage2(pull, query) {
2592
+ if (query.status !== void 0 && query.status.length > 0 && !query.status.includes(pull.status.category)) return false;
2593
+ if (query.author !== void 0 && pull.author?.toLowerCase() !== query.author.toLowerCase()) return false;
2594
+ if (query.source !== void 0 && pull.source !== query.source) return false;
2595
+ if (query.target !== void 0 && pull.target !== query.target) return false;
2596
+ const labels = pull.labels.map((label) => label.toLowerCase());
2597
+ if (query.labels !== void 0 && !query.labels.every((label) => labels.includes(label.toLowerCase()))) return false;
2598
+ if (query.search !== void 0 && !containsText([pull.title, pull.description], query.search)) return false;
2599
+ return inTimeRange(pull.createdAt, query.createdAfter, query.createdBefore) && inTimeRange(pull.updatedAt, query.updatedAfter, query.updatedBefore);
2600
+ }
2601
+ function loginOf(value) {
2602
+ return isRecord(value) ? text(value.login) ?? text(value.username) : void 0;
2603
+ }
2604
+ function statusOf3(item) {
2605
+ const state = text(item.state) ?? "open";
2606
+ if (item.merged === true) return { category: "merged", name: "merged" };
2607
+ if (item.draft === true && state === "open") return { category: "draft", name: "draft" };
2608
+ return { category: lookup(GITVERSE_PULL_STATES, state, "open"), name: state };
2609
+ }
2610
+ function pullOf2(raw, web) {
2611
+ const item = isRecord(raw) ? raw : {};
2612
+ const head = isRecord(item.head) ? item.head : {};
2613
+ const base = isRecord(item.base) ? item.base : {};
2614
+ const key = typeof item.number === "number" && Number.isSafeInteger(item.number) ? String(item.number) : text(item.number) ?? "?";
2615
+ const headSha = text(head.sha);
2616
+ const description = text(item.body);
2617
+ const author = loginOf(item.user);
2618
+ const reviewers = Array.isArray(item.requested_reviewers) ? stringList(item.requested_reviewers.map(loginOf)) : void 0;
2619
+ const url = text(item.html_url) ?? (web === void 0 ? void 0 : `${web}/pulls/${key}`);
2620
+ const createdAt = toTimestamp(item.created_at);
2621
+ const updatedAt = toTimestamp(item.updated_at);
2622
+ const mergedAt = toTimestamp(item.merged_at);
2623
+ const closedAt = toTimestamp(item.closed_at);
2624
+ return {
2625
+ key,
2626
+ title: text(item.title) ?? key,
2627
+ ...description === void 0 ? {} : { description },
2628
+ status: statusOf3(item),
2629
+ ...author === void 0 ? {} : { author },
2630
+ source: text(head.ref) ?? "?",
2631
+ target: text(base.ref) ?? "?",
2632
+ ...headSha !== void 0 && SHA5.test(headSha) ? { headSha } : {},
2633
+ labels: Array.isArray(item.labels) ? stringList(item.labels.map((label) => isRecord(label) ? label.name : label)) : [],
2634
+ ...reviewers === void 0 ? {} : { reviewers },
2635
+ links: url === void 0 ? [] : [{ url, title: "GitVerse" }],
2636
+ ...createdAt === void 0 ? {} : { createdAt },
2637
+ ...updatedAt === void 0 ? {} : { updatedAt },
2638
+ ...mergedAt === void 0 ? {} : { mergedAt },
2639
+ ...closedAt === void 0 ? {} : { closedAt }
2640
+ };
2641
+ }
2642
+ function commentOf3(raw) {
2643
+ const item = isRecord(raw) ? raw : {};
2644
+ const id = typeof item.id === "number" && Number.isSafeInteger(item.id) ? String(item.id) : text(item.id) ?? "?";
2645
+ const author = loginOf(item.user);
2646
+ const createdAt = toTimestamp(item.created_at);
2647
+ return {
2648
+ comment: { id, ...author === void 0 ? {} : { author }, body: typeof item.body === "string" ? item.body : "", ...createdAt === void 0 ? {} : { createdAt } },
2649
+ updatedAt: toTimestamp(item.updated_at) ?? createdAt
2650
+ };
2651
+ }
2652
+
2653
+ // src/tracker/gitverse-repo.lib.ts
2654
+ var TARGET9 = /^[\w.-]+\/[\w.-]+$/;
2655
+ var SHA6 = /^[0-9a-f]{7,64}$/;
2656
+ var BRANCHES_SUBJECT2 = "\u0412\u0435\u0442\u043A\u0438 GitVerse";
2657
+ var BRANCH_FILTERS2 = branchFilterSchema.options;
2658
+ var PAGE_SIZE_MAX3 = 100;
2659
+ var CAPABILITIES6 = {
2660
+ branches: { filters: BRANCH_FILTERS2, clientSide: ["search"], sorts: [] }
2661
+ };
2662
+ function gitverseRepo() {
2663
+ return { capabilities: CAPABILITIES6, create: (context) => createGitverseRepoAdapter(context) };
2664
+ }
2665
+ function createGitverseRepoAdapter({ connection, fetch }) {
2666
+ const target = requireTarget(connection.target, "owner/repo \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F GitVerse");
2667
+ if (!TARGET9.test(target)) throw new TrackerError("invalid_request", "target GitVerse \u2014 owner/repo.");
2668
+ const api = baseUrl(connection.url, GITVERSE_API);
2669
+ const origin = new URL(api).origin;
2670
+ const repo = `${api}/repos/${target}`;
2671
+ const headers = connection.token === void 0 || connection.token === "" ? {} : { authorization: `Bearer ${connection.token}` };
2672
+ const web = connection.url === void 0 ? `${GITVERSE_WEB}/${target}` : void 0;
2673
+ const missing = { failure: "not_found", message: `\u0412 GitVerse \u043D\u0435\u0442 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F ${target}.` };
2674
+ return {
2675
+ get: async () => repoOf2((await requestJson(repo, { fetch, origin, headers, accept: GITVERSE_ACCEPT, missing })).body, target, web),
2676
+ branches: async (query) => {
2677
+ assertListQuery(BRANCHES_SUBJECT2, CAPABILITIES6.branches, query, BRANCH_FILTERS2);
2678
+ if (query.cursor !== void 0 && !/^[1-9]\d{0,8}$/.test(query.cursor)) throw new TrackerError("invalid_request", `${BRANCHES_SUBJECT2}: \u043A\u0443\u0440\u0441\u043E\u0440 \u2014 \u043D\u043E\u043C\u0435\u0440 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B \u0438\u0437 nextCursor.`);
2679
+ const page = query.cursor === void 0 ? 1 : Number(query.cursor);
2680
+ const perPage = Math.min(query.limit, PAGE_SIZE_MAX3);
2681
+ const params = new URLSearchParams({ page: String(page), per_page: String(perPage) });
2682
+ if (query.search !== void 0 && !query.search.includes("/")) params.set("q", query.search);
2683
+ const { body, headers: responseHeaders } = await requestJson(`${repo}/branches?${params.toString()}`, { fetch, origin, headers, accept: GITVERSE_ACCEPT, missing });
2684
+ const rows = Array.isArray(body) ? body : [];
2685
+ const search = query.search;
2686
+ const branches = rows.map(branchOf2).filter((branch) => search === void 0 || containsText([branch.name], search));
2687
+ return { items: branches, ...nextPageCursor({ headers: responseHeaders, page, perPage, count: rows.length }) };
2688
+ }
2689
+ };
2690
+ }
2691
+ function repoOf2(raw, target, web) {
2692
+ const item = isRecord(raw) ? raw : {};
2693
+ const defaultBranch = text(item.default_branch);
2694
+ const description = text(item.description);
2695
+ const visibility = visibilityOf2(item.visibility) ?? (typeof item.private === "boolean" ? item.private ? "private" : "public" : void 0);
2696
+ const url = text(item.html_url) ?? web;
2697
+ return {
2698
+ name: text(item.name) ?? target.slice(target.indexOf("/") + 1),
2699
+ ...defaultBranch === void 0 ? {} : { defaultBranch },
2700
+ ...description === void 0 ? {} : { description },
2701
+ ...visibility === void 0 ? {} : { visibility },
2702
+ links: url === void 0 ? [] : [{ url, title: "GitVerse" }]
2703
+ };
2704
+ }
2705
+ function branchOf2(raw) {
2706
+ const item = isRecord(raw) ? raw : {};
2707
+ const sha = isRecord(item.commit) ? text(item.commit.sha) ?? text(item.commit.id) : void 0;
2708
+ return {
2709
+ name: text(item.name) ?? "?",
2710
+ ...sha === void 0 || !SHA6.test(sha) ? {} : { sha },
2711
+ ...typeof item.protected === "boolean" ? { protected: item.protected } : {}
2712
+ };
2713
+ }
2714
+ function visibilityOf2(value) {
2715
+ const parsed = repoVisibilitySchema.safeParse(value);
2716
+ return parsed.success ? parsed.data : void 0;
2717
+ }
2718
+
2719
+ // src/tracker/gitverse.lib.ts
2720
+ var TARGET10 = /^[\w.-]+\/[\w.-]+$/;
2721
+ var NUMBER4 = /^[1-9]\d{0,14}$/;
2722
+ var OFFSET2 = /^\d{1,9}$/;
2723
+ var LIST_SUBJECT4 = "\u0417\u0430\u0434\u0430\u0447\u0438 GitVerse";
2724
+ var COMMENTS_SUBJECT4 = "\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 GitVerse";
2725
+ var PAGE_SIZE_MAX4 = 100;
2726
+ var LABEL_PAGES_MAX = 10;
2727
+ var SORT_FIELDS_UPSTREAM2 = { created: "created", updated: "updated" };
2728
+ var CAPABILITIES7 = {
2729
+ list: { filters: ISSUE_FILTERS, clientSide: ["status", "labels", "assignee", "search", "createdAfter", "createdBefore"], sorts: ["created", "updated"] },
2730
+ comments: { filters: COMMENT_FILTERS, clientSide: COMMENT_FILTERS, order: ["asc", "desc"] }
2731
+ };
2732
+ function gitverseTracker() {
2733
+ return {
2734
+ descriptor: {
2735
+ tracker: "gitverse",
2736
+ title: "GitVerse",
2737
+ fields: [
2738
+ { name: "target", required: true, secret: false },
2739
+ { name: "token", required: true, secret: true },
2740
+ { name: "url", required: false, secret: false }
2741
+ ],
2742
+ features: { create: true, update: true, comment: true, priority: false, labels: true, assignee: true, statuses: ["open", "done"] }
2743
+ },
2744
+ issues: CAPABILITIES7,
2745
+ create: (context) => createGitverseAdapter(context),
2746
+ origin: (connection) => new URL(baseUrl(connection.url, GITVERSE_API)).origin,
2747
+ pulls: gitversePulls(),
2748
+ ci: gitverseCi(),
2749
+ repo: gitverseRepo()
2750
+ };
2751
+ }
2752
+ function createGitverseAdapter({ connection, fetch }) {
2753
+ const target = requireTarget(connection.target, "owner/repo \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F GitVerse");
2754
+ if (!TARGET10.test(target)) throw new TrackerError("invalid_request", "target GitVerse \u2014 owner/repo.");
2755
+ const api = baseUrl(connection.url, GITVERSE_API);
2756
+ const origin = new URL(api).origin;
2757
+ const repo = `${api}/repos/${target}`;
2758
+ const root = `${repo}/issues`;
2759
+ const headers = connection.token === void 0 || connection.token === "" ? {} : { authorization: `Bearer ${connection.token}` };
2760
+ const web = connection.url === void 0 ? `${GITVERSE_WEB}/${target}` : void 0;
2761
+ const call = (extra = {}) => ({ fetch, origin, headers, accept: GITVERSE_ACCEPT, missingIsIssue: true, ...extra });
2762
+ const number = (key) => {
2763
+ if (!NUMBER4.test(key)) throw new TrackerError("unknown_issue", `\u0412 GitVerse \u043D\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 ${key}.`);
2764
+ return key;
2765
+ };
2766
+ const toIssue2 = (raw) => issueOf2(raw, web);
2767
+ const rawOf = async (key) => {
2768
+ const { body } = await requestJson(`${root}/${number(key)}`, call());
2769
+ return isRecord(body) ? body : {};
2770
+ };
2771
+ const issueRawOf = async (key) => {
2772
+ const raw = await rawOf(key);
2773
+ if (isRecord(raw.pull_request)) throw new TrackerError("unknown_issue", `\u0412 GitVerse ${key} \u2014 pull request, \u0430 \u043D\u0435 \u0437\u0430\u0434\u0430\u0447\u0430.`);
2774
+ return raw;
2775
+ };
2776
+ const repoLabels = async () => {
2777
+ const names = [];
2778
+ for (let page = 1; page <= LABEL_PAGES_MAX; page += 1) {
2779
+ const { body } = await requestJson(`${repo}/labels?page=${String(page)}`, call({ missingIsIssue: false, missing: { failure: "not_found", message: `\u0412 GitVerse \u043D\u0435\u0442 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F ${target}.` } }));
2780
+ const fresh = (Array.isArray(body) ? body : []).map((label) => isRecord(label) ? text(label.name) : void 0).filter((name) => name !== void 0 && !names.includes(name));
2781
+ if (fresh.length === 0) break;
2782
+ names.push(...fresh);
2783
+ }
2784
+ return names;
2785
+ };
2786
+ const knownLabels = async (wanted) => {
2787
+ if (wanted.length === 0) return [];
2788
+ const known = await repoLabels();
2789
+ const resolved = [];
2790
+ const unknown = [];
2791
+ for (const name of new Set(wanted)) {
2792
+ const found = known.find((label) => label === name) ?? known.find((label) => label.toLowerCase() === name.toLowerCase());
2793
+ if (found === void 0) unknown.push(name);
2794
+ else if (!resolved.includes(found)) resolved.push(found);
2795
+ }
2796
+ if (unknown.length > 0) {
2797
+ throw new TrackerError("invalid_request", `\u0412 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 GitVerse ${target} \u043D\u0435\u0442 \u043C\u0435\u0442\u043E\u043A: ${unknown.join(", ")}. GitVerse \u043C\u043E\u043B\u0447\u0430 \u043E\u0442\u0431\u0440\u043E\u0441\u0438\u043B \u0431\u044B \u0438\u0445, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u0437\u0430\u043F\u0440\u043E\u0441 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D.`);
2798
+ }
2799
+ return resolved;
2800
+ };
2801
+ const moveToColumn = async (key, raw, state) => {
2802
+ const stuck = () => new TrackerError(
2803
+ "conflict",
2804
+ `GitVerse \u043D\u0435 \u0441\u043C\u0435\u043D\u0438\u043B \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 ${key} \u043D\u0430 ${state}: \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0441\u043B\u0435\u0434\u0443\u0435\u0442 \u0437\u0430 \u043A\u043E\u043B\u043E\u043D\u043A\u043E\u0439 \u0435\u0451 \u043F\u0440\u043E\u0435\u043A\u0442\u0430. \u041E\u0441\u0442\u0430\u043B\u044C\u043D\u044B\u0435 \u043F\u043E\u043B\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u044B; \u0441\u043C\u0435\u043D\u0438\u0442\u0435 \u0441\u0442\u0430\u0442\u0443\u0441 \u0432 \u0432\u0435\u0431-\u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435${web === void 0 ? "" : `: ${web}/issues/${key}`}.`
2805
+ );
2806
+ const project = projectIdOf(raw);
2807
+ if (project === void 0) throw stuck();
2808
+ const { body } = await requestJson(`${repo}/projects/${String(project)}`, call({ missingIsIssue: false, missing: { failure: "conflict", message: stuck().message } }));
2809
+ const columns = (isRecord(body) && Array.isArray(body.columns) ? body.columns : []).filter(isRecord);
2810
+ const done = (column2) => GITVERSE_DONE_COLUMN.test((text(column2.title) ?? text(column2.name) ?? "").trim());
2811
+ const column = state === "closed" ? columns.find(done) : columns.find((item) => item.default === true && !done(item)) ?? columns.find((item) => !done(item));
2812
+ const id = column === void 0 ? void 0 : column.id;
2813
+ if (typeof id !== "number" && typeof id !== "string") throw stuck();
2814
+ await requestJson(`${root}/${key}`, call({ method: "PATCH", body: { project_id: project, project_status_id: id } }));
2815
+ const reread = await rawOf(key);
2816
+ if (text(reread.state) !== state) throw stuck();
2817
+ return reread;
2818
+ };
2819
+ return {
2820
+ list: async (query) => {
2821
+ assertListQuery(LIST_SUBJECT4, CAPABILITIES7.list, query, ISSUE_FILTERS);
2822
+ const page = pageOf8(query.cursor, LIST_SUBJECT4);
2823
+ const perPage = Math.min(query.limit, PAGE_SIZE_MAX4);
2824
+ const params = new URLSearchParams({ state: stateFilter4(query.status), page: String(page), per_page: String(perPage) });
2825
+ const sent = (query.labels ?? []).filter((label) => !label.includes("/") && !label.includes(","));
2826
+ if (sent.length > 0) params.set("labels", sent.join(","));
2827
+ if (typeof query.assignee === "string") params.set("assigned_by", query.assignee);
2828
+ if (query.author !== void 0) params.set("created_by", query.author);
2829
+ if (query.search !== void 0 && !query.search.includes("/")) params.set("q", query.search);
2830
+ if (query.updatedAfter !== void 0) params.set("since", isoTime(query.updatedAfter));
2831
+ if (query.updatedBefore !== void 0) params.set("before", isoTime(query.updatedBefore));
2832
+ if (query.sort !== void 0) {
2833
+ params.set("sort", sortFieldOf2(query.sort.field));
2834
+ params.set("direction", query.sort.direction ?? "desc");
2835
+ }
2836
+ const { body, headers: responseHeaders } = await requestJson(`${root}?${params.toString()}`, call({ missingIsIssue: false }));
2837
+ const rows = Array.isArray(body) ? body : [];
2838
+ const issues = rows.filter((raw) => !(isRecord(raw) && isRecord(raw.pull_request))).map(toIssue2).filter((issue) => matchesPage3(issue, query));
2839
+ return { items: issues, ...nextPageCursor({ headers: responseHeaders, page, perPage, count: rows.length }) };
2840
+ },
2841
+ get: async (key) => toIssue2(await issueRawOf(key)),
2842
+ create: async (input) => {
2843
+ if (input.priority !== void 0) throw new TrackerError("unsupported", "\u0417\u0430\u0434\u0430\u0447\u0438 GitVerse: \u043F\u0440\u0438\u043E\u0440\u0438\u0442\u0435\u0442\u0430 \u0443 \u0437\u0430\u0434\u0430\u0447\u0438 \u043D\u0435\u0442.");
2844
+ const labels = await knownLabels(input.labels ?? []);
2845
+ const body = {
2846
+ title: input.title,
2847
+ ...input.description === void 0 ? {} : { body: input.description },
2848
+ ...labels.length === 0 ? {} : { labels },
2849
+ ...input.assignee === void 0 ? {} : { assignees: [input.assignee] }
2850
+ };
2851
+ return toIssue2((await requestJson(root, call({ method: "POST", body, missingIsIssue: false }))).body);
2852
+ },
2853
+ update: async (key, patch) => {
2854
+ const id = number(key);
2855
+ if (patch.priority !== void 0) throw new TrackerError("unsupported", "\u0417\u0430\u0434\u0430\u0447\u0438 GitVerse: \u043F\u0440\u0438\u043E\u0440\u0438\u0442\u0435\u0442\u0430 \u0443 \u0437\u0430\u0434\u0430\u0447\u0438 \u043D\u0435\u0442.");
2856
+ const state = patch.status === void 0 ? void 0 : stateNameOf(patch.status);
2857
+ const before = await issueRawOf(id);
2858
+ const labels = patch.labels === void 0 ? void 0 : await knownLabels(patch.labels);
2859
+ const body = {
2860
+ ...patch.title === void 0 ? {} : { title: patch.title },
2861
+ ...patch.description === void 0 ? {} : { body: patch.description },
2862
+ ...labels === void 0 ? {} : { labels },
2863
+ ...patch.assignee === void 0 ? {} : { assignees: patch.assignee === null ? [] : [patch.assignee] },
2864
+ ...state === void 0 ? {} : { state }
2865
+ };
2866
+ if (Object.keys(body).length === 0) return toIssue2(before);
2867
+ const { body: updated } = await requestJson(`${root}/${id}`, call({ method: "PATCH", body }));
2868
+ let raw = isRecord(updated) ? updated : await rawOf(id);
2869
+ if (state !== void 0 && text(raw.state) !== state) raw = await moveToColumn(id, raw, state);
2870
+ return toIssue2(raw);
2871
+ },
2872
+ comments: async (key, query) => {
2873
+ assertCommentQuery(COMMENTS_SUBJECT4, CAPABILITIES7.comments, query);
2874
+ const offset = offsetOf3(query.cursor);
2875
+ const id = number(key);
2876
+ await issueRawOf(id);
2877
+ const { body } = await requestJson(`${root}/${id}/comments`, call());
2878
+ const all = (Array.isArray(body) ? body : []).map(commentOf4);
2879
+ const visible = all.filter((comment) => query.updatedAfter === void 0 || inTimeRange(comment.updatedAt, query.updatedAfter, void 0)).sort((left, right) => (left.comment.createdAt ?? 0) - (right.comment.createdAt ?? 0));
2880
+ if (query.order === "desc") visible.reverse();
2881
+ const end = offset + query.limit;
2882
+ return { items: visible.slice(offset, end).map((item) => item.comment), ...end < visible.length ? { nextCursor: String(end) } : {} };
2883
+ },
2884
+ comment: async (key, body) => {
2885
+ const id = number(key);
2886
+ await issueRawOf(id);
2887
+ return commentOf4((await requestJson(`${root}/${id}/comments`, call({ method: "POST", body: { body } }))).body).comment;
2888
+ }
2889
+ };
2890
+ }
2891
+ function pageOf8(cursor, subject) {
2892
+ if (cursor === void 0) return 1;
2893
+ if (!/^[1-9]\d{0,8}$/.test(cursor)) throw new TrackerError("invalid_request", `${subject}: \u043A\u0443\u0440\u0441\u043E\u0440 \u2014 \u043D\u043E\u043C\u0435\u0440 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B \u0438\u0437 nextCursor.`);
2894
+ return Number(cursor);
2895
+ }
2896
+ function offsetOf3(cursor) {
2897
+ if (cursor === void 0) return 0;
2898
+ if (!OFFSET2.test(cursor)) throw new TrackerError("invalid_request", `${COMMENTS_SUBJECT4}: \u043A\u0443\u0440\u0441\u043E\u0440 \u2014 nextCursor \u043F\u0440\u043E\u0448\u043B\u043E\u0439 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B.`);
2899
+ return Number(cursor);
2900
+ }
2901
+ function stateFilter4(status) {
2902
+ if (status === void 0 || status.length === 0) return "all";
2903
+ if (status.every((item) => item === "open")) return "open";
2904
+ if (status.every((item) => item === "done")) return "closed";
2905
+ return "all";
2906
+ }
2907
+ function stateNameOf(status) {
2908
+ const name = GITVERSE_ISSUE_STATE_NAMES[status];
2909
+ if (name === void 0) throw new TrackerError("unsupported", `\u0417\u0430\u0434\u0430\u0447\u0438 GitVerse: \u0441\u0442\u0430\u0442\u0443\u0441\u0430 ${status} \u043D\u0435\u0442 \u2014 \u0442\u043E\u043B\u044C\u043A\u043E open \u0438 done.`);
2910
+ return name;
2911
+ }
2912
+ function sortFieldOf2(field) {
2913
+ const upstream = SORT_FIELDS_UPSTREAM2[field];
2914
+ if (upstream === void 0) throw new TrackerError("unsupported", `${LIST_SUBJECT4}: \u0441\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u043A\u0430 \u043F\u043E ${field} \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F.`);
2915
+ return upstream;
2916
+ }
2917
+ function matchesPage3(issue, query) {
2918
+ if (query.status !== void 0 && query.status.length > 0 && !query.status.includes(issue.status.category)) return false;
2919
+ const labels = issue.labels.map((label) => label.toLowerCase());
2920
+ if (query.labels !== void 0 && !query.labels.every((label) => labels.includes(label.toLowerCase()))) return false;
2921
+ if (query.assignee === null && issue.assignee !== void 0) return false;
2922
+ if (query.search !== void 0 && query.search.includes("/") && !containsText([issue.title, issue.description], query.search)) return false;
2923
+ return inTimeRange(issue.createdAt, query.createdAfter, query.createdBefore);
2924
+ }
2925
+ function projectIdOf(raw) {
2926
+ const value = raw.project_id ?? (isRecord(raw.project) ? raw.project.id : void 0);
2927
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0 || typeof value === "string" && /^\d{1,15}$/.test(value) ? value : void 0;
2928
+ }
2929
+ function loginOf2(value) {
2930
+ return isRecord(value) ? text(value.login) ?? text(value.username) : void 0;
2931
+ }
2932
+ function issueOf2(raw, web) {
2933
+ const item = isRecord(raw) ? raw : {};
2934
+ const key = typeof item.number === "number" && Number.isSafeInteger(item.number) ? String(item.number) : text(item.number) ?? "?";
2935
+ const state = text(item.state) ?? "open";
2936
+ const description = text(item.body);
2937
+ const assignee = (Array.isArray(item.assignees) ? item.assignees.map(loginOf2).find((login) => login !== void 0) : void 0) ?? loginOf2(item.assignee);
2938
+ const url = text(item.html_url) ?? (web === void 0 ? void 0 : `${web}/issues/${key}`);
2939
+ const createdAt = toTimestamp(item.created_at);
2940
+ const updatedAt = toTimestamp(item.updated_at);
2941
+ return {
2942
+ key,
2943
+ title: text(item.title) ?? key,
2944
+ ...description === void 0 ? {} : { description },
2945
+ status: { category: lookup(GITVERSE_ISSUE_STATES, state, "open"), name: state },
2946
+ labels: Array.isArray(item.labels) ? stringList(item.labels.map((label) => isRecord(label) ? label.name : label)) : [],
2947
+ ...assignee === void 0 ? {} : { assignee },
2948
+ links: url === void 0 ? [] : [{ url, title: "GitVerse" }],
2949
+ ...createdAt === void 0 ? {} : { createdAt },
2950
+ ...updatedAt === void 0 ? {} : { updatedAt }
2951
+ };
2952
+ }
2953
+ function commentOf4(raw) {
2954
+ const item = isRecord(raw) ? raw : {};
2955
+ const id = typeof item.id === "number" && Number.isSafeInteger(item.id) ? String(item.id) : text(item.id) ?? "?";
2956
+ const author = loginOf2(item.user) ?? loginOf2(item.author);
2957
+ const createdAt = toTimestamp(item.created_at);
2958
+ return {
2959
+ comment: { id, ...author === void 0 ? {} : { author }, body: typeof item.body === "string" ? item.body : "", ...createdAt === void 0 ? {} : { createdAt } },
2960
+ updatedAt: toTimestamp(item.updated_at) ?? createdAt
2961
+ };
2962
+ }
2963
+
2140
2964
  // src/tracker/gitlab-pull.lib.ts
2141
2965
  var DEFAULT_URL2 = "https://gitlab.com";
2142
- var TARGET7 = /^(\d{1,15}|[\w.-]+(\/[\w.-]+)+)$/;
2966
+ var TARGET11 = /^(\d{1,15}|[\w.-]+(\/[\w.-]+)+)$/;
2143
2967
  var IID = /^[1-9]\d{0,19}$/;
2144
- var SHA4 = /^[0-9a-f]{7,64}$/;
2145
- var PULL_CAPABILITIES2 = {
2968
+ var SHA7 = /^[0-9a-f]{7,64}$/;
2969
+ var PULL_CAPABILITIES3 = {
2146
2970
  list: { filters: pullRequestFilterSchema.options, clientSide: ["status"], sorts: ["created", "updated", "title"] },
2147
2971
  comments: { filters: COMMENT_FILTERS, clientSide: COMMENT_FILTERS, order: ["asc", "desc"] },
2148
2972
  create: true,
@@ -2156,13 +2980,13 @@ var PULL_CAPABILITIES2 = {
2156
2980
  var ORDER_BY = { created: "created_at", updated: "updated_at", title: "title" };
2157
2981
  function gitlabPulls() {
2158
2982
  return {
2159
- capabilities: PULL_CAPABILITIES2,
2983
+ capabilities: PULL_CAPABILITIES3,
2160
2984
  create: (context) => createGitlabPullAdapter(context)
2161
2985
  };
2162
2986
  }
2163
2987
  function createGitlabPullAdapter({ connection, fetch }) {
2164
2988
  const target = requireTarget(connection.target, "\u043F\u0443\u0442\u044C \u043F\u0440\u043E\u0435\u043A\u0442\u0430 GitLab, group/project, \u0438\u043B\u0438 \u0435\u0433\u043E id");
2165
- if (!TARGET7.test(target)) throw new TrackerError("invalid_request", "target GitLab \u2014 group/project \u0438\u043B\u0438 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 id.");
2989
+ if (!TARGET11.test(target)) throw new TrackerError("invalid_request", "target GitLab \u2014 group/project \u0438\u043B\u0438 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 id.");
2166
2990
  const site = baseUrl(connection.url, DEFAULT_URL2);
2167
2991
  const origin = new URL(site).origin;
2168
2992
  const root = `${site}/api/v4/projects/${encodeURIComponent(target)}/merge_requests`;
@@ -2171,14 +2995,14 @@ function createGitlabPullAdapter({ connection, fetch }) {
2171
2995
  if (!IID.test(key)) throw new TrackerError("not_found", `\u0412 GitLab \u043D\u0435\u0442 MR ${key}.`);
2172
2996
  return key;
2173
2997
  };
2174
- const currentOf = async (key) => pullOf2((await requestJson(`${root}/${iid(key)}`, { fetch, origin, headers, missing: { failure: "not_found", message: `\u0412 GitLab \u043D\u0435\u0442 MR ${key}.` } })).body);
2998
+ const currentOf = async (key) => pullOf3((await requestJson(`${root}/${iid(key)}`, { fetch, origin, headers, missing: { failure: "not_found", message: `\u0412 GitLab \u043D\u0435\u0442 MR ${key}.` } })).body);
2175
2999
  return {
2176
3000
  list: async (query) => {
2177
- assertListQuery("PR GitLab", PULL_CAPABILITIES2.list, query, pullRequestFilterSchema.options);
3001
+ assertListQuery("PR GitLab", PULL_CAPABILITIES3.list, query, pullRequestFilterSchema.options);
2178
3002
  const status = usedStatuses2(query.status);
2179
- const page = pageOf6(query.cursor);
3003
+ const page = pageOf9(query.cursor);
2180
3004
  const params = new URLSearchParams({
2181
- state: stateFilter3(status),
3005
+ state: stateFilter5(status),
2182
3006
  per_page: String(query.limit),
2183
3007
  page: String(page),
2184
3008
  order_by: query.sort === void 0 ? "created_at" : ORDER_BY[query.sort.field] ?? "created_at",
@@ -2198,7 +3022,7 @@ function createGitlabPullAdapter({ connection, fetch }) {
2198
3022
  if (query.updatedBefore !== void 0) params.set("updated_before", isoTime(query.updatedBefore));
2199
3023
  const { body, headers: responseHeaders } = await requestJson(`${root}?${params.toString()}`, { fetch, origin, headers });
2200
3024
  const next = responseHeaders.get("x-next-page");
2201
- const items = (Array.isArray(body) ? body : []).map(pullOf2);
3025
+ const items = (Array.isArray(body) ? body : []).map(pullOf3);
2202
3026
  return {
2203
3027
  items: status === void 0 ? items : items.filter((pr) => status.includes(pr.status.category) === true),
2204
3028
  ...next === null || next === "" ? {} : { nextCursor: next }
@@ -2212,7 +3036,7 @@ function createGitlabPullAdapter({ connection, fetch }) {
2212
3036
  title: (input.draft === true ? GITLAB_DRAFT_PREFIX : "") + input.title,
2213
3037
  ...input.description === void 0 ? {} : { description: input.description }
2214
3038
  };
2215
- return pullOf2((await requestJson(root, { fetch, origin, headers, method: "POST", body, conflictOn: [409] })).body);
3039
+ return pullOf3((await requestJson(root, { fetch, origin, headers, method: "POST", body, conflictOn: [409] })).body);
2216
3040
  },
2217
3041
  update: async (key, patch) => {
2218
3042
  const current = patch.status === void 0 ? void 0 : await currentOf(key);
@@ -2220,11 +3044,11 @@ function createGitlabPullAdapter({ connection, fetch }) {
2220
3044
  throw new TrackerError("unsupported", "PR GitLab: \u043F\u0435\u0440\u0435\u0432\u043E\u0434 \u0437\u0430\u043A\u0440\u044B\u0442\u043E\u0433\u043E MR \u0432 \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D.");
2221
3045
  }
2222
3046
  const body = patchBody3(patch, current);
2223
- if (Object.keys(body).length === 0) return current ?? pullOf2((await requestJson(`${root}/${iid(key)}`, { fetch, origin, headers, missing: { failure: "not_found", message: `\u0412 GitLab \u043D\u0435\u0442 MR ${key}.` } })).body);
2224
- return pullOf2((await requestJson(`${root}/${iid(key)}`, { fetch, origin, headers, method: "PUT", body, missing: { failure: "not_found", message: `\u0412 GitLab \u043D\u0435\u0442 MR ${key}.` } })).body);
3047
+ if (Object.keys(body).length === 0) return current ?? pullOf3((await requestJson(`${root}/${iid(key)}`, { fetch, origin, headers, missing: { failure: "not_found", message: `\u0412 GitLab \u043D\u0435\u0442 MR ${key}.` } })).body);
3048
+ return pullOf3((await requestJson(`${root}/${iid(key)}`, { fetch, origin, headers, method: "PUT", body, missing: { failure: "not_found", message: `\u0412 GitLab \u043D\u0435\u0442 MR ${key}.` } })).body);
2225
3049
  },
2226
3050
  merge: async (key, input) => {
2227
- if (input.method !== void 0 && !PULL_CAPABILITIES2.merge.includes(input.method)) {
3051
+ if (input.method !== void 0 && !PULL_CAPABILITIES3.merge.includes(input.method)) {
2228
3052
  throw new TrackerError("unsupported", `PR GitLab: \u0441\u043B\u0438\u044F\u043D\u0438\u0435 \u0441\u043F\u043E\u0441\u043E\u0431\u043E\u043C ${input.method} \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F.`);
2229
3053
  }
2230
3054
  const body = {
@@ -2234,25 +3058,25 @@ function createGitlabPullAdapter({ connection, fetch }) {
2234
3058
  ...input.deleteSource === void 0 ? {} : { should_remove_source_branch: input.deleteSource },
2235
3059
  ...input.headSha === void 0 ? {} : { sha: input.headSha }
2236
3060
  };
2237
- return pullOf2((await requestJson(`${root}/${iid(key)}/merge`, { fetch, origin, headers, method: "PUT", body, conflictOn: [405, 409, 422], missing: { failure: "not_found", message: `\u0412 GitLab \u043D\u0435\u0442 MR ${key}.` } })).body);
3061
+ return pullOf3((await requestJson(`${root}/${iid(key)}/merge`, { fetch, origin, headers, method: "PUT", body, conflictOn: [405, 409, 422], missing: { failure: "not_found", message: `\u0412 GitLab \u043D\u0435\u0442 MR ${key}.` } })).body);
2238
3062
  },
2239
3063
  comments: async (key, query) => {
2240
- assertCommentQuery("\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 GitLab", PULL_CAPABILITIES2.comments, query);
2241
- const page = pageOf6(query.cursor);
3064
+ assertCommentQuery("\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 GitLab", PULL_CAPABILITIES3.comments, query);
3065
+ const page = pageOf9(query.cursor);
2242
3066
  const params = new URLSearchParams({ sort: query.order ?? "asc", order_by: "created_at", per_page: String(query.limit), page: String(page) });
2243
3067
  const { body, headers: responseHeaders } = await requestJson(`${root}/${iid(key)}/notes?${params.toString()}`, { fetch, origin, headers, missing: { failure: "not_found", message: `\u0412 GitLab \u043D\u0435\u0442 MR ${key}.` } });
2244
3068
  const next = responseHeaders.get("x-next-page");
2245
- const items = (Array.isArray(body) ? body : []).filter((note) => isRecord(note) && note.system !== true && inTimeRange(toTimestamp(note.updated_at), query.updatedAfter, void 0)).map(commentOf3);
3069
+ const items = (Array.isArray(body) ? body : []).filter((note) => isRecord(note) && note.system !== true && inTimeRange(toTimestamp(note.updated_at), query.updatedAfter, void 0)).map(commentOf5);
2246
3070
  return { items, ...next === null || next === "" ? {} : { nextCursor: next } };
2247
3071
  },
2248
- comment: async (key, body) => commentOf3((await requestJson(`${root}/${iid(key)}/notes`, { fetch, origin, headers, method: "POST", body: { body }, missing: { failure: "not_found", message: `\u0412 GitLab \u043D\u0435\u0442 MR ${key}.` } })).body)
3072
+ comment: async (key, body) => commentOf5((await requestJson(`${root}/${iid(key)}/notes`, { fetch, origin, headers, method: "POST", body: { body }, missing: { failure: "not_found", message: `\u0412 GitLab \u043D\u0435\u0442 MR ${key}.` } })).body)
2249
3073
  };
2250
3074
  }
2251
- function pageOf6(cursor) {
3075
+ function pageOf9(cursor) {
2252
3076
  const page = cursor === void 0 ? 1 : Number(cursor);
2253
3077
  return Number.isInteger(page) && page >= 1 ? page : 1;
2254
3078
  }
2255
- function stateFilter3(status) {
3079
+ function stateFilter5(status) {
2256
3080
  if (status === void 0) return "all";
2257
3081
  if (status.every((item) => item === "draft" || item === "open")) return "opened";
2258
3082
  if (status.every((item) => item === "merged")) return "merged";
@@ -2294,7 +3118,7 @@ function usernames(value) {
2294
3118
  const names = value.map((item) => isRecord(item) ? text(item.username) : void 0);
2295
3119
  return [...new Set(names.filter((name) => name !== void 0 && name !== ""))];
2296
3120
  }
2297
- function pullOf2(raw) {
3121
+ function pullOf3(raw) {
2298
3122
  const item = isRecord(raw) ? raw : {};
2299
3123
  const state = text(item.state) ?? "opened";
2300
3124
  const draft = item.draft === true;
@@ -2316,7 +3140,7 @@ function pullOf2(raw) {
2316
3140
  ...author === void 0 ? {} : { author },
2317
3141
  source: text(item.source_branch) ?? "?",
2318
3142
  target: text(item.target_branch) ?? "?",
2319
- ...sha !== void 0 && SHA4.test(sha) ? { headSha: sha } : {},
3143
+ ...sha !== void 0 && SHA7.test(sha) ? { headSha: sha } : {},
2320
3144
  labels: labelNames(item.labels),
2321
3145
  reviewers: usernames(item.reviewers),
2322
3146
  links: url === void 0 ? [] : [{ url, title: "GitLab" }],
@@ -2326,7 +3150,7 @@ function pullOf2(raw) {
2326
3150
  ...closedAt === void 0 ? {} : { closedAt }
2327
3151
  };
2328
3152
  }
2329
- function commentOf3(raw) {
3153
+ function commentOf5(raw) {
2330
3154
  const item = isRecord(raw) ? raw : {};
2331
3155
  const author = isRecord(item.author) ? text(item.author.username) : void 0;
2332
3156
  const createdAt = toTimestamp(item.created_at);
@@ -2335,37 +3159,37 @@ function commentOf3(raw) {
2335
3159
 
2336
3160
  // src/tracker/gitlab-repo.lib.ts
2337
3161
  var DEFAULT_URL3 = "https://gitlab.com";
2338
- var TARGET8 = /^(\d{1,15}|[\w.-]+(\/[\w.-]+)+)$/;
2339
- var SHA5 = /^[0-9a-f]{7,64}$/;
2340
- var BRANCHES_SUBJECT2 = "\u0412\u0435\u0442\u043A\u0438 GitLab";
2341
- var BRANCH_FILTERS2 = branchFilterSchema.options;
2342
- var CAPABILITIES5 = {
2343
- branches: { filters: BRANCH_FILTERS2, clientSide: [], sorts: [] }
3162
+ var TARGET12 = /^(\d{1,15}|[\w.-]+(\/[\w.-]+)+)$/;
3163
+ var SHA8 = /^[0-9a-f]{7,64}$/;
3164
+ var BRANCHES_SUBJECT3 = "\u0412\u0435\u0442\u043A\u0438 GitLab";
3165
+ var BRANCH_FILTERS3 = branchFilterSchema.options;
3166
+ var CAPABILITIES8 = {
3167
+ branches: { filters: BRANCH_FILTERS3, clientSide: [], sorts: [] }
2344
3168
  };
2345
3169
  function gitlabRepo() {
2346
- return { capabilities: CAPABILITIES5, create: (context) => createGitlabRepoAdapter(context) };
3170
+ return { capabilities: CAPABILITIES8, create: (context) => createGitlabRepoAdapter(context) };
2347
3171
  }
2348
3172
  function createGitlabRepoAdapter({ connection, fetch }) {
2349
3173
  const target = requireTarget(connection.target, "\u043F\u0443\u0442\u044C \u043F\u0440\u043E\u0435\u043A\u0442\u0430 GitLab, group/project, \u0438\u043B\u0438 \u0435\u0433\u043E id");
2350
- if (!TARGET8.test(target)) throw new TrackerError("invalid_request", "target GitLab \u2014 group/project \u0438\u043B\u0438 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 id.");
3174
+ if (!TARGET12.test(target)) throw new TrackerError("invalid_request", "target GitLab \u2014 group/project \u0438\u043B\u0438 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 id.");
2351
3175
  const site = baseUrl(connection.url, DEFAULT_URL3);
2352
3176
  const origin = new URL(site).origin;
2353
3177
  const project = `${site}/api/v4/projects/${encodeURIComponent(target)}`;
2354
3178
  const headers = connection.token === void 0 || connection.token === "" ? {} : { "private-token": connection.token };
2355
3179
  const missing = { failure: "not_found", message: `\u0412 GitLab \u043D\u0435\u0442 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 ${target}.` };
2356
3180
  return {
2357
- get: async () => repoOf2((await requestJson(project, { fetch, origin, headers, missing })).body, target),
3181
+ get: async () => repoOf3((await requestJson(project, { fetch, origin, headers, missing })).body, target),
2358
3182
  branches: async (query) => {
2359
- assertListQuery(BRANCHES_SUBJECT2, CAPABILITIES5.branches, query, BRANCH_FILTERS2);
2360
- const params = new URLSearchParams({ per_page: String(query.limit), page: String(pageOf7(query.cursor)) });
3183
+ assertListQuery(BRANCHES_SUBJECT3, CAPABILITIES8.branches, query, BRANCH_FILTERS3);
3184
+ const params = new URLSearchParams({ per_page: String(query.limit), page: String(pageOf10(query.cursor)) });
2361
3185
  if (query.search !== void 0) params.set("search", searchOf(query.search));
2362
3186
  const { body, headers: responseHeaders } = await requestJson(`${project}/repository/branches?${params.toString()}`, { fetch, origin, headers, missing });
2363
3187
  const next = responseHeaders.get("x-next-page");
2364
- return { items: (Array.isArray(body) ? body : []).map(branchOf2), ...next === null || next === "" ? {} : { nextCursor: next } };
3188
+ return { items: (Array.isArray(body) ? body : []).map(branchOf3), ...next === null || next === "" ? {} : { nextCursor: next } };
2365
3189
  }
2366
3190
  };
2367
3191
  }
2368
- function pageOf7(cursor) {
3192
+ function pageOf10(cursor) {
2369
3193
  const page = cursor === void 0 ? 1 : Number(cursor);
2370
3194
  return Number.isInteger(page) && page >= 1 ? page : 1;
2371
3195
  }
@@ -2373,11 +3197,11 @@ function searchOf(value) {
2373
3197
  const stripped = value.replace(/^\^/, "").replace(/\$$/, "");
2374
3198
  return stripped === "" ? value : stripped;
2375
3199
  }
2376
- function repoOf2(raw, target) {
3200
+ function repoOf3(raw, target) {
2377
3201
  const item = isRecord(raw) ? raw : {};
2378
3202
  const defaultBranch = text(item.default_branch);
2379
3203
  const description = text(item.description);
2380
- const visibility = visibilityOf2(item.visibility);
3204
+ const visibility = visibilityOf3(item.visibility);
2381
3205
  const url = text(item.web_url);
2382
3206
  return {
2383
3207
  name: text(item.name) ?? text(item.path_with_namespace) ?? target,
@@ -2387,24 +3211,24 @@ function repoOf2(raw, target) {
2387
3211
  links: url === void 0 ? [] : [{ url, title: "GitLab" }]
2388
3212
  };
2389
3213
  }
2390
- function branchOf2(raw) {
3214
+ function branchOf3(raw) {
2391
3215
  const item = isRecord(raw) ? raw : {};
2392
3216
  const sha = isRecord(item.commit) ? text(item.commit.id) : void 0;
2393
3217
  return {
2394
3218
  name: text(item.name) ?? "?",
2395
- ...sha === void 0 || !SHA5.test(sha) ? {} : { sha },
3219
+ ...sha === void 0 || !SHA8.test(sha) ? {} : { sha },
2396
3220
  ...typeof item.protected === "boolean" ? { protected: item.protected } : {},
2397
3221
  ...typeof item.default === "boolean" ? { isDefault: item.default } : {}
2398
3222
  };
2399
3223
  }
2400
- function visibilityOf2(value) {
3224
+ function visibilityOf3(value) {
2401
3225
  const parsed = repoVisibilitySchema.safeParse(value);
2402
3226
  return parsed.success ? parsed.data : void 0;
2403
3227
  }
2404
3228
 
2405
3229
  // src/tracker/gitlab.lib.ts
2406
3230
  var DEFAULT_URL4 = "https://gitlab.com";
2407
- var TARGET9 = /^(\d{1,15}|[\w.-]+(\/[\w.-]+)+)$/;
3231
+ var TARGET13 = /^(\d{1,15}|[\w.-]+(\/[\w.-]+)+)$/;
2408
3232
  var IID2 = /^[1-9]\d{0,14}$/;
2409
3233
  var ISSUE_CAPABILITIES = {
2410
3234
  list: { filters: ISSUE_FILTERS, clientSide: ["status"], sorts: ["created", "updated", "priority", "title"] },
@@ -2438,7 +3262,7 @@ function gitlabTracker() {
2438
3262
  }
2439
3263
  function createGitlabAdapter({ connection, fetch }) {
2440
3264
  const target = requireTarget(connection.target, "\u043F\u0443\u0442\u044C \u043F\u0440\u043E\u0435\u043A\u0442\u0430 GitLab, group/project, \u0438\u043B\u0438 \u0435\u0433\u043E id");
2441
- if (!TARGET9.test(target)) throw new TrackerError("invalid_request", "target GitLab \u2014 group/project \u0438\u043B\u0438 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 id.");
3265
+ if (!TARGET13.test(target)) throw new TrackerError("invalid_request", "target GitLab \u2014 group/project \u0438\u043B\u0438 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0439 id.");
2442
3266
  const site = baseUrl(connection.url, DEFAULT_URL4);
2443
3267
  const origin = new URL(site).origin;
2444
3268
  const root = `${site}/api/v4/projects/${encodeURIComponent(target)}/issues`;
@@ -2447,13 +3271,13 @@ function createGitlabAdapter({ connection, fetch }) {
2447
3271
  if (!IID2.test(key)) throw new TrackerError("unknown_issue", `\u0412 GitLab \u043D\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 ${key}.`);
2448
3272
  return key;
2449
3273
  };
2450
- const currentOf = async (key) => issueOf2((await requestJson(`${root}/${iid(key)}`, { fetch, origin, headers, missingIsIssue: true })).body);
3274
+ const currentOf = async (key) => issueOf3((await requestJson(`${root}/${iid(key)}`, { fetch, origin, headers, missingIsIssue: true })).body);
2451
3275
  return {
2452
3276
  list: async (query) => {
2453
3277
  assertListQuery("\u0417\u0430\u0434\u0430\u0447\u0438 GitLab", ISSUE_CAPABILITIES.list, query, ISSUE_FILTERS);
2454
- const page = pageOf8(query.cursor);
3278
+ const page = pageOf11(query.cursor);
2455
3279
  const params = new URLSearchParams({
2456
- state: stateFilter4(query.status),
3280
+ state: stateFilter6(query.status),
2457
3281
  per_page: String(query.limit),
2458
3282
  page: String(page),
2459
3283
  order_by: query.sort === void 0 ? "created_at" : ORDER_BY2[query.sort.field] ?? "created_at",
@@ -2471,7 +3295,7 @@ function createGitlabAdapter({ connection, fetch }) {
2471
3295
  if (query.updatedAfter !== void 0) params.set("updated_after", isoTime(query.updatedAfter));
2472
3296
  if (query.updatedBefore !== void 0) params.set("updated_before", isoTime(query.updatedBefore));
2473
3297
  const { body, headers: responseHeaders } = await requestJson(`${root}?${params.toString()}`, { fetch, origin, headers });
2474
- const items = (Array.isArray(body) ? body : []).map(issueOf2);
3298
+ const items = (Array.isArray(body) ? body : []).map(issueOf3);
2475
3299
  const next = responseHeaders.get("x-next-page");
2476
3300
  return {
2477
3301
  items: query.status === void 0 ? items : items.filter((issue) => query.status?.includes(issue.status.category) === true),
@@ -2482,29 +3306,29 @@ function createGitlabAdapter({ connection, fetch }) {
2482
3306
  create: async (input) => {
2483
3307
  const labels = composeLabels2(input.labels ?? [], input.priority, void 0);
2484
3308
  const body = { title: input.title, ...input.description === void 0 ? {} : { description: input.description }, ...labels.length === 0 ? {} : { labels: labels.join(",") } };
2485
- return issueOf2((await requestJson(root, { fetch, origin, headers, method: "POST", body })).body);
3309
+ return issueOf3((await requestJson(root, { fetch, origin, headers, method: "POST", body })).body);
2486
3310
  },
2487
3311
  update: async (key, patch) => {
2488
3312
  const current = patch.labels === void 0 && patch.priority === void 0 && patch.status === void 0 ? void 0 : await currentOf(key);
2489
- return issueOf2((await requestJson(`${root}/${iid(key)}`, { fetch, origin, headers, method: "PUT", body: patchBody4(patch, current), missingIsIssue: true })).body);
3313
+ return issueOf3((await requestJson(`${root}/${iid(key)}`, { fetch, origin, headers, method: "PUT", body: patchBody4(patch, current), missingIsIssue: true })).body);
2490
3314
  },
2491
3315
  comments: async (key, query) => {
2492
3316
  assertCommentQuery("\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 GitLab", ISSUE_CAPABILITIES.comments, query);
2493
- const page = pageOf8(query.cursor);
3317
+ const page = pageOf11(query.cursor);
2494
3318
  const params = new URLSearchParams({ sort: query.order ?? "asc", order_by: "created_at", per_page: String(query.limit), page: String(page) });
2495
3319
  const { body, headers: responseHeaders } = await requestJson(`${root}/${iid(key)}/notes?${params.toString()}`, { fetch, origin, headers, missingIsIssue: true });
2496
3320
  const next = responseHeaders.get("x-next-page");
2497
- const items = (Array.isArray(body) ? body : []).filter((note) => isRecord(note) && note.system !== true && inTimeRange(toTimestamp(note.updated_at), query.updatedAfter, void 0)).map(commentOf4);
3321
+ const items = (Array.isArray(body) ? body : []).filter((note) => isRecord(note) && note.system !== true && inTimeRange(toTimestamp(note.updated_at), query.updatedAfter, void 0)).map(commentOf6);
2498
3322
  return { items, ...next === null || next === "" ? {} : { nextCursor: next } };
2499
3323
  },
2500
- comment: async (key, body) => commentOf4((await requestJson(`${root}/${iid(key)}/notes`, { fetch, origin, headers, method: "POST", body: { body }, missingIsIssue: true })).body)
3324
+ comment: async (key, body) => commentOf6((await requestJson(`${root}/${iid(key)}/notes`, { fetch, origin, headers, method: "POST", body: { body }, missingIsIssue: true })).body)
2501
3325
  };
2502
3326
  }
2503
- function pageOf8(cursor) {
3327
+ function pageOf11(cursor) {
2504
3328
  const page = cursor === void 0 ? 1 : Number(cursor);
2505
3329
  return Number.isInteger(page) && page >= 1 ? page : 1;
2506
3330
  }
2507
- function stateFilter4(status) {
3331
+ function stateFilter6(status) {
2508
3332
  if (status === void 0) return "all";
2509
3333
  if (status.every((item) => item === "open" || item === "in_progress")) return "opened";
2510
3334
  if (status.every((item) => item === "done" || item === "declined")) return "closed";
@@ -2525,7 +3349,7 @@ function patchBody4(patch, current) {
2525
3349
  ...touchesLabels ? { labels: composeLabels2(patch.labels ?? current?.labels ?? [], patch.priority ?? current?.priority, status).join(",") } : {}
2526
3350
  };
2527
3351
  }
2528
- function issueOf2(raw) {
3352
+ function issueOf3(raw) {
2529
3353
  const item = isRecord(raw) ? raw : {};
2530
3354
  const names = stringList(item.labels);
2531
3355
  const closed = item.state === "closed";
@@ -2552,7 +3376,7 @@ function issueOf2(raw) {
2552
3376
  ...updatedAt === void 0 ? {} : { updatedAt }
2553
3377
  };
2554
3378
  }
2555
- function commentOf4(raw) {
3379
+ function commentOf6(raw) {
2556
3380
  const item = isRecord(raw) ? raw : {};
2557
3381
  const author = isRecord(item.author) ? text(item.author.username) : void 0;
2558
3382
  const createdAt = toTimestamp(item.created_at);
@@ -2603,7 +3427,7 @@ function createJiraAdapter({ connection, fetch }) {
2603
3427
  if (!ISSUE_KEY.test(value)) throw new TrackerError("unknown_issue", `\u0412 Jira \u043D\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 ${value}.`);
2604
3428
  return value;
2605
3429
  };
2606
- const toIssue2 = (raw) => issueOf3(raw, site);
3430
+ const toIssue2 = (raw) => issueOf4(raw, site);
2607
3431
  const load = async (value) => toIssue2((await requestJson(`${api}/issue/${key(value)}?fields=${FIELDS}`, { fetch, origin, headers, missingIsIssue: true })).body);
2608
3432
  return {
2609
3433
  list: async (query) => {
@@ -2662,9 +3486,9 @@ function createJiraAdapter({ connection, fetch }) {
2662
3486
  const items = raw.filter((item) => inTimeRange(toTimestamp(isRecord(item) ? item.updated : void 0), query.updatedAfter, void 0));
2663
3487
  const total = typeof record.total === "number" ? record.total : 0;
2664
3488
  const at = typeof record.startAt === "number" ? record.startAt : 0;
2665
- return { items: items.map(commentOf5), ...at + raw.length < total && raw.length > 0 ? { nextCursor: String(at + raw.length) } : {} };
3489
+ return { items: items.map(commentOf7), ...at + raw.length < total && raw.length > 0 ? { nextCursor: String(at + raw.length) } : {} };
2666
3490
  },
2667
- comment: async (value, body) => commentOf5((await requestJson(`${api}/issue/${key(value)}/comment`, { fetch, origin, headers, method: "POST", body: { body }, missingIsIssue: true })).body)
3491
+ comment: async (value, body) => commentOf7((await requestJson(`${api}/issue/${key(value)}/comment`, { fetch, origin, headers, method: "POST", body: { body }, missingIsIssue: true })).body)
2668
3492
  };
2669
3493
  }
2670
3494
  function listJql(project, query) {
@@ -2712,7 +3536,7 @@ async function transition(api, key, category, request) {
2712
3536
  if (id === void 0) throw new TrackerError("invalid_request", `\u0418\u0437 \u0442\u0435\u043A\u0443\u0449\u0435\u0433\u043E \u0441\u0442\u0430\u0442\u0443\u0441\u0430 Jira \u043D\u0435\u0442 \u043F\u0435\u0440\u0435\u0445\u043E\u0434\u0430 \u0432 \u043A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u044E ${category}.`);
2713
3537
  await requestJson(`${api}/issue/${key}/transitions`, { fetch, origin, headers, method: "POST", body: { transition: { id } }, missingIsIssue: true });
2714
3538
  }
2715
- function issueOf3(raw, site) {
3539
+ function issueOf4(raw, site) {
2716
3540
  const item = isRecord(raw) ? raw : {};
2717
3541
  const fields = isRecord(item.fields) ? item.fields : {};
2718
3542
  const status = isRecord(fields.status) ? fields.status : {};
@@ -2737,7 +3561,7 @@ function issueOf3(raw, site) {
2737
3561
  ...updatedAt === void 0 ? {} : { updatedAt }
2738
3562
  };
2739
3563
  }
2740
- function commentOf5(raw) {
3564
+ function commentOf7(raw) {
2741
3565
  const item = isRecord(raw) ? raw : {};
2742
3566
  const author = isRecord(item.author) ? text(item.author.displayName) : void 0;
2743
3567
  const createdAt = toTimestamp(item.created);
@@ -2826,15 +3650,15 @@ function toComment(row) {
2826
3650
 
2827
3651
  // src/tracker/sourcecraft-pull.lib.ts
2828
3652
  var DEFAULT_API6 = "https://api.sourcecraft.tech";
2829
- var TARGET10 = /^[\w.-]+\/[\w.-]+$/;
3653
+ var TARGET14 = /^[\w.-]+\/[\w.-]+$/;
2830
3654
  var SLUG = /^[\w-]{1,100}$/;
2831
- var SHA6 = /^[0-9a-f]{7,64}$/;
3655
+ var SHA9 = /^[0-9a-f]{7,64}$/;
2832
3656
  var PULL_SORT_FIELDS_UPSTREAM = {
2833
3657
  created: "created_at",
2834
3658
  updated: "updated_at",
2835
3659
  title: "title"
2836
3660
  };
2837
- var PULL_CAPABILITIES3 = {
3661
+ var PULL_CAPABILITIES4 = {
2838
3662
  list: {
2839
3663
  filters: pullRequestFilterSchema.options,
2840
3664
  clientSide: ["labels", "search", "createdAfter", "createdBefore", "updatedAfter", "updatedBefore"],
@@ -2850,11 +3674,11 @@ var PULL_CAPABILITIES3 = {
2850
3674
  deleteSourceOnMerge: true
2851
3675
  };
2852
3676
  function sourcecraftPulls() {
2853
- return { capabilities: PULL_CAPABILITIES3, create: (context) => createPullAdapter(context) };
3677
+ return { capabilities: PULL_CAPABILITIES4, create: (context) => createPullAdapter2(context) };
2854
3678
  }
2855
- function createPullAdapter({ connection, fetch }) {
3679
+ function createPullAdapter2({ connection, fetch }) {
2856
3680
  const target = requireTarget(connection.target, "org/repo \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F SourceCraft");
2857
- if (!TARGET10.test(target)) throw new TrackerError("invalid_request", "target SourceCraft \u2014 org/repo.");
3681
+ if (!TARGET14.test(target)) throw new TrackerError("invalid_request", "target SourceCraft \u2014 org/repo.");
2858
3682
  const api = baseUrl(connection.url, DEFAULT_API6);
2859
3683
  const origin = new URL(api).origin;
2860
3684
  const root = `${api}/repos/${target}/pulls`;
@@ -2864,10 +3688,10 @@ function createPullAdapter({ connection, fetch }) {
2864
3688
  if (!SLUG.test(key)) throw new TrackerError("not_found", `\u0412 SourceCraft \u043D\u0435\u0442 PR ${key}.`);
2865
3689
  return key;
2866
3690
  };
2867
- const getPull = async (key) => pullOf3((await requestJson(`${root}/${slug(key)}`, { fetch, origin, headers, missing: missing(key) })).body);
3691
+ const getPull = async (key) => pullOf4((await requestJson(`${root}/${slug(key)}`, { fetch, origin, headers, missing: missing(key) })).body);
2868
3692
  return {
2869
3693
  list: async (query) => {
2870
- assertListQuery("PR SourceCraft", PULL_CAPABILITIES3.list, query, pullRequestFilterSchema.options);
3694
+ assertListQuery("PR SourceCraft", PULL_CAPABILITIES4.list, query, pullRequestFilterSchema.options);
2871
3695
  const status = usedStatuses3(query.status);
2872
3696
  const params = new URLSearchParams({ page_size: String(query.limit) });
2873
3697
  if (query.cursor !== void 0) params.set("page_token", query.cursor);
@@ -2876,11 +3700,11 @@ function createPullAdapter({ connection, fetch }) {
2876
3700
  if (query.sort !== void 0) params.set("sort_by", `${query.sort.direction === "asc" ? "" : "-"}${PULL_SORT_FIELDS_UPSTREAM[query.sort.field]}`);
2877
3701
  const { body } = await requestJson(`${root}?${params.toString()}`, { fetch, origin, headers });
2878
3702
  const record = isRecord(body) ? body : {};
2879
- const pulls = (Array.isArray(record.pull_requests) ? record.pull_requests : []).map(pullOf3).filter((pull) => matchesPage2(pull, query));
3703
+ const pulls = (Array.isArray(record.pull_requests) ? record.pull_requests : []).map(pullOf4).filter((pull) => matchesPage4(pull, query));
2880
3704
  return { items: pulls, ...nextOf3(record.next_page_token) };
2881
3705
  },
2882
3706
  get: (key) => getPull(key),
2883
- create: async (input) => pullOf3((await requestJson(root, { fetch, origin, headers, method: "POST", body: createBody2(input) })).body),
3707
+ create: async (input) => pullOf4((await requestJson(root, { fetch, origin, headers, method: "POST", body: createBody2(input) })).body),
2884
3708
  update: async (key, patch) => {
2885
3709
  const id = slug(key);
2886
3710
  let updated;
@@ -2897,12 +3721,12 @@ function createPullAdapter({ connection, fetch }) {
2897
3721
  if (action !== void 0) await requestJson(`${root}/${id}/${action}`, { fetch, origin, headers, method: "POST", missing: missing(key) });
2898
3722
  return getPull(key);
2899
3723
  }
2900
- return pullOf3(updated ?? (await requestJson(`${root}/${id}`, { fetch, origin, headers, missing: missing(key) })).body);
3724
+ return pullOf4(updated ?? (await requestJson(`${root}/${id}`, { fetch, origin, headers, missing: missing(key) })).body);
2901
3725
  },
2902
3726
  merge: async (key, input) => {
2903
3727
  const id = slug(key);
2904
3728
  if (input.message !== void 0) throw new TrackerError("unsupported", "PR SourceCraft: \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043A\u043E\u043C\u043C\u0438\u0442\u0430 \u0441\u043B\u0438\u044F\u043D\u0438\u044F \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F.");
2905
- if (input.method !== void 0 && !PULL_CAPABILITIES3.merge.includes(input.method)) {
3729
+ if (input.method !== void 0 && !PULL_CAPABILITIES4.merge.includes(input.method)) {
2906
3730
  throw new TrackerError("unsupported", `PR SourceCraft: \u0441\u043B\u0438\u044F\u043D\u0438\u0435 \u0441\u043F\u043E\u0441\u043E\u0431\u043E\u043C ${input.method} \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F.`);
2907
3731
  }
2908
3732
  if (input.headSha !== void 0) {
@@ -2932,7 +3756,7 @@ function createPullAdapter({ connection, fetch }) {
2932
3756
  },
2933
3757
  comments: async (key, query) => {
2934
3758
  const id = slug(key);
2935
- assertCommentQuery("\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 PR SourceCraft", PULL_CAPABILITIES3.comments, query);
3759
+ assertCommentQuery("\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 PR SourceCraft", PULL_CAPABILITIES4.comments, query);
2936
3760
  const params = new URLSearchParams({ page_size: String(query.limit) });
2937
3761
  if (query.cursor !== void 0) params.set("page_token", query.cursor);
2938
3762
  if (query.order !== void 0) params.set("sort_by", query.order === "asc" ? "created_at" : "-created_at");
@@ -2941,9 +3765,9 @@ function createPullAdapter({ connection, fetch }) {
2941
3765
  const rawComments = Array.isArray(record.pull_request_comments) ? record.pull_request_comments : [];
2942
3766
  const visible = rawComments.filter((raw) => !(isRecord(raw) && raw.is_deleted === true));
2943
3767
  const filtered = query.updatedAfter === void 0 ? visible : visible.filter((raw) => inTimeRange(toTimestamp(isRecord(raw) ? raw.updated_at : void 0), query.updatedAfter, void 0));
2944
- return { items: filtered.map(commentOf6), ...nextOf3(record.next_page_token) };
3768
+ return { items: filtered.map(commentOf8), ...nextOf3(record.next_page_token) };
2945
3769
  },
2946
- comment: async (key, body) => commentOf6((await requestJson(`${root}/${slug(key)}/comments`, { fetch, origin, headers, method: "POST", body: { body }, missing: missing(key) })).body)
3770
+ comment: async (key, body) => commentOf8((await requestJson(`${root}/${slug(key)}/comments`, { fetch, origin, headers, method: "POST", body: { body }, missing: missing(key) })).body)
2947
3771
  };
2948
3772
  }
2949
3773
  function createBody2(input) {
@@ -2970,7 +3794,7 @@ function usedStatuses3(status) {
2970
3794
  }
2971
3795
  return status;
2972
3796
  }
2973
- function matchesPage2(pull, query) {
3797
+ function matchesPage4(pull, query) {
2974
3798
  if (query.labels !== void 0 && !query.labels.every((label) => pull.labels.includes(label))) return false;
2975
3799
  if (query.search !== void 0 && !containsText([pull.title, pull.description], query.search)) return false;
2976
3800
  return inTimeRange(pull.createdAt, query.createdAfter, query.createdBefore) && inTimeRange(pull.updatedAt, query.updatedAfter, query.updatedBefore);
@@ -2990,7 +3814,7 @@ function nextOf3(token) {
2990
3814
  const value = text(token);
2991
3815
  return value === void 0 ? {} : { nextCursor: value };
2992
3816
  }
2993
- function pullOf3(raw) {
3817
+ function pullOf4(raw) {
2994
3818
  const item = isRecord(raw) ? raw : {};
2995
3819
  const source = isRecord(item.source) ? item.source : {};
2996
3820
  const target = isRecord(item.target) ? item.target : {};
@@ -3011,7 +3835,7 @@ function pullOf3(raw) {
3011
3835
  ...author === void 0 ? {} : { author },
3012
3836
  source: text(source.ref) ?? text(item.source_branch) ?? "?",
3013
3837
  target: text(target.ref) ?? text(item.target_branch) ?? "?",
3014
- ...headSha !== void 0 && SHA6.test(headSha) ? { headSha } : {},
3838
+ ...headSha !== void 0 && SHA9.test(headSha) ? { headSha } : {},
3015
3839
  labels: Array.isArray(item.labels) ? stringList(item.labels.map((label) => isRecord(label) ? label.name : void 0)) : [],
3016
3840
  links: [],
3017
3841
  ...createdAt === void 0 ? {} : { createdAt },
@@ -3020,7 +3844,7 @@ function pullOf3(raw) {
3020
3844
  ...closedAt === void 0 ? {} : { closedAt }
3021
3845
  };
3022
3846
  }
3023
- function commentOf6(raw) {
3847
+ function commentOf8(raw) {
3024
3848
  const item = isRecord(raw) ? raw : {};
3025
3849
  const author = isRecord(item.author) ? text(item.author.slug) : void 0;
3026
3850
  const createdAt = toTimestamp(item.created_at);
@@ -3029,45 +3853,45 @@ function commentOf6(raw) {
3029
3853
 
3030
3854
  // src/tracker/sourcecraft-repo.lib.ts
3031
3855
  var DEFAULT_API7 = "https://api.sourcecraft.tech";
3032
- var TARGET11 = /^[\w.-]+\/[\w.-]+$/;
3033
- var SHA7 = /^[0-9a-f]{7,64}$/;
3034
- var BRANCHES_SUBJECT3 = "\u0412\u0435\u0442\u043A\u0438 SourceCraft";
3035
- var BRANCH_FILTERS3 = branchFilterSchema.options;
3036
- var PAGE_SIZE_MAX = 100;
3037
- var CAPABILITIES6 = {
3038
- branches: { filters: BRANCH_FILTERS3, clientSide: [], sorts: [] }
3856
+ var TARGET15 = /^[\w.-]+\/[\w.-]+$/;
3857
+ var SHA10 = /^[0-9a-f]{7,64}$/;
3858
+ var BRANCHES_SUBJECT4 = "\u0412\u0435\u0442\u043A\u0438 SourceCraft";
3859
+ var BRANCH_FILTERS4 = branchFilterSchema.options;
3860
+ var PAGE_SIZE_MAX5 = 100;
3861
+ var CAPABILITIES9 = {
3862
+ branches: { filters: BRANCH_FILTERS4, clientSide: [], sorts: [] }
3039
3863
  };
3040
3864
  function sourcecraftRepo() {
3041
- return { capabilities: CAPABILITIES6, create: (context) => createSourceCraftRepoAdapter(context) };
3865
+ return { capabilities: CAPABILITIES9, create: (context) => createSourceCraftRepoAdapter(context) };
3042
3866
  }
3043
3867
  function createSourceCraftRepoAdapter({ connection, fetch }) {
3044
3868
  const target = requireTarget(connection.target, "org/repo \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F SourceCraft");
3045
- if (!TARGET11.test(target)) throw new TrackerError("invalid_request", "target SourceCraft \u2014 org/repo.");
3869
+ if (!TARGET15.test(target)) throw new TrackerError("invalid_request", "target SourceCraft \u2014 org/repo.");
3046
3870
  const api = baseUrl(connection.url, DEFAULT_API7);
3047
3871
  const origin = new URL(api).origin;
3048
3872
  const repo = `${api}/repos/${target}`;
3049
3873
  const headers = { authorization: `Bearer ${connection.token ?? ""}` };
3050
3874
  const missing = { failure: "not_found", message: `\u0412 SourceCraft \u043D\u0435\u0442 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F ${target}.` };
3051
3875
  return {
3052
- get: async () => repoOf3((await requestJson(repo, { fetch, origin, headers, missing })).body, target),
3876
+ get: async () => repoOf4((await requestJson(repo, { fetch, origin, headers, missing })).body, target),
3053
3877
  branches: async (query) => {
3054
- assertListQuery(BRANCHES_SUBJECT3, CAPABILITIES6.branches, query, BRANCH_FILTERS3);
3055
- const params = new URLSearchParams({ page_size: String(Math.min(query.limit, PAGE_SIZE_MAX)) });
3878
+ assertListQuery(BRANCHES_SUBJECT4, CAPABILITIES9.branches, query, BRANCH_FILTERS4);
3879
+ const params = new URLSearchParams({ page_size: String(Math.min(query.limit, PAGE_SIZE_MAX5)) });
3056
3880
  if (query.cursor !== void 0) params.set("page_token", query.cursor);
3057
3881
  if (query.search !== void 0) params.set("filter", query.search);
3058
3882
  const { body } = await requestJson(`${repo}/branches?${params.toString()}`, { fetch, origin, headers, missing });
3059
3883
  const record = isRecord(body) ? body : {};
3060
3884
  const branches = Array.isArray(record.branches) ? record.branches : [];
3061
3885
  const next = text(record.next_page_token);
3062
- return { items: branches.map(branchOf3), ...next === void 0 ? {} : { nextCursor: next } };
3886
+ return { items: branches.map(branchOf4), ...next === void 0 ? {} : { nextCursor: next } };
3063
3887
  }
3064
3888
  };
3065
3889
  }
3066
- function repoOf3(raw, target) {
3890
+ function repoOf4(raw, target) {
3067
3891
  const item = isRecord(raw) ? raw : {};
3068
3892
  const defaultBranch = text(item.default_branch);
3069
3893
  const description = text(item.description);
3070
- const visibility = visibilityOf3(item.visibility);
3894
+ const visibility = visibilityOf4(item.visibility);
3071
3895
  const url = text(item.web_url);
3072
3896
  return {
3073
3897
  name: text(item.slug) ?? text(item.name) ?? target,
@@ -3077,15 +3901,15 @@ function repoOf3(raw, target) {
3077
3901
  links: url === void 0 ? [] : [{ url, title: "SourceCraft" }]
3078
3902
  };
3079
3903
  }
3080
- function branchOf3(raw) {
3904
+ function branchOf4(raw) {
3081
3905
  const item = isRecord(raw) ? raw : {};
3082
3906
  const sha = isRecord(item.commit) ? text(item.commit.hash) : void 0;
3083
3907
  return {
3084
3908
  name: text(item.name) ?? "?",
3085
- ...sha === void 0 || !SHA7.test(sha) ? {} : { sha }
3909
+ ...sha === void 0 || !SHA10.test(sha) ? {} : { sha }
3086
3910
  };
3087
3911
  }
3088
- function visibilityOf3(value) {
3912
+ function visibilityOf4(value) {
3089
3913
  const parsed = repoVisibilitySchema.safeParse(value);
3090
3914
  return parsed.success ? parsed.data : void 0;
3091
3915
  }
@@ -3093,7 +3917,7 @@ function visibilityOf3(value) {
3093
3917
  // src/tracker/sourcecraft.lib.ts
3094
3918
  var DEFAULT_API8 = "https://api.sourcecraft.tech";
3095
3919
  var DEFAULT_WEB = "https://sourcecraft.dev";
3096
- var TARGET12 = /^[\w.-]+\/[\w.-]+$/;
3920
+ var TARGET16 = /^[\w.-]+\/[\w.-]+$/;
3097
3921
  var SLUG2 = /^[\w-]{1,100}$/;
3098
3922
  var ISSUE_SORT_FIELDS_UPSTREAM = {
3099
3923
  created: "created_at",
@@ -3127,7 +3951,7 @@ function sourcecraftTracker() {
3127
3951
  }
3128
3952
  function createSourceCraftAdapter({ connection, fetch }) {
3129
3953
  const target = requireTarget(connection.target, "org/repo \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F SourceCraft");
3130
- if (!TARGET12.test(target)) throw new TrackerError("invalid_request", "target SourceCraft \u2014 org/repo.");
3954
+ if (!TARGET16.test(target)) throw new TrackerError("invalid_request", "target SourceCraft \u2014 org/repo.");
3131
3955
  const api = baseUrl(connection.url, DEFAULT_API8);
3132
3956
  const origin = new URL(api).origin;
3133
3957
  const root = `${api}/repos/${target}/issues`;
@@ -3137,7 +3961,7 @@ function createSourceCraftAdapter({ connection, fetch }) {
3137
3961
  if (!SLUG2.test(key)) throw new TrackerError("unknown_issue", `\u0412 SourceCraft \u043D\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 ${key}.`);
3138
3962
  return key;
3139
3963
  };
3140
- const toIssue2 = (raw) => issueOf4(raw, web);
3964
+ const toIssue2 = (raw) => issueOf5(raw, web);
3141
3965
  return {
3142
3966
  list: async (query) => {
3143
3967
  assertListQuery("\u0417\u0430\u0434\u0430\u0447\u0438 SourceCraft", ISSUE_CAPABILITIES4.list, query, ISSUE_FILTERS);
@@ -3168,9 +3992,9 @@ function createSourceCraftAdapter({ connection, fetch }) {
3168
3992
  const record = isRecord(body) ? body : {};
3169
3993
  const rawComments = Array.isArray(record.issue_comments) ? record.issue_comments : [];
3170
3994
  const filtered = query.updatedAfter === void 0 ? rawComments : rawComments.filter((raw) => inTimeRange(toTimestamp(isRecord(raw) ? raw.updated_at : void 0), query.updatedAfter, void 0));
3171
- return { items: filtered.map(commentOf7), ...nextOf4(record.next_page_token) };
3995
+ return { items: filtered.map(commentOf9), ...nextOf4(record.next_page_token) };
3172
3996
  },
3173
- comment: async (key, body) => commentOf7((await requestJson(`${root}/${slug(key)}/comments`, { fetch, origin, headers, method: "POST", body: { body }, missingIsIssue: true })).body)
3997
+ comment: async (key, body) => commentOf9((await requestJson(`${root}/${slug(key)}/comments`, { fetch, origin, headers, method: "POST", body: { body }, missingIsIssue: true })).body)
3174
3998
  };
3175
3999
  }
3176
4000
  function patchBody5(patch) {
@@ -3206,7 +4030,7 @@ function issueFilterOf(query) {
3206
4030
  if (query.updatedBefore !== void 0) clauses.push(`updated_at<=${quoted(isoTime(query.updatedBefore))}`);
3207
4031
  return clauses.length === 0 ? void 0 : clauses.join(" and ");
3208
4032
  }
3209
- function issueOf4(raw, web) {
4033
+ function issueOf5(raw, web) {
3210
4034
  const item = isRecord(raw) ? raw : {};
3211
4035
  const status = isRecord(item.status) ? item.status : {};
3212
4036
  const slug = text(item.slug) ?? "?";
@@ -3230,7 +4054,7 @@ function issueOf4(raw, web) {
3230
4054
  ...updatedAt === void 0 ? {} : { updatedAt }
3231
4055
  };
3232
4056
  }
3233
- function commentOf7(raw) {
4057
+ function commentOf9(raw) {
3234
4058
  const item = isRecord(raw) ? raw : {};
3235
4059
  const author = isRecord(item.author) ? text(item.author.slug) : void 0;
3236
4060
  const createdAt = toTimestamp(item.created_at);
@@ -3242,22 +4066,35 @@ var registered = false;
3242
4066
  function registerBuiltinTrackers() {
3243
4067
  if (registered) return;
3244
4068
  registered = true;
3245
- for (const definition of [localTracker(), sourcecraftTracker(), githubTracker(), gitlabTracker(), jiraTracker()]) registerTracker(definition);
4069
+ for (const definition of [localTracker(), sourcecraftTracker(), githubTracker(), gitlabTracker(), jiraTracker(), gitverseTracker()]) registerTracker(definition);
3246
4070
  }
3247
4071
 
3248
4072
  // src/tracker/secrets.lib.ts
3249
4073
  import { mkdir, readFile, rename, writeFile } from "fs/promises";
3250
4074
  import { join } from "path";
4075
+ var SECRETS_SLOT_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/;
4076
+ var SECRETS_FILE_SUFFIX = "-secrets.json";
4077
+ function trackerSecretsFile(slot) {
4078
+ if (slot === void 0) return TRACKER_SECRETS_FILE;
4079
+ if (typeof slot !== "string" || !SECRETS_SLOT_PATTERN.test(slot)) {
4080
+ throw new TrackerError("invalid_request", `\u0441\u043B\u043E\u0442 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430 \u0442\u043E\u043A\u0435\u043D\u043E\u0432 '${String(slot)}' \u0432\u043D\u0435 \u043F\u0440\u0430\u0432\u0438\u043B\u0430: \u0441\u0442\u0440\u043E\u0447\u043D\u0430\u044F \u043B\u0430\u0442\u0438\u043D\u0438\u0446\u0430, \u0446\u0438\u0444\u0440\u044B, '-' \u0438 '_', \u043F\u0435\u0440\u0432\u044B\u0439 \u0441\u0438\u043C\u0432\u043E\u043B \u2014 \u0431\u0443\u043A\u0432\u0430 \u0438\u043B\u0438 \u0446\u0438\u0444\u0440\u0430, \u0434\u043B\u0438\u043D\u0430 \u043D\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 64`);
4081
+ }
4082
+ const file = `${slot}${SECRETS_FILE_SUFFIX}`;
4083
+ if (file === TRACKER_SECRETS_FILE) {
4084
+ throw new TrackerError("invalid_request", `\u0441\u043B\u043E\u0442 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430 \u0442\u043E\u043A\u0435\u043D\u043E\u0432 '${slot}' \u0437\u0430\u0440\u0435\u0437\u0435\u0440\u0432\u0438\u0440\u043E\u0432\u0430\u043D: \u043E\u043D \u0443\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u043D\u0430 \u0444\u0430\u0439\u043B \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E ${TRACKER_SECRETS_FILE}`);
4085
+ }
4086
+ return file;
4087
+ }
3251
4088
  var queue = Promise.resolve();
3252
4089
  function exclusive(task) {
3253
4090
  const run = queue.then(task, task);
3254
4091
  queue = run.catch(() => void 0);
3255
4092
  return run;
3256
4093
  }
3257
- async function readAll(dataDir) {
4094
+ async function readAll(dataDir, file) {
3258
4095
  let raw;
3259
4096
  try {
3260
- raw = await readFile(join(dataDir, TRACKER_SECRETS_FILE), "utf8");
4097
+ raw = await readFile(join(dataDir, file), "utf8");
3261
4098
  } catch (error) {
3262
4099
  if (error.code === "ENOENT") return {};
3263
4100
  throw error;
@@ -3266,32 +4103,34 @@ async function readAll(dataDir) {
3266
4103
  if (!isRecord(parsed)) return {};
3267
4104
  return Object.fromEntries(Object.entries(parsed).filter((entry) => typeof entry[1] === "string"));
3268
4105
  }
3269
- async function writeAll(dataDir, secrets) {
4106
+ async function writeAll(dataDir, file, secrets) {
3270
4107
  await mkdir(dataDir, { recursive: true, mode: 448 });
3271
- const target = join(dataDir, TRACKER_SECRETS_FILE);
4108
+ const target = join(dataDir, file);
3272
4109
  const temp = `${target}.${String(process.pid)}.tmp`;
3273
4110
  await writeFile(temp, JSON.stringify(secrets), { mode: 384 });
3274
4111
  await rename(temp, target);
3275
4112
  }
3276
- async function readTrackerToken(dataDir, projectId, sink = defaultSecretSink) {
3277
- const token = (await readAll(dataDir))[projectId];
4113
+ async function readTrackerToken(dataDir, projectId, sink = defaultSecretSink, slot) {
4114
+ const token = (await readAll(dataDir, trackerSecretsFile(slot)))[projectId];
3278
4115
  if (token !== void 0) sink.register(token);
3279
4116
  return token;
3280
4117
  }
3281
- function hasTrackerToken(dataDir, projectId) {
3282
- return readAll(dataDir).then((secrets) => secrets[projectId] !== void 0);
4118
+ async function hasTrackerToken(dataDir, projectId, slot) {
4119
+ const secrets = await readAll(dataDir, trackerSecretsFile(slot));
4120
+ return secrets[projectId] !== void 0;
3283
4121
  }
3284
- function writeTrackerToken(dataDir, projectId, token, sink = defaultSecretSink) {
4122
+ async function writeTrackerToken(dataDir, projectId, token, sink = defaultSecretSink, slot) {
4123
+ const file = trackerSecretsFile(slot);
3285
4124
  if (token !== "") sink.register(token);
3286
- return exclusive(async () => {
3287
- const secrets = await readAll(dataDir);
4125
+ await exclusive(async () => {
4126
+ const secrets = await readAll(dataDir, file);
3288
4127
  if (token === "") delete secrets[projectId];
3289
4128
  else secrets[projectId] = token;
3290
- await writeAll(dataDir, secrets);
4129
+ await writeAll(dataDir, file, secrets);
3291
4130
  });
3292
4131
  }
3293
- function deleteTrackerToken(dataDir, projectId, sink = defaultSecretSink) {
3294
- return writeTrackerToken(dataDir, projectId, "", sink);
4132
+ function deleteTrackerToken(dataDir, projectId, sink = defaultSecretSink, slot) {
4133
+ return writeTrackerToken(dataDir, projectId, "", sink, slot);
3295
4134
  }
3296
4135
 
3297
4136
  // src/tracker/pull.lib.ts
@@ -3311,7 +4150,7 @@ var TRACKER_CONFIG_MAX_BYTES = 256 * 1024;
3311
4150
  var TRACKER_CONFIG_ISSUES_MAX = 100;
3312
4151
  var TRACKER_CONFIG_YAML_ALIAS_MAX = 100;
3313
4152
  var TRACKER_CONFIG_DEPTH_MAX = 16;
3314
- var BUILTIN_TRACKER_KINDS = ["local", "sourcecraft", "github", "gitlab", "jira"];
4153
+ var BUILTIN_TRACKER_KINDS = ["local", "sourcecraft", "github", "gitlab", "jira", "gitverse"];
3315
4154
 
3316
4155
  // src/tracker/declarative/declarative.schema.ts
3317
4156
  import { z as z4 } from "zod";
@@ -4550,7 +5389,7 @@ function parseConfigInput(input) {
4550
5389
 
4551
5390
  // src/tracker/declarative/declarative-pull.lib.ts
4552
5391
  var MERGE_CONFLICT = [405, 409, 422];
4553
- function createPullAdapter2(config, adapterContext, capabilities) {
5392
+ function createPullAdapter3(config, adapterContext, capabilities) {
4554
5393
  const context = contextOf(config, adapterContext);
4555
5394
  const subject = `PR \xAB${config.title}\xBB`;
4556
5395
  const missing = (key) => ({ missing: { failure: "not_found", message: `\u0412 \u0442\u0440\u0435\u043A\u0435\u0440\u0435 \xAB${config.title}\xBB \u043D\u0435\u0442 PR ${key}.` } });
@@ -4878,7 +5717,7 @@ function definitionFromConfig(input) {
4878
5717
  },
4879
5718
  create: (context) => createIssuesAdapter(config, context, issues, features),
4880
5719
  issues,
4881
- ...pulls === void 0 ? {} : { pulls: { capabilities: pulls, create: (context) => createPullAdapter2(config, context, pulls) } },
5720
+ ...pulls === void 0 ? {} : { pulls: { capabilities: pulls, create: (context) => createPullAdapter3(config, context, pulls) } },
4882
5721
  ...ci === void 0 ? {} : { ci: { capabilities: ci, create: (context) => createCiAdapter(config, context, ci) } },
4883
5722
  origin: (connection) => baseOf(config, connection).origin
4884
5723
  };
@@ -5215,6 +6054,17 @@ export {
5215
6054
  GITLAB_MERGE_REQUEST_STATES,
5216
6055
  GITLAB_PIPELINE_SOURCES,
5217
6056
  GITLAB_PRIORITY_LABELS,
6057
+ GITVERSE_ACCEPT,
6058
+ GITVERSE_API,
6059
+ GITVERSE_CI_STATUSES,
6060
+ GITVERSE_DONE_COLUMN,
6061
+ GITVERSE_ISSUE_STATES,
6062
+ GITVERSE_ISSUE_STATE_NAMES,
6063
+ GITVERSE_PULL_STATES,
6064
+ GITVERSE_REMOTE_HOSTS,
6065
+ GITVERSE_RUN_EVENTS,
6066
+ GITVERSE_TOKEN_ENV,
6067
+ GITVERSE_WEB,
5218
6068
  ISSUE_FILTERS,
5219
6069
  ISSUE_SORT_FIELDS,
5220
6070
  JIRA_ISSUE_TYPE,
@@ -5273,6 +6123,10 @@ export {
5273
6123
  gitlabPulls,
5274
6124
  gitlabRepo,
5275
6125
  gitlabTracker,
6126
+ gitverseCi,
6127
+ gitversePulls,
6128
+ gitverseRepo,
6129
+ gitverseTracker,
5276
6130
  hasTrackerToken,
5277
6131
  inTimeRange,
5278
6132
  isFinished,
@@ -5281,6 +6135,7 @@ export {
5281
6135
  issueCapabilitiesOf,
5282
6136
  jiraTracker,
5283
6137
  keysFor,
6138
+ linkNextPage,
5284
6139
  listTrackers,
5285
6140
  localTracker,
5286
6141
  logCursor,
@@ -5289,6 +6144,7 @@ export {
5289
6144
  lookup,
5290
6145
  mergeMethodSchema,
5291
6146
  nameOf,
6147
+ nextPageCursor,
5292
6148
  priorityName,
5293
6149
  pullRequestAdapterOf,
5294
6150
  pullRequestFilterSchema,
@@ -5315,6 +6171,7 @@ export {
5315
6171
  text,
5316
6172
  toTimestamp,
5317
6173
  trackerConfigSchema,
6174
+ trackerSecretsFile,
5318
6175
  usedFilters,
5319
6176
  validateTrackerConfig,
5320
6177
  writeTrackerToken