@dev.fast/whiteboard 0.3.5-preview.20261022.134 → 0.3.5-preview.20261023.136
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/dist/{agent-cli-DpSj5jCW.js → agent-cli-CHNDd4Ga.js} +1 -1
- package/dist/agent-cli-Cn4-16XA.js +2 -0
- package/dist/{authoring-tools-BBksBplC.js → authoring-tools-D8EkIDjm.js} +2 -2
- package/dist/build-info.json +1 -1
- package/dist/{cli-runner-D1PRTICL.js → cli-runner-DFBaVGij.js} +4 -4
- package/dist/cli.js +1 -1
- package/dist/{client-CiB4MRlo.js → client-p9WB8Sj0.js} +3 -1
- package/dist/{headless-host-D9y2sBIj.js → headless-host-DJhcab6r.js} +84 -29
- package/dist/{headless-host-Ck5-JjGV.js → headless-host-dwlqhqMY.js} +1 -1
- package/dist/{local-data-m4BCbtSA.js → local-data-DHedfhxw.js} +173 -38
- package/dist/{mcp-IkZ0jBxU.js → mcp-Br-r_wwR.js} +1 -1
- package/dist/profile-BopGMZUh.js +2 -0
- package/dist/{profile-BfvRNmRc.js → profile-D9wdlPgw.js} +1 -1
- package/dist/server/desktop-host.js +5 -5
- package/dist/sharing/index.d.ts +32 -2
- package/dist/sharing/index.js +1 -1
- package/dist/{stored-document-migration-B-lTO-_P.js → stored-document-migration-DEt8iwem.js} +11 -4
- package/package.json +3 -3
- package/src/github-pr-graph.ts +97 -0
- package/src/review-api/http.ts +38 -24
- package/src/review-api/local-data.ts +101 -10
- package/src/review-api/origin.ts +13 -5
- package/src/review-api/store.ts +222 -59
- package/src/review-stack.ts +125 -22
- package/src/sharing/import.ts +2 -0
- package/dist/agent-cli-a7X9Th1U.js +0 -2
- package/dist/profile-BfY9lECc.js +0 -2
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export type RunGitHubApi = (host: string, endpoint: string) => Promise<string>;
|
|
4
|
+
|
|
5
|
+
const repository = z.object({ full_name: z.string() });
|
|
6
|
+
|
|
7
|
+
const pullRequest = z.object({
|
|
8
|
+
number: z.number().int().positive(),
|
|
9
|
+
title: z.string(),
|
|
10
|
+
draft: z.boolean().optional(),
|
|
11
|
+
head: z.object({ ref: z.string(), repo: repository.nullable() }),
|
|
12
|
+
base: z.object({ ref: z.string(), repo: repository }),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export interface GitHubPullRequest {
|
|
16
|
+
number: number;
|
|
17
|
+
url: string;
|
|
18
|
+
title: string;
|
|
19
|
+
headBranch: string;
|
|
20
|
+
baseBranch: string;
|
|
21
|
+
draft: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Repository facts are independent of which PRs have whiteboards. Share a
|
|
25
|
+
// short-lived, coalesced read between source resolution and stack discovery.
|
|
26
|
+
const caches = new WeakMap<
|
|
27
|
+
RunGitHubApi,
|
|
28
|
+
Map<
|
|
29
|
+
string,
|
|
30
|
+
{
|
|
31
|
+
expires: number;
|
|
32
|
+
read: Promise<GitHubPullRequest[]>;
|
|
33
|
+
}
|
|
34
|
+
>
|
|
35
|
+
>();
|
|
36
|
+
|
|
37
|
+
export function readGitHubPullRequests(
|
|
38
|
+
host: string,
|
|
39
|
+
slug: string,
|
|
40
|
+
run: RunGitHubApi,
|
|
41
|
+
): Promise<GitHubPullRequest[]> {
|
|
42
|
+
let cache = caches.get(run);
|
|
43
|
+
|
|
44
|
+
if (!cache) caches.set(run, (cache = new Map()));
|
|
45
|
+
const key = `${host}/${slug}`.toLowerCase();
|
|
46
|
+
const previous = cache.get(key);
|
|
47
|
+
|
|
48
|
+
if (previous && previous.expires > Date.now()) return previous.read;
|
|
49
|
+
|
|
50
|
+
const read = (async () => {
|
|
51
|
+
const result: GitHubPullRequest[] = [];
|
|
52
|
+
|
|
53
|
+
for (let page = 1; page <= 10; page++) {
|
|
54
|
+
const records = z
|
|
55
|
+
.array(pullRequest)
|
|
56
|
+
.parse(
|
|
57
|
+
JSON.parse(
|
|
58
|
+
await run(
|
|
59
|
+
host,
|
|
60
|
+
`repos/${slug}/pulls?state=open&per_page=100&page=${page}`,
|
|
61
|
+
),
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
for (const pr of records) {
|
|
66
|
+
// A fork's identically named branch is not this repository's branch.
|
|
67
|
+
if (
|
|
68
|
+
pr.head.repo?.full_name.toLowerCase() !== slug.toLowerCase() ||
|
|
69
|
+
pr.base.repo.full_name.toLowerCase() !== slug.toLowerCase()
|
|
70
|
+
)
|
|
71
|
+
continue;
|
|
72
|
+
result.push({
|
|
73
|
+
number: pr.number,
|
|
74
|
+
url: `https://${host}/${slug}/pull/${pr.number}`,
|
|
75
|
+
title: pr.title,
|
|
76
|
+
headBranch: pr.head.ref,
|
|
77
|
+
baseBranch: pr.base.ref,
|
|
78
|
+
draft: pr.draft ?? false,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (records.length < 100) return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
throw new Error(
|
|
86
|
+
"PR discovery exceeded 1000 open pull requests; refusing a partial stack.",
|
|
87
|
+
);
|
|
88
|
+
})();
|
|
89
|
+
|
|
90
|
+
cache.set(key, { expires: Date.now() + 60_000, read });
|
|
91
|
+
// Failures are optional context, but must not poison the next read.
|
|
92
|
+
void read.catch(() => {
|
|
93
|
+
if (cache.get(key)?.read === read) cache.delete(key);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return read;
|
|
97
|
+
}
|
package/src/review-api/http.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
type JsonObject,
|
|
3
5
|
type JsonValue,
|
|
@@ -608,10 +610,27 @@ export function createReviewApi(
|
|
|
608
610
|
[...reviewIds].map((reviewId) => [reviewId, linksKey(reviewId)] as const),
|
|
609
611
|
);
|
|
610
612
|
|
|
611
|
-
//
|
|
612
|
-
//
|
|
613
|
-
const
|
|
614
|
-
|
|
613
|
+
// A digest of a review's place and its project's branches, so a later
|
|
614
|
+
// stream's matches unless its header or stack moved.
|
|
615
|
+
const placeKey = (id: string) => {
|
|
616
|
+
const project = !isShared(id) && store.summary(id)?.project;
|
|
617
|
+
|
|
618
|
+
const branches = project
|
|
619
|
+
? store
|
|
620
|
+
.listProject(project.id)
|
|
621
|
+
.filter(({ origin }) => origin?.branch)
|
|
622
|
+
.map(({ reviewId, origin, trashedAt }) => [
|
|
623
|
+
reviewId,
|
|
624
|
+
origin,
|
|
625
|
+
trashedAt,
|
|
626
|
+
])
|
|
627
|
+
: [];
|
|
628
|
+
|
|
629
|
+
return createHash("sha1")
|
|
630
|
+
.update(JSON.stringify([placeOf(id), branches]))
|
|
631
|
+
.digest()
|
|
632
|
+
.readUInt32BE(0);
|
|
633
|
+
};
|
|
615
634
|
|
|
616
635
|
const placeKeys = new Map(
|
|
617
636
|
[...reviewIds].map((reviewId) => [reviewId, placeKey(reviewId)] as const),
|
|
@@ -627,7 +646,7 @@ export function createReviewApi(
|
|
|
627
646
|
activity: store.activity.read(reviewId),
|
|
628
647
|
coverageRevision: data?.coverageRevision ?? 0,
|
|
629
648
|
linksRevision: linksRevision.get(reviewId) ?? 0,
|
|
630
|
-
placeRevision:
|
|
649
|
+
placeRevision: placeKeys.get(reviewId)!,
|
|
631
650
|
},
|
|
632
651
|
};
|
|
633
652
|
} catch (error) {
|
|
@@ -681,7 +700,6 @@ export function createReviewApi(
|
|
|
681
700
|
|
|
682
701
|
if (next === key) continue;
|
|
683
702
|
placeKeys.set(id, next);
|
|
684
|
-
placeRevision.set(id, (placeRevision.get(id) ?? 0) + 1);
|
|
685
703
|
marked = markReview(id) || marked;
|
|
686
704
|
}
|
|
687
705
|
|
|
@@ -1996,10 +2014,12 @@ export function createReviewApi(
|
|
|
1996
2014
|
app.get("/:id/stack", async (context) => {
|
|
1997
2015
|
const query = readQuerySchemas.get.parse(context.req.query());
|
|
1998
2016
|
const id = context.req.param("id");
|
|
1999
|
-
|
|
2017
|
+
let snapshot = readReview(id, query.version);
|
|
2000
2018
|
|
|
2001
2019
|
if (isShared(id) || !snapshot.pins) return context.json({ layers: [] });
|
|
2002
2020
|
|
|
2021
|
+
if (data) snapshot = await data.discoverPullRequest(snapshot);
|
|
2022
|
+
|
|
2003
2023
|
const repositoryPath = store
|
|
2004
2024
|
.repositories()
|
|
2005
2025
|
.find(
|
|
@@ -2163,7 +2183,15 @@ export function createReviewApi(
|
|
|
2163
2183
|
app.get("/:id", async (context) => {
|
|
2164
2184
|
const query = readQuerySchemas.get.parse(context.req.query());
|
|
2165
2185
|
|
|
2166
|
-
|
|
2186
|
+
let snapshot = { ...readReview(context.req.param("id"), query.version) };
|
|
2187
|
+
|
|
2188
|
+
if (
|
|
2189
|
+
data &&
|
|
2190
|
+
query.full &&
|
|
2191
|
+
query.version === undefined &&
|
|
2192
|
+
!isShared(snapshot.reviewId)
|
|
2193
|
+
)
|
|
2194
|
+
snapshot = await data.discoverPullRequest(snapshot);
|
|
2167
2195
|
|
|
2168
2196
|
if (data && query.full) {
|
|
2169
2197
|
try {
|
|
@@ -2223,17 +2251,7 @@ export function createReviewApi(
|
|
|
2223
2251
|
|
|
2224
2252
|
const request = withBoardRepository(
|
|
2225
2253
|
await locateRepositories(
|
|
2226
|
-
await pullRequestTargets(
|
|
2227
|
-
body,
|
|
2228
|
-
register,
|
|
2229
|
-
(url, repositoryId) => {
|
|
2230
|
-
if (!data)
|
|
2231
|
-
throw new ReviewInputError("Repositories are unavailable.");
|
|
2232
|
-
|
|
2233
|
-
return data.resolvePullRequest(url, { id: repositoryId });
|
|
2234
|
-
},
|
|
2235
|
-
(id) => store.read(id),
|
|
2236
|
-
),
|
|
2254
|
+
await pullRequestTargets(body, register, (id) => store.read(id)),
|
|
2237
2255
|
register,
|
|
2238
2256
|
),
|
|
2239
2257
|
(id) => (store.has(id) ? store.read(id) : undefined),
|
|
@@ -2433,10 +2451,6 @@ async function pullRequestTargets(
|
|
|
2433
2451
|
// oxlint-disable-next-line anti-slop/no-unknown-parameters -- Request body boundary: commandSchema parses the result.
|
|
2434
2452
|
body: unknown,
|
|
2435
2453
|
register: (path: string) => Promise<{ id: string }>,
|
|
2436
|
-
resolve: (
|
|
2437
|
-
url: string,
|
|
2438
|
-
repositoryId: string,
|
|
2439
|
-
) => Promise<{ target: ReviewTarget }>,
|
|
2440
2454
|
board: (id: string) => void,
|
|
2441
2455
|
) {
|
|
2442
2456
|
const target = z.strictObject({
|
|
@@ -2480,7 +2494,7 @@ async function pullRequestTargets(
|
|
|
2480
2494
|
...located.data,
|
|
2481
2495
|
operation: {
|
|
2482
2496
|
...operation,
|
|
2483
|
-
|
|
2497
|
+
repositoryId,
|
|
2484
2498
|
pullRequestUrl: url,
|
|
2485
2499
|
},
|
|
2486
2500
|
};
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
gitCommonDir,
|
|
21
21
|
listCommitRange,
|
|
22
22
|
listTrackedFilesAtCommit,
|
|
23
|
+
parseGitRemote,
|
|
23
24
|
readFileAtCommit,
|
|
24
25
|
resolveRepoContext,
|
|
25
26
|
runVcs,
|
|
@@ -39,6 +40,10 @@ import type {
|
|
|
39
40
|
} from "@parcel/watcher";
|
|
40
41
|
import { textIncludesQuote } from "@review/evidence.js";
|
|
41
42
|
import { isMissingFileError } from "@review/fs-utils.js";
|
|
43
|
+
import {
|
|
44
|
+
type GitHubPullRequest,
|
|
45
|
+
readGitHubPullRequests,
|
|
46
|
+
} from "@review/github-pr-graph.js";
|
|
42
47
|
import { reviewManagedCheckoutRoot } from "@review/review-checkout-paths.js";
|
|
43
48
|
import { ensureReviewPinnedCheckout } from "@review/review-head-checkout.js";
|
|
44
49
|
import { StructuralComparisons } from "@review/server/structural-comparisons.js";
|
|
@@ -79,6 +84,7 @@ import {
|
|
|
79
84
|
} from "./document.js";
|
|
80
85
|
import { decodeImage } from "./image-decode.js";
|
|
81
86
|
import { mapInputSchema } from "./map-input.js";
|
|
87
|
+
import { pullRequestKey, sourceBranchName } from "./origin.js";
|
|
82
88
|
import {
|
|
83
89
|
type PullRequestAddress,
|
|
84
90
|
type PullRequestProvider,
|
|
@@ -1052,22 +1058,101 @@ export class LocalReviewData {
|
|
|
1052
1058
|
|
|
1053
1059
|
return snapshot.pins;
|
|
1054
1060
|
}
|
|
1061
|
+
/** Optional forge context; never changes the comparison's pins. */
|
|
1062
|
+
private readonly readGitHubApi = (host: string, endpoint: string) =>
|
|
1063
|
+
(this.options.pullRequests ?? defaultPullRequestDeps).run(
|
|
1064
|
+
"gh",
|
|
1065
|
+
["api", "--hostname", { value: host }, { value: endpoint }],
|
|
1066
|
+
{ timeoutMs: 10_000 },
|
|
1067
|
+
);
|
|
1068
|
+
|
|
1069
|
+
async findPullRequest(
|
|
1070
|
+
pins: Pins,
|
|
1071
|
+
branch: string,
|
|
1072
|
+
): Promise<GitHubPullRequest | undefined> {
|
|
1073
|
+
const context = await resolveRepoContext(
|
|
1074
|
+
this.store.repositoryPath(pins.repositoryId),
|
|
1075
|
+
);
|
|
1076
|
+
|
|
1077
|
+
if (!context?.githubSlug || !context.originUrl) return undefined;
|
|
1078
|
+
const remote = parseGitRemote(context.originUrl);
|
|
1079
|
+
|
|
1080
|
+
if (!remote) return undefined;
|
|
1081
|
+
const name = sourceBranchName(branch);
|
|
1082
|
+
|
|
1083
|
+
const matches = (
|
|
1084
|
+
await readGitHubPullRequests(
|
|
1085
|
+
remote.host,
|
|
1086
|
+
context.githubSlug,
|
|
1087
|
+
this.readGitHubApi,
|
|
1088
|
+
)
|
|
1089
|
+
).filter((pr) => pr.headBranch === name);
|
|
1090
|
+
|
|
1091
|
+
return matches.length === 1 ? matches[0] : undefined;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/** Enrich current display metadata without repinning or rewriting history. */
|
|
1095
|
+
async discoverPullRequest(snapshot: Snapshot): Promise<Snapshot> {
|
|
1096
|
+
const { origin, pins } = snapshot;
|
|
1097
|
+
|
|
1098
|
+
if (!pins || !origin?.branch) return snapshot;
|
|
1099
|
+
|
|
1100
|
+
const found = await this.findPullRequest(pins, origin.branch).catch(
|
|
1101
|
+
() => undefined,
|
|
1102
|
+
);
|
|
1103
|
+
|
|
1104
|
+
if (!found) return snapshot;
|
|
1105
|
+
|
|
1106
|
+
// A manually selected PR must not be replaced by a branch-name match.
|
|
1107
|
+
if (
|
|
1108
|
+
origin.pullRequestUrl &&
|
|
1109
|
+
pullRequestKey(origin.pullRequestUrl) !== pullRequestKey(found.url)
|
|
1110
|
+
)
|
|
1111
|
+
return snapshot;
|
|
1112
|
+
|
|
1113
|
+
return {
|
|
1114
|
+
...snapshot,
|
|
1115
|
+
origin: {
|
|
1116
|
+
...origin,
|
|
1117
|
+
pullRequestUrl: found.url,
|
|
1118
|
+
pullRequestNumber: found.number,
|
|
1119
|
+
pullRequestTitle: found.title,
|
|
1120
|
+
pullRequestBaseBranch: found.baseBranch,
|
|
1121
|
+
},
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1055
1125
|
/** Capture a label only when its ref still resolves to these exact pins. */
|
|
1056
1126
|
async headBranch(pins: Pins, headRef?: string): Promise<string | undefined> {
|
|
1127
|
+
const context = await resolveRepoContext(
|
|
1128
|
+
this.store.repositoryPath(pins.repositoryId),
|
|
1129
|
+
);
|
|
1130
|
+
|
|
1131
|
+
if (!context) return undefined;
|
|
1132
|
+
|
|
1057
1133
|
const vcs = await this.vcs(pins.repositoryId);
|
|
1058
1134
|
|
|
1059
|
-
|
|
1135
|
+
const symbolicRef =
|
|
1136
|
+
headRef &&
|
|
1137
|
+
headRef !== "HEAD" &&
|
|
1138
|
+
headRef !== "@" &&
|
|
1139
|
+
!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i.test(headRef)
|
|
1140
|
+
? headRef
|
|
1141
|
+
: undefined;
|
|
1142
|
+
|
|
1143
|
+
let location: VcsArg[];
|
|
1144
|
+
|
|
1145
|
+
if (symbolicRef) location = ["--git-dir", context.commonDir];
|
|
1146
|
+
else if (vcs?.kind === "git") location = ["-C", vcs.rootPath];
|
|
1147
|
+
else return undefined;
|
|
1060
1148
|
|
|
1061
1149
|
try {
|
|
1062
1150
|
const { stdout } = await runVcs("git", [
|
|
1063
|
-
|
|
1064
|
-
vcs.rootPath,
|
|
1151
|
+
...location,
|
|
1065
1152
|
"rev-parse",
|
|
1066
1153
|
"--symbolic-full-name",
|
|
1067
1154
|
"--verify",
|
|
1068
|
-
|
|
1069
|
-
? "HEAD"
|
|
1070
|
-
: { rev: headRef },
|
|
1155
|
+
symbolicRef ? { rev: symbolicRef } : "HEAD",
|
|
1071
1156
|
]);
|
|
1072
1157
|
|
|
1073
1158
|
const ref = stdout.trim();
|
|
@@ -1075,13 +1160,18 @@ export class LocalReviewData {
|
|
|
1075
1160
|
if (!ref.startsWith("refs/heads/") && !ref.startsWith("refs/remotes/"))
|
|
1076
1161
|
return undefined;
|
|
1077
1162
|
|
|
1078
|
-
const
|
|
1163
|
+
const { stdout: commit } = await runVcs("git", [
|
|
1164
|
+
...location,
|
|
1165
|
+
"rev-parse",
|
|
1166
|
+
{ rev: ref },
|
|
1167
|
+
]);
|
|
1079
1168
|
|
|
1080
|
-
if (
|
|
1169
|
+
if (commit.trim() !== pins.head) return undefined;
|
|
1081
1170
|
|
|
1082
|
-
return ref
|
|
1171
|
+
return ref
|
|
1172
|
+
.replace(/^refs\/remotes\/origin\//, "")
|
|
1173
|
+
.replace(/^refs\/(heads|remotes)\//, "");
|
|
1083
1174
|
} catch {
|
|
1084
|
-
// Optional provenance must not block authoring in a detached checkout.
|
|
1085
1175
|
return undefined;
|
|
1086
1176
|
}
|
|
1087
1177
|
}
|
|
@@ -2215,6 +2305,7 @@ export function openLocalReviewStore(
|
|
|
2215
2305
|
resolvePullRequest: (url, repository) =>
|
|
2216
2306
|
data.resolvePullRequest(url, repository),
|
|
2217
2307
|
pullRequestTitle: (url) => data.pullRequestTitle(url),
|
|
2308
|
+
findPullRequest: (pins, branch) => data.findPullRequest(pins, branch),
|
|
2218
2309
|
headBranch: (pins, headRef) => data.headBranch(pins, headRef),
|
|
2219
2310
|
sourcePins: (snapshot) => data.sourcePins(snapshot),
|
|
2220
2311
|
unavailableAnchors: (snapshot) => data.unavailableAnchors(snapshot),
|
package/src/review-api/origin.ts
CHANGED
|
@@ -16,11 +16,18 @@ export function pullRequestKey(url: string): string {
|
|
|
16
16
|
return url.toLowerCase();
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
/**
|
|
19
|
+
/** Older snapshots kept origin/ on remote branch labels. */
|
|
20
|
+
export const sourceBranchName = (branch: string | undefined) =>
|
|
21
|
+
branch?.replace(/^origin\//, "");
|
|
22
|
+
|
|
23
|
+
/** Omission preserves identity; null removes the PR without changing import
|
|
24
|
+
* metadata. A new url keeps the title and PR base when the caller omits them
|
|
25
|
+
* for the same PR. */
|
|
20
26
|
export function setPullRequest(
|
|
21
27
|
snapshot: Pick<Snapshot, "origin">,
|
|
22
28
|
url: string | null | undefined,
|
|
23
29
|
title?: string,
|
|
30
|
+
baseBranch?: string,
|
|
24
31
|
) {
|
|
25
32
|
if (url === undefined) return;
|
|
26
33
|
|
|
@@ -29,19 +36,20 @@ export function setPullRequest(
|
|
|
29
36
|
delete snapshot.origin.pullRequestUrl;
|
|
30
37
|
delete snapshot.origin.pullRequestNumber;
|
|
31
38
|
delete snapshot.origin.pullRequestTitle;
|
|
39
|
+
delete snapshot.origin.pullRequestBaseBranch;
|
|
32
40
|
}
|
|
33
41
|
|
|
34
42
|
return;
|
|
35
43
|
}
|
|
36
44
|
|
|
45
|
+
const same = snapshot.origin?.pullRequestUrl === url;
|
|
37
46
|
snapshot.origin = {
|
|
38
47
|
...snapshot.origin,
|
|
39
48
|
pullRequestUrl: url,
|
|
40
49
|
pullRequestNumber: Number(url.split("/").at(-1)),
|
|
41
50
|
pullRequestTitle:
|
|
42
|
-
title ??
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
: undefined),
|
|
51
|
+
title ?? (same ? snapshot.origin?.pullRequestTitle : undefined),
|
|
52
|
+
pullRequestBaseBranch:
|
|
53
|
+
baseBranch ?? (same ? snapshot.origin?.pullRequestBaseBranch : undefined),
|
|
46
54
|
};
|
|
47
55
|
}
|