create-interview-cockpit 0.8.0 → 0.10.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.
@@ -281,12 +281,235 @@ This lab uses real webpack 5 + webpack-dev-server + Module Federation.
281
281
  "build": "webpack --config webpack.config.js"
282
282
  }
283
283
  }
284
+ `,
285
+ "apps/shared/mfInspector.js": `import React from "react";
286
+ import ReactDOM from "react-dom";
287
+ // NOTE: default import (not namespace import) is intentional — namespace objects
288
+ // are never reference-equal across MF consumers even when the singleton is shared,
289
+ // so comparing the default export is the only way to prove same-instance.
290
+
291
+ const app =
292
+ typeof __MF_INSPECTOR_APP__ === "string" ? __MF_INSPECTOR_APP__ : "unknown";
293
+ const sandboxId =
294
+ typeof __MF_INSPECTOR_SANDBOX_ID__ === "string"
295
+ ? __MF_INSPECTOR_SANDBOX_ID__
296
+ : "";
297
+ const declaredConfig =
298
+ typeof __MF_INSPECTOR_DECLARED_CONFIG__ !== "undefined"
299
+ ? __MF_INSPECTOR_DECLARED_CONFIG__
300
+ : null;
301
+ const runtimeId =
302
+ typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
303
+ ? crypto.randomUUID()
304
+ : app + "-" + Date.now() + "-" + Math.random().toString(36).slice(2);
305
+
306
+ let attachedRouteListeners = false;
307
+
308
+ function getRoute() {
309
+ if (typeof window === "undefined" || !window.location) return "/";
310
+ const route =
311
+ window.location.pathname +
312
+ window.location.search +
313
+ window.location.hash;
314
+ return route || "/";
315
+ }
316
+
317
+ function cloneJsonSafe(value) {
318
+ if (value == null) return value;
319
+ try {
320
+ return JSON.parse(JSON.stringify(value));
321
+ } catch {
322
+ return value;
323
+ }
324
+ }
325
+
326
+ export async function ensureShareScopeReady() {
327
+ try {
328
+ if (typeof __webpack_init_sharing__ === "function") {
329
+ await __webpack_init_sharing__("default");
330
+ }
331
+ } catch (error) {
332
+ emitInspectorEvent("share-init-error", {
333
+ message: error instanceof Error ? error.message : String(error),
334
+ });
335
+ }
336
+ }
337
+
338
+ export function snapshotShareScopes() {
339
+ try {
340
+ if (typeof __webpack_share_scopes__ === "undefined") return {};
341
+ return Object.fromEntries(
342
+ Object.entries(__webpack_share_scopes__).map(([scopeName, scopeValue]) => [
343
+ scopeName,
344
+ Object.fromEntries(
345
+ Object.entries(scopeValue || {}).map(([packageName, versions]) => [
346
+ packageName,
347
+ Object.entries(versions || {}).map(([version, entry]) => ({
348
+ version,
349
+ from: entry && typeof entry.from === "string" ? entry.from : null,
350
+ eager: Boolean(entry && entry.eager),
351
+ loaded: Boolean(entry && entry.loaded),
352
+ hasGet: Boolean(entry && typeof entry.get === "function"),
353
+ })),
354
+ ]),
355
+ ),
356
+ ]),
357
+ );
358
+ } catch (error) {
359
+ return {
360
+ __error: error instanceof Error ? error.message : String(error),
361
+ };
362
+ }
363
+ }
364
+
365
+ export function emitInspectorEvent(kind, payload) {
366
+ if (!sandboxId || typeof window === "undefined" || !window.parent) return;
367
+ window.parent.postMessage(
368
+ {
369
+ type: "mf-inspector-event",
370
+ sandboxId,
371
+ app,
372
+ runtimeId,
373
+ kind,
374
+ route: getRoute(),
375
+ timestamp: Date.now(),
376
+ payload: cloneJsonSafe(payload),
377
+ },
378
+ "*",
379
+ );
380
+ }
381
+
382
+ function attachRouteListeners() {
383
+ if (attachedRouteListeners || typeof window === "undefined") return;
384
+ attachedRouteListeners = true;
385
+ window.addEventListener("popstate", () => {
386
+ void emitRouteSnapshot("popstate");
387
+ });
388
+ window.addEventListener("hashchange", () => {
389
+ void emitRouteSnapshot("hashchange");
390
+ });
391
+ }
392
+
393
+ export function emitDeclaredConfig() {
394
+ emitInspectorEvent("declared-config", {
395
+ declaredConfig: cloneJsonSafe(declaredConfig),
396
+ });
397
+ }
398
+
399
+ export async function emitRuntimeBoot(label) {
400
+ attachRouteListeners();
401
+ await ensureShareScopeReady();
402
+ emitDeclaredConfig();
403
+ emitInspectorEvent("runtime-boot", {
404
+ label: label || "boot",
405
+ reactVersion: React.version || null,
406
+ reactDomVersion: ReactDOM.version || null,
407
+ declaredConfig: cloneJsonSafe(declaredConfig),
408
+ });
409
+ emitInspectorEvent("share-snapshot", {
410
+ label: (label || "boot") + ":share-snapshot",
411
+ shareScopes: snapshotShareScopes(),
412
+ });
413
+ }
414
+
415
+ export async function emitRouteSnapshot(label) {
416
+ await ensureShareScopeReady();
417
+ emitInspectorEvent("route-change", {
418
+ label: label || "route",
419
+ shareScopes: snapshotShareScopes(),
420
+ });
421
+ }
422
+
423
+ export function getRemoteInspectorBridge() {
424
+ return {
425
+ app,
426
+ reactVersion: React.version || null,
427
+ reactDomVersion: ReactDOM.version || null,
428
+ reactObject: React,
429
+ reactDomObject: ReactDOM,
430
+ declaredConfig: cloneJsonSafe(declaredConfig),
431
+ shareScopes: snapshotShareScopes(),
432
+ };
433
+ }
434
+
435
+ export function makeInspectableLazy(remoteKey, componentLoad, debugLoad) {
436
+ return async () => {
437
+ await ensureShareScopeReady();
438
+ emitInspectorEvent("remote-load-start", {
439
+ remoteKey,
440
+ shareScopes: snapshotShareScopes(),
441
+ });
442
+ const startedAt =
443
+ typeof performance !== "undefined" && typeof performance.now === "function"
444
+ ? performance.now()
445
+ : Date.now();
446
+
447
+ try {
448
+ const componentModule = await componentLoad();
449
+ const finishedAt =
450
+ typeof performance !== "undefined" && typeof performance.now === "function"
451
+ ? performance.now()
452
+ : Date.now();
453
+
454
+ emitInspectorEvent("remote-load-success", {
455
+ remoteKey,
456
+ durationMs: Number((finishedAt - startedAt).toFixed(2)),
457
+ exportKeys: Object.keys(componentModule || {}),
458
+ shareScopes: snapshotShareScopes(),
459
+ });
460
+
461
+ if (debugLoad) {
462
+ try {
463
+ const debugModule = await debugLoad();
464
+ const bridgeValue =
465
+ typeof debugModule.getRemoteInspectorBridge === "function"
466
+ ? debugModule.getRemoteInspectorBridge()
467
+ : typeof debugModule.default === "function"
468
+ ? debugModule.default()
469
+ : debugModule.default || debugModule;
470
+
471
+ if (bridgeValue) {
472
+ emitInspectorEvent("identity-check", {
473
+ remoteKey,
474
+ remoteApp:
475
+ typeof bridgeValue.app === "string"
476
+ ? bridgeValue.app
477
+ : remoteKey.split("/")[0],
478
+ reactVersion: bridgeValue.reactVersion || null,
479
+ reactDomVersion: bridgeValue.reactDomVersion || null,
480
+ sameReactInstance: bridgeValue.reactObject === React,
481
+ sameReactDomInstance: bridgeValue.reactDomObject === ReactDOM,
482
+ declaredConfig: cloneJsonSafe(bridgeValue.declaredConfig),
483
+ shareScopes: cloneJsonSafe(bridgeValue.shareScopes),
484
+ });
485
+ }
486
+ } catch (error) {
487
+ emitInspectorEvent("identity-check-error", {
488
+ remoteKey,
489
+ message: error instanceof Error ? error.message : String(error),
490
+ shareScopes: snapshotShareScopes(),
491
+ });
492
+ }
493
+ }
494
+
495
+ return componentModule;
496
+ } catch (error) {
497
+ emitInspectorEvent("remote-load-error", {
498
+ remoteKey,
499
+ message: error instanceof Error ? error.message : String(error),
500
+ shareScopes: snapshotShareScopes(),
501
+ });
502
+ throw error;
503
+ }
504
+ };
505
+ }
284
506
  `,
285
507
  "apps/host/src/index.jsx": `import("./bootstrap");
286
508
  `,
287
509
  "apps/host/src/bootstrap.jsx": `import React from "react";
288
510
  import { createRoot } from "react-dom/client";
289
511
  import App from "./App";
512
+ import { emitRuntimeBoot } from "../../shared/mfInspector";
290
513
 
291
514
  const root = createRoot(document.getElementById("root"));
292
515
 
@@ -295,11 +518,26 @@ root.render(
295
518
  <App />
296
519
  </React.StrictMode>,
297
520
  );
521
+
522
+ void emitRuntimeBoot("host-bootstrap");
298
523
  `,
299
524
  "apps/host/src/App.jsx": `import React, { Suspense } from "react";
300
-
301
- const ProfileCard = React.lazy(() => import("profile/ProfileCard"));
302
- const CheckoutPanel = React.lazy(() => import("checkout/CheckoutPanel"));
525
+ import { makeInspectableLazy } from "../../shared/mfInspector";
526
+
527
+ const ProfileCard = React.lazy(
528
+ makeInspectableLazy(
529
+ "profile/ProfileCard",
530
+ () => import("profile/ProfileCard"),
531
+ () => import("profile/InspectorBridge"),
532
+ ),
533
+ );
534
+ const CheckoutPanel = React.lazy(
535
+ makeInspectableLazy(
536
+ "checkout/CheckoutPanel",
537
+ () => import("checkout/CheckoutPanel"),
538
+ () => import("checkout/InspectorBridge"),
539
+ ),
540
+ );
303
541
 
304
542
  function RemoteBoundary({ title, children }) {
305
543
  return (
@@ -366,12 +604,26 @@ export default function App() {
366
604
  }
367
605
  `,
368
606
  "apps/host/webpack.config.js": `const path = require("path");
607
+ const webpack = require("webpack");
369
608
  const HtmlWebpackPlugin = require("html-webpack-plugin");
370
- const { ModuleFederationPlugin } = require("webpack").container;
609
+ const { ModuleFederationPlugin } = webpack.container;
371
610
 
372
611
  const hostPort = Number(process.env.HOST_PORT || 3100);
373
612
  const profilePort = Number(process.env.PROFILE_PORT || 3101);
374
613
  const checkoutPort = Number(process.env.CHECKOUT_PORT || 3102);
614
+ const sharedConfig = {
615
+ react: { singleton: true, requiredVersion: false },
616
+ "react-dom": { singleton: true, requiredVersion: false },
617
+ };
618
+ const remoteConfig = {
619
+ profile: "profile@http://localhost:" + profilePort + "/remoteEntry.js",
620
+ checkout: "checkout@http://localhost:" + checkoutPort + "/remoteEntry.js",
621
+ };
622
+ const inspectorConfig = {
623
+ app: "host",
624
+ remotes: remoteConfig,
625
+ shared: sharedConfig,
626
+ };
375
627
 
376
628
  module.exports = {
377
629
  mode: "development",
@@ -409,16 +661,15 @@ module.exports = {
409
661
  },
410
662
  },
411
663
  plugins: [
664
+ new webpack.DefinePlugin({
665
+ __MF_INSPECTOR_APP__: JSON.stringify("host"),
666
+ __MF_INSPECTOR_SANDBOX_ID__: JSON.stringify(process.env.MF_SANDBOX_ID || ""),
667
+ __MF_INSPECTOR_DECLARED_CONFIG__: JSON.stringify(inspectorConfig),
668
+ }),
412
669
  new ModuleFederationPlugin({
413
670
  name: "host",
414
- remotes: {
415
- profile: "profile@http://localhost:" + profilePort + "/remoteEntry.js",
416
- checkout: "checkout@http://localhost:" + checkoutPort + "/remoteEntry.js",
417
- },
418
- shared: {
419
- react: { singleton: true, requiredVersion: false },
420
- "react-dom": { singleton: true, requiredVersion: false },
421
- },
671
+ remotes: remoteConfig,
672
+ shared: sharedConfig,
422
673
  }),
423
674
  new HtmlWebpackPlugin({
424
675
  template: path.resolve(__dirname, "./public/index.html"),
@@ -446,12 +697,18 @@ module.exports = {
446
697
  "build": "webpack --config webpack.config.js"
447
698
  }
448
699
  }
700
+ `,
701
+ "apps/profile/src/inspectorBridge.js": `import { getRemoteInspectorBridge } from "../../shared/mfInspector";
702
+
703
+ export { getRemoteInspectorBridge };
704
+ export default getRemoteInspectorBridge;
449
705
  `,
450
706
  "apps/profile/src/index.jsx": `import("./bootstrap");
451
707
  `,
452
708
  "apps/profile/src/bootstrap.jsx": `import React from "react";
453
709
  import { createRoot } from "react-dom/client";
454
710
  import App from "./App";
711
+ import { emitRuntimeBoot } from "../../shared/mfInspector";
455
712
 
456
713
  const root = createRoot(document.getElementById("root"));
457
714
 
@@ -460,6 +717,8 @@ root.render(
460
717
  <App />
461
718
  </React.StrictMode>,
462
719
  );
720
+
721
+ void emitRuntimeBoot("profile-bootstrap");
463
722
  `,
464
723
  "apps/profile/src/App.jsx": `import React from "react";
465
724
  import ProfileCard from "./ProfileCard";
@@ -496,10 +755,24 @@ export default function ProfileCard() {
496
755
  }
497
756
  `,
498
757
  "apps/profile/webpack.config.js": `const path = require("path");
758
+ const webpack = require("webpack");
499
759
  const HtmlWebpackPlugin = require("html-webpack-plugin");
500
- const { ModuleFederationPlugin } = require("webpack").container;
760
+ const { ModuleFederationPlugin } = webpack.container;
501
761
 
502
762
  const profilePort = Number(process.env.PROFILE_PORT || 3101);
763
+ const sharedConfig = {
764
+ react: { singleton: true, requiredVersion: false },
765
+ "react-dom": { singleton: true, requiredVersion: false },
766
+ };
767
+ const exposeConfig = {
768
+ "./ProfileCard": path.resolve(__dirname, "./src/ProfileCard.jsx"),
769
+ "./InspectorBridge": path.resolve(__dirname, "./src/inspectorBridge.js"),
770
+ };
771
+ const inspectorConfig = {
772
+ app: "profile",
773
+ exposes: Object.keys(exposeConfig),
774
+ shared: sharedConfig,
775
+ };
503
776
 
504
777
  module.exports = {
505
778
  mode: "development",
@@ -536,16 +809,16 @@ module.exports = {
536
809
  },
537
810
  },
538
811
  plugins: [
812
+ new webpack.DefinePlugin({
813
+ __MF_INSPECTOR_APP__: JSON.stringify("profile"),
814
+ __MF_INSPECTOR_SANDBOX_ID__: JSON.stringify(process.env.MF_SANDBOX_ID || ""),
815
+ __MF_INSPECTOR_DECLARED_CONFIG__: JSON.stringify(inspectorConfig),
816
+ }),
539
817
  new ModuleFederationPlugin({
540
818
  name: "profile",
541
819
  filename: "remoteEntry.js",
542
- exposes: {
543
- "./ProfileCard": path.resolve(__dirname, "./src/ProfileCard.jsx"),
544
- },
545
- shared: {
546
- react: { singleton: true, requiredVersion: false },
547
- "react-dom": { singleton: true, requiredVersion: false },
548
- },
820
+ exposes: exposeConfig,
821
+ shared: sharedConfig,
549
822
  }),
550
823
  new HtmlWebpackPlugin({
551
824
  template: path.resolve(__dirname, "./public/index.html"),
@@ -573,12 +846,18 @@ module.exports = {
573
846
  "build": "webpack --config webpack.config.js"
574
847
  }
575
848
  }
849
+ `,
850
+ "apps/checkout/src/inspectorBridge.js": `import { getRemoteInspectorBridge } from "../../shared/mfInspector";
851
+
852
+ export { getRemoteInspectorBridge };
853
+ export default getRemoteInspectorBridge;
576
854
  `,
577
855
  "apps/checkout/src/index.jsx": `import("./bootstrap");
578
856
  `,
579
857
  "apps/checkout/src/bootstrap.jsx": `import React from "react";
580
858
  import { createRoot } from "react-dom/client";
581
859
  import App from "./App";
860
+ import { emitRuntimeBoot } from "../../shared/mfInspector";
582
861
 
583
862
  const root = createRoot(document.getElementById("root"));
584
863
 
@@ -587,6 +866,8 @@ root.render(
587
866
  <App />
588
867
  </React.StrictMode>,
589
868
  );
869
+
870
+ void emitRuntimeBoot("checkout-bootstrap");
590
871
  `,
591
872
  "apps/checkout/src/App.jsx": `import React from "react";
592
873
  import CheckoutPanel from "./CheckoutPanel";
@@ -631,10 +912,24 @@ export default function CheckoutPanel() {
631
912
  }
632
913
  `,
633
914
  "apps/checkout/webpack.config.js": `const path = require("path");
915
+ const webpack = require("webpack");
634
916
  const HtmlWebpackPlugin = require("html-webpack-plugin");
635
- const { ModuleFederationPlugin } = require("webpack").container;
917
+ const { ModuleFederationPlugin } = webpack.container;
636
918
 
637
919
  const checkoutPort = Number(process.env.CHECKOUT_PORT || 3102);
920
+ const sharedConfig = {
921
+ react: { singleton: true, requiredVersion: false },
922
+ "react-dom": { singleton: true, requiredVersion: false },
923
+ };
924
+ const exposeConfig = {
925
+ "./CheckoutPanel": path.resolve(__dirname, "./src/CheckoutPanel.jsx"),
926
+ "./InspectorBridge": path.resolve(__dirname, "./src/inspectorBridge.js"),
927
+ };
928
+ const inspectorConfig = {
929
+ app: "checkout",
930
+ exposes: Object.keys(exposeConfig),
931
+ shared: sharedConfig,
932
+ };
638
933
 
639
934
  module.exports = {
640
935
  mode: "development",
@@ -671,16 +966,16 @@ module.exports = {
671
966
  },
672
967
  },
673
968
  plugins: [
969
+ new webpack.DefinePlugin({
970
+ __MF_INSPECTOR_APP__: JSON.stringify("checkout"),
971
+ __MF_INSPECTOR_SANDBOX_ID__: JSON.stringify(process.env.MF_SANDBOX_ID || ""),
972
+ __MF_INSPECTOR_DECLARED_CONFIG__: JSON.stringify(inspectorConfig),
973
+ }),
674
974
  new ModuleFederationPlugin({
675
975
  name: "checkout",
676
976
  filename: "remoteEntry.js",
677
- exposes: {
678
- "./CheckoutPanel": path.resolve(__dirname, "./src/CheckoutPanel.jsx"),
679
- },
680
- shared: {
681
- react: { singleton: true, requiredVersion: false },
682
- "react-dom": { singleton: true, requiredVersion: false },
683
- },
977
+ exposes: exposeConfig,
978
+ shared: sharedConfig,
684
979
  }),
685
980
  new HtmlWebpackPlugin({
686
981
  template: path.resolve(__dirname, "./public/index.html"),
@@ -1,30 +1 @@
1
- {
2
- "root": [
3
- "./src/app.tsx",
4
- "./src/api.ts",
5
- "./src/main.tsx",
6
- "./src/store.ts",
7
- "./src/types.ts",
8
- "./src/vite-env.d.ts",
9
- "./src/components/aisettingsmodal.tsx",
10
- "./src/components/annotationdialog.tsx",
11
- "./src/components/chatmessage.tsx",
12
- "./src/components/chatview.tsx",
13
- "./src/components/codecontextpanel.tsx",
14
- "./src/components/codelineannotationpopup.tsx",
15
- "./src/components/coderunnermodal.tsx",
16
- "./src/components/docrefmodal.tsx",
17
- "./src/components/fileattachments.tsx",
18
- "./src/components/filepickermodal.tsx",
19
- "./src/components/fileviewermodal.tsx",
20
- "./src/components/linkedconvospicker.tsx",
21
- "./src/components/markdownrenderer.tsx",
22
- "./src/components/mermaiddiagram.tsx",
23
- "./src/components/plotembed.tsx",
24
- "./src/components/sidebar.tsx",
25
- "./src/components/textannotator.tsx",
26
- "./src/components/vizcraftembed.tsx",
27
- "./src/components/workspaceswitcher.tsx"
28
- ],
29
- "version": "5.9.3"
30
- }
1
+ {"root":["./src/app.tsx","./src/api.ts","./src/infralab.ts","./src/main.tsx","./src/reactlab.ts","./src/store.ts","./src/types.ts","./src/vite-env.d.ts","./src/components/aisettingsmodal.tsx","./src/components/annotationdialog.tsx","./src/components/chatmessage.tsx","./src/components/chatview.tsx","./src/components/codecontextpanel.tsx","./src/components/codelineannotationpopup.tsx","./src/components/coderunnermodal.tsx","./src/components/docrefmodal.tsx","./src/components/fileattachments.tsx","./src/components/filepickermodal.tsx","./src/components/fileviewermodal.tsx","./src/components/infralabmodal.tsx","./src/components/linkedconvospicker.tsx","./src/components/markdownrenderer.tsx","./src/components/mermaiddiagram.tsx","./src/components/notesmodal.tsx","./src/components/plotembed.tsx","./src/components/sidebar.tsx","./src/components/textannotator.tsx","./src/components/vizcraftembed.tsx","./src/components/workspaceswitcher.tsx"],"errors":true,"version":"5.9.3"}
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "0.6.0"
2
+ "version": "0.8.0"
3
3
  }
@@ -2293,6 +2293,7 @@ const NEXT_MODULES_DIR = path.join(NEXT_NPX_DIR, "node_modules");
2293
2293
  const NEXT_SANDBOX_BASE = path.join(NEXT_NPX_DIR, ".sandboxes");
2294
2294
 
2295
2295
  interface ModuleFederationSandboxEntry {
2296
+ id: string;
2296
2297
  child: ReturnType<typeof spawn>;
2297
2298
  dir: string;
2298
2299
  hostUrl: string;
@@ -2526,6 +2527,7 @@ function getModuleFederationCommandEnv(
2526
2527
  HOST_PORT: hostPort,
2527
2528
  PROFILE_PORT: profilePort,
2528
2529
  CHECKOUT_PORT: checkoutPort,
2530
+ MF_SANDBOX_ID: sandbox.id,
2529
2531
  npm_config_update_notifier: "false",
2530
2532
  };
2531
2533
  }
@@ -2877,11 +2879,13 @@ app.post("/api/module-federation/start", async (req, res) => {
2877
2879
  HOST_PORT: String(hostPort),
2878
2880
  PROFILE_PORT: String(profilePort),
2879
2881
  CHECKOUT_PORT: String(checkoutPort),
2882
+ MF_SANDBOX_ID: id,
2880
2883
  npm_config_update_notifier: "false",
2881
2884
  },
2882
2885
  });
2883
2886
 
2884
2887
  const entry: ModuleFederationSandboxEntry = {
2888
+ id,
2885
2889
  child,
2886
2890
  dir,
2887
2891
  hostUrl: appUrls.host,