brave-mcp 1.3.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -5
- package/build/src/HeapSnapshotManager.js +83 -16
- package/build/src/McpContext.js +218 -367
- package/build/src/McpPage.js +242 -31
- package/build/src/McpResponse.js +165 -60
- package/build/src/PageCollector.js +48 -122
- package/build/src/TextSnapshot.js +20 -2
- package/build/src/ToolHandler.js +13 -3
- package/build/src/WaitForHelper.js +18 -8
- package/build/src/bin/brave-devtools-cli-options.js +76 -4
- package/build/src/bin/brave-devtools-mcp-cli-options.js +17 -8
- package/build/src/bin/brave-devtools-mcp-main.js +2 -2
- package/build/src/bin/brave-devtools.js +25 -1
- package/build/src/bin/check-latest-version.js +1 -0
- package/build/src/browser.js +1 -1
- package/build/src/daemon/client.js +33 -3
- package/build/src/daemon/daemon.js +3 -2
- package/build/src/daemon/utils.js +1 -1
- package/build/src/devtools/DevtoolsUtils.js +2 -63
- package/build/src/{issue-descriptions.js → devtools/issueDescriptions.js} +8 -6
- package/build/src/formatters/HeapSnapshotFormatter.js +43 -0
- package/build/src/formatters/IssueFormatter.js +2 -2
- package/build/src/formatters/NetworkFormatter.js +3 -1
- package/build/src/index.js +14 -5
- package/build/src/telemetry/ClearcutLogger.js +1 -1
- package/build/src/telemetry/WatchdogClient.js +1 -1
- package/build/src/telemetry/metricsRegistry.js +2 -9
- package/build/src/telemetry/persistence.js +1 -1
- package/build/src/telemetry/transformation.js +20 -0
- package/build/src/telemetry/watchdog/ClearcutSender.js +1 -1
- package/build/src/telemetry/watchdog/main.js +1 -1
- package/build/src/third_party/THIRD_PARTY_NOTICES +14 -280
- package/build/src/third_party/bundled-packages.json +4 -6
- package/build/src/third_party/devtools-heap-snapshot-worker.js +94 -0
- package/build/src/third_party/index.js +3918 -2405
- package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +3082 -6343
- package/build/src/tools/emulation.js +2 -2
- package/build/src/tools/extensions.js +1 -1
- package/build/src/tools/input.js +17 -8
- package/build/src/tools/lighthouse.js +1 -1
- package/build/src/tools/memory.js +76 -6
- package/build/src/tools/network.js +4 -4
- package/build/src/tools/pages.js +21 -89
- package/build/src/tools/performance.js +2 -2
- package/build/src/tools/screencast.js +1 -2
- package/build/src/tools/script.js +6 -10
- package/build/src/tools/snapshot.js +2 -2
- package/build/src/tools/thirdPartyDeveloper.js +7 -7
- package/build/src/tools/webmcp.js +1 -1
- package/build/src/trace-processing/parse.js +1 -1
- package/build/src/utils/check-for-updates.js +1 -0
- package/build/src/utils/files.js +0 -4
- package/build/src/{logger.js → utils/logger.js} +1 -1
- package/build/src/{polyfill.js → utils/polyfill.js} +1 -1
- package/build/src/version.js +1 -1
- package/package.json +21 -8
- package/skills/a11y-debugging/SKILL.md +89 -0
- package/skills/a11y-debugging/references/a11y-snippets.md +92 -0
- package/skills/chrome-devtools/SKILL.md +72 -0
- package/skills/chrome-devtools-cli/SKILL.md +153 -0
- package/skills/chrome-devtools-cli/references/installation.md +14 -0
- package/skills/debug-optimize-lcp/SKILL.md +121 -0
- package/skills/debug-optimize-lcp/references/elements-and-size.md +27 -0
- package/skills/debug-optimize-lcp/references/lcp-breakdown.md +23 -0
- package/skills/debug-optimize-lcp/references/lcp-snippets.md +79 -0
- package/skills/debug-optimize-lcp/references/optimization-strategies.md +38 -0
- package/skills/memory-leak-debugging/SKILL.md +58 -0
- package/skills/memory-leak-debugging/references/common-leaks.md +33 -0
- package/skills/memory-leak-debugging/references/compare_snapshots.js +109 -0
- package/skills/memory-leak-debugging/references/memlab.md +29 -0
- package/skills/troubleshooting/SKILL.md +98 -0
- package/build/src/utils/types.js +0 -7
- /package/build/src/{Mutex.js → utils/Mutex.js} +0 -0
package/README.md
CHANGED
|
@@ -239,9 +239,9 @@ To use the Chrome DevTools MCP server follow the instructions from <a href="http
|
|
|
239
239
|
"chrome-devtools": {
|
|
240
240
|
"command": "npx",
|
|
241
241
|
"args": [
|
|
242
|
+
"-y",
|
|
242
243
|
"chrome-devtools-mcp@latest",
|
|
243
|
-
"--browser-url=http://127.0.0.1:9222"
|
|
244
|
-
"-y"
|
|
244
|
+
"--browser-url=http://127.0.0.1:9222"
|
|
245
245
|
]
|
|
246
246
|
}
|
|
247
247
|
}
|
|
@@ -451,6 +451,16 @@ Alternatively, follow the <a href="https://github.com/google-gemini/gemini-cli/b
|
|
|
451
451
|
using the standard config from above.
|
|
452
452
|
</details>
|
|
453
453
|
|
|
454
|
+
<details>
|
|
455
|
+
<summary>Grok Build CLI</summary>
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
grok mcp add chrome-devtools npx chrome-devtools-mcp@latest
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
See the <a href="https://docs.x.ai/build/features/skills-plugins-marketplaces">docs</a> for more options
|
|
462
|
+
</details>
|
|
463
|
+
|
|
454
464
|
<details>
|
|
455
465
|
<summary>JetBrains AI Assistant & Junie</summary>
|
|
456
466
|
|
|
@@ -632,12 +642,14 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles
|
|
|
632
642
|
- [`take_snapshot`](docs/tool-reference.md#take_snapshot)
|
|
633
643
|
- [`screencast_start`](docs/tool-reference.md#screencast_start)
|
|
634
644
|
- [`screencast_stop`](docs/tool-reference.md#screencast_stop)
|
|
635
|
-
- **Memory** (
|
|
645
|
+
- **Memory** (11 tools)
|
|
636
646
|
- [`take_heapsnapshot`](docs/tool-reference.md#take_heapsnapshot)
|
|
637
647
|
- [`close_heapsnapshot`](docs/tool-reference.md#close_heapsnapshot)
|
|
648
|
+
- [`compare_heapsnapshots`](docs/tool-reference.md#compare_heapsnapshots)
|
|
638
649
|
- [`get_heapsnapshot_class_nodes`](docs/tool-reference.md#get_heapsnapshot_class_nodes)
|
|
639
650
|
- [`get_heapsnapshot_details`](docs/tool-reference.md#get_heapsnapshot_details)
|
|
640
651
|
- [`get_heapsnapshot_dominators`](docs/tool-reference.md#get_heapsnapshot_dominators)
|
|
652
|
+
- [`get_heapsnapshot_duplicate_strings`](docs/tool-reference.md#get_heapsnapshot_duplicate_strings)
|
|
641
653
|
- [`get_heapsnapshot_edges`](docs/tool-reference.md#get_heapsnapshot_edges)
|
|
642
654
|
- [`get_heapsnapshot_retainers`](docs/tool-reference.md#get_heapsnapshot_retainers)
|
|
643
655
|
- [`get_heapsnapshot_retaining_paths`](docs/tool-reference.md#get_heapsnapshot_retaining_paths)
|
|
@@ -671,14 +683,17 @@ The Brave DevTools MCP server supports the following configuration options:
|
|
|
671
683
|
- **`--browserUrl`/ `--browser-url`, `-u`**
|
|
672
684
|
Connect to a running, debuggable Brave instance (e.g. `http://127.0.0.1:9222`).
|
|
673
685
|
- **Type:** string
|
|
686
|
+
- **Default:** `false`
|
|
674
687
|
|
|
675
688
|
- **`--wsEndpoint`/ `--ws-endpoint`, `-w`**
|
|
676
689
|
WebSocket endpoint to connect to a running Brave instance (e.g., ws://127.0.0.1:9222/devtools/browser/<id>). Alternative to --browserUrl.
|
|
677
690
|
- **Type:** string
|
|
691
|
+
- **Default:** `false`
|
|
678
692
|
|
|
679
693
|
- **`--wsHeaders`/ `--ws-headers`**
|
|
680
694
|
Custom headers for WebSocket connection in JSON format (e.g., '{"Authorization":"Bearer token"}'). Only works with --wsEndpoint.
|
|
681
695
|
- **Type:** string
|
|
696
|
+
- **Default:** `false`
|
|
682
697
|
|
|
683
698
|
- **`--headless`**
|
|
684
699
|
Whether to run in headless (no UI) mode.
|
|
@@ -688,87 +703,108 @@ The Brave DevTools MCP server supports the following configuration options:
|
|
|
688
703
|
- **`--executablePath`/ `--executable-path`, `-e`**
|
|
689
704
|
Path to custom Brave executable. Can also be set via BRAVE_PATH environment variable.
|
|
690
705
|
- **Type:** string
|
|
706
|
+
- **Default:** `false`
|
|
691
707
|
|
|
692
708
|
- **`--isolated`**
|
|
693
709
|
If specified, creates a temporary user-data-dir that is automatically cleaned up after the browser is closed. Defaults to false.
|
|
694
710
|
- **Type:** boolean
|
|
711
|
+
- **Default:** `false`
|
|
695
712
|
|
|
696
713
|
- **`--userDataDir`/ `--user-data-dir`**
|
|
697
714
|
Path to the user data directory for Brave. Default is $HOME/.cache/brave-devtools-mcp/brave-profile$CHANNEL_SUFFIX_IF_NON_RELEASE
|
|
698
715
|
- **Type:** string
|
|
716
|
+
- **Default:** `false`
|
|
699
717
|
|
|
700
718
|
- **`--channel`**
|
|
701
719
|
Specify a different Brave channel that should be used. The default is the release channel.
|
|
702
720
|
- **Type:** string
|
|
703
721
|
- **Choices:** `release`, `beta`, `nightly`, `dev`
|
|
722
|
+
- **Default:** `false`
|
|
704
723
|
|
|
705
724
|
- **`--logFile`/ `--log-file`**
|
|
706
725
|
Path to a file to write debug logs to. Set the env variable `DEBUG` to `*` to enable verbose logs. Useful for submitting bug reports.
|
|
707
726
|
- **Type:** string
|
|
727
|
+
- **Default:** `false`
|
|
708
728
|
|
|
709
729
|
- **`--viewport`**
|
|
710
730
|
Initial viewport size for the Brave instances started by the server. For example, `1280x720`. In headless mode, max size is 3840x2160px.
|
|
711
731
|
- **Type:** string
|
|
732
|
+
- **Default:** `false`
|
|
712
733
|
|
|
713
734
|
- **`--proxyServer`/ `--proxy-server`**
|
|
714
735
|
Proxy server configuration for Brave passed as --proxy-server when launching the browser. See https://www.chromium.org/developers/design-documents/network-settings/ for details.
|
|
715
736
|
- **Type:** string
|
|
737
|
+
- **Default:** `false`
|
|
716
738
|
|
|
717
739
|
- **`--acceptInsecureCerts`/ `--accept-insecure-certs`**
|
|
718
740
|
If enabled, ignores errors relative to self-signed and expired certificates. Use with caution.
|
|
719
741
|
- **Type:** boolean
|
|
742
|
+
- **Default:** `false`
|
|
720
743
|
|
|
721
744
|
- **`--experimentalPageIdRouting`/ `--experimental-page-id-routing`**
|
|
722
745
|
Whether to expose pageId on page-scoped tools and route requests by page ID (useful for concurrent agent sessions).
|
|
723
746
|
- **Type:** boolean
|
|
747
|
+
- **Default:** `false`
|
|
724
748
|
|
|
725
749
|
- **`--experimentalDevtools`/ `--experimental-devtools`**
|
|
726
750
|
Whether to enable automation over DevTools targets
|
|
727
751
|
- **Type:** boolean
|
|
752
|
+
- **Default:** `false`
|
|
728
753
|
|
|
729
754
|
- **`--experimentalVision`/ `--experimental-vision`**
|
|
730
755
|
Whether to enable coordinate-based tools such as click_at(x,y). Usually requires a computer-use model able to produce accurate coordinates by looking at screenshots.
|
|
731
756
|
- **Type:** boolean
|
|
757
|
+
- **Default:** `false`
|
|
732
758
|
|
|
733
759
|
- **`--memoryDebugging`/ `--memory-debugging`, `-experimentalMemory`**
|
|
734
760
|
Whether to enable memory debugging tools.
|
|
735
761
|
- **Type:** boolean
|
|
762
|
+
- **Default:** `false`
|
|
736
763
|
|
|
737
764
|
- **`--experimentalStructuredContent`/ `--experimental-structured-content`**
|
|
738
765
|
Whether to output structured formatted content.
|
|
739
766
|
- **Type:** boolean
|
|
767
|
+
- **Default:** `false`
|
|
740
768
|
|
|
741
769
|
- **`--experimentalIncludeAllPages`/ `--experimental-include-all-pages`**
|
|
742
770
|
Whether to include all kinds of pages such as webviews or background pages as pages.
|
|
743
771
|
- **Type:** boolean
|
|
772
|
+
- **Default:** `false`
|
|
744
773
|
|
|
745
774
|
- **`--experimentalScreencast`/ `--experimental-screencast`**
|
|
746
775
|
Exposes experimental screencast tools (requires ffmpeg). Install ffmpeg https://www.ffmpeg.org/download.html and ensure it is available in the MCP server PATH.
|
|
747
776
|
- **Type:** boolean
|
|
777
|
+
- **Default:** `false`
|
|
748
778
|
|
|
749
779
|
- **`--experimentalFfmpegPath`/ `--experimental-ffmpeg-path`**
|
|
750
780
|
Path to ffmpeg executable for screencast recording.
|
|
751
781
|
- **Type:** string
|
|
782
|
+
- **Default:** `false`
|
|
752
783
|
|
|
753
784
|
- **`--categoryExperimentalWebmcp`/ `--category-experimental-webmcp`**
|
|
754
785
|
Set to true to enable debugging WebMCP tools. Requires Brave with the following flags: `--enable-features=WebMCP,DevToolsWebMCPSupport`
|
|
755
786
|
- **Type:** boolean
|
|
787
|
+
- **Default:** `false`
|
|
756
788
|
|
|
757
789
|
- **`--braveArg`/ `--brave-arg`**
|
|
758
790
|
Additional arguments for Brave. Only applies when Brave is launched by brave-devtools-mcp.
|
|
759
791
|
- **Type:** array
|
|
792
|
+
- **Default:** `false`
|
|
760
793
|
|
|
761
794
|
- **`--blockedUrlPattern`/ `--blocked-url-pattern`**
|
|
762
|
-
Restricts network access by blocking specified URL patterns (uses https://urlpattern.spec.whatwg.org/). Silently detaches from targets with blocked URLs upon connection, and blocks runtime requests (including navigations and subresources). Accepts an array of patterns.
|
|
795
|
+
Restricts browser's network access by blocking specified URL patterns (uses https://urlpattern.spec.whatwg.org/). Silently detaches from targets with blocked URLs upon connection, and blocks runtime requests (including navigations and subresources). Accepts an array of patterns.
|
|
763
796
|
- **Type:** array
|
|
797
|
+
- **Default:** `false`
|
|
764
798
|
|
|
765
799
|
- **`--allowedUrlPattern`/ `--allowed-url-pattern`**
|
|
766
|
-
Restricts network access by allowing only specified URL patterns (uses https://urlpattern.spec.whatwg.org/). Requires Chrome 149+. Silently detaches from targets with unallowed URLs upon connection, and blocks runtime requests (including navigations and subresources). Accepts an array of patterns.
|
|
800
|
+
Restricts browser's network access by allowing only specified URL patterns (uses https://urlpattern.spec.whatwg.org/). Requires Chrome 149+. Silently detaches from targets with unallowed URLs upon connection, and blocks runtime requests (including navigations and subresources). Accepts an array of patterns.
|
|
767
801
|
- **Type:** array
|
|
802
|
+
- **Default:** `false`
|
|
768
803
|
|
|
769
804
|
- **`--ignoreDefaultBraveArg`/ `--ignore-default-brave-arg`**
|
|
770
805
|
Explicitly disable default arguments for Brave. Only applies when Brave is launched by brave-devtools-mcp.
|
|
771
806
|
- **Type:** array
|
|
807
|
+
- **Default:** `false`
|
|
772
808
|
|
|
773
809
|
- **`--categoryEmulation`/ `--category-emulation`**
|
|
774
810
|
Set to false to exclude tools related to emulation.
|
|
@@ -809,28 +845,38 @@ The Brave DevTools MCP server supports the following configuration options:
|
|
|
809
845
|
Override the default output format used by take_screenshot when the caller does not specify one. JPEG and WebP are ~3-5x smaller than PNG, which helps reduce context size in AI conversations. Unset preserves the existing default ("png").
|
|
810
846
|
- **Type:** string
|
|
811
847
|
- **Choices:** `jpeg`, `png`, `webp`
|
|
848
|
+
- **Default:** `false`
|
|
812
849
|
|
|
813
850
|
- **`--screenshotQuality`/ `--screenshot-quality`**
|
|
814
851
|
Override the default compression quality (0-100) used by take_screenshot for JPEG and WebP when the caller does not specify one. Lower values mean smaller files. Ignored for PNG. Unset preserves the Puppeteer default.
|
|
815
852
|
- **Type:** number
|
|
853
|
+
- **Default:** `false`
|
|
816
854
|
|
|
817
855
|
- **`--screenshotMaxWidth`/ `--screenshot-max-width`**
|
|
818
856
|
Maximum width in pixels for screenshots. If the captured image is wider, it is downscaled (preserving aspect ratio) before being returned. Reduces context size in AI conversations. Unset means no resize.
|
|
819
857
|
- **Type:** number
|
|
858
|
+
- **Default:** `false`
|
|
820
859
|
|
|
821
860
|
- **`--screenshotMaxHeight`/ `--screenshot-max-height`**
|
|
822
861
|
Maximum height in pixels for screenshots. If the captured image is taller, it is downscaled (preserving aspect ratio) before being returned. Can be combined with --screenshot-max-width; the smaller scale factor wins. Unset means no resize.
|
|
823
862
|
- **Type:** number
|
|
863
|
+
- **Default:** `false`
|
|
824
864
|
|
|
825
865
|
- **`--slim`**
|
|
826
866
|
Exposes a "slim" set of 3 tools covering navigation, script execution and screenshots only. Useful for basic browser tasks.
|
|
827
867
|
- **Type:** boolean
|
|
868
|
+
- **Default:** `false`
|
|
828
869
|
|
|
829
870
|
- **`--redactNetworkHeaders`/ `--redact-network-headers`**
|
|
830
871
|
If true, redacts some of the network headers considered sensitive before returning to the client.
|
|
831
872
|
- **Type:** boolean
|
|
832
873
|
- **Default:** `false`
|
|
833
874
|
|
|
875
|
+
- **`--allowUnrestrictedPaths`/ `--allow-unrestricted-paths`**
|
|
876
|
+
If set, disables the default path restriction that applies when the MCP client does not negotiate the roots capability. By default, file-writing tools are restricted to the OS temp directory when no roots are configured. Use this only when connecting a trusted local client that does not implement MCP roots and requires access to paths outside the temp directory.
|
|
877
|
+
- **Type:** boolean
|
|
878
|
+
- **Default:** `false`
|
|
879
|
+
|
|
834
880
|
<!-- END AUTO GENERATED OPTIONS -->
|
|
835
881
|
|
|
836
882
|
Pass them via the `args` property in the JSON configuration. For example:
|
|
@@ -930,3 +976,9 @@ Please consult [these instructions](./docs/debugging-android.md) (originally wri
|
|
|
930
976
|
## Known limitations
|
|
931
977
|
|
|
932
978
|
See [Troubleshooting](./docs/troubleshooting.md).
|
|
979
|
+
|
|
980
|
+
## Integrating as a browser subagent
|
|
981
|
+
|
|
982
|
+
If you are developing agentic tooling and want to provide an integrated browser subagent as part of your product, we recommend building on top of Chrome DevTools for agents.
|
|
983
|
+
|
|
984
|
+
For a reference implementation, see the [Gemini CLI browser agent documentation](https://geminicli.com/docs/core/subagents/#browser-agent).
|
|
@@ -8,6 +8,7 @@ import path from 'node:path';
|
|
|
8
8
|
import { DevTools } from './third_party/index.js';
|
|
9
9
|
import { createIdGenerator, stableIdSymbol, } from './utils/id.js';
|
|
10
10
|
export class HeapSnapshotManager {
|
|
11
|
+
#snapshotIdGenerator = createIdGenerator();
|
|
11
12
|
#snapshots = new Map();
|
|
12
13
|
async getSnapshot(filePath) {
|
|
13
14
|
const absolutePath = path.resolve(filePath);
|
|
@@ -15,28 +16,36 @@ export class HeapSnapshotManager {
|
|
|
15
16
|
if (cached) {
|
|
16
17
|
return cached.snapshot;
|
|
17
18
|
}
|
|
18
|
-
const
|
|
19
|
+
const uid = this.#snapshotIdGenerator();
|
|
20
|
+
const { snapshot, worker } = await this.#loadSnapshot(absolutePath, uid);
|
|
19
21
|
this.#snapshots.set(absolutePath, {
|
|
20
22
|
snapshot,
|
|
21
23
|
worker,
|
|
22
|
-
idToClassKey:
|
|
24
|
+
idToClassKey: [''],
|
|
23
25
|
classKeyToId: new Map(),
|
|
24
|
-
idGenerator: createIdGenerator(),
|
|
25
26
|
});
|
|
26
27
|
return snapshot;
|
|
27
28
|
}
|
|
28
|
-
async getAggregates(filePath) {
|
|
29
|
+
async getAggregates(filePath, filterName) {
|
|
29
30
|
const snapshot = await this.getSnapshot(filePath);
|
|
30
31
|
const filter = new DevTools.HeapSnapshotModel.HeapSnapshotModel.NodeFilter();
|
|
32
|
+
if (filterName) {
|
|
33
|
+
filter.filterName = filterName;
|
|
34
|
+
}
|
|
31
35
|
const aggregates = await snapshot.aggregatesWithFilter(filter);
|
|
32
|
-
|
|
36
|
+
let objectCount = 0;
|
|
37
|
+
let totalSelfSize = 0;
|
|
38
|
+
for (const [key, aggregate] of Object.entries(aggregates)) {
|
|
33
39
|
const id = await this.getOrCreateIdForClassKey(filePath, key);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
40
|
+
aggregate[stableIdSymbol] = id;
|
|
41
|
+
objectCount += aggregate.count;
|
|
42
|
+
totalSelfSize += aggregate.self;
|
|
38
43
|
}
|
|
39
|
-
return
|
|
44
|
+
return {
|
|
45
|
+
aggregates,
|
|
46
|
+
objectCount,
|
|
47
|
+
totalSelfSize,
|
|
48
|
+
};
|
|
40
49
|
}
|
|
41
50
|
async getStats(filePath) {
|
|
42
51
|
const snapshot = await this.getSnapshot(filePath);
|
|
@@ -50,15 +59,18 @@ export class HeapSnapshotManager {
|
|
|
50
59
|
const cached = this.#getCachedSnapshot(filePath);
|
|
51
60
|
let id = cached.classKeyToId.get(classKey);
|
|
52
61
|
if (!id) {
|
|
53
|
-
id = cached.
|
|
62
|
+
id = cached.idToClassKey.length;
|
|
54
63
|
cached.classKeyToId.set(classKey, id);
|
|
55
|
-
cached.idToClassKey.
|
|
64
|
+
cached.idToClassKey.push(classKey);
|
|
56
65
|
}
|
|
57
66
|
return id;
|
|
58
67
|
}
|
|
59
|
-
async getNodesById(filePath, id) {
|
|
68
|
+
async getNodesById(filePath, id, filterName) {
|
|
60
69
|
const snapshot = await this.getSnapshot(filePath);
|
|
61
70
|
const filter = new DevTools.HeapSnapshotModel.HeapSnapshotModel.NodeFilter();
|
|
71
|
+
if (filterName) {
|
|
72
|
+
filter.filterName = filterName;
|
|
73
|
+
}
|
|
62
74
|
const className = await this.resolveClassKeyFromId(filePath, id);
|
|
63
75
|
if (!className) {
|
|
64
76
|
throw new Error(`Class with ID ${id} not found in heap snapshot`);
|
|
@@ -100,6 +112,38 @@ export class HeapSnapshotManager {
|
|
|
100
112
|
const provider = snapshot.createEdgesProvider(nodeIndex);
|
|
101
113
|
return await provider.serializeItemsRange(0, Infinity);
|
|
102
114
|
}
|
|
115
|
+
async getClassDiffs(baseFilePath, currentFilePath) {
|
|
116
|
+
const rawDiffs = await this.#getSortedRawClassDiffs(baseFilePath, currentFilePath);
|
|
117
|
+
return rawDiffs.map(rawDiff => ({
|
|
118
|
+
className: rawDiff.name,
|
|
119
|
+
addedCount: rawDiff.addedCount,
|
|
120
|
+
removedCount: rawDiff.removedCount,
|
|
121
|
+
countDelta: rawDiff.countDelta,
|
|
122
|
+
addedSize: rawDiff.addedSize,
|
|
123
|
+
removedSize: rawDiff.removedSize,
|
|
124
|
+
sizeDelta: rawDiff.sizeDelta,
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
async getDetailedClassDiff(baseFilePath, currentFilePath, classIndex) {
|
|
128
|
+
const classDiffs = await this.#getSortedRawClassDiffs(baseFilePath, currentFilePath);
|
|
129
|
+
const rawDiff = classDiffs[classIndex];
|
|
130
|
+
if (!rawDiff) {
|
|
131
|
+
throw new Error(`Invalid classIndex: ${classIndex}. Total classes with changes: ${classDiffs.length}`);
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
className: rawDiff.name,
|
|
135
|
+
addedCount: rawDiff.addedCount,
|
|
136
|
+
removedCount: rawDiff.removedCount,
|
|
137
|
+
countDelta: rawDiff.countDelta,
|
|
138
|
+
addedSize: rawDiff.addedSize,
|
|
139
|
+
removedSize: rawDiff.removedSize,
|
|
140
|
+
sizeDelta: rawDiff.sizeDelta,
|
|
141
|
+
addedIds: rawDiff.addedIds ?? [],
|
|
142
|
+
addedSelfSizes: rawDiff.addedSelfSizes ?? [],
|
|
143
|
+
deletedIds: rawDiff.deletedIds ?? [],
|
|
144
|
+
deletedSelfSizes: rawDiff.deletedSelfSizes ?? [],
|
|
145
|
+
};
|
|
146
|
+
}
|
|
103
147
|
#getCachedSnapshot(filePath) {
|
|
104
148
|
const absolutePath = path.resolve(filePath);
|
|
105
149
|
const cached = this.#snapshots.get(absolutePath);
|
|
@@ -108,16 +152,35 @@ export class HeapSnapshotManager {
|
|
|
108
152
|
}
|
|
109
153
|
return cached;
|
|
110
154
|
}
|
|
155
|
+
async #getSortedRawClassDiffs(baseFilePath, currentFilePath) {
|
|
156
|
+
const baseSnapshot = await this.getSnapshot(baseFilePath);
|
|
157
|
+
const currentSnapshot = await this.getSnapshot(currentFilePath);
|
|
158
|
+
const interfaceDefinitions = await currentSnapshot.interfaceDefinitions();
|
|
159
|
+
const aggregatesForDiff = await baseSnapshot.aggregatesForDiff(interfaceDefinitions);
|
|
160
|
+
const baseSnapshotId = baseSnapshot.uid;
|
|
161
|
+
if (baseSnapshotId === undefined) {
|
|
162
|
+
throw new Error('Base snapshot UID is undefined');
|
|
163
|
+
}
|
|
164
|
+
// DevTools calculateSnapshotDiff uses the first parameter (baseSnapshotId)
|
|
165
|
+
// as a cache key. We pass the unique UID of the base snapshot.
|
|
166
|
+
const rawDiffs = await currentSnapshot.calculateSnapshotDiff(baseSnapshotId, aggregatesForDiff);
|
|
167
|
+
// Return a filtered and sorted array here to ensure that
|
|
168
|
+
// compare_heapsnapshot_summary and compare_heapsnapshot_details agree
|
|
169
|
+
// on indices.
|
|
170
|
+
return Object.values(rawDiffs)
|
|
171
|
+
.filter(diff => diff.addedCount > 0 || diff.removedCount > 0)
|
|
172
|
+
.sort((a, b) => b.sizeDelta - a.sizeDelta);
|
|
173
|
+
}
|
|
111
174
|
async resolveClassKeyFromId(filePath, id) {
|
|
112
175
|
const cached = this.#getCachedSnapshot(filePath);
|
|
113
|
-
return cached.idToClassKey
|
|
176
|
+
return cached.idToClassKey[id];
|
|
114
177
|
}
|
|
115
|
-
async #loadSnapshot(absolutePath) {
|
|
178
|
+
async #loadSnapshot(absolutePath, uid) {
|
|
116
179
|
const workerProxy = new DevTools.HeapSnapshotModel.HeapSnapshotProxy.HeapSnapshotWorkerProxy(() => {
|
|
117
180
|
/* noop */
|
|
118
181
|
}, import.meta.resolve('./third_party/devtools-heap-snapshot-worker.js'));
|
|
119
182
|
const { promise: snapshotPromise, resolve: resolveSnapshot } = Promise.withResolvers();
|
|
120
|
-
const loaderProxy = workerProxy.createLoader(
|
|
183
|
+
const loaderProxy = workerProxy.createLoader(uid, snapshotProxy => {
|
|
121
184
|
resolveSnapshot(snapshotProxy);
|
|
122
185
|
});
|
|
123
186
|
const fileStream = fsSync.createReadStream(absolutePath, {
|
|
@@ -131,6 +194,10 @@ export class HeapSnapshotManager {
|
|
|
131
194
|
const snapshot = await snapshotPromise;
|
|
132
195
|
return { snapshot, worker: workerProxy };
|
|
133
196
|
}
|
|
197
|
+
async getDuplicateStrings(filePath) {
|
|
198
|
+
const snapshot = await this.getSnapshot(filePath);
|
|
199
|
+
return await snapshot.getDuplicateStrings();
|
|
200
|
+
}
|
|
134
201
|
hasSnapshots() {
|
|
135
202
|
return this.#snapshots.size > 0;
|
|
136
203
|
}
|