create-interview-cockpit 0.14.0 → 0.16.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/src/App.tsx +3 -0
- package/template/client/src/api.ts +39 -0
- package/template/client/src/browserSecurityTemplates.ts +3242 -0
- package/template/client/src/components/BrowserSecurityLabModal.tsx +1510 -0
- package/template/client/src/components/CanvasLabModal.tsx +585 -0
- package/template/client/src/components/CodeRunnerModal.tsx +406 -55
- package/template/client/src/components/LabsPanel.tsx +120 -7
- package/template/client/src/components/LinkedConvosPicker.tsx +121 -63
- package/template/client/src/components/Sidebar.tsx +125 -1
- package/template/client/src/components/WorkspaceSwitcher.tsx +36 -0
- package/template/client/src/reactLab.ts +1206 -0
- package/template/client/src/store.ts +39 -2
- package/template/client/src/types.ts +5 -1
- package/template/client/tsconfig.tsbuildinfo +1 -1
- package/template/client/vite.config.ts +15 -8
- package/template/cockpit.json +1 -1
- package/template/server/src/google-drive.ts +6 -1
- package/template/server/src/index.ts +110 -6
- package/template/server/src/storage.ts +3 -0
|
@@ -220,10 +220,12 @@ interface Store {
|
|
|
220
220
|
| "user"
|
|
221
221
|
| "ai"
|
|
222
222
|
| "sandbox"
|
|
223
|
+
| "browser-security"
|
|
223
224
|
| "infra"
|
|
224
225
|
| "react"
|
|
225
226
|
| "nextjs"
|
|
226
|
-
| "module-federation"
|
|
227
|
+
| "module-federation"
|
|
228
|
+
| "canvas",
|
|
227
229
|
) => Promise<import("./types").ContextFile>;
|
|
228
230
|
clearMessages: (questionId: string) => Promise<void>;
|
|
229
231
|
|
|
@@ -283,6 +285,8 @@ interface Store {
|
|
|
283
285
|
clientCode: string;
|
|
284
286
|
clientLang: string;
|
|
285
287
|
fileId?: string;
|
|
288
|
+
label?: string;
|
|
289
|
+
origin?: "sandbox" | "browser-security";
|
|
286
290
|
/** If set, the client panel opens in React or Next.js preview mode instead of script mode */
|
|
287
291
|
clientType?: "script" | "react" | "nextjs" | "module-federation";
|
|
288
292
|
reactFiles?: Record<string, string> | null;
|
|
@@ -298,6 +302,8 @@ interface Store {
|
|
|
298
302
|
clientLang: string,
|
|
299
303
|
fileId?: string,
|
|
300
304
|
opts?: {
|
|
305
|
+
label?: string;
|
|
306
|
+
origin?: "sandbox" | "browser-security";
|
|
301
307
|
clientType?: "script" | "react" | "nextjs" | "module-federation";
|
|
302
308
|
reactFiles?: Record<string, string>;
|
|
303
309
|
reactActiveFile?: string;
|
|
@@ -320,6 +326,11 @@ interface Store {
|
|
|
320
326
|
openDeploymentLab: () => void;
|
|
321
327
|
closeDeploymentLab: () => void;
|
|
322
328
|
|
|
329
|
+
// ── Browser Security Lab ─────────────────────────────────────
|
|
330
|
+
showBrowserSecurityLab: boolean;
|
|
331
|
+
openBrowserSecurityLab: () => void;
|
|
332
|
+
closeBrowserSecurityLab: () => void;
|
|
333
|
+
|
|
323
334
|
// ── Infra Lab ────────────────────────────────────────────────
|
|
324
335
|
showInfraLab: boolean;
|
|
325
336
|
runnerInitialInfra: InfraLabWorkspace | null;
|
|
@@ -346,6 +357,13 @@ interface Store {
|
|
|
346
357
|
serverCode?: string,
|
|
347
358
|
serverLang?: string,
|
|
348
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;
|
|
349
367
|
}
|
|
350
368
|
|
|
351
369
|
export const useStore = create<Store>((set, get) => ({
|
|
@@ -373,9 +391,13 @@ export const useStore = create<Store>((set, get) => ({
|
|
|
373
391
|
runnerInitialSandbox: null,
|
|
374
392
|
runnerInitialFileId: null,
|
|
375
393
|
showDeploymentLab: false,
|
|
394
|
+
showBrowserSecurityLab: false,
|
|
376
395
|
showInfraLab: false,
|
|
377
396
|
runnerInitialInfra: null,
|
|
378
397
|
runnerInitialInfraFileId: null,
|
|
398
|
+
showCanvasLab: false,
|
|
399
|
+
canvasLabInitialCode: null,
|
|
400
|
+
canvasLabInitialFileId: null,
|
|
379
401
|
|
|
380
402
|
// ── Workspaces ───────────────────────────────────────────────
|
|
381
403
|
workspaces: [],
|
|
@@ -971,6 +993,8 @@ export const useStore = create<Store>((set, get) => ({
|
|
|
971
993
|
clientCode,
|
|
972
994
|
clientLang,
|
|
973
995
|
fileId,
|
|
996
|
+
label: opts?.label,
|
|
997
|
+
origin: opts?.origin,
|
|
974
998
|
clientType: opts?.clientType,
|
|
975
999
|
reactFiles: opts?.reactFiles,
|
|
976
1000
|
reactActiveFile: opts?.reactActiveFile,
|
|
@@ -1058,10 +1082,23 @@ export const useStore = create<Store>((set, get) => ({
|
|
|
1058
1082
|
}));
|
|
1059
1083
|
},
|
|
1060
1084
|
closeCodeRunner: () => set({ showCodeRunner: false }),
|
|
1061
|
-
showDeploymentLab: false,
|
|
1062
1085
|
openDeploymentLab: () => set({ showDeploymentLab: true }),
|
|
1063
1086
|
closeDeploymentLab: () => set({ showDeploymentLab: false }),
|
|
1087
|
+
openBrowserSecurityLab: () => set({ showBrowserSecurityLab: true }),
|
|
1088
|
+
closeBrowserSecurityLab: () => set({ showBrowserSecurityLab: false }),
|
|
1064
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
|
+
}),
|
|
1065
1102
|
|
|
1066
1103
|
fetchAiSettings: async () => {
|
|
1067
1104
|
const settings = await api.fetchAiSettings();
|
|
@@ -3,10 +3,12 @@ export type ContextFileOrigin =
|
|
|
3
3
|
| "ai"
|
|
4
4
|
| "upload"
|
|
5
5
|
| "sandbox"
|
|
6
|
+
| "browser-security"
|
|
6
7
|
| "infra"
|
|
7
8
|
| "react"
|
|
8
9
|
| "nextjs"
|
|
9
|
-
| "module-federation"
|
|
10
|
+
| "module-federation"
|
|
11
|
+
| "canvas";
|
|
10
12
|
|
|
11
13
|
export interface ContextFile {
|
|
12
14
|
id: string;
|
|
@@ -17,6 +19,7 @@ export interface ContextFile {
|
|
|
17
19
|
/** Distinguishes how this file was added. 'upload' = user-uploaded doc,
|
|
18
20
|
* 'user' = code saved from Code Runner, 'ai' = AI-generated code block,
|
|
19
21
|
* 'sandbox' = paired server+client sandbox saved as JSON,
|
|
22
|
+
* 'browser-security' = paired server+client security lab saved as JSON,
|
|
20
23
|
* 'infra' = Terraform-style infra lab workspace saved as JSON. */
|
|
21
24
|
origin?: ContextFileOrigin;
|
|
22
25
|
/** Language hint for code snippets (e.g. 'typescript', 'javascript'). */
|
|
@@ -49,6 +52,7 @@ export interface WorkspaceMeta {
|
|
|
49
52
|
id: string;
|
|
50
53
|
name: string;
|
|
51
54
|
type: "local" | "google_drive";
|
|
55
|
+
questionSortOrder?: "name" | "createdAt";
|
|
52
56
|
driveConfig?: {
|
|
53
57
|
folderId: string;
|
|
54
58
|
folderName: string;
|
|
@@ -1 +1 @@
|
|
|
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
|
+
{"root":["./src/app.tsx","./src/api.ts","./src/browsersecuritytemplates.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/browsersecuritylabmodal.tsx","./src/components/chatmessage.tsx","./src/components/chatview.tsx","./src/components/codecontextpanel.tsx","./src/components/codelineannotationpopup.tsx","./src/components/coderunnermodal.tsx","./src/components/deploymentlabmodal.tsx","./src/components/docrefmodal.tsx","./src/components/fileattachments.tsx","./src/components/filepickermodal.tsx","./src/components/fileviewermodal.tsx","./src/components/infralabmodal.tsx","./src/components/labspanel.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,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 (
|
|
@@ -358,6 +360,7 @@ export async function syncWorkspace(
|
|
|
358
360
|
(cs.origin === "user" ||
|
|
359
361
|
cs.origin === "ai" ||
|
|
360
362
|
cs.origin === "sandbox" ||
|
|
363
|
+
cs.origin === "browser-security" ||
|
|
361
364
|
cs.origin === "react" ||
|
|
362
365
|
cs.origin === "nextjs" ||
|
|
363
366
|
cs.origin === "module-federation" ||
|
|
@@ -398,7 +401,7 @@ export async function syncWorkspace(
|
|
|
398
401
|
contextFiles: restoredContextFiles,
|
|
399
402
|
messages,
|
|
400
403
|
codeAnnotations,
|
|
401
|
-
createdAt: new Date().toISOString(),
|
|
404
|
+
createdAt: restoredCreatedAt ?? new Date().toISOString(),
|
|
402
405
|
};
|
|
403
406
|
await storage.saveQuestion(q);
|
|
404
407
|
result.filesImported++;
|
|
@@ -769,6 +772,7 @@ export async function exportWorkspace(
|
|
|
769
772
|
cf.origin === "user" ||
|
|
770
773
|
cf.origin === "ai" ||
|
|
771
774
|
cf.origin === "sandbox" ||
|
|
775
|
+
cf.origin === "browser-security" ||
|
|
772
776
|
cf.origin === "react" ||
|
|
773
777
|
cf.origin === "nextjs" ||
|
|
774
778
|
cf.origin === "module-federation" ||
|
|
@@ -796,6 +800,7 @@ export async function exportWorkspace(
|
|
|
796
800
|
codeContextFiles: q.codeContextFiles,
|
|
797
801
|
codeAnnotations: q.codeAnnotations ?? {},
|
|
798
802
|
codeSnippets: contextFilesWithContent,
|
|
803
|
+
createdAt: q.createdAt,
|
|
799
804
|
},
|
|
800
805
|
null,
|
|
801
806
|
2,
|
|
@@ -726,6 +726,7 @@ app.post("/api/questions/:questionId/save-code-snippet", async (req, res) => {
|
|
|
726
726
|
| "user"
|
|
727
727
|
| "ai"
|
|
728
728
|
| "sandbox"
|
|
729
|
+
| "browser-security"
|
|
729
730
|
| "infra"
|
|
730
731
|
| "react"
|
|
731
732
|
| "nextjs"
|
|
@@ -738,14 +739,16 @@ app.post("/api/questions/:questionId/save-code-snippet", async (req, res) => {
|
|
|
738
739
|
origin !== "user" &&
|
|
739
740
|
origin !== "ai" &&
|
|
740
741
|
origin !== "sandbox" &&
|
|
742
|
+
origin !== "browser-security" &&
|
|
741
743
|
origin !== "infra" &&
|
|
742
744
|
origin !== "react" &&
|
|
743
745
|
origin !== "nextjs" &&
|
|
744
|
-
origin !== "module-federation"
|
|
746
|
+
origin !== "module-federation" &&
|
|
747
|
+
origin !== "canvas"
|
|
745
748
|
) {
|
|
746
749
|
return res.status(400).json({
|
|
747
750
|
error:
|
|
748
|
-
"origin must be 'user', 'ai', 'sandbox', 'infra', 'react', 'nextjs',
|
|
751
|
+
"origin must be 'user', 'ai', 'sandbox', 'browser-security', 'infra', 'react', 'nextjs', 'module-federation', or 'canvas'",
|
|
749
752
|
});
|
|
750
753
|
}
|
|
751
754
|
try {
|
|
@@ -2885,6 +2888,51 @@ app.post("/api/nextjs/:id/update-files", async (req, res) => {
|
|
|
2885
2888
|
res.json({ ok: true });
|
|
2886
2889
|
});
|
|
2887
2890
|
|
|
2891
|
+
app.post("/api/nextjs/:id/command-stream", async (req, res) => {
|
|
2892
|
+
const sb = nextSandboxes.get(req.params.id);
|
|
2893
|
+
|
|
2894
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
2895
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
2896
|
+
res.setHeader("Connection", "keep-alive");
|
|
2897
|
+
res.flushHeaders();
|
|
2898
|
+
|
|
2899
|
+
const send = (payload: unknown) => {
|
|
2900
|
+
res.write(`data: ${JSON.stringify(payload)}\n\n`);
|
|
2901
|
+
};
|
|
2902
|
+
|
|
2903
|
+
if (!sb) {
|
|
2904
|
+
send({ type: "error", error: "Sandbox not found" });
|
|
2905
|
+
res.end();
|
|
2906
|
+
return;
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2909
|
+
const { command } = req.body as { command?: string };
|
|
2910
|
+
if (typeof command !== "string" || !command.trim()) {
|
|
2911
|
+
send({ type: "error", error: "command is required" });
|
|
2912
|
+
res.end();
|
|
2913
|
+
return;
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2916
|
+
try {
|
|
2917
|
+
const parsed = parseReactLabCommand(command);
|
|
2918
|
+
send({ type: "output", kind: "info", text: `$ ${command.trim()}\n` });
|
|
2919
|
+
await runStreamedCommand(
|
|
2920
|
+
npmCommand(),
|
|
2921
|
+
parsed.args,
|
|
2922
|
+
{
|
|
2923
|
+
cwd: sb.dir,
|
|
2924
|
+
env: { ...process.env, npm_config_update_notifier: "false" },
|
|
2925
|
+
},
|
|
2926
|
+
({ kind, text }) => send({ type: "output", kind, text }),
|
|
2927
|
+
);
|
|
2928
|
+
send({ type: "complete" });
|
|
2929
|
+
} catch (error: any) {
|
|
2930
|
+
send({ type: "error", error: error?.message || "Command failed" });
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
res.end();
|
|
2934
|
+
});
|
|
2935
|
+
|
|
2888
2936
|
app.get("/api/nextjs/:id/status", (req, res) => {
|
|
2889
2937
|
const sb = nextSandboxes.get(req.params.id);
|
|
2890
2938
|
if (!sb) return res.json({ running: false });
|
|
@@ -2925,7 +2973,7 @@ app.post("/api/module-federation/start", async (req, res) => {
|
|
|
2925
2973
|
|
|
2926
2974
|
await runLoggedCommand(
|
|
2927
2975
|
npmCommand(),
|
|
2928
|
-
["install", "--no-audit", "--no-fund", "--
|
|
2976
|
+
["install", "--no-audit", "--no-fund", "--legacy-peer-deps"],
|
|
2929
2977
|
{
|
|
2930
2978
|
cwd: dir,
|
|
2931
2979
|
env: {
|
|
@@ -2936,12 +2984,36 @@ app.post("/api/module-federation/start", async (req, res) => {
|
|
|
2936
2984
|
logs,
|
|
2937
2985
|
);
|
|
2938
2986
|
|
|
2987
|
+
// Detect new modern Next.js MFE template types.
|
|
2988
|
+
const isMultiZones = typeof files["apps/zone-b/package.json"] === "string";
|
|
2989
|
+
const isMfRuntimeApi =
|
|
2990
|
+
typeof files["apps/mf-remote/package.json"] === "string";
|
|
2991
|
+
const isRspackShell =
|
|
2992
|
+
typeof files["apps/rspack-shell/package.json"] === "string";
|
|
2993
|
+
|
|
2939
2994
|
// Detect isolated 2-app pattern (host + mfe-auth, no profile/checkout).
|
|
2940
2995
|
const isIsolated =
|
|
2941
2996
|
typeof files["apps/mfe-auth/package.json"] === "string" &&
|
|
2942
2997
|
typeof files["apps/checkout/package.json"] !== "string";
|
|
2943
2998
|
|
|
2944
|
-
|
|
2999
|
+
// Detect Next.js MF pattern (shell + remote, no mfe-auth/checkout/profile).
|
|
3000
|
+
// Excludes Multi-Zones (zone-b) and MF Runtime API (mf-remote) which also use apps/shell.
|
|
3001
|
+
const isNextjsMf =
|
|
3002
|
+
typeof files["apps/shell/package.json"] === "string" &&
|
|
3003
|
+
!isMultiZones &&
|
|
3004
|
+
!isMfRuntimeApi &&
|
|
3005
|
+
typeof files["apps/mfe-auth/package.json"] !== "string" &&
|
|
3006
|
+
typeof files["apps/checkout/package.json"] !== "string";
|
|
3007
|
+
|
|
3008
|
+
const ports = await getDistinctPorts(
|
|
3009
|
+
isIsolated ||
|
|
3010
|
+
isNextjsMf ||
|
|
3011
|
+
isMultiZones ||
|
|
3012
|
+
isMfRuntimeApi ||
|
|
3013
|
+
isRspackShell
|
|
3014
|
+
? 2
|
|
3015
|
+
: 3,
|
|
3016
|
+
);
|
|
2945
3017
|
const [hostPort] = ports;
|
|
2946
3018
|
|
|
2947
3019
|
const appUrls: Record<string, string> = {
|
|
@@ -2954,7 +3026,32 @@ app.post("/api/module-federation/start", async (req, res) => {
|
|
|
2954
3026
|
npm_config_update_notifier: "false",
|
|
2955
3027
|
};
|
|
2956
3028
|
|
|
2957
|
-
if (
|
|
3029
|
+
if (isNextjsMf) {
|
|
3030
|
+
const [, remotePort] = ports;
|
|
3031
|
+
appUrls.remote = `http://localhost:${remotePort}`;
|
|
3032
|
+
spawnEnv.REMOTE_PORT = String(remotePort);
|
|
3033
|
+
// Next.js remotes put the entry at /_next/static/chunks/; webpack remotes put it at root.
|
|
3034
|
+
const isNextjsRemote =
|
|
3035
|
+
typeof files["apps/remote/next.config.js"] === "string";
|
|
3036
|
+
const remoteEntryPath = isNextjsRemote
|
|
3037
|
+
? "/_next/static/chunks/remoteEntry.js"
|
|
3038
|
+
: "/remoteEntry.js";
|
|
3039
|
+
spawnEnv.NEXT_PUBLIC_REMOTE_URL = `http://localhost:${remotePort}${remoteEntryPath}`;
|
|
3040
|
+
} else if (isMultiZones) {
|
|
3041
|
+
const [, remotePort] = ports;
|
|
3042
|
+
// Zone B serves everything under /store (basePath), so point the preview there.
|
|
3043
|
+
appUrls.zoneB = `http://localhost:${remotePort}/store`;
|
|
3044
|
+
spawnEnv.REMOTE_PORT = String(remotePort);
|
|
3045
|
+
} else if (isMfRuntimeApi) {
|
|
3046
|
+
const [, remotePort] = ports;
|
|
3047
|
+
appUrls.mfRemote = `http://localhost:${remotePort}`;
|
|
3048
|
+
spawnEnv.REMOTE_PORT = String(remotePort);
|
|
3049
|
+
spawnEnv.NEXT_PUBLIC_REMOTE_URL = `http://localhost:${remotePort}/remoteEntry.js`;
|
|
3050
|
+
} else if (isRspackShell) {
|
|
3051
|
+
const [, remotePort] = ports;
|
|
3052
|
+
appUrls.remote = `http://localhost:${remotePort}`;
|
|
3053
|
+
spawnEnv.REMOTE_PORT = String(remotePort);
|
|
3054
|
+
} else if (isIsolated) {
|
|
2958
3055
|
const [, mfeAuthPort] = ports;
|
|
2959
3056
|
appUrls.mfeAuth = `http://localhost:${mfeAuthPort}`;
|
|
2960
3057
|
spawnEnv.MFE_AUTH_PORT = String(mfeAuthPort);
|
|
@@ -2967,7 +3064,14 @@ app.post("/api/module-federation/start", async (req, res) => {
|
|
|
2967
3064
|
}
|
|
2968
3065
|
|
|
2969
3066
|
const readyPorts = new Set<string>();
|
|
2970
|
-
const requiredPorts =
|
|
3067
|
+
const requiredPorts =
|
|
3068
|
+
isIsolated ||
|
|
3069
|
+
isNextjsMf ||
|
|
3070
|
+
isMultiZones ||
|
|
3071
|
+
isMfRuntimeApi ||
|
|
3072
|
+
isRspackShell
|
|
3073
|
+
? 2
|
|
3074
|
+
: 3;
|
|
2971
3075
|
|
|
2972
3076
|
const child = spawn(npmCommand(), ["run", "dev"], {
|
|
2973
3077
|
cwd: dir,
|
|
@@ -59,6 +59,7 @@ export interface ContextFile {
|
|
|
59
59
|
/** Distinguishes how this file was added. 'upload' = user-uploaded doc,
|
|
60
60
|
* 'user' = code saved from Code Runner, 'ai' = AI-generated code block,
|
|
61
61
|
* 'sandbox' = paired server+client sandbox saved as JSON,
|
|
62
|
+
* 'browser-security' = paired server+client security lab saved as JSON,
|
|
62
63
|
* 'infra' = Terraform-style infra lab workspace saved as JSON,
|
|
63
64
|
* 'react' = React + TypeScript lab workspace,
|
|
64
65
|
* 'nextjs' = Next.js App Router lab workspace,
|
|
@@ -68,6 +69,7 @@ export interface ContextFile {
|
|
|
68
69
|
| "ai"
|
|
69
70
|
| "upload"
|
|
70
71
|
| "sandbox"
|
|
72
|
+
| "browser-security"
|
|
71
73
|
| "infra"
|
|
72
74
|
| "react"
|
|
73
75
|
| "nextjs"
|
|
@@ -152,6 +154,7 @@ export interface WorkspaceMeta {
|
|
|
152
154
|
id: string;
|
|
153
155
|
name: string;
|
|
154
156
|
type: "local" | "google_drive";
|
|
157
|
+
questionSortOrder?: "name" | "createdAt";
|
|
155
158
|
driveConfig?: DriveConfig;
|
|
156
159
|
createdAt: string;
|
|
157
160
|
}
|