@pulse-editor/cli 0.1.2 → 0.1.4
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/dist/app.js +2 -1
- package/dist/components/commands/code.d.ts +5 -0
- package/dist/components/commands/code.js +366 -0
- package/dist/lib/cli-flags.d.ts +4 -0
- package/dist/lib/cli-flags.js +4 -0
- package/dist/lib/manual.js +12 -0
- package/dist/lib/server/express.js +3 -0
- package/dist/lib/server/preview/frontend/index.js +63 -6
- package/dist/lib/webpack/configs/mf-client.js +7 -7
- package/dist/lib/webpack/configs/mf-server.js +2 -2
- package/dist/lib/webpack/configs/preview.js +7 -7
- package/package.json +3 -1
- package/dist/lib/server/utils.d.ts +0 -1
- package/dist/lib/server/utils.js +0 -17
- package/dist/lib/webpack/dist/pregistered-actions.d.ts +0 -2
- package/dist/lib/webpack/dist/pulse.config.d.ts +0 -7
- package/dist/lib/webpack/dist/src/lib/agents/code-modifier-agent.d.ts +0 -2
- package/dist/lib/webpack/dist/src/lib/agents/vibe-coding-agent.d.ts +0 -3
- package/dist/lib/webpack/dist/src/lib/mcp/utils.d.ts +0 -3
- package/dist/lib/webpack/dist/src/lib/streaming/message-stream-controller.d.ts +0 -10
- package/dist/lib/webpack/dist/src/lib/types.d.ts +0 -58
- package/dist/lib/webpack/dist/src/server-function/generate-code/v1/generate.d.ts +0 -5
- package/dist/lib/webpack/dist/src/server-function/generate-code/v2/generate.d.ts +0 -1
- package/dist/lib/webpack/tsconfig.server.json +0 -19
- package/dist/lib/webpack/webpack.config.d.ts +0 -2
- package/dist/lib/webpack/webpack.config.js +0 -527
package/dist/app.js
CHANGED
|
@@ -14,6 +14,7 @@ import Start from './components/commands/start.js';
|
|
|
14
14
|
import Clean from './components/commands/clean.js';
|
|
15
15
|
import Upgrade from './components/commands/upgrade.js';
|
|
16
16
|
import Skill from './components/commands/skill.js';
|
|
17
|
+
import Code from './components/commands/code.js';
|
|
17
18
|
export default function App({ cli }) {
|
|
18
19
|
const [command, setCommand] = useState(undefined);
|
|
19
20
|
if (cli.flags.stage) {
|
|
@@ -26,5 +27,5 @@ export default function App({ cli }) {
|
|
|
26
27
|
const cmd = cli.input[0] ?? 'help';
|
|
27
28
|
setCommand(cmd);
|
|
28
29
|
}, [cli.input]);
|
|
29
|
-
return (_jsxs(_Fragment, { children: [cli.flags.stage && (_jsx(Text, { color: 'yellow', children: "\u26A0\uFE0F You are in development mode." })), command === 'help' ? (_jsx(Help, { cli: cli })) : command === 'chat' ? (_jsx(Chat, { cli: cli })) : command === 'login' ? (_jsx(Login, { cli: cli })) : command === 'logout' ? (_jsx(Logout, { cli: cli })) : command === 'publish' ? (_jsx(Publish, { cli: cli })) : command === 'create' ? (_jsx(Create, { cli: cli })) : command === 'dev' ? (_jsx(Dev, { cli: cli })) : command === 'build' ? (_jsx(Build, { cli: cli })) : command === 'preview' ? (_jsx(Preview, { cli: cli })) : command === 'start' ? (_jsx(Start, { cli: cli })) : command === 'clean' ? (_jsx(Clean, { cli: cli })) : command === 'upgrade' ? (_jsx(Upgrade, { cli: cli })) : command === 'skill' ? (_jsx(Skill, { cli: cli })) : (command !== undefined && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: 'redBright', children: ["Invalid command: ", command] }), _jsxs(Text, { children: ["Run ", _jsx(Text, { color: 'blueBright', children: "pulse help" }), " to see the list of available commands."] })] })))] }));
|
|
30
|
+
return (_jsxs(_Fragment, { children: [cli.flags.stage && (_jsx(Text, { color: 'yellow', children: "\u26A0\uFE0F You are in development mode." })), command === 'help' ? (_jsx(Help, { cli: cli })) : command === 'chat' ? (_jsx(Chat, { cli: cli })) : command === 'login' ? (_jsx(Login, { cli: cli })) : command === 'logout' ? (_jsx(Logout, { cli: cli })) : command === 'publish' ? (_jsx(Publish, { cli: cli })) : command === 'create' ? (_jsx(Create, { cli: cli })) : command === 'dev' ? (_jsx(Dev, { cli: cli })) : command === 'build' ? (_jsx(Build, { cli: cli })) : command === 'preview' ? (_jsx(Preview, { cli: cli })) : command === 'start' ? (_jsx(Start, { cli: cli })) : command === 'clean' ? (_jsx(Clean, { cli: cli })) : command === 'upgrade' ? (_jsx(Upgrade, { cli: cli })) : command === 'skill' ? (_jsx(Skill, { cli: cli })) : command === 'code' ? (_jsx(Code, { cli: cli })) : (command !== undefined && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: 'redBright', children: ["Invalid command: ", command] }), _jsxs(Text, { children: ["Run ", _jsx(Text, { color: 'blueBright', children: "pulse help" }), " to see the list of available commands."] })] })))] }));
|
|
30
31
|
}
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { Box, Text } from "ink";
|
|
4
|
+
import Spinner from "ink-spinner";
|
|
5
|
+
import TextInput from "ink-text-input";
|
|
6
|
+
import JSZip from "jszip";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
9
|
+
import { getBackendUrl } from "../../lib/backend-url.js";
|
|
10
|
+
import { checkToken, getToken } from "../../lib/token.js";
|
|
11
|
+
function readContinueData(cwd) {
|
|
12
|
+
const pkgPath = path.resolve(cwd, "package.json");
|
|
13
|
+
if (!fs.existsSync(pkgPath)) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
18
|
+
const name = typeof pkg["name"] === "string" ? pkg["name"] : undefined;
|
|
19
|
+
const version = typeof pkg["version"] === "string" ? pkg["version"] : undefined;
|
|
20
|
+
const appId = typeof pkg["appId"] === "string" ? pkg["appId"] : name;
|
|
21
|
+
if (!appId || !version) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return { appId, version, name: name ?? appId };
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function makeAppName(prompt) {
|
|
31
|
+
const normalized = prompt
|
|
32
|
+
.toLowerCase()
|
|
33
|
+
.replaceAll(/[^a-z0-9\s-]/g, "")
|
|
34
|
+
.trim()
|
|
35
|
+
.split(/\s+/)
|
|
36
|
+
.filter(Boolean)
|
|
37
|
+
.slice(0, 5)
|
|
38
|
+
.join("_");
|
|
39
|
+
const suffix = Math.random().toString(36).slice(2, 8);
|
|
40
|
+
return `${normalized || "pulse_app"}_${suffix}`;
|
|
41
|
+
}
|
|
42
|
+
function parseArtifact(result) {
|
|
43
|
+
if (!result) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(result);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export default function Code({ cli }) {
|
|
54
|
+
// ── Continue mode ────────────────────────────────────────────────────────
|
|
55
|
+
const continueData = useMemo(() => {
|
|
56
|
+
if (!cli.flags.continue)
|
|
57
|
+
return undefined;
|
|
58
|
+
return readContinueData(process.cwd());
|
|
59
|
+
}, [cli.flags.continue]);
|
|
60
|
+
// ── Resolved inputs ──────────────────────────────────────────────────────
|
|
61
|
+
const [appName, setAppName] = useState(() => {
|
|
62
|
+
if (cli.flags.name)
|
|
63
|
+
return cli.flags.name;
|
|
64
|
+
if (cli.flags.continue) {
|
|
65
|
+
const data = readContinueData(process.cwd());
|
|
66
|
+
return data?.name ?? undefined;
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
});
|
|
70
|
+
const [appNameInput, setAppNameInput] = useState("");
|
|
71
|
+
const [prompt, setPrompt] = useState(undefined);
|
|
72
|
+
const [promptInput, setPromptInput] = useState("");
|
|
73
|
+
// ── Auth ─────────────────────────────────────────────────────────────────
|
|
74
|
+
const [isCheckingAuth, setIsCheckingAuth] = useState(true);
|
|
75
|
+
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
76
|
+
// ── Generation state ─────────────────────────────────────────────────────
|
|
77
|
+
const [isGenerating, setIsGenerating] = useState(false);
|
|
78
|
+
const [isGenerated, setIsGenerated] = useState(false);
|
|
79
|
+
const [error, setError] = useState(undefined);
|
|
80
|
+
const [elapsedSeconds, setElapsedSeconds] = useState(0);
|
|
81
|
+
const generationStartRef = useRef(undefined);
|
|
82
|
+
const [statusLines, setStatusLines] = useState([]);
|
|
83
|
+
// Per-message live streaming buffers
|
|
84
|
+
const [liveBuffers, setLiveBuffers] = useState(new Map());
|
|
85
|
+
const [toolCallErrors, setToolCallErrors] = useState([]);
|
|
86
|
+
const [artifact, setArtifact] = useState(undefined);
|
|
87
|
+
const [isDownloading, setIsDownloading] = useState(false);
|
|
88
|
+
const [downloadedPath, setDownloadedPath] = useState(undefined);
|
|
89
|
+
const [downloadError, setDownloadError] = useState(undefined);
|
|
90
|
+
const token = useMemo(() => getToken(cli.flags.stage), [cli.flags.stage]);
|
|
91
|
+
// Pre-fill prompt from inline CLI args, e.g. `pulse code "my prompt"`
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
const initialPrompt = cli.input.slice(1).join(" ").trim();
|
|
94
|
+
if (initialPrompt) {
|
|
95
|
+
setPrompt(initialPrompt);
|
|
96
|
+
}
|
|
97
|
+
}, [cli.input]);
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
async function runAuthCheck() {
|
|
100
|
+
if (!token) {
|
|
101
|
+
setIsAuthenticated(false);
|
|
102
|
+
setIsCheckingAuth(false);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const isValid = await checkToken(token, cli.flags.stage);
|
|
106
|
+
setIsAuthenticated(isValid);
|
|
107
|
+
setIsCheckingAuth(false);
|
|
108
|
+
}
|
|
109
|
+
runAuthCheck();
|
|
110
|
+
}, [token, cli.flags.stage]);
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
async function runCodeGeneration(userPrompt, resolvedAppName) {
|
|
113
|
+
if (!token) {
|
|
114
|
+
setError("Missing access token. Please run pulse login first.");
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
setError(undefined);
|
|
118
|
+
setStatusLines([]);
|
|
119
|
+
setLiveBuffers(new Map());
|
|
120
|
+
setToolCallErrors([]);
|
|
121
|
+
setArtifact(undefined);
|
|
122
|
+
setIsDownloading(false);
|
|
123
|
+
setDownloadedPath(undefined);
|
|
124
|
+
setDownloadError(undefined);
|
|
125
|
+
generationStartRef.current = Date.now();
|
|
126
|
+
setElapsedSeconds(0);
|
|
127
|
+
setIsGenerating(true);
|
|
128
|
+
const controller = new AbortController();
|
|
129
|
+
let didTimeout = false;
|
|
130
|
+
const timeoutId = setTimeout(() => {
|
|
131
|
+
didTimeout = true;
|
|
132
|
+
controller.abort("Generation timeout after 5 minutes");
|
|
133
|
+
}, 5 * 60 * 1000);
|
|
134
|
+
const collectedMessages = new Map();
|
|
135
|
+
// Per-message live streaming buffers
|
|
136
|
+
const localBuffers = new Map();
|
|
137
|
+
let _artifact = undefined;
|
|
138
|
+
try {
|
|
139
|
+
const response = await fetch(`${getBackendUrl(cli.flags.stage)}/api/server-function/vibe_dev_flow/latest/generate-code/v2/generate`, {
|
|
140
|
+
method: "POST",
|
|
141
|
+
headers: {
|
|
142
|
+
Authorization: `Bearer ${token}`,
|
|
143
|
+
"Content-Type": "application/json",
|
|
144
|
+
},
|
|
145
|
+
body: JSON.stringify({
|
|
146
|
+
prompt: userPrompt,
|
|
147
|
+
...(continueData
|
|
148
|
+
? {
|
|
149
|
+
appId: continueData.appId,
|
|
150
|
+
version: continueData.version,
|
|
151
|
+
}
|
|
152
|
+
: {
|
|
153
|
+
appName: resolvedAppName,
|
|
154
|
+
}),
|
|
155
|
+
}),
|
|
156
|
+
signal: controller.signal,
|
|
157
|
+
});
|
|
158
|
+
if (!response.ok) {
|
|
159
|
+
throw new Error(`Code generation failed: ${response.status} ${response.statusText} - ${await response.text()}`);
|
|
160
|
+
}
|
|
161
|
+
if (!response.body) {
|
|
162
|
+
throw new Error("No stream returned from server.");
|
|
163
|
+
}
|
|
164
|
+
const reader = response.body.getReader();
|
|
165
|
+
const decoder = new TextDecoder();
|
|
166
|
+
let buffer = "";
|
|
167
|
+
while (true) {
|
|
168
|
+
const { value, done } = await reader.read();
|
|
169
|
+
if (done) {
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
buffer += decoder.decode(value, { stream: true });
|
|
173
|
+
const parts = buffer.split("\n\n");
|
|
174
|
+
buffer = parts.pop() ?? "";
|
|
175
|
+
for (const part of parts) {
|
|
176
|
+
if (!part.startsWith("data:")) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const json = part.replace(/^data:\s*/, "");
|
|
180
|
+
let message;
|
|
181
|
+
try {
|
|
182
|
+
message = JSON.parse(json);
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (message.type === "creation") {
|
|
188
|
+
collectedMessages.set(message.messageId, message);
|
|
189
|
+
if (message.data.type === "notification") {
|
|
190
|
+
const text = [message.data.title, message.data.description]
|
|
191
|
+
.filter(Boolean)
|
|
192
|
+
.join(": ");
|
|
193
|
+
if (text.length > 0) {
|
|
194
|
+
setStatusLines((previous) => [...previous, { text }]);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (message.data.type === "toolCall") {
|
|
198
|
+
const label = message.data.title
|
|
199
|
+
? `Tool call: ${message.data.title}`
|
|
200
|
+
: "Tool call executed";
|
|
201
|
+
setStatusLines((previous) => [
|
|
202
|
+
...previous,
|
|
203
|
+
{
|
|
204
|
+
text: label,
|
|
205
|
+
messageId: message.messageId,
|
|
206
|
+
toolTitle: message.data.title,
|
|
207
|
+
},
|
|
208
|
+
]);
|
|
209
|
+
// Start tracking this tool call's buffer
|
|
210
|
+
localBuffers.set(message.messageId, message.data.description
|
|
211
|
+
? `${message.data.description}\n`
|
|
212
|
+
: "");
|
|
213
|
+
setLiveBuffers(new Map(localBuffers));
|
|
214
|
+
}
|
|
215
|
+
if (message.data.type === "artifactOutput") {
|
|
216
|
+
_artifact = parseArtifact(message.data.result);
|
|
217
|
+
setArtifact(_artifact);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
// update message
|
|
222
|
+
const existing = collectedMessages.get(message.messageId);
|
|
223
|
+
if (!existing) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
const deltaResult = message.delta.result ?? "";
|
|
227
|
+
const deltaError = message.delta.error ?? "";
|
|
228
|
+
existing.data.result = (existing.data.result ?? "") + deltaResult;
|
|
229
|
+
existing.data.error = (existing.data.error ?? "") + deltaError;
|
|
230
|
+
existing.isFinal = message.isFinal;
|
|
231
|
+
// Accumulate delta into this message's own buffer
|
|
232
|
+
if (localBuffers.has(message.messageId) &&
|
|
233
|
+
deltaResult.length > 0) {
|
|
234
|
+
const current = localBuffers.get(message.messageId) ?? "";
|
|
235
|
+
localBuffers.set(message.messageId, current + deltaResult);
|
|
236
|
+
setLiveBuffers(new Map(localBuffers));
|
|
237
|
+
}
|
|
238
|
+
// Surface tool-level errors when the message is finalized
|
|
239
|
+
if (message.isFinal && existing.data.error) {
|
|
240
|
+
const errText = existing.data.error.trim();
|
|
241
|
+
if (errText.length > 0) {
|
|
242
|
+
setToolCallErrors((previous) => [...previous, errText]);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
setIsGenerated(true);
|
|
249
|
+
// Download the source zip
|
|
250
|
+
if (_artifact && _artifact.appId) {
|
|
251
|
+
setIsDownloading(true);
|
|
252
|
+
try {
|
|
253
|
+
// Step 1: exchange the archive endpoint for a SAS URL
|
|
254
|
+
const sasEndpoint = `${getBackendUrl(cli.flags.stage)}/api/app/source?app=${encodeURIComponent(_artifact.appId)}`;
|
|
255
|
+
const sasResponse = await fetch(sasEndpoint, {
|
|
256
|
+
headers: {
|
|
257
|
+
Accept: "application/zip",
|
|
258
|
+
Authorization: `Bearer ${token}`,
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
if (!sasResponse.ok) {
|
|
262
|
+
throw new Error(`Failed to get download link for ${_artifact.appId}: ${sasResponse.status} ${sasResponse.statusText}`);
|
|
263
|
+
}
|
|
264
|
+
const { url: sasUrl } = (await sasResponse.json());
|
|
265
|
+
console.log(`SAS URL for ${_artifact.appId}: ${sasUrl}`);
|
|
266
|
+
// Step 2: download the actual zip from the SAS URL
|
|
267
|
+
const downloadResponse = await fetch(sasUrl);
|
|
268
|
+
if (!downloadResponse.ok) {
|
|
269
|
+
throw new Error(`Failed to download archive: ${downloadResponse.status} ${downloadResponse.statusText}`);
|
|
270
|
+
}
|
|
271
|
+
const arrayBuffer = await downloadResponse.arrayBuffer();
|
|
272
|
+
// Extract zip in-memory and write files one by one
|
|
273
|
+
const zip = await JSZip.loadAsync(arrayBuffer);
|
|
274
|
+
const destDir = process.cwd();
|
|
275
|
+
for (const [relativePath, zipEntry] of Object.entries(zip.files)) {
|
|
276
|
+
if (zipEntry.dir)
|
|
277
|
+
continue;
|
|
278
|
+
const filePath = path.join(destDir, relativePath);
|
|
279
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
280
|
+
const content = await zipEntry.async("nodebuffer");
|
|
281
|
+
fs.writeFileSync(filePath, content);
|
|
282
|
+
}
|
|
283
|
+
setDownloadedPath(destDir);
|
|
284
|
+
}
|
|
285
|
+
catch (downloadErr) {
|
|
286
|
+
setDownloadError(downloadErr instanceof Error
|
|
287
|
+
? downloadErr.message
|
|
288
|
+
: "Failed to download source archive.");
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
setIsDownloading(false);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch (generationError) {
|
|
296
|
+
if (controller.signal.aborted && didTimeout) {
|
|
297
|
+
setError("Code generation timed out after 5 minutes.");
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
setError(generationError instanceof Error
|
|
301
|
+
? generationError.message
|
|
302
|
+
: "Failed to generate code.");
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
finally {
|
|
306
|
+
clearTimeout(timeoutId);
|
|
307
|
+
if (generationStartRef.current !== undefined) {
|
|
308
|
+
setElapsedSeconds(Math.floor((Date.now() - generationStartRef.current) / 1000));
|
|
309
|
+
}
|
|
310
|
+
setIsGenerating(false);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (isAuthenticated && prompt && appName && continueData !== null) {
|
|
314
|
+
runCodeGeneration(prompt, appName);
|
|
315
|
+
}
|
|
316
|
+
}, [prompt, appName, cli.flags.stage, isAuthenticated, token, continueData]);
|
|
317
|
+
useEffect(() => {
|
|
318
|
+
if (!isGenerating)
|
|
319
|
+
return;
|
|
320
|
+
const interval = setInterval(() => {
|
|
321
|
+
if (generationStartRef.current !== undefined) {
|
|
322
|
+
setElapsedSeconds(Math.floor((Date.now() - generationStartRef.current) / 1000));
|
|
323
|
+
}
|
|
324
|
+
}, 1000);
|
|
325
|
+
return () => clearInterval(interval);
|
|
326
|
+
}, [isGenerating]);
|
|
327
|
+
function formatElapsed(seconds) {
|
|
328
|
+
if (seconds < 60)
|
|
329
|
+
return `${seconds}s`;
|
|
330
|
+
const m = Math.floor(seconds / 60);
|
|
331
|
+
const s = seconds % 60;
|
|
332
|
+
return `${m}m ${String(s).padStart(2, "0")}s`;
|
|
333
|
+
}
|
|
334
|
+
function getLiveLines(messageId, toolTitle) {
|
|
335
|
+
const text = liveBuffers.get(messageId);
|
|
336
|
+
if (!text)
|
|
337
|
+
return { lines: [], truncated: false };
|
|
338
|
+
const all = text.split("\n").filter((l) => l.length > 0);
|
|
339
|
+
if (toolTitle === "Agent Message") {
|
|
340
|
+
return { lines: all, truncated: false };
|
|
341
|
+
}
|
|
342
|
+
const truncated = all.length > 8;
|
|
343
|
+
return { lines: all.slice(-8), truncated };
|
|
344
|
+
}
|
|
345
|
+
return (_jsx(_Fragment, { children: isCheckingAuth ? (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Checking authentication..." })] })) : !isAuthenticated ? (_jsx(Text, { color: "redBright", children: "\u26D4 You are not authenticated. Run pulse login and try again." })) : cli.flags.continue && continueData === null ? (_jsx(Text, { color: "redBright", children: "\u26D4 Could not read app name and version from package.json in the current directory. Make sure a valid package.json with \"name\", \"version\" (and optionally \"appId\") exists." })) : appName === undefined ? (
|
|
346
|
+
// Step 1 — app name
|
|
347
|
+
_jsxs(Box, { children: [_jsx(Text, { children: "App name: " }), _jsx(TextInput, { value: appNameInput, onChange: setAppNameInput, onSubmit: (value) => {
|
|
348
|
+
const trimmed = value.trim();
|
|
349
|
+
setAppName(trimmed.length > 0 ? trimmed : makeAppName(""));
|
|
350
|
+
} })] })) : prompt === undefined ? (
|
|
351
|
+
// Step 2 — prompt
|
|
352
|
+
_jsxs(Box, { children: [_jsx(Text, { children: "Describe the Pulse app you want to generate: " }), _jsx(TextInput, { value: promptInput, onChange: setPromptInput, onSubmit: (value) => {
|
|
353
|
+
const trimmed = value.trim();
|
|
354
|
+
if (trimmed.length === 0) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
setPrompt(trimmed);
|
|
358
|
+
} })] })) : (
|
|
359
|
+
// Step 3 — generating
|
|
360
|
+
_jsxs(_Fragment, { children: [cli.flags.continue && continueData && (_jsxs(Text, { dimColor: true, children: ["Continuing from v", continueData.version, " (", continueData.appId, ")"] })), _jsxs(Text, { children: ["App name: ", _jsx(Text, { color: "yellow", children: appName })] }), _jsxs(Text, { children: ["Prompt: ", _jsx(Text, { color: "cyan", children: prompt })] }), isGenerating && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Generating app... " }), _jsxs(Text, { color: "gray", children: ["[", formatElapsed(elapsedSeconds), "]"] })] })), statusLines.map((item, index) => {
|
|
361
|
+
const { lines, truncated } = item.messageId !== undefined
|
|
362
|
+
? getLiveLines(item.messageId, item.toolTitle)
|
|
363
|
+
: { lines: [], truncated: false };
|
|
364
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: index < statusLines.length - 1, children: ["\u2022 ", item.text] }), lines.length > 0 && (_jsxs(Box, { borderStyle: "round", borderColor: "gray", flexDirection: "column", paddingX: 1, children: [truncated && (_jsx(Text, { color: "gray", dimColor: true, children: "<truncated>" })), lines.map((line, lineIndex) => (_jsx(Text, { color: "gray", children: line }, lineIndex)))] }))] }, `${item.text}-${index}`));
|
|
365
|
+
}), toolCallErrors.map((err, index) => (_jsxs(Text, { color: "redBright", children: ["\u274C ", err] }, index))), error && _jsxs(Text, { color: "redBright", children: ["\u274C ", error] }), isGenerated && !error && (_jsxs(Text, { color: "greenBright", children: ["\u2705 Code generation completed in ", formatElapsed(elapsedSeconds), "."] })), artifact?.publishedAppLink && (_jsxs(Text, { color: "greenBright", children: ["Preview: ", artifact.publishedAppLink] })), artifact?.sourceCodeArchiveLink && (_jsxs(Text, { color: "greenBright", children: ["Source (.zip): ", artifact.sourceCodeArchiveLink] })), isDownloading && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Downloading source archive..." })] })), downloadedPath && (_jsxs(Text, { color: "greenBright", children: ["Source extracted: ", downloadedPath] })), downloadError && (_jsxs(Text, { color: "redBright", children: ["\u274C Download failed: ", downloadError] }))] })) }));
|
|
366
|
+
}
|
package/dist/lib/cli-flags.d.ts
CHANGED
package/dist/lib/cli-flags.js
CHANGED
package/dist/lib/manual.js
CHANGED
|
@@ -9,6 +9,17 @@ const chat = `\
|
|
|
9
9
|
--interactive, -i
|
|
10
10
|
Start an interactive chat session
|
|
11
11
|
|
|
12
|
+
`;
|
|
13
|
+
const code = `\
|
|
14
|
+
code Generate a new Pulse App from a prompt using AI.
|
|
15
|
+
|
|
16
|
+
Flags:
|
|
17
|
+
--name, -n [app-name]
|
|
18
|
+
Optional app name used for generation.
|
|
19
|
+
--continue, -c
|
|
20
|
+
Continue building on an existing app. Reads the app ID
|
|
21
|
+
and version from the package.json in the current directory.
|
|
22
|
+
|
|
12
23
|
`;
|
|
13
24
|
const login = `\
|
|
14
25
|
login Login to the Pulse Editor Platform.
|
|
@@ -101,6 +112,7 @@ const skill = `\
|
|
|
101
112
|
export const commandsManual = {
|
|
102
113
|
help,
|
|
103
114
|
chat,
|
|
115
|
+
code,
|
|
104
116
|
login,
|
|
105
117
|
logout,
|
|
106
118
|
publish,
|
|
@@ -97,6 +97,9 @@ app.all(/^\/server-function\/(.*)/, async (req, res) => {
|
|
|
97
97
|
});
|
|
98
98
|
if (isPreview) {
|
|
99
99
|
/* Preview mode */
|
|
100
|
+
app.get("/pulse.config.json", (_req, res) => {
|
|
101
|
+
res.sendFile(path.resolve("dist/pulse.config.json"));
|
|
102
|
+
});
|
|
100
103
|
app.use(express.static("dist/client"));
|
|
101
104
|
// Expose skill actions as REST API endpoints in dev and preview modes
|
|
102
105
|
const skillActions = pulseConfig?.actions || [];
|
|
@@ -1,10 +1,67 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/* This folder contains temporary code to be moved to a different package in the future. */
|
|
3
|
+
import React from 'react';
|
|
3
4
|
import ReactDOM from 'react-dom/client';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return
|
|
5
|
+
function ErrorPage({ error }) {
|
|
6
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7
|
+
const stack = error instanceof Error ? error.stack : undefined;
|
|
8
|
+
return (_jsxs("div", { style: {
|
|
9
|
+
display: 'flex',
|
|
10
|
+
flexDirection: 'column',
|
|
11
|
+
alignItems: 'center',
|
|
12
|
+
justifyContent: 'center',
|
|
13
|
+
height: '100vh',
|
|
14
|
+
width: '100vw',
|
|
15
|
+
fontFamily: 'monospace',
|
|
16
|
+
backgroundColor: '#1a1a1a',
|
|
17
|
+
color: '#ff6b6b',
|
|
18
|
+
padding: '2rem',
|
|
19
|
+
boxSizing: 'border-box',
|
|
20
|
+
}, children: [_jsx("div", { style: { fontSize: '3rem', marginBottom: '1rem' }, children: "\u26A0" }), _jsx("h1", { style: { margin: '0 0 0.5rem', fontSize: '1.5rem', color: '#ff6b6b' }, children: "Rendering Error" }), _jsx("p", { style: { margin: '0 0 1.5rem', color: '#aaa', fontSize: '0.9rem' }, children: "An error occurred while rendering the preview." }), _jsxs("pre", { style: {
|
|
21
|
+
background: '#2a2a2a',
|
|
22
|
+
border: '1px solid #444',
|
|
23
|
+
borderRadius: '6px',
|
|
24
|
+
padding: '1rem',
|
|
25
|
+
maxWidth: '100%',
|
|
26
|
+
overflowX: 'auto',
|
|
27
|
+
color: '#ff9999',
|
|
28
|
+
fontSize: '0.85rem',
|
|
29
|
+
whiteSpace: 'pre-wrap',
|
|
30
|
+
wordBreak: 'break-word',
|
|
31
|
+
}, children: [message, stack ? `\n\n${stack}` : ''] })] }));
|
|
32
|
+
}
|
|
33
|
+
class ErrorBoundary extends React.Component {
|
|
34
|
+
constructor(props) {
|
|
35
|
+
super(props);
|
|
36
|
+
this.state = { error: null };
|
|
37
|
+
}
|
|
38
|
+
static getDerivedStateFromError(error) {
|
|
39
|
+
return { error };
|
|
40
|
+
}
|
|
41
|
+
render() {
|
|
42
|
+
if (this.state.error) {
|
|
43
|
+
return _jsx(ErrorPage, { error: this.state.error });
|
|
44
|
+
}
|
|
45
|
+
return this.props.children;
|
|
46
|
+
}
|
|
8
47
|
}
|
|
9
48
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
|
10
|
-
|
|
49
|
+
function showError(error) {
|
|
50
|
+
root.render(_jsx(ErrorPage, { error: error }));
|
|
51
|
+
}
|
|
52
|
+
// Fallback: catch errors that slip past the React error boundary
|
|
53
|
+
// (e.g. React 18 re-throws initial-render errors as uncaught)
|
|
54
|
+
window.addEventListener('error', (event) => {
|
|
55
|
+
showError(event.error ?? new Error(event.message));
|
|
56
|
+
});
|
|
57
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
58
|
+
showError(event.reason instanceof Error ? event.reason : new Error(String(event.reason)));
|
|
59
|
+
});
|
|
60
|
+
// Use dynamic import so module-level errors in main.tsx are also caught
|
|
61
|
+
// @ts-ignore
|
|
62
|
+
import("../../../../../src/main.tsx")
|
|
63
|
+
.then((mod) => {
|
|
64
|
+
const Main = mod.default;
|
|
65
|
+
root.render(_jsx(ErrorBoundary, { children: _jsx(Main, {}) }));
|
|
66
|
+
})
|
|
67
|
+
.catch(showError);
|
|
@@ -33,13 +33,13 @@ class MFClientPlugin {
|
|
|
33
33
|
compiler.hooks.invalid.tap("LogFileUpdates", (file, changeTime) => {
|
|
34
34
|
console.log(`[watch] change detected in: ${file} at ${new Date(changeTime || Date.now()).toLocaleTimeString()}`);
|
|
35
35
|
});
|
|
36
|
-
const devStartupMessage = `
|
|
37
|
-
🎉 Your Pulse extension \x1b[1m${this.pulseConfig.displayName}\x1b[0m is LIVE!
|
|
38
|
-
|
|
39
|
-
⚡️ Local: http://localhost:3030/${this.pulseConfig.id}/${this.pulseConfig.version}/
|
|
40
|
-
⚡️ Network: http://${this.origin}:3030/${this.pulseConfig.id}/${this.pulseConfig.version}/
|
|
41
|
-
|
|
42
|
-
✨ Try it out in the Pulse Editor and let the magic happen! 🚀
|
|
36
|
+
const devStartupMessage = `
|
|
37
|
+
🎉 Your Pulse extension \x1b[1m${this.pulseConfig.displayName}\x1b[0m is LIVE!
|
|
38
|
+
|
|
39
|
+
⚡️ Local: http://localhost:3030/${this.pulseConfig.id}/${this.pulseConfig.version}/
|
|
40
|
+
⚡️ Network: http://${this.origin}:3030/${this.pulseConfig.id}/${this.pulseConfig.version}/
|
|
41
|
+
|
|
42
|
+
✨ Try it out in the Pulse Editor and let the magic happen! 🚀
|
|
43
43
|
`;
|
|
44
44
|
// After build finishes
|
|
45
45
|
compiler.hooks.done.tap("ReloadMessagePlugin", () => {
|
|
@@ -57,12 +57,12 @@ class MFServerPlugin {
|
|
|
57
57
|
this.compileAppActionSkills();
|
|
58
58
|
console.log(`[Server] ✅ Successfully built server.`);
|
|
59
59
|
const funcs = discoverServerFunctions();
|
|
60
|
-
console.log(`\n🛜 Server functions:
|
|
60
|
+
console.log(`\n🛜 Server functions:
|
|
61
61
|
${Object.entries(funcs)
|
|
62
62
|
.map(([name, file]) => {
|
|
63
63
|
return ` - ${name.slice(2)} (from ${file})`;
|
|
64
64
|
})
|
|
65
|
-
.join("\n")}
|
|
65
|
+
.join("\n")}
|
|
66
66
|
`);
|
|
67
67
|
}
|
|
68
68
|
});
|
|
@@ -28,13 +28,13 @@ class PreviewClientPlugin {
|
|
|
28
28
|
// After build finishes
|
|
29
29
|
compiler.hooks.done.tap("ReloadMessagePlugin", () => {
|
|
30
30
|
if (isFirstRun) {
|
|
31
|
-
const previewStartupMessage = `
|
|
32
|
-
🎉 Your Pulse extension preview \x1b[1m${this.pulseConfig.displayName}\x1b[0m is LIVE!
|
|
33
|
-
|
|
34
|
-
⚡️ Local: http://localhost:3030
|
|
35
|
-
⚡️ Network: http://${this.origin}:3030
|
|
36
|
-
|
|
37
|
-
✨ Try it out in your browser and let the magic happen! 🚀
|
|
31
|
+
const previewStartupMessage = `
|
|
32
|
+
🎉 Your Pulse extension preview \x1b[1m${this.pulseConfig.displayName}\x1b[0m is LIVE!
|
|
33
|
+
|
|
34
|
+
⚡️ Local: http://localhost:3030
|
|
35
|
+
⚡️ Network: http://${this.origin}:3030
|
|
36
|
+
|
|
37
|
+
✨ Try it out in your browser and let the magic happen! 🚀
|
|
38
38
|
`;
|
|
39
39
|
console.log("[client-preview] ✅ Successfully built preview.");
|
|
40
40
|
const skillActions = this.pulseConfig?.actions || [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pulse-editor/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bin": {
|
|
6
6
|
"pulse": "dist/cli.js"
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"ink-select-input": "^6.2.0",
|
|
38
38
|
"ink-spinner": "^5.0.0",
|
|
39
39
|
"ink-text-input": "^6.0.0",
|
|
40
|
+
"jszip": "^3.10.1",
|
|
40
41
|
"livereload": "^0.10.3",
|
|
41
42
|
"meow": "^14.1.0",
|
|
42
43
|
"mini-css-extract-plugin": "^2.10.0",
|
|
@@ -52,6 +53,7 @@
|
|
|
52
53
|
"@types/connect-livereload": "^0.6.3",
|
|
53
54
|
"@types/cors": "^2.8.19",
|
|
54
55
|
"@types/express": "^5.0.6",
|
|
56
|
+
"@types/jszip": "^3.4.0",
|
|
55
57
|
"@types/livereload": "^0.9.5",
|
|
56
58
|
"@types/react": "^19.2.14",
|
|
57
59
|
"@types/react-dom": "^19.2.3",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function readConfigFile(): Promise<any>;
|
package/dist/lib/server/utils.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
export async function readConfigFile() {
|
|
3
|
-
// Read pulse.config.json from dist/client
|
|
4
|
-
// Wait until dist/pulse.config.json exists
|
|
5
|
-
while (true) {
|
|
6
|
-
try {
|
|
7
|
-
await fs.access('dist/pulse.config.json');
|
|
8
|
-
break;
|
|
9
|
-
}
|
|
10
|
-
catch (err) {
|
|
11
|
-
// Wait for 100ms before trying again
|
|
12
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
const data = await fs.readFile('dist/pulse.config.json', 'utf-8');
|
|
16
|
-
return JSON.parse(data);
|
|
17
|
-
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
|
|
2
|
-
import { MessageStreamController } from "../streaming/message-stream-controller";
|
|
3
|
-
export declare function runVibeCoding(mcpClient: MultiServerMCPClient, userPrompt: string, controller: MessageStreamController): Promise<void>;
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
|
|
2
|
-
export declare function writeFileToFS(mcpClient: MultiServerMCPClient, uri: string, content: string): Promise<string>;
|
|
3
|
-
export declare function callTerminal(mcpClient: MultiServerMCPClient, command: string): Promise<string>;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { AgentTaskMessageData } from "../types";
|
|
2
|
-
export declare class MessageStreamController {
|
|
3
|
-
private controller;
|
|
4
|
-
private msgCounter;
|
|
5
|
-
private messages;
|
|
6
|
-
private timeCountMap;
|
|
7
|
-
constructor(controller: ReadableStreamDefaultController);
|
|
8
|
-
enqueueNew(data: AgentTaskMessageData, isFinal: boolean): number;
|
|
9
|
-
enqueueUpdate(data: AgentTaskMessageData, isFinal: boolean): void;
|
|
10
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
export type VibeDevFlowNode = {
|
|
2
|
-
id: string;
|
|
3
|
-
children: VibeDevFlowNode[];
|
|
4
|
-
};
|
|
5
|
-
export declare enum AgentTaskMessageType {
|
|
6
|
-
Creation = "creation",
|
|
7
|
-
Update = "update"
|
|
8
|
-
}
|
|
9
|
-
export declare enum AgentTaskMessageDataType {
|
|
10
|
-
Notification = "notification",
|
|
11
|
-
ToolCall = "toolCall",
|
|
12
|
-
ArtifactOutput = "artifactOutput"
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Data associated with an AgentTaskItem.
|
|
16
|
-
* The fields included depend on the type of the task item.
|
|
17
|
-
*
|
|
18
|
-
* @property type - The type of the task item, defined by the AgentTaskItemType enum.
|
|
19
|
-
* @property title - (Optional) A brief title or summary of the task item.
|
|
20
|
-
* @property description - (Optional) A detailed description of the task item.
|
|
21
|
-
* @property toolName - (Optional) The name of the tool being called (if applicable).
|
|
22
|
-
* @property parameters - (Optional) A record of parameters associated with the tool call (if applicable).
|
|
23
|
-
* @property error - (Optional) An error message if the task item represents an error.
|
|
24
|
-
* @property result - (Optional) The result or output of the task item (if applicable).
|
|
25
|
-
*/
|
|
26
|
-
export type AgentTaskMessageData = {
|
|
27
|
-
type?: AgentTaskMessageDataType;
|
|
28
|
-
title?: string;
|
|
29
|
-
description?: string;
|
|
30
|
-
toolName?: string;
|
|
31
|
-
parameters?: Record<string, unknown>;
|
|
32
|
-
error?: string;
|
|
33
|
-
result?: string;
|
|
34
|
-
};
|
|
35
|
-
/**
|
|
36
|
-
* Represents a single task item generated by the agent.
|
|
37
|
-
* Each task item can be of different types such as tool calls, notifications, or errors.
|
|
38
|
-
*
|
|
39
|
-
* @property type - The type of the task item, defined by the AgentTaskItemType enum.
|
|
40
|
-
* @property messageId - The unique identifier for the task item.
|
|
41
|
-
* This is an incremental number representing the n-th task item.
|
|
42
|
-
* @property data - The data associated with the task item, which varies based on the type.
|
|
43
|
-
* @property isFinal - (Optional) Indicates if the task item is final and no further updates are expected.
|
|
44
|
-
*/
|
|
45
|
-
export type AgentTaskMessage = {
|
|
46
|
-
type: AgentTaskMessageType;
|
|
47
|
-
messageId: number;
|
|
48
|
-
data: AgentTaskMessageData;
|
|
49
|
-
isFinal?: boolean;
|
|
50
|
-
};
|
|
51
|
-
export type AgentTaskMessageUpdate = {
|
|
52
|
-
type: AgentTaskMessageType;
|
|
53
|
-
messageId: number;
|
|
54
|
-
updateType: "append";
|
|
55
|
-
delta: AgentTaskMessageData;
|
|
56
|
-
isFinal?: boolean;
|
|
57
|
-
timeUsedSec?: string;
|
|
58
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default function handler(req: Request): Promise<Response>;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"module": "esnext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"strict": true,
|
|
7
|
-
"declaration": true,
|
|
8
|
-
"outDir": "dist",
|
|
9
|
-
},
|
|
10
|
-
"include": [
|
|
11
|
-
"../../../../../../src/server-function/**/*",
|
|
12
|
-
"../../../../../../pulse.config.ts",
|
|
13
|
-
"../../../../../../global.d.ts",
|
|
14
|
-
],
|
|
15
|
-
"exclude": [
|
|
16
|
-
"../../../../../../node_modules",
|
|
17
|
-
"../../../../../../dist",
|
|
18
|
-
]
|
|
19
|
-
}
|
|
@@ -1,527 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { ModuleFederationPlugin } from '@module-federation/enhanced/webpack';
|
|
3
|
-
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
|
4
|
-
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
|
5
|
-
import { networkInterfaces } from 'os';
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import { globSync } from 'glob';
|
|
8
|
-
import fs from 'fs';
|
|
9
|
-
import CopyWebpackPlugin from 'copy-webpack-plugin';
|
|
10
|
-
import ts from 'typescript';
|
|
11
|
-
import { pathToFileURL } from 'url';
|
|
12
|
-
import mfNode from '@module-federation/node';
|
|
13
|
-
const { NodeFederationPlugin } = mfNode;
|
|
14
|
-
import wp from 'webpack';
|
|
15
|
-
const { webpack } = wp;
|
|
16
|
-
export async function createWebpackConfig(isPreview, buildTarget, mode) {
|
|
17
|
-
const projectDirName = process.cwd();
|
|
18
|
-
async function loadPulseConfig() {
|
|
19
|
-
// compile to js file and import
|
|
20
|
-
const program = ts.createProgram({
|
|
21
|
-
rootNames: [path.join(projectDirName, 'pulse.config.ts')],
|
|
22
|
-
options: {
|
|
23
|
-
module: ts.ModuleKind.ESNext,
|
|
24
|
-
target: ts.ScriptTarget.ES2020,
|
|
25
|
-
outDir: path.join(projectDirName, 'node_modules/.pulse/config'),
|
|
26
|
-
esModuleInterop: true,
|
|
27
|
-
skipLibCheck: true,
|
|
28
|
-
forceConsistentCasingInFileNames: true,
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
program.emit();
|
|
32
|
-
// Fix imports in the generated js file for all files in node_modules/.pulse/config
|
|
33
|
-
globSync('node_modules/.pulse/config/**/*.js', {
|
|
34
|
-
cwd: projectDirName,
|
|
35
|
-
absolute: true,
|
|
36
|
-
}).forEach(jsFile => {
|
|
37
|
-
let content = fs.readFileSync(jsFile, 'utf-8');
|
|
38
|
-
content = content.replace(/(from\s+["']\.\/[^\s"']+)(["'])/g, (match, p1, p2) => {
|
|
39
|
-
// No change if the import already has any extension
|
|
40
|
-
if (p1.match(/\.(js|cjs|mjs|ts|tsx|json)$/)) {
|
|
41
|
-
return match; // No change needed
|
|
42
|
-
}
|
|
43
|
-
return `${p1}.js${p2}`;
|
|
44
|
-
});
|
|
45
|
-
fs.writeFileSync(jsFile, content);
|
|
46
|
-
});
|
|
47
|
-
// Copy package.json if exists
|
|
48
|
-
const pkgPath = path.join(projectDirName, 'package.json');
|
|
49
|
-
if (fs.existsSync(pkgPath)) {
|
|
50
|
-
const destPath = path.join(projectDirName, 'node_modules/.pulse/config/package.json');
|
|
51
|
-
fs.copyFileSync(pkgPath, destPath);
|
|
52
|
-
}
|
|
53
|
-
const compiledConfig = path.join(projectDirName, 'node_modules/.pulse/config/pulse.config.js');
|
|
54
|
-
const mod = await import(pathToFileURL(compiledConfig).href);
|
|
55
|
-
// delete the compiled config after importing
|
|
56
|
-
fs.rmSync(path.join(projectDirName, 'node_modules/.pulse/config'), {
|
|
57
|
-
recursive: true,
|
|
58
|
-
force: true,
|
|
59
|
-
});
|
|
60
|
-
return mod.default;
|
|
61
|
-
}
|
|
62
|
-
const pulseConfig = await loadPulseConfig();
|
|
63
|
-
function getLocalNetworkIP() {
|
|
64
|
-
const interfaces = networkInterfaces();
|
|
65
|
-
for (const iface of Object.values(interfaces)) {
|
|
66
|
-
if (!iface)
|
|
67
|
-
continue;
|
|
68
|
-
for (const config of iface) {
|
|
69
|
-
if (config.family === 'IPv4' && !config.internal) {
|
|
70
|
-
return config.address; // Returns the first non-internal IPv4 address
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return 'localhost'; // Fallback
|
|
75
|
-
}
|
|
76
|
-
const origin = getLocalNetworkIP();
|
|
77
|
-
const previewStartupMessage = `
|
|
78
|
-
🎉 Your Pulse extension preview \x1b[1m${pulseConfig.displayName}\x1b[0m is LIVE!
|
|
79
|
-
|
|
80
|
-
⚡️ Local: http://localhost:3030
|
|
81
|
-
⚡️ Network: http://${origin}:3030
|
|
82
|
-
|
|
83
|
-
✨ Try it out in your browser and let the magic happen! 🚀
|
|
84
|
-
`;
|
|
85
|
-
const devStartupMessage = `
|
|
86
|
-
🎉 Your Pulse extension \x1b[1m${pulseConfig.displayName}\x1b[0m is LIVE!
|
|
87
|
-
|
|
88
|
-
⚡️ Local: http://localhost:3030/${pulseConfig.id}/${pulseConfig.version}/
|
|
89
|
-
⚡️ Network: http://${origin}:3030/${pulseConfig.id}/${pulseConfig.version}/
|
|
90
|
-
|
|
91
|
-
✨ Try it out in the Pulse Editor and let the magic happen! 🚀
|
|
92
|
-
`;
|
|
93
|
-
// #region Node Federation Plugin for Server Functions
|
|
94
|
-
function makeNodeFederationPlugin() {
|
|
95
|
-
function discoverServerFunctions() {
|
|
96
|
-
// Get all .ts files under src/server-function and read use default exports as entry points
|
|
97
|
-
const files = globSync('./src/server-function/**/*.ts');
|
|
98
|
-
const entryPoints = files
|
|
99
|
-
.map(file => file.replaceAll('\\', '/'))
|
|
100
|
-
.map(file => {
|
|
101
|
-
return {
|
|
102
|
-
['./' +
|
|
103
|
-
file.replace('src/server-function/', '').replace(/\.ts$/, '')]: './' + file,
|
|
104
|
-
};
|
|
105
|
-
})
|
|
106
|
-
.reduce((acc, curr) => {
|
|
107
|
-
return { ...acc, ...curr };
|
|
108
|
-
}, {});
|
|
109
|
-
return entryPoints;
|
|
110
|
-
}
|
|
111
|
-
const funcs = discoverServerFunctions();
|
|
112
|
-
console.log(`Discovered server functions:
|
|
113
|
-
${Object.entries(funcs)
|
|
114
|
-
.map(([name, file]) => {
|
|
115
|
-
return ` - ${name.slice(2)} (from ${file})`;
|
|
116
|
-
})
|
|
117
|
-
.join('\n')}
|
|
118
|
-
`);
|
|
119
|
-
return new NodeFederationPlugin({
|
|
120
|
-
name: pulseConfig.id + '_server',
|
|
121
|
-
remoteType: 'script',
|
|
122
|
-
useRuntimePlugin: true,
|
|
123
|
-
library: { type: 'commonjs-module' },
|
|
124
|
-
filename: 'remoteEntry.js',
|
|
125
|
-
exposes: {
|
|
126
|
-
...funcs,
|
|
127
|
-
},
|
|
128
|
-
}, {});
|
|
129
|
-
}
|
|
130
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
131
|
-
function compileServerFunctions(compiler) {
|
|
132
|
-
// Remove existing entry points
|
|
133
|
-
try {
|
|
134
|
-
fs.rmSync('dist/server', { recursive: true, force: true });
|
|
135
|
-
}
|
|
136
|
-
catch (e) {
|
|
137
|
-
console.error('Error removing dist/server:', e);
|
|
138
|
-
console.log('Continuing...');
|
|
139
|
-
}
|
|
140
|
-
// Generate tsconfig for server functions
|
|
141
|
-
function generateTempTsConfig() {
|
|
142
|
-
const tempTsConfigPath = path.join(process.cwd(), 'node_modules/.pulse/tsconfig.server.json');
|
|
143
|
-
const tsConfig = {
|
|
144
|
-
compilerOptions: {
|
|
145
|
-
target: 'ES2020',
|
|
146
|
-
module: 'esnext',
|
|
147
|
-
moduleResolution: 'bundler',
|
|
148
|
-
strict: true,
|
|
149
|
-
declaration: true,
|
|
150
|
-
outDir: path.join(process.cwd(), 'dist'),
|
|
151
|
-
},
|
|
152
|
-
include: [
|
|
153
|
-
path.join(process.cwd(), 'src/server-function/**/*'),
|
|
154
|
-
path.join(process.cwd(), 'pulse.config.ts'),
|
|
155
|
-
path.join(process.cwd(), 'global.d.ts'),
|
|
156
|
-
],
|
|
157
|
-
exclude: [
|
|
158
|
-
path.join(process.cwd(), 'node_modules'),
|
|
159
|
-
path.join(process.cwd(), 'dist'),
|
|
160
|
-
],
|
|
161
|
-
};
|
|
162
|
-
fs.writeFileSync(tempTsConfigPath, JSON.stringify(tsConfig, null, 2));
|
|
163
|
-
}
|
|
164
|
-
generateTempTsConfig();
|
|
165
|
-
// Run a new webpack compilation to pick up new server functions
|
|
166
|
-
const options = {
|
|
167
|
-
...compiler.options,
|
|
168
|
-
watch: false,
|
|
169
|
-
plugins: [
|
|
170
|
-
// Add a new NodeFederationPlugin with updated entry points
|
|
171
|
-
makeNodeFederationPlugin(),
|
|
172
|
-
],
|
|
173
|
-
};
|
|
174
|
-
const newCompiler = webpack(options);
|
|
175
|
-
// Run the new compiler
|
|
176
|
-
newCompiler?.run((err, stats) => {
|
|
177
|
-
if (err) {
|
|
178
|
-
console.error(`[Server] ❌ Error during recompilation:`, err);
|
|
179
|
-
}
|
|
180
|
-
else if (stats?.hasErrors()) {
|
|
181
|
-
console.error(`[Server] ❌ Compilation errors:`, stats.toJson().errors);
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
console.log(`[Server] ✅ Compiled server functions successfully.`);
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
// #endregion
|
|
189
|
-
// #region Source file parser for Pulse Config plugin
|
|
190
|
-
class PulseConfigPlugin {
|
|
191
|
-
requireFS = false;
|
|
192
|
-
apply(compiler) {
|
|
193
|
-
compiler.hooks.beforeCompile.tap('PulseConfigPlugin', () => {
|
|
194
|
-
this.requireFS = false;
|
|
195
|
-
globSync(['src/**/*.tsx', 'src/**/*.ts']).forEach(file => {
|
|
196
|
-
const source = fs.readFileSync(file, 'utf8');
|
|
197
|
-
this.scanSource(source);
|
|
198
|
-
});
|
|
199
|
-
// Persist result
|
|
200
|
-
pulseConfig.requireWorkspace = this.requireFS;
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
isWorkspaceHook(node) {
|
|
204
|
-
return (ts.isCallExpression(node) &&
|
|
205
|
-
ts.isIdentifier(node.expression) &&
|
|
206
|
-
[
|
|
207
|
-
'useFileSystem',
|
|
208
|
-
'useFile',
|
|
209
|
-
'useReceiveFile',
|
|
210
|
-
'useTerminal',
|
|
211
|
-
'useWorkspaceInfo',
|
|
212
|
-
].includes(node.expression.text));
|
|
213
|
-
}
|
|
214
|
-
scanSource(sourceText) {
|
|
215
|
-
const sourceFile = ts.createSourceFile('temp.tsx', sourceText, ts.ScriptTarget.Latest, true);
|
|
216
|
-
const visit = (node) => {
|
|
217
|
-
// Detect: useFileSystem(...)
|
|
218
|
-
if (this.isWorkspaceHook(node)) {
|
|
219
|
-
this.requireFS = true;
|
|
220
|
-
}
|
|
221
|
-
ts.forEachChild(node, visit);
|
|
222
|
-
};
|
|
223
|
-
visit(sourceFile);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
// #endregion
|
|
227
|
-
// #region Webpack Configs
|
|
228
|
-
const previewClientConfig = {
|
|
229
|
-
mode: mode,
|
|
230
|
-
entry: {
|
|
231
|
-
main: './node_modules/.pulse/server/preview/frontend/index.js',
|
|
232
|
-
},
|
|
233
|
-
output: {
|
|
234
|
-
path: path.resolve(projectDirName, 'dist/client'),
|
|
235
|
-
},
|
|
236
|
-
resolve: {
|
|
237
|
-
extensions: ['.ts', '.tsx', '.js'],
|
|
238
|
-
},
|
|
239
|
-
plugins: [
|
|
240
|
-
new PulseConfigPlugin(),
|
|
241
|
-
new HtmlWebpackPlugin({
|
|
242
|
-
template: './node_modules/.pulse/server/preview/frontend/index.html',
|
|
243
|
-
}),
|
|
244
|
-
new MiniCssExtractPlugin({
|
|
245
|
-
filename: 'globals.css',
|
|
246
|
-
}),
|
|
247
|
-
new CopyWebpackPlugin({
|
|
248
|
-
patterns: [{ from: 'src/assets', to: 'assets' }],
|
|
249
|
-
}),
|
|
250
|
-
{
|
|
251
|
-
apply: compiler => {
|
|
252
|
-
let isFirstRun = true;
|
|
253
|
-
// Before build starts
|
|
254
|
-
compiler.hooks.watchRun.tap('ReloadMessagePlugin', () => {
|
|
255
|
-
if (!isFirstRun) {
|
|
256
|
-
console.log('[client-preview] 🔄 Reloading app...');
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
console.log('[client-preview] 🔄 Building app...');
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
// After build finishes
|
|
263
|
-
compiler.hooks.done.tap('ReloadMessagePlugin', () => {
|
|
264
|
-
if (isFirstRun) {
|
|
265
|
-
console.log('[client-preview] ✅ Successfully built preview.');
|
|
266
|
-
console.log(previewStartupMessage);
|
|
267
|
-
isFirstRun = false;
|
|
268
|
-
}
|
|
269
|
-
else {
|
|
270
|
-
console.log('[client-preview] ✅ Reload finished');
|
|
271
|
-
}
|
|
272
|
-
// Write pulse config to dist
|
|
273
|
-
fs.writeFileSync(path.resolve(projectDirName, 'dist/client/pulse.config.json'), JSON.stringify(pulseConfig, null, 2));
|
|
274
|
-
fs.writeFileSync(path.resolve(projectDirName, 'dist/server/pulse.config.json'), JSON.stringify(pulseConfig, null, 2));
|
|
275
|
-
});
|
|
276
|
-
},
|
|
277
|
-
},
|
|
278
|
-
],
|
|
279
|
-
watchOptions: {
|
|
280
|
-
ignored: /src\/server-function/,
|
|
281
|
-
},
|
|
282
|
-
module: {
|
|
283
|
-
rules: [
|
|
284
|
-
{
|
|
285
|
-
test: /\.tsx?$/,
|
|
286
|
-
use: 'ts-loader',
|
|
287
|
-
exclude: [/node_modules/, /dist/],
|
|
288
|
-
},
|
|
289
|
-
{
|
|
290
|
-
test: /\.css$/i,
|
|
291
|
-
use: [
|
|
292
|
-
MiniCssExtractPlugin.loader,
|
|
293
|
-
'css-loader',
|
|
294
|
-
{
|
|
295
|
-
loader: 'postcss-loader',
|
|
296
|
-
},
|
|
297
|
-
],
|
|
298
|
-
},
|
|
299
|
-
],
|
|
300
|
-
},
|
|
301
|
-
stats: {
|
|
302
|
-
all: false,
|
|
303
|
-
errors: true,
|
|
304
|
-
warnings: true,
|
|
305
|
-
logging: 'warn',
|
|
306
|
-
colors: true,
|
|
307
|
-
},
|
|
308
|
-
infrastructureLogging: {
|
|
309
|
-
level: 'warn',
|
|
310
|
-
},
|
|
311
|
-
};
|
|
312
|
-
const mfClientConfig = {
|
|
313
|
-
mode: mode,
|
|
314
|
-
name: 'client',
|
|
315
|
-
entry: './src/main.tsx',
|
|
316
|
-
output: {
|
|
317
|
-
publicPath: 'auto',
|
|
318
|
-
path: path.resolve(projectDirName, 'dist/client'),
|
|
319
|
-
},
|
|
320
|
-
resolve: {
|
|
321
|
-
extensions: ['.ts', '.tsx', '.js'],
|
|
322
|
-
},
|
|
323
|
-
plugins: [
|
|
324
|
-
new PulseConfigPlugin(),
|
|
325
|
-
new MiniCssExtractPlugin({
|
|
326
|
-
filename: 'globals.css',
|
|
327
|
-
}),
|
|
328
|
-
// Copy assets to dist
|
|
329
|
-
new CopyWebpackPlugin({
|
|
330
|
-
patterns: [{ from: 'src/assets', to: 'assets' }],
|
|
331
|
-
}),
|
|
332
|
-
new ModuleFederationPlugin({
|
|
333
|
-
// Do not use hyphen character '-' in the name
|
|
334
|
-
name: pulseConfig.id,
|
|
335
|
-
filename: 'remoteEntry.js',
|
|
336
|
-
exposes: {
|
|
337
|
-
'./main': './src/main.tsx',
|
|
338
|
-
},
|
|
339
|
-
shared: {
|
|
340
|
-
react: {
|
|
341
|
-
requiredVersion: '19.2.0',
|
|
342
|
-
import: 'react', // the "react" package will be used a provided and fallback module
|
|
343
|
-
shareKey: 'react', // under this name the shared module will be placed in the share scope
|
|
344
|
-
shareScope: 'default', // share scope with this name will be used
|
|
345
|
-
singleton: true, // only a single version of the shared module is allowed
|
|
346
|
-
},
|
|
347
|
-
'react-dom': {
|
|
348
|
-
requiredVersion: '19.2.0',
|
|
349
|
-
singleton: true, // only a single version of the shared module is allowed
|
|
350
|
-
},
|
|
351
|
-
},
|
|
352
|
-
}),
|
|
353
|
-
{
|
|
354
|
-
apply: compiler => {
|
|
355
|
-
if (compiler.options.mode === 'development') {
|
|
356
|
-
let isFirstRun = true;
|
|
357
|
-
// Before build starts
|
|
358
|
-
compiler.hooks.watchRun.tap('ReloadMessagePlugin', () => {
|
|
359
|
-
if (!isFirstRun) {
|
|
360
|
-
console.log('[client] 🔄 reloading app...');
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
console.log('[client] 🔄 building app...');
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
// Log file updates
|
|
367
|
-
compiler.hooks.invalid.tap('LogFileUpdates', (file, changeTime) => {
|
|
368
|
-
console.log(`[watch] change detected in: ${file} at ${new Date(changeTime || Date.now()).toLocaleTimeString()}`);
|
|
369
|
-
});
|
|
370
|
-
// After build finishes
|
|
371
|
-
compiler.hooks.done.tap('ReloadMessagePlugin', () => {
|
|
372
|
-
if (isFirstRun) {
|
|
373
|
-
console.log('[client] ✅ Successfully built client.');
|
|
374
|
-
console.log(devStartupMessage);
|
|
375
|
-
isFirstRun = false;
|
|
376
|
-
}
|
|
377
|
-
else {
|
|
378
|
-
console.log('[client] ✅ Reload finished.');
|
|
379
|
-
}
|
|
380
|
-
// Write pulse config to dist
|
|
381
|
-
fs.writeFileSync(path.resolve(projectDirName, 'dist/client/pulse.config.json'), JSON.stringify(pulseConfig, null, 2));
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
else {
|
|
385
|
-
// Print build success/failed message
|
|
386
|
-
compiler.hooks.done.tap('BuildMessagePlugin', stats => {
|
|
387
|
-
if (stats.hasErrors()) {
|
|
388
|
-
console.log(`[client] ❌ Failed to build client.`);
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
console.log(`[client] ✅ Successfully built client.`);
|
|
392
|
-
// Write pulse config to dist
|
|
393
|
-
fs.writeFileSync(path.resolve(projectDirName, 'dist/client/pulse.config.json'), JSON.stringify(pulseConfig, null, 2));
|
|
394
|
-
}
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
},
|
|
398
|
-
},
|
|
399
|
-
],
|
|
400
|
-
module: {
|
|
401
|
-
rules: [
|
|
402
|
-
{
|
|
403
|
-
test: /\.tsx?$/,
|
|
404
|
-
use: 'ts-loader',
|
|
405
|
-
exclude: [/node_modules/, /dist/],
|
|
406
|
-
},
|
|
407
|
-
{
|
|
408
|
-
test: /\.css$/i,
|
|
409
|
-
use: [
|
|
410
|
-
MiniCssExtractPlugin.loader,
|
|
411
|
-
'css-loader',
|
|
412
|
-
{
|
|
413
|
-
loader: 'postcss-loader',
|
|
414
|
-
},
|
|
415
|
-
],
|
|
416
|
-
exclude: [/dist/],
|
|
417
|
-
},
|
|
418
|
-
],
|
|
419
|
-
},
|
|
420
|
-
stats: {
|
|
421
|
-
all: false,
|
|
422
|
-
errors: true,
|
|
423
|
-
warnings: true,
|
|
424
|
-
logging: 'warn',
|
|
425
|
-
colors: true,
|
|
426
|
-
assets: false,
|
|
427
|
-
},
|
|
428
|
-
infrastructureLogging: {
|
|
429
|
-
level: 'warn',
|
|
430
|
-
},
|
|
431
|
-
};
|
|
432
|
-
const mfServerConfig = {
|
|
433
|
-
mode: mode,
|
|
434
|
-
name: 'server',
|
|
435
|
-
entry: {},
|
|
436
|
-
target: 'async-node',
|
|
437
|
-
output: {
|
|
438
|
-
publicPath: 'auto',
|
|
439
|
-
path: path.resolve(projectDirName, 'dist/server'),
|
|
440
|
-
},
|
|
441
|
-
resolve: {
|
|
442
|
-
extensions: ['.ts', '.js'],
|
|
443
|
-
},
|
|
444
|
-
plugins: [
|
|
445
|
-
{
|
|
446
|
-
apply: compiler => {
|
|
447
|
-
if (compiler.options.mode === 'development') {
|
|
448
|
-
let isFirstRun = true;
|
|
449
|
-
// Before build starts
|
|
450
|
-
compiler.hooks.watchRun.tap('ReloadMessagePlugin', () => {
|
|
451
|
-
if (!isFirstRun) {
|
|
452
|
-
console.log(`[Server] 🔄 Reloading app...`);
|
|
453
|
-
}
|
|
454
|
-
else {
|
|
455
|
-
console.log(`[Server] 🔄 Building app...`);
|
|
456
|
-
}
|
|
457
|
-
compileServerFunctions(compiler);
|
|
458
|
-
});
|
|
459
|
-
// After build finishes
|
|
460
|
-
compiler.hooks.done.tap('ReloadMessagePlugin', () => {
|
|
461
|
-
if (isFirstRun) {
|
|
462
|
-
console.log(`[Server] ✅ Successfully built server.`);
|
|
463
|
-
isFirstRun = false;
|
|
464
|
-
}
|
|
465
|
-
else {
|
|
466
|
-
console.log(`[Server] ✅ Reload finished.`);
|
|
467
|
-
}
|
|
468
|
-
});
|
|
469
|
-
// Watch for changes in the server-function directory to trigger rebuilds
|
|
470
|
-
compiler.hooks.thisCompilation.tap('WatchServerFunctions', compilation => {
|
|
471
|
-
compilation.contextDependencies.add(path.resolve(projectDirName, 'src/server-function'));
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
else {
|
|
475
|
-
// Print build success/failed message
|
|
476
|
-
compiler.hooks.done.tap('BuildMessagePlugin', stats => {
|
|
477
|
-
if (stats.hasErrors()) {
|
|
478
|
-
console.log(`[Server] ❌ Failed to build server.`);
|
|
479
|
-
}
|
|
480
|
-
else {
|
|
481
|
-
compileServerFunctions(compiler);
|
|
482
|
-
console.log(`[Server] ✅ Successfully built server.`);
|
|
483
|
-
}
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
},
|
|
487
|
-
},
|
|
488
|
-
],
|
|
489
|
-
module: {
|
|
490
|
-
rules: [
|
|
491
|
-
{
|
|
492
|
-
test: /\.tsx?$/,
|
|
493
|
-
use: {
|
|
494
|
-
loader: 'ts-loader',
|
|
495
|
-
options: {
|
|
496
|
-
configFile: 'node_modules/.pulse/tsconfig.server.json',
|
|
497
|
-
},
|
|
498
|
-
},
|
|
499
|
-
exclude: [/node_modules/, /dist/],
|
|
500
|
-
},
|
|
501
|
-
],
|
|
502
|
-
},
|
|
503
|
-
stats: {
|
|
504
|
-
all: false,
|
|
505
|
-
errors: true,
|
|
506
|
-
warnings: true,
|
|
507
|
-
logging: 'warn',
|
|
508
|
-
colors: true,
|
|
509
|
-
},
|
|
510
|
-
infrastructureLogging: {
|
|
511
|
-
level: 'warn',
|
|
512
|
-
},
|
|
513
|
-
};
|
|
514
|
-
// #endregion
|
|
515
|
-
if (isPreview) {
|
|
516
|
-
return [previewClientConfig, mfServerConfig];
|
|
517
|
-
}
|
|
518
|
-
else if (buildTarget === 'server') {
|
|
519
|
-
return [mfServerConfig];
|
|
520
|
-
}
|
|
521
|
-
else if (buildTarget === 'client') {
|
|
522
|
-
return [mfClientConfig];
|
|
523
|
-
}
|
|
524
|
-
else {
|
|
525
|
-
return [mfClientConfig, mfServerConfig];
|
|
526
|
-
}
|
|
527
|
-
}
|