@bananacool467/authtics-host 0.2.6 → 0.2.7
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/.agents/memory/MEMORY.md +1 -0
- package/.agents/memory/node-child-process-tests.md +10 -0
- package/README.md +131 -6
- package/assets/dev_panel.jpg +0 -0
- package/assets/result.jpg +0 -0
- package/dist/BrowserAPIs.d.ts +10 -0
- package/dist/BrowserAPIs.js +53 -0
- package/dist/cli.d.ts +5 -3
- package/dist/cli.js +408 -58
- package/dist/config.d.ts +5 -0
- package/dist/config.js +15 -1
- package/dist/devPanel.d.ts +11 -0
- package/dist/devPanel.js +358 -56
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/renderEngine.d.ts +4 -3
- package/dist/renderEngine.js +265 -68
- package/dist/rendererTempFiles.d.ts +10 -1
- package/dist/rendererTempFiles.js +101 -17
- package/dist/superBuildOutput.d.ts +9 -0
- package/dist/superBuildOutput.js +175 -0
- package/dist/svg_assets/slope_top_left.d.ts +1 -0
- package/dist/svg_assets/slope_top_left.js +4 -0
- package/package.json +8 -1
- package/scripts/post-merge.sh +5 -0
- package/svg_assets/slope_top_left.tsx +19 -0
- package/tests/source-map-access.test.mjs +167 -0
- package/tests/source-map-location.test.mjs +237 -0
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createServer } from "node:http";
|
|
3
3
|
import { createJiti } from "jiti";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import fs from "node:fs";
|
|
6
7
|
import url from "node:url";
|
|
7
|
-
import { renderPage, handleSrtwsConnection } from "./renderEngine.js";
|
|
8
|
-
import { tempFileForTsx } from "./rendererTempFiles.js";
|
|
8
|
+
import { renderPage, handleSrtwsConnection, deleteSrtwsServer, handleHTML } from "./renderEngine.js";
|
|
9
|
+
import { setBuildStyle, tempFileForTsx } from "./rendererTempFiles.js";
|
|
10
|
+
import { initializeSuperBuildOutput, refreshSuperRouteManifest } from "./superBuildOutput.js";
|
|
9
11
|
let config_ = {};
|
|
10
12
|
let uptime = 0;
|
|
11
13
|
let totalRequests = 0;
|
|
@@ -17,12 +19,174 @@ const routes = new Map();
|
|
|
17
19
|
const __filename = url.fileURLToPath(import.meta.url);
|
|
18
20
|
const __dirname = path.dirname(__filename);
|
|
19
21
|
const hmrClients = new Set();
|
|
22
|
+
const DAT_GUESS_WINDOW_MS = 30 * 60 * 1000;
|
|
23
|
+
const DAT_DISTINCT_GUESS_LIMIT = 5;
|
|
24
|
+
const DAT_IP_BLOCK_MS = 24 * 60 * 60 * 1000;
|
|
25
|
+
const ACTION_RATE_WINDOW_MS = 60 * 1000;
|
|
26
|
+
const ACTION_RATE_RULES = {
|
|
27
|
+
"/__authtics_host_apis__/set-users-paused": {
|
|
28
|
+
maxRequests: 5,
|
|
29
|
+
windowMs: ACTION_RATE_WINDOW_MS,
|
|
30
|
+
bucket: "pause",
|
|
31
|
+
},
|
|
32
|
+
"/__authtics_host_apis__/restart": {
|
|
33
|
+
maxRequests: 1,
|
|
34
|
+
windowMs: ACTION_RATE_WINDOW_MS,
|
|
35
|
+
bucket: "lifecycle",
|
|
36
|
+
},
|
|
37
|
+
"/__authtics_host_apis__/shutdown": {
|
|
38
|
+
maxRequests: 1,
|
|
39
|
+
windowMs: ACTION_RATE_WINDOW_MS,
|
|
40
|
+
bucket: "lifecycle",
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
const failedDatGuesses = new Map();
|
|
44
|
+
const blockedDatIps = new Map();
|
|
45
|
+
const actionRequestTimes = new Map();
|
|
46
|
+
let datLockoutFile = "";
|
|
47
|
+
function fingerprint(value) {
|
|
48
|
+
return createHash("sha256").update(value).digest("hex");
|
|
49
|
+
}
|
|
50
|
+
function clientIpForRateLimit(req) {
|
|
51
|
+
return (req.ip || req.socket.remoteAddress || "unknown").trim();
|
|
52
|
+
}
|
|
53
|
+
function persistDatLockouts() {
|
|
54
|
+
if (!datLockoutFile)
|
|
55
|
+
return;
|
|
56
|
+
try {
|
|
57
|
+
fs.mkdirSync(path.dirname(datLockoutFile), { recursive: true });
|
|
58
|
+
const temporaryPath = `${datLockoutFile}.${process.pid}.tmp`;
|
|
59
|
+
fs.writeFileSync(temporaryPath, JSON.stringify(Object.fromEntries(blockedDatIps)), { encoding: "utf8", mode: 0o600 });
|
|
60
|
+
try {
|
|
61
|
+
fs.chmodSync(temporaryPath, 0o600);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
}
|
|
65
|
+
fs.renameSync(temporaryPath, datLockoutFile);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.error("[HostJS] Could not persist DAT lockouts:", error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function loadDatLockouts() {
|
|
72
|
+
if (!datLockoutFile)
|
|
73
|
+
return;
|
|
74
|
+
try {
|
|
75
|
+
const parsed = JSON.parse(fs.readFileSync(datLockoutFile, "utf8"));
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
for (const [ipHash, until] of Object.entries(parsed)) {
|
|
78
|
+
if (typeof until === "number" && until > now) {
|
|
79
|
+
blockedDatIps.set(ipHash, until);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (error.code !== "ENOENT") {
|
|
85
|
+
console.warn("[HostJS] Could not read DAT lockouts; starting with an empty lockout list.");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function sendDatGuardResponse(res, statusCode, code, message, retryAfterSeconds) {
|
|
90
|
+
const headers = {
|
|
91
|
+
"Content-Type": "application/json",
|
|
92
|
+
"Cache-Control": "no-store",
|
|
93
|
+
};
|
|
94
|
+
if (retryAfterSeconds !== undefined) {
|
|
95
|
+
headers["Retry-After"] = String(retryAfterSeconds);
|
|
96
|
+
}
|
|
97
|
+
res.writeHead(statusCode, headers);
|
|
98
|
+
res.end(JSON.stringify({ error: code, message, retryAfter: retryAfterSeconds }));
|
|
99
|
+
}
|
|
100
|
+
function recordInvalidDatGuess(ipHash, suppliedDat, now) {
|
|
101
|
+
let guesses = failedDatGuesses.get(ipHash);
|
|
102
|
+
if (!guesses) {
|
|
103
|
+
guesses = new Map();
|
|
104
|
+
failedDatGuesses.set(ipHash, guesses);
|
|
105
|
+
}
|
|
106
|
+
for (const [candidateHash, timestamp] of guesses) {
|
|
107
|
+
if (now - timestamp >= DAT_GUESS_WINDOW_MS)
|
|
108
|
+
guesses.delete(candidateHash);
|
|
109
|
+
}
|
|
110
|
+
guesses.set(fingerprint(suppliedDat), now);
|
|
111
|
+
if (guesses.size >= DAT_DISTINCT_GUESS_LIMIT) {
|
|
112
|
+
blockedDatIps.set(ipHash, now + DAT_IP_BLOCK_MS);
|
|
113
|
+
failedDatGuesses.delete(ipHash);
|
|
114
|
+
persistDatLockouts();
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
function checkActionRateLimit(ipHash, pathname, now) {
|
|
120
|
+
const rule = ACTION_RATE_RULES[pathname];
|
|
121
|
+
if (!rule)
|
|
122
|
+
return undefined;
|
|
123
|
+
const key = `${ipHash}:${rule.bucket}`;
|
|
124
|
+
const recent = (actionRequestTimes.get(key) ?? [])
|
|
125
|
+
.filter(timestamp => now - timestamp < rule.windowMs);
|
|
126
|
+
if (recent.length >= rule.maxRequests) {
|
|
127
|
+
actionRequestTimes.set(key, recent);
|
|
128
|
+
return Math.max(1, Math.ceil((recent[0] + rule.windowMs - now) / 1000));
|
|
129
|
+
}
|
|
130
|
+
recent.push(now);
|
|
131
|
+
actionRequestTimes.set(key, recent);
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
function guardDatApiRequest(req, res, pathname) {
|
|
135
|
+
const now = Date.now();
|
|
136
|
+
const ipHash = fingerprint(clientIpForRateLimit(req));
|
|
137
|
+
const blockedUntil = blockedDatIps.get(ipHash);
|
|
138
|
+
if (blockedUntil !== undefined) {
|
|
139
|
+
if (blockedUntil > now) {
|
|
140
|
+
sendDatGuardResponse(res, 429, "DAT_IP_BLOCKED", "DAT-protected endpoints are temporarily unavailable from this IP.", Math.ceil((blockedUntil - now) / 1000));
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
blockedDatIps.delete(ipHash);
|
|
144
|
+
persistDatLockouts();
|
|
145
|
+
}
|
|
146
|
+
const configuredDats = typeof config_.dat === "string"
|
|
147
|
+
? [config_.dat]
|
|
148
|
+
: Array.isArray(config_.dat)
|
|
149
|
+
? config_.dat.filter((value) => typeof value === "string")
|
|
150
|
+
: [];
|
|
151
|
+
const datRestrictionEnabled = config_.dat !== undefined && config_.dat !== null;
|
|
152
|
+
const datMatches = !datRestrictionEnabled ||
|
|
153
|
+
configuredDats.includes(req.dat ?? "");
|
|
154
|
+
if (datRestrictionEnabled && !datMatches) {
|
|
155
|
+
const justBlocked = recordInvalidDatGuess(ipHash, req.dat ?? "<missing>", now);
|
|
156
|
+
if (justBlocked) {
|
|
157
|
+
sendDatGuardResponse(res, 429, "DAT_IP_BLOCKED", "Too many distinct invalid DAT values. DAT-protected endpoints are blocked for 24 hours.", Math.ceil(DAT_IP_BLOCK_MS / 1000));
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
sendDatGuardResponse(res, 401, "INVALID_DAT", "DAT authorization failed.");
|
|
161
|
+
}
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
if (datRestrictionEnabled) {
|
|
165
|
+
failedDatGuesses.delete(ipHash);
|
|
166
|
+
}
|
|
167
|
+
const retryAfter = checkActionRateLimit(ipHash, pathname, now);
|
|
168
|
+
if (retryAfter !== undefined) {
|
|
169
|
+
sendDatGuardResponse(res, 429, "ACTION_RATE_LIMITED", "This action is temporarily rate-limited. Try again later.", retryAfter);
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
20
174
|
function safeWatch(target, onChange) {
|
|
21
175
|
const shouldReact = (filename) => {
|
|
22
176
|
if (!filename)
|
|
23
177
|
return false;
|
|
24
178
|
if (filename.includes('.authtics_host'))
|
|
25
179
|
return false;
|
|
180
|
+
if (filename.includes('.hostjs'))
|
|
181
|
+
return false;
|
|
182
|
+
if (filename.includes('.upm'))
|
|
183
|
+
return false;
|
|
184
|
+
if (filename.includes('.tsbuildinfo'))
|
|
185
|
+
return false;
|
|
186
|
+
if (filename.includes('.local'))
|
|
187
|
+
return false;
|
|
188
|
+
if (filename.includes('.cache'))
|
|
189
|
+
return false;
|
|
26
190
|
if (filename.includes('node_modules'))
|
|
27
191
|
return false;
|
|
28
192
|
return /\.(tsx?|jsx?|html|css)$/i.test(filename);
|
|
@@ -69,20 +233,89 @@ function notifyHmrClients() {
|
|
|
69
233
|
hmrDebounceTimer = setTimeout(() => {
|
|
70
234
|
hmrDebounceTimer = null;
|
|
71
235
|
for (const client of hmrClients) {
|
|
236
|
+
if (client.destroyed || client.writableEnded) {
|
|
237
|
+
hmrClients.delete(client);
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
72
240
|
try {
|
|
73
241
|
client.write('data: reload\n\n');
|
|
74
242
|
}
|
|
75
243
|
catch {
|
|
76
244
|
hmrClients.delete(client);
|
|
245
|
+
client.destroy();
|
|
77
246
|
}
|
|
78
247
|
}
|
|
79
248
|
}, 120);
|
|
80
249
|
}
|
|
81
250
|
const MIME_TYPES = {
|
|
82
251
|
'.html': 'text/html',
|
|
252
|
+
'.htm': 'text/html',
|
|
83
253
|
'.css': 'text/css',
|
|
84
254
|
'.js': 'application/javascript',
|
|
255
|
+
'.mjs': 'application/javascript',
|
|
256
|
+
'.jsx': 'text/jsx',
|
|
257
|
+
'.ts': 'text/typescript',
|
|
258
|
+
'.tsx': 'text/tsx',
|
|
85
259
|
'.json': 'application/json',
|
|
260
|
+
'.jsonld': 'application/ld+json',
|
|
261
|
+
'.map': 'application/json',
|
|
262
|
+
'.c': 'text/x-csrc',
|
|
263
|
+
'.cpp': 'text/x-c++src',
|
|
264
|
+
'.h': 'text/x-chdr',
|
|
265
|
+
'.hpp': 'text/x-c++hdr',
|
|
266
|
+
'.cs': 'text/x-csharp',
|
|
267
|
+
'.go': 'text/x-gosrc',
|
|
268
|
+
'.py': 'text/x-python',
|
|
269
|
+
'.rb': 'text/x-ruby',
|
|
270
|
+
'.php': 'application/x-httpd-php',
|
|
271
|
+
'.java': 'text/x-java-source',
|
|
272
|
+
'.kt': 'text/x-kotlin',
|
|
273
|
+
'.swift': 'text/x-swift',
|
|
274
|
+
'.rs': 'text/rust',
|
|
275
|
+
'.sh': 'application/x-sh',
|
|
276
|
+
'.bat': 'application/x-msdos-program',
|
|
277
|
+
'.ps1': 'application/x-powershell',
|
|
278
|
+
'.pl': 'text/x-perl',
|
|
279
|
+
'.r': 'text/x-rsrc',
|
|
280
|
+
'.sql': 'text/x-sql',
|
|
281
|
+
'.xml': 'application/xml',
|
|
282
|
+
'.yaml': 'text/yaml',
|
|
283
|
+
'.yml': 'text/yaml',
|
|
284
|
+
'.toml': 'text/toml',
|
|
285
|
+
'.ini': 'text/plain',
|
|
286
|
+
'.cfg': 'text/plain',
|
|
287
|
+
'.conf': 'text/plain',
|
|
288
|
+
'.md': 'text/markdown',
|
|
289
|
+
'.txt': 'text/plain',
|
|
290
|
+
'.csv': 'text/csv',
|
|
291
|
+
'.tsv': 'text/tab-separated-values',
|
|
292
|
+
'.pdf': 'application/pdf',
|
|
293
|
+
'.wasm': 'application/wasm',
|
|
294
|
+
'.elf': 'application/x-elf',
|
|
295
|
+
'.exe': 'application/x-msdownload',
|
|
296
|
+
'.dll': 'application/x-msdownload',
|
|
297
|
+
'.o': 'application/x-object',
|
|
298
|
+
'.a': 'application/x-archive',
|
|
299
|
+
'.so': 'application/x-sharedlib',
|
|
300
|
+
'.dylib': 'application/x-apple-binary',
|
|
301
|
+
'.msi': 'application/x-msi',
|
|
302
|
+
'.apk': 'application/vnd.android.package-archive',
|
|
303
|
+
'.bin': 'application/octet-stream',
|
|
304
|
+
'.iso': 'application/x-iso9660-image',
|
|
305
|
+
'.img': 'application/octet-stream',
|
|
306
|
+
'.dmg': 'application/x-apple-diskimage',
|
|
307
|
+
'.vmdk': 'application/x-virtualbox-vmdk',
|
|
308
|
+
'.vhd': 'application/x-vhd',
|
|
309
|
+
'.qcow2': 'application/x-qcow2',
|
|
310
|
+
'.zip': 'application/zip',
|
|
311
|
+
'.tar': 'application/x-tar',
|
|
312
|
+
'.gz': 'application/gzip',
|
|
313
|
+
'.tgz': 'application/gzip',
|
|
314
|
+
'.bz2': 'application/x-bzip2',
|
|
315
|
+
'.xz': 'application/x-xz',
|
|
316
|
+
'.rar': 'application/vnd.rar',
|
|
317
|
+
'.7z': 'application/x-7z-compressed',
|
|
318
|
+
'.cab': 'application/vnd.ms-cab-compressed',
|
|
86
319
|
'.png': 'image/png',
|
|
87
320
|
'.jpg': 'image/jpeg',
|
|
88
321
|
'.jpeg': 'image/jpeg',
|
|
@@ -90,13 +323,49 @@ const MIME_TYPES = {
|
|
|
90
323
|
'.svg': 'image/svg+xml',
|
|
91
324
|
'.ico': 'image/x-icon',
|
|
92
325
|
'.webp': 'image/webp',
|
|
326
|
+
'.avif': 'image/avif',
|
|
327
|
+
'.bmp': 'image/bmp',
|
|
328
|
+
'.tiff': 'image/tiff',
|
|
329
|
+
'.tif': 'image/tiff',
|
|
330
|
+
'.psd': 'image/vnd.adobe.photoshop',
|
|
331
|
+
'.ai': 'application/postscript',
|
|
332
|
+
'.mp4': 'video/mp4',
|
|
333
|
+
'.mkv': 'video/x-matroska',
|
|
334
|
+
'.avi': 'video/x-msvideo',
|
|
335
|
+
'.mov': 'video/quicktime',
|
|
336
|
+
'.wmv': 'video/x-ms-wmv',
|
|
337
|
+
'.webm': 'video/webm',
|
|
338
|
+
'.flv': 'video/x-flv',
|
|
339
|
+
'.mp3': 'audio/mpeg',
|
|
340
|
+
'.wav': 'audio/wav',
|
|
341
|
+
'.ogg': 'audio/ogg',
|
|
342
|
+
'.oga': 'audio/ogg',
|
|
343
|
+
'.ogv': 'video/ogg',
|
|
344
|
+
'.flac': 'audio/flac',
|
|
345
|
+
'.aac': 'audio/aac',
|
|
346
|
+
'.m4a': 'audio/x-m4a',
|
|
93
347
|
'.woff': 'font/woff',
|
|
94
348
|
'.woff2': 'font/woff2',
|
|
95
349
|
'.ttf': 'font/ttf',
|
|
96
|
-
'.
|
|
97
|
-
'.
|
|
98
|
-
'.
|
|
99
|
-
'.
|
|
350
|
+
'.otf': 'font/otf',
|
|
351
|
+
'.eot': 'application/vnd.ms-fontobject',
|
|
352
|
+
'.sqlite': 'application/vnd.sqlite3',
|
|
353
|
+
'.sqlite3': 'application/vnd.sqlite3',
|
|
354
|
+
'.db': 'application/octet-stream',
|
|
355
|
+
'.mdb': 'application/x-msaccess',
|
|
356
|
+
'.obj': 'model/obj',
|
|
357
|
+
'.stl': 'model/stl',
|
|
358
|
+
'.gltf': 'model/gltf+json',
|
|
359
|
+
'.glb': 'model/gltf-binary',
|
|
360
|
+
'.fbx': 'application/octet-stream',
|
|
361
|
+
'.dae': 'model/vnd.collada+xml',
|
|
362
|
+
'.3ds': 'image/x-3ds',
|
|
363
|
+
'.doc': 'application/msword',
|
|
364
|
+
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
365
|
+
'.xls': 'application/vnd.ms-excel',
|
|
366
|
+
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
367
|
+
'.ppt': 'application/vnd.ms-powerpoint',
|
|
368
|
+
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
100
369
|
};
|
|
101
370
|
const getCpu = () => {
|
|
102
371
|
return new Promise((resolve) => {
|
|
@@ -260,7 +529,7 @@ export default class App {
|
|
|
260
529
|
stdio: 'inherit'
|
|
261
530
|
});
|
|
262
531
|
child.unref();
|
|
263
|
-
this.server
|
|
532
|
+
this.server?.close(() => {
|
|
264
533
|
process.kill(process.pid, 'SIGTERM');
|
|
265
534
|
});
|
|
266
535
|
}
|
|
@@ -274,7 +543,7 @@ export default class App {
|
|
|
274
543
|
if (datMatched) {
|
|
275
544
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
276
545
|
res.end(JSON.stringify({ message: "Shutting down..." }));
|
|
277
|
-
this.server
|
|
546
|
+
this.server?.close(() => {
|
|
278
547
|
process.kill(process.pid, 'SIGTERM');
|
|
279
548
|
});
|
|
280
549
|
}
|
|
@@ -390,15 +659,15 @@ export default class App {
|
|
|
390
659
|
this.server = await start();
|
|
391
660
|
}
|
|
392
661
|
catch (error) {
|
|
393
|
-
console.error("Server failed to start:", error);
|
|
662
|
+
console.error("[HostJS] Server failed to start: ", error);
|
|
394
663
|
}
|
|
395
664
|
}
|
|
396
|
-
get(routePath, handler) {
|
|
397
|
-
routes.set(`GET:${routePath}`, handler);
|
|
398
|
-
}
|
|
399
665
|
registerApp(route) {
|
|
400
666
|
routes.set(`APP_TSX`, route);
|
|
401
667
|
}
|
|
668
|
+
get(routePath, handler) {
|
|
669
|
+
routes.set(`GET:${routePath}`, handler);
|
|
670
|
+
}
|
|
402
671
|
post(routePath, handler) {
|
|
403
672
|
routes.set(`POST:${routePath}`, handler);
|
|
404
673
|
}
|
|
@@ -408,6 +677,15 @@ export default class App {
|
|
|
408
677
|
delete(routePath, handler) {
|
|
409
678
|
routes.set(`DELETE:${routePath}`, handler);
|
|
410
679
|
}
|
|
680
|
+
use(handler) {
|
|
681
|
+
const randomMultiplier = BigInt(Math.floor(Math.random() * 1000000000));
|
|
682
|
+
let newId = randomMultiplier * 1000000000000000000000000000000n;
|
|
683
|
+
routes.set(`USE:${newId}`, handler);
|
|
684
|
+
}
|
|
685
|
+
on(method, handler) {
|
|
686
|
+
if (this.server)
|
|
687
|
+
this.server.on(method, handler);
|
|
688
|
+
}
|
|
411
689
|
remove(method, routePath) {
|
|
412
690
|
if (routes.has(`${method}:${routePath}`))
|
|
413
691
|
routes.delete(`${method}:${routePath}`);
|
|
@@ -422,6 +700,12 @@ async function start() {
|
|
|
422
700
|
}
|
|
423
701
|
catch {
|
|
424
702
|
}
|
|
703
|
+
const requestedBuildStyle = config.buildStyle ?? "default";
|
|
704
|
+
if (requestedBuildStyle !== "default" && requestedBuildStyle !== "super") {
|
|
705
|
+
throw new Error('[HostJS] Invalid buildStyle. Expected "default" or "super".');
|
|
706
|
+
}
|
|
707
|
+
const buildStyle = requestedBuildStyle;
|
|
708
|
+
setBuildStyle(buildStyle, process.cwd());
|
|
425
709
|
const PORT = config.port ?? 3000;
|
|
426
710
|
const renderType = config.renderType ?? 'ssr';
|
|
427
711
|
const showPanel = config.devMode !== false;
|
|
@@ -431,6 +715,9 @@ async function start() {
|
|
|
431
715
|
const pagesDir = config.autoPage === true ? path.join(process.cwd(), config.autoPageDir || "") : undefined;
|
|
432
716
|
const publicDir = config.autoFile === true ? path.join(process.cwd(), config.autoFileDir || "") : undefined;
|
|
433
717
|
const apiDir = pagesDir ? path.join(pagesDir, "api") : undefined;
|
|
718
|
+
if (buildStyle === "super") {
|
|
719
|
+
initializeSuperBuildOutput(process.cwd(), config, pagesDir, publicDir);
|
|
720
|
+
}
|
|
434
721
|
const server = createServer(async (req, res) => {
|
|
435
722
|
const { url = "/", method } = req;
|
|
436
723
|
const actuallyShowPanel = showPanel;
|
|
@@ -542,8 +829,17 @@ async function start() {
|
|
|
542
829
|
res.writeHead(302, { Location: req.url + '/' });
|
|
543
830
|
res.end();
|
|
544
831
|
};
|
|
545
|
-
res.setCookie = function (name, value) {
|
|
546
|
-
|
|
832
|
+
res.setCookie = function (name, value, sameSite = "Lax", HttpOnly = true, Secure = true, MaxAge = null) {
|
|
833
|
+
const cookieParts = [`${name}=${value}`, "Path=/"];
|
|
834
|
+
if (MaxAge !== null)
|
|
835
|
+
cookieParts.push(`Max-Age=${MaxAge}`);
|
|
836
|
+
if (HttpOnly)
|
|
837
|
+
cookieParts.push("HttpOnly");
|
|
838
|
+
if (Secure)
|
|
839
|
+
cookieParts.push("Secure");
|
|
840
|
+
if (sameSite)
|
|
841
|
+
cookieParts.push(`SameSite=${sameSite}`);
|
|
842
|
+
res.setHeader("Set-Cookie", cookieParts.join("; "));
|
|
547
843
|
};
|
|
548
844
|
res.rmvCookie = function (name) {
|
|
549
845
|
res.setHeader("Set-Cookie", `${name}=; Path=/; Max-Age=0`);
|
|
@@ -553,9 +849,28 @@ async function start() {
|
|
|
553
849
|
pathname = safePath;
|
|
554
850
|
if (pathname !== "/" && pathname.endsWith("/"))
|
|
555
851
|
pathname = pathname.slice(0, -1);
|
|
556
|
-
if (!pathname.includes('/__authtics') && !pathname.includes('/hmr')) {
|
|
852
|
+
if (!pathname.includes('/__authtics') && !pathname.includes('/hmr') && !pathname.includes('/srtws')) {
|
|
557
853
|
totalRequests++;
|
|
558
854
|
}
|
|
855
|
+
const useRoutes = Array.from(routes.entries()).filter(([key]) => key.startsWith("USE:"));
|
|
856
|
+
if (useRoutes.length > 0 && !pathname.includes('/__authtics') && !pathname.includes('/hmr') && !pathname.includes('/srtws')) {
|
|
857
|
+
let i = 0;
|
|
858
|
+
const next = () => {
|
|
859
|
+
i++;
|
|
860
|
+
if (i >= useRoutes.length)
|
|
861
|
+
return;
|
|
862
|
+
const [key, handler] = useRoutes[i];
|
|
863
|
+
if (typeof handler === 'function') {
|
|
864
|
+
const id = BigInt(key.split(":")[1]);
|
|
865
|
+
handler(req, res, id, next);
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
const [key, handler] = useRoutes[0];
|
|
869
|
+
if (typeof handler === 'function') {
|
|
870
|
+
const id = BigInt(key.split(":")[1]);
|
|
871
|
+
handler(req, res, id, next);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
559
874
|
const routeKey = `${method}:${pathname}`;
|
|
560
875
|
if (routes.has(routeKey)) {
|
|
561
876
|
if (Array.isArray(config_.dat) && !config_.dat.includes(req.dat) && usersPaused && !routeKey.endsWith('.js')) {
|
|
@@ -564,8 +879,8 @@ async function start() {
|
|
|
564
879
|
if (fs.existsSync(pausedTsxFile || "")) {
|
|
565
880
|
try {
|
|
566
881
|
let html = await renderPage(pausedTsxFile || "", url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
567
|
-
routes.set(`GET:${p}`, (
|
|
568
|
-
res.setHeader("Content-Type", "application/javascript");
|
|
882
|
+
routes.set(`GET:${p}`, (_, res) => {
|
|
883
|
+
res.setHeader("Content-Type", p.toLowerCase().endsWith(".map") ? "application/json" : "application/javascript");
|
|
569
884
|
res.end(f);
|
|
570
885
|
});
|
|
571
886
|
}, (p) => { routes.delete(`GET:${p}`); }, req.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
@@ -574,7 +889,7 @@ async function start() {
|
|
|
574
889
|
return res.end(html);
|
|
575
890
|
}
|
|
576
891
|
catch (e) {
|
|
577
|
-
console.error("[
|
|
892
|
+
console.error("[HostJS] renderPage error:", e.message);
|
|
578
893
|
res.writeHead(500);
|
|
579
894
|
return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
|
|
580
895
|
}
|
|
@@ -587,8 +902,8 @@ async function start() {
|
|
|
587
902
|
if (fs.existsSync(appPath)) {
|
|
588
903
|
try {
|
|
589
904
|
let html = await renderPage(appPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
590
|
-
routes.set(`GET:${p}`, (
|
|
591
|
-
res.setHeader("Content-Type", "application/javascript");
|
|
905
|
+
routes.set(`GET:${p}`, (_, res) => {
|
|
906
|
+
res.setHeader("Content-Type", p.toLowerCase().endsWith(".map") ? "application/json" : "application/javascript");
|
|
592
907
|
res.end(f);
|
|
593
908
|
});
|
|
594
909
|
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType);
|
|
@@ -597,7 +912,7 @@ async function start() {
|
|
|
597
912
|
return res.end(html);
|
|
598
913
|
}
|
|
599
914
|
catch (e) {
|
|
600
|
-
console.error("[
|
|
915
|
+
console.error("[HostJS] renderPage error:", e.message);
|
|
601
916
|
res.writeHead(500);
|
|
602
917
|
return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
|
|
603
918
|
}
|
|
@@ -617,8 +932,8 @@ async function start() {
|
|
|
617
932
|
if (fs.existsSync(pausedTsxFile || "")) {
|
|
618
933
|
try {
|
|
619
934
|
let html = await renderPage(pausedTsxFile || "", url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
620
|
-
routes.set(`GET:${p}`, (
|
|
621
|
-
res.setHeader("Content-Type", "application/javascript");
|
|
935
|
+
routes.set(`GET:${p}`, (_, res) => {
|
|
936
|
+
res.setHeader("Content-Type", p.toLowerCase().endsWith(".map") ? "application/json" : "application/javascript");
|
|
622
937
|
res.end(f);
|
|
623
938
|
});
|
|
624
939
|
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
@@ -627,7 +942,7 @@ async function start() {
|
|
|
627
942
|
return res.end(html);
|
|
628
943
|
}
|
|
629
944
|
catch (e) {
|
|
630
|
-
console.error("[
|
|
945
|
+
console.error("[HostJS] renderPage error:", e.message);
|
|
631
946
|
res.writeHead(500);
|
|
632
947
|
return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
|
|
633
948
|
}
|
|
@@ -640,8 +955,8 @@ async function start() {
|
|
|
640
955
|
if (fs.existsSync(appPath)) {
|
|
641
956
|
try {
|
|
642
957
|
let html = await renderPage(appPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
643
|
-
routes.set(`GET:${p}`, (
|
|
644
|
-
res.setHeader("Content-Type", "application/javascript");
|
|
958
|
+
routes.set(`GET:${p}`, (_, res) => {
|
|
959
|
+
res.setHeader("Content-Type", p.toLowerCase().endsWith(".map") ? "application/json" : "application/javascript");
|
|
645
960
|
res.end(f);
|
|
646
961
|
});
|
|
647
962
|
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType);
|
|
@@ -650,7 +965,7 @@ async function start() {
|
|
|
650
965
|
return res.end(html);
|
|
651
966
|
}
|
|
652
967
|
catch (e) {
|
|
653
|
-
console.error("[
|
|
968
|
+
console.error("[HostJS] renderPage error:", e.message);
|
|
654
969
|
res.writeHead(500);
|
|
655
970
|
return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
|
|
656
971
|
}
|
|
@@ -665,6 +980,17 @@ async function start() {
|
|
|
665
980
|
}));
|
|
666
981
|
}
|
|
667
982
|
const handler = routes.get(routeKey);
|
|
983
|
+
if (typeof handler === "function" && routeKey.toLowerCase().endsWith(".map")) {
|
|
984
|
+
const configuredDAT = config_.dat;
|
|
985
|
+
const hasMatchingDAT = configuredDAT === undefined ||
|
|
986
|
+
(Array.isArray(configuredDAT)
|
|
987
|
+
? configuredDAT.includes(req.dat ?? "")
|
|
988
|
+
: configuredDAT === req.dat);
|
|
989
|
+
if (!hasMatchingDAT) {
|
|
990
|
+
res.writeHead(404);
|
|
991
|
+
return res.end();
|
|
992
|
+
}
|
|
993
|
+
}
|
|
668
994
|
if (typeof handler === 'string') {
|
|
669
995
|
return;
|
|
670
996
|
}
|
|
@@ -675,17 +1001,24 @@ async function start() {
|
|
|
675
1001
|
if (pathname === '/hmr') {
|
|
676
1002
|
res.writeHead(200, {
|
|
677
1003
|
'Content-Type': 'text/event-stream',
|
|
678
|
-
'Cache-Control': 'no-cache',
|
|
1004
|
+
'Cache-Control': 'no-cache, no-transform',
|
|
679
1005
|
'Connection': 'keep-alive',
|
|
680
1006
|
'Access-Control-Allow-Origin': '*',
|
|
681
1007
|
});
|
|
682
1008
|
res.write('data: connected\n\n');
|
|
683
1009
|
hmrClients.add(res);
|
|
684
1010
|
users++;
|
|
685
|
-
|
|
1011
|
+
const cleanup = () => {
|
|
1012
|
+
hmrClients.delete(res);
|
|
1013
|
+
users = Math.max(0, users - 1);
|
|
1014
|
+
};
|
|
1015
|
+
req.on('close', cleanup);
|
|
1016
|
+
req.on('aborted', cleanup);
|
|
1017
|
+
res.on('close', cleanup);
|
|
1018
|
+
res.on('error', cleanup);
|
|
686
1019
|
return;
|
|
687
1020
|
}
|
|
688
|
-
if (pathname === '/
|
|
1021
|
+
if (pathname === '/srtws') {
|
|
689
1022
|
return;
|
|
690
1023
|
}
|
|
691
1024
|
if (Array.isArray(config_.dat) && !config_.dat.includes(req.dat) && usersPaused) {
|
|
@@ -694,8 +1027,8 @@ async function start() {
|
|
|
694
1027
|
if (fs.existsSync(pausedTsxFile || "")) {
|
|
695
1028
|
try {
|
|
696
1029
|
let html = await renderPage(pausedTsxFile || "", url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
697
|
-
routes.set(`GET:${p}`, (
|
|
698
|
-
res.setHeader("Content-Type", "application/javascript");
|
|
1030
|
+
routes.set(`GET:${p}`, (_, res) => {
|
|
1031
|
+
res.setHeader("Content-Type", p.toLowerCase().endsWith(".map") ? "application/json" : "application/javascript");
|
|
699
1032
|
res.end(f);
|
|
700
1033
|
});
|
|
701
1034
|
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
@@ -704,7 +1037,7 @@ async function start() {
|
|
|
704
1037
|
return res.end(html);
|
|
705
1038
|
}
|
|
706
1039
|
catch (e) {
|
|
707
|
-
console.error("[
|
|
1040
|
+
console.error("[HostJS] renderPage error:", e.message);
|
|
708
1041
|
res.writeHead(500);
|
|
709
1042
|
return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
|
|
710
1043
|
}
|
|
@@ -717,8 +1050,8 @@ async function start() {
|
|
|
717
1050
|
if (fs.existsSync(appPath)) {
|
|
718
1051
|
try {
|
|
719
1052
|
let html = await renderPage(appPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
720
|
-
routes.set(`GET:${p}`, (
|
|
721
|
-
res.setHeader("Content-Type", "application/javascript");
|
|
1053
|
+
routes.set(`GET:${p}`, (_, res) => {
|
|
1054
|
+
res.setHeader("Content-Type", p.toLowerCase().endsWith(".map") ? "application/json" : "application/javascript");
|
|
722
1055
|
res.end(f);
|
|
723
1056
|
});
|
|
724
1057
|
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
@@ -727,7 +1060,7 @@ async function start() {
|
|
|
727
1060
|
return res.end(html);
|
|
728
1061
|
}
|
|
729
1062
|
catch (e) {
|
|
730
|
-
console.error("[
|
|
1063
|
+
console.error("[HostJS] renderPage error:", e.message);
|
|
731
1064
|
res.writeHead(500);
|
|
732
1065
|
return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
|
|
733
1066
|
}
|
|
@@ -747,8 +1080,8 @@ async function start() {
|
|
|
747
1080
|
if (fs.existsSync(pausedTsxFile || "")) {
|
|
748
1081
|
try {
|
|
749
1082
|
let html = await renderPage(pausedTsxFile || "", url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
750
|
-
routes.set(`GET:${p}`, (
|
|
751
|
-
res.setHeader("Content-Type", "application/javascript");
|
|
1083
|
+
routes.set(`GET:${p}`, (_, res) => {
|
|
1084
|
+
res.setHeader("Content-Type", p.toLowerCase().endsWith(".map") ? "application/json" : "application/javascript");
|
|
752
1085
|
res.end(f);
|
|
753
1086
|
});
|
|
754
1087
|
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
@@ -757,7 +1090,7 @@ async function start() {
|
|
|
757
1090
|
return res.end(html);
|
|
758
1091
|
}
|
|
759
1092
|
catch (e) {
|
|
760
|
-
console.error("[
|
|
1093
|
+
console.error("[HostJS] renderPage error:", e.message);
|
|
761
1094
|
res.writeHead(500);
|
|
762
1095
|
return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
|
|
763
1096
|
}
|
|
@@ -770,8 +1103,8 @@ async function start() {
|
|
|
770
1103
|
if (fs.existsSync(appPath)) {
|
|
771
1104
|
try {
|
|
772
1105
|
let html = await renderPage(appPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
773
|
-
routes.set(`GET:${p}`, (
|
|
774
|
-
res.setHeader("Content-Type", "application/javascript");
|
|
1106
|
+
routes.set(`GET:${p}`, (_, res) => {
|
|
1107
|
+
res.setHeader("Content-Type", p.toLowerCase().endsWith(".map") ? "application/json" : "application/javascript");
|
|
775
1108
|
res.end(f);
|
|
776
1109
|
});
|
|
777
1110
|
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
@@ -780,7 +1113,7 @@ async function start() {
|
|
|
780
1113
|
return res.end(html);
|
|
781
1114
|
}
|
|
782
1115
|
catch (e) {
|
|
783
|
-
console.error("[
|
|
1116
|
+
console.error("[HostJS] renderPage error:", e.message);
|
|
784
1117
|
res.writeHead(500);
|
|
785
1118
|
return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
|
|
786
1119
|
}
|
|
@@ -814,12 +1147,12 @@ async function start() {
|
|
|
814
1147
|
const handler = apiModule.default ?? apiModule.handler ?? null;
|
|
815
1148
|
if (typeof handler !== 'function') {
|
|
816
1149
|
res.writeHead(500);
|
|
817
|
-
return res.end(`[
|
|
1150
|
+
return res.end(`[HostJS] API route ${pathname} must export a default function`);
|
|
818
1151
|
}
|
|
819
1152
|
return handler(req, res);
|
|
820
1153
|
}
|
|
821
1154
|
catch (e) {
|
|
822
|
-
console.error(`[
|
|
1155
|
+
console.error(`[HostJS] API route error (${pathname}):`, e.message);
|
|
823
1156
|
res.writeHead(500);
|
|
824
1157
|
return res.end(JSON.stringify({ error: e.message }));
|
|
825
1158
|
}
|
|
@@ -828,8 +1161,8 @@ async function start() {
|
|
|
828
1161
|
if (fs.existsSync(notFoundPath)) {
|
|
829
1162
|
try {
|
|
830
1163
|
let html = await renderPage(notFoundPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
831
|
-
routes.set(`GET:${p}`, (
|
|
832
|
-
res.setHeader("Content-Type", "application/javascript");
|
|
1164
|
+
routes.set(`GET:${p}`, (_, res) => {
|
|
1165
|
+
res.setHeader("Content-Type", p.toLowerCase().endsWith(".map") ? "application/json" : "application/javascript");
|
|
833
1166
|
res.end(f);
|
|
834
1167
|
});
|
|
835
1168
|
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
@@ -851,15 +1184,21 @@ async function start() {
|
|
|
851
1184
|
const urlPath = pathname === "/" ? "/index" : pathname;
|
|
852
1185
|
const tsxPath = pagesDir ? path.join(pagesDir, `${urlPath}.tsx`) : undefined;
|
|
853
1186
|
const htmlPath = pagesDir ? path.join(pagesDir, `${urlPath}.html`) : undefined;
|
|
854
|
-
if (
|
|
855
|
-
|
|
856
|
-
|
|
1187
|
+
if (srtws)
|
|
1188
|
+
deleteSrtwsServer();
|
|
1189
|
+
if (fs.existsSync(tempFileForTsx(tsxPath || "")) && renderType === "srtws" && !srtws) {
|
|
1190
|
+
srtws = handleSrtwsConnection(server, tsxPath || "", config, url, req.cookies.dat, (p) => { routes.delete(`GET:${p}`); }, (f, p) => {
|
|
1191
|
+
routes.set(`GET:${p}`, (_, res) => {
|
|
1192
|
+
res.setHeader("Content-Type", p.toLowerCase().endsWith(".map") ? "application/json" : "application/javascript");
|
|
1193
|
+
res.end(f);
|
|
1194
|
+
});
|
|
1195
|
+
});
|
|
857
1196
|
}
|
|
858
1197
|
if (tsxPath && fs.existsSync(tsxPath)) {
|
|
859
1198
|
try {
|
|
860
1199
|
const html = await renderPage(tsxPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
861
|
-
routes.set(`GET:${p}`, (
|
|
862
|
-
res.setHeader("Content-Type", "application/javascript");
|
|
1200
|
+
routes.set(`GET:${p}`, (_, res) => {
|
|
1201
|
+
res.setHeader("Content-Type", p.toLowerCase().endsWith(".map") ? "application/json" : "application/javascript");
|
|
863
1202
|
res.end(f);
|
|
864
1203
|
});
|
|
865
1204
|
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
@@ -873,8 +1212,9 @@ async function start() {
|
|
|
873
1212
|
}
|
|
874
1213
|
}
|
|
875
1214
|
if (htmlPath && fs.existsSync(htmlPath)) {
|
|
876
|
-
|
|
877
|
-
|
|
1215
|
+
const html = await handleHTML(fs.readFileSync(htmlPath, "utf-8"), actuallyShowPanel, config_.dat, req.cookies.dat, hotReload);
|
|
1216
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
1217
|
+
return res.end(html);
|
|
878
1218
|
}
|
|
879
1219
|
if (routes.has("APP_TSX")) {
|
|
880
1220
|
const appTsxPath = routes.get("APP_TSX");
|
|
@@ -882,8 +1222,8 @@ async function start() {
|
|
|
882
1222
|
if (fs.existsSync(appPath)) {
|
|
883
1223
|
try {
|
|
884
1224
|
const html = await renderPage(appPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
885
|
-
routes.set(`GET:${p}`, (
|
|
886
|
-
res.setHeader("Content-Type", "application/javascript");
|
|
1225
|
+
routes.set(`GET:${p}`, (_, res) => {
|
|
1226
|
+
res.setHeader("Content-Type", p.toLowerCase().endsWith(".map") ? "application/json" : "application/javascript");
|
|
887
1227
|
res.end(f);
|
|
888
1228
|
});
|
|
889
1229
|
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
@@ -901,8 +1241,8 @@ async function start() {
|
|
|
901
1241
|
if (fs.existsSync(notFoundPath)) {
|
|
902
1242
|
try {
|
|
903
1243
|
let html = await renderPage(notFoundPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
904
|
-
routes.set(`GET:${p}`, (
|
|
905
|
-
res.setHeader("Content-Type", "application/javascript");
|
|
1244
|
+
routes.set(`GET:${p}`, (_, res) => {
|
|
1245
|
+
res.setHeader("Content-Type", p.toLowerCase().endsWith(".map") ? "application/json" : "application/javascript");
|
|
906
1246
|
res.end(f);
|
|
907
1247
|
});
|
|
908
1248
|
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
@@ -944,7 +1284,17 @@ async function start() {
|
|
|
944
1284
|
if (fs.existsSync(_404Built))
|
|
945
1285
|
watchSet.add(_404Built);
|
|
946
1286
|
for (const dir of watchSet) {
|
|
947
|
-
safeWatch(dir,
|
|
1287
|
+
safeWatch(dir, () => {
|
|
1288
|
+
if (buildStyle === "super") {
|
|
1289
|
+
try {
|
|
1290
|
+
refreshSuperRouteManifest(process.cwd(), config, pagesDir, publicDir);
|
|
1291
|
+
}
|
|
1292
|
+
catch (error) {
|
|
1293
|
+
console.error("[HostJS] Failed to refresh Super route manifest:", error);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
notifyHmrClients();
|
|
1297
|
+
});
|
|
948
1298
|
}
|
|
949
1299
|
}
|
|
950
1300
|
return server;
|