@ascribed/review 0.1.2-next.2

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.
Files changed (70) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +183 -0
  3. package/dist/github/index.d.ts +7 -0
  4. package/dist/github/index.js +8 -0
  5. package/dist/github/index.js.map +1 -0
  6. package/dist/github/marker.d.ts +63 -0
  7. package/dist/github/marker.js +156 -0
  8. package/dist/github/marker.js.map +1 -0
  9. package/dist/github/queries.d.ts +16 -0
  10. package/dist/github/queries.js +174 -0
  11. package/dist/github/queries.js.map +1 -0
  12. package/dist/github/read.d.ts +84 -0
  13. package/dist/github/read.js +141 -0
  14. package/dist/github/read.js.map +1 -0
  15. package/dist/github/repository.d.ts +30 -0
  16. package/dist/github/repository.js +77 -0
  17. package/dist/github/repository.js.map +1 -0
  18. package/dist/github/requests.d.ts +65 -0
  19. package/dist/github/requests.js +274 -0
  20. package/dist/github/requests.js.map +1 -0
  21. package/dist/github/session.d.ts +143 -0
  22. package/dist/github/session.js +581 -0
  23. package/dist/github/session.js.map +1 -0
  24. package/dist/github/transport.d.ts +33 -0
  25. package/dist/github/transport.js +206 -0
  26. package/dist/github/transport.js.map +1 -0
  27. package/dist/marks/index.d.ts +92 -0
  28. package/dist/marks/index.js +766 -0
  29. package/dist/marks/index.js.map +1 -0
  30. package/dist/overlay/index.d.ts +4 -0
  31. package/dist/overlay/index.js +7 -0
  32. package/dist/overlay/index.js.map +1 -0
  33. package/dist/overlay/markdown.d.ts +4 -0
  34. package/dist/overlay/markdown.js +368 -0
  35. package/dist/overlay/markdown.js.map +1 -0
  36. package/dist/overlay/overlay.d.ts +47 -0
  37. package/dist/overlay/overlay.js +1710 -0
  38. package/dist/overlay/overlay.js.map +1 -0
  39. package/dist/overlay/style.d.ts +1 -0
  40. package/dist/overlay/style.js +570 -0
  41. package/dist/overlay/style.js.map +1 -0
  42. package/dist/overlay/text.d.ts +33 -0
  43. package/dist/overlay/text.js +200 -0
  44. package/dist/overlay/text.js.map +1 -0
  45. package/dist/overlay/types.d.ts +60 -0
  46. package/dist/overlay/types.js +2 -0
  47. package/dist/overlay/types.js.map +1 -0
  48. package/dist/place/anchor.d.ts +23 -0
  49. package/dist/place/anchor.js +49 -0
  50. package/dist/place/anchor.js.map +1 -0
  51. package/dist/place/index.d.ts +4 -0
  52. package/dist/place/index.js +6 -0
  53. package/dist/place/index.js.map +1 -0
  54. package/dist/place/lines.d.ts +56 -0
  55. package/dist/place/lines.js +280 -0
  56. package/dist/place/lines.js.map +1 -0
  57. package/dist/place/place.d.ts +77 -0
  58. package/dist/place/place.js +182 -0
  59. package/dist/place/place.js.map +1 -0
  60. package/dist/shared/errors.d.ts +40 -0
  61. package/dist/shared/errors.js +22 -0
  62. package/dist/shared/errors.js.map +1 -0
  63. package/dist/shared/git.d.ts +4 -0
  64. package/dist/shared/git.js +29 -0
  65. package/dist/shared/git.js.map +1 -0
  66. package/dist/shared/types.d.ts +56 -0
  67. package/dist/shared/types.js +3 -0
  68. package/dist/shared/types.js.map +1 -0
  69. package/package.json +61 -0
  70. package/src/marks/marks.css +361 -0
@@ -0,0 +1,77 @@
1
+ // The checkout's GitHub repository and branch, from git's configuration.
2
+ import { git, gitMaybe } from "../shared/git.js";
3
+ import { ReviewError } from "../shared/errors.js";
4
+ /**
5
+ * The repository a remote URL names, or `undefined` if it isn't a GitHub-style
6
+ * URL: `https://host/owner/name(.git)`, `git@host:owner/name(.git)`, or
7
+ * `ssh://git@host[:port]/owner/name(.git)`.
8
+ */
9
+ export function parseRemote(url) {
10
+ const trimmed = url.trim();
11
+ let host;
12
+ let rest;
13
+ const scp = /^(?:[^@/]+@)?([^:/]+):(?!\/)(.+)$/.exec(trimmed);
14
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) {
15
+ let parsed;
16
+ try {
17
+ parsed = new URL(trimmed);
18
+ }
19
+ catch {
20
+ return undefined;
21
+ }
22
+ if (!["https:", "http:", "ssh:", "git:"].includes(parsed.protocol))
23
+ return undefined;
24
+ host = parsed.hostname;
25
+ rest = parsed.pathname;
26
+ }
27
+ else if (scp !== null) {
28
+ host = scp[1] ?? "";
29
+ rest = scp[2] ?? "";
30
+ }
31
+ else {
32
+ return undefined;
33
+ }
34
+ const parts = rest.replace(/^\/+|\/+$/g, "").split("/");
35
+ if (parts.length !== 2)
36
+ return undefined;
37
+ const owner = parts[0] ?? "";
38
+ const name = (parts[1] ?? "").replace(/\.git$/, "");
39
+ if (owner === "" || name === "")
40
+ return undefined;
41
+ // SSH connections to github.com sometimes go through ssh.github.com.
42
+ if (host === "ssh.github.com")
43
+ host = "github.com";
44
+ return { host: host.toLowerCase(), owner, name };
45
+ }
46
+ /** Reads the checkout at `cwd`. Rejects with a `git` error if it isn't a repository. */
47
+ export async function readCheckout(cwd) {
48
+ const root = (await git(cwd, ["rev-parse", "--show-toplevel"])).trim();
49
+ const headOid = (await gitMaybe(root, ["rev-parse", "--verify", "--quiet", "HEAD"]))?.trim();
50
+ if (headOid === undefined || headOid === "") {
51
+ throw new ReviewError("git", "The repository has no commits yet.");
52
+ }
53
+ const branch = (await gitMaybe(root, ["symbolic-ref", "--quiet", "--short", "HEAD"]))?.trim();
54
+ if (branch === undefined || branch === "") {
55
+ return { root, branch: undefined, headRef: undefined, head: undefined, bases: [], headOid };
56
+ }
57
+ const config = async (key) => (await gitMaybe(root, ["config", "--get", key]))?.trim() || undefined;
58
+ const remoteName = (await config(`branch.${branch}.pushRemote`)) ??
59
+ (await config("remote.pushDefault")) ??
60
+ (await config(`branch.${branch}.remote`)) ??
61
+ "origin";
62
+ const merge = await config(`branch.${branch}.merge`);
63
+ const headRef = merge?.replace(/^refs\/heads\//, "") ?? branch;
64
+ const remoteUrl = async (name) => {
65
+ const url = await config(`remote.${name}.url`);
66
+ return url === undefined ? undefined : parseRemote(url);
67
+ };
68
+ const head = await remoteUrl(remoteName);
69
+ const bases = [];
70
+ const upstream = remoteName === "upstream" ? undefined : await remoteUrl("upstream");
71
+ if (upstream !== undefined)
72
+ bases.push(upstream);
73
+ if (head !== undefined)
74
+ bases.push(head);
75
+ return { root, branch, headRef, head, bases, headOid };
76
+ }
77
+ //# sourceMappingURL=repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.js","sourceRoot":"","sources":["../../src/github/repository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import type { PageRef } from "../place/place.js";
2
+ import type { PullRequestInfo, ReviewSession } from "./session.js";
3
+ /** The overlay's requests, one per `OverlayHost` method that reads or writes threads. */
4
+ export type OverlayMethod = "load" | "commentTarget" | "comment" | "reply" | "allThreads" | "resolve" | "submit" | "discard";
5
+ export declare const OVERLAY_METHODS: readonly OverlayMethod[];
6
+ /** A changed page as `ascribe diff` lists it, with its title when the host knows it. */
7
+ export interface ChangedPageRef {
8
+ path: string;
9
+ status: "added" | "removed" | "changed";
10
+ because: readonly string[];
11
+ title?: string | null;
12
+ }
13
+ /** What answering a request needs. */
14
+ export interface RequestContext {
15
+ session: ReviewSession;
16
+ /** The page the overlay is on: its build and content path. */
17
+ page: {
18
+ build: string;
19
+ path: string;
20
+ } | undefined;
21
+ /** The page as the overlay last read it, with its anchors: comments are made on it. */
22
+ lastPage: PageRef | undefined;
23
+ /** The changed pages, for which pages show a thread. */
24
+ changedPages(): Promise<readonly ChangedPageRef[]>;
25
+ /**
26
+ * For a host whose pages are rendered from an editor's unsaved text: a
27
+ * file's text on disk (`saved`) and in the editor (`current`), by content
28
+ * path, or `undefined` when they're the same. The session counts lines on
29
+ * disk, so the page's blocks are moved to their lines there, and back.
30
+ */
31
+ unsaved?(path: string): Promise<{
32
+ saved: string;
33
+ current: string;
34
+ } | undefined>;
35
+ }
36
+ /** An answer: the result, the page read (for `load`), and whether the request changed the threads. */
37
+ export interface Answer {
38
+ result: unknown;
39
+ page?: PageRef;
40
+ changed: boolean;
41
+ }
42
+ /** Answers one of the overlay's requests. Rejects with a `ReviewError`. */
43
+ export declare function answerRequest(context: RequestContext, method: OverlayMethod, params: Record<string, unknown>): Promise<Answer>;
44
+ /**
45
+ * The changed pages that show a file (a content path): the file's own page,
46
+ * and the pages that changed through it (an include). `[]` when none does.
47
+ */
48
+ export declare function pagesShowing(file: string, pages: readonly ChangedPageRef[]): {
49
+ path: string;
50
+ title: string | null;
51
+ }[];
52
+ /**
53
+ * The git revision to compare with for a pull request's base: the remote
54
+ * branch, from the remote whose URL is the pull request's repository, when
55
+ * there is one; else the branch's name.
56
+ */
57
+ export declare function pullRequestBase(remotes: readonly {
58
+ name: string;
59
+ repository: string | undefined;
60
+ }[], repository: string, baseRefName: string): string;
61
+ /**
62
+ * The git revision for the pull request's base in the checkout at `root`:
63
+ * `origin/main` when that remote branch exists, else the branch's name.
64
+ */
65
+ export declare function baseRevision(root: string, pr: PullRequestInfo): Promise<string>;
@@ -0,0 +1,274 @@
1
+ // Answering the overlay's requests (`OverlayHost`) from a review session, for
2
+ // a host that keeps GitHub to itself and passes the overlay's requests on as
3
+ // messages: the page preview's extension, or the site preview's dev server.
4
+ // The parameters arrive as untyped messages, so each is checked here.
5
+ import { anchorKey, parseSource } from "../place/anchor.js";
6
+ import { lineHunks, shiftLine } from "../place/lines.js";
7
+ import { ReviewError } from "../shared/errors.js";
8
+ import { gitMaybe } from "../shared/git.js";
9
+ import { parseRemote } from "./repository.js";
10
+ export const OVERLAY_METHODS = [
11
+ "load",
12
+ "commentTarget",
13
+ "comment",
14
+ "reply",
15
+ "allThreads",
16
+ "resolve",
17
+ "submit",
18
+ "discard",
19
+ ];
20
+ /** Answers one of the overlay's requests. Rejects with a `ReviewError`. */
21
+ export async function answerRequest(context, method, params) {
22
+ const { session } = context;
23
+ switch (method) {
24
+ case "load": {
25
+ const page = context.page;
26
+ if (!page)
27
+ throw new ReviewError("not-found", "There's no page to review here.");
28
+ const shown = anchors(params["anchors"]);
29
+ const edits = await unsavedEdits(context, shown);
30
+ // The page's blocks at their lines on disk, and the way back.
31
+ const back = new Map();
32
+ for (const block of shown) {
33
+ const saved = toSaved(edits, block, false);
34
+ if (saved !== undefined && !back.has(anchorKey(saved)))
35
+ back.set(anchorKey(saved), block);
36
+ }
37
+ const ref = {
38
+ build: page.build,
39
+ path: page.path,
40
+ anchors: edits.size === 0 ? shown : [...back.keys()].map((key) => fromKey(key)),
41
+ removed: anchors(params["removed"]),
42
+ };
43
+ const [placed, pending, viewer] = await Promise.all([
44
+ session.threads(ref),
45
+ session.pending(),
46
+ session.viewer(),
47
+ ]);
48
+ const threads = edits.size === 0
49
+ ? placed
50
+ : {
51
+ ...placed,
52
+ blocks: placed.blocks.map((block) => ({
53
+ ...block,
54
+ anchor: back.get(anchorKey(block.anchor)) ?? block.anchor,
55
+ })),
56
+ };
57
+ const pr = session.pullRequest;
58
+ return {
59
+ result: { pullRequest: { number: pr.number, url: pr.url }, threads, pending, viewer },
60
+ page: ref,
61
+ changed: false,
62
+ };
63
+ }
64
+ case "commentTarget": {
65
+ const shown = anchor(params["anchor"]);
66
+ const saved = toSaved(await unsavedEdits(context, [shown]), shown, true);
67
+ if (saved === undefined)
68
+ return { result: { kind: "push-first", message: UNSAVED }, changed: false };
69
+ return { result: await session.commentTarget(saved), changed: false };
70
+ }
71
+ case "allThreads": {
72
+ const [threads, pages] = await Promise.all([
73
+ session.allThreads(),
74
+ context.changedPages().catch(() => []),
75
+ ]);
76
+ return {
77
+ result: threads.map((thread) => ({ thread, pages: pagesShowing(thread.path, pages) })),
78
+ changed: false,
79
+ };
80
+ }
81
+ case "comment": {
82
+ const shown = anchor(params["anchor"]);
83
+ const target = toSaved(await unsavedEdits(context, [shown]), shown, true);
84
+ if (target === undefined)
85
+ throw new ReviewError("push-first", UNSAVED);
86
+ const page = context.lastPage ?? {
87
+ build: context.page?.build ?? "",
88
+ path: context.page?.path ?? "",
89
+ anchors: [target],
90
+ };
91
+ const quote = typeof params["quote"] === "string" ? params["quote"] : undefined;
92
+ return {
93
+ result: await session.comment(target, text(params["body"]), page, quote),
94
+ changed: true,
95
+ };
96
+ }
97
+ case "reply":
98
+ return {
99
+ result: await session.reply(text(params["threadId"]), text(params["body"]), params["when"] === "now" ? "now" : "withReview"),
100
+ changed: true,
101
+ };
102
+ case "resolve":
103
+ await session.resolve(text(params["threadId"]), params["resolved"] === true);
104
+ return { result: null, changed: true };
105
+ case "submit": {
106
+ const body = typeof params["body"] === "string" ? params["body"] : undefined;
107
+ await session.submit(event(params["event"]), body);
108
+ return { result: null, changed: true };
109
+ }
110
+ case "discard":
111
+ await session.discard();
112
+ return { result: null, changed: true };
113
+ }
114
+ }
115
+ const UNSAVED = "This block has changes that aren't saved. Save the file, then comment.";
116
+ /** The line changes from each shown file's text in the editor to its text on disk, where they differ. */
117
+ async function unsavedEdits(context, shown) {
118
+ const edits = new Map();
119
+ if (!context.unsaved)
120
+ return edits;
121
+ const files = new Set();
122
+ for (const block of shown) {
123
+ const range = parseSource(block.source);
124
+ if (range)
125
+ files.add(range.path);
126
+ for (const include of block.via) {
127
+ const at = parseInclude(include);
128
+ if (at)
129
+ files.add(at.path);
130
+ }
131
+ }
132
+ for (const file of files) {
133
+ const text = await context.unsaved(file);
134
+ if (text === undefined)
135
+ continue;
136
+ const hunks = lineHunks(text.current, text.saved);
137
+ if (hunks.length > 0) {
138
+ edits.set(file, {
139
+ hunks,
140
+ sides: { old: text.current.split(/\r?\n/), new: text.saved.split(/\r?\n/) },
141
+ });
142
+ }
143
+ }
144
+ return edits;
145
+ }
146
+ /**
147
+ * A block's anchor with its lines on disk. `strict` (for commenting) wants
148
+ * every line of the block saved as it is; otherwise an edited block takes
149
+ * the lines its text has on disk. `undefined` when nothing of it is saved.
150
+ */
151
+ function toSaved(edits, block, strict) {
152
+ if (edits.size === 0)
153
+ return block;
154
+ const range = parseSource(block.source);
155
+ if (range === undefined)
156
+ return block;
157
+ const edit = edits.get(range.path);
158
+ let source = block.source;
159
+ if (edit !== undefined) {
160
+ const lines = [];
161
+ for (let line = range.first; line <= range.last; line++) {
162
+ const moved = shiftLine(edit.hunks, line, edit.sides);
163
+ if (moved === undefined || moved.replaced) {
164
+ if (strict)
165
+ return undefined;
166
+ if (moved === undefined)
167
+ continue;
168
+ }
169
+ lines.push(moved.line);
170
+ }
171
+ if (lines.length === 0)
172
+ return undefined;
173
+ const colon = block.source.lastIndexOf(":");
174
+ source = `${block.source.slice(0, colon)}:${Math.min(...lines)}-${Math.max(...lines)}`;
175
+ }
176
+ const via = [];
177
+ for (const include of block.via) {
178
+ const at = parseInclude(include);
179
+ const includeEdit = at && edits.get(at.path);
180
+ if (!at || !includeEdit) {
181
+ via.push(include);
182
+ continue;
183
+ }
184
+ const moved = shiftLine(includeEdit.hunks, at.line, includeEdit.sides);
185
+ if (moved === undefined || (strict && moved.replaced))
186
+ return undefined;
187
+ via.push(`${include.slice(0, include.lastIndexOf(":"))}:${moved.line}`);
188
+ }
189
+ return { source, via };
190
+ }
191
+ /** An include, `<path>:<line>`, parsed, with its path decoded. */
192
+ function parseInclude(include) {
193
+ const colon = include.lastIndexOf(":");
194
+ const line = Number(include.slice(colon + 1));
195
+ if (colon <= 0 || !Number.isInteger(line) || line < 1)
196
+ return undefined;
197
+ const range = parseSource(`${include.slice(0, colon)}:1-1`);
198
+ return range && { path: range.path, line };
199
+ }
200
+ /** The anchor `anchorKey` wrote. */
201
+ function fromKey(key) {
202
+ const [source = "", ...via] = key.split(" ");
203
+ return { source, via };
204
+ }
205
+ /**
206
+ * The changed pages that show a file (a content path): the file's own page,
207
+ * and the pages that changed through it (an include). `[]` when none does.
208
+ */
209
+ export function pagesShowing(file, pages) {
210
+ return pages
211
+ .filter((page) => page.status !== "removed")
212
+ .filter((page) => page.path === file || page.because.includes(file))
213
+ .sort((a, b) => Number(b.path === file) - Number(a.path === file))
214
+ .map((page) => ({ path: page.path, title: page.title ?? null }));
215
+ }
216
+ /**
217
+ * The git revision to compare with for a pull request's base: the remote
218
+ * branch, from the remote whose URL is the pull request's repository, when
219
+ * there is one; else the branch's name.
220
+ */
221
+ export function pullRequestBase(remotes, repository, baseRefName) {
222
+ const remote = remotes.find((r) => r.repository === repository && r.name === "upstream") ??
223
+ remotes.find((r) => r.repository === repository && r.name === "origin") ??
224
+ remotes.find((r) => r.repository === repository);
225
+ return remote ? `${remote.name}/${baseRefName}` : baseRefName;
226
+ }
227
+ /**
228
+ * The git revision for the pull request's base in the checkout at `root`:
229
+ * `origin/main` when that remote branch exists, else the branch's name.
230
+ */
231
+ export async function baseRevision(root, pr) {
232
+ const out = (await gitMaybe(root, ["remote", "-v"]).catch(() => undefined)) ?? "";
233
+ const remotes = out
234
+ .split("\n")
235
+ .map((line) => /^(\S+)\s+(\S+)\s+\(fetch\)$/.exec(line))
236
+ .filter((match) => match !== null)
237
+ .map((match) => {
238
+ const repository = parseRemote(match[2] ?? "");
239
+ return { name: match[1] ?? "", repository: repository && repoKey(repository) };
240
+ });
241
+ const revision = pullRequestBase(remotes, repoKey(pr.repository), pr.baseRefName);
242
+ if (revision !== pr.baseRefName) {
243
+ const exists = await gitMaybe(root, ["rev-parse", "--verify", "--quiet", revision]).catch(() => undefined);
244
+ if (exists !== undefined)
245
+ return revision;
246
+ }
247
+ return pr.baseRefName;
248
+ }
249
+ function repoKey(repository) {
250
+ return `${repository.host}/${repository.owner}/${repository.name}`.toLowerCase();
251
+ }
252
+ const EVENTS = ["COMMENT", "APPROVE", "REQUEST_CHANGES"];
253
+ function event(value) {
254
+ if (EVENTS.includes(value))
255
+ return value;
256
+ throw new ReviewError("refused", `A review can't be submitted as ${String(value)}.`);
257
+ }
258
+ function text(value) {
259
+ if (typeof value !== "string")
260
+ throw new ReviewError("refused", "The request is missing text.");
261
+ return value;
262
+ }
263
+ function anchor(value) {
264
+ const record = value;
265
+ if (typeof record?.source !== "string") {
266
+ throw new ReviewError("refused", "The request is missing a block.");
267
+ }
268
+ const via = Array.isArray(record.via) ? record.via.filter((v) => typeof v === "string") : [];
269
+ return { source: record.source, via };
270
+ }
271
+ function anchors(value) {
272
+ return Array.isArray(value) ? value.map(anchor) : [];
273
+ }
274
+ //# sourceMappingURL=requests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requests.js","sourceRoot":"","sources":["../../src/github/requests.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,143 @@
1
+ import { type Anchor } from "../place/anchor.js";
2
+ import { type LocatedThread, type PageRef, type PlacedThreads } from "../place/place.js";
3
+ import type { Thread, ThreadComment } from "../shared/types.js";
4
+ import { type CheckoutInfo, type RepositoryRef } from "./repository.js";
5
+ import { type GitHubTransport } from "./transport.js";
6
+ /** How the local `HEAD` relates to the pull request's head commit. */
7
+ export type LocalState =
8
+ /** `HEAD` is the pull request's head commit. */
9
+ "same"
10
+ /** The pull request has commits `HEAD` doesn't: pull them. */
11
+ | "behind"
12
+ /** `HEAD` has commits the pull request doesn't: push them. */
13
+ | "ahead"
14
+ /** Both have commits the other doesn't. */
15
+ | "diverged"
16
+ /** The pull request's head commit isn't in the local repository: fetch it. */
17
+ | "missing";
18
+ /** The pull request for the checkout. */
19
+ export interface PullRequestInfo {
20
+ /** Its GraphQL node id. */
21
+ id: string;
22
+ number: number;
23
+ url: string;
24
+ repository: RepositoryRef;
25
+ baseRefName: string;
26
+ baseOid: string;
27
+ headRefName: string;
28
+ headOid: string;
29
+ local: LocalState;
30
+ }
31
+ /** What's in the viewer's pending review: comments only they can see until they submit. */
32
+ export interface PendingReview {
33
+ /** The review's node id, or `undefined` when there's no pending review. */
34
+ id: string | undefined;
35
+ /** New threads. */
36
+ threads: Thread[];
37
+ /** Replies to existing threads. */
38
+ replies: {
39
+ threadId: string;
40
+ comment: ThreadComment;
41
+ }[];
42
+ /** Comments on blocks GitHub can't anchor, which go on the conversation when submitted. */
43
+ conversation: Thread[];
44
+ /** How many comments there are in all. */
45
+ count: number;
46
+ }
47
+ /** How a review ends: as a comment, an approval, or a request for changes. */
48
+ export type ReviewEvent = "COMMENT" | "APPROVE" | "REQUEST_CHANGES";
49
+ /** How a comment on a block would go, worked out before it's made. */
50
+ export type CommentTarget =
51
+ /** As a review thread on the block's lines. */
52
+ {
53
+ kind: "thread";
54
+ }
55
+ /**
56
+ * In the pending review's body, since GitHub can't anchor it: the file
57
+ * isn't in the pull request (`"file"`), or the lines are away from its
58
+ * changes (`"lines"`).
59
+ */
60
+ | {
61
+ kind: "summary";
62
+ reason: "file" | "lines";
63
+ }
64
+ /** Not until the block's lines are pushed (or fetched): `message` says which. */
65
+ | {
66
+ kind: "push-first";
67
+ message: string;
68
+ };
69
+ /** One pull request's review, for a host and the overlay behind it. */
70
+ export interface ReviewSession {
71
+ pullRequest: PullRequestInfo;
72
+ /** The page's threads. Cached until `refresh()` or a change made through the session. */
73
+ threads(page: PageRef): Promise<PlacedThreads>;
74
+ /** Every thread on the pull request's files in the content root, located in the working tree. */
75
+ allThreads(): Promise<LocatedThread[]>;
76
+ /** The signed-in viewer's login. */
77
+ viewer(): Promise<string>;
78
+ /** How a comment on a block would go: as a thread, in the review's summary, or not yet. */
79
+ commentTarget(anchor: Anchor): Promise<CommentTarget>;
80
+ /** Comments on a block, in the pending review. */
81
+ /**
82
+ * Comments on a block. `quote` is the block's text as the page shows it,
83
+ * which a comment held in the review's summary quotes; without it, the
84
+ * comment quotes the block's source.
85
+ */
86
+ comment(anchor: Anchor, body: string, page: PageRef, quote?: string): Promise<Thread>;
87
+ /** Replies to a thread: sent at once (`"now"`), or held in the pending review. */
88
+ reply(threadId: string, body: string, when: "now" | "withReview"): Promise<ThreadComment>;
89
+ /** Resolves or reopens a thread, at once. */
90
+ resolve(threadId: string, resolved: boolean): Promise<void>;
91
+ pending(): Promise<PendingReview>;
92
+ /** Submits the pending review, making its comments visible. */
93
+ submit(event: ReviewEvent, body?: string): Promise<void>;
94
+ /** Deletes the pending review and everything in it. */
95
+ discard(): Promise<void>;
96
+ /** Forgets what's cached, so the next call reads from GitHub again. */
97
+ refresh(): Promise<void>;
98
+ }
99
+ /** Options for `openReview`. */
100
+ export interface OpenReviewOptions {
101
+ /** The directory holding the project's `ascribe.toml`. */
102
+ projectDir: string;
103
+ /**
104
+ * The content root's path in the repository, with a trailing `/`. By
105
+ * default it's worked out from `ascribe.toml`'s `content-root`.
106
+ */
107
+ contentPrefix?: string;
108
+ /** Makes the transport for a host. By default, `ghTransport`. */
109
+ transport?: (host: string) => GitHubTransport;
110
+ /**
111
+ * The least time between two requests that change something, in
112
+ * milliseconds, to stay under GitHub's secondary rate limit. Default 1000.
113
+ */
114
+ mutationInterval?: number;
115
+ }
116
+ /**
117
+ * Opens a review of the pull request for the project's checkout: the open
118
+ * pull request whose head is the current branch. Resolves with `undefined`
119
+ * when there's none, or `HEAD` is detached.
120
+ */
121
+ export declare function openReview(options: OpenReviewOptions): Promise<ReviewSession | undefined>;
122
+ /** The content root's path in the repository, from the project's `ascribe.toml`. */
123
+ export declare function contentPrefixOf(root: string, projectDir: string): string;
124
+ /**
125
+ * The open pull request whose head is the checkout's branch, looked for in an
126
+ * `upstream` remote's repository and then in the one the branch is pushed to.
127
+ */
128
+ export declare function findPullRequest(checkout: CheckoutInfo, transportFor: (host: string) => GitHubTransport): Promise<PullRequestInfo | undefined>;
129
+ /** What a session needs, for hosts and tests that find the pull request themselves. */
130
+ export interface SessionOptions {
131
+ root: string;
132
+ contentPrefix: string;
133
+ pullRequest: PullRequestInfo;
134
+ transport: GitHubTransport;
135
+ mutationInterval: number;
136
+ /**
137
+ * How long to wait before reading again when GitHub fails a read with its
138
+ * generic error, in milliseconds. Default 2000.
139
+ */
140
+ retryDelay?: number;
141
+ }
142
+ /** A session for a pull request already found. */
143
+ export declare function createSession(options: SessionOptions): ReviewSession;