@module-federation/devtools 2.9.0 → 2.9.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 (45) hide show
  1. package/README.md +125 -0
  2. package/dist/es/App.js +6 -1
  3. package/dist/es/App.module.js +1 -1
  4. package/dist/es/App_module.css +106 -0
  5. package/dist/es/component/Form/index_module.css +40 -0
  6. package/dist/es/component/Layout/index.js +38 -21
  7. package/dist/es/component/Layout/index_module.css +30 -0
  8. package/dist/es/utils/chrome/fast-refresh.js +84 -5
  9. package/dist/es/utils/chrome/index.js +4 -1
  10. package/dist/es/utils/chrome/observability.js +1 -0
  11. package/dist/es/utils/chrome/webmcp-entry.js +2 -0
  12. package/dist/es/utils/chrome/webmcp.js +468 -0
  13. package/dist/es/utils/sdk/index.js +6 -0
  14. package/dist/es/worker/index.js +1 -1
  15. package/dist/lib/App.js +6 -1
  16. package/dist/lib/App.module.js +1 -1
  17. package/dist/lib/App_module.css +106 -0
  18. package/dist/lib/component/Form/index_module.css +40 -0
  19. package/dist/lib/component/Layout/index.js +38 -21
  20. package/dist/lib/component/Layout/index_module.css +30 -0
  21. package/dist/lib/utils/chrome/fast-refresh.js +83 -4
  22. package/dist/lib/utils/chrome/index.js +4 -1
  23. package/dist/lib/utils/chrome/observability.js +2 -0
  24. package/dist/lib/utils/chrome/webmcp-entry.js +2 -0
  25. package/dist/lib/utils/chrome/webmcp.js +484 -0
  26. package/dist/lib/utils/sdk/index.js +7 -0
  27. package/dist/lib/worker/index.js +1 -1
  28. package/dist/types/App.js +6 -1
  29. package/dist/types/App.module.js +1 -1
  30. package/dist/types/App_module.css +106 -0
  31. package/dist/types/component/Form/index_module.css +40 -0
  32. package/dist/types/component/Layout/index.js +38 -21
  33. package/dist/types/component/Layout/index_module.css +30 -0
  34. package/dist/types/src/utils/chrome/observability.d.ts +11 -0
  35. package/dist/types/src/utils/chrome/webmcp-entry.d.ts +1 -0
  36. package/dist/types/src/utils/chrome/webmcp.d.ts +25 -0
  37. package/dist/types/src/utils/sdk/index.d.ts +1 -0
  38. package/dist/types/utils/chrome/fast-refresh.js +83 -4
  39. package/dist/types/utils/chrome/index.js +4 -1
  40. package/dist/types/utils/chrome/observability.js +2 -0
  41. package/dist/types/utils/chrome/webmcp-entry.js +2 -0
  42. package/dist/types/utils/chrome/webmcp.js +484 -0
  43. package/dist/types/utils/sdk/index.js +7 -0
  44. package/dist/types/worker/index.js +1 -1
  45. package/package.json +5 -4
package/README.md CHANGED
@@ -9,3 +9,128 @@
9
9
  loading reports
10
10
 
11
11
  https://module-federation.io/
12
+
13
+ ## Extension builds
14
+
15
+ From the repository root:
16
+
17
+ ```sh
18
+ pnpm --filter @module-federation/devtools run build:devtool
19
+ ```
20
+
21
+ One compilation produces two unpacked extensions:
22
+
23
+ | Directory | Interface |
24
+ | --------------------------------------- | ------------------------------------------------------------------------- |
25
+ | `packages/chrome-devtools/dist/chrome` | Chrome side panel and DevTools panel |
26
+ | `packages/chrome-devtools/dist/browser` | 800 × 600 popup with fixed navigation/header and a scrolling content area |
27
+
28
+ Load the appropriate directory, then reload the page being inspected. Both
29
+ variants retain the same `document_start` MAIN-world scripts for proxying,
30
+ React Fast Refresh and loading traces. The browser variant omits `sidePanel`,
31
+ `side_panel`, and `devtools_page`. It still needs Manifest V3 content scripts,
32
+ service workers, storage, tabs and scripting APIs from the host browser.
33
+
34
+ `build:devtool:popup` is a compatibility alias that now builds both variants.
35
+ `dist/.extension-build` is intermediate output, not an installable extension.
36
+ The library `build` command cleans `dist`, so run `build:devtool` afterwards
37
+ when you also need the unpacked extensions. Remove the old root-`dist` extension
38
+ registration when migrating; do not enable two variants on the same page.
39
+
40
+ ## WebMCP
41
+
42
+ Both extensions register page-scoped tools from a MAIN-world content script at
43
+ `document_start`. Tools remain available with the extension UI closed. Registration
44
+ uses native `document.modelContext` or the earlier `navigator.modelContext` API.
45
+ `registerTool` is required; `unregisterTool` is optional. On hosts without
46
+ unregistration, tools live until the document is destroyed; partial registration
47
+ failures cannot be rolled back and require a page reload.
48
+ The entry retries host API detection every 250 ms for up to 30 seconds. Inspect
49
+ `window.__MF_DEVTOOLS_WEBMCP__` in the page console for `waiting`, `registering`,
50
+ `registered`, `unavailable` or `error`, tool names, and registration errors.
51
+ After installing/reloading the extension, refresh existing pages. If the host API
52
+ arrives after the retry window, refresh the page once it is available.
53
+ When neither API is available, the extension continues to work normally without
54
+ WebMCP. No polyfill is installed: the browser/agent must support discovering and
55
+ calling registered tools. A page exposing tools alone does not establish that
56
+ an embedded browser exposes them to its agent.
57
+
58
+ | Tool | Capability |
59
+ | ---------------------------- | ------------------------------------------------------------------------------------------------------ |
60
+ | `mf_get_state` | Module snapshots, shared scopes, registered plugins, saved proxy/HMR configuration and loading reports |
61
+ | `mf_get_modules` | All module snapshots or one exact `moduleId` |
62
+ | `mf_get_dependencies` | Dependency graph source data, consumers and remote IDs |
63
+ | `mf_get_shared` | Shared versions and loading state; factories are not executed |
64
+ | `mf_set_proxy` | Replace rules, enable/disable individual rules and configure snapshot clipping |
65
+ | `mf_clear_proxy` | Clear proxy rules and cached proxy snapshots, preserving HMR/tracing |
66
+ | `mf_set_hmr` | Enable/disable development React replacement and Fast Refresh |
67
+ | `mf_configure_loading_trace` | Enable/disable tracing; level, event limit, console, browser output, scope, mode and start logging |
68
+ | `mf_get_loading_reports` | Current reports across observability scopes |
69
+ | `mf_export_snapshot` | Serializable JSON export of the current diagnostic state |
70
+
71
+ These tools cover the implemented runtime features. Graph layout, filtering,
72
+ module selection, theme and language remain presentation controls in the UI;
73
+ an agent receives the underlying structured data. The Performance tab is still
74
+ a placeholder and has no performance-analysis tool. Export tools return JSON
75
+ rather than silently downloading a file.
76
+
77
+ Example tool inputs:
78
+
79
+ ```json
80
+ {
81
+ "rules": [
82
+ {
83
+ "key": "mf_playground",
84
+ "value": "https://localhost:3006/mf-manifest.json",
85
+ "checked": true
86
+ }
87
+ ],
88
+ "clip": false
89
+ }
90
+ ```
91
+
92
+ Pass that object to `mf_set_proxy`. Pass `{ "enabled": true }` to `mf_set_hmr`.
93
+ `mf_set_proxy` replaces the complete rule set, so read `mf_get_state` before
94
+ editing existing rules. An unchecked rule remains saved but is not applied.
95
+
96
+ Configuration tools synchronously persist the inspected origin's localStorage
97
+ and return `reloadRequired: true`. They do not reload inside the tool invocation,
98
+ which could destroy its response before the agent receives it. The agent should
99
+ finish configuration, reload through browser navigation, then call `mf_get_state`
100
+ to verify. Saved configuration is distinct from the currently running runtime.
101
+ Eager React shares may cause one additional automatic refresh while the existing
102
+ Fast Refresh plugin records their version and share scopes.
103
+
104
+ React 19 development replacement is pinned to the same pair used by Divebell:
105
+ `umd-react@19.2.4/dist/react.development.js` and
106
+ `umd-react@19.2.4/dist/react-dom.development.js` from unpkg. All `19.x` React,
107
+ ReactDOM and `react-dom/client` registrations use version `19.2.4` while HMR is
108
+ enabled. The client entry reuses ReactDOM's development bundle. Both async and
109
+ eager loading initialize React before ReactDOM; React 19 eager shares can load
110
+ synchronously without the first-discovery refresh. React 17/18 keep their existing
111
+ official development UMD URLs. Disabling HMR preserves the application's providers.
112
+
113
+ The UI reads the same page configuration; opening it does not replay a stale
114
+ global HMR preference or trigger a reload. Open extension views receive a snapshot
115
+ notification after a tool changes configuration. Settings are origin-scoped and
116
+ persist across navigation; they are not isolated to one tab of the same origin.
117
+
118
+ The tools expose only named diagnostic operations, not arbitrary script execution,
119
+ arbitrary storage access or extension APIs. Page CSP, CORS and certificate checks
120
+ still apply. In particular, HTTPS sites whose CSP excludes local HTTP resources
121
+ need a permitted remote endpoint; WebMCP does not bypass that policy.
122
+
123
+ ## Validation
124
+
125
+ ```sh
126
+ pnpm --filter @module-federation/devtools run test
127
+ pnpm --filter @module-federation/devtools run build
128
+ pnpm --filter @module-federation/devtools run build:devtool
129
+ PLAYWRIGHT_BROWSERS_PATH=0 pnpm --filter @module-federation/devtools exec playwright install chromium
130
+ PLAYWRIGHT_BROWSERS_PATH=0 pnpm --filter @module-federation/devtools exec playwright test e2e/webmcp.spec.ts --workers=1
131
+ ```
132
+
133
+ The WebMCP E2E fixture loads both real unpacked extensions in Chromium, with a
134
+ host-API test double. It verifies early injection, persisted configuration across
135
+ reloads, tool execution, and popup layout. It does not claim to validate native
136
+ WebMCP discovery in Codex or another embedded browser.
package/dist/es/App.js CHANGED
@@ -250,6 +250,8 @@ const InnerApp = (props) => {
250
250
  const bootstrap = () => __async(void 0, null, function* () {
251
251
  const tab = yield syncActiveTab();
252
252
  setInspectedTab(tab || void 0);
253
+ if (typeof (tab == null ? void 0 : tab.id) !== "number")
254
+ return;
253
255
  const detach = yield getGlobalModuleInfo(
254
256
  (info) => applyModuleUpdate(info)
255
257
  );
@@ -361,6 +363,9 @@ const InnerApp = (props) => {
361
363
  }, [activePanel, applyModuleUpdate]);
362
364
  const renderContent = () => {
363
365
  var _a;
366
+ if (typeof (inspectedTab == null ? void 0 : inspectedTab.id) !== "number") {
367
+ return /* @__PURE__ */ jsx(Empty, { description: t("app.header.scope.waiting") });
368
+ }
364
369
  switch (activePanel) {
365
370
  case "moduleInfo":
366
371
  return /* @__PURE__ */ jsx(
@@ -401,7 +406,7 @@ const InnerApp = (props) => {
401
406
  SharedDepsExplorer,
402
407
  {
403
408
  shareData: JSON.parse(
404
- JSON.stringify((_a = window.__FEDERATION__) == null ? void 0 : _a.__SHARE__)
409
+ JSON.stringify(((_a = window.__FEDERATION__) == null ? void 0 : _a.__SHARE__) || {})
405
410
  )
406
411
  }
407
412
  );
@@ -1,5 +1,5 @@
1
1
  import "./App_module.css";
2
- var App_module_default = { "shell": "shell_ae2ee", "sidebar": "sidebar_ae2ee", "tabItem": "tabItem_ae2ee", "activeTab": "activeTab_ae2ee", "panel": "panel_ae2ee", "header": "header_ae2ee", "headerTop": "headerTop_ae2ee", "branding": "branding_ae2ee", "logo": "logo_ae2ee", "subtitle": "subtitle_ae2ee", "meta": "meta_ae2ee", "scope": "scope_ae2ee", "scopeLabel": "scopeLabel_ae2ee", "stats": "stats_ae2ee", "statBlock": "statBlock_ae2ee", "statValue": "statValue_ae2ee", "statLabel": "statLabel_ae2ee", "body": "body_ae2ee", "content": "content_ae2ee", "emptyState": "emptyState_ae2ee", "empty": "empty_ae2ee", "placeholder": "placeholder_ae2ee", "overrideArco": "overrideArco_ae2ee" };
2
+ var App_module_default = { "shell": "shell_ae2ee", "sidebar": "sidebar_ae2ee", "tabItem": "tabItem_ae2ee", "activeTab": "activeTab_ae2ee", "panel": "panel_ae2ee", "header": "header_ae2ee", "headerTop": "headerTop_ae2ee", "branding": "branding_ae2ee", "logo": "logo_ae2ee", "subtitle": "subtitle_ae2ee", "meta": "meta_ae2ee", "scope": "scope_ae2ee", "scopeLabel": "scopeLabel_ae2ee", "stats": "stats_ae2ee", "statBlock": "statBlock_ae2ee", "statValue": "statValue_ae2ee", "statLabel": "statLabel_ae2ee", "body": "body_ae2ee", "content": "content_ae2ee", "emptyState": "emptyState_ae2ee", "empty": "empty_ae2ee", "placeholder": "placeholder_ae2ee", "overrideArco": "overrideArco_ae2ee", "headerActions": "headerActions_ae2ee" };
3
3
  export {
4
4
  App_module_default as default
5
5
  };
@@ -304,4 +304,110 @@ body[arco-theme=dark] {
304
304
 
305
305
  .shell_ae2ee.arco-theme-dark {
306
306
  background: radial-gradient(circle at 10% 20%, rgba(15, 23, 42, 0.9) 0%, rgba(15, 23, 42, 0.98) 55%), linear-gradient(135deg, var(--color-bg-1, #020617) 0%, var(--color-bg-2, #020617) 100%);
307
+ }
308
+
309
+ html[data-devtools-view="popup"] {
310
+ overflow: hidden;
311
+ }
312
+ html[data-devtools-view="popup"] body {
313
+ margin: 0;
314
+ height: 100%;
315
+ overflow: hidden;
316
+ }
317
+ html[data-devtools-view="popup"] .shell_ae2ee {
318
+ height: 100vh;
319
+ min-height: 0;
320
+ padding: 10px;
321
+ gap: 8px;
322
+ flex-direction: column;
323
+ background: var(--color-bg-1, #fff);
324
+ }
325
+ html[data-devtools-view="popup"] .sidebar_ae2ee {
326
+ flex: 0 0 auto;
327
+ width: 100%;
328
+ min-width: 0;
329
+ padding: 4px;
330
+ gap: 2px;
331
+ border-radius: 8px;
332
+ box-shadow: none;
333
+ }
334
+ html[data-devtools-view="popup"] .tabItem_ae2ee {
335
+ padding: 7px 9px;
336
+ font-size: 12px;
337
+ border-radius: 5px;
338
+ letter-spacing: 0;
339
+ text-transform: none;
340
+ }
341
+ html[data-devtools-view="popup"] .tabItem_ae2ee:focus-visible {
342
+ outline: 2px solid var(--color-primary-6, #165dff);
343
+ outline-offset: -2px;
344
+ }
345
+ html[data-devtools-view="popup"] .panel_ae2ee {
346
+ flex: 1 1;
347
+ min-height: 0;
348
+ padding: 12px;
349
+ gap: 10px;
350
+ border-radius: 8px;
351
+ box-shadow: none;
352
+ overflow: hidden;
353
+ }
354
+ html[data-devtools-view="popup"] .header_ae2ee {
355
+ flex: 0 0 auto;
356
+ gap: 6px;
357
+ }
358
+ html[data-devtools-view="popup"] .logo_ae2ee {
359
+ font-size: 16px;
360
+ }
361
+ html[data-devtools-view="popup"] .subtitle_ae2ee,
362
+ html[data-devtools-view="popup"] .scopeLabel_ae2ee {
363
+ display: none;
364
+ }
365
+ html[data-devtools-view="popup"] .headerActions_ae2ee {
366
+ display: flex;
367
+ align-items: center;
368
+ gap: 6px;
369
+ }
370
+ html[data-devtools-view="popup"] .meta_ae2ee {
371
+ align-items: center;
372
+ gap: 8px;
373
+ flex-wrap: nowrap;
374
+ }
375
+ html[data-devtools-view="popup"] .scope_ae2ee {
376
+ min-width: 0;
377
+ flex: 1 1;
378
+ overflow: hidden;
379
+ }
380
+ html[data-devtools-view="popup"] .scope_ae2ee .arco-tag {
381
+ max-width: 100%;
382
+ overflow: hidden;
383
+ text-overflow: ellipsis;
384
+ }
385
+ html[data-devtools-view="popup"] .stats_ae2ee {
386
+ flex: 0 0 auto;
387
+ gap: 12px;
388
+ width: auto;
389
+ }
390
+ html[data-devtools-view="popup"] .statBlock_ae2ee {
391
+ padding: 0;
392
+ min-width: 0;
393
+ flex-direction: row;
394
+ align-items: baseline;
395
+ gap: 4px;
396
+ border: 0;
397
+ background: none;
398
+ box-shadow: none;
399
+ }
400
+ html[data-devtools-view="popup"] .statValue_ae2ee {
401
+ font-size: 14px;
402
+ }
403
+ html[data-devtools-view="popup"] .statLabel_ae2ee {
404
+ font-size: 10px;
405
+ text-transform: none;
406
+ }
407
+ html[data-devtools-view="popup"] .body_ae2ee {
408
+ flex: 1 1;
409
+ }
410
+ html[data-devtools-view="popup"] .content_ae2ee {
411
+ flex: 1 1;
412
+ overscroll-behavior: contain;
307
413
  }
@@ -195,4 +195,44 @@
195
195
  align-items: center;
196
196
  justify-content: center;
197
197
  display: flex;
198
+ }
199
+
200
+ html[data-devtools-view="popup"] .wrapper_20b79,
201
+ html[data-devtools-view="popup"] .rules_20b79 {
202
+ gap: 10px;
203
+ }
204
+ html[data-devtools-view="popup"] .sectionHeader_20b79 {
205
+ padding: 8px;
206
+ gap: 8px;
207
+ flex-wrap: wrap;
208
+ }
209
+ html[data-devtools-view="popup"] .titleRow_20b79 {
210
+ font-size: 13px;
211
+ flex-wrap: wrap;
212
+ gap: 6px;
213
+ }
214
+ html[data-devtools-view="popup"] .subtitle_20b79 {
215
+ display: none;
216
+ }
217
+ html[data-devtools-view="popup"] .ruleCard_20b79 {
218
+ padding: 10px;
219
+ gap: 6px;
220
+ border-radius: 6px;
221
+ box-shadow: none;
222
+ }
223
+ html[data-devtools-view="popup"] .inputs_20b79 {
224
+ gap: 8px;
225
+ }
226
+ html[data-devtools-view="popup"] .field_20b79 {
227
+ min-width: 0;
228
+ margin-bottom: 0;
229
+ }
230
+ html[data-devtools-view="popup"] .field_20b79 .arco-select-view {
231
+ min-height: 32px;
232
+ border-radius: 5px;
233
+ padding: 2px 8px;
234
+ }
235
+ html[data-devtools-view="popup"] .emptyWrapper_20b79 {
236
+ padding: 16px;
237
+ border-radius: 6px;
198
238
  }
@@ -207,7 +207,8 @@ const Layout = (props) => {
207
207
  if (hadPreviousEffective) {
208
208
  yield removeStorage(MODULE_DEVTOOL_IDENTIFIER);
209
209
  yield removeStorage(BROWSER_ENV_KEY);
210
- yield removeStorageKey(__FEDERATION_DEVTOOLS__, "overrides");
210
+ yield mergeStorage(__FEDERATION_DEVTOOLS__, "overrides", {});
211
+ yield mergeStorage(__FEDERATION_DEVTOOLS__, "proxyRules", rawRules);
211
212
  yield injectScript(reloadPage, false);
212
213
  setCondition(statusInfo.noProxy);
213
214
  lastEffectiveRulesRef.current = "";
@@ -253,6 +254,8 @@ const Layout = (props) => {
253
254
  yield setStorage(MODULE_DEVTOOL_IDENTIFIER, snapshotJson);
254
255
  yield setStorage(BROWSER_ENV_KEY);
255
256
  yield mergeStorage(__FEDERATION_DEVTOOLS__, "overrides", overrides);
257
+ yield mergeStorage(__FEDERATION_DEVTOOLS__, "proxyRules", rawRules);
258
+ yield mergeStorage(__FEDERATION_DEVTOOLS__, ENABLE_CLIP, enableClip);
256
259
  yield injectScript(reloadPage, false);
257
260
  window.__FEDERATION__.moduleInfo = moduleInfo2;
258
261
  onModuleInfoChange == null ? void 0 : onModuleInfoChange(moduleInfo2);
@@ -295,6 +298,10 @@ const Layout = (props) => {
295
298
  if (typeof overridesState === "string" && overridesState) {
296
299
  const parsedState = JSON.parse(overridesState);
297
300
  const overrides = parsedState == null ? void 0 : parsedState.overrides;
301
+ setEnableClip(Boolean(parsedState == null ? void 0 : parsedState[ENABLE_CLIP]));
302
+ setEnalbeHMR(
303
+ (parsedState == null ? void 0 : parsedState[__ENABLE_FAST_REFRESH__]) ? "enable" : "disable"
304
+ );
298
305
  if (isObject(overrides)) {
299
306
  const overrideRules = Object.entries(overrides).map(([key, value]) => ({
300
307
  key,
@@ -304,12 +311,10 @@ const Layout = (props) => {
304
311
  (rule) => Boolean(rule.key) && Boolean(rule.value)
305
312
  );
306
313
  const filteredRules = producer.length ? overrideRules.filter((rule) => producer.includes(rule.key)) : overrideRules;
307
- if (filteredRules.length) {
308
- storeData = __spreadProps(__spreadValues({}, storeData), {
309
- [proxyFormField]: filteredRules
310
- });
311
- overridesApplied = true;
312
- }
314
+ storeData = __spreadProps(__spreadValues({}, storeData), {
315
+ [proxyFormField]: Array.isArray(parsedState.proxyRules) ? parsedState.proxyRules : filteredRules
316
+ });
317
+ overridesApplied = true;
313
318
  }
314
319
  }
315
320
  } catch (error) {
@@ -347,15 +352,25 @@ const Layout = (props) => {
347
352
  return () => {
348
353
  cancelled = true;
349
354
  };
350
- }, [moduleInfo, producerKey, form]);
355
+ }, [moduleInfo, producerKey, form, tabId]);
351
356
  useEffect(() => {
352
- chrome.storage.sync.get([ENABLEHMR]).then((data) => {
353
- const enable = data[ENABLEHMR];
354
- if (typeof enable === "boolean") {
355
- onHMRChange(enable);
357
+ let cancelled = false;
358
+ getStorageValue(__FEDERATION_DEVTOOLS__).then((raw) => {
359
+ try {
360
+ const config = typeof raw === "string" ? JSON.parse(raw) : {};
361
+ if (!cancelled) {
362
+ setEnalbeHMR(
363
+ (config == null ? void 0 : config[__ENABLE_FAST_REFRESH__]) ? "enable" : "disable"
364
+ );
365
+ setEnableClip(Boolean(config == null ? void 0 : config[ENABLE_CLIP]));
366
+ }
367
+ } catch (e) {
356
368
  }
357
369
  });
358
- }, []);
370
+ return () => {
371
+ cancelled = true;
372
+ };
373
+ }, [tabId]);
359
374
  useEffect(() => {
360
375
  validateForm(form);
361
376
  }, []);
@@ -396,20 +411,22 @@ const Layout = (props) => {
396
411
  setChromeStorage(formData);
397
412
  run(formData);
398
413
  };
399
- const onHMRChange = (on) => {
414
+ const onHMRChange = (on) => __async(void 0, null, function* () {
400
415
  setEnalbeHMR(on ? "enable" : "disable");
401
- chrome.storage.sync.set({
416
+ yield chrome.storage.sync.set({
402
417
  [ENABLEHMR]: on
403
418
  });
404
419
  if (on) {
405
- mergeStorage(__FEDERATION_DEVTOOLS__, __ENABLE_FAST_REFRESH__, on);
420
+ yield mergeStorage(__FEDERATION_DEVTOOLS__, __ENABLE_FAST_REFRESH__, on);
406
421
  } else {
407
- removeStorageKey(__FEDERATION_DEVTOOLS__, __ENABLE_FAST_REFRESH__);
408
- removeStorageKey(__FEDERATION_DEVTOOLS__, __EAGER_SHARE__);
422
+ yield removeStorageKey(__FEDERATION_DEVTOOLS__, __ENABLE_FAST_REFRESH__);
423
+ yield removeStorageKey(__FEDERATION_DEVTOOLS__, __EAGER_SHARE__);
409
424
  }
410
425
  injectScript(reloadPage, false);
411
- };
412
- const onClipChange = (on) => {
426
+ });
427
+ const onClipChange = (on) => __async(void 0, null, function* () {
428
+ yield mergeStorage(__FEDERATION_DEVTOOLS__, ENABLE_CLIP, on);
429
+ lastFormSignatureRef.current = "";
413
430
  setEnableClip(on);
414
431
  try {
415
432
  if (tabId) {
@@ -419,7 +436,7 @@ const Layout = (props) => {
419
436
  console.error(e);
420
437
  }
421
438
  run(form.getFieldsValue());
422
- };
439
+ });
423
440
  const remotePreview = producer.map((id) => {
424
441
  const [, name] = id.split(":");
425
442
  return name || id;
@@ -65,4 +65,34 @@
65
65
  padding: 22px 24px;
66
66
  border: 1px solid rgba(228, 228, 231, 0.5);
67
67
  background: var(--color-bg-1, rgba(255, 255, 255, 0.9));
68
+ }
69
+
70
+ html[data-devtools-view="popup"] .wrapper_084ab {
71
+ gap: 10px;
72
+ }
73
+ html[data-devtools-view="popup"] .summaryCard_084ab {
74
+ padding: 10px;
75
+ min-height: 0;
76
+ gap: 6px;
77
+ flex-direction: row;
78
+ flex-wrap: wrap;
79
+ align-items: center;
80
+ }
81
+ html[data-devtools-view="popup"] .summaryHeader_084ab {
82
+ flex: 1 1 100%;
83
+ }
84
+ html[data-devtools-view="popup"] .summaryTitle_084ab {
85
+ font-size: 13px;
86
+ }
87
+ html[data-devtools-view="popup"] .summaryHint_084ab {
88
+ display: none;
89
+ }
90
+ html[data-devtools-view="popup"] .chip_084ab,
91
+ html[data-devtools-view="popup"] .moreChip_084ab,
92
+ html[data-devtools-view="popup"] .chipPlaceholder_084ab {
93
+ padding: 3px 7px;
94
+ border-radius: 4px;
95
+ }
96
+ html[data-devtools-view="popup"] .formSection_084ab {
97
+ padding: 10px;
68
98
  }
@@ -22,7 +22,7 @@ var __async = (__this, __arguments, generator) => {
22
22
  step((generator = generator.apply(__this, __arguments)).next());
23
23
  });
24
24
  };
25
- import { getUnpkgUrl } from "../index";
25
+ import { getUnpkgUrl, REACT_19_DEV_VERSION } from "../index";
26
26
  import { definePropertyGlobalVal } from "../sdk";
27
27
  import {
28
28
  __FEDERATION_DEVTOOLS__,
@@ -210,6 +210,25 @@ var require_fast_refresh = __commonJS({
210
210
  return null;
211
211
  };
212
212
  const devtoolsMessage = getDevtoolsMessage();
213
+ if ((devtoolsMessage == null ? void 0 : devtoolsMessage[__ENABLE_FAST_REFRESH__]) && !window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
214
+ let nextId = 0;
215
+ const renderers = /* @__PURE__ */ new Map();
216
+ window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
217
+ supportsFiber: true,
218
+ renderers,
219
+ inject(renderer) {
220
+ const id = nextId++;
221
+ renderers.set(id, renderer);
222
+ return id;
223
+ },
224
+ onScheduleFiberRoot() {
225
+ },
226
+ onCommitFiberRoot() {
227
+ },
228
+ onCommitFiberUnmount() {
229
+ }
230
+ };
231
+ }
213
232
  const eagerShareInfo = getEagerShareInfo(devtoolsMessage == null ? void 0 : devtoolsMessage[__EAGER_SHARE__]);
214
233
  if ((devtoolsMessage == null ? void 0 : devtoolsMessage[__ENABLE_FAST_REFRESH__]) && eagerShareInfo) {
215
234
  const { version, scopes } = eagerShareInfo;
@@ -221,6 +240,43 @@ var require_fast_refresh = __commonJS({
221
240
  );
222
241
  }
223
242
  const fastRefreshPlugin = () => {
243
+ const react19Pending = /* @__PURE__ */ new Map();
244
+ const loadReact19 = (pkg, scopes, sync = false) => {
245
+ const existing = getScopeGlobal(pkg, scopes);
246
+ if ((existing == null ? void 0 : existing.version) === REACT_19_DEV_VERSION) {
247
+ return setScopeGlobals(pkg, scopes, existing);
248
+ }
249
+ const save = (library) => {
250
+ const cached = getScopeGlobal(pkg, scopes);
251
+ if ((cached == null ? void 0 : cached.version) === REACT_19_DEV_VERSION)
252
+ return setScopeGlobals(pkg, scopes, cached);
253
+ if ((library == null ? void 0 : library.version) !== REACT_19_DEV_VERSION)
254
+ throw new Error(`Expected development ${pkg}@${REACT_19_DEV_VERSION}.`);
255
+ if (pkg === "react" && (typeof library.createElement !== "function" || !Object.isFrozen(library.createElement("div"))))
256
+ throw new Error("React provider is not a development build.");
257
+ return setScopeGlobals(pkg, scopes, library);
258
+ };
259
+ if (sync) {
260
+ if (pkg === "react-dom")
261
+ loadReact19("react", scopes, true);
262
+ return save(loadUmdModuleSync(pkg, REACT_19_DEV_VERSION, scopes));
263
+ }
264
+ const key = `${pkg}:${JSON.stringify([...scopes].sort())}`;
265
+ let pending = react19Pending.get(key);
266
+ if (!pending) {
267
+ pending = (() => __async(exports, null, function* () {
268
+ if (pkg === "react-dom")
269
+ yield loadReact19("react", scopes);
270
+ const cached = getScopeGlobal(pkg, scopes);
271
+ if ((cached == null ? void 0 : cached.version) === REACT_19_DEV_VERSION)
272
+ return setScopeGlobals(pkg, scopes, cached);
273
+ return save(yield loadUmdModule(pkg, REACT_19_DEV_VERSION, scopes));
274
+ }))();
275
+ react19Pending.set(key, pending);
276
+ void pending.catch(() => react19Pending.delete(key));
277
+ }
278
+ return pending;
279
+ };
224
280
  let orderResolve;
225
281
  const orderPromise = new Promise((resolve) => {
226
282
  orderResolve = resolve;
@@ -228,12 +284,13 @@ var require_fast_refresh = __commonJS({
228
284
  return {
229
285
  name: "mf-fast-refresh-plugin",
230
286
  beforeRegisterShare(args) {
231
- var _a2;
287
+ var _a2, _b;
232
288
  const { pkgName, shared } = args;
233
- if (!SUPPORT_PKGS.includes(pkgName)) {
289
+ const isReact19 = /^19\./.test(shared.version);
290
+ if (!SUPPORT_PKGS.includes(pkgName) && !(isReact19 && pkgName === "react-dom/client")) {
234
291
  return args;
235
292
  }
236
- const supportPkgName = pkgName;
293
+ const supportPkgName = pkgName === "react-dom/client" ? "react-dom" : pkgName;
237
294
  const shareScopes = getShareScopes(shared.scope);
238
295
  let enableFastRefresh = false;
239
296
  let devtoolsMessage2 = {};
@@ -249,7 +306,29 @@ var require_fast_refresh = __commonJS({
249
306
  if (!enableFastRefresh) {
250
307
  return args;
251
308
  }
252
- if (((_a2 = shared.shareConfig) == null ? void 0 : _a2.eager) || shared.lib) {
309
+ if (isReact19) {
310
+ shared.version = REACT_19_DEV_VERSION;
311
+ shared.get = () => __async(this, null, function* () {
312
+ const library = yield loadReact19(supportPkgName, shareScopes);
313
+ return () => library;
314
+ });
315
+ if (((_a2 = shared.shareConfig) == null ? void 0 : _a2.eager) || shared.lib) {
316
+ const library = loadReact19(supportPkgName, shareScopes, true);
317
+ shared.lib = () => library;
318
+ updateEagerShareInfo(
319
+ devtoolsMessage2,
320
+ pkgName,
321
+ REACT_19_DEV_VERSION,
322
+ shareScopes
323
+ );
324
+ localStorage.setItem(
325
+ __FEDERATION_DEVTOOLS__,
326
+ JSON.stringify(devtoolsMessage2)
327
+ );
328
+ }
329
+ return args;
330
+ }
331
+ if (((_b = shared.shareConfig) == null ? void 0 : _b.eager) || shared.lib) {
253
332
  if (!(devtoolsMessage2 == null ? void 0 : devtoolsMessage2[__EAGER_SHARE__])) {
254
333
  const eagerShareInfo2 = updateEagerShareInfo(
255
334
  devtoolsMessage2,
@@ -153,7 +153,10 @@ const getGlobalModuleInfo = (callback) => __async(void 0, null, function* () {
153
153
  callback(normalizeModuleInfoPayload((_b = window.__FEDERATION__) == null ? void 0 : _b.moduleInfo));
154
154
  }
155
155
  yield sleep(300);
156
- const listener = (message) => {
156
+ const listener = (message, sender) => {
157
+ var _a2;
158
+ if (((_a2 = sender == null ? void 0 : sender.tab) == null ? void 0 : _a2.id) !== void 0 && sender.tab.id !== getCurrentTabId())
159
+ return;
157
160
  const { data } = message;
158
161
  if (!isModuleInfoSyncMessage(data) || (data == null ? void 0 : data.appInfos)) {
159
162
  return;
@@ -204,5 +204,6 @@ export {
204
204
  mergeObservabilityReports,
205
205
  readObservabilityConfig,
206
206
  readObservabilitySnapshot,
207
+ readSnapshotFromPage,
207
208
  reloadInspectedPage
208
209
  };
@@ -0,0 +1,2 @@
1
+ import { startDevtoolsWebMCP } from "./webmcp";
2
+ startDevtoolsWebMCP();