chrome-devtools-mcp 0.5.1 → 0.6.1

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.
Files changed (76) hide show
  1. package/README.md +60 -5
  2. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Color.js +13 -9
  3. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ColorConverter.js +9 -7
  4. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Gzip.js +1 -1
  5. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/MapWithDefault.js +5 -3
  6. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ResourceType.js +0 -11
  7. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ReturnToPanel.js +6 -4
  8. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/AidaClient.js +1 -1
  9. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/GdpClient.js +116 -59
  10. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHost.js +3 -0
  11. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/Platform.js +5 -3
  12. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +6 -4
  13. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/ArrayUtilities.js +1 -1
  14. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/StringUtilities.js +33 -31
  15. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMetadata.js +4 -2
  16. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSModel.js +6 -0
  17. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParser.js +11 -9
  18. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParserMatchers.js +19 -13
  19. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSRule.js +4 -0
  20. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSStartingStyle.js +21 -0
  21. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ChildTargetManager.js +30 -0
  22. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +20 -2
  23. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/EventBreakpointsModel.js +4 -2
  24. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/HttpReasonPhraseStrings.js +4 -2
  25. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +9 -41
  26. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkRequest.js +0 -14
  27. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/PageResourceLoader.js +1 -1
  28. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/PreloadingModel.js +7 -5
  29. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +6 -2
  30. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RemoteObject.js +1 -1
  31. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ResourceTreeModel.js +1 -0
  32. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ScreenCaptureModel.js +20 -18
  33. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Target.js +7 -1
  34. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/TraceObject.js +2 -2
  35. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/sdk.js +2 -1
  36. package/build/node_modules/chrome-devtools-frontend/front_end/generated/Deprecation.js +4 -4
  37. package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +5 -4
  38. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.js +30 -3
  39. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIContext.js +18 -4
  40. package/build/node_modules/chrome-devtools-frontend/front_end/models/crux-manager/CrUXManager.js +1 -1
  41. package/build/node_modules/chrome-devtools-frontend/front_end/models/network_time_calculator/RequestTimeRanges.js +6 -4
  42. package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/NamesResolver.js +7 -5
  43. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/LanternComputationData.js +1 -0
  44. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/TraceTree.js +1 -1
  45. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/FramesHandler.js +7 -5
  46. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/LayoutShiftsHandler.js +8 -4
  47. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/NetworkRequestsHandler.js +17 -0
  48. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/ScriptsHandler.js +2 -1
  49. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/helpers.js +1 -1
  50. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Timing.js +4 -2
  51. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Trace.js +8 -4
  52. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DocumentLatency.js +10 -10
  53. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/INPBreakdown.js +12 -1
  54. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPBreakdown.js +11 -1
  55. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LegacyJavaScript.js +2 -1
  56. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/NetworkDependencyTree.js +2 -2
  57. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js +5 -3
  58. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace_source_maps_resolver/SourceMapsResolver.js +1 -1
  59. package/build/src/McpContext.js +28 -4
  60. package/build/src/McpResponse.js +25 -16
  61. package/build/src/Mutex.js +3 -6
  62. package/build/src/PageCollector.js +17 -6
  63. package/build/src/browser.js +21 -12
  64. package/build/src/cli.js +38 -1
  65. package/build/src/formatters/consoleFormatter.js +1 -1
  66. package/build/src/logger.js +2 -2
  67. package/build/src/main.js +18 -10
  68. package/build/src/tools/ToolDefinition.js +11 -0
  69. package/build/src/tools/input.js +1 -1
  70. package/build/src/tools/network.js +0 -1
  71. package/build/src/tools/pages.js +15 -5
  72. package/build/src/tools/performance.js +3 -3
  73. package/build/src/tools/screenshot.js +11 -3
  74. package/build/src/tools/snapshot.js +11 -5
  75. package/build/src/trace-processing/parse.js +1 -1
  76. package/package.json +5 -5
