@dev.fast/whiteboard 0.0.0-stage → 0.2.1-preview.20261005.98
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +146 -2
- package/THIRD_PARTY_NOTICES.md +93 -0
- package/dist/account-alias-DOHN1RSH.js +973 -0
- package/dist/agent-cli-ChOq0ZuO.js +117 -0
- package/dist/agent-cli-DzWfZr6G.js +2 -0
- package/dist/agent-client-CPQo7iTI.js +305 -0
- package/dist/authoring-tools-Cr6Kpjsx.js +234 -0
- package/dist/build-info.json +1 -0
- package/dist/cli-hVgHcdsv.js +47 -0
- package/dist/cli-runner-D8y2_luU.js +5095 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +128 -0
- package/dist/client-DFoh50W3.js +950 -0
- package/dist/desktop-discovery-DBO-PM2V.js +164 -0
- package/dist/error-message-OtiDonty.js +6 -0
- package/dist/fs-utils-BMPLt0cr.js +22 -0
- package/dist/fuzzy-match-BoAmcyak.js +30 -0
- package/dist/headless-host-BJxf4dUU.js +94 -0
- package/dist/input-error-OLgB_h31.js +11 -0
- package/dist/local-data-CxLk75rx.js +4491 -0
- package/dist/mcp-QeF8vkdf.js +140 -0
- package/dist/package-paths-B6-zxvIO.js +47 -0
- package/dist/process-error-telemetry-v9e5h6D7.js +3952 -0
- package/dist/profile-4ry2f3AM.js +76 -0
- package/dist/profile-Bh5FIrmf.js +2 -0
- package/dist/request-origin-D_4QIdoN.js +811 -0
- package/dist/review-agent-traces-DNJFYzc8.js +3866 -0
- package/dist/review-home-paths-6zZH-1c9.js +2560 -0
- package/dist/review-telemetry-DEFnQsQ2.js +1195 -0
- package/dist/runtime-CKiWhGrf.js +52 -0
- package/dist/runtime.d.ts +9 -0
- package/dist/runtime.js +2 -0
- package/dist/s3-SzLpeCG6.js +345 -0
- package/dist/s3-config-BgxkSoOy.js +2 -0
- package/dist/s3-config-CAxhO9u_.js +676 -0
- package/dist/server/desktop-host.d.ts +4 -0
- package/dist/server/desktop-host.js +1702 -0
- package/dist/server-discovery-Dbzn3w6Z.js +66 -0
- package/dist/sharing/index.d.ts +1977 -0
- package/dist/sharing/index.js +2 -0
- package/dist/src-CGP5ytbV.js +283 -0
- package/dist/src-CmdiBL20.js +3676 -0
- package/dist/src-DnwdaQ2r.js +865 -0
- package/dist/src-X9phtB2j.js +68 -0
- package/dist/stored-document-migration-DHeMpLMb.js +2671 -0
- package/dist/tool-failure-C8zB73HV.js +98 -0
- package/dist/tutorial-trace-DrBvkaeL.js +43 -0
- package/instructions/authoring.md +36 -0
- package/instructions/file-lenses.md +13 -0
- package/instructions/scratchpad.md +23 -0
- package/instructions/trace-archaeology.md +113 -0
- package/onboarding.md +8 -0
- package/package.json +106 -3
- package/src/agent-selection.ts +100 -0
- package/src/agent-session-ref.ts +80 -0
- package/src/ask/agents.ts +351 -0
- package/src/ask/checkout-files.ts +47 -0
- package/src/ask/file-refs.ts +111 -0
- package/src/ask/pi-mcp.ts +154 -0
- package/src/ask/protocol.ts +244 -0
- package/src/ask/thread-state.ts +350 -0
- package/src/ask/thread.ts +1400 -0
- package/src/ask/threads.ts +219 -0
- package/src/ask/watch.ts +79 -0
- package/src/cli-install.ts +1041 -0
- package/src/cli-runner.ts +1461 -0
- package/src/cli-runtime-info.ts +35 -0
- package/src/cli.ts +222 -0
- package/src/connect-prompts.ts +248 -0
- package/src/cursor-deeplink.ts +11 -0
- package/src/desktop-discovery.ts +355 -0
- package/src/diff-selection-migration.ts +63 -0
- package/src/embedded-posthog-key.ts +6 -0
- package/src/error-telemetry.ts +247 -0
- package/src/evidence.ts +14 -0
- package/src/exception-telemetry.ts +126 -0
- package/src/fixtures/blocks/call_stack_diff.json +27 -0
- package/src/fixtures/blocks/callout.json +11 -0
- package/src/fixtures/blocks/code.json +9 -0
- package/src/fixtures/blocks/code_peek.json +8 -0
- package/src/fixtures/blocks/database_lens.json +51 -0
- package/src/fixtures/blocks/divider.json +1 -0
- package/src/fixtures/blocks/fixtures.ts +33 -0
- package/src/fixtures/blocks/flow_diagram.json +36 -0
- package/src/fixtures/blocks/ids.ts +8 -0
- package/src/fixtures/blocks/image.json +9 -0
- package/src/fixtures/blocks/markdown.json +7 -0
- package/src/fixtures/blocks/section.json +11 -0
- package/src/fixtures/blocks/sequence.json +31 -0
- package/src/fixtures/blocks/software_map.json +7 -0
- package/src/fixtures/blocks/trace_quote.json +9 -0
- package/src/fixtures/blocks/tutorial.json +51 -0
- package/src/fs-utils.ts +32 -0
- package/src/fuzzy-match.ts +94 -0
- package/src/install.ts +29 -0
- package/src/legacy-skills.ts +133 -0
- package/src/lens-selection.ts +230 -0
- package/src/markdown-latex-math.ts +230 -0
- package/src/markdown.ts +81 -0
- package/src/package-paths.ts +53 -0
- package/src/posthog-capture-client.ts +610 -0
- package/src/review-api/README.md +295 -0
- package/src/review-api/activity.ts +340 -0
- package/src/review-api/agent-cli.ts +219 -0
- package/src/review-api/agent-client.ts +188 -0
- package/src/review-api/anchor-quotes.ts +88 -0
- package/src/review-api/ask-history.ts +242 -0
- package/src/review-api/authoring-tools.ts +211 -0
- package/src/review-api/blocks/call_stack_diff.ts +75 -0
- package/src/review-api/blocks/callout.ts +24 -0
- package/src/review-api/blocks/code.ts +9 -0
- package/src/review-api/blocks/code_peek.ts +13 -0
- package/src/review-api/blocks/database_lens.ts +150 -0
- package/src/review-api/blocks/definition.ts +40 -0
- package/src/review-api/blocks/divider.ts +6 -0
- package/src/review-api/blocks/flow_diagram.ts +111 -0
- package/src/review-api/blocks/image.ts +10 -0
- package/src/review-api/blocks/index.ts +90 -0
- package/src/review-api/blocks/markdown.ts +17 -0
- package/src/review-api/blocks/section.ts +24 -0
- package/src/review-api/blocks/sequence.ts +56 -0
- package/src/review-api/blocks/software_map.ts +9 -0
- package/src/review-api/blocks/trace_quote.ts +10 -0
- package/src/review-api/blocks/tutorial.ts +39 -0
- package/src/review-api/checkout-fs.ts +14 -0
- package/src/review-api/client.ts +1 -0
- package/src/review-api/comparison-coverage.ts +304 -0
- package/src/review-api/component-reference.ts +30 -0
- package/src/review-api/diff-lenses.ts +175 -0
- package/src/review-api/document-headings.ts +51 -0
- package/src/review-api/document-text.ts +200 -0
- package/src/review-api/document.ts +906 -0
- package/src/review-api/file-lenses.ts +100 -0
- package/src/review-api/http.ts +1877 -0
- package/src/review-api/image-decode.ts +29 -0
- package/src/review-api/input-error.ts +9 -0
- package/src/review-api/instructions.ts +89 -0
- package/src/review-api/lens-alignment.ts +52 -0
- package/src/review-api/local-data.ts +1837 -0
- package/src/review-api/map-input.ts +154 -0
- package/src/review-api/mcp-client-agent.ts +32 -0
- package/src/review-api/mcp.ts +240 -0
- package/src/review-api/origin.ts +43 -0
- package/src/review-api/profile.ts +146 -0
- package/src/review-api/public-tools.ts +102 -0
- package/src/review-api/pull-request.ts +389 -0
- package/src/review-api/read-schemas.ts +85 -0
- package/src/review-api/recovery.ts +2 -0
- package/src/review-api/request-origin.ts +33 -0
- package/src/review-api/review-progress.ts +382 -0
- package/src/review-api/status-tool.ts +10 -0
- package/src/review-api/store-schema.ts +22 -0
- package/src/review-api/store.ts +1647 -0
- package/src/review-api/tool-failure.ts +64 -0
- package/src/review-api/trace-schema.ts +13 -0
- package/src/review-api/traces.ts +119 -0
- package/src/review-api/unsupported-files.integration.ts +91 -0
- package/src/review-api/workspaces.ts +692 -0
- package/src/review-api/worktree-source.ts +170 -0
- package/src/review-api/worktree-structural.integration.ts +320 -0
- package/src/review-app-launcher.ts +432 -0
- package/src/review-app-picker.ts +168 -0
- package/src/review-app.ts +134 -0
- package/src/review-bundled-tools.ts +249 -0
- package/src/review-checkout-paths.ts +37 -0
- package/src/review-diff-files.ts +89 -0
- package/src/review-head-checkout.ts +300 -0
- package/src/review-home-paths.ts +73 -0
- package/src/review-info.ts +59 -0
- package/src/review-instances.ts +113 -0
- package/src/review-logger.ts +183 -0
- package/src/review-preferences.ts +88 -0
- package/src/review-prepare.ts +291 -0
- package/src/review-stack.ts +112 -0
- package/src/review-telemetry.ts +1066 -0
- package/src/runtime.ts +74 -0
- package/src/server/account-alias.ts +30 -0
- package/src/server/bounded-stream.ts +34 -0
- package/src/server/bug-report.ts +301 -0
- package/src/server/client-error-budget.ts +45 -0
- package/src/server/crash-report.ts +273 -0
- package/src/server/desktop-host-shutdown.ts +64 -0
- package/src/server/desktop-host.ts +190 -0
- package/src/server/desktop-server.ts +703 -0
- package/src/server/diffr-config.ts +509 -0
- package/src/server/diffr-languages.ts +97 -0
- package/src/server/global-verb-relay.ts +190 -0
- package/src/server/headless-host.ts +149 -0
- package/src/server/hono-http.ts +162 -0
- package/src/server/http-json.ts +24 -0
- package/src/server/json-review-reporting.ts +174 -0
- package/src/server/process-error-telemetry.ts +174 -0
- package/src/server/review-api-parsers.ts +90 -0
- package/src/server/review-lifecycle-telemetry.ts +73 -0
- package/src/server/review-open-watchdog.ts +46 -0
- package/src/server/review-server-core.ts +290 -0
- package/src/server/structural-comparisons.ts +153 -0
- package/src/server/structural-diff.ts +186 -0
- package/src/server/tutorial-service.ts +241 -0
- package/src/server/ui-telemetry.ts +198 -0
- package/src/server-discovery.ts +95 -0
- package/src/session-markers.ts +132 -0
- package/src/sharing/auth.ts +41 -0
- package/src/sharing/cli.ts +83 -0
- package/src/sharing/client.ts +334 -0
- package/src/sharing/export.ts +195 -0
- package/src/sharing/host.ts +316 -0
- package/src/sharing/import.ts +787 -0
- package/src/sharing/index.ts +16 -0
- package/src/sharing/repository.ts +145 -0
- package/src/sharing/routes.ts +34 -0
- package/src/slug.ts +23 -0
- package/src/software-map-diff-counts.ts +517 -0
- package/src/software-map-model.ts +1147 -0
- package/src/software-map-topology-diff.ts +260 -0
- package/src/source.ts +92 -0
- package/src/startup-trace.ts +232 -0
- package/src/stored-document-migration.ts +151 -0
- package/src/telemetry-clean-text.ts +257 -0
- package/src/telemetry-config.ts +298 -0
- package/src/telemetry-debug-sink.ts +38 -0
- package/src/telemetry.ts +13 -0
- package/src/trace-cli.ts +156 -0
- package/src/trace-storage-cli.ts +509 -0
- package/src/tutorial-conversation.ts +18 -0
- package/src/ui-telemetry-events.ts +765 -0
- package/src/unified-diff.ts +71 -0
- package/src/viewed-coverage.ts +259 -0
- package/src/windows-cli.ts +125 -0
- package/tutorial/document.json +275 -0
- package/tutorial/runtime-manifest.json +12 -0
- package/tutorial/sample-service/package.json +9 -0
- package/tutorial/sample-service/src/api/checkout-api.ts +17 -0
- package/tutorial/sample-service/src/app.ts +26 -0
- package/tutorial/sample-service/src/database/schema.ts +10 -0
- package/tutorial/sample-service/src/fulfillment/fulfillment-queue.ts +15 -0
- package/tutorial/sample-service/src/fulfillment/fulfillment-worker.ts +24 -0
- package/tutorial/sample-service/src/inventory/inventory-service.ts +11 -0
- package/tutorial/sample-service/src/orders/order-service.ts +34 -0
- package/tutorial/sample-service/src/orders/order.ts +21 -0
- package/tutorial/sample-service/src/orders/orders-repository.ts +24 -0
- package/tutorial/sample-service/src/payments/payment-gateway.ts +12 -0
- package/tutorial/sample-service/src/shipping/shipping-gateway.ts +15 -0
- package/tutorial/sample-service/tsconfig.json +12 -0
- package/tutorial/software-map.json +139 -0
- package/tutorial/trace.json +20 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export interface DiffHunk {
|
|
2
|
+
file: string;
|
|
3
|
+
oldStart: number;
|
|
4
|
+
oldLines: number;
|
|
5
|
+
newStart: number;
|
|
6
|
+
newLines: number;
|
|
7
|
+
lines: DiffHunkLine[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface DiffHunkLine {
|
|
11
|
+
kind: "context" | "add" | "remove";
|
|
12
|
+
oldLine: number | null;
|
|
13
|
+
newLine: number | null;
|
|
14
|
+
text: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const HUNK_HEADER =
|
|
18
|
+
/^@@ -(?<oldStart>\d+)(?:,(?<oldLines>\d+))? \+(?<newStart>\d+)(?:,(?<newLines>\d+))? @@/;
|
|
19
|
+
|
|
20
|
+
export function parseUnifiedPatch(
|
|
21
|
+
file: string,
|
|
22
|
+
patch: string | null | undefined,
|
|
23
|
+
): DiffHunk[] {
|
|
24
|
+
if (!patch) return [];
|
|
25
|
+
|
|
26
|
+
const hunks: DiffHunk[] = [];
|
|
27
|
+
let current: DiffHunk | null = null;
|
|
28
|
+
let oldLine = 0;
|
|
29
|
+
let newLine = 0;
|
|
30
|
+
|
|
31
|
+
for (const row of patch.split(/\r?\n/)) {
|
|
32
|
+
const header = HUNK_HEADER.exec(row);
|
|
33
|
+
|
|
34
|
+
if (header?.groups) {
|
|
35
|
+
current = {
|
|
36
|
+
file,
|
|
37
|
+
oldStart: Number(header.groups.oldStart),
|
|
38
|
+
oldLines: Number(header.groups.oldLines ?? "1"),
|
|
39
|
+
newStart: Number(header.groups.newStart),
|
|
40
|
+
newLines: Number(header.groups.newLines ?? "1"),
|
|
41
|
+
lines: [],
|
|
42
|
+
};
|
|
43
|
+
oldLine = current.oldStart;
|
|
44
|
+
newLine = current.newStart;
|
|
45
|
+
hunks.push(current);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!current || row.startsWith("\")) continue;
|
|
50
|
+
|
|
51
|
+
const marker = row[0];
|
|
52
|
+
const text = row.slice(1);
|
|
53
|
+
let line: DiffHunkLine | null = null;
|
|
54
|
+
|
|
55
|
+
if (marker === " ") {
|
|
56
|
+
line = { kind: "context", oldLine, newLine, text };
|
|
57
|
+
oldLine += 1;
|
|
58
|
+
newLine += 1;
|
|
59
|
+
} else if (marker === "+") {
|
|
60
|
+
line = { kind: "add", oldLine: null, newLine, text };
|
|
61
|
+
newLine += 1;
|
|
62
|
+
} else if (marker === "-") {
|
|
63
|
+
line = { kind: "remove", oldLine, newLine: null, text };
|
|
64
|
+
oldLine += 1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (line) current.lines.push(line);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return hunks;
|
|
71
|
+
}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { structuralChangeCounts } from "@dev.fast/review-protocol";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import type { FileLineRange } from "./source.js";
|
|
5
|
+
|
|
6
|
+
/** Zero-based, half-open intervals; always normalized at the storage boundary. */
|
|
7
|
+
export type LineInterval = [number, number];
|
|
8
|
+
|
|
9
|
+
export interface Coverage {
|
|
10
|
+
base: LineInterval[];
|
|
11
|
+
head: LineInterval[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const coverageSchema = z.object({
|
|
15
|
+
base: z.array(
|
|
16
|
+
z.tuple([z.number().int().nonnegative(), z.number().int().nonnegative()]),
|
|
17
|
+
),
|
|
18
|
+
head: z.array(
|
|
19
|
+
z.tuple([z.number().int().nonnegative(), z.number().int().nonnegative()]),
|
|
20
|
+
),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export interface ChangeCounts {
|
|
24
|
+
additions: number;
|
|
25
|
+
deletions: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Changed lines that diffr folds by default count as done, the way the
|
|
30
|
+
* reader's viewed marks do. `folded` is how many of the scope's lines are
|
|
31
|
+
* done only because they are folded; `remaining` excludes them.
|
|
32
|
+
* "folded" is the state of a scope with nothing left to read and nothing the
|
|
33
|
+
* reader marked: all of it starts folded.
|
|
34
|
+
*/
|
|
35
|
+
export interface CoverageProgress {
|
|
36
|
+
state: "unread" | "partial" | "viewed" | "folded";
|
|
37
|
+
total: ChangeCounts;
|
|
38
|
+
remaining: ChangeCounts;
|
|
39
|
+
folded: ChangeCounts;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface CoverageFile {
|
|
43
|
+
path: string;
|
|
44
|
+
previousPath?: string;
|
|
45
|
+
fingerprint: string;
|
|
46
|
+
changed: Coverage;
|
|
47
|
+
/** The changed lines diffr folds by default: every changed line of a file
|
|
48
|
+
* it hides, or those under a region that starts collapsed. Absent without
|
|
49
|
+
* structural data, where every changed line is left to read. */
|
|
50
|
+
folded?: Coverage;
|
|
51
|
+
viewed: Coverage;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const emptyCoverage = (): Coverage => ({ base: [], head: [] });
|
|
55
|
+
|
|
56
|
+
export function unionIntervals(
|
|
57
|
+
ranges: readonly LineInterval[],
|
|
58
|
+
): LineInterval[] {
|
|
59
|
+
const result: LineInterval[] = [];
|
|
60
|
+
|
|
61
|
+
for (const [start, end] of [...ranges].sort((a, b) => a[0] - b[0])) {
|
|
62
|
+
if (end <= start) continue;
|
|
63
|
+
const last = result.at(-1);
|
|
64
|
+
|
|
65
|
+
if (last && start <= last[1]) last[1] = Math.max(last[1], end);
|
|
66
|
+
else result.push([start, end]);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function intersectIntervals(
|
|
73
|
+
left: readonly LineInterval[],
|
|
74
|
+
right: readonly LineInterval[],
|
|
75
|
+
): LineInterval[] {
|
|
76
|
+
const result: LineInterval[] = [];
|
|
77
|
+
|
|
78
|
+
const a = unionIntervals(left),
|
|
79
|
+
b = unionIntervals(right);
|
|
80
|
+
|
|
81
|
+
let i = 0,
|
|
82
|
+
j = 0;
|
|
83
|
+
|
|
84
|
+
while (i < a.length && j < b.length) {
|
|
85
|
+
const start = Math.max(a[i][0], b[j][0]),
|
|
86
|
+
end = Math.min(a[i][1], b[j][1]);
|
|
87
|
+
|
|
88
|
+
if (start < end) result.push([start, end]);
|
|
89
|
+
|
|
90
|
+
if (a[i][1] < b[j][1]) i++;
|
|
91
|
+
else j++;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function subtractIntervals(
|
|
98
|
+
left: readonly LineInterval[],
|
|
99
|
+
right: readonly LineInterval[],
|
|
100
|
+
): LineInterval[] {
|
|
101
|
+
const result: LineInterval[] = [];
|
|
102
|
+
const removed = unionIntervals(right);
|
|
103
|
+
|
|
104
|
+
for (const [start, end] of unionIntervals(left)) {
|
|
105
|
+
let cursor = start;
|
|
106
|
+
|
|
107
|
+
for (const [from, to] of removed) {
|
|
108
|
+
if (to <= cursor) continue;
|
|
109
|
+
|
|
110
|
+
if (from >= end) break;
|
|
111
|
+
|
|
112
|
+
if (from > cursor) result.push([cursor, from]);
|
|
113
|
+
cursor = Math.min(end, Math.max(cursor, to));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (cursor < end) result.push([cursor, end]);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function updateCoverage(
|
|
123
|
+
current: Coverage,
|
|
124
|
+
scope: Coverage,
|
|
125
|
+
viewed: boolean,
|
|
126
|
+
): Coverage {
|
|
127
|
+
const apply = (side: "base" | "head") =>
|
|
128
|
+
viewed
|
|
129
|
+
? unionIntervals([...current[side], ...scope[side]])
|
|
130
|
+
: subtractIntervals(current[side], scope[side]);
|
|
131
|
+
|
|
132
|
+
return { base: apply("base"), head: apply("head") };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function scopedCoverage(
|
|
136
|
+
file: CoverageFile,
|
|
137
|
+
sources?: readonly FileLineRange[],
|
|
138
|
+
): Coverage {
|
|
139
|
+
if (!sources) return file.changed;
|
|
140
|
+
|
|
141
|
+
const side = (side: "base" | "head") =>
|
|
142
|
+
intersectIntervals(
|
|
143
|
+
file.changed[side],
|
|
144
|
+
sources.flatMap((source): LineInterval[] =>
|
|
145
|
+
source.side === side &&
|
|
146
|
+
source.file ===
|
|
147
|
+
(side === "base" ? (file.previousPath ?? file.path) : file.path)
|
|
148
|
+
? [[source.fromLine - 1, source.toLine]]
|
|
149
|
+
: [],
|
|
150
|
+
),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
return { base: side("base"), head: side("head") };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function coverageProgress(
|
|
157
|
+
files: readonly CoverageFile[],
|
|
158
|
+
sources?: readonly FileLineRange[],
|
|
159
|
+
): CoverageProgress {
|
|
160
|
+
const total = { additions: 0, deletions: 0 },
|
|
161
|
+
remaining = { additions: 0, deletions: 0 },
|
|
162
|
+
folded = { additions: 0, deletions: 0 };
|
|
163
|
+
|
|
164
|
+
for (const file of files) {
|
|
165
|
+
const scope = scopedCoverage(file, sources);
|
|
166
|
+
const counts = structuralChangeCounts(scope);
|
|
167
|
+
|
|
168
|
+
const side = (side: "base" | "head") => {
|
|
169
|
+
const unviewed = subtractIntervals(scope[side], file.viewed[side]);
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
unread: subtractIntervals(unviewed, file.folded?.[side] ?? []),
|
|
173
|
+
folded: intersectIntervals(unviewed, file.folded?.[side] ?? []),
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const base = side("base"),
|
|
178
|
+
head = side("head");
|
|
179
|
+
|
|
180
|
+
const unread = structuralChangeCounts({
|
|
181
|
+
base: base.unread,
|
|
182
|
+
head: head.unread,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const foldedCounts = structuralChangeCounts({
|
|
186
|
+
base: base.folded,
|
|
187
|
+
head: head.folded,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
total.additions += counts.added;
|
|
191
|
+
total.deletions += counts.removed;
|
|
192
|
+
remaining.additions += unread.added;
|
|
193
|
+
remaining.deletions += unread.removed;
|
|
194
|
+
folded.additions += foldedCounts.added;
|
|
195
|
+
folded.deletions += foldedCounts.removed;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
total,
|
|
200
|
+
remaining,
|
|
201
|
+
folded,
|
|
202
|
+
state: progressState(total, remaining, folded),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Progress over several comparisons at once: the sums, and the same state
|
|
207
|
+
* rule as one. */
|
|
208
|
+
export function mergeCoverageProgress(
|
|
209
|
+
parts: readonly CoverageProgress[],
|
|
210
|
+
): CoverageProgress {
|
|
211
|
+
const total = { additions: 0, deletions: 0 },
|
|
212
|
+
remaining = { additions: 0, deletions: 0 },
|
|
213
|
+
folded = { additions: 0, deletions: 0 };
|
|
214
|
+
|
|
215
|
+
for (const part of parts) {
|
|
216
|
+
total.additions += part.total.additions;
|
|
217
|
+
total.deletions += part.total.deletions;
|
|
218
|
+
remaining.additions += part.remaining.additions;
|
|
219
|
+
remaining.deletions += part.remaining.deletions;
|
|
220
|
+
folded.additions += part.folded.additions;
|
|
221
|
+
folded.deletions += part.folded.deletions;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
total,
|
|
226
|
+
remaining,
|
|
227
|
+
folded,
|
|
228
|
+
state: progressState(total, remaining, folded),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Done lines are the viewed and the folded ones; the state names which. */
|
|
233
|
+
function progressState(
|
|
234
|
+
total: ChangeCounts,
|
|
235
|
+
remaining: ChangeCounts,
|
|
236
|
+
folded: ChangeCounts,
|
|
237
|
+
): CoverageProgress["state"] {
|
|
238
|
+
const size = total.additions + total.deletions,
|
|
239
|
+
unread = remaining.additions + remaining.deletions,
|
|
240
|
+
viewed = size - unread - folded.additions - folded.deletions;
|
|
241
|
+
|
|
242
|
+
if (size > 0 && unread === 0) return viewed > 0 ? "viewed" : "folded";
|
|
243
|
+
|
|
244
|
+
return viewed > 0 ? "partial" : "unread";
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export function coverageSources(
|
|
248
|
+
file: CoverageFile,
|
|
249
|
+
coverage = file.viewed,
|
|
250
|
+
): FileLineRange[] {
|
|
251
|
+
return (["base", "head"] as const).flatMap((side) =>
|
|
252
|
+
coverage[side].map(([start, end]) => ({
|
|
253
|
+
side,
|
|
254
|
+
file: side === "base" ? (file.previousPath ?? file.path) : file.path,
|
|
255
|
+
fromLine: start + 1,
|
|
256
|
+
toLine: end,
|
|
257
|
+
})),
|
|
258
|
+
);
|
|
259
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
|
|
5
|
+
const execFileAsync = promisify(execFile);
|
|
6
|
+
|
|
7
|
+
/** cmd expands percent signs even inside quotes; delayed expansion stays off. */
|
|
8
|
+
function batchPath(value: string): string {
|
|
9
|
+
if (/["\r\n\0]/.test(value)) throw new Error("Invalid Windows command path");
|
|
10
|
+
|
|
11
|
+
return value.replaceAll("%", "%%");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function windowsCliShim(
|
|
15
|
+
cliPath: string,
|
|
16
|
+
runtimePath: string,
|
|
17
|
+
devHome?: string,
|
|
18
|
+
): string {
|
|
19
|
+
const lines = [
|
|
20
|
+
"@echo off",
|
|
21
|
+
"rem Managed by Whiteboard Desktop. Do not edit.",
|
|
22
|
+
"setlocal DisableDelayedExpansion",
|
|
23
|
+
'set "ELECTRON_RUN_AS_NODE=1"',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
if (devHome)
|
|
27
|
+
lines.push(
|
|
28
|
+
`if not defined DEV_REVIEW_HOME set "DEV_REVIEW_HOME=${batchPath(devHome)}"`,
|
|
29
|
+
);
|
|
30
|
+
lines.push(
|
|
31
|
+
`"${batchPath(runtimePath)}" "${batchPath(cliPath)}" %*`,
|
|
32
|
+
"exit /b %errorlevel%",
|
|
33
|
+
"",
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
return lines.join("\r\n");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Preserve the unexpanded user PATH, including entries such as %USERPROFILE%. */
|
|
40
|
+
export async function updateWindowsUserPath(
|
|
41
|
+
directory: string,
|
|
42
|
+
remove = false,
|
|
43
|
+
): Promise<void> {
|
|
44
|
+
const script = `
|
|
45
|
+
$ErrorActionPreference = 'Stop'
|
|
46
|
+
$key = [Microsoft.Win32.Registry]::CurrentUser.CreateSubKey('Environment')
|
|
47
|
+
$state = [Microsoft.Win32.Registry]::CurrentUser.CreateSubKey('Software\\dev.fast\\Whiteboard\\CLI')
|
|
48
|
+
try {
|
|
49
|
+
$old = [string]$key.GetValue('Path', '', [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
|
|
50
|
+
$dir = $env:WHITEBOARD_COMMAND_DIRECTORY
|
|
51
|
+
$entries = @($old.Split(';'))
|
|
52
|
+
$matches = @($entries | Where-Object { [Environment]::ExpandEnvironmentVariables($_).TrimEnd('\\') -ieq $dir.TrimEnd('\\') })
|
|
53
|
+
if ($env:WHITEBOARD_REMOVE_COMMAND_PATH -eq '1') {
|
|
54
|
+
if ([string]$state.GetValue('Path', '') -ine $dir) { return }
|
|
55
|
+
$entries = @($entries | Where-Object { [Environment]::ExpandEnvironmentVariables($_).TrimEnd('\\') -ine $dir.TrimEnd('\\') })
|
|
56
|
+
$key.SetValue('Path', ($entries -join ';'), [Microsoft.Win32.RegistryValueKind]::ExpandString)
|
|
57
|
+
$state.DeleteValue('Path', $false)
|
|
58
|
+
} else {
|
|
59
|
+
if ($matches.Count -gt 0) { return }
|
|
60
|
+
$state.SetValue('Path', $dir)
|
|
61
|
+
$value = if ($old) { $dir + ';' + $old } else { $dir }
|
|
62
|
+
$key.SetValue('Path', $value, [Microsoft.Win32.RegistryValueKind]::ExpandString)
|
|
63
|
+
}
|
|
64
|
+
} finally { $state.Dispose(); $key.Dispose() }
|
|
65
|
+
Add-Type -Namespace Whiteboard -Name EnvironmentChange -MemberDefinition '[System.Runtime.InteropServices.DllImport("user32.dll", CharSet=System.Runtime.InteropServices.CharSet.Unicode)] public static extern System.IntPtr SendMessageTimeout(System.IntPtr h, uint m, System.UIntPtr w, string l, uint f, uint t, out System.UIntPtr r);'
|
|
66
|
+
$result = [UIntPtr]::Zero
|
|
67
|
+
[void][Whiteboard.EnvironmentChange]::SendMessageTimeout([IntPtr]0xffff, 0x1a, [UIntPtr]::Zero, 'Environment', 2, 5000, [ref]$result)
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
await execFileAsync(
|
|
71
|
+
"powershell.exe",
|
|
72
|
+
[
|
|
73
|
+
"-NoProfile",
|
|
74
|
+
"-NonInteractive",
|
|
75
|
+
"-ExecutionPolicy",
|
|
76
|
+
"Bypass",
|
|
77
|
+
"-EncodedCommand",
|
|
78
|
+
Buffer.from(script, "utf16le").toString("base64"),
|
|
79
|
+
],
|
|
80
|
+
{
|
|
81
|
+
windowsHide: true,
|
|
82
|
+
env: {
|
|
83
|
+
...process.env,
|
|
84
|
+
WHITEBOARD_COMMAND_DIRECTORY: directory,
|
|
85
|
+
WHITEBOARD_REMOVE_COMMAND_PATH: remove ? "1" : "0",
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const WINDOWS_MACHINE_ENVIRONMENT_KEY =
|
|
92
|
+
"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment";
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* A running process keeps the PATH it started with, so the saved user PATH is
|
|
96
|
+
* what tells whether new terminals will find the command.
|
|
97
|
+
*/
|
|
98
|
+
export async function windowsUserPathContains(
|
|
99
|
+
directory: string,
|
|
100
|
+
key = "HKCU\\Environment",
|
|
101
|
+
): Promise<boolean> {
|
|
102
|
+
let stdout: string;
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
({ stdout } = await execFileAsync("reg.exe", ["query", key, "/v", "Path"], {
|
|
106
|
+
windowsHide: true,
|
|
107
|
+
}));
|
|
108
|
+
} catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const saved = stdout.match(/^\s*Path\s+REG_\w+\s*(.*)$/im)?.[1] ?? "";
|
|
113
|
+
const target = path.win32.resolve(directory).toLowerCase();
|
|
114
|
+
|
|
115
|
+
return saved.split(";").some((entry) => {
|
|
116
|
+
const expanded = entry
|
|
117
|
+
.trim()
|
|
118
|
+
.replace(/%([^%]+)%/g, (whole, name) => process.env[name] ?? whole);
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
expanded.length > 0 &&
|
|
122
|
+
path.win32.resolve(expanded).toLowerCase() === target
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
}
|