@effect-agent/pr-review 0.1.0-beta.8 → 0.1.0-beta.9
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/README.md +10 -0
- package/dist/action.d.mts +9 -4
- package/dist/action.mjs +22 -5
- package/dist/action.mjs.map +1 -1
- package/dist/cli.mjs +4 -3
- package/dist/cli.mjs.map +1 -1
- package/dist/{fan-out-BBEATQwc.d.mts → fan-out-C6gq3CFg.d.mts} +87 -3
- package/dist/{github-BZNzmxao.mjs → github-Lfa_ox-u.mjs} +308 -7
- package/dist/github-Lfa_ox-u.mjs.map +1 -0
- package/dist/index.d.mts +4 -3
- package/dist/index.mjs +3 -3
- package/dist/{providers-J6BKHyHe.mjs → providers-CaOnz7mK.mjs} +5 -4
- package/dist/{providers-J6BKHyHe.mjs.map → providers-CaOnz7mK.mjs.map} +1 -1
- package/dist/testing.d.mts +1 -1
- package/dist/testing.mjs +5 -3
- package/dist/testing.mjs.map +1 -1
- package/package.json +2 -2
- package/src/action.ts +32 -1
- package/src/cli.ts +2 -1
- package/src/index.ts +1 -0
- package/src/internal/action-entry.ts +1 -0
- package/src/internal/fixtures.ts +5 -1
- package/src/internal/github-env.ts +20 -6
- package/src/internal/github.ts +232 -2
- package/src/internal/retirement.ts +332 -0
- package/dist/github-BZNzmxao.mjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Context, Effect, Encoding, Layer, Option, Redacted, Result, Schema } from "effect";
|
|
1
|
+
import { Context, DateTime, Effect, Encoding, Layer, Option, Redacted, Result, Schema } from "effect";
|
|
2
2
|
import { Agent, AgentPolicy, Subagent, SubagentPolicy, SubagentRuntime, ToolExecutionClass } from "effect-agent";
|
|
3
3
|
import { Tool, Toolkit } from "effect/unstable/ai";
|
|
4
4
|
import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";
|
|
@@ -1094,11 +1094,202 @@ const buildProfileMission = (metadata, files) => ReviewMission.make({
|
|
|
1094
1094
|
changedFileCount: files.length
|
|
1095
1095
|
});
|
|
1096
1096
|
//#endregion
|
|
1097
|
+
//#region src/internal/retirement.ts
|
|
1098
|
+
const PositiveLine = Schema.Int.check(Schema.isGreaterThan(0));
|
|
1099
|
+
/** One previously posted review as observed through the retirement host. */
|
|
1100
|
+
var RetirableReview = class extends Schema.Class("@effect-agent/pr-review/RetirableReview")({
|
|
1101
|
+
reviewId: Schema.Int.check(Schema.isGreaterThan(0)),
|
|
1102
|
+
body: Schema.String.check(Schema.isMaxLength(6e4)),
|
|
1103
|
+
commitSha: Schema.NonEmptyString.check(Schema.isMaxLength(64)),
|
|
1104
|
+
authorNodeId: Schema.NullOr(Schema.NonEmptyString.check(Schema.isMaxLength(200))),
|
|
1105
|
+
submittedAt: Schema.NullOr(Schema.DateTimeUtc)
|
|
1106
|
+
}) {};
|
|
1107
|
+
/** One inline comment attached to a previously posted review. */
|
|
1108
|
+
var RetirableReviewComment = class extends Schema.Class("@effect-agent/pr-review/RetirableReviewComment")({
|
|
1109
|
+
nodeId: Schema.NonEmptyString.check(Schema.isMaxLength(200)),
|
|
1110
|
+
path: Schema.NonEmptyString.check(Schema.isMaxLength(500)),
|
|
1111
|
+
startLine: Schema.NullOr(PositiveLine),
|
|
1112
|
+
endLine: Schema.NullOr(PositiveLine),
|
|
1113
|
+
body: Schema.String.check(Schema.isMaxLength(65536))
|
|
1114
|
+
}) {};
|
|
1115
|
+
/** A GitHub retirement read or mutation failed. */
|
|
1116
|
+
var ReviewRetirementFailure = class extends Schema.TaggedError()("ReviewRetirementFailure", {
|
|
1117
|
+
operation: Schema.String,
|
|
1118
|
+
reason: Schema.String
|
|
1119
|
+
}) {
|
|
1120
|
+
get message() {
|
|
1121
|
+
return `Review retirement operation '${this.operation}' failed: ${this.reason}`;
|
|
1122
|
+
}
|
|
1123
|
+
};
|
|
1124
|
+
/**
|
|
1125
|
+
* Host-side GitHub operations used by retirement. Domain code never reaches
|
|
1126
|
+
* into REST or GraphQL directly, and deterministic tests substitute this port.
|
|
1127
|
+
*/
|
|
1128
|
+
var ReviewRetirementHost = class extends Context.Service()("@effect-agent/pr-review/ReviewRetirementHost") {};
|
|
1129
|
+
/** Observable cosmetic work completed by one fail-open retirement pass. */
|
|
1130
|
+
var ReviewRetirementReport = class extends Schema.Class("@effect-agent/pr-review/ReviewRetirementReport")({
|
|
1131
|
+
reviewsRetired: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
1132
|
+
findingsResolved: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
1133
|
+
commentsMinimized: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
1134
|
+
failures: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))
|
|
1135
|
+
}) {};
|
|
1136
|
+
const findingIdentity = (finding) => `${finding.path}\u0000${finding.startLine}\u0000${finding.endLine}\u0000${finding.title}`;
|
|
1137
|
+
const REVIEW_METADATA_PATTERN = /<!-- effect-agent-pr-review metadata\n[\s\S]*?\n-->/g;
|
|
1138
|
+
const FINGERPRINT_PATTERN = /<!-- effect-agent-pr-review fingerprint=sha256:[0-9a-f]{64} -->/g;
|
|
1139
|
+
const STATE_PATTERN = /<!-- effect-agent-pr-review state-v1:[A-Za-z0-9+/]+={0,2}\.[0-9a-f]{64} -->/g;
|
|
1140
|
+
const RETIRED_ORIGINAL_PATTERN = /<!-- effect-agent-pr-review retired-original:start -->\n([\s\S]*?)\n<!-- effect-agent-pr-review retired-original:end -->/;
|
|
1141
|
+
const MACHINE_COMMENT_PATTERN = new RegExp(`${REVIEW_METADATA_PATTERN.source}|${FINGERPRINT_PATTERN.source}|${STATE_PATTERN.source}`, "g");
|
|
1142
|
+
const VERDICT_CALLOUT_PATTERN = /^(?:> \[!(?:CAUTION|IMPORTANT)\]\n> [^\n]*(?:\n> [^\n]*)*|> (?:ℹ️|✅)[^\n]*)\n*/;
|
|
1143
|
+
const INLINE_FINDING_TITLE_PATTERN = /^\*\*\[(?:🛑 blocking|⚠️ important|💅 nit)\] ([^\n]+)\*\*$/;
|
|
1144
|
+
const MAX_REVIEW_BODY_CHARS = 6e4;
|
|
1145
|
+
/** The host-authored metadata marker is the authority gate for any edit. */
|
|
1146
|
+
const hasReviewMetadataMarker = (body) => /<!-- effect-agent-pr-review metadata\n/.test(body);
|
|
1147
|
+
const machineComments = (body) => Array.from(body.matchAll(MACHINE_COMMENT_PATTERN), (match) => match[0]);
|
|
1148
|
+
const originalVisibleBody = (body) => {
|
|
1149
|
+
const retired = RETIRED_ORIGINAL_PATTERN.exec(body)?.[1];
|
|
1150
|
+
if (retired !== void 0) return retired;
|
|
1151
|
+
return body.replace(MACHINE_COMMENT_PATTERN, "").trim().replace(VERDICT_CALLOUT_PATTERN, "");
|
|
1152
|
+
};
|
|
1153
|
+
const findingLocation = (finding) => `${finding.path}:${finding.startLine}${finding.endLine === finding.startLine ? "" : `-${finding.endLine}`}`;
|
|
1154
|
+
const renderRetiredBody = (input) => {
|
|
1155
|
+
const shortSha = input.currentState.reviewedHeadSha.slice(0, 7);
|
|
1156
|
+
const comments = machineComments(input.priorBody);
|
|
1157
|
+
const original = originalVisibleBody(input.priorBody);
|
|
1158
|
+
const resolved = input.resolvedFindings.length === 0 ? [] : [
|
|
1159
|
+
"### Findings resolved by later review",
|
|
1160
|
+
"",
|
|
1161
|
+
...input.resolvedFindings.map((finding) => `- \`${findingLocation(finding)}\` ~~${finding.title}~~ · resolved at \`${shortSha}\``),
|
|
1162
|
+
""
|
|
1163
|
+
];
|
|
1164
|
+
const prefix = [
|
|
1165
|
+
`> ℹ️ Superseded — ${input.resolvedFindings.length} of ${input.priorState.unresolvedFindings.length} findings resolved at \`${shortSha}\`; see [the latest review](${input.currentReviewUrl}).`,
|
|
1166
|
+
"",
|
|
1167
|
+
"<details>",
|
|
1168
|
+
"<summary>Previous review details</summary>",
|
|
1169
|
+
"",
|
|
1170
|
+
...resolved,
|
|
1171
|
+
"<!-- effect-agent-pr-review retired-original:start -->"
|
|
1172
|
+
];
|
|
1173
|
+
const suffix = [
|
|
1174
|
+
"<!-- effect-agent-pr-review retired-original:end -->",
|
|
1175
|
+
"",
|
|
1176
|
+
"</details>",
|
|
1177
|
+
...comments.length === 0 ? [] : ["", ...comments]
|
|
1178
|
+
];
|
|
1179
|
+
const render = (visible) => [
|
|
1180
|
+
...prefix,
|
|
1181
|
+
visible,
|
|
1182
|
+
...suffix
|
|
1183
|
+
].join("\n");
|
|
1184
|
+
if (render(original).length <= MAX_REVIEW_BODY_CHARS) return render(original);
|
|
1185
|
+
const truncationNotice = "\n\n_Original review content truncated during retirement._";
|
|
1186
|
+
const budget = Math.max(0, MAX_REVIEW_BODY_CHARS - render(truncationNotice).length);
|
|
1187
|
+
return render(`${original.slice(0, budget)}${truncationNotice}`);
|
|
1188
|
+
};
|
|
1189
|
+
/** Compute one prior review's resolved subset and deterministic retired body. */
|
|
1190
|
+
const decideReviewRetirement = (input) => {
|
|
1191
|
+
const current = new Set(input.currentState.unresolvedFindings.map(findingIdentity));
|
|
1192
|
+
const resolvedFindings = input.priorState.unresolvedFindings.filter((finding) => !current.has(findingIdentity(finding)));
|
|
1193
|
+
return {
|
|
1194
|
+
body: renderRetiredBody({
|
|
1195
|
+
...input,
|
|
1196
|
+
resolvedFindings
|
|
1197
|
+
}),
|
|
1198
|
+
resolvedFindings,
|
|
1199
|
+
priorFindingCount: input.priorState.unresolvedFindings.length
|
|
1200
|
+
};
|
|
1201
|
+
};
|
|
1202
|
+
const inlineCommentIdentity = (comment) => {
|
|
1203
|
+
if (comment.startLine === null || comment.endLine === null) return void 0;
|
|
1204
|
+
const firstLine = comment.body.split("\n", 1)[0] ?? "";
|
|
1205
|
+
const title = INLINE_FINDING_TITLE_PATTERN.exec(firstLine)?.[1];
|
|
1206
|
+
return title === void 0 ? void 0 : findingIdentity({
|
|
1207
|
+
path: comment.path,
|
|
1208
|
+
startLine: comment.startLine,
|
|
1209
|
+
endLine: comment.endLine,
|
|
1210
|
+
title
|
|
1211
|
+
});
|
|
1212
|
+
};
|
|
1213
|
+
const failOpen = (effect, fallback, message) => effect.pipe(Effect.catch((error) => Effect.logWarning(`${message}: ${String(error)}`).pipe(Effect.as(fallback))));
|
|
1214
|
+
const isStrictlyOlderReview = (review, input) => {
|
|
1215
|
+
if (review.submittedAt === null) return false;
|
|
1216
|
+
const submittedAt = DateTime.toEpochMillis(review.submittedAt);
|
|
1217
|
+
const currentSubmittedAt = DateTime.toEpochMillis(input.currentSubmittedAt);
|
|
1218
|
+
return submittedAt < currentSubmittedAt || submittedAt === currentSubmittedAt && review.reviewId < input.currentReviewId;
|
|
1219
|
+
};
|
|
1220
|
+
/**
|
|
1221
|
+
* Retire every marker-bearing prior review against the newest posted state.
|
|
1222
|
+
* Every lookup, edit, and minimization is isolated: retirement is cosmetic
|
|
1223
|
+
* and can never change the run or check outcome.
|
|
1224
|
+
*/
|
|
1225
|
+
const retireStaleReviews = Effect.fn("retireStaleReviews")(function* (input) {
|
|
1226
|
+
const host = yield* ReviewRetirementHost;
|
|
1227
|
+
const authenticator = yield* ReviewStateAuthenticator;
|
|
1228
|
+
if (authenticator.status !== "available") {
|
|
1229
|
+
yield* Effect.logWarning("Skipping stale-review retirement because authenticated review state is unavailable.");
|
|
1230
|
+
return ReviewRetirementReport.make({
|
|
1231
|
+
reviewsRetired: 0,
|
|
1232
|
+
findingsResolved: 0,
|
|
1233
|
+
commentsMinimized: 0,
|
|
1234
|
+
failures: 0
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
let failures = 0;
|
|
1238
|
+
let reviewsRetired = 0;
|
|
1239
|
+
let findingsResolved = 0;
|
|
1240
|
+
let commentsMinimized = 0;
|
|
1241
|
+
const reviews = yield* failOpen(host.listReviews, void 0, "Could not list prior reviews");
|
|
1242
|
+
if (reviews === void 0) return ReviewRetirementReport.make({
|
|
1243
|
+
reviewsRetired,
|
|
1244
|
+
findingsResolved,
|
|
1245
|
+
commentsMinimized,
|
|
1246
|
+
failures: 1
|
|
1247
|
+
});
|
|
1248
|
+
for (const review of reviews) {
|
|
1249
|
+
if (review.authorNodeId !== input.currentAuthorNodeId || !isStrictlyOlderReview(review, input) || !hasReviewMetadataMarker(review.body)) continue;
|
|
1250
|
+
const priorState = yield* failOpen(authenticator.extract(review.body), Option.none(), `Could not authenticate prior review ${review.reviewId}`);
|
|
1251
|
+
if (Option.isNone(priorState)) continue;
|
|
1252
|
+
const decision = decideReviewRetirement({
|
|
1253
|
+
priorBody: review.body,
|
|
1254
|
+
priorState: priorState.value,
|
|
1255
|
+
currentState: input.currentState,
|
|
1256
|
+
currentReviewUrl: input.currentReviewUrl
|
|
1257
|
+
});
|
|
1258
|
+
if (yield* failOpen(host.updateBody(review.reviewId, decision.body).pipe(Effect.as(true)), false, `Could not retire prior review ${review.reviewId}`)) {
|
|
1259
|
+
reviewsRetired += 1;
|
|
1260
|
+
findingsResolved += decision.resolvedFindings.length;
|
|
1261
|
+
} else failures += 1;
|
|
1262
|
+
if (decision.resolvedFindings.length === 0) continue;
|
|
1263
|
+
const comments = yield* failOpen(host.listComments(review.reviewId), void 0, `Could not list inline comments for prior review ${review.reviewId}`);
|
|
1264
|
+
if (comments === void 0) {
|
|
1265
|
+
failures += 1;
|
|
1266
|
+
continue;
|
|
1267
|
+
}
|
|
1268
|
+
const resolved = new Set(decision.resolvedFindings.map(findingIdentity));
|
|
1269
|
+
for (const comment of comments) {
|
|
1270
|
+
const identity = inlineCommentIdentity(comment);
|
|
1271
|
+
if (identity === void 0 || !resolved.has(identity)) continue;
|
|
1272
|
+
if (yield* failOpen(host.minimizeComment(comment.nodeId).pipe(Effect.as(true)), false, `Could not minimize resolved inline comment ${comment.nodeId}`)) commentsMinimized += 1;
|
|
1273
|
+
else failures += 1;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
return ReviewRetirementReport.make({
|
|
1277
|
+
reviewsRetired,
|
|
1278
|
+
findingsResolved,
|
|
1279
|
+
commentsMinimized,
|
|
1280
|
+
failures
|
|
1281
|
+
});
|
|
1282
|
+
});
|
|
1283
|
+
//#endregion
|
|
1097
1284
|
//#region src/internal/github.ts
|
|
1285
|
+
const defaultGraphqlUrl = (apiUrl) => apiUrl === "https://api.github.com" ? "https://api.github.com/graphql" : apiUrl.replace(/\/api\/v3$/, "/api/graphql");
|
|
1098
1286
|
/** Which pull request to review and how to reach the API. */
|
|
1099
1287
|
var GitHubReviewTarget = class GitHubReviewTarget extends Context.Service()("@effect-agent/pr-review/GitHubReviewTarget") {
|
|
1100
1288
|
static layer(config) {
|
|
1101
|
-
return Layer.succeed(this, GitHubReviewTarget.of(
|
|
1289
|
+
return Layer.succeed(this, GitHubReviewTarget.of({
|
|
1290
|
+
...config,
|
|
1291
|
+
graphqlUrl: config.graphqlUrl ?? defaultGraphqlUrl(config.apiUrl)
|
|
1292
|
+
}));
|
|
1102
1293
|
}
|
|
1103
1294
|
};
|
|
1104
1295
|
/** A GitHub API call failed: transport, status, or payload decode. */
|
|
@@ -1133,16 +1324,46 @@ const GitHubFileWire = Schema.Struct({
|
|
|
1133
1324
|
previous_filename: Schema.optionalKey(Schema.String)
|
|
1134
1325
|
});
|
|
1135
1326
|
const GitHubFilesPageWire = Schema.Array(GitHubFileWire);
|
|
1327
|
+
const GitHubActorWire = Schema.Struct({ node_id: Schema.String });
|
|
1136
1328
|
const GitHubReviewWire = Schema.Struct({
|
|
1137
1329
|
id: Schema.Int,
|
|
1138
|
-
html_url: Schema.String
|
|
1330
|
+
html_url: Schema.String,
|
|
1331
|
+
user: Schema.NullOr(GitHubActorWire),
|
|
1332
|
+
submitted_at: Schema.NullOr(Schema.String)
|
|
1333
|
+
});
|
|
1334
|
+
const GitHubRetirableReviewWire = Schema.Struct({
|
|
1335
|
+
id: Schema.Int,
|
|
1336
|
+
body: Schema.NullOr(Schema.String),
|
|
1337
|
+
commit_id: Schema.String,
|
|
1338
|
+
user: Schema.NullOr(GitHubActorWire),
|
|
1339
|
+
submitted_at: Schema.NullOr(Schema.String)
|
|
1139
1340
|
});
|
|
1341
|
+
const GitHubRetirableReviewsPageWire = Schema.Array(GitHubRetirableReviewWire);
|
|
1342
|
+
const GitHubReviewCommentWire = Schema.Struct({
|
|
1343
|
+
node_id: Schema.String,
|
|
1344
|
+
path: Schema.String,
|
|
1345
|
+
body: Schema.String,
|
|
1346
|
+
line: Schema.NullOr(Schema.Int),
|
|
1347
|
+
original_line: Schema.NullOr(Schema.Int),
|
|
1348
|
+
start_line: Schema.optionalKey(Schema.NullOr(Schema.Int)),
|
|
1349
|
+
original_start_line: Schema.optionalKey(Schema.NullOr(Schema.Int))
|
|
1350
|
+
});
|
|
1351
|
+
const GitHubReviewCommentsPageWire = Schema.Array(GitHubReviewCommentWire);
|
|
1352
|
+
const GitHubMinimizeCommentWire = Schema.Struct({
|
|
1353
|
+
data: Schema.optionalKey(Schema.NullOr(Schema.Struct({ minimizeComment: Schema.NullOr(Schema.Struct({ minimizedComment: Schema.NullOr(Schema.Struct({ isMinimized: Schema.Boolean })) })) }))),
|
|
1354
|
+
errors: Schema.optionalKey(Schema.Array(Schema.Struct({ message: Schema.String })))
|
|
1355
|
+
});
|
|
1356
|
+
/** Decode GitHub's external timestamp before it participates in mutation ordering. */
|
|
1357
|
+
const parseGitHubSubmittedAt = (value) => value === null ? null : Option.getOrNull(DateTime.make(value));
|
|
1140
1358
|
/** The publication receipt callers report back to the operator. */
|
|
1141
1359
|
var PublishedReview = class extends Schema.Class("@effect-agent/pr-review/PublishedReview")({
|
|
1142
1360
|
reviewId: Schema.Int,
|
|
1143
1361
|
url: Schema.String,
|
|
1144
1362
|
event: Schema.String,
|
|
1145
|
-
inlineComments: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))
|
|
1363
|
+
inlineComments: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
1364
|
+
/** Actor and ordering boundary returned by the create-review response. */
|
|
1365
|
+
authorNodeId: Schema.NullOr(Schema.NonEmptyString.check(Schema.isMaxLength(200))),
|
|
1366
|
+
submittedAt: Schema.NullOr(Schema.DateTimeUtc)
|
|
1146
1367
|
}) {};
|
|
1147
1368
|
/** Posts one planned review; the ONLY mutating operation in this package. */
|
|
1148
1369
|
var ReviewPublisher = class extends Context.Service()("@effect-agent/pr-review/ReviewPublisher") {};
|
|
@@ -1266,10 +1487,90 @@ const gitHubReviewPublisherLayer = Layer.effect(ReviewPublisher)(Effect.gen(func
|
|
|
1266
1487
|
reviewId: wire.id,
|
|
1267
1488
|
url: wire.html_url,
|
|
1268
1489
|
event: plan.event,
|
|
1269
|
-
inlineComments: plan.comments.length
|
|
1490
|
+
inlineComments: plan.comments.length,
|
|
1491
|
+
authorNodeId: wire.user?.node_id ?? null,
|
|
1492
|
+
submittedAt: parseGitHubSubmittedAt(wire.submitted_at)
|
|
1270
1493
|
});
|
|
1271
1494
|
}) });
|
|
1272
1495
|
}));
|
|
1496
|
+
const MAX_RETIREMENT_PAGES = 5;
|
|
1497
|
+
const MINIMIZE_REVIEW_COMMENT_MUTATION = `mutation MinimizeReviewComment($subjectId: ID!) {
|
|
1498
|
+
minimizeComment(input: { subjectId: $subjectId, classifier: OUTDATED }) {
|
|
1499
|
+
minimizedComment { isMinimized }
|
|
1500
|
+
}
|
|
1501
|
+
}`;
|
|
1502
|
+
/** GitHub-backed host operations for cosmetic retirement after publication. */
|
|
1503
|
+
const gitHubReviewRetirementHostLayer = Layer.effect(ReviewRetirementHost)(Effect.gen(function* () {
|
|
1504
|
+
const target = yield* GitHubReviewTarget;
|
|
1505
|
+
const client = yield* HttpClient.HttpClient;
|
|
1506
|
+
const prefix = `${target.apiUrl}/repos/${target.repository}/pulls/${target.number}`;
|
|
1507
|
+
const asRetirementFailure = (operation) => (error) => ReviewRetirementFailure.make({
|
|
1508
|
+
operation,
|
|
1509
|
+
reason: `${error._tag}: ${error.message ?? "request failed"}`.slice(0, 2048)
|
|
1510
|
+
});
|
|
1511
|
+
const executeRetirement = (operation, request) => HttpClient.execute(request).pipe(Effect.flatMap(HttpClientResponse.filterStatusOk), Effect.mapError(asRetirementFailure(operation)), Effect.provideService(HttpClient.HttpClient, client));
|
|
1512
|
+
const decodeRetirement = (schema, operation) => {
|
|
1513
|
+
const decode = Schema.decodeUnknownEffect(schema);
|
|
1514
|
+
return (response) => response.json.pipe(Effect.mapError(asRetirementFailure(operation)), Effect.flatMap((body) => decode(body).pipe(Effect.mapError(asRetirementFailure(operation)))));
|
|
1515
|
+
};
|
|
1516
|
+
const listPaged = (input) => Effect.gen(function* () {
|
|
1517
|
+
const values = [];
|
|
1518
|
+
const perPage = 100;
|
|
1519
|
+
for (let page = 1; page <= MAX_RETIREMENT_PAGES; page += 1) {
|
|
1520
|
+
const response = yield* executeRetirement(input.operation, withCommonHeaders(HttpClientRequest.get(input.url).pipe(HttpClientRequest.acceptJson, HttpClientRequest.setUrlParams({
|
|
1521
|
+
per_page: String(perPage),
|
|
1522
|
+
page: String(page)
|
|
1523
|
+
})), target.token));
|
|
1524
|
+
const pageValues = yield* input.decode(response);
|
|
1525
|
+
values.push(...pageValues);
|
|
1526
|
+
if (pageValues.length < perPage) return values;
|
|
1527
|
+
}
|
|
1528
|
+
return yield* ReviewRetirementFailure.make({
|
|
1529
|
+
operation: input.operation,
|
|
1530
|
+
reason: `history exceeds the bounded ${MAX_RETIREMENT_PAGES * 100}-item lookup`
|
|
1531
|
+
});
|
|
1532
|
+
});
|
|
1533
|
+
return ReviewRetirementHost.of({
|
|
1534
|
+
listReviews: listPaged({
|
|
1535
|
+
operation: "listReviewsForRetirement",
|
|
1536
|
+
url: `${prefix}/reviews`,
|
|
1537
|
+
decode: decodeRetirement(GitHubRetirableReviewsPageWire, "listReviewsForRetirement")
|
|
1538
|
+
}).pipe(Effect.map((reviews) => reviews.map((review) => RetirableReview.make({
|
|
1539
|
+
reviewId: review.id,
|
|
1540
|
+
body: review.body ?? "",
|
|
1541
|
+
commitSha: review.commit_id,
|
|
1542
|
+
authorNodeId: review.user?.node_id ?? null,
|
|
1543
|
+
submittedAt: parseGitHubSubmittedAt(review.submitted_at)
|
|
1544
|
+
})))),
|
|
1545
|
+
listComments: (reviewId) => listPaged({
|
|
1546
|
+
operation: "listReviewCommentsForRetirement",
|
|
1547
|
+
url: `${prefix}/reviews/${reviewId}/comments`,
|
|
1548
|
+
decode: decodeRetirement(GitHubReviewCommentsPageWire, "listReviewCommentsForRetirement")
|
|
1549
|
+
}).pipe(Effect.map((comments) => comments.map((comment) => {
|
|
1550
|
+
const endLine = comment.line ?? comment.original_line;
|
|
1551
|
+
const startLine = comment.start_line ?? comment.original_start_line ?? endLine;
|
|
1552
|
+
return RetirableReviewComment.make({
|
|
1553
|
+
nodeId: comment.node_id,
|
|
1554
|
+
path: comment.path,
|
|
1555
|
+
startLine,
|
|
1556
|
+
endLine,
|
|
1557
|
+
body: comment.body
|
|
1558
|
+
});
|
|
1559
|
+
}))),
|
|
1560
|
+
updateBody: (reviewId, body) => executeRetirement("updateReview", withCommonHeaders(HttpClientRequest.put(`${prefix}/reviews/${reviewId}`).pipe(HttpClientRequest.acceptJson, HttpClientRequest.bodyJsonUnsafe({ body })), target.token)).pipe(Effect.asVoid),
|
|
1561
|
+
minimizeComment: (nodeId) => Effect.gen(function* () {
|
|
1562
|
+
const response = yield* executeRetirement("minimizeComment", withCommonHeaders(HttpClientRequest.post(target.graphqlUrl).pipe(HttpClientRequest.acceptJson, HttpClientRequest.bodyJsonUnsafe({
|
|
1563
|
+
query: MINIMIZE_REVIEW_COMMENT_MUTATION,
|
|
1564
|
+
variables: { subjectId: nodeId }
|
|
1565
|
+
})), target.token));
|
|
1566
|
+
const wire = yield* decodeRetirement(GitHubMinimizeCommentWire, "minimizeComment")(response);
|
|
1567
|
+
if ((wire.errors?.length ?? 0) > 0 || wire.data?.minimizeComment?.minimizedComment?.isMinimized !== true) return yield* ReviewRetirementFailure.make({
|
|
1568
|
+
operation: "minimizeComment",
|
|
1569
|
+
reason: wire.errors?.map((error) => error.message).join("; ").slice(0, 2048) ?? "GitHub did not confirm comment minimization"
|
|
1570
|
+
});
|
|
1571
|
+
})
|
|
1572
|
+
});
|
|
1573
|
+
}));
|
|
1273
1574
|
/** Reading the pull request's previously posted reviews failed. */
|
|
1274
1575
|
var PriorReviewLookupFailure = class extends Schema.TaggedError()("PriorReviewLookupFailure", { reason: Schema.String }) {
|
|
1275
1576
|
get message() {
|
|
@@ -1367,6 +1668,6 @@ const fingerprintUnchanged = (current) => Effect.gen(function* () {
|
|
|
1367
1668
|
return Option.isSome(latest) && latest.value === current;
|
|
1368
1669
|
});
|
|
1369
1670
|
//#endregion
|
|
1370
|
-
export {
|
|
1671
|
+
export { FanOutReviewer as $, ReviewToolkit as $t, ReviewStateMarker as A, ReviewUnitId as At, toStoredConcern as B, FileSlice as Bt, ReviewExecutionContext as C, makeFanOutReviewSuite as Ct, ReviewState as D, MAX_REVIEW_UNITS as Dt, ReviewScopeMode as E, MAX_MERGED_FINDINGS as Et, computeProfileFingerprint as F, ChangedFileSummary as Ft, FINGERPRINT_MARKER_LENGTH as G, MAX_CONCERNS as Gt, unavailableReviewStateAuthenticatorLayer as H, FindingSeverity as Ht, fromStoredConcern as I, ChangedFilesView as It, renderFingerprintMarker as J, ReadFile as Jt, computeChangesetFingerprint as K, MAX_FINDINGS as Kt, fromStoredFinding as L, CodeReview as Lt, StoredReviewConcern as M, UNIT_CHANGED_LINE_BUDGET as Mt, StoredReviewFinding as N, planReviewUnits as Nt, ReviewStateAuthenticationFailure as O, MAX_UNIT_FILES as Ot, buildProfileMission as P, rankAndDedupeFindings as Pt, FanOutReviewToolkit as Q, ReviewMission as Qt, selectReviewRange as R, FileDiffQuery as Rt, MAX_REVIEW_STATE_MARKER_CHARS as S, parsePatch as Sn, makeFanOutReviewInstructions as St, ReviewMode as T, mapFileReviewChildFailure as Tt, validateReviewState as U, ListChangedFiles as Ut, toStoredFinding as V, FileSliceQuery as Vt, webCryptoReviewStateAuthenticatorLayer as W, ListChangedFilesQuery as Wt, FanOutCoordinatorToolkit as X, ReviewConcern as Xt, DelegateFileReview as Y, ReadFileDiff as Yt, FanOutCoordinatorToolkitLayer as Z, ReviewFinding as Zt, ReviewRetirementReport as _, ChangedFile as _n, fanOutHandlersLayerFor as _t, PublishedReview as a, makeReviewInstructions as an, FileReviewToolkitLayer as at, retireStaleReviews as b, annotatePatch as bn, fileReviewPolicy as bt, gitHubPriorReviewsLayer as c, resolveGuidance as cn, FileReviewer as ct, gitHubReviewRetirementHostLayer as d, MAX_FILE_CHARS as dn, MAX_CHILD_CONCERNS as dt, ReviewToolkitLayer as en, FileReviewBrief as et, parseGitHubSubmittedAt as f, PullRequestMetadata as fn, MAX_CHILD_FINDINGS as ft, ReviewRetirementHost as g, normalizeRepoRelativePath as gn, fanOutHandlersLayer as gt, ReviewRetirementFailure as h, ReviewInputViolation as hn, defaultFileReviewerPolicy as ht, PriorReviews as i, listChangedFilesHandler as in, FileReviewToolkit as it, ReviewStateMarkerTooLarge as j, ReviewUnitPlan as jt, ReviewStateAuthenticator as k, ReviewUnit as kt, gitHubPullRequestSourceLayer as l, reviewInstructions as ln, ListReviewUnits as lt, RetirableReviewComment as m, PullRequestSourceFailure as mn, defaultFanOutPolicy as mt, GitHubReviewTarget as n, clampMaxFindings as nn, FileReviewReport as nt, ReviewPublisher as o, readFileDiffHandler as on, FileReviewUnitFailed as ot, RetirableReview as p, PullRequestSource as pn, MAX_FILE_REVIEW_TOOL_CALLS as pt, extractFingerprint as q, PullRequestReviewer as qt, PriorReviewLookupFailure as r, defaultReviewPolicy as rn, FileReviewRequest as rt, fingerprintUnchanged as s, readFileHandler as sn, FileReviewUnitResult as st, GitHubApiFailure as t, ReviewVerdict as tn, FileReviewDelegationFailure as tt, gitHubReviewPublisherLayer as u, MAX_CHANGED_FILES as un, ListReviewUnitsQuery as ut, decideReviewRetirement as v, ChangedFileStatus as vn, fanOutReviewInstructions as vt, ReviewHeadComparison as w, makeFileReviewerInstructions as wt, GitCommitSha as x, commentableLines as xn, fileReviewerInstructions as xt, hasReviewMetadataMarker as y, ChangedPath as yn, fileReviewDelegation as yt, selectedPullRequestSourceLayer as z, FileDiffView as zt };
|
|
1371
1672
|
|
|
1372
|
-
//# sourceMappingURL=github-
|
|
1673
|
+
//# sourceMappingURL=github-Lfa_ox-u.mjs.map
|