package/README.md CHANGED
@@ -7,6 +7,8 @@ control and inspect a live Chrome browser. It acts as a Model-Context-Protocol
7
7
  (MCP) server, giving your AI coding assistant access to the full power of
8
8
  Chrome DevTools for reliable automation, in-depth debugging, and performance analysis.
9
9
 
10
+ ## [Tool reference](./docs/tool-reference.md) | [Changelog](./CHANGELOG.md) | [Contributing](./CONTRIBUTING.md) | [Troubleshooting](./docs/troubleshooting.md)
11
+
10
12
  ## Key features
11
13
 
12
14
  - **Get performance insights**: Uses [Chrome
@@ -27,7 +29,7 @@ MCP clients.
27
29
 
28
30
  ## Requirements
29
31
 
30
- - [Node.js 20](https://nodejs.org/) or a newer [latest maintainance LTS](https://github.com/nodejs/Release#release-schedule) version.
32
+ - [Node.js](https://nodejs.org/) v20.19 or a newer [latest maintenance LTS](https://github.com/nodejs/Release#release-schedule) version.
31
33
  - [Chrome](https://www.google.com/chrome/) current stable version or newer.
32
34
  - [npm](https://www.npmjs.com/).
33
35
 
@@ -40,7 +42,7 @@ Add the following config to your MCP client:
40
42
  "mcpServers": {
41
43
  "chrome-devtools": {
42
44
  "command": "npx",
43
- "args": ["chrome-devtools-mcp@latest"]
45
+ "args": ["-y", "chrome-devtools-mcp@latest"]
44
46
  }
45
47
  }
46
48
  }
@@ -94,6 +96,30 @@ startup_timeout_ms = 20_000
94
96
 
95
97
  </details>
96
98
 
99
+ <details>
100
+ <summary>Copilot CLI</summary>
101
+
102
+ Start Copilot CLI:
103
+
104
+ ```
105
+ copilot
106
+ ```
107
+
108
+ Start the dialog to add a new MCP server by running:
109
+
110
+ ```
111
+ /mcp add
112
+ ```
113
+
114
+ Configure the following fields and press `CTR-S` to save the configuration:
115
+
116
+ - **Server name:** `chrome-devtools`
117
+ - **Server Type:** `[1] Local`
118
+ - **Command:** `npx`
119
+ - **Arguments:** `-y, chrome-devtools-mcp@latest`
120
+
121
+ </details>
122
+
97
123
  <details>
98
124
  <summary>Copilot / VS Code</summary>
99
125
  Follow the MCP install <a href="https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server">guide</a>,
@@ -109,7 +135,7 @@ startup_timeout_ms = 20_000
109
135
 
110
136
  **Click the button to install:**
111
137
 
112
- [<img src="https://cursor.com/deeplink/mcp-install-dark.svg" alt="Install in Cursor">](https://cursor.com/en/install-mcp?name=chrome-devtools&config=eyJjb21tYW5kIjoibnB4IGNocm9tZS1kZXZ0b29scy1tY3BAbGF0ZXN0In0%3D)
138
+ [<img src="https://cursor.com/deeplink/mcp-install-dark.svg" alt="Install in Cursor">](https://cursor.com/en/install-mcp?name=chrome-devtools&config=eyJjb21tYW5kIjoibnB4IC15IGNocm9tZS1kZXZ0b29scy1tY3BAbGF0ZXN0In0%3D)
113
139
 
114
140
  **Or install manually:**
115
141
 
@@ -151,6 +177,21 @@ The same way chrome-devtools-mcp can be configured for JetBrains Junie in `Setti
151
177
 
152
178
  </details>
153
179
 
180
+ <details>
181
+ <summary>Visual Studio</summary>
182
+
183
+ **Click the button to install:**
184
+
185
+ [<img src="https://img.shields.io/badge/Visual_Studio-Install-C16FDE?logo=visualstudio&logoColor=white" alt="Install in Visual Studio">](https://vs-open.link/mcp-install?%7B%22name%22%3A%22chrome-devtools%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22chrome-devtools-mcp%40latest%22%5D%7D)
186
+ </details>
187
+
188
+ <details>
189
+ <summary>Warp</summary>
190
+
191
+ Go to `Settings | AI | Manage MCP Servers` -> `+ Add` to [add an MCP Server](https://docs.warp.dev/knowledge-and-collaboration/mcp#adding-an-mcp-server). Use the config provided above.
192
+
193
+ </details>
194
+
154
195
  ### Your first prompt
155
196
 
156
197
  Enter the following prompt in your MCP Client to check if everything is working:
@@ -166,6 +207,8 @@ Your MCP client should open the browser and record a performance trace.
166
207
 
167
208
  ## Tools
168
209
 
210
+ If you run into any issues, checkout our [troubleshooting guide](./docs/troubleshooting.md).
211
+
169
212
  <!-- BEGIN AUTO GENERATED TOOLS -->
170
213
 
171
214
  - **Input automation** (7 tools)
@@ -236,6 +279,18 @@ The Chrome DevTools MCP server supports the following configuration option:
236
279
  Path to a file to write debug logs to. Set the env variable `DEBUG` to `*` to enable verbose logs. Useful for submitting bug reports.
237
280
  - **Type:** string
238
281
 
282
+ - **`--viewport`**
283
+ Initial viewport size for the Chrome instances started by the server. For example, `1280x720`. In headless mode, max size is 3840x2160px.
284
+ - **Type:** string
285
+
286
+ - **`--proxyServer`**
287
+ Proxy server configuration for Chrome passed as --proxy-server when launching the browser. See https://www.chromium.org/developers/design-documents/network-settings/ for details.
288
+ - **Type:** string
289
+
290
+ - **`--acceptInsecureCerts`**
291
+ If enabled, ignores errors relative to self-signed and expired certificates. Use with caution.
292
+ - **Type:** boolean
293
+
239
294
  <!-- END AUTO GENERATED OPTIONS -->
240
295
 
241
296
  Pass them via the `args` property in the JSON configuration. For example:
@@ -265,8 +320,8 @@ You can also run `npx chrome-devtools-mcp@latest --help` to see all available co
265
320
  `chrome-devtools-mcp` starts a Chrome's stable channel instance using the following user
266
321
  data directory:
267
322
 
268
- - Linux / MacOS: `$HOME/.cache/chrome-devtools-mcp/chrome-profile-$CHANNEL`
269
- - Window: `%HOMEPATH%/.cache/chrome-devtools-mcp/chrome-profile-$CHANNEL`
323
+ - Linux / macOS: `$HOME/.cache/chrome-devtools-mcp/chrome-profile-$CHANNEL`
324
+ - Windows: `%HOMEPATH%/.cache/chrome-devtools-mcp/chrome-profile-$CHANNEL`
270
325
 
271
326
  The user data directory is not cleared between runs and shared across
272
327
  all instances of `chrome-devtools-mcp`. Set the `isolated` option to `true`
@@ -32,11 +32,13 @@
32
32
  import * as Platform from '../platform/platform.js';
33
33
  import { ColorConverter } from './ColorConverter.js';
34
34
  import { blendColors, contrastRatioAPCA, desiredLuminanceAPCA, luminance, luminanceAPCA, rgbToHsl, rgbToHwb, } from './ColorUtils.js';
35
- // <hue> is defined as a <number> or <angle>
36
- // and we hold this in degrees. However, after
37
- // the conversions, these degrees can result in
38
- // negative values. That's why we normalize the hue to be
39
- // between [0 - 360].
35
+ /**
36
+ * <hue> is defined as a <number> or <angle>
37
+ * and we hold this in degrees. However, after
38
+ * the conversions, these degrees can result in
39
+ * negative values. That's why we normalize the hue to be
40
+ * between [0 - 360].
41
+ **/
40
42
  function normalizeHue(hue) {
41
43
  // Even though it is highly unlikely, hue can be
42
44
  // very negative like -400. The initial modulo
@@ -44,9 +46,11 @@ function normalizeHue(hue) {
44
46
  // negative, it is between [-360, 0].
45
47
  return ((hue % 360) + 360) % 360;
46
48
  }
47
- // Parses angle in the form of
48
- // `<angle>deg`, `<angle>turn`, `<angle>grad and `<angle>rad`
49
- // and returns the canonicalized `degree`.
49
+ /**
50
+ * Parses angle in the form of
51
+ * `<angle>deg`, `<angle>turn`, `<angle>grad and `<angle>rad`
52
+ * and returns the canonicalized `degree`.
53
+ **/
50
54
  function parseAngle(angleText) {
51
55
  const angle = angleText.replace(/(deg|g?rad|turn)$/, '');
52
56
  // @ts-expect-error: isNaN can accept strings
@@ -69,7 +73,7 @@ function parseAngle(angleText) {
69
73
  // 1deg === 1deg ^_^
70
74
  return number;
71
75
  }
72
- // Returns the `Format` equivalent from the format text
76
+ /** Returns the `Format` equivalent from the format text **/
73
77
  export function getFormat(formatText) {
74
78
  switch (formatText) {
75
79
  case "hex" /* Format.HEX */:
@@ -37,13 +37,15 @@ class Matrix3x3 {
37
37
  return dst;
38
38
  }
39
39
  }
40
- // A transfer function mapping encoded values to linear values,
41
- // represented by this 7-parameter piecewise function:
42
- //
43
- // linear = sign(encoded) * (c*|encoded| + f) , 0 <= |encoded| < d
44
- // = sign(encoded) * ((a*|encoded| + b)^g + e), d <= |encoded|
45
- //
46
- // (A simple gamma transfer function sets g to gamma and a to 1.)
40
+ /**
41
+ * A transfer function mapping encoded values to linear values,
42
+ * represented by this 7-parameter piecewise function:
43
+ *
44
+ * linear = sign(encoded) * (c*|encoded| + f) , 0 <= |encoded| < d
45
+ * = sign(encoded) * ((a*|encoded| + b)^g + e), d <= |encoded|
46
+ *
47
+ * (A simple gamma transfer function sets g to gamma and a to 1.)
48
+ **/
47
49
  class TransferFunction {
48
50
  g;
49
51
  a;
@@ -43,7 +43,7 @@ export async function compress(str) {
43
43
  const buffer = await gzipCodec(encoded, new CompressionStream('gzip'));
44
44
  return buffer;
45
45
  }
46
- // Private coder/decoder
46
+ /** Private coder/decoder **/
47
47
  function gzipCodec(buffer, codecStream) {
48
48
  const { readable, writable } = new TransformStream();
49
49
  const codecReadable = readable.pipeThrough(codecStream);
@@ -1,9 +1,11 @@
1
1
  // Copyright 2024 The Chromium Authors
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
- // Polyfill of https://github.com/tc39/proposal-upsert with a subclass.
5
- //
6
- // TODO: Once the proposal is merged, just replace `MapWithDefault` with `Map` and remove it.
4
+ /**
5
+ * Polyfill of https://github.com/tc39/proposal-upsert with a subclass.
6
+ *
7
+ * TODO: Once the proposal is merged, just replace `MapWithDefault` with `Map` and remove it.
8
+ **/
7
9
  export class MapWithDefault extends Map {
8
10
  getOrInsert(key, defaultValue) {
9
11
  if (!this.has(key)) {
@@ -140,10 +140,6 @@ const UIStrings = {
140
140
  * @description Name of a network initiator type
141
141
  */
142
142
  preflight: 'Preflight',
143
- /**
144
- * @description Name of a network initiator type
145
- */
146
- webbundle: 'WebBundle',
147
143
  /**
148
144
  * @description Name of a network initiator type for FedCM requests
149
145
  */
@@ -199,9 +195,6 @@ export class ResourceType {
199
195
  if (mimeType === 'application/wasm') {
200
196
  return resourceTypes.Wasm;
201
197
  }
202
- if (mimeType === 'application/webbundle') {
203
- return resourceTypes.WebBundle;
204
- }
205
198
  return null;
206
199
  }
207
200
  static fromURL(url) {
@@ -299,9 +292,6 @@ export class ResourceType {
299
292
  isFromSourceMap() {
300
293
  return this.#name.startsWith('sm-');
301
294
  }
302
- isWebbundle() {
303
- return this.#name === 'webbundle';
304
- }
305
295
  toString() {
306
296
  return this.#name;
307
297
  }
@@ -371,7 +361,6 @@ export const resourceTypes = {
371
361
  Preflight: new ResourceType('preflight', i18nLazyString(UIStrings.preflight), resourceCategories.Other, true),
372
362
  SourceMapScript: new ResourceType('sm-script', i18nLazyString(UIStrings.script), resourceCategories.Script, true),
373
363
  SourceMapStyleSheet: new ResourceType('sm-stylesheet', i18nLazyString(UIStrings.stylesheet), resourceCategories.Stylesheet, true),
374
- WebBundle: new ResourceType('webbundle', i18nLazyString(UIStrings.webbundle), resourceCategories.Other, false),
375
364
  FedCM: new ResourceType('fedcm', i18nLazyString(UIStrings.fedcm), resourceCategories.Other, false),
376
365
  };
377
366
  const mimeTypeByName = new Map([
@@ -1,10 +1,12 @@
1
1
  // Copyright 2025 The Chromium Authors
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
- // Set instance of this class as flavor to mark what panel triggered the
5
- // 'elements.toggle-element-search' action if it was not the elements panel.
6
- // This will cause specified panel to be made visible instead of the elements
7
- // panel after the inspection is done.
4
+ /**
5
+ * Set instance of this class as flavor to mark what panel triggered the
6
+ * 'elements.toggle-element-search' action if it was not the elements panel.
7
+ * This will cause specified panel to be made visible instead of the elements
8
+ * panel after the inspection is done.
9
+ **/
8
10
  export class ReturnToPanelFlavor {
9
11
  viewId;
10
12
  constructor(viewId) {
@@ -24,7 +24,7 @@ export var FunctionalityType;
24
24
  FunctionalityType[FunctionalityType["EXPLAIN_ERROR"] = 2] = "EXPLAIN_ERROR";
25
25
  FunctionalityType[FunctionalityType["AGENTIC_CHAT"] = 5] = "AGENTIC_CHAT";
26
26
  })(FunctionalityType || (FunctionalityType = {}));
27
- // See: cs/aida.proto (google3).
27
+ /** See: cs/aida.proto (google3). **/
28
28
  export var ClientFeature;
29
29
  (function (ClientFeature) {
30
30
  // Unspecified client feature.
@@ -29,27 +29,49 @@ export var EmailPreference;
29
29
  EmailPreference["ENABLED"] = "ENABLED";
30
30
  EmailPreference["DISABLED"] = "DISABLED";
31
31
  })(EmailPreference || (EmailPreference = {}));
32
- // The `batchGet` awards endpoint returns badge names with an
33
- // obfuscated user ID (e.g., `profiles/12345/awards/badge-name`).
34
- // This function normalizes them to use `me` instead of the ID
35
- // (e.g., `profiles/me/awards/badge-path`) to match the format
36
- // used for client-side requests.
32
+ export var GdpErrorType;
33
+ (function (GdpErrorType) {
34
+ GdpErrorType["HTTP_RESPONSE_UNAVAILABLE"] = "HTTP_RESPONSE_UNAVAILABLE";
35
+ GdpErrorType["NOT_FOUND"] = "NOT_FOUND";
36
+ })(GdpErrorType || (GdpErrorType = {}));
37
+ class GdpError extends Error {
38
+ type;
39
+ constructor(type, options) {
40
+ super(undefined, options);
41
+ this.type = type;
42
+ }
43
+ }
44
+ /**
45
+ * The `batchGet` awards endpoint returns badge names with an
46
+ * obfuscated user ID (e.g., `profiles/12345/awards/badge-name`).
47
+ * This function normalizes them to use `me` instead of the ID
48
+ * (e.g., `profiles/me/awards/badge-path`) to match the format
49
+ * used for client-side requests.
50
+ **/
37
51
  function normalizeBadgeName(name) {
38
52
  return name.replace(/profiles\/[^/]+\/awards\//, 'profiles/me/awards/');
39
53
  }
40
54
  export const GOOGLE_DEVELOPER_PROGRAM_PROFILE_LINK = 'https://developers.google.com/profile/u/me';
41
55
  async function makeHttpRequest(request) {
42
56
  if (!isGdpProfilesAvailable()) {
43
- return null;
57
+ throw new GdpError(GdpErrorType.HTTP_RESPONSE_UNAVAILABLE);
44
58
  }
45
59
  const response = await new Promise(resolve => {
46
60
  InspectorFrontendHostInstance.dispatchHttpRequest(request, resolve);
47
61
  });
48
62
  debugLog({ request, response });
63
+ if (response.statusCode === 404) {
64
+ throw new GdpError(GdpErrorType.NOT_FOUND);
65
+ }
49
66
  if ('response' in response && response.statusCode === 200) {
50
- return JSON.parse(response.response);
67
+ try {
68
+ return JSON.parse(response.response);
69
+ }
70
+ catch (err) {
71
+ throw new GdpError(GdpErrorType.HTTP_RESPONSE_UNAVAILABLE, { cause: err });
72
+ }
51
73
  }
52
- return null;
74
+ throw new GdpError(GdpErrorType.HTTP_RESPONSE_UNAVAILABLE);
53
75
  }
54
76
  const SERVICE_NAME = 'gdpService';
55
77
  let gdpClientInstance = null;
@@ -64,21 +86,41 @@ export class GdpClient {
64
86
  }
65
87
  return gdpClientInstance;
66
88
  }
67
- async initialize() {
68
- const profile = await this.getProfile();
69
- if (profile) {
89
+ /**
90
+ * Fetches the user's GDP profile and eligibility status.
91
+ *
92
+ * It first attempts to fetch the profile. If the profile is not found
93
+ * (a `NOT_FOUND` error), this is handled gracefully by treating the profile
94
+ * as `null` and then proceeding to check for eligibility.
95
+ *
96
+ * @returns A promise that resolves with an object containing the `profile`
97
+ * and `isEligible` status, or `null` if an unexpected error occurs.
98
+ */
99
+ async getProfile() {
100
+ try {
101
+ const profile = await this.#getProfile();
70
102
  return {
71
- hasProfile: true,
103
+ profile,
72
104
  isEligible: true,
73
105
  };
74
106
  }
75
- const isEligible = await this.isEligibleToCreateProfile();
76
- return {
77
- hasProfile: false,
78
- isEligible,
79
- };
107
+ catch (err) {
108
+ if (err instanceof GdpError && err.type === GdpErrorType.HTTP_RESPONSE_UNAVAILABLE) {
109
+ return null;
110
+ }
111
+ }
112
+ try {
113
+ const checkEligibilityResponse = await this.#checkEligibility();
114
+ return {
115
+ profile: null,
116
+ isEligible: checkEligibilityResponse.createProfile === EligibilityStatus.ELIGIBLE,
117
+ };
118
+ }
119
+ catch {
120
+ return null;
121
+ }
80
122
  }
81
- async getProfile() {
123
+ async #getProfile() {
82
124
  if (this.#cachedProfilePromise) {
83
125
  return await this.#cachedProfilePromise;
84
126
  }
@@ -86,14 +128,13 @@ export class GdpClient {
86
128
  service: SERVICE_NAME,
87
129
  path: '/v1beta1/profile:get',
88
130
  method: 'GET',
89
- });
90
- const profile = await this.#cachedProfilePromise;
91
- if (profile) {
131
+ }).then(profile => {
92
132
  this.#cachedEligibilityPromise = Promise.resolve({ createProfile: EligibilityStatus.ELIGIBLE });
93
- }
94
- return profile;
133
+ return profile;
134
+ });
135
+ return await this.#cachedProfilePromise;
95
136
  }
96
- async checkEligibility() {
137
+ async #checkEligibility() {
97
138
  if (this.#cachedEligibilityPromise) {
98
139
  return await this.#cachedEligibilityPromise;
99
140
  }
@@ -105,52 +146,60 @@ export class GdpClient {
105
146
  * @returns null if the request fails, the awarded badge names otherwise.
106
147
  */
107
148
  async getAwardedBadgeNames({ names }) {
108
- const result = await makeHttpRequest({
109
- service: SERVICE_NAME,
110
- path: '/v1beta1/profiles/me/awards:batchGet',
111
- method: 'GET',
112
- queryParams: {
113
- allowMissing: 'true',
114
- names,
115
- }
116
- });
117
- if (!result) {
149
+ try {
150
+ const response = await makeHttpRequest({
151
+ service: SERVICE_NAME,
152
+ path: '/v1beta1/profiles/me/awards:batchGet',
153
+ method: 'GET',
154
+ queryParams: {
155
+ allowMissing: 'true',
156
+ names,
157
+ }
158
+ });
159
+ return new Set(response.awards?.map(award => normalizeBadgeName(award.name)) ?? []);
160
+ }
161
+ catch {
118
162
  return null;
119
163
  }
120
- return new Set(result.awards?.map(award => normalizeBadgeName(award.name)) ?? []);
121
- }
122
- async isEligibleToCreateProfile() {
123
- return (await this.checkEligibility())?.createProfile === EligibilityStatus.ELIGIBLE;
124
164
  }
125
165
  async createProfile({ user, emailPreference }) {
126
- const result = await makeHttpRequest({
127
- service: SERVICE_NAME,
128
- path: '/v1beta1/profiles',
129
- method: 'POST',
130
- body: JSON.stringify({
131
- user,
132
- newsletter_email: emailPreference,
133
- }),
134
- });
135
- if (result) {
166
+ try {
167
+ const response = await makeHttpRequest({
168
+ service: SERVICE_NAME,
169
+ path: '/v1beta1/profiles',
170
+ method: 'POST',
171
+ body: JSON.stringify({
172
+ user,
173
+ newsletter_email: emailPreference,
174
+ }),
175
+ });
136
176
  this.#clearCache();
177
+ return response;
178
+ }
179
+ catch {
180
+ return null;
137
181
  }
138
- return result;
139
182
  }
140
183
  #clearCache() {
141
184
  this.#cachedProfilePromise = undefined;
142
185
  this.#cachedEligibilityPromise = undefined;
143
186
  }
144
- createAward({ name }) {
145
- return makeHttpRequest({
146
- service: SERVICE_NAME,
147
- path: '/v1beta1/profiles/me/awards',
148
- method: 'POST',
149
- body: JSON.stringify({
150
- awardingUri: 'devtools://devtools',
151
- name,
152
- })
153
- });
187
+ async createAward({ name }) {
188
+ try {
189
+ const response = await makeHttpRequest({
190
+ service: SERVICE_NAME,
191
+ path: '/v1beta1/profiles/me/awards',
192
+ method: 'POST',
193
+ body: JSON.stringify({
194
+ awardingUri: 'devtools://devtools',
195
+ name,
196
+ })
197
+ });
198
+ return response;
199
+ }
200
+ catch {
201
+ return null;
202
+ }
154
203
  }
155
204
  }
156
205
  function isDebugMode() {
@@ -182,5 +231,13 @@ export function getGdpProfilesEnterprisePolicy() {
182
231
  return (Root.Runtime.hostConfig.devToolsGdpProfilesAvailability?.enterprisePolicyValue ??
183
232
  Root.Runtime.GdpProfilesEnterprisePolicyValue.DISABLED);
184
233
  }
234
+ export function isBadgesEnabled() {
235
+ const isBadgesEnabledByEnterprisePolicy = getGdpProfilesEnterprisePolicy() === Root.Runtime.GdpProfilesEnterprisePolicyValue.ENABLED;
236
+ const isBadgesEnabledByFeatureFlag = Boolean(Root.Runtime.hostConfig.devToolsGdpProfiles?.badgesEnabled);
237
+ return isBadgesEnabledByEnterprisePolicy && isBadgesEnabledByFeatureFlag;
238
+ }
239
+ export function isStarterBadgeEnabled() {
240
+ return Boolean(Root.Runtime.hostConfig.devToolsGdpProfiles?.starterBadgeEnabled);
241
+ }
185
242
  // @ts-expect-error
186
243
  globalThis.setDebugGdpIntegrationEnabled = setDebugGdpIntegrationEnabled;
@@ -301,6 +301,9 @@ export class InspectorFrontendHostStub {
301
301
  devToolsFlexibleLayout: {
302
302
  verticalDrawerEnabled: true,
303
303
  },
304
+ devToolsStartingStyleDebugging: {
305
+ enabled: false,
306
+ },
304
307
  };
305
308
  if ('hostConfigForTesting' in globalThis) {
306
309
  const { hostConfigForTesting } = globalThis;
@@ -24,9 +24,11 @@ export function isWin() {
24
24
  }
25
25
  return _isWin;
26
26
  }
27
- // In Chrome Layout tests the imported 'Platform' object is not writable/
28
- // configurable, which prevents us from monkey-patching 'Platform''s methods.
29
- // We circumvent this by adding 'setPlatformForTests'.
27
+ /**
28
+ * In Chrome Layout tests the imported 'Platform' object is not writable/
29
+ * configurable, which prevents us from monkey-patching 'Platform''s methods.
30
+ * We circumvent this by adding 'setPlatformForTests'.
31
+ **/
30
32
  export function setPlatformForTests(platform) {
31
33
  _platform = platform;
32
34
  _isMac = undefined;
@@ -231,9 +231,11 @@ export class UserMetrics {
231
231
  * 1. Delete the line with the unneeded value
232
232
  * 2. Do not update any 'MAX_VALUE' or any other value.
233
233
  */
234
- // Codes below are used to collect UMA histograms in the Chromium port.
235
- // Do not change the values below, additional actions are needed on the Chromium side
236
- // in order to add more codes.
234
+ /**
235
+ * Codes below are used to collect UMA histograms in the Chromium port.
236
+ * Do not change the values below, additional actions are needed on the Chromium side
237
+ * in order to add more codes.
238
+ **/
237
239
  export var Action;
238
240
  (function (Action) {
239
241
  /* eslint-disable @typescript-eslint/naming-convention */
@@ -706,7 +708,7 @@ export var DevtoolsExperiments;
706
708
  // Increment this when new experiments are added.
707
709
  DevtoolsExperiments[DevtoolsExperiments["MAX_VALUE"] = 110] = "MAX_VALUE";
708
710
  })(DevtoolsExperiments || (DevtoolsExperiments = {}));
709
- // Update DevToolsIssuesPanelIssueExpanded from tools/metrics/histograms/enums.xml if new enum is added.
711
+ /** Update DevToolsIssuesPanelIssueExpanded from tools/metrics/histograms/enums.xml if new enum is added. **/
710
712
  export var IssueExpanded;
711
713
  (function (IssueExpanded) {
712
714
  /* eslint-disable @typescript-eslint/naming-convention */
@@ -193,7 +193,7 @@ export function nearestIndexFromBeginning(arr, predicate) {
193
193
  export function nearestIndexFromEnd(arr, predicate) {
194
194
  return nearestIndex(arr, predicate, "END" /* NearestSearchStart.END */);
195
195
  }
196
- // Type guard for ensuring that `arr` does not contain null or undefined
196
+ /** Type guard for ensuring that `arr` does not contain null or undefined **/
197
197
  export function arrayDoesNotContainNullOrUndefined(arr) {
198
198
  return !arr.includes(null) && !arr.includes(undefined);
199
199
  }