@dev.fast/whiteboard 0.2.4-preview.20261009.108 → 0.2.4
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-BR2j-2n5.js +2 -0
- package/dist/{agent-cli-BYMMEOlO.js → agent-cli-BrFqhnzM.js} +1 -1
- package/dist/{authoring-tools-CUJ_56bd.js → authoring-tools-DvIO906l.js} +1 -1
- package/dist/build-info.json +1 -1
- package/dist/{cli-runner-CyBLX8Oj.js → cli-runner-Cjl8h376.js} +4 -4
- package/dist/cli.js +1 -1
- package/dist/{headless-host-wMPUx0Aj.js → headless-host-DcWaxx9P.js} +2 -2
- package/dist/{local-data-BfAaQmqC.js → local-data-Bm84Xdck.js} +68 -74
- package/dist/{mcp-BWp_kC0V.js → mcp-BDy9Aybd.js} +1 -1
- package/dist/{process-error-telemetry-WjZ2FTnX.js → process-error-telemetry-Dzh5a3s_.js} +13 -10
- package/dist/profile-BQGaX8aX.js +2 -0
- package/dist/{profile-CviK33_F.js → profile-RDEioF8b.js} +1 -1
- package/dist/server/desktop-host.js +3 -3
- package/dist/sharing/index.d.ts +10 -11
- package/package.json +3 -3
- package/src/review-api/comparison-coverage.ts +191 -169
- package/src/review-api/local-data.ts +9 -3
- package/src/review-api/review-progress.ts +19 -9
- package/src/server/structural-diff.ts +7 -7
- package/dist/agent-cli-BJ_SxT1P.js +0 -2
- package/dist/profile-YLwWPRQp.js +0 -2
|
@@ -23,7 +23,7 @@ async function runReviewAgentCli(input) {
|
|
|
23
23
|
[REVIEW_AGENT_HEADER]: reviewSessionAgent(env)
|
|
24
24
|
};
|
|
25
25
|
if (mode === "mcp") {
|
|
26
|
-
const { serveReviewMcp } = await import("./mcp-
|
|
26
|
+
const { serveReviewMcp } = await import("./mcp-BDy9Aybd.js");
|
|
27
27
|
await serveReviewMcp(async (key, agentKind) => {
|
|
28
28
|
const connected = await connectReviewInstance(key ? {
|
|
29
29
|
...env,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { g as activityUpdateSchema, h as activityEndSchema, i as commandSchema, m as activityBeginSchema, n as uploadSchema } from "./local-data-
|
|
1
|
+
import { g as activityUpdateSchema, h as activityEndSchema, i as commandSchema, m as activityBeginSchema, n as uploadSchema } from "./local-data-Bm84Xdck.js";
|
|
2
2
|
import { x as publishedEditSchema, y as pathTargetSchema } from "./stored-document-migration-BxZWQQjv.js";
|
|
3
3
|
import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
|
|
4
4
|
import { t as findReviewPackageRoot } from "./package-paths-B6-zxvIO.js";
|
package/dist/build-info.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.2.4
|
|
1
|
+
{"version":"0.2.4","commit":"5b00f0e66d034e187a29d270b3690a37b57ccf7b","dirty":true,"builtAt":"2026-10-09T06:23:07.418Z"}
|
|
@@ -13,7 +13,7 @@ import { t as ReviewTelemetry } from "./review-telemetry-DBy3eVCo.js";
|
|
|
13
13
|
import { a as isOwnedShim, f as windowsInstallerCommand, m as connectPrompts, o as pathShimPath, p as scanLegacySkills, t as aliasInstallationToAccount } from "./account-alias-_1_7vROx.js";
|
|
14
14
|
import { t as fuzzyRank } from "./fuzzy-match-BoAmcyak.js";
|
|
15
15
|
import { n as readReviewServerDiscovery, o as reviewServerStateDir, r as readReviewServerHealth, s as serverNotReady } from "./server-discovery-Dbzn3w6Z.js";
|
|
16
|
-
import { t as reviewAgentCliHelp } from "./agent-cli-
|
|
16
|
+
import { t as reviewAgentCliHelp } from "./agent-cli-BrFqhnzM.js";
|
|
17
17
|
import { closeSync, existsSync, fstatSync, mkdirSync, mkdtempSync, openSync, readSync, renameSync, writeFileSync } from "node:fs";
|
|
18
18
|
import { realpath } from "node:fs/promises";
|
|
19
19
|
import path from "node:path";
|
|
@@ -4495,7 +4495,7 @@ async function runReviewCli(input) {
|
|
|
4495
4495
|
process.once("SIGINT", stop);
|
|
4496
4496
|
process.once("SIGTERM", stop);
|
|
4497
4497
|
try {
|
|
4498
|
-
const { runHeadlessServer } = await import("./headless-host-
|
|
4498
|
+
const { runHeadlessServer } = await import("./headless-host-DcWaxx9P.js");
|
|
4499
4499
|
await runHeadlessServer({
|
|
4500
4500
|
stateDir,
|
|
4501
4501
|
port,
|
|
@@ -4546,7 +4546,7 @@ async function runReviewCli(input) {
|
|
|
4546
4546
|
if (problem) throw new Error(`Cannot tell whether a Whiteboard Desktop is using ${stateDir}: ${problem.message}`);
|
|
4547
4547
|
const inUse = () => /* @__PURE__ */ new Error(`A Whiteboard server is using ${stateDir}. Stop it first, then run whiteboard server reset-id again.`);
|
|
4548
4548
|
if (desktops.instances.some(({ discovery }) => [discovery.appPid, discovery.serverPid].some((pid) => processIsAlive(pid)))) throw inUse();
|
|
4549
|
-
const [{ openReviewProfile }, { withHeadlessServerLock }] = await Promise.all([import("./profile-
|
|
4549
|
+
const [{ openReviewProfile }, { withHeadlessServerLock }] = await Promise.all([import("./profile-BQGaX8aX.js"), import("./headless-host-DcWaxx9P.js")]);
|
|
4550
4550
|
const reset = await withHeadlessServerLock(await realpath(stateDir), async () => {
|
|
4551
4551
|
const local = await openReviewProfile(stateDir, { manageWorkspaces: false });
|
|
4552
4552
|
try {
|
|
@@ -4774,7 +4774,7 @@ async function runReviewCli(input) {
|
|
|
4774
4774
|
return commands;
|
|
4775
4775
|
} });
|
|
4776
4776
|
for (const [name, description] of [["api", "Call a JSON review authoring tool on the running server"], ["mcp", "Serve the JSON review authoring tools over stdio MCP"]]) configureOutput(program.command(name).description(description).argument("[args...]", "tool name and JSON input").allowUnknownOption().allowExcessArguments().helpOption(false).passThroughOptions().addHelpText("after", `\n${reviewAgentCliHelp}`), "plain").action(async (args) => {
|
|
4777
|
-
const { runReviewAgentCli } = await import("./agent-cli-
|
|
4777
|
+
const { runReviewAgentCli } = await import("./agent-cli-BR2j-2n5.js");
|
|
4778
4778
|
state.exitCode = await runReviewAgentCli({
|
|
4779
4779
|
...input,
|
|
4780
4780
|
env: authoringEnv(),
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { p as isObjectValue } from "./src-X9phtB2j.js";
|
|
2
2
|
import { O as withFileLock, P as writePrivateJsonAtomic } from "./s3-config-CAxhO9u_.js";
|
|
3
3
|
import "./review-home-paths-6zZH-1c9.js";
|
|
4
|
-
import { t as openReviewProfile } from "./profile-
|
|
5
|
-
import { i as createWhiteboardCore, l as createNodeRequestListener, m as migrateDiffrConfig, n as installProcessErrorTelemetry, p as GlobalReviewDesktopVerbRelay, t as drainServerCrashReport } from "./process-error-telemetry-
|
|
4
|
+
import { t as openReviewProfile } from "./profile-RDEioF8b.js";
|
|
5
|
+
import { i as createWhiteboardCore, l as createNodeRequestListener, m as migrateDiffrConfig, n as installProcessErrorTelemetry, p as GlobalReviewDesktopVerbRelay, t as drainServerCrashReport } from "./process-error-telemetry-Dzh5a3s_.js";
|
|
6
6
|
import { i as reviewServerDiscoveryPath, t as headlessServerLockPath } from "./server-discovery-Dbzn3w6Z.js";
|
|
7
7
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
8
8
|
import { mkdir, realpath, rm } from "node:fs/promises";
|
|
@@ -425,9 +425,9 @@ async function* structuralDiff(input) {
|
|
|
425
425
|
case "merge-base": args.push(`${comparison.base}...${comparison.head}`);
|
|
426
426
|
}
|
|
427
427
|
args.push("--", ...input.paths ?? []);
|
|
428
|
-
const
|
|
429
|
-
const
|
|
430
|
-
const signal = AbortSignal.any([
|
|
428
|
+
const startupAbort = new AbortController();
|
|
429
|
+
const startup = setTimeout(() => startupAbort.abort(), 12e4);
|
|
430
|
+
const signal = AbortSignal.any([startupAbort.signal, input.signal]);
|
|
431
431
|
console.info(`[Review] structural diff: ${executable} ${args.join(" ")}`);
|
|
432
432
|
const child = spawn(executable, args, {
|
|
433
433
|
cwd: input.repositoryPath,
|
|
@@ -461,7 +461,6 @@ async function* structuralDiff(input) {
|
|
|
461
461
|
});
|
|
462
462
|
try {
|
|
463
463
|
for await (const line of lines) {
|
|
464
|
-
idle.refresh();
|
|
465
464
|
if (Buffer.byteLength(line) > 100663296) throw new Error("Structural diff record exceeded 96 MiB.");
|
|
466
465
|
if (!line.trim()) continue;
|
|
467
466
|
const event = decodeStructuralDiffEvent(line);
|
|
@@ -469,6 +468,7 @@ async function* structuralDiff(input) {
|
|
|
469
468
|
if (!started) {
|
|
470
469
|
if (event.type !== "start" || event.version !== 3) throw new Error("Unsupported diffr stream protocol.");
|
|
471
470
|
started = true;
|
|
471
|
+
clearTimeout(startup);
|
|
472
472
|
} else if (event.type === "complete") {
|
|
473
473
|
completed = true;
|
|
474
474
|
failed = event.failed;
|
|
@@ -484,7 +484,7 @@ async function* structuralDiff(input) {
|
|
|
484
484
|
const code = await exited;
|
|
485
485
|
if (code !== 0 && !(code === 2 && (failed > 0 || aborted !== void 0))) throw exitError(code);
|
|
486
486
|
} finally {
|
|
487
|
-
clearTimeout(
|
|
487
|
+
clearTimeout(startup);
|
|
488
488
|
lines.close();
|
|
489
489
|
if (child.exitCode === null) child.kill();
|
|
490
490
|
await exited.catch(() => {});
|
|
@@ -960,9 +960,9 @@ async function comparisonCoverage(data, reviewId, pins, mode, signal, publish) {
|
|
|
960
960
|
const fileSources = /* @__PURE__ */ new Map();
|
|
961
961
|
const alignments = /* @__PURE__ */ new Map();
|
|
962
962
|
const files = [];
|
|
963
|
-
|
|
963
|
+
for (const file of await data.changes(pins)) {
|
|
964
964
|
signal.throwIfAborted();
|
|
965
|
-
const patch = await data.changes(pins, file.path);
|
|
965
|
+
const patch = await data.changes(pins, file.path, file.previousPath);
|
|
966
966
|
const changed = emptyCoverage();
|
|
967
967
|
for (const hunk of parseUnifiedPatch(file.path, patch)) for (const line of hunk.lines) {
|
|
968
968
|
if (line.kind === "add") changed.head.push([line.newLine - 1, line.newLine]);
|
|
@@ -1001,80 +1001,72 @@ async function comparisonCoverage(data, reviewId, pins, mode, signal, publish) {
|
|
|
1001
1001
|
changed,
|
|
1002
1002
|
viewed: emptyCoverage()
|
|
1003
1003
|
});
|
|
1004
|
-
publish?.({
|
|
1004
|
+
if (mode === "textual") publish?.({
|
|
1005
|
+
counted: false,
|
|
1005
1006
|
files: [...files],
|
|
1006
1007
|
fileSources: new Map(fileSources),
|
|
1007
1008
|
alignments: new Map(alignments)
|
|
1008
1009
|
});
|
|
1009
1010
|
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1011
|
+
const coverage = {
|
|
1012
|
+
files,
|
|
1013
|
+
fileSources,
|
|
1014
|
+
alignments,
|
|
1015
|
+
counted: true
|
|
1016
|
+
};
|
|
1017
|
+
if (mode === "textual") return coverage;
|
|
1018
|
+
const publishCurrent = () => publish?.({
|
|
1019
|
+
...coverage,
|
|
1020
|
+
files: [...coverage.files]
|
|
1021
|
+
});
|
|
1022
|
+
publishCurrent();
|
|
1023
|
+
const baseFiles = new Map(coverage.files.map((file, index) => [file.previousPath ?? file.path, index]));
|
|
1024
|
+
const headFiles = new Map(coverage.files.map((file, index) => [file.path, index]));
|
|
1025
|
+
const remaining = /* @__PURE__ */ new Set();
|
|
1026
|
+
for await (const event of data.structuralChanges({
|
|
1027
|
+
reviewId,
|
|
1028
|
+
pins,
|
|
1029
|
+
signal
|
|
1030
|
+
})) {
|
|
1031
|
+
if (event.type === "start") {
|
|
1032
|
+
for (const entry of event.files) remaining.add((entry.file.rhs ?? entry.file.lhs).path);
|
|
1033
|
+
if (!remaining.size) break;
|
|
1034
|
+
continue;
|
|
1035
|
+
}
|
|
1036
|
+
if (event.type === "complete" && (event.failed || event.aborted)) throw new Error(event.aborted?.message ?? "Structural coverage is incomplete.");
|
|
1037
|
+
if (event.type !== "file") continue;
|
|
1038
|
+
const path = (event.file.rhs ?? event.file.lhs).path;
|
|
1039
|
+
if (!remaining.delete(path)) throw new Error(`Unexpected structural result: ${path}`);
|
|
1040
|
+
if (event.error) {
|
|
1041
|
+
if (event.error.code !== "unsupported_file_type" && event.error.code !== "not_utf8") throw new Error(`Cannot count ${path}: ${event.error.message}`);
|
|
1042
|
+
} else if (event.diff.type === "text") {
|
|
1043
|
+
const diff = event.diff;
|
|
1044
|
+
const folded = foldedChanges(diff);
|
|
1045
|
+
for (const side of ["base", "head"]) {
|
|
1046
|
+
const source = side === "base" ? event.file.lhs : event.file.rhs;
|
|
1047
|
+
const index = source && (side === "base" ? baseFiles : headFiles).get(source.path);
|
|
1048
|
+
if (index === void 0) continue;
|
|
1049
|
+
const file = coverage.files[index];
|
|
1050
|
+
const visible = subtractIntervals(diff.structural_changes[side], folded[side]);
|
|
1051
|
+
coverage.files[index] = {
|
|
1052
|
+
...file,
|
|
1053
|
+
folded: {
|
|
1054
|
+
...file.folded ?? emptyCoverage(),
|
|
1055
|
+
[side]: subtractIntervals(file.changed[side], visible)
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1021
1058
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
if (event.error) {
|
|
1027
|
-
if (event.error.code !== "unsupported_file_type" && event.error.code !== "not_utf8") throw new Error(`Cannot count ${path}: ${event.error.message}`);
|
|
1028
|
-
if (!remaining.size) break;
|
|
1029
|
-
continue;
|
|
1059
|
+
const baseIndex = event.file.lhs && baseFiles.get(event.file.lhs.path);
|
|
1060
|
+
const index = (event.file.rhs && headFiles.get(event.file.rhs.path)) ?? baseIndex;
|
|
1061
|
+
if (index !== void 0) {
|
|
1062
|
+
if ((coverage.fileSources.get(coverage.files[index].path) ?? []).every((source) => source.file === (source.side === "base" ? event.file.lhs : event.file.rhs)?.path)) coverage.alignments = new Map(coverage.alignments).set(coverage.files[index].path, structuralRows(diff));
|
|
1030
1063
|
}
|
|
1031
|
-
|
|
1032
|
-
const previousPath = event.file.lhs?.path !== path ? event.file.lhs?.path : void 0;
|
|
1033
|
-
const base = diff.type === "text" ? diff.lhs?.text ?? null : null;
|
|
1034
|
-
const head = diff.type === "text" ? diff.rhs?.text ?? null : null;
|
|
1035
|
-
const fingerprint = hash([
|
|
1036
|
-
pins.repositoryId,
|
|
1037
|
-
base,
|
|
1038
|
-
head,
|
|
1039
|
-
...diff.type === "binary" ? [event.file.lhs?.oid ?? null, event.file.rhs?.oid ?? null] : []
|
|
1040
|
-
]);
|
|
1041
|
-
const sources = [];
|
|
1042
|
-
if (base !== null) sources.push({
|
|
1043
|
-
side: "base",
|
|
1044
|
-
file: event.file.lhs.path,
|
|
1045
|
-
fromLine: 1,
|
|
1046
|
-
toLine: base.split("\n").length
|
|
1047
|
-
});
|
|
1048
|
-
if (head !== null) sources.push({
|
|
1049
|
-
side: "head",
|
|
1050
|
-
file: path,
|
|
1051
|
-
fromLine: 1,
|
|
1052
|
-
toLine: head.split("\n").length
|
|
1053
|
-
});
|
|
1054
|
-
fileSources.set(path, sources);
|
|
1055
|
-
if (diff.type === "text") alignments.set(path, structuralRows(diff));
|
|
1056
|
-
files.push({
|
|
1057
|
-
path,
|
|
1058
|
-
previousPath,
|
|
1059
|
-
fingerprint,
|
|
1060
|
-
changed: diff.type === "text" ? diff.structural_changes : emptyCoverage(),
|
|
1061
|
-
folded: foldedChanges(diff),
|
|
1062
|
-
viewed: emptyCoverage()
|
|
1063
|
-
});
|
|
1064
|
-
publish?.({
|
|
1065
|
-
files: [...files],
|
|
1066
|
-
fileSources: new Map(fileSources),
|
|
1067
|
-
alignments: new Map(alignments)
|
|
1068
|
-
});
|
|
1069
|
-
if (!remaining.size) break;
|
|
1064
|
+
publishCurrent();
|
|
1070
1065
|
}
|
|
1071
|
-
if (remaining.size)
|
|
1066
|
+
if (!remaining.size) break;
|
|
1072
1067
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
fileSources,
|
|
1076
|
-
alignments
|
|
1077
|
-
};
|
|
1068
|
+
if (remaining.size) throw new Error("Structural coverage is incomplete.");
|
|
1069
|
+
return coverage;
|
|
1078
1070
|
}
|
|
1079
1071
|
/**
|
|
1080
1072
|
* The changed lines diffr folds by default. A file it hides (lockfiles,
|
|
@@ -4221,6 +4213,7 @@ var LocalReviewData = class {
|
|
|
4221
4213
|
return {
|
|
4222
4214
|
pending: this.coveragePending(reviewId, pins, mode),
|
|
4223
4215
|
comparison: this.coverageCache.get(JSON.stringify([pins, mode])).partial ?? {
|
|
4216
|
+
counted: false,
|
|
4224
4217
|
files: [],
|
|
4225
4218
|
fileSources: /* @__PURE__ */ new Map(),
|
|
4226
4219
|
alignments: /* @__PURE__ */ new Map()
|
|
@@ -4272,10 +4265,11 @@ var LocalReviewData = class {
|
|
|
4272
4265
|
}
|
|
4273
4266
|
return entry.promise;
|
|
4274
4267
|
}
|
|
4275
|
-
async changes(pins, file) {
|
|
4268
|
+
async changes(pins, file, previousPath) {
|
|
4276
4269
|
if (file === void 0) return this.summaries(pins);
|
|
4277
4270
|
checkRelativePath(file);
|
|
4278
|
-
|
|
4271
|
+
if (previousPath) checkRelativePath(previousPath);
|
|
4272
|
+
return this.rawPatch(pins, { paths: previousPath ? [previousPath, file] : [file] });
|
|
4279
4273
|
}
|
|
4280
4274
|
async summaries(pins) {
|
|
4281
4275
|
if (pins.worktreeRevision) return diffFileSummariesWorkingTree(await this.worktreeInput(pins));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as toolResultText, s as ReviewApiError } from "./agent-client-B_b1GtUc.js";
|
|
2
2
|
import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
|
|
3
|
-
import { n as REVIEW_STATUS_TOOL, t as authoringTools } from "./authoring-tools-
|
|
3
|
+
import { n as REVIEW_STATUS_TOOL, t as authoringTools } from "./authoring-tools-DvIO906l.js";
|
|
4
4
|
import { i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-DJmTieh5.js";
|
|
5
5
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
6
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { c as jsonString, m as isStringValue, n as isJsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
|
|
2
2
|
import { a as sharedReviewId, c as verifyShareRepository, d as StreamLimitError, f as readBoundedStream, l as exportShare, m as shareIdSchema, n as ShareClient, p as parseShareLink, r as SharePreflightError, s as readShareRepository, t as ShareAuthError, u as validateShareSources } from "./client-BFClfQLO.js";
|
|
3
|
-
import { _ as invalidateStructuralComparisons, a as inspectSnapshot, c as askAgentIds, d as askQuestionSchema, f as AgentSelectionSchema, i as commandSchema, l as askChoiceKinds, p as selectionMarkdown, r as SCRATCHPAD_ID, s as applyAskChange, u as askPicksSchema, v as diffrExecutable, y as diffrMissingError } from "./local-data-
|
|
3
|
+
import { _ as invalidateStructuralComparisons, a as inspectSnapshot, c as askAgentIds, d as askQuestionSchema, f as AgentSelectionSchema, i as commandSchema, l as askChoiceKinds, p as selectionMarkdown, r as SCRATCHPAD_ID, s as applyAskChange, u as askPicksSchema, v as diffrExecutable, y as diffrMissingError } from "./local-data-Bm84Xdck.js";
|
|
4
4
|
import { d as REVIEW_CLIENT_REMOTE, i as parseReviewVerbRequest, r as parseReviewDesktopVerbResult, u as REVIEW_CLIENT_HEADER, v as reviewDiffrSummarizerInputSchema, y as decodeStructuralDiffEvent } from "./src-WCbabvdS.js";
|
|
5
5
|
import { C as TraceStorageDeniedError, G as readStoreAuth, H as DEFAULT_STORE_ORIGIN, U as clearStoreAuth, W as openUrlInBrowser, _ as isTraceStorageConfigured, b as selectTraceStorage, o as listReviewTraceSessions, q as runStoreLogin, s as loadReviewAgentTrace, y as resolveTraceStorage } from "./review-agent-traces-DNJFYzc8.js";
|
|
6
6
|
import { $ as unionIntervals, G as selectionKey, H as comparisonKey, I as fileLineRangeSchema, J as coverageSources, N as lensSelections, Q as subtractIntervals, T as selectionReferences, U as formatAnchor, V as anchorSelection, W as resolveDiffSelection, Y as emptyCoverage, Z as scopedCoverage, h as elements, k as UNCATEGORIZED_LENS_ID, u as anchorPins } from "./stored-document-migration-BxZWQQjv.js";
|
|
@@ -10,7 +10,7 @@ import { t as errorMessage } from "./error-message-OtiDonty.js";
|
|
|
10
10
|
import { T as traceMachineEnabled } from "./review-home-paths-6zZH-1c9.js";
|
|
11
11
|
import { i as readReviewPackageVersion, n as readBuildCommit } from "./package-paths-B6-zxvIO.js";
|
|
12
12
|
import { a as REVIEW_APP_SESSION_ID_HEADER, c as sanitizeUiTelemetryEvent, i as BUNDLE_FRAME_PATTERN, l as cleanTelemetryText, r as reviewRequestOrigin, s as isReportableCleanedMessage } from "./request-origin-DA4K1qWY.js";
|
|
13
|
-
import { a as scratchpadAvailable, c as readQuerySchemas, i as renderInstructions, o as inspectQuerySchema, r as instructionsQuerySchema, s as queryAnchor, t as authoringTools } from "./authoring-tools-
|
|
13
|
+
import { a as scratchpadAvailable, c as readQuerySchemas, i as renderInstructions, o as inspectQuerySchema, r as instructionsQuerySchema, s as queryAnchor, t as authoringTools } from "./authoring-tools-DvIO906l.js";
|
|
14
14
|
import { t as fuzzyRank } from "./fuzzy-match-BoAmcyak.js";
|
|
15
15
|
import { createRequire } from "node:module";
|
|
16
16
|
import crypto$1, { createHash, randomUUID } from "node:crypto";
|
|
@@ -2909,10 +2909,12 @@ async function reviewProgress(store, data, snapshot, signal = new AbortControlle
|
|
|
2909
2909
|
const marks = snapshot.shared ? /* @__PURE__ */ new Map() : store.viewedCoverage(snapshot.reviewId);
|
|
2910
2910
|
const pins = snapshot.pins ? (await data.resolveSource(snapshot)).pins : void 0;
|
|
2911
2911
|
const comparison = pins ? partial ?? await data.coverage(snapshot.reviewId, pins, mode) : {
|
|
2912
|
+
counted: true,
|
|
2912
2913
|
files: [],
|
|
2913
2914
|
fileSources: /* @__PURE__ */ new Map(),
|
|
2914
2915
|
alignments: /* @__PURE__ */ new Map()
|
|
2915
2916
|
};
|
|
2917
|
+
const counting = !comparison.counted;
|
|
2916
2918
|
signal.throwIfAborted();
|
|
2917
2919
|
const documentKey = JSON.stringify(pins);
|
|
2918
2920
|
const comparisons = /* @__PURE__ */ new Map();
|
|
@@ -2922,7 +2924,8 @@ async function reviewProgress(store, data, snapshot, signal = new AbortControlle
|
|
|
2922
2924
|
let loading = comparisons.get(key);
|
|
2923
2925
|
if (loading) return loading;
|
|
2924
2926
|
if (partial) {
|
|
2925
|
-
|
|
2927
|
+
const state = data.coverageSnapshot(snapshot.reviewId, own, mode);
|
|
2928
|
+
if (state.pending) return state.comparison.counted ? Promise.resolve(state.comparison) : void 0;
|
|
2926
2929
|
}
|
|
2927
2930
|
loading = data.coverage(snapshot.reviewId, own, mode);
|
|
2928
2931
|
comparisons.set(key, loading);
|
|
@@ -2962,7 +2965,7 @@ async function reviewProgress(store, data, snapshot, signal = new AbortControlle
|
|
|
2962
2965
|
if (file) group[file.path] ??= withViewed(file);
|
|
2963
2966
|
}
|
|
2964
2967
|
let rows = file && ownAlignments.get(file.path);
|
|
2965
|
-
if (!file &&
|
|
2968
|
+
if (!file && counting && !own) {
|
|
2966
2969
|
pendingSources.add(key);
|
|
2967
2970
|
return [];
|
|
2968
2971
|
}
|
|
@@ -2995,8 +2998,8 @@ async function reviewProgress(store, data, snapshot, signal = new AbortControlle
|
|
|
2995
2998
|
return {
|
|
2996
2999
|
...lens,
|
|
2997
3000
|
...resolved,
|
|
2998
|
-
pending:
|
|
2999
|
-
unavailable:
|
|
3001
|
+
pending: counting,
|
|
3002
|
+
unavailable: counting || resolved.fileCount ? void 0 : "No files match these targets"
|
|
3000
3003
|
};
|
|
3001
3004
|
} catch {
|
|
3002
3005
|
return {
|
|
@@ -3011,18 +3014,18 @@ async function reviewProgress(store, data, snapshot, signal = new AbortControlle
|
|
|
3011
3014
|
await resolveAvailable(source);
|
|
3012
3015
|
} catch {}
|
|
3013
3016
|
}));
|
|
3014
|
-
const uncategorized =
|
|
3017
|
+
const uncategorized = counting ? [] : uncategorizedSources(files, lenses.filter((lens) => !lens.unavailable).flatMap((lens) => lens.sources));
|
|
3015
3018
|
lenses.push({
|
|
3016
3019
|
id: UNCATEGORIZED_LENS_ID,
|
|
3017
3020
|
title: "Uncategorized changes",
|
|
3018
3021
|
sources: uncategorized,
|
|
3019
3022
|
wholeFiles: false,
|
|
3020
3023
|
fileCount: new Set(uncategorized.map((source) => files.find((file) => source.file === (source.side === "base" ? file.previousPath ?? file.path : file.path))?.path ?? source.file)).size,
|
|
3021
|
-
pending:
|
|
3022
|
-
unavailable:
|
|
3024
|
+
pending: counting,
|
|
3025
|
+
unavailable: counting || uncategorized.length ? void 0 : "All changed lines are covered by lenses"
|
|
3023
3026
|
});
|
|
3024
3027
|
const progress = {
|
|
3025
|
-
complete: !
|
|
3028
|
+
complete: !counting,
|
|
3026
3029
|
files,
|
|
3027
3030
|
referenceFiles,
|
|
3028
3031
|
lenses,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as initializeReviewStoreSchema, t as openLocalReviewStore } from "./local-data-
|
|
1
|
+
import { o as initializeReviewStoreSchema, t as openLocalReviewStore } from "./local-data-Bm84Xdck.js";
|
|
2
2
|
import { O as withFileLock } from "./s3-config-CAxhO9u_.js";
|
|
3
3
|
import "./review-home-paths-6zZH-1c9.js";
|
|
4
4
|
import { access, mkdir } from "node:fs/promises";
|
|
@@ -9,12 +9,12 @@ import { A as devReviewHome, P as writePrivateJsonAtomic } from "../s3-config-CA
|
|
|
9
9
|
import { U as shellQuote, i as reviewInstanceDiscoveryPath, o as reviewLegacyDiscoveryPath } from "../review-home-paths-6zZH-1c9.js";
|
|
10
10
|
import { i as readReviewPackageVersion, t as findReviewPackageRoot } from "../package-paths-B6-zxvIO.js";
|
|
11
11
|
import { c as reviewInstanceIdentity, t as REVIEW_INSTANCE_ENV } from "../desktop-discovery-C-Qz54wO.js";
|
|
12
|
-
import { t as openReviewProfile } from "../profile-
|
|
12
|
+
import { t as openReviewProfile } from "../profile-RDEioF8b.js";
|
|
13
13
|
import { r as reviewTelemetryChannel, t as ReviewTelemetry } from "../review-telemetry-DBy3eVCo.js";
|
|
14
14
|
import { o as REVIEW_TELEMETRY_TABS } from "../request-origin-DA4K1qWY.js";
|
|
15
15
|
import { c as removeLegacyReviewSkills, d as skipCliInstall, i as finishCliInstallUpdate, l as resetCliInstall, n as applyCliInstall, r as declineCliInstall, s as removeCliInstall, t as aliasInstallationToAccount, u as resolveCliInstallStatus } from "../account-alias-_1_7vROx.js";
|
|
16
|
-
import { _ as setDiffrConfigValue, a as serverJson, c as recordClientError, d as HttpJsonError, f as ReviewServerError, g as saveDiffrSummarizer, h as readDiffrConfig, i as createWhiteboardCore, l as createNodeRequestListener, m as migrateDiffrConfig, n as installProcessErrorTelemetry, o as captureSanitizedUiTelemetry, p as GlobalReviewDesktopVerbRelay, r as serverCrashReportPath, s as clientErrorsForSession, t as drainServerCrashReport, u as readBoundedRequestJson, v as testDiffrSummarizer } from "../process-error-telemetry-
|
|
17
|
-
import { c as readQuerySchemas } from "../authoring-tools-
|
|
16
|
+
import { _ as setDiffrConfigValue, a as serverJson, c as recordClientError, d as HttpJsonError, f as ReviewServerError, g as saveDiffrSummarizer, h as readDiffrConfig, i as createWhiteboardCore, l as createNodeRequestListener, m as migrateDiffrConfig, n as installProcessErrorTelemetry, o as captureSanitizedUiTelemetry, p as GlobalReviewDesktopVerbRelay, r as serverCrashReportPath, s as clientErrorsForSession, t as drainServerCrashReport, u as readBoundedRequestJson, v as testDiffrSummarizer } from "../process-error-telemetry-Dzh5a3s_.js";
|
|
17
|
+
import { c as readQuerySchemas } from "../authoring-tools-DvIO906l.js";
|
|
18
18
|
import crypto, { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
19
19
|
import { z } from "zod";
|
|
20
20
|
import fs, { existsSync } from "node:fs";
|
package/dist/sharing/index.d.ts
CHANGED
|
@@ -312,7 +312,7 @@ declare const fileLineRangeSchema: z.ZodObject<{
|
|
|
312
312
|
type FileLineRange = z.infer<typeof fileLineRangeSchema>;
|
|
313
313
|
//#endregion
|
|
314
314
|
//#region src/lens-selection.d.ts
|
|
315
|
-
type AlignmentRow
|
|
315
|
+
type AlignmentRow = readonly [number | null, number | null];
|
|
316
316
|
//#endregion
|
|
317
317
|
//#region src/viewed-coverage.d.ts
|
|
318
318
|
/** Zero-based, half-open intervals; always normalized at the storage boundary. */
|
|
@@ -321,7 +321,7 @@ interface Coverage {
|
|
|
321
321
|
base: LineInterval[];
|
|
322
322
|
head: LineInterval[];
|
|
323
323
|
}
|
|
324
|
-
interface CoverageFile
|
|
324
|
+
interface CoverageFile {
|
|
325
325
|
path: string;
|
|
326
326
|
previousPath?: string;
|
|
327
327
|
fingerprint: string;
|
|
@@ -1040,9 +1040,11 @@ interface WrittenComponent {
|
|
|
1040
1040
|
//#region src/review-api/comparison-coverage.d.ts
|
|
1041
1041
|
type CoverageMode = "structural" | "textual";
|
|
1042
1042
|
interface ComparisonCoverage {
|
|
1043
|
-
|
|
1043
|
+
/** All Git changes are counted, even if folding is still loading. */
|
|
1044
|
+
counted: boolean;
|
|
1045
|
+
files: CoverageFile[];
|
|
1044
1046
|
fileSources: Map<string, FileLineRange[]>;
|
|
1045
|
-
alignments: Map<string, readonly AlignmentRow
|
|
1047
|
+
alignments: Map<string, readonly AlignmentRow[]>;
|
|
1046
1048
|
}
|
|
1047
1049
|
//#endregion
|
|
1048
1050
|
//#region src/review-api/pull-request.d.ts
|
|
@@ -1696,6 +1698,7 @@ declare class LocalReviewData {
|
|
|
1696
1698
|
coverageSnapshot(reviewId: string, pins: Pins, mode: CoverageMode): {
|
|
1697
1699
|
pending: boolean;
|
|
1698
1700
|
comparison: ComparisonCoverage | {
|
|
1701
|
+
counted: false;
|
|
1699
1702
|
files: never[];
|
|
1700
1703
|
fileSources: Map<any, any>;
|
|
1701
1704
|
alignments: Map<any, any>;
|
|
@@ -1703,14 +1706,10 @@ declare class LocalReviewData {
|
|
|
1703
1706
|
};
|
|
1704
1707
|
private coverageNotification;
|
|
1705
1708
|
private notifyCoverage;
|
|
1706
|
-
coverage(reviewId: string, pins: Pins, mode: CoverageMode): Promise<
|
|
1707
|
-
files: CoverageFile[];
|
|
1708
|
-
fileSources: Map<string, FileLineRange[]>;
|
|
1709
|
-
alignments: Map<string, readonly AlignmentRow[]>;
|
|
1710
|
-
}>;
|
|
1709
|
+
coverage(reviewId: string, pins: Pins, mode: CoverageMode): Promise<ComparisonCoverage>;
|
|
1711
1710
|
changes(pins: Pins): Promise<LocalVcsDiffFileSummary[]>;
|
|
1712
|
-
changes(pins: Pins, file: string): Promise<string>;
|
|
1713
|
-
changes(pins: Pins, file?: string): Promise<LocalVcsDiffFileSummary[] | string>;
|
|
1711
|
+
changes(pins: Pins, file: string, previousPath?: string): Promise<string>;
|
|
1712
|
+
changes(pins: Pins, file?: string, previousPath?: string): Promise<LocalVcsDiffFileSummary[] | string>;
|
|
1714
1713
|
private summaries;
|
|
1715
1714
|
/** Raw Git patch text for exact filenames, or every change. */
|
|
1716
1715
|
private rawPatch;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dev.fast/whiteboard",
|
|
3
|
-
"version": "0.2.4
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -71,10 +71,10 @@
|
|
|
71
71
|
"tsx": "4.21.0",
|
|
72
72
|
"typescript": "6.0.3",
|
|
73
73
|
"vitest": "4.1.11",
|
|
74
|
+
"@dev.fast/json": "0.0.0",
|
|
74
75
|
"@dev.fast/review-protocol": "0.0.0",
|
|
75
76
|
"@dev.fast/review-share-protocol": "0.1.2",
|
|
76
77
|
"@dev.fast/trace-core": "0.0.0",
|
|
77
|
-
"@dev.fast/json": "0.0.0",
|
|
78
78
|
"@dev.fast/trace-protocol": "0.5.0"
|
|
79
79
|
},
|
|
80
80
|
"engines": {
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"repo": "devdotfast/review",
|
|
86
86
|
"docs": "packages/review/instructions/authoring.md"
|
|
87
87
|
},
|
|
88
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "5b00f0e66d034e187a29d270b3690a37b57ccf7b",
|
|
89
89
|
"scripts": {
|
|
90
90
|
"prebuild": "pnpm run build:workspace-deps",
|
|
91
91
|
"build": "node --max-old-space-size=8192 node_modules/tsdown/dist/run.mjs",
|
|
@@ -29,6 +29,8 @@ const hash = (parts: (string | null)[]) =>
|
|
|
29
29
|
createHash("sha256").update(JSON.stringify(parts)).digest("hex");
|
|
30
30
|
|
|
31
31
|
export interface ComparisonCoverage {
|
|
32
|
+
/** All Git changes are counted, even if folding is still loading. */
|
|
33
|
+
counted: boolean;
|
|
32
34
|
files: CoverageFile[];
|
|
33
35
|
fileSources: Map<string, FileLineRange[]>;
|
|
34
36
|
alignments: Map<string, readonly AlignmentRow[]>;
|
|
@@ -42,201 +44,221 @@ export async function comparisonCoverage(
|
|
|
42
44
|
mode: CoverageMode,
|
|
43
45
|
signal: AbortSignal,
|
|
44
46
|
publish?: (coverage: ComparisonCoverage) => void,
|
|
45
|
-
) {
|
|
47
|
+
): Promise<ComparisonCoverage> {
|
|
46
48
|
const fileSources = new Map<string, FileLineRange[]>();
|
|
47
49
|
const alignments = new Map<string, readonly AlignmentRow[]>();
|
|
48
50
|
|
|
49
51
|
const files: CoverageFile[] = [];
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const changed = emptyCoverage();
|
|
56
|
-
|
|
57
|
-
for (const hunk of parseUnifiedPatch(file.path, patch))
|
|
58
|
-
for (const line of hunk.lines) {
|
|
59
|
-
if (line.kind === "add")
|
|
60
|
-
changed.head.push([line.newLine! - 1, line.newLine!]);
|
|
61
|
-
|
|
62
|
-
if (line.kind === "remove")
|
|
63
|
-
changed.base.push([line.oldLine! - 1, line.oldLine!]);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
changed.base = unionIntervals(changed.base);
|
|
67
|
-
changed.head = unionIntervals(changed.head);
|
|
68
|
-
|
|
69
|
-
const [base, head] = await Promise.all([
|
|
70
|
-
file.status === "added"
|
|
71
|
-
? null
|
|
72
|
-
: data
|
|
73
|
-
.file(pins, "base", file.previousPath ?? file.path)
|
|
74
|
-
.then((value) => value.text)
|
|
75
|
-
.catch(() => null),
|
|
76
|
-
file.status === "deleted"
|
|
77
|
-
? null
|
|
78
|
-
: data
|
|
79
|
-
.file(pins, "head", file.path)
|
|
80
|
-
.then((value) => value.text)
|
|
81
|
-
.catch(() => null),
|
|
82
|
-
]);
|
|
83
|
-
|
|
84
|
-
alignments.set(
|
|
85
|
-
file.path,
|
|
86
|
-
textualRows(
|
|
87
|
-
file.path,
|
|
88
|
-
patch,
|
|
89
|
-
base === null ? 0 : base.split("\n").length,
|
|
90
|
-
head === null ? 0 : head.split("\n").length,
|
|
91
|
-
),
|
|
92
|
-
);
|
|
53
|
+
for (const file of await data.changes(pins)) {
|
|
54
|
+
signal.throwIfAborted();
|
|
55
|
+
const patch = await data.changes(pins, file.path, file.previousPath);
|
|
56
|
+
const changed = emptyCoverage();
|
|
93
57
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
58
|
+
for (const hunk of parseUnifiedPatch(file.path, patch))
|
|
59
|
+
for (const line of hunk.lines) {
|
|
60
|
+
if (line.kind === "add")
|
|
61
|
+
changed.head.push([line.newLine! - 1, line.newLine!]);
|
|
62
|
+
|
|
63
|
+
if (line.kind === "remove")
|
|
64
|
+
changed.base.push([line.oldLine! - 1, line.oldLine!]);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
changed.base = unionIntervals(changed.base);
|
|
68
|
+
changed.head = unionIntervals(changed.head);
|
|
69
|
+
|
|
70
|
+
const [base, head] = await Promise.all([
|
|
71
|
+
file.status === "added"
|
|
72
|
+
? null
|
|
73
|
+
: data
|
|
74
|
+
.file(pins, "base", file.previousPath ?? file.path)
|
|
75
|
+
.then((value) => value.text)
|
|
76
|
+
.catch(() => null),
|
|
77
|
+
file.status === "deleted"
|
|
78
|
+
? null
|
|
79
|
+
: data
|
|
80
|
+
.file(pins, "head", file.path)
|
|
81
|
+
.then((value) => value.text)
|
|
82
|
+
.catch(() => null),
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
alignments.set(
|
|
86
|
+
file.path,
|
|
87
|
+
textualRows(
|
|
88
|
+
file.path,
|
|
89
|
+
patch,
|
|
90
|
+
base === null ? 0 : base.split("\n").length,
|
|
91
|
+
head === null ? 0 : head.split("\n").length,
|
|
92
|
+
),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const readable =
|
|
96
|
+
(file.status === "added" || base !== null) &&
|
|
97
|
+
(file.status === "deleted" || head !== null);
|
|
98
|
+
|
|
99
|
+
const fingerprint = hash([
|
|
100
|
+
pins.repositoryId,
|
|
101
|
+
base,
|
|
102
|
+
head,
|
|
103
|
+
...(readable ? [] : [pins.base, pins.head, patch]),
|
|
104
|
+
]);
|
|
105
|
+
|
|
106
|
+
fileSources.set(file.path, [
|
|
107
|
+
...(base !== null
|
|
108
|
+
? [
|
|
109
|
+
{
|
|
110
|
+
side: "base" as const,
|
|
111
|
+
file: file.previousPath ?? file.path,
|
|
112
|
+
fromLine: 1,
|
|
113
|
+
toLine: base.split("\n").length,
|
|
114
|
+
},
|
|
115
|
+
]
|
|
116
|
+
: []),
|
|
117
|
+
...(head !== null
|
|
118
|
+
? [
|
|
119
|
+
{
|
|
120
|
+
side: "head" as const,
|
|
121
|
+
file: file.path,
|
|
122
|
+
fromLine: 1,
|
|
123
|
+
toLine: head.split("\n").length,
|
|
124
|
+
},
|
|
125
|
+
]
|
|
126
|
+
: []),
|
|
127
|
+
]);
|
|
128
|
+
files.push({
|
|
129
|
+
path: file.path,
|
|
130
|
+
previousPath: file.previousPath,
|
|
131
|
+
fingerprint,
|
|
132
|
+
changed,
|
|
133
|
+
viewed: emptyCoverage(),
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (mode === "textual")
|
|
134
137
|
publish?.({
|
|
138
|
+
counted: false,
|
|
135
139
|
files: [...files],
|
|
136
140
|
fileSources: new Map(fileSources),
|
|
137
141
|
alignments: new Map(alignments),
|
|
138
142
|
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const coverage: ComparisonCoverage = {
|
|
146
|
+
files,
|
|
147
|
+
fileSources,
|
|
148
|
+
alignments,
|
|
149
|
+
counted: true,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
if (mode === "textual") return coverage;
|
|
153
|
+
|
|
154
|
+
// Keep Git's totals and file identities while structural results add folding.
|
|
155
|
+
const publishCurrent = () =>
|
|
156
|
+
publish?.({ ...coverage, files: [...coverage.files] });
|
|
157
|
+
|
|
158
|
+
publishCurrent();
|
|
159
|
+
|
|
160
|
+
const baseFiles = new Map(
|
|
161
|
+
coverage.files.map((file, index) => [
|
|
162
|
+
file.previousPath ?? file.path,
|
|
163
|
+
index,
|
|
164
|
+
]),
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const headFiles = new Map(
|
|
168
|
+
coverage.files.map((file, index) => [file.path, index]),
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const remaining = new Set<string>();
|
|
172
|
+
|
|
173
|
+
for await (const event of data.structuralChanges({
|
|
174
|
+
reviewId,
|
|
175
|
+
pins,
|
|
176
|
+
signal,
|
|
177
|
+
})) {
|
|
178
|
+
if (event.type === "start") {
|
|
179
|
+
for (const entry of event.files)
|
|
180
|
+
remaining.add((entry.file.rhs ?? entry.file.lhs)!.path);
|
|
181
|
+
|
|
182
|
+
if (!remaining.size) break;
|
|
183
|
+
continue;
|
|
139
184
|
}
|
|
140
|
-
} else {
|
|
141
|
-
const remaining = new Set<string>();
|
|
142
|
-
|
|
143
|
-
for await (const event of data.structuralChanges({
|
|
144
|
-
reviewId,
|
|
145
|
-
pins,
|
|
146
|
-
signal,
|
|
147
|
-
})) {
|
|
148
|
-
if (event.type === "start") {
|
|
149
|
-
for (const entry of event.files)
|
|
150
|
-
remaining.add((entry.file.rhs ?? entry.file.lhs)!.path);
|
|
151
|
-
|
|
152
|
-
if (!remaining.size) break;
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
185
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
186
|
+
if (event.type === "complete" && (event.failed || event.aborted))
|
|
187
|
+
throw new Error(
|
|
188
|
+
event.aborted?.message ?? "Structural coverage is incomplete.",
|
|
189
|
+
);
|
|
160
190
|
|
|
161
|
-
|
|
191
|
+
if (event.type !== "file") continue;
|
|
162
192
|
|
|
163
|
-
|
|
193
|
+
const path = (event.file.rhs ?? event.file.lhs)!.path;
|
|
164
194
|
|
|
165
|
-
|
|
166
|
-
|
|
195
|
+
if (!remaining.delete(path))
|
|
196
|
+
throw new Error(`Unexpected structural result: ${path}`);
|
|
167
197
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
198
|
+
if (event.error) {
|
|
199
|
+
if (
|
|
200
|
+
event.error.code !== "unsupported_file_type" &&
|
|
201
|
+
event.error.code !== "not_utf8"
|
|
202
|
+
)
|
|
203
|
+
throw new Error(`Cannot count ${path}: ${event.error.message}`);
|
|
204
|
+
} else if (event.diff.type === "text") {
|
|
205
|
+
const diff = event.diff;
|
|
206
|
+
const folded = foldedChanges(diff);
|
|
207
|
+
|
|
208
|
+
// Match each side separately: diffr can split a Git rename into two files.
|
|
209
|
+
for (const side of ["base", "head"] as const) {
|
|
210
|
+
const source = side === "base" ? event.file.lhs : event.file.rhs;
|
|
211
|
+
|
|
212
|
+
const index =
|
|
213
|
+
source && (side === "base" ? baseFiles : headFiles).get(source.path);
|
|
214
|
+
|
|
215
|
+
if (index === undefined) continue;
|
|
216
|
+
const file = coverage.files[index];
|
|
176
217
|
|
|
177
|
-
|
|
178
|
-
|
|
218
|
+
const visible = subtractIntervals(
|
|
219
|
+
diff.structural_changes[side],
|
|
220
|
+
folded[side],
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
coverage.files[index] = {
|
|
224
|
+
...file,
|
|
225
|
+
folded: {
|
|
226
|
+
...(file.folded ?? emptyCoverage()),
|
|
227
|
+
[side]: subtractIntervals(file.changed[side], visible),
|
|
228
|
+
},
|
|
229
|
+
};
|
|
179
230
|
}
|
|
180
231
|
|
|
181
|
-
const
|
|
232
|
+
const baseIndex = event.file.lhs && baseFiles.get(event.file.lhs.path);
|
|
233
|
+
const headIndex = event.file.rhs && headFiles.get(event.file.rhs.path);
|
|
234
|
+
const index = headIndex ?? baseIndex;
|
|
182
235
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const base = diff.type === "text" ? (diff.lhs?.text ?? null) : null;
|
|
187
|
-
const head = diff.type === "text" ? (diff.rhs?.text ?? null) : null;
|
|
188
|
-
|
|
189
|
-
const fingerprint = hash([
|
|
190
|
-
pins.repositoryId,
|
|
191
|
-
base,
|
|
192
|
-
head,
|
|
193
|
-
...(diff.type === "binary"
|
|
194
|
-
? [event.file.lhs?.oid ?? null, event.file.rhs?.oid ?? null]
|
|
195
|
-
: []),
|
|
196
|
-
]);
|
|
197
|
-
|
|
198
|
-
const sources: FileLineRange[] = [];
|
|
199
|
-
|
|
200
|
-
if (base !== null)
|
|
201
|
-
sources.push({
|
|
202
|
-
side: "base",
|
|
203
|
-
file: event.file.lhs!.path,
|
|
204
|
-
fromLine: 1,
|
|
205
|
-
toLine: base.split("\n").length,
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
if (head !== null)
|
|
209
|
-
sources.push({
|
|
210
|
-
side: "head",
|
|
211
|
-
file: path,
|
|
212
|
-
fromLine: 1,
|
|
213
|
-
toLine: head.split("\n").length,
|
|
214
|
-
});
|
|
215
|
-
fileSources.set(path, sources);
|
|
216
|
-
|
|
217
|
-
if (diff.type === "text") alignments.set(path, structuralRows(diff));
|
|
218
|
-
files.push({
|
|
219
|
-
path,
|
|
220
|
-
previousPath,
|
|
221
|
-
fingerprint,
|
|
222
|
-
changed:
|
|
223
|
-
diff.type === "text" ? diff.structural_changes : emptyCoverage(),
|
|
224
|
-
folded: foldedChanges(diff),
|
|
225
|
-
viewed: emptyCoverage(),
|
|
226
|
-
});
|
|
227
|
-
publish?.({
|
|
228
|
-
files: [...files],
|
|
229
|
-
fileSources: new Map(fileSources),
|
|
230
|
-
alignments: new Map(alignments),
|
|
231
|
-
});
|
|
236
|
+
if (index !== undefined) {
|
|
237
|
+
const sources =
|
|
238
|
+
coverage.fileSources.get(coverage.files[index].path) ?? [];
|
|
232
239
|
|
|
233
|
-
|
|
240
|
+
if (
|
|
241
|
+
sources.every(
|
|
242
|
+
(source) =>
|
|
243
|
+
source.file ===
|
|
244
|
+
(source.side === "base" ? event.file.lhs : event.file.rhs)?.path,
|
|
245
|
+
)
|
|
246
|
+
)
|
|
247
|
+
coverage.alignments = new Map(coverage.alignments).set(
|
|
248
|
+
coverage.files[index].path,
|
|
249
|
+
structuralRows(diff),
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
publishCurrent();
|
|
234
254
|
}
|
|
235
255
|
|
|
236
|
-
if (remaining.size)
|
|
256
|
+
if (!remaining.size) break;
|
|
237
257
|
}
|
|
238
258
|
|
|
239
|
-
|
|
259
|
+
if (remaining.size) throw new Error("Structural coverage is incomplete.");
|
|
260
|
+
|
|
261
|
+
return coverage;
|
|
240
262
|
}
|
|
241
263
|
|
|
242
264
|
/**
|
|
@@ -1409,6 +1409,7 @@ export class LocalReviewData {
|
|
|
1409
1409
|
return {
|
|
1410
1410
|
pending,
|
|
1411
1411
|
comparison: entry.partial ?? {
|
|
1412
|
+
counted: false,
|
|
1412
1413
|
files: [],
|
|
1413
1414
|
fileSources: new Map(),
|
|
1414
1415
|
alignments: new Map(),
|
|
@@ -1496,16 +1497,21 @@ export class LocalReviewData {
|
|
|
1496
1497
|
}
|
|
1497
1498
|
|
|
1498
1499
|
changes(pins: Pins): Promise<LocalVcsDiffFileSummary[]>;
|
|
1499
|
-
changes(pins: Pins, file: string): Promise<string>;
|
|
1500
|
+
changes(pins: Pins, file: string, previousPath?: string): Promise<string>;
|
|
1500
1501
|
changes(
|
|
1501
1502
|
pins: Pins,
|
|
1502
1503
|
file?: string,
|
|
1504
|
+
previousPath?: string,
|
|
1503
1505
|
): Promise<LocalVcsDiffFileSummary[] | string>;
|
|
1504
|
-
async changes(pins: Pins, file?: string) {
|
|
1506
|
+
async changes(pins: Pins, file?: string, previousPath?: string) {
|
|
1505
1507
|
if (file === undefined) return this.summaries(pins);
|
|
1506
1508
|
checkRelativePath(file);
|
|
1507
1509
|
|
|
1508
|
-
|
|
1510
|
+
if (previousPath) checkRelativePath(previousPath);
|
|
1511
|
+
|
|
1512
|
+
return this.rawPatch(pins, {
|
|
1513
|
+
paths: previousPath ? [previousPath, file] : [file],
|
|
1514
|
+
});
|
|
1509
1515
|
}
|
|
1510
1516
|
private async summaries(pins: Pins) {
|
|
1511
1517
|
if (pins.worktreeRevision) {
|
|
@@ -79,7 +79,14 @@ export async function reviewProgress(
|
|
|
79
79
|
// each resolve against the comparison their own pins name.
|
|
80
80
|
const comparison: ComparisonCoverage = pins
|
|
81
81
|
? (partial ?? (await data.coverage(snapshot.reviewId, pins, mode)))
|
|
82
|
-
: {
|
|
82
|
+
: {
|
|
83
|
+
counted: true,
|
|
84
|
+
files: [],
|
|
85
|
+
fileSources: new Map(),
|
|
86
|
+
alignments: new Map(),
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const counting = !comparison.counted;
|
|
83
90
|
|
|
84
91
|
signal.throwIfAborted();
|
|
85
92
|
|
|
@@ -98,7 +105,10 @@ export async function reviewProgress(
|
|
|
98
105
|
if (partial) {
|
|
99
106
|
const state = data.coverageSnapshot(snapshot.reviewId, own, mode);
|
|
100
107
|
|
|
101
|
-
if (state.pending)
|
|
108
|
+
if (state.pending)
|
|
109
|
+
return state.comparison.counted
|
|
110
|
+
? Promise.resolve(state.comparison)
|
|
111
|
+
: undefined;
|
|
102
112
|
}
|
|
103
113
|
|
|
104
114
|
loading = data.coverage(snapshot.reviewId, own, mode);
|
|
@@ -162,7 +172,7 @@ export async function reviewProgress(
|
|
|
162
172
|
|
|
163
173
|
// Context-only files are absent from the changed-file stream. Read both pins
|
|
164
174
|
// and require equality rather than inventing correspondence for missing diffs.
|
|
165
|
-
if (!file &&
|
|
175
|
+
if (!file && counting && !own) {
|
|
166
176
|
pendingSources.add(key);
|
|
167
177
|
|
|
168
178
|
return [];
|
|
@@ -228,9 +238,9 @@ export async function reviewProgress(
|
|
|
228
238
|
return {
|
|
229
239
|
...lens,
|
|
230
240
|
...resolved,
|
|
231
|
-
pending:
|
|
241
|
+
pending: counting,
|
|
232
242
|
unavailable:
|
|
233
|
-
|
|
243
|
+
counting || resolved.fileCount
|
|
234
244
|
? undefined
|
|
235
245
|
: "No files match these targets",
|
|
236
246
|
};
|
|
@@ -257,7 +267,7 @@ export async function reviewProgress(
|
|
|
257
267
|
),
|
|
258
268
|
);
|
|
259
269
|
|
|
260
|
-
const uncategorized =
|
|
270
|
+
const uncategorized = counting
|
|
261
271
|
? []
|
|
262
272
|
: uncategorizedSources(
|
|
263
273
|
files,
|
|
@@ -283,15 +293,15 @@ export async function reviewProgress(
|
|
|
283
293
|
)?.path ?? source.file,
|
|
284
294
|
),
|
|
285
295
|
).size,
|
|
286
|
-
pending:
|
|
296
|
+
pending: counting,
|
|
287
297
|
unavailable:
|
|
288
|
-
|
|
298
|
+
counting || uncategorized.length
|
|
289
299
|
? undefined
|
|
290
300
|
: "All changed lines are covered by lenses",
|
|
291
301
|
});
|
|
292
302
|
|
|
293
303
|
const progress: ReviewProgress = {
|
|
294
|
-
complete: !
|
|
304
|
+
complete: !counting,
|
|
295
305
|
files,
|
|
296
306
|
referenceFiles,
|
|
297
307
|
lenses,
|
|
@@ -63,9 +63,11 @@ export async function* structuralDiff(
|
|
|
63
63
|
|
|
64
64
|
args.push("--", ...(input.paths ?? []));
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
66
|
+
// Plugins can exceed two minutes between records. Apply this timeout only
|
|
67
|
+
// until the manifest arrives; plugins enforce their own request timeouts.
|
|
68
|
+
const startupAbort = new AbortController();
|
|
69
|
+
const startup = setTimeout(() => startupAbort.abort(), 120_000);
|
|
70
|
+
const signal = AbortSignal.any([startupAbort.signal, input.signal]);
|
|
69
71
|
|
|
70
72
|
// The host inherits its own environment and runs from the repository, so
|
|
71
73
|
// diffr reads the user's config and keys exactly as it would from a shell.
|
|
@@ -103,9 +105,6 @@ export async function* structuralDiff(
|
|
|
103
105
|
|
|
104
106
|
try {
|
|
105
107
|
for await (const line of lines) {
|
|
106
|
-
// Large comparisons may take minutes while continuing to make progress.
|
|
107
|
-
idle.refresh();
|
|
108
|
-
|
|
109
108
|
// Bound individual records, not the entire streamed comparison: a
|
|
110
109
|
// directory move can legitimately contain thousands of small files.
|
|
111
110
|
// Generated parsers in diffr can exceed 64 MiB after JSON encoding.
|
|
@@ -126,6 +125,7 @@ export async function* structuralDiff(
|
|
|
126
125
|
}
|
|
127
126
|
|
|
128
127
|
started = true;
|
|
128
|
+
clearTimeout(startup);
|
|
129
129
|
} else if (event.type === "complete") {
|
|
130
130
|
completed = true;
|
|
131
131
|
|
|
@@ -151,7 +151,7 @@ export async function* structuralDiff(
|
|
|
151
151
|
if (code !== 0 && !(code === 2 && (failed > 0 || aborted !== undefined)))
|
|
152
152
|
throw exitError(code);
|
|
153
153
|
} finally {
|
|
154
|
-
clearTimeout(
|
|
154
|
+
clearTimeout(startup);
|
|
155
155
|
lines.close();
|
|
156
156
|
|
|
157
157
|
if (child.exitCode === null) child.kill();
|
package/dist/profile-YLwWPRQp.js
DELETED