@reviewflow/sdk 0.0.0-stage → 6.22.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.
- package/LICENSE +15 -0
- package/README.md +86 -2
- package/build/files-node.mjs +70 -0
- package/build/git-node.mjs +1792 -0
- package/build/index-browser.es.js +3328 -0
- package/build/index-node.mjs +3328 -0
- package/package.json +135 -3
- package/src/api/pullRequest.ts +617 -0
- package/src/config/insertCheckoutEntry.ts +54 -0
- package/src/diff/diffPaths.ts +78 -0
- package/src/diff/impacts.ts +47 -0
- package/src/diff/readingOrder.ts +428 -0
- package/src/diff/themes.ts +105 -0
- package/src/diff/types.ts +141 -0
- package/src/files/editableFile.ts +163 -0
- package/src/files.ts +13 -0
- package/src/git/groupDiffPaths.ts +134 -0
- package/src/git/manifests.ts +96 -0
- package/src/git/moduleBindings.ts +571 -0
- package/src/git/pullRequestDiff.ts +159 -0
- package/src/git/readBranchDiff.ts +359 -0
- package/src/git/readImpacts.ts +797 -0
- package/src/git/readingOrder.ts +190 -0
- package/src/git/run.ts +213 -0
- package/src/git.ts +38 -0
- package/src/github/applySuggestions.ts +399 -0
- package/src/github/editPullRequest.ts +790 -0
- package/src/github/fetchCompareDiff.ts +21 -0
- package/src/github/imageProxies.ts +43 -0
- package/src/github/lineDiff.ts +145 -0
- package/src/github/prQuery.ts +298 -0
- package/src/github/pullRequestDetail.ts +1347 -0
- package/src/github/pullRequestHead.ts +65 -0
- package/src/github/pullRequestRevisions.ts +124 -0
- package/src/github/transport.ts +171 -0
- package/src/index.ts +35 -0
- package/src/reviewflow/board.ts +333 -0
- package/src/reviewflow/changedParts.ts +80 -0
- package/src/reviewflow/checksFromReviewflow.ts +75 -0
- package/src/reviewflow/client.ts +589 -0
- package/src/reviewflow/follow.ts +345 -0
- package/src/reviewflow/status.ts +17 -0
- package/src/reviewflow/stream.ts +199 -0
- package/src/types/entities.ts +8 -0
- package/src/types/ids.ts +1 -0
- package/src/types/reviewflow.ts +32 -0
- package/src/utils/pullRequestEdit.ts +108 -0
- package/src/utils/pullRequestPart.ts +12 -0
- package/src/utils/pullRequestReaction.ts +19 -0
- package/src/utils/pullRequestSuggestion.ts +148 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { parseSync } from "oxc-parser";
|
|
2
|
+
import { isTypescriptPath, orderForReading } from "../diff/readingOrder.ts";
|
|
3
|
+
import type { DiffReadingOrderResponse } from "../diff/types.ts";
|
|
4
|
+
import {
|
|
5
|
+
groupDiffPaths,
|
|
6
|
+
listTree,
|
|
7
|
+
packageOf,
|
|
8
|
+
packagesInListing,
|
|
9
|
+
} from "./groupDiffPaths.ts";
|
|
10
|
+
import type { IsPackage } from "./groupDiffPaths.ts";
|
|
11
|
+
import {
|
|
12
|
+
dependencyNamesOf,
|
|
13
|
+
readManifests,
|
|
14
|
+
resolveLocalSpecifier,
|
|
15
|
+
} from "./manifests.ts";
|
|
16
|
+
import type { Manifest } from "./manifests.ts";
|
|
17
|
+
import { ensureCommits, isSha } from "./pullRequestDiff.ts";
|
|
18
|
+
import { readNumstat } from "./readBranchDiff.ts";
|
|
19
|
+
import { catFiles, git } from "./run.ts";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* How many unchanged files an import is followed through to reach a changed
|
|
23
|
+
* one: a barrel `index.ts` is one, a barrel re-exporting a barrel two.
|
|
24
|
+
*/
|
|
25
|
+
const maxUnchangedHops = 2;
|
|
26
|
+
|
|
27
|
+
const stringLiteral = /^(["'`])(.*)\1$/s;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The modules a TypeScript file imports or re-exports, as written, in source
|
|
31
|
+
* order: `import type` included, a dynamic `import()` only when its argument
|
|
32
|
+
* is a plain string. A syntax error yields whatever was parsed before it.
|
|
33
|
+
*/
|
|
34
|
+
export const importSpecifiers = (path: string, source: string): string[] => {
|
|
35
|
+
const { staticImports, staticExports, dynamicImports } = parseSync(
|
|
36
|
+
path,
|
|
37
|
+
source,
|
|
38
|
+
).module;
|
|
39
|
+
const found: { start: number; specifier: string }[] = [
|
|
40
|
+
...staticImports.map(({ start, moduleRequest }) => ({
|
|
41
|
+
start,
|
|
42
|
+
specifier: moduleRequest.value,
|
|
43
|
+
})),
|
|
44
|
+
...staticExports.flatMap(({ start, entries }) =>
|
|
45
|
+
entries.flatMap(({ moduleRequest }) =>
|
|
46
|
+
moduleRequest === null
|
|
47
|
+
? []
|
|
48
|
+
: [{ start, specifier: moduleRequest.value }],
|
|
49
|
+
),
|
|
50
|
+
),
|
|
51
|
+
...dynamicImports.flatMap(({ start, moduleRequest }) => {
|
|
52
|
+
const literal = stringLiteral.exec(
|
|
53
|
+
source.slice(moduleRequest.start, moduleRequest.end),
|
|
54
|
+
);
|
|
55
|
+
const specifier = literal?.[2];
|
|
56
|
+
return specifier === undefined || specifier.includes("${")
|
|
57
|
+
? []
|
|
58
|
+
: [{ start, specifier }];
|
|
59
|
+
}),
|
|
60
|
+
];
|
|
61
|
+
return [
|
|
62
|
+
...new Set(
|
|
63
|
+
found
|
|
64
|
+
.toSorted((left, right) => left.start - right.start)
|
|
65
|
+
.map(({ specifier }) => specifier),
|
|
66
|
+
),
|
|
67
|
+
];
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
interface ReadImportsParams {
|
|
71
|
+
checkout: string;
|
|
72
|
+
head: string;
|
|
73
|
+
changed: readonly string[];
|
|
74
|
+
exists: (path: string) => boolean;
|
|
75
|
+
isPackage: IsPackage;
|
|
76
|
+
manifests: ReadonlyMap<string, Manifest>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The resolved imports within the package of the changed TypeScript files,
|
|
81
|
+
* and of the unchanged files of the same package they reach within
|
|
82
|
+
* `maxUnchangedHops`.
|
|
83
|
+
*/
|
|
84
|
+
const readImports = async ({
|
|
85
|
+
checkout,
|
|
86
|
+
head,
|
|
87
|
+
changed,
|
|
88
|
+
exists,
|
|
89
|
+
isPackage,
|
|
90
|
+
manifests,
|
|
91
|
+
}: ReadImportsParams): Promise<Map<string, string[]>> => {
|
|
92
|
+
const changedSet = new Set(changed);
|
|
93
|
+
const imports = new Map<string, string[]>();
|
|
94
|
+
let toRead = changed.filter((path) => isTypescriptPath(path) && exists(path));
|
|
95
|
+
for (let hop = 0; hop <= maxUnchangedHops && toRead.length > 0; hop++) {
|
|
96
|
+
const contents = await catFiles(checkout, head, toRead);
|
|
97
|
+
const next = new Set<string>();
|
|
98
|
+
for (const path of toRead) {
|
|
99
|
+
const source = contents.get(path);
|
|
100
|
+
const resolved = (
|
|
101
|
+
source === undefined ? [] : importSpecifiers(path, source)
|
|
102
|
+
)
|
|
103
|
+
.map((specifier) =>
|
|
104
|
+
resolveLocalSpecifier({ from: path, specifier, exists, manifests }),
|
|
105
|
+
)
|
|
106
|
+
.filter((target) => target !== null);
|
|
107
|
+
imports.set(path, resolved);
|
|
108
|
+
const ownPackage = packageOf(path, isPackage);
|
|
109
|
+
for (const target of resolved) {
|
|
110
|
+
if (changedSet.has(target) || imports.has(target)) continue;
|
|
111
|
+
if (packageOf(target, isPackage) === ownPackage) next.add(target);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
toRead = [...next];
|
|
115
|
+
}
|
|
116
|
+
return imports;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Which package directory depends on which: a dependency of any kind naming
|
|
121
|
+
* another package of the repository.
|
|
122
|
+
*/
|
|
123
|
+
const packageDependencies = (
|
|
124
|
+
manifests: ReadonlyMap<string, Manifest>,
|
|
125
|
+
): Map<string, Set<string>> => {
|
|
126
|
+
const directoryOfName = new Map<string, string>();
|
|
127
|
+
for (const [directory, { name }] of manifests) {
|
|
128
|
+
if (typeof name === "string") directoryOfName.set(name, directory);
|
|
129
|
+
}
|
|
130
|
+
return new Map(
|
|
131
|
+
[...manifests].map(([directory, manifest]) => [
|
|
132
|
+
directory,
|
|
133
|
+
new Set(
|
|
134
|
+
dependencyNamesOf(manifest).flatMap((name) => {
|
|
135
|
+
const dependency = directoryOfName.get(name);
|
|
136
|
+
return dependency === undefined || dependency === directory
|
|
137
|
+
? []
|
|
138
|
+
: [dependency];
|
|
139
|
+
}),
|
|
140
|
+
),
|
|
141
|
+
]),
|
|
142
|
+
);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export interface ReadingOrderRequest {
|
|
146
|
+
/** the registered checkout, resolved by the caller */
|
|
147
|
+
checkout: string;
|
|
148
|
+
head: string;
|
|
149
|
+
base: string;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The files of the range `/api/prs/diff` reads (head against its merge base
|
|
154
|
+
* with base), with whitespace shown, ordered for reading. The commits are
|
|
155
|
+
* fetched by sha when the checkout lacks them, as the diff does.
|
|
156
|
+
*/
|
|
157
|
+
export const readReadingOrder = async ({
|
|
158
|
+
checkout,
|
|
159
|
+
head,
|
|
160
|
+
base,
|
|
161
|
+
}: ReadingOrderRequest): Promise<DiffReadingOrderResponse> => {
|
|
162
|
+
if (!isSha(head) || !isSha(base)) {
|
|
163
|
+
throw new Error("head and base must be commit shas");
|
|
164
|
+
}
|
|
165
|
+
await ensureCommits(checkout, [head, base], undefined);
|
|
166
|
+
const mergeBaseOutput = await git(checkout, ["merge-base", base, head]);
|
|
167
|
+
const mergeBase = mergeBaseOutput.trim();
|
|
168
|
+
const numstat = await readNumstat(checkout, [mergeBase, head], []);
|
|
169
|
+
const changed = numstat.map(({ path }) => path);
|
|
170
|
+
const tree = await listTree(checkout, head);
|
|
171
|
+
const inTree = new Set(tree);
|
|
172
|
+
const exists = (path: string): boolean => inTree.has(path);
|
|
173
|
+
const isPackage = packagesInListing(tree);
|
|
174
|
+
const manifests = await readManifests(checkout, head, tree);
|
|
175
|
+
const imports = await readImports({
|
|
176
|
+
checkout,
|
|
177
|
+
head,
|
|
178
|
+
changed,
|
|
179
|
+
exists,
|
|
180
|
+
isPackage,
|
|
181
|
+
manifests,
|
|
182
|
+
});
|
|
183
|
+
return {
|
|
184
|
+
groups: orderForReading({
|
|
185
|
+
groups: groupDiffPaths(isPackage, changed),
|
|
186
|
+
imports,
|
|
187
|
+
dependsOn: packageDependencies(manifests),
|
|
188
|
+
}),
|
|
189
|
+
};
|
|
190
|
+
};
|
package/src/git/run.ts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { execFile, spawn } from "node:child_process";
|
|
2
|
+
import { env } from "node:process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
|
|
5
|
+
const execFileAsync = promisify(execFile);
|
|
6
|
+
|
|
7
|
+
const timeoutMs = 10_000;
|
|
8
|
+
const networkTimeoutMs = 60_000;
|
|
9
|
+
const defaultMaxBuffer = 4 * 1024 * 1024;
|
|
10
|
+
const batchMaxBuffer = 256 * 1024 * 1024;
|
|
11
|
+
|
|
12
|
+
export interface GitOptions {
|
|
13
|
+
/** a fetch, pull or push: talks to a remote */
|
|
14
|
+
network?: boolean;
|
|
15
|
+
/** bytes of stdout past which git is killed; 4MB unless a caller has its own budget */
|
|
16
|
+
maxBuffer?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Exit status 1 is an answer rather than a failure, as for
|
|
19
|
+
* `diff --no-index`, which exits 1 when the files differ.
|
|
20
|
+
*/
|
|
21
|
+
exitOneIsAnswer?: boolean;
|
|
22
|
+
/** kills git when the caller no longer wants the answer */
|
|
23
|
+
signal?: AbortSignal;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* With no terminal (a server, a background job) to answer a credential or passphrase
|
|
28
|
+
* prompt, so one would hang until the timeout and then report a kill rather
|
|
29
|
+
* than the reason. Refusing to prompt makes git fail at once with the reason on
|
|
30
|
+
* stderr, which is what the user reads. A user's own
|
|
31
|
+
* `GIT_SSH_COMMAND` is kept: it is how a custom agent or key is configured.
|
|
32
|
+
*/
|
|
33
|
+
const networkEnv = (): NodeJS.ProcessEnv => ({
|
|
34
|
+
...env,
|
|
35
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
36
|
+
GIT_SSH_COMMAND: env.GIT_SSH_COMMAND ?? "ssh -o BatchMode=yes",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export const gitFailedCode = "ERR_GIT_FAILED";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* git's reason, which is not always its first line: a push opens with
|
|
43
|
+
* `To <remote>` and states the rejection on a ` ! [rejected]` line below, and a
|
|
44
|
+
* refused fast-forward prints its `hint:` block before the `fatal:`.
|
|
45
|
+
*/
|
|
46
|
+
export const reasonLine = (stderr: string): string => {
|
|
47
|
+
const lines = stderr
|
|
48
|
+
.split("\n")
|
|
49
|
+
.map((line) => line.trim())
|
|
50
|
+
.filter((line) => line !== "");
|
|
51
|
+
return (
|
|
52
|
+
lines.find((line) => line.startsWith("! ")) ??
|
|
53
|
+
lines.find((line) => /^(?:fatal|error):/.test(line)) ??
|
|
54
|
+
lines.find((line) => !line.startsWith("hint:")) ??
|
|
55
|
+
""
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A git killed by the timeout has written nothing to stderr, so without this
|
|
61
|
+
* the message names the command and no reason at all.
|
|
62
|
+
*/
|
|
63
|
+
const failureReason = (error: unknown, timeout: number): string => {
|
|
64
|
+
if (typeof error !== "object" || error === null) return "";
|
|
65
|
+
if ("killed" in error && error.killed === true) {
|
|
66
|
+
return `killed after ${String(timeout / 1000)}s without finishing`;
|
|
67
|
+
}
|
|
68
|
+
return "stderr" in error ? reasonLine(String(error.stderr)) : "";
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/** git exits 1 on some errors too, which print nothing to stdout */
|
|
72
|
+
const isExitOneAnswer = (error: unknown): error is { stdout: string } =>
|
|
73
|
+
typeof error === "object" &&
|
|
74
|
+
error !== null &&
|
|
75
|
+
"code" in error &&
|
|
76
|
+
error.code === 1 &&
|
|
77
|
+
"stdout" in error &&
|
|
78
|
+
typeof error.stdout === "string" &&
|
|
79
|
+
error.stdout !== "";
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* git reads are on-demand: nothing here caches, and every call names the repository it ran in so a failure is actionable.
|
|
83
|
+
*
|
|
84
|
+
* `execFile` with an argument array and no shell, which is what makes a branch
|
|
85
|
+
* name safe to pass through: nothing here is ever interpolated into a command
|
|
86
|
+
* line. *
|
|
87
|
+
* The message keeps only one stderr line on purpose (`reasonLine`): it is
|
|
88
|
+
* rendered to the user verbatim, and the rest is
|
|
89
|
+
* hints.
|
|
90
|
+
*/
|
|
91
|
+
export const git = async (
|
|
92
|
+
cwd: string,
|
|
93
|
+
args: string[],
|
|
94
|
+
options: GitOptions = {},
|
|
95
|
+
): Promise<string> => {
|
|
96
|
+
const timeout = options.network ? networkTimeoutMs : timeoutMs;
|
|
97
|
+
try {
|
|
98
|
+
const { stdout } = await execFileAsync("git", args, {
|
|
99
|
+
cwd,
|
|
100
|
+
timeout,
|
|
101
|
+
maxBuffer: options.maxBuffer ?? defaultMaxBuffer,
|
|
102
|
+
env: options.network ? networkEnv() : env,
|
|
103
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
104
|
+
});
|
|
105
|
+
return stdout;
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (options.exitOneIsAnswer && isExitOneAnswer(error)) return error.stdout;
|
|
108
|
+
const reason = failureReason(error, timeout);
|
|
109
|
+
throw Object.assign(
|
|
110
|
+
new Error(
|
|
111
|
+
`git ${args.join(" ")} failed in ${cwd}${reason ? `: ${reason}` : ""}`,
|
|
112
|
+
{ cause: error },
|
|
113
|
+
),
|
|
114
|
+
{ code: gitFailedCode },
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/** `<request> missing`: the request echoes the path, which may contain spaces */
|
|
120
|
+
const unansweredHeader = / (?:missing|ambiguous)$/;
|
|
121
|
+
|
|
122
|
+
const parseBatchOutput = (
|
|
123
|
+
output: Buffer,
|
|
124
|
+
paths: readonly string[],
|
|
125
|
+
): Map<string, string> => {
|
|
126
|
+
const contents = new Map<string, string>();
|
|
127
|
+
let offset = 0;
|
|
128
|
+
for (const path of paths) {
|
|
129
|
+
const newline = output.indexOf("\n", offset);
|
|
130
|
+
if (newline === -1) break;
|
|
131
|
+
const header = output.toString("utf8", offset, newline);
|
|
132
|
+
offset = newline + 1;
|
|
133
|
+
if (unansweredHeader.test(header)) continue;
|
|
134
|
+
const [, type, sizeText] = header.split(" ");
|
|
135
|
+
const size = Number(sizeText);
|
|
136
|
+
// past an unreadable header every offset is wrong: stop rather than misattribute
|
|
137
|
+
if (!Number.isSafeInteger(size) || size < 0) break;
|
|
138
|
+
if (type === "blob") {
|
|
139
|
+
contents.set(path, output.toString("utf8", offset, offset + size));
|
|
140
|
+
}
|
|
141
|
+
offset += size + 1;
|
|
142
|
+
}
|
|
143
|
+
return contents;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Many files of one commit from a single `git cat-file --batch`, rather than a
|
|
148
|
+
* process per file. A path missing at `sha`, or naming no blob, is absent from
|
|
149
|
+
* the answer. The commit must be in the checkout.
|
|
150
|
+
*/
|
|
151
|
+
export const catFiles = async (
|
|
152
|
+
cwd: string,
|
|
153
|
+
sha: string,
|
|
154
|
+
paths: readonly string[],
|
|
155
|
+
): Promise<Map<string, string>> => {
|
|
156
|
+
// a newline ends a batch request: such a path cannot be asked
|
|
157
|
+
const asked = paths.filter((path) => !path.includes("\n"));
|
|
158
|
+
if (asked.length === 0) return new Map();
|
|
159
|
+
const args = ["cat-file", "--batch"];
|
|
160
|
+
return new Promise((resolve, reject) => {
|
|
161
|
+
const child = spawn("git", args, { cwd, env, timeout: timeoutMs });
|
|
162
|
+
const chunks: Buffer[] = [];
|
|
163
|
+
let size = 0;
|
|
164
|
+
let stderr = "";
|
|
165
|
+
child.stdout.on("data", (chunk: Buffer) => {
|
|
166
|
+
size += chunk.length;
|
|
167
|
+
if (size > batchMaxBuffer) {
|
|
168
|
+
child.kill();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
chunks.push(chunk);
|
|
172
|
+
});
|
|
173
|
+
child.stderr.on("data", (chunk: Buffer) => {
|
|
174
|
+
stderr += chunk.toString("utf8");
|
|
175
|
+
});
|
|
176
|
+
child.on("error", reject);
|
|
177
|
+
// git exiting before reading every request (not a repo, killed) breaks the
|
|
178
|
+
// pipe; `close` reports why, an unhandled EPIPE would crash the process
|
|
179
|
+
child.stdin.on("error", () => {});
|
|
180
|
+
child.on("close", (code, signal) => {
|
|
181
|
+
if (code === 0) {
|
|
182
|
+
resolve(parseBatchOutput(Buffer.concat(chunks), asked));
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const reason =
|
|
186
|
+
signal === null
|
|
187
|
+
? reasonLine(stderr)
|
|
188
|
+
: `killed (${signal}) after reading ${String(size)} bytes`;
|
|
189
|
+
reject(
|
|
190
|
+
Object.assign(
|
|
191
|
+
new Error(
|
|
192
|
+
`git ${args.join(" ")} failed in ${cwd}${reason ? `: ${reason}` : ""}`,
|
|
193
|
+
),
|
|
194
|
+
{ code: gitFailedCode },
|
|
195
|
+
),
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
child.stdin.end(asked.map((path) => `${sha}:${path}\n`).join(""));
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/** For calls whose failure is an expected answer, e.g. "no remote". */
|
|
203
|
+
export const gitOrNull = async (
|
|
204
|
+
cwd: string,
|
|
205
|
+
args: string[],
|
|
206
|
+
options: GitOptions = {},
|
|
207
|
+
): Promise<string | null> => {
|
|
208
|
+
try {
|
|
209
|
+
return await git(cwd, args, options);
|
|
210
|
+
} catch {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
};
|
package/src/git.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node-only surface: reads a pull request diff from a local git checkout.
|
|
3
|
+
* Import it as `@reviewflow/sdk/git`; the root entry stays browser-safe.
|
|
4
|
+
*/
|
|
5
|
+
export { git, gitFailedCode, gitOrNull, reasonLine } from "./git/run.ts";
|
|
6
|
+
export type { GitOptions } from "./git/run.ts";
|
|
7
|
+
export {
|
|
8
|
+
defaultDiffLimits,
|
|
9
|
+
parseNumstat,
|
|
10
|
+
readBranchDiff,
|
|
11
|
+
} from "./git/readBranchDiff.ts";
|
|
12
|
+
export type {
|
|
13
|
+
BranchDiff,
|
|
14
|
+
BranchDiffScope,
|
|
15
|
+
DiffLimits,
|
|
16
|
+
NumstatEntry,
|
|
17
|
+
} from "./git/readBranchDiff.ts";
|
|
18
|
+
export {
|
|
19
|
+
groupDiffPaths,
|
|
20
|
+
noPackages,
|
|
21
|
+
packagesAtTree,
|
|
22
|
+
packagesOnDisk,
|
|
23
|
+
} from "./git/groupDiffPaths.ts";
|
|
24
|
+
export type { IsPackage } from "./git/groupDiffPaths.ts";
|
|
25
|
+
export { readFileAtSha, readPullRequestDiff } from "./git/pullRequestDiff.ts";
|
|
26
|
+
export type { PullRequestDiffRequest } from "./git/pullRequestDiff.ts";
|
|
27
|
+
export { readReadingOrder } from "./git/readingOrder.ts";
|
|
28
|
+
export type { ReadingOrderRequest } from "./git/readingOrder.ts";
|
|
29
|
+
export {
|
|
30
|
+
createBindingsCache,
|
|
31
|
+
maxTypescriptFiles,
|
|
32
|
+
readImpacts,
|
|
33
|
+
} from "./git/readImpacts.ts";
|
|
34
|
+
export type {
|
|
35
|
+
BindingsCache,
|
|
36
|
+
ImpactsRequest,
|
|
37
|
+
ModuleSummary,
|
|
38
|
+
} from "./git/readImpacts.ts";
|