@gxp-dev/tools 2.0.87 → 2.0.89
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/bin/lib/cli.js +6 -0
- package/bin/lib/commands/dev.js +197 -59
- package/package.json +1 -1
- package/runtime/vite.config.js +44 -30
package/bin/lib/cli.js
CHANGED
|
@@ -157,6 +157,12 @@ const cli = yargs
|
|
|
157
157
|
default: false,
|
|
158
158
|
alias: "m",
|
|
159
159
|
},
|
|
160
|
+
json: {
|
|
161
|
+
describe:
|
|
162
|
+
"Emit all dev-server logs as newline-delimited JSON (one record per line) for cloud log collectors",
|
|
163
|
+
type: "boolean",
|
|
164
|
+
default: false,
|
|
165
|
+
},
|
|
160
166
|
},
|
|
161
167
|
devCommand,
|
|
162
168
|
)
|
package/bin/lib/commands/dev.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const path = require("path")
|
|
9
9
|
const fs = require("fs")
|
|
10
|
+
const { spawn } = require("child_process")
|
|
10
11
|
const shell = require("shelljs")
|
|
11
12
|
const dotenv = require("dotenv")
|
|
12
13
|
const {
|
|
@@ -16,6 +17,123 @@ const {
|
|
|
16
17
|
findExistingCertificates,
|
|
17
18
|
} = require("../utils")
|
|
18
19
|
|
|
20
|
+
const ANSI_REGEX = /\x1b\[[0-9;]*[a-zA-Z]/g
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create a logger that either emits NDJSON lines or plain text.
|
|
24
|
+
* In JSON mode, every record is a single line: {timestamp, service, level, message}
|
|
25
|
+
*/
|
|
26
|
+
function createLogger(jsonMode) {
|
|
27
|
+
function emit(level, service, message) {
|
|
28
|
+
if (jsonMode) {
|
|
29
|
+
process.stdout.write(
|
|
30
|
+
JSON.stringify({
|
|
31
|
+
timestamp: new Date().toISOString(),
|
|
32
|
+
service,
|
|
33
|
+
level,
|
|
34
|
+
message,
|
|
35
|
+
}) + "\n",
|
|
36
|
+
)
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
const stream =
|
|
40
|
+
level === "error" || level === "warn" ? process.stderr : process.stdout
|
|
41
|
+
stream.write(message + "\n")
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
jsonMode,
|
|
45
|
+
info: (message, service = "GXDEV") => emit("info", service, message),
|
|
46
|
+
warn: (message, service = "GXDEV") => emit("warn", service, message),
|
|
47
|
+
error: (message, service = "GXDEV") => emit("error", service, message),
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Spawn a service and pipe each line of its stdout/stderr through the logger.
|
|
53
|
+
* Returns the child process.
|
|
54
|
+
*/
|
|
55
|
+
function spawnService(name, command, logger) {
|
|
56
|
+
const child = spawn(command, {
|
|
57
|
+
shell: true,
|
|
58
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
59
|
+
env: process.env,
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
function pipe(stream, level) {
|
|
63
|
+
let buffer = ""
|
|
64
|
+
stream.setEncoding("utf8")
|
|
65
|
+
stream.on("data", (chunk) => {
|
|
66
|
+
buffer += chunk
|
|
67
|
+
let idx
|
|
68
|
+
while ((idx = buffer.indexOf("\n")) !== -1) {
|
|
69
|
+
const line = buffer.slice(0, idx).replace(/\r$/, "")
|
|
70
|
+
buffer = buffer.slice(idx + 1)
|
|
71
|
+
const clean = line.replace(ANSI_REGEX, "")
|
|
72
|
+
if (clean.length > 0) {
|
|
73
|
+
logger[level](clean, name)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
stream.on("end", () => {
|
|
78
|
+
if (buffer.length > 0) {
|
|
79
|
+
const clean = buffer.replace(ANSI_REGEX, "").trim()
|
|
80
|
+
if (clean) {
|
|
81
|
+
logger[level](clean, name)
|
|
82
|
+
}
|
|
83
|
+
buffer = ""
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
pipe(child.stdout, "info")
|
|
89
|
+
pipe(child.stderr, "error")
|
|
90
|
+
return child
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Run a list of services concurrently, wiring up line-by-line JSON logging
|
|
95
|
+
* and best-effort shutdown when any one exits or the user hits Ctrl+C.
|
|
96
|
+
*/
|
|
97
|
+
function runServicesJson(services, logger) {
|
|
98
|
+
const children = services.map((svc) => {
|
|
99
|
+
logger.info(`starting ${svc.name}: ${svc.command}`, svc.name)
|
|
100
|
+
return { svc, child: spawnService(svc.name, svc.command, logger) }
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
let shuttingDown = false
|
|
104
|
+
function shutdown(code) {
|
|
105
|
+
if (shuttingDown) {
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
shuttingDown = true
|
|
109
|
+
for (const { child } of children) {
|
|
110
|
+
if (!child.killed && child.exitCode === null) {
|
|
111
|
+
child.kill("SIGTERM")
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
process.exit(code ?? 0)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
for (const { svc, child } of children) {
|
|
118
|
+
child.on("exit", (code, signal) => {
|
|
119
|
+
logger.info(
|
|
120
|
+
`${svc.name} exited (code=${code ?? "null"}${
|
|
121
|
+
signal ? `, signal=${signal}` : ""
|
|
122
|
+
})`,
|
|
123
|
+
svc.name,
|
|
124
|
+
)
|
|
125
|
+
shutdown(code ?? 0)
|
|
126
|
+
})
|
|
127
|
+
child.on("error", (err) => {
|
|
128
|
+
logger.error(`${svc.name} failed to spawn: ${err.message}`, svc.name)
|
|
129
|
+
shutdown(1)
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
process.on("SIGINT", () => shutdown(130))
|
|
134
|
+
process.on("SIGTERM", () => shutdown(143))
|
|
135
|
+
}
|
|
136
|
+
|
|
19
137
|
/**
|
|
20
138
|
* Get browser extension paths and commands
|
|
21
139
|
* @param {string} browser - "firefox" or "chrome"
|
|
@@ -96,22 +214,38 @@ function getBrowserExtensionConfig(browser, projectPath, paths, options = {}) {
|
|
|
96
214
|
* Development command - starts the dev server
|
|
97
215
|
*/
|
|
98
216
|
function devCommand(argv) {
|
|
217
|
+
const logger = createLogger(!!argv.json)
|
|
99
218
|
const paths = resolveGxPaths()
|
|
100
219
|
const projectPath = findProjectRoot()
|
|
101
220
|
|
|
221
|
+
// Surface which toolkit install is being used. resolveGxPaths() prefers
|
|
222
|
+
// <project>/node_modules/@gxp-dev/tools (local) and falls back to the
|
|
223
|
+
// CLI's own install location (global / npm link / workspace).
|
|
224
|
+
const localToolkitDir = path.join(
|
|
225
|
+
projectPath,
|
|
226
|
+
"node_modules",
|
|
227
|
+
"@gxp-dev",
|
|
228
|
+
"tools",
|
|
229
|
+
)
|
|
230
|
+
const installLocation =
|
|
231
|
+
paths.packageRoot === localToolkitDir ? "local" : "package"
|
|
232
|
+
logger.info(
|
|
233
|
+
`📦 Using ${installLocation} toolkit install: ${paths.packageRoot}`,
|
|
234
|
+
)
|
|
235
|
+
|
|
102
236
|
// Load .env file if it exists for default values
|
|
103
237
|
const envPath = path.join(projectPath, ".env")
|
|
104
238
|
const envExamplePath = path.join(projectPath, ".env.example")
|
|
105
239
|
|
|
106
240
|
// Load .env file into process.env
|
|
107
241
|
if (fs.existsSync(envPath)) {
|
|
108
|
-
|
|
242
|
+
logger.info("📋 Loading environment variables from .env file")
|
|
109
243
|
dotenv.config({ path: envPath })
|
|
110
244
|
} else if (fs.existsSync(envExamplePath)) {
|
|
111
|
-
|
|
245
|
+
logger.info(
|
|
112
246
|
"💡 Tip: Create .env file from .env.example to customize your environment settings",
|
|
113
247
|
)
|
|
114
|
-
|
|
248
|
+
logger.info(" cp .env.example .env")
|
|
115
249
|
}
|
|
116
250
|
|
|
117
251
|
// Check for SSL certificates unless explicitly disabled
|
|
@@ -124,32 +258,32 @@ function devCommand(argv) {
|
|
|
124
258
|
const existingCerts = findExistingCertificates(certsDir)
|
|
125
259
|
|
|
126
260
|
if (!existingCerts) {
|
|
127
|
-
|
|
261
|
+
logger.warn(
|
|
128
262
|
"⚠ SSL certificates not found. Run 'npm run setup-ssl' to enable HTTPS",
|
|
129
263
|
)
|
|
130
|
-
|
|
264
|
+
logger.info("🌐 Starting HTTP development server...")
|
|
131
265
|
useHttps = false
|
|
132
266
|
} else {
|
|
133
|
-
|
|
134
|
-
|
|
267
|
+
logger.info("🔒 Starting HTTPS development server...")
|
|
268
|
+
logger.info(
|
|
135
269
|
`📁 Using certificate: ${path.basename(existingCerts.certPath)}`,
|
|
136
270
|
)
|
|
137
|
-
|
|
271
|
+
logger.info(`🔑 Using key: ${path.basename(existingCerts.keyPath)}`)
|
|
138
272
|
certPath = existingCerts.certPath
|
|
139
273
|
keyPath = existingCerts.keyPath
|
|
140
274
|
}
|
|
141
275
|
} else {
|
|
142
|
-
|
|
276
|
+
logger.info("🌐 Starting HTTP development server...")
|
|
143
277
|
}
|
|
144
278
|
|
|
145
279
|
// Determine final port value (priority: CLI arg > .env > default)
|
|
146
280
|
const finalPort = argv.port || process.env.NODE_PORT || 3000
|
|
147
|
-
|
|
281
|
+
logger.info(`🌐 Development server will start on port: ${finalPort}`)
|
|
148
282
|
|
|
149
283
|
// Check if mock API should be enabled
|
|
150
284
|
const withMock = argv["with-mock"]
|
|
151
285
|
if (withMock) {
|
|
152
|
-
|
|
286
|
+
logger.info("🎭 Mock API will be enabled")
|
|
153
287
|
}
|
|
154
288
|
|
|
155
289
|
// Socket server starts by default unless --no-socket is passed
|
|
@@ -159,15 +293,15 @@ function devCommand(argv) {
|
|
|
159
293
|
// Check for local server.js first, then runtime directory
|
|
160
294
|
const serverJs = resolveFilePath("server.cjs", "", "runtime")
|
|
161
295
|
if (!fs.existsSync(serverJs.path)) {
|
|
162
|
-
|
|
296
|
+
logger.warn("⚠ server.js not found. Skipping Socket.IO server.")
|
|
163
297
|
} else {
|
|
164
298
|
serverJsPath = serverJs.path
|
|
165
|
-
|
|
299
|
+
logger.info(
|
|
166
300
|
`📡 Starting Socket.IO server with nodemon... (${
|
|
167
301
|
serverJs.isLocal ? "local" : "package"
|
|
168
302
|
} version)`,
|
|
169
303
|
)
|
|
170
|
-
|
|
304
|
+
logger.info(`📁 Using: ${serverJsPath}`)
|
|
171
305
|
}
|
|
172
306
|
}
|
|
173
307
|
|
|
@@ -184,16 +318,16 @@ function devCommand(argv) {
|
|
|
184
318
|
fs.existsSync(path.join(projectPath, "vite.extend.mjs"))
|
|
185
319
|
|
|
186
320
|
if (hasLocalIndexHtml) {
|
|
187
|
-
|
|
321
|
+
logger.info("📁 Using local index.html")
|
|
188
322
|
}
|
|
189
323
|
if (hasLocalMainJs) {
|
|
190
|
-
|
|
324
|
+
logger.info("📁 Using local main.js")
|
|
191
325
|
}
|
|
192
326
|
if (hasLocalExtend) {
|
|
193
|
-
|
|
327
|
+
logger.info("🧩 Extending vite config from vite.extend.js")
|
|
194
328
|
}
|
|
195
329
|
if (!hasLocalIndexHtml && !hasLocalMainJs && !hasLocalExtend) {
|
|
196
|
-
|
|
330
|
+
logger.info(
|
|
197
331
|
"📦 Using runtime dev files (create vite.extend.js to customize)",
|
|
198
332
|
)
|
|
199
333
|
}
|
|
@@ -234,11 +368,11 @@ function devCommand(argv) {
|
|
|
234
368
|
port: finalPort,
|
|
235
369
|
})
|
|
236
370
|
if (firefoxConfig) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
371
|
+
logger.info("🦊 Firefox extension will launch with dev server")
|
|
372
|
+
logger.info(`📁 Extension path: ${firefoxConfig.extensionPath}`)
|
|
373
|
+
logger.info(`🌐 Start URL: ${firefoxConfig.startUrl}`)
|
|
240
374
|
} else {
|
|
241
|
-
|
|
375
|
+
logger.warn("⚠️ Firefox extension not found, skipping")
|
|
242
376
|
}
|
|
243
377
|
}
|
|
244
378
|
|
|
@@ -248,11 +382,11 @@ function devCommand(argv) {
|
|
|
248
382
|
port: finalPort,
|
|
249
383
|
})
|
|
250
384
|
if (chromeConfig) {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
385
|
+
logger.info("🚀 Chrome extension will launch with dev server")
|
|
386
|
+
logger.info(`📁 Extension path: ${chromeConfig.extensionPath}`)
|
|
387
|
+
logger.info(`🌐 Start URL: ${chromeConfig.startUrl}`)
|
|
254
388
|
} else {
|
|
255
|
-
|
|
389
|
+
logger.warn("⚠️ Chrome extension not found, skipping")
|
|
256
390
|
}
|
|
257
391
|
}
|
|
258
392
|
|
|
@@ -261,54 +395,58 @@ function devCommand(argv) {
|
|
|
261
395
|
process.env.CHROME_EXTENSION_PATH = chromeConfig.extensionPath
|
|
262
396
|
}
|
|
263
397
|
|
|
264
|
-
// Build the command based on what's requested
|
|
265
|
-
let command
|
|
266
|
-
|
|
267
|
-
// Collect all processes to run
|
|
268
|
-
const processes = []
|
|
269
|
-
const names = []
|
|
270
|
-
const colors = []
|
|
271
|
-
|
|
272
398
|
// Normalize path separators to forward slashes for cross-platform shell compatibility
|
|
273
399
|
const normalizedViteConfigPath = viteConfigPath.replace(/\\/g, "/")
|
|
274
400
|
|
|
275
|
-
//
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
401
|
+
// Build the canonical service list (raw commands, no concurrently wrapping)
|
|
402
|
+
const services = []
|
|
403
|
+
services.push({
|
|
404
|
+
name: "VITE",
|
|
405
|
+
color: "cyan",
|
|
406
|
+
command: `npx vite dev --config "${normalizedViteConfigPath}"`,
|
|
407
|
+
})
|
|
280
408
|
|
|
281
|
-
// Socket server (on by default, skip if --no-socket or server.js not found)
|
|
282
409
|
if (serverJsPath) {
|
|
283
410
|
const normalizedServerPath = serverJsPath.replace(/\\/g, "/")
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
411
|
+
services.push({
|
|
412
|
+
name: "SOCKET",
|
|
413
|
+
color: "green",
|
|
414
|
+
command: `npx nodemon "${normalizedServerPath}"`,
|
|
415
|
+
})
|
|
287
416
|
}
|
|
288
417
|
|
|
289
|
-
// Firefox extension (optional)
|
|
290
418
|
if (firefoxConfig) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
419
|
+
services.push({
|
|
420
|
+
name: firefoxConfig.name,
|
|
421
|
+
color: firefoxConfig.color,
|
|
422
|
+
command: firefoxConfig.command,
|
|
423
|
+
})
|
|
294
424
|
}
|
|
295
425
|
|
|
296
|
-
// Chrome extension (optional)
|
|
297
426
|
if (chromeConfig) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
427
|
+
services.push({
|
|
428
|
+
name: chromeConfig.name,
|
|
429
|
+
color: chromeConfig.color,
|
|
430
|
+
command: chromeConfig.command,
|
|
431
|
+
})
|
|
301
432
|
}
|
|
302
433
|
|
|
303
|
-
//
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
434
|
+
// In JSON mode we orchestrate the children ourselves so we can wrap every
|
|
435
|
+
// stdout/stderr line as NDJSON. Concurrently's prefixed output would defeat
|
|
436
|
+
// that. Outside JSON mode, keep the legacy concurrently-based behavior.
|
|
437
|
+
if (logger.jsonMode) {
|
|
438
|
+
runServicesJson(services, logger)
|
|
439
|
+
return
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
let command
|
|
443
|
+
if (services.length > 1) {
|
|
444
|
+
const quoted = services.map((s) => `"${s.command}"`).join(" ")
|
|
445
|
+
const names = services.map((s) => s.name).join(",")
|
|
446
|
+
const colors = services.map((s) => s.color).join(",")
|
|
447
|
+
command = `npx concurrently --names "${names}" --prefix-colors "${colors}" ${quoted}`
|
|
309
448
|
} else {
|
|
310
|
-
|
|
311
|
-
command = `npx vite dev --config "${normalizedViteConfigPath}"`
|
|
449
|
+
command = services[0].command
|
|
312
450
|
}
|
|
313
451
|
|
|
314
452
|
shell.exec(command)
|
package/package.json
CHANGED
package/runtime/vite.config.js
CHANGED
|
@@ -172,6 +172,22 @@ export default defineConfig(async (ctx) => {
|
|
|
172
172
|
console.log(`📄 index.html: ${useLocalIndex ? "local" : "runtime"}`)
|
|
173
173
|
console.log(`📄 main.js: ${useLocalMain ? "local" : "runtime"}`)
|
|
174
174
|
|
|
175
|
+
// Build /@fs/ URLs that work regardless of whether the toolkit is inside
|
|
176
|
+
// the project's node_modules or installed globally. Vite's /@fs/ handler
|
|
177
|
+
// serves these via server.fs.allow (which already includes toolkitPath),
|
|
178
|
+
// so absolute paths outside process.cwd() just work.
|
|
179
|
+
const realRuntimeDir = (() => {
|
|
180
|
+
try {
|
|
181
|
+
return fs.realpathSync(runtimeDir)
|
|
182
|
+
} catch {
|
|
183
|
+
return runtimeDir
|
|
184
|
+
}
|
|
185
|
+
})().replace(/\\/g, "/")
|
|
186
|
+
const toFsUrl = (relPath) =>
|
|
187
|
+
`/@fs${realRuntimeDir.startsWith("/") ? "" : "/"}${realRuntimeDir}/${relPath}`
|
|
188
|
+
const runtimeMainFsUrl = toFsUrl("main.js")
|
|
189
|
+
const runtimeLogoFsUrl = toFsUrl("logo.png")
|
|
190
|
+
|
|
175
191
|
// Create plugin to serve runtime files (index.html and main.js) if no local ones exist
|
|
176
192
|
const runtimeFilesPlugin = {
|
|
177
193
|
name: "runtime-files",
|
|
@@ -205,15 +221,21 @@ export default defineConfig(async (ctx) => {
|
|
|
205
221
|
) {
|
|
206
222
|
const runtimeIndexPath = path.join(runtimeDir, "index.html")
|
|
207
223
|
if (fs.existsSync(runtimeIndexPath)) {
|
|
208
|
-
//
|
|
224
|
+
// Rewrite hard-coded references to runtime assets so they
|
|
225
|
+
// resolve via Vite's /@fs/ handler instead of a guessed
|
|
226
|
+
// /node_modules/... path. This is what makes the same
|
|
227
|
+
// runtime work for local, linked, and global installs.
|
|
228
|
+
let html = fs.readFileSync(runtimeIndexPath, "utf-8")
|
|
229
|
+
html = html
|
|
230
|
+
.split("/node_modules/@gxp-dev/tools/runtime/logo.png")
|
|
231
|
+
.join(runtimeLogoFsUrl)
|
|
232
|
+
.split("/@gx-runtime/main.js")
|
|
233
|
+
.join(useLocalMain ? "/main.js" : runtimeMainFsUrl)
|
|
209
234
|
server
|
|
210
|
-
.transformIndexHtml(
|
|
211
|
-
|
|
212
|
-
fs.readFileSync(runtimeIndexPath, "utf-8"),
|
|
213
|
-
)
|
|
214
|
-
.then((html) => {
|
|
235
|
+
.transformIndexHtml(rawUrl, html)
|
|
236
|
+
.then((transformed) => {
|
|
215
237
|
res.setHeader("Content-Type", "text/html")
|
|
216
|
-
res.end(
|
|
238
|
+
res.end(transformed)
|
|
217
239
|
})
|
|
218
240
|
.catch((err) => {
|
|
219
241
|
console.error("Error transforming index.html:", err)
|
|
@@ -223,32 +245,24 @@ export default defineConfig(async (ctx) => {
|
|
|
223
245
|
}
|
|
224
246
|
}
|
|
225
247
|
|
|
226
|
-
//
|
|
248
|
+
// Back-compat: anything still hitting the legacy
|
|
249
|
+
// `/@gx-runtime/main.js` URL (e.g. a hand-rolled index.html)
|
|
250
|
+
// is redirected to the /@fs/ URL. The old transformRequest
|
|
251
|
+
// approach passed an absolute filesystem path and only worked
|
|
252
|
+
// when runtimeDir was inside process.cwd() — i.e. for local
|
|
253
|
+
// installs but not global ones.
|
|
227
254
|
if (
|
|
228
255
|
!useLocalMain &&
|
|
229
|
-
(
|
|
230
|
-
|
|
256
|
+
(urlPath === "/@gx-runtime/main.js" ||
|
|
257
|
+
urlPath.startsWith("/@gx-runtime/main.js"))
|
|
231
258
|
) {
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if (result) {
|
|
240
|
-
res.setHeader("Content-Type", "application/javascript")
|
|
241
|
-
res.end(result.code)
|
|
242
|
-
} else {
|
|
243
|
-
next()
|
|
244
|
-
}
|
|
245
|
-
})
|
|
246
|
-
.catch((err) => {
|
|
247
|
-
console.error("Error transforming main.js:", err)
|
|
248
|
-
next(err)
|
|
249
|
-
})
|
|
250
|
-
return
|
|
251
|
-
}
|
|
259
|
+
const query = rawUrl.includes("?")
|
|
260
|
+
? rawUrl.slice(rawUrl.indexOf("?"))
|
|
261
|
+
: ""
|
|
262
|
+
res.statusCode = 302
|
|
263
|
+
res.setHeader("Location", runtimeMainFsUrl + query)
|
|
264
|
+
res.end()
|
|
265
|
+
return
|
|
252
266
|
}
|
|
253
267
|
|
|
254
268
|
next()
|