create-interview-cockpit 0.15.0 → 0.17.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/package.json +1 -1
- package/template/client/package-lock.json +75 -2
- package/template/client/package.json +3 -1
- package/template/client/src/App.tsx +3 -0
- package/template/client/src/browserSecurityTemplates.ts +75 -72
- package/template/client/src/components/CanvasLabModal.tsx +585 -0
- package/template/client/src/components/CodeRunnerModal.tsx +406 -22
- package/template/client/src/components/LabsPanel.tsx +63 -0
- package/template/client/src/components/Sidebar.tsx +12 -1
- package/template/client/src/components/WorkspaceSwitcher.tsx +36 -0
- package/template/client/src/reactLab.ts +1582 -154
- package/template/client/src/store.ts +24 -1
- package/template/client/src/types.ts +3 -1
- package/template/client/vite.config.ts +15 -8
- package/template/cockpit.json +1 -1
- package/template/server/src/google-drive.ts +4 -1
- package/template/server/src/index.ts +197 -53
- package/template/server/src/storage.ts +1 -0
|
@@ -224,7 +224,8 @@ interface Store {
|
|
|
224
224
|
| "infra"
|
|
225
225
|
| "react"
|
|
226
226
|
| "nextjs"
|
|
227
|
-
| "module-federation"
|
|
227
|
+
| "module-federation"
|
|
228
|
+
| "canvas",
|
|
228
229
|
) => Promise<import("./types").ContextFile>;
|
|
229
230
|
clearMessages: (questionId: string) => Promise<void>;
|
|
230
231
|
|
|
@@ -356,6 +357,13 @@ interface Store {
|
|
|
356
357
|
serverCode?: string,
|
|
357
358
|
serverLang?: string,
|
|
358
359
|
) => void;
|
|
360
|
+
|
|
361
|
+
// ── Canvas Lab ───────────────────────────────────────────────
|
|
362
|
+
showCanvasLab: boolean;
|
|
363
|
+
canvasLabInitialCode: string | null;
|
|
364
|
+
canvasLabInitialFileId: string | null;
|
|
365
|
+
openCanvasLab: (code?: string, fileId?: string) => void;
|
|
366
|
+
closeCanvasLab: () => void;
|
|
359
367
|
}
|
|
360
368
|
|
|
361
369
|
export const useStore = create<Store>((set, get) => ({
|
|
@@ -387,6 +395,9 @@ export const useStore = create<Store>((set, get) => ({
|
|
|
387
395
|
showInfraLab: false,
|
|
388
396
|
runnerInitialInfra: null,
|
|
389
397
|
runnerInitialInfraFileId: null,
|
|
398
|
+
showCanvasLab: false,
|
|
399
|
+
canvasLabInitialCode: null,
|
|
400
|
+
canvasLabInitialFileId: null,
|
|
390
401
|
|
|
391
402
|
// ── Workspaces ───────────────────────────────────────────────
|
|
392
403
|
workspaces: [],
|
|
@@ -1076,6 +1087,18 @@ export const useStore = create<Store>((set, get) => ({
|
|
|
1076
1087
|
openBrowserSecurityLab: () => set({ showBrowserSecurityLab: true }),
|
|
1077
1088
|
closeBrowserSecurityLab: () => set({ showBrowserSecurityLab: false }),
|
|
1078
1089
|
closeInfraLab: () => set({ showInfraLab: false }),
|
|
1090
|
+
openCanvasLab: (code?, fileId?) =>
|
|
1091
|
+
set({
|
|
1092
|
+
showCanvasLab: true,
|
|
1093
|
+
canvasLabInitialCode: code ?? null,
|
|
1094
|
+
canvasLabInitialFileId: fileId ?? null,
|
|
1095
|
+
}),
|
|
1096
|
+
closeCanvasLab: () =>
|
|
1097
|
+
set({
|
|
1098
|
+
showCanvasLab: false,
|
|
1099
|
+
canvasLabInitialCode: null,
|
|
1100
|
+
canvasLabInitialFileId: null,
|
|
1101
|
+
}),
|
|
1079
1102
|
|
|
1080
1103
|
fetchAiSettings: async () => {
|
|
1081
1104
|
const settings = await api.fetchAiSettings();
|
|
@@ -7,7 +7,8 @@ export type ContextFileOrigin =
|
|
|
7
7
|
| "infra"
|
|
8
8
|
| "react"
|
|
9
9
|
| "nextjs"
|
|
10
|
-
| "module-federation"
|
|
10
|
+
| "module-federation"
|
|
11
|
+
| "canvas";
|
|
11
12
|
|
|
12
13
|
export interface ContextFile {
|
|
13
14
|
id: string;
|
|
@@ -51,6 +52,7 @@ export interface WorkspaceMeta {
|
|
|
51
52
|
id: string;
|
|
52
53
|
name: string;
|
|
53
54
|
type: "local" | "google_drive";
|
|
55
|
+
questionSortOrder?: "name" | "createdAt";
|
|
54
56
|
driveConfig?: {
|
|
55
57
|
folderId: string;
|
|
56
58
|
folderName: string;
|
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import { defineConfig } from "vite";
|
|
1
|
+
import { defineConfig, loadEnv } from "vite";
|
|
2
2
|
import react from "@vitejs/plugin-react";
|
|
3
3
|
|
|
4
|
-
export default defineConfig({
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
export default defineConfig(({ mode }) => {
|
|
5
|
+
// Load the root .env (one level above client/)
|
|
6
|
+
const env = loadEnv(mode, "../", "");
|
|
7
|
+
const clientPort = parseInt(env.CLIENT_PORT || "5173", 10);
|
|
8
|
+
const serverPort = parseInt(env.PORT || "3001", 10);
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
plugins: [react()],
|
|
12
|
+
server: {
|
|
13
|
+
port: clientPort,
|
|
14
|
+
proxy: {
|
|
15
|
+
"/api": `http://localhost:${serverPort}`,
|
|
16
|
+
},
|
|
10
17
|
},
|
|
11
|
-
}
|
|
18
|
+
};
|
|
12
19
|
});
|
package/template/cockpit.json
CHANGED
|
@@ -330,6 +330,7 @@ export async function syncWorkspace(
|
|
|
330
330
|
let codeContextFiles: string[] = [];
|
|
331
331
|
let codeAnnotations: storage.Question["codeAnnotations"] = {};
|
|
332
332
|
let parentTitle: string | null = null;
|
|
333
|
+
let restoredCreatedAt: string | null = null;
|
|
333
334
|
const restoredContextFiles: storage.ContextFile[] = [];
|
|
334
335
|
|
|
335
336
|
if (filename.endsWith(".json")) {
|
|
@@ -342,6 +343,7 @@ export async function syncWorkspace(
|
|
|
342
343
|
codeContextFiles = parsed.codeContextFiles || [];
|
|
343
344
|
codeAnnotations = parsed.codeAnnotations || {};
|
|
344
345
|
parentTitle = parsed.parentTitle || null;
|
|
346
|
+
if (parsed.createdAt) restoredCreatedAt = parsed.createdAt;
|
|
345
347
|
|
|
346
348
|
// Restore code snippet context files (user/ai origin)
|
|
347
349
|
if (
|
|
@@ -399,7 +401,7 @@ export async function syncWorkspace(
|
|
|
399
401
|
contextFiles: restoredContextFiles,
|
|
400
402
|
messages,
|
|
401
403
|
codeAnnotations,
|
|
402
|
-
createdAt: new Date().toISOString(),
|
|
404
|
+
createdAt: restoredCreatedAt ?? new Date().toISOString(),
|
|
403
405
|
};
|
|
404
406
|
await storage.saveQuestion(q);
|
|
405
407
|
result.filesImported++;
|
|
@@ -798,6 +800,7 @@ export async function exportWorkspace(
|
|
|
798
800
|
codeContextFiles: q.codeContextFiles,
|
|
799
801
|
codeAnnotations: q.codeAnnotations ?? {},
|
|
800
802
|
codeSnippets: contextFilesWithContent,
|
|
803
|
+
createdAt: q.createdAt,
|
|
801
804
|
},
|
|
802
805
|
null,
|
|
803
806
|
2,
|
|
@@ -743,11 +743,12 @@ app.post("/api/questions/:questionId/save-code-snippet", async (req, res) => {
|
|
|
743
743
|
origin !== "infra" &&
|
|
744
744
|
origin !== "react" &&
|
|
745
745
|
origin !== "nextjs" &&
|
|
746
|
-
origin !== "module-federation"
|
|
746
|
+
origin !== "module-federation" &&
|
|
747
|
+
origin !== "canvas"
|
|
747
748
|
) {
|
|
748
749
|
return res.status(400).json({
|
|
749
750
|
error:
|
|
750
|
-
"origin must be 'user', 'ai', 'sandbox', 'browser-security', 'infra', 'react', 'nextjs',
|
|
751
|
+
"origin must be 'user', 'ai', 'sandbox', 'browser-security', 'infra', 'react', 'nextjs', 'module-federation', or 'canvas'",
|
|
751
752
|
});
|
|
752
753
|
}
|
|
753
754
|
try {
|
|
@@ -1640,7 +1641,34 @@ Examples (illustrative only — use real ids and names from the list above):
|
|
|
1640
1641
|
|
|
1641
1642
|
// ─── Code Context Browser ────────────────────────────────
|
|
1642
1643
|
|
|
1643
|
-
|
|
1644
|
+
function parseListEnv(value: string | undefined): string[] {
|
|
1645
|
+
if (!value) return [];
|
|
1646
|
+
return value
|
|
1647
|
+
.split(/[\n,;]/)
|
|
1648
|
+
.map((item) => item.trim())
|
|
1649
|
+
.filter(Boolean);
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
function normalizeName(value: string): string {
|
|
1653
|
+
return path.basename(value).trim().toLowerCase();
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
function normalizePathPrefix(value: string): string {
|
|
1657
|
+
return value
|
|
1658
|
+
.replaceAll("\\", "/")
|
|
1659
|
+
.replace(/^\.\//, "")
|
|
1660
|
+
.replace(/^\/+/, "")
|
|
1661
|
+
.replace(/\/+$/, "")
|
|
1662
|
+
.toLowerCase();
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
function normalizeExtension(value: string): string {
|
|
1666
|
+
const ext = value.trim().toLowerCase();
|
|
1667
|
+
if (!ext) return "";
|
|
1668
|
+
return ext.startsWith(".") ? ext : `.${ext}`;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
const DEFAULT_IGNORE_DIRS = [
|
|
1644
1672
|
"node_modules",
|
|
1645
1673
|
".git",
|
|
1646
1674
|
"dist",
|
|
@@ -1666,70 +1694,130 @@ const IGNORE_DIRS = new Set([
|
|
|
1666
1694
|
"venv",
|
|
1667
1695
|
".venv",
|
|
1668
1696
|
// macOS / editor noise
|
|
1669
|
-
".DS_Store",
|
|
1670
1697
|
".Spotlight-V100",
|
|
1671
1698
|
".Trashes",
|
|
1699
|
+
];
|
|
1700
|
+
|
|
1701
|
+
const DEFAULT_IGNORE_FILES = [
|
|
1702
|
+
".DS_Store",
|
|
1672
1703
|
".env",
|
|
1673
1704
|
".env.local",
|
|
1674
1705
|
".env.development",
|
|
1675
1706
|
".env.production",
|
|
1676
|
-
]
|
|
1707
|
+
];
|
|
1708
|
+
|
|
1709
|
+
const IGNORE_DIRS = new Set(
|
|
1710
|
+
[
|
|
1711
|
+
...DEFAULT_IGNORE_DIRS,
|
|
1712
|
+
...parseListEnv(process.env.CODE_CONTEXT_IGNORE_DIRS),
|
|
1713
|
+
...parseListEnv(process.env.IGNORE_DIRS),
|
|
1714
|
+
].map(normalizeName),
|
|
1715
|
+
);
|
|
1716
|
+
|
|
1717
|
+
const IGNORE_FILES = new Set(
|
|
1718
|
+
[
|
|
1719
|
+
...DEFAULT_IGNORE_FILES,
|
|
1720
|
+
...parseListEnv(process.env.CODE_CONTEXT_IGNORE_FILES),
|
|
1721
|
+
].map(normalizeName),
|
|
1722
|
+
);
|
|
1677
1723
|
|
|
1678
1724
|
// File extensions that are binary or auto-generated — never useful as context
|
|
1679
|
-
const IGNORE_EXTENSIONS = new Set(
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1725
|
+
const IGNORE_EXTENSIONS = new Set(
|
|
1726
|
+
[
|
|
1727
|
+
// .NET compiled outputs
|
|
1728
|
+
".dll",
|
|
1729
|
+
".exe",
|
|
1730
|
+
".pdb",
|
|
1731
|
+
".nupkg",
|
|
1732
|
+
".snupkg",
|
|
1733
|
+
".suo",
|
|
1734
|
+
// Lock / generated
|
|
1735
|
+
".lock",
|
|
1736
|
+
".user",
|
|
1737
|
+
// Images
|
|
1738
|
+
".png",
|
|
1739
|
+
".jpg",
|
|
1740
|
+
".jpeg",
|
|
1741
|
+
".gif",
|
|
1742
|
+
".svg",
|
|
1743
|
+
".ico",
|
|
1744
|
+
".webp",
|
|
1745
|
+
".bmp",
|
|
1746
|
+
// Fonts
|
|
1747
|
+
".woff",
|
|
1748
|
+
".woff2",
|
|
1749
|
+
".ttf",
|
|
1750
|
+
".eot",
|
|
1751
|
+
// Archives / binaries
|
|
1752
|
+
".zip",
|
|
1753
|
+
".tar",
|
|
1754
|
+
".gz",
|
|
1755
|
+
".rar",
|
|
1756
|
+
".7z",
|
|
1757
|
+
".bin",
|
|
1758
|
+
".so",
|
|
1759
|
+
".dylib",
|
|
1760
|
+
".lib",
|
|
1761
|
+
".obj",
|
|
1762
|
+
// Misc
|
|
1763
|
+
".min.js",
|
|
1764
|
+
".d.ts",
|
|
1765
|
+
".map",
|
|
1766
|
+
...parseListEnv(process.env.CODE_CONTEXT_IGNORE_EXTENSIONS),
|
|
1767
|
+
]
|
|
1768
|
+
.map(normalizeExtension)
|
|
1769
|
+
.filter(Boolean),
|
|
1770
|
+
);
|
|
1771
|
+
|
|
1772
|
+
const IGNORE_MULTI_EXTENSIONS = Array.from(IGNORE_EXTENSIONS).filter((ext) =>
|
|
1773
|
+
ext.slice(1).includes("."),
|
|
1774
|
+
);
|
|
1775
|
+
|
|
1776
|
+
const IGNORE_PATH_PREFIXES = new Set(
|
|
1777
|
+
parseListEnv(process.env.CODE_CONTEXT_IGNORE_PATHS)
|
|
1778
|
+
.map(normalizePathPrefix)
|
|
1779
|
+
.filter(Boolean),
|
|
1780
|
+
);
|
|
1781
|
+
|
|
1782
|
+
function shouldIgnorePath(relPath: string): boolean {
|
|
1783
|
+
if (IGNORE_PATH_PREFIXES.size === 0) return false;
|
|
1784
|
+
const normalized = normalizePathPrefix(relPath);
|
|
1785
|
+
if (!normalized) return false;
|
|
1786
|
+
|
|
1787
|
+
for (const prefix of IGNORE_PATH_PREFIXES) {
|
|
1788
|
+
if (normalized === prefix || normalized.startsWith(`${prefix}/`)) {
|
|
1789
|
+
return true;
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
return false;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
function hasIgnoredExtension(fileNameLower: string): boolean {
|
|
1796
|
+
if (IGNORE_EXTENSIONS.has(path.extname(fileNameLower))) {
|
|
1797
|
+
return true;
|
|
1798
|
+
}
|
|
1799
|
+
for (const ext of IGNORE_MULTI_EXTENSIONS) {
|
|
1800
|
+
if (fileNameLower.endsWith(ext)) {
|
|
1801
|
+
return true;
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
return false;
|
|
1805
|
+
}
|
|
1719
1806
|
|
|
1720
1807
|
async function walkDir(dir: string, prefix = ""): Promise<string[]> {
|
|
1721
1808
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
1722
1809
|
const files: string[] = [];
|
|
1723
1810
|
for (const entry of entries) {
|
|
1724
|
-
if (IGNORE_DIRS.has(entry.name)) continue;
|
|
1725
1811
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
1812
|
+
const lowerName = entry.name.toLowerCase();
|
|
1813
|
+
|
|
1726
1814
|
if (entry.isDirectory()) {
|
|
1815
|
+
if (IGNORE_DIRS.has(lowerName) || shouldIgnorePath(rel)) continue;
|
|
1727
1816
|
files.push(...(await walkDir(path.join(dir, entry.name), rel)));
|
|
1728
1817
|
} else {
|
|
1818
|
+
if (IGNORE_FILES.has(lowerName) || shouldIgnorePath(rel)) continue;
|
|
1729
1819
|
const lower = entry.name.toLowerCase();
|
|
1730
|
-
if (
|
|
1731
|
-
// Also catch compound extensions like .min.js, .d.ts generated files
|
|
1732
|
-
if (lower.endsWith(".min.js") || lower.endsWith(".d.ts")) continue;
|
|
1820
|
+
if (hasIgnoredExtension(lower)) continue;
|
|
1733
1821
|
files.push(rel);
|
|
1734
1822
|
}
|
|
1735
1823
|
}
|
|
@@ -2972,7 +3060,7 @@ app.post("/api/module-federation/start", async (req, res) => {
|
|
|
2972
3060
|
|
|
2973
3061
|
await runLoggedCommand(
|
|
2974
3062
|
npmCommand(),
|
|
2975
|
-
["install", "--no-audit", "--no-fund", "--
|
|
3063
|
+
["install", "--no-audit", "--no-fund", "--legacy-peer-deps"],
|
|
2976
3064
|
{
|
|
2977
3065
|
cwd: dir,
|
|
2978
3066
|
env: {
|
|
@@ -2983,12 +3071,36 @@ app.post("/api/module-federation/start", async (req, res) => {
|
|
|
2983
3071
|
logs,
|
|
2984
3072
|
);
|
|
2985
3073
|
|
|
3074
|
+
// Detect new modern Next.js MFE template types.
|
|
3075
|
+
const isMultiZones = typeof files["apps/zone-b/package.json"] === "string";
|
|
3076
|
+
const isMfRuntimeApi =
|
|
3077
|
+
typeof files["apps/mf-remote/package.json"] === "string";
|
|
3078
|
+
const isRspackShell =
|
|
3079
|
+
typeof files["apps/rspack-shell/package.json"] === "string";
|
|
3080
|
+
|
|
2986
3081
|
// Detect isolated 2-app pattern (host + mfe-auth, no profile/checkout).
|
|
2987
3082
|
const isIsolated =
|
|
2988
3083
|
typeof files["apps/mfe-auth/package.json"] === "string" &&
|
|
2989
3084
|
typeof files["apps/checkout/package.json"] !== "string";
|
|
2990
3085
|
|
|
2991
|
-
|
|
3086
|
+
// Detect Next.js MF pattern (shell + remote, no mfe-auth/checkout/profile).
|
|
3087
|
+
// Excludes Multi-Zones (zone-b) and MF Runtime API (mf-remote) which also use apps/shell.
|
|
3088
|
+
const isNextjsMf =
|
|
3089
|
+
typeof files["apps/shell/package.json"] === "string" &&
|
|
3090
|
+
!isMultiZones &&
|
|
3091
|
+
!isMfRuntimeApi &&
|
|
3092
|
+
typeof files["apps/mfe-auth/package.json"] !== "string" &&
|
|
3093
|
+
typeof files["apps/checkout/package.json"] !== "string";
|
|
3094
|
+
|
|
3095
|
+
const ports = await getDistinctPorts(
|
|
3096
|
+
isIsolated ||
|
|
3097
|
+
isNextjsMf ||
|
|
3098
|
+
isMultiZones ||
|
|
3099
|
+
isMfRuntimeApi ||
|
|
3100
|
+
isRspackShell
|
|
3101
|
+
? 2
|
|
3102
|
+
: 3,
|
|
3103
|
+
);
|
|
2992
3104
|
const [hostPort] = ports;
|
|
2993
3105
|
|
|
2994
3106
|
const appUrls: Record<string, string> = {
|
|
@@ -3001,7 +3113,32 @@ app.post("/api/module-federation/start", async (req, res) => {
|
|
|
3001
3113
|
npm_config_update_notifier: "false",
|
|
3002
3114
|
};
|
|
3003
3115
|
|
|
3004
|
-
if (
|
|
3116
|
+
if (isNextjsMf) {
|
|
3117
|
+
const [, remotePort] = ports;
|
|
3118
|
+
appUrls.remote = `http://localhost:${remotePort}`;
|
|
3119
|
+
spawnEnv.REMOTE_PORT = String(remotePort);
|
|
3120
|
+
// Next.js remotes put the entry at /_next/static/chunks/; webpack remotes put it at root.
|
|
3121
|
+
const isNextjsRemote =
|
|
3122
|
+
typeof files["apps/remote/next.config.js"] === "string";
|
|
3123
|
+
const remoteEntryPath = isNextjsRemote
|
|
3124
|
+
? "/_next/static/chunks/remoteEntry.js"
|
|
3125
|
+
: "/remoteEntry.js";
|
|
3126
|
+
spawnEnv.NEXT_PUBLIC_REMOTE_URL = `http://localhost:${remotePort}${remoteEntryPath}`;
|
|
3127
|
+
} else if (isMultiZones) {
|
|
3128
|
+
const [, remotePort] = ports;
|
|
3129
|
+
// Zone B serves everything under /store (basePath), so point the preview there.
|
|
3130
|
+
appUrls.zoneB = `http://localhost:${remotePort}/store`;
|
|
3131
|
+
spawnEnv.REMOTE_PORT = String(remotePort);
|
|
3132
|
+
} else if (isMfRuntimeApi) {
|
|
3133
|
+
const [, remotePort] = ports;
|
|
3134
|
+
appUrls.mfRemote = `http://localhost:${remotePort}`;
|
|
3135
|
+
spawnEnv.REMOTE_PORT = String(remotePort);
|
|
3136
|
+
spawnEnv.NEXT_PUBLIC_REMOTE_URL = `http://localhost:${remotePort}/remoteEntry.js`;
|
|
3137
|
+
} else if (isRspackShell) {
|
|
3138
|
+
const [, remotePort] = ports;
|
|
3139
|
+
appUrls.remote = `http://localhost:${remotePort}`;
|
|
3140
|
+
spawnEnv.REMOTE_PORT = String(remotePort);
|
|
3141
|
+
} else if (isIsolated) {
|
|
3005
3142
|
const [, mfeAuthPort] = ports;
|
|
3006
3143
|
appUrls.mfeAuth = `http://localhost:${mfeAuthPort}`;
|
|
3007
3144
|
spawnEnv.MFE_AUTH_PORT = String(mfeAuthPort);
|
|
@@ -3014,7 +3151,14 @@ app.post("/api/module-federation/start", async (req, res) => {
|
|
|
3014
3151
|
}
|
|
3015
3152
|
|
|
3016
3153
|
const readyPorts = new Set<string>();
|
|
3017
|
-
const requiredPorts =
|
|
3154
|
+
const requiredPorts =
|
|
3155
|
+
isIsolated ||
|
|
3156
|
+
isNextjsMf ||
|
|
3157
|
+
isMultiZones ||
|
|
3158
|
+
isMfRuntimeApi ||
|
|
3159
|
+
isRspackShell
|
|
3160
|
+
? 2
|
|
3161
|
+
: 3;
|
|
3018
3162
|
|
|
3019
3163
|
const child = spawn(npmCommand(), ["run", "dev"], {
|
|
3020
3164
|
cwd: dir,
|