@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,517 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type LocalVcsKind,
|
|
3
|
+
diff as readLocalVcsDiff,
|
|
4
|
+
} from "@dev.fast/local-vcs";
|
|
5
|
+
import { jsonString, parseJsonText } from "@dev.fast/review-protocol";
|
|
6
|
+
|
|
7
|
+
import { type DiffHunkLine, parseUnifiedPatch } from "./unified-diff";
|
|
8
|
+
|
|
9
|
+
export interface SoftwareMapDiffLineCount {
|
|
10
|
+
additions: number;
|
|
11
|
+
deletions: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface SoftwareMapCoverageFileInput {
|
|
15
|
+
path: string;
|
|
16
|
+
ranges?: SoftwareMapLineRangeInput[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface SoftwareMapCoverageClaimInput {
|
|
20
|
+
path: string;
|
|
21
|
+
files?: SoftwareMapCoverageFileInput[];
|
|
22
|
+
globs?: string[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface SoftwareMapLineRangeInput {
|
|
26
|
+
fromLine: number;
|
|
27
|
+
toLine: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface SoftwareMapCodeElementInput {
|
|
31
|
+
path: string;
|
|
32
|
+
label?: string;
|
|
33
|
+
description?: string;
|
|
34
|
+
changeStatus?: "added" | "removed" | "modified" | "unchanged";
|
|
35
|
+
sourceRanges?: Array<SoftwareMapLineRangeInput & { file: string }>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface SoftwareMapUnmappedDiffLine {
|
|
39
|
+
kind: "add" | "remove";
|
|
40
|
+
oldLine: number | null;
|
|
41
|
+
newLine: number | null;
|
|
42
|
+
text: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface SoftwareMapUnmappedDiffHunk {
|
|
46
|
+
startLine: number;
|
|
47
|
+
lines: SoftwareMapUnmappedDiffLine[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface SoftwareMapUnmappedDiffFile extends SoftwareMapDiffLineCount {
|
|
51
|
+
file: string;
|
|
52
|
+
hunks: SoftwareMapUnmappedDiffHunk[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface SoftwareMapUnmappedDiffSummary extends SoftwareMapDiffLineCount {
|
|
56
|
+
files: SoftwareMapUnmappedDiffFile[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type SoftwareMapDiffCountsByElementPath = Record<
|
|
60
|
+
string,
|
|
61
|
+
SoftwareMapDiffLineCount
|
|
62
|
+
>;
|
|
63
|
+
|
|
64
|
+
type SoftwareMapUnmappedDiffByElementPath = Record<
|
|
65
|
+
string,
|
|
66
|
+
SoftwareMapUnmappedDiffSummary
|
|
67
|
+
>;
|
|
68
|
+
|
|
69
|
+
interface SoftwareMapDiffCountsResult {
|
|
70
|
+
baseRef?: string;
|
|
71
|
+
headRef?: string;
|
|
72
|
+
countsByElementPath: SoftwareMapDiffCountsByElementPath;
|
|
73
|
+
unmappedByElementPath: SoftwareMapUnmappedDiffByElementPath;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface ResolveSoftwareMapDiffCountsInput {
|
|
77
|
+
/** Already retained source diff, when the source is a working generation. */
|
|
78
|
+
patch?: string;
|
|
79
|
+
sourceRootPath: string;
|
|
80
|
+
/** The source repository's kind, when the caller already detected it. */
|
|
81
|
+
sourceVcsKind?: LocalVcsKind;
|
|
82
|
+
side?: "base" | "head";
|
|
83
|
+
baseRef?: string;
|
|
84
|
+
headRef?: string;
|
|
85
|
+
codeElements: SoftwareMapCodeElementInput[];
|
|
86
|
+
coverageClaims?: SoftwareMapCoverageClaimInput[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface FileLineChange extends SoftwareMapDiffLineCount {
|
|
90
|
+
rows: SoftwareMapUnmappedDiffLine[];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
type FileLineCounts = Map<string, Map<number, FileLineChange>>;
|
|
94
|
+
|
|
95
|
+
export async function resolveSoftwareMapDiffCounts(
|
|
96
|
+
input: ResolveSoftwareMapDiffCountsInput,
|
|
97
|
+
): Promise<SoftwareMapDiffCountsResult> {
|
|
98
|
+
const baseRef = input.baseRef?.trim();
|
|
99
|
+
|
|
100
|
+
if (!baseRef) {
|
|
101
|
+
return { countsByElementPath: {}, unmappedByElementPath: {} };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const headRef = input.headRef?.trim() || undefined;
|
|
105
|
+
const sourceRootPath = input.sourceRootPath;
|
|
106
|
+
|
|
107
|
+
const diff =
|
|
108
|
+
input.patch ??
|
|
109
|
+
(await readLocalVcsDiff({
|
|
110
|
+
rootPath: sourceRootPath,
|
|
111
|
+
kind: input.sourceVcsKind,
|
|
112
|
+
baseRef,
|
|
113
|
+
headRef,
|
|
114
|
+
contextLines: 0,
|
|
115
|
+
}).catch(() => ""));
|
|
116
|
+
|
|
117
|
+
if (!diff.trim()) {
|
|
118
|
+
return {
|
|
119
|
+
baseRef,
|
|
120
|
+
headRef,
|
|
121
|
+
countsByElementPath: {},
|
|
122
|
+
unmappedByElementPath: {},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const countsByFile = parseGitUnifiedDiffLineCounts(diff, input.side);
|
|
127
|
+
|
|
128
|
+
if (countsByFile.size === 0) {
|
|
129
|
+
return {
|
|
130
|
+
baseRef,
|
|
131
|
+
headRef,
|
|
132
|
+
countsByElementPath: {},
|
|
133
|
+
unmappedByElementPath: {},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const mapped = await mapDiffLineCountsToSoftwareMapElements({
|
|
138
|
+
codeElements: input.codeElements,
|
|
139
|
+
countsByFile,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const unmappedByElementPath = mapDiffLineCountsToCoverageClaims({
|
|
143
|
+
coverageClaims: input.coverageClaims ?? [],
|
|
144
|
+
countsByFile,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
baseRef,
|
|
149
|
+
headRef,
|
|
150
|
+
countsByElementPath: mapped.countsByElementPath,
|
|
151
|
+
unmappedByElementPath,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function parseGitUnifiedDiffLineCounts(
|
|
156
|
+
diff: string,
|
|
157
|
+
side: "base" | "head" = "head",
|
|
158
|
+
): FileLineCounts {
|
|
159
|
+
const countsByFile: FileLineCounts = new Map();
|
|
160
|
+
|
|
161
|
+
for (const fileDiff of splitGitDiff(diff)) {
|
|
162
|
+
const file =
|
|
163
|
+
side === "base"
|
|
164
|
+
? (fileDiff.oldFile ?? fileDiff.newFile)
|
|
165
|
+
: (fileDiff.newFile ?? fileDiff.oldFile);
|
|
166
|
+
|
|
167
|
+
if (!file) continue;
|
|
168
|
+
|
|
169
|
+
for (const hunk of parseUnifiedPatch(file, fileDiff.patch)) {
|
|
170
|
+
let currentNewLine = Math.max(1, hunk.newStart);
|
|
171
|
+
let currentOldLine = Math.max(1, hunk.oldStart);
|
|
172
|
+
// Base-side additions belong on the line the removed block occupied,
|
|
173
|
+
// mirroring how the head side places deletions on the replacing line.
|
|
174
|
+
let replacedOldLine: number | null = null;
|
|
175
|
+
|
|
176
|
+
for (const line of hunk.lines) {
|
|
177
|
+
if (line.kind === "add" && line.newLine !== null) {
|
|
178
|
+
addLineCount(
|
|
179
|
+
countsByFile,
|
|
180
|
+
file,
|
|
181
|
+
side === "base"
|
|
182
|
+
? (replacedOldLine ?? currentOldLine)
|
|
183
|
+
: line.newLine,
|
|
184
|
+
"additions",
|
|
185
|
+
line,
|
|
186
|
+
);
|
|
187
|
+
currentNewLine = line.newLine + 1;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (line.kind === "remove") {
|
|
192
|
+
const oldLine = line.oldLine ?? currentOldLine;
|
|
193
|
+
addLineCount(
|
|
194
|
+
countsByFile,
|
|
195
|
+
file,
|
|
196
|
+
side === "base" ? oldLine : currentNewLine,
|
|
197
|
+
"deletions",
|
|
198
|
+
line,
|
|
199
|
+
);
|
|
200
|
+
replacedOldLine ??= oldLine;
|
|
201
|
+
currentOldLine = oldLine + 1;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
replacedOldLine = null;
|
|
206
|
+
|
|
207
|
+
if (line.newLine !== null) {
|
|
208
|
+
currentNewLine = line.newLine + 1;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (line.oldLine !== null) currentOldLine = line.oldLine + 1;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return countsByFile;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
interface SoftwareMapElementDiffCounts {
|
|
220
|
+
countsByElementPath: SoftwareMapDiffCountsByElementPath;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function mapDiffLineCountsToSoftwareMapElements(input: {
|
|
224
|
+
codeElements: SoftwareMapCodeElementInput[];
|
|
225
|
+
countsByFile: FileLineCounts;
|
|
226
|
+
}): SoftwareMapElementDiffCounts {
|
|
227
|
+
const countsByElementPath: SoftwareMapDiffCountsByElementPath = {};
|
|
228
|
+
|
|
229
|
+
for (const element of input.codeElements) {
|
|
230
|
+
const total: SoftwareMapDiffLineCount = { additions: 0, deletions: 0 };
|
|
231
|
+
const countedLines = new Set<string>();
|
|
232
|
+
|
|
233
|
+
for (const range of element.sourceRanges ?? []) {
|
|
234
|
+
const fileCounts = input.countsByFile.get(range.file);
|
|
235
|
+
|
|
236
|
+
if (!fileCounts) continue;
|
|
237
|
+
|
|
238
|
+
for (const [line, counts] of fileCounts) {
|
|
239
|
+
if (line < range.fromLine || line > range.toLine) continue;
|
|
240
|
+
const lineKey = `${range.file}:${line}`;
|
|
241
|
+
|
|
242
|
+
if (countedLines.has(lineKey)) continue;
|
|
243
|
+
countedLines.add(lineKey);
|
|
244
|
+
total.additions +=
|
|
245
|
+
element.changeStatus === "removed" ? 0 : counts.additions;
|
|
246
|
+
total.deletions +=
|
|
247
|
+
element.changeStatus === "added" ? 0 : counts.deletions;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (total.additions > 0 || total.deletions > 0) {
|
|
252
|
+
countsByElementPath[element.path] = total;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return { countsByElementPath };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function mapDiffLineCountsToCoverageClaims(input: {
|
|
260
|
+
coverageClaims: SoftwareMapCoverageClaimInput[];
|
|
261
|
+
countsByFile: FileLineCounts;
|
|
262
|
+
}) {
|
|
263
|
+
const result: SoftwareMapUnmappedDiffByElementPath = {};
|
|
264
|
+
|
|
265
|
+
const claims = input.coverageClaims
|
|
266
|
+
.filter(
|
|
267
|
+
(claim) =>
|
|
268
|
+
(claim.files?.length ?? 0) > 0 || (claim.globs?.length ?? 0) > 0,
|
|
269
|
+
)
|
|
270
|
+
.sort(compareCoverageClaims);
|
|
271
|
+
|
|
272
|
+
for (const [file, fileCounts] of input.countsByFile) {
|
|
273
|
+
for (const [line, counts] of fileCounts) {
|
|
274
|
+
for (const claim of claims) {
|
|
275
|
+
if (!softwareMapCoverageClaimMatchesLine(claim, file, line)) continue;
|
|
276
|
+
|
|
277
|
+
const summary = (result[claim.path] ??= {
|
|
278
|
+
additions: 0,
|
|
279
|
+
deletions: 0,
|
|
280
|
+
files: [],
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
summary.additions += counts.additions;
|
|
284
|
+
summary.deletions += counts.deletions;
|
|
285
|
+
addUnmappedFileLine(summary, file, line, counts.rows);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function addLineCount(
|
|
294
|
+
countsByFile: FileLineCounts,
|
|
295
|
+
file: string,
|
|
296
|
+
line: number,
|
|
297
|
+
kind: keyof SoftwareMapDiffLineCount,
|
|
298
|
+
diffLine: DiffHunkLine,
|
|
299
|
+
): void {
|
|
300
|
+
const fileCounts = countsByFile.get(file) ?? new Map();
|
|
301
|
+
|
|
302
|
+
const current = fileCounts.get(line) ?? {
|
|
303
|
+
additions: 0,
|
|
304
|
+
deletions: 0,
|
|
305
|
+
rows: [],
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
current[kind] += 1;
|
|
309
|
+
current.rows.push(toUnmappedDiffLine(diffLine));
|
|
310
|
+
fileCounts.set(line, current);
|
|
311
|
+
countsByFile.set(file, fileCounts);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function addUnmappedFileLine(
|
|
315
|
+
summary: SoftwareMapUnmappedDiffSummary,
|
|
316
|
+
file: string,
|
|
317
|
+
line: number,
|
|
318
|
+
rows: SoftwareMapUnmappedDiffLine[],
|
|
319
|
+
): void {
|
|
320
|
+
let fileSummary = summary.files.find((candidate) => candidate.file === file);
|
|
321
|
+
|
|
322
|
+
if (!fileSummary) {
|
|
323
|
+
fileSummary = { file, additions: 0, deletions: 0, hunks: [] };
|
|
324
|
+
summary.files.push(fileSummary);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
for (const row of rows) {
|
|
328
|
+
if (row.kind === "add") fileSummary.additions += 1;
|
|
329
|
+
|
|
330
|
+
if (row.kind === "remove") fileSummary.deletions += 1;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const lastHunk = fileSummary.hunks.at(-1);
|
|
334
|
+
|
|
335
|
+
if (lastHunk && line <= lastHunk.startLine + lastHunk.lines.length + 1) {
|
|
336
|
+
lastHunk.lines.push(...rows);
|
|
337
|
+
} else {
|
|
338
|
+
fileSummary.hunks.push({ startLine: line, lines: [...rows] });
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function toUnmappedDiffLine(line: DiffHunkLine): SoftwareMapUnmappedDiffLine {
|
|
343
|
+
return {
|
|
344
|
+
kind: line.kind === "add" ? "add" : "remove",
|
|
345
|
+
oldLine: line.oldLine,
|
|
346
|
+
newLine: line.newLine,
|
|
347
|
+
text: line.text,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function compareCoverageClaims(
|
|
352
|
+
left: SoftwareMapCoverageClaimInput,
|
|
353
|
+
right: SoftwareMapCoverageClaimInput,
|
|
354
|
+
) {
|
|
355
|
+
return coverageSpecificity(right) - coverageSpecificity(left);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function coverageSpecificity(claim: SoftwareMapCoverageClaimInput) {
|
|
359
|
+
const pathDepth = claim.path.split(".").length * 1000;
|
|
360
|
+
const exactFiles = (claim.files?.length ?? 0) * 20;
|
|
361
|
+
|
|
362
|
+
const globs =
|
|
363
|
+
claim.globs?.reduce((total, glob) => total + glob.length, 0) ?? 0;
|
|
364
|
+
|
|
365
|
+
return pathDepth + exactFiles + globs;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function softwareMapCoverageClaimMatchesLine(
|
|
369
|
+
claim: SoftwareMapCoverageClaimInput,
|
|
370
|
+
file: string,
|
|
371
|
+
line: number,
|
|
372
|
+
) {
|
|
373
|
+
for (const claimedFile of claim.files ?? []) {
|
|
374
|
+
if (claimedFile.path !== file) continue;
|
|
375
|
+
|
|
376
|
+
if (softwareMapLineInRanges(line, claimedFile.ranges)) return true;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return (claim.globs ?? []).some((glob) => softwareMapGlobMatches(glob, file));
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function softwareMapLineInRanges(
|
|
383
|
+
line: number,
|
|
384
|
+
ranges: SoftwareMapLineRangeInput[] = [],
|
|
385
|
+
) {
|
|
386
|
+
if (ranges.length === 0) return true;
|
|
387
|
+
|
|
388
|
+
return ranges.some((range) => line >= range.fromLine && line <= range.toLine);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const softwareMapGlobRegexes = new Map<string, RegExp>();
|
|
392
|
+
|
|
393
|
+
export function softwareMapGlobMatches(pattern: string, value: string) {
|
|
394
|
+
let regex = softwareMapGlobRegexes.get(pattern);
|
|
395
|
+
|
|
396
|
+
if (!regex) {
|
|
397
|
+
regex = new RegExp(`^${globToRegex(pattern)}$`);
|
|
398
|
+
softwareMapGlobRegexes.set(pattern, regex);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return regex.test(value);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function globToRegex(pattern: string) {
|
|
405
|
+
let result = "";
|
|
406
|
+
|
|
407
|
+
for (let index = 0; index < pattern.length; index += 1) {
|
|
408
|
+
const char = pattern[index];
|
|
409
|
+
const next = pattern[index + 1];
|
|
410
|
+
|
|
411
|
+
if (char === "*" && next === "*") {
|
|
412
|
+
result += ".*";
|
|
413
|
+
index += 1;
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (char === "*") {
|
|
418
|
+
result += "[^/]*";
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (char === "?") {
|
|
423
|
+
result += "[^/]";
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
result += escapeRegex(char);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return result;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function escapeRegex(char: string) {
|
|
434
|
+
return /[\\^$.*+?()[\]{}|]/.test(char) ? `\\${char}` : char;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function splitGitDiff(
|
|
438
|
+
diff: string,
|
|
439
|
+
): Array<{ oldFile: string | null; newFile: string | null; patch: string }> {
|
|
440
|
+
const result: Array<{
|
|
441
|
+
oldFile: string | null;
|
|
442
|
+
newFile: string | null;
|
|
443
|
+
patch: string;
|
|
444
|
+
}> = [];
|
|
445
|
+
|
|
446
|
+
let current: {
|
|
447
|
+
oldFile: string | null;
|
|
448
|
+
newFile: string | null;
|
|
449
|
+
lines: string[];
|
|
450
|
+
} | null = null;
|
|
451
|
+
|
|
452
|
+
for (const line of diff.split(/\r?\n/)) {
|
|
453
|
+
if (line.startsWith("diff --git ")) {
|
|
454
|
+
if (current) {
|
|
455
|
+
result.push({
|
|
456
|
+
oldFile: current.oldFile,
|
|
457
|
+
newFile: current.newFile,
|
|
458
|
+
patch: current.lines.join("\n"),
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
current = { oldFile: null, newFile: null, lines: [] };
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (!current) continue;
|
|
467
|
+
const oldFile = parseGitFileLine(line, "--- ");
|
|
468
|
+
const newFile = parseGitFileLine(line, "+++ ");
|
|
469
|
+
|
|
470
|
+
if (oldFile !== undefined) {
|
|
471
|
+
current.oldFile = oldFile;
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (newFile !== undefined) {
|
|
476
|
+
current.newFile = newFile;
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
current.lines.push(line);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (current) {
|
|
484
|
+
result.push({
|
|
485
|
+
oldFile: current.oldFile,
|
|
486
|
+
newFile: current.newFile,
|
|
487
|
+
patch: current.lines.join("\n"),
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return result;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function parseGitFileLine(
|
|
495
|
+
line: string,
|
|
496
|
+
prefix: "--- " | "+++ ",
|
|
497
|
+
): string | null | undefined {
|
|
498
|
+
if (!line.startsWith(prefix)) return undefined;
|
|
499
|
+
const raw = unquoteGitPath(line.slice(prefix.length).trim());
|
|
500
|
+
|
|
501
|
+
if (raw === "/dev/null") return null;
|
|
502
|
+
|
|
503
|
+
if (raw.startsWith("a/") || raw.startsWith("b/")) return raw.slice(2);
|
|
504
|
+
|
|
505
|
+
return raw;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function unquoteGitPath(value: string): string {
|
|
509
|
+
if (!value.startsWith(`"`)) return value;
|
|
510
|
+
const unquoted = value.slice(1, value.endsWith(`"`) ? -1 : undefined);
|
|
511
|
+
|
|
512
|
+
try {
|
|
513
|
+
return jsonString(parseJsonText(value)) ?? unquoted;
|
|
514
|
+
} catch {
|
|
515
|
+
return unquoted;
|
|
516
|
+
}
|
|
517
|
+
}
|