@bananacool467/authtics-host 0.2.5 → 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 +145 -36
- 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 +427 -67
- package/dist/config.d.ts +8 -1
- package/dist/config.js +20 -0
- package/dist/devPanel.d.ts +11 -0
- package/dist/devPanel.js +415 -59
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/renderEngine.d.ts +5 -1
- package/dist/renderEngine.js +429 -79
- 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 +23 -11
- 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,26 +1,192 @@
|
|
|
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 } from "./renderEngine.js";
|
|
8
|
+
import { renderPage, handleSrtwsConnection, deleteSrtwsServer, handleHTML } from "./renderEngine.js";
|
|
9
|
+
import { setBuildStyle, tempFileForTsx } from "./rendererTempFiles.js";
|
|
10
|
+
import { initializeSuperBuildOutput, refreshSuperRouteManifest } from "./superBuildOutput.js";
|
|
8
11
|
let config_ = {};
|
|
9
12
|
let uptime = 0;
|
|
10
13
|
let totalRequests = 0;
|
|
11
14
|
let users = 0;
|
|
12
15
|
let usersPaused = false;
|
|
16
|
+
let srtws;
|
|
13
17
|
const jiti = createJiti(import.meta.url);
|
|
14
18
|
const routes = new Map();
|
|
15
19
|
const __filename = url.fileURLToPath(import.meta.url);
|
|
16
20
|
const __dirname = path.dirname(__filename);
|
|
17
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
|
+
}
|
|
18
174
|
function safeWatch(target, onChange) {
|
|
19
175
|
const shouldReact = (filename) => {
|
|
20
176
|
if (!filename)
|
|
21
177
|
return false;
|
|
22
178
|
if (filename.includes('.authtics_host'))
|
|
23
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;
|
|
24
190
|
if (filename.includes('node_modules'))
|
|
25
191
|
return false;
|
|
26
192
|
return /\.(tsx?|jsx?|html|css)$/i.test(filename);
|
|
@@ -67,20 +233,89 @@ function notifyHmrClients() {
|
|
|
67
233
|
hmrDebounceTimer = setTimeout(() => {
|
|
68
234
|
hmrDebounceTimer = null;
|
|
69
235
|
for (const client of hmrClients) {
|
|
236
|
+
if (client.destroyed || client.writableEnded) {
|
|
237
|
+
hmrClients.delete(client);
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
70
240
|
try {
|
|
71
241
|
client.write('data: reload\n\n');
|
|
72
242
|
}
|
|
73
243
|
catch {
|
|
74
244
|
hmrClients.delete(client);
|
|
245
|
+
client.destroy();
|
|
75
246
|
}
|
|
76
247
|
}
|
|
77
248
|
}, 120);
|
|
78
249
|
}
|
|
79
250
|
const MIME_TYPES = {
|
|
80
251
|
'.html': 'text/html',
|
|
252
|
+
'.htm': 'text/html',
|
|
81
253
|
'.css': 'text/css',
|
|
82
254
|
'.js': 'application/javascript',
|
|
255
|
+
'.mjs': 'application/javascript',
|
|
256
|
+
'.jsx': 'text/jsx',
|
|
257
|
+
'.ts': 'text/typescript',
|
|
258
|
+
'.tsx': 'text/tsx',
|
|
83
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',
|
|
84
319
|
'.png': 'image/png',
|
|
85
320
|
'.jpg': 'image/jpeg',
|
|
86
321
|
'.jpeg': 'image/jpeg',
|
|
@@ -88,13 +323,49 @@ const MIME_TYPES = {
|
|
|
88
323
|
'.svg': 'image/svg+xml',
|
|
89
324
|
'.ico': 'image/x-icon',
|
|
90
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',
|
|
91
347
|
'.woff': 'font/woff',
|
|
92
348
|
'.woff2': 'font/woff2',
|
|
93
349
|
'.ttf': 'font/ttf',
|
|
94
|
-
'.
|
|
95
|
-
'.
|
|
96
|
-
'.
|
|
97
|
-
'.
|
|
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',
|
|
98
369
|
};
|
|
99
370
|
const getCpu = () => {
|
|
100
371
|
return new Promise((resolve) => {
|
|
@@ -258,7 +529,7 @@ export default class App {
|
|
|
258
529
|
stdio: 'inherit'
|
|
259
530
|
});
|
|
260
531
|
child.unref();
|
|
261
|
-
this.server
|
|
532
|
+
this.server?.close(() => {
|
|
262
533
|
process.kill(process.pid, 'SIGTERM');
|
|
263
534
|
});
|
|
264
535
|
}
|
|
@@ -272,12 +543,12 @@ export default class App {
|
|
|
272
543
|
if (datMatched) {
|
|
273
544
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
274
545
|
res.end(JSON.stringify({ message: "Shutting down..." }));
|
|
275
|
-
this.server
|
|
546
|
+
this.server?.close(() => {
|
|
276
547
|
process.kill(process.pid, 'SIGTERM');
|
|
277
548
|
});
|
|
278
549
|
}
|
|
279
550
|
});
|
|
280
|
-
this.post("/__authtics_host_apis__/get-
|
|
551
|
+
this.post("/__authtics_host_apis__/get-render-type", async (req, res) => {
|
|
281
552
|
let datMatched = false;
|
|
282
553
|
if (config_.dat && config_.dat == req.dat)
|
|
283
554
|
datMatched = true;
|
|
@@ -286,7 +557,7 @@ export default class App {
|
|
|
286
557
|
if (datMatched) {
|
|
287
558
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
288
559
|
res.end(JSON.stringify({
|
|
289
|
-
|
|
560
|
+
renderType: config_.renderType
|
|
290
561
|
}));
|
|
291
562
|
}
|
|
292
563
|
});
|
|
@@ -388,15 +659,15 @@ export default class App {
|
|
|
388
659
|
this.server = await start();
|
|
389
660
|
}
|
|
390
661
|
catch (error) {
|
|
391
|
-
console.error("Server failed to start:", error);
|
|
662
|
+
console.error("[HostJS] Server failed to start: ", error);
|
|
392
663
|
}
|
|
393
664
|
}
|
|
394
|
-
get(routePath, handler) {
|
|
395
|
-
routes.set(`GET:${routePath}`, handler);
|
|
396
|
-
}
|
|
397
665
|
registerApp(route) {
|
|
398
666
|
routes.set(`APP_TSX`, route);
|
|
399
667
|
}
|
|
668
|
+
get(routePath, handler) {
|
|
669
|
+
routes.set(`GET:${routePath}`, handler);
|
|
670
|
+
}
|
|
400
671
|
post(routePath, handler) {
|
|
401
672
|
routes.set(`POST:${routePath}`, handler);
|
|
402
673
|
}
|
|
@@ -406,6 +677,15 @@ export default class App {
|
|
|
406
677
|
delete(routePath, handler) {
|
|
407
678
|
routes.set(`DELETE:${routePath}`, handler);
|
|
408
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
|
+
}
|
|
409
689
|
remove(method, routePath) {
|
|
410
690
|
if (routes.has(`${method}:${routePath}`))
|
|
411
691
|
routes.delete(`${method}:${routePath}`);
|
|
@@ -420,7 +700,14 @@ async function start() {
|
|
|
420
700
|
}
|
|
421
701
|
catch {
|
|
422
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());
|
|
423
709
|
const PORT = config.port ?? 3000;
|
|
710
|
+
const renderType = config.renderType ?? 'ssr';
|
|
424
711
|
const showPanel = config.devMode !== false;
|
|
425
712
|
const autoPaging = config.autoPage !== false;
|
|
426
713
|
const compiler = 'esbuild';
|
|
@@ -428,6 +715,9 @@ async function start() {
|
|
|
428
715
|
const pagesDir = config.autoPage === true ? path.join(process.cwd(), config.autoPageDir || "") : undefined;
|
|
429
716
|
const publicDir = config.autoFile === true ? path.join(process.cwd(), config.autoFileDir || "") : undefined;
|
|
430
717
|
const apiDir = pagesDir ? path.join(pagesDir, "api") : undefined;
|
|
718
|
+
if (buildStyle === "super") {
|
|
719
|
+
initializeSuperBuildOutput(process.cwd(), config, pagesDir, publicDir);
|
|
720
|
+
}
|
|
431
721
|
const server = createServer(async (req, res) => {
|
|
432
722
|
const { url = "/", method } = req;
|
|
433
723
|
const actuallyShowPanel = showPanel;
|
|
@@ -539,8 +829,17 @@ async function start() {
|
|
|
539
829
|
res.writeHead(302, { Location: req.url + '/' });
|
|
540
830
|
res.end();
|
|
541
831
|
};
|
|
542
|
-
res.setCookie = function (name, value) {
|
|
543
|
-
|
|
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("; "));
|
|
544
843
|
};
|
|
545
844
|
res.rmvCookie = function (name) {
|
|
546
845
|
res.setHeader("Set-Cookie", `${name}=; Path=/; Max-Age=0`);
|
|
@@ -550,9 +849,28 @@ async function start() {
|
|
|
550
849
|
pathname = safePath;
|
|
551
850
|
if (pathname !== "/" && pathname.endsWith("/"))
|
|
552
851
|
pathname = pathname.slice(0, -1);
|
|
553
|
-
if (!pathname.includes('/__authtics') && !pathname.includes('/hmr')) {
|
|
852
|
+
if (!pathname.includes('/__authtics') && !pathname.includes('/hmr') && !pathname.includes('/srtws')) {
|
|
554
853
|
totalRequests++;
|
|
555
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
|
+
}
|
|
556
874
|
const routeKey = `${method}:${pathname}`;
|
|
557
875
|
if (routes.has(routeKey)) {
|
|
558
876
|
if (Array.isArray(config_.dat) && !config_.dat.includes(req.dat) && usersPaused && !routeKey.endsWith('.js')) {
|
|
@@ -561,17 +879,17 @@ async function start() {
|
|
|
561
879
|
if (fs.existsSync(pausedTsxFile || "")) {
|
|
562
880
|
try {
|
|
563
881
|
let html = await renderPage(pausedTsxFile || "", url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
564
|
-
routes.set(`GET:${p}`, (
|
|
565
|
-
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");
|
|
566
884
|
res.end(f);
|
|
567
885
|
});
|
|
568
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.dat, config_.dat);
|
|
886
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
569
887
|
html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
|
|
570
888
|
res.setHeader("Content-Type", "text/html");
|
|
571
889
|
return res.end(html);
|
|
572
890
|
}
|
|
573
891
|
catch (e) {
|
|
574
|
-
console.error("[
|
|
892
|
+
console.error("[HostJS] renderPage error:", e.message);
|
|
575
893
|
res.writeHead(500);
|
|
576
894
|
return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
|
|
577
895
|
}
|
|
@@ -584,17 +902,17 @@ async function start() {
|
|
|
584
902
|
if (fs.existsSync(appPath)) {
|
|
585
903
|
try {
|
|
586
904
|
let html = await renderPage(appPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
587
|
-
routes.set(`GET:${p}`, (
|
|
588
|
-
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");
|
|
589
907
|
res.end(f);
|
|
590
908
|
});
|
|
591
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
909
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType);
|
|
592
910
|
html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
|
|
593
911
|
res.setHeader("Content-Type", "text/html");
|
|
594
912
|
return res.end(html);
|
|
595
913
|
}
|
|
596
914
|
catch (e) {
|
|
597
|
-
console.error("[
|
|
915
|
+
console.error("[HostJS] renderPage error:", e.message);
|
|
598
916
|
res.writeHead(500);
|
|
599
917
|
return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
|
|
600
918
|
}
|
|
@@ -614,17 +932,17 @@ async function start() {
|
|
|
614
932
|
if (fs.existsSync(pausedTsxFile || "")) {
|
|
615
933
|
try {
|
|
616
934
|
let html = await renderPage(pausedTsxFile || "", url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
617
|
-
routes.set(`GET:${p}`, (
|
|
618
|
-
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");
|
|
619
937
|
res.end(f);
|
|
620
938
|
});
|
|
621
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
939
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
622
940
|
html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
|
|
623
941
|
res.setHeader("Content-Type", "text/html");
|
|
624
942
|
return res.end(html);
|
|
625
943
|
}
|
|
626
944
|
catch (e) {
|
|
627
|
-
console.error("[
|
|
945
|
+
console.error("[HostJS] renderPage error:", e.message);
|
|
628
946
|
res.writeHead(500);
|
|
629
947
|
return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
|
|
630
948
|
}
|
|
@@ -637,17 +955,17 @@ async function start() {
|
|
|
637
955
|
if (fs.existsSync(appPath)) {
|
|
638
956
|
try {
|
|
639
957
|
let html = await renderPage(appPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
640
|
-
routes.set(`GET:${p}`, (
|
|
641
|
-
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");
|
|
642
960
|
res.end(f);
|
|
643
961
|
});
|
|
644
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
962
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType);
|
|
645
963
|
html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
|
|
646
964
|
res.setHeader("Content-Type", "text/html");
|
|
647
965
|
return res.end(html);
|
|
648
966
|
}
|
|
649
967
|
catch (e) {
|
|
650
|
-
console.error("[
|
|
968
|
+
console.error("[HostJS] renderPage error:", e.message);
|
|
651
969
|
res.writeHead(500);
|
|
652
970
|
return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
|
|
653
971
|
}
|
|
@@ -662,6 +980,17 @@ async function start() {
|
|
|
662
980
|
}));
|
|
663
981
|
}
|
|
664
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
|
+
}
|
|
665
994
|
if (typeof handler === 'string') {
|
|
666
995
|
return;
|
|
667
996
|
}
|
|
@@ -672,14 +1001,24 @@ async function start() {
|
|
|
672
1001
|
if (pathname === '/hmr') {
|
|
673
1002
|
res.writeHead(200, {
|
|
674
1003
|
'Content-Type': 'text/event-stream',
|
|
675
|
-
'Cache-Control': 'no-cache',
|
|
1004
|
+
'Cache-Control': 'no-cache, no-transform',
|
|
676
1005
|
'Connection': 'keep-alive',
|
|
677
1006
|
'Access-Control-Allow-Origin': '*',
|
|
678
1007
|
});
|
|
679
1008
|
res.write('data: connected\n\n');
|
|
680
1009
|
hmrClients.add(res);
|
|
681
1010
|
users++;
|
|
682
|
-
|
|
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);
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
if (pathname === '/srtws') {
|
|
683
1022
|
return;
|
|
684
1023
|
}
|
|
685
1024
|
if (Array.isArray(config_.dat) && !config_.dat.includes(req.dat) && usersPaused) {
|
|
@@ -688,17 +1027,17 @@ async function start() {
|
|
|
688
1027
|
if (fs.existsSync(pausedTsxFile || "")) {
|
|
689
1028
|
try {
|
|
690
1029
|
let html = await renderPage(pausedTsxFile || "", url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
691
|
-
routes.set(`GET:${p}`, (
|
|
692
|
-
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");
|
|
693
1032
|
res.end(f);
|
|
694
1033
|
});
|
|
695
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
1034
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
696
1035
|
html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
|
|
697
1036
|
res.setHeader("Content-Type", "text/html");
|
|
698
1037
|
return res.end(html);
|
|
699
1038
|
}
|
|
700
1039
|
catch (e) {
|
|
701
|
-
console.error("[
|
|
1040
|
+
console.error("[HostJS] renderPage error:", e.message);
|
|
702
1041
|
res.writeHead(500);
|
|
703
1042
|
return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
|
|
704
1043
|
}
|
|
@@ -711,17 +1050,17 @@ async function start() {
|
|
|
711
1050
|
if (fs.existsSync(appPath)) {
|
|
712
1051
|
try {
|
|
713
1052
|
let html = await renderPage(appPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
714
|
-
routes.set(`GET:${p}`, (
|
|
715
|
-
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");
|
|
716
1055
|
res.end(f);
|
|
717
1056
|
});
|
|
718
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
1057
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
719
1058
|
html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
|
|
720
1059
|
res.setHeader("Content-Type", "text/html");
|
|
721
1060
|
return res.end(html);
|
|
722
1061
|
}
|
|
723
1062
|
catch (e) {
|
|
724
|
-
console.error("[
|
|
1063
|
+
console.error("[HostJS] renderPage error:", e.message);
|
|
725
1064
|
res.writeHead(500);
|
|
726
1065
|
return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
|
|
727
1066
|
}
|
|
@@ -741,17 +1080,17 @@ async function start() {
|
|
|
741
1080
|
if (fs.existsSync(pausedTsxFile || "")) {
|
|
742
1081
|
try {
|
|
743
1082
|
let html = await renderPage(pausedTsxFile || "", url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
744
|
-
routes.set(`GET:${p}`, (
|
|
745
|
-
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");
|
|
746
1085
|
res.end(f);
|
|
747
1086
|
});
|
|
748
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
1087
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
749
1088
|
html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
|
|
750
1089
|
res.setHeader("Content-Type", "text/html");
|
|
751
1090
|
return res.end(html);
|
|
752
1091
|
}
|
|
753
1092
|
catch (e) {
|
|
754
|
-
console.error("[
|
|
1093
|
+
console.error("[HostJS] renderPage error:", e.message);
|
|
755
1094
|
res.writeHead(500);
|
|
756
1095
|
return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
|
|
757
1096
|
}
|
|
@@ -764,17 +1103,17 @@ async function start() {
|
|
|
764
1103
|
if (fs.existsSync(appPath)) {
|
|
765
1104
|
try {
|
|
766
1105
|
let html = await renderPage(appPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
767
|
-
routes.set(`GET:${p}`, (
|
|
768
|
-
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");
|
|
769
1108
|
res.end(f);
|
|
770
1109
|
});
|
|
771
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
1110
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
772
1111
|
html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
|
|
773
1112
|
res.setHeader("Content-Type", "text/html");
|
|
774
1113
|
return res.end(html);
|
|
775
1114
|
}
|
|
776
1115
|
catch (e) {
|
|
777
|
-
console.error("[
|
|
1116
|
+
console.error("[HostJS] renderPage error:", e.message);
|
|
778
1117
|
res.writeHead(500);
|
|
779
1118
|
return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
|
|
780
1119
|
}
|
|
@@ -808,12 +1147,12 @@ async function start() {
|
|
|
808
1147
|
const handler = apiModule.default ?? apiModule.handler ?? null;
|
|
809
1148
|
if (typeof handler !== 'function') {
|
|
810
1149
|
res.writeHead(500);
|
|
811
|
-
return res.end(`[
|
|
1150
|
+
return res.end(`[HostJS] API route ${pathname} must export a default function`);
|
|
812
1151
|
}
|
|
813
1152
|
return handler(req, res);
|
|
814
1153
|
}
|
|
815
1154
|
catch (e) {
|
|
816
|
-
console.error(`[
|
|
1155
|
+
console.error(`[HostJS] API route error (${pathname}):`, e.message);
|
|
817
1156
|
res.writeHead(500);
|
|
818
1157
|
return res.end(JSON.stringify({ error: e.message }));
|
|
819
1158
|
}
|
|
@@ -822,11 +1161,11 @@ async function start() {
|
|
|
822
1161
|
if (fs.existsSync(notFoundPath)) {
|
|
823
1162
|
try {
|
|
824
1163
|
let html = await renderPage(notFoundPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
825
|
-
routes.set(`GET:${p}`, (
|
|
826
|
-
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");
|
|
827
1166
|
res.end(f);
|
|
828
1167
|
});
|
|
829
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
1168
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
830
1169
|
res.setHeader("Content-Type", "text/html");
|
|
831
1170
|
res.writeHead(404);
|
|
832
1171
|
html = html.replace("<script>", "<script>\nwindow.__status_code__ = 404;");
|
|
@@ -845,14 +1184,24 @@ async function start() {
|
|
|
845
1184
|
const urlPath = pathname === "/" ? "/index" : pathname;
|
|
846
1185
|
const tsxPath = pagesDir ? path.join(pagesDir, `${urlPath}.tsx`) : undefined;
|
|
847
1186
|
const htmlPath = pagesDir ? path.join(pagesDir, `${urlPath}.html`) : undefined;
|
|
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
|
+
});
|
|
1196
|
+
}
|
|
848
1197
|
if (tsxPath && fs.existsSync(tsxPath)) {
|
|
849
1198
|
try {
|
|
850
1199
|
const html = await renderPage(tsxPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
851
|
-
routes.set(`GET:${p}`, (
|
|
852
|
-
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");
|
|
853
1202
|
res.end(f);
|
|
854
1203
|
});
|
|
855
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
1204
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
856
1205
|
res.setHeader("Content-Type", "text/html");
|
|
857
1206
|
return res.end(html);
|
|
858
1207
|
}
|
|
@@ -863,8 +1212,9 @@ async function start() {
|
|
|
863
1212
|
}
|
|
864
1213
|
}
|
|
865
1214
|
if (htmlPath && fs.existsSync(htmlPath)) {
|
|
866
|
-
|
|
867
|
-
|
|
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);
|
|
868
1218
|
}
|
|
869
1219
|
if (routes.has("APP_TSX")) {
|
|
870
1220
|
const appTsxPath = routes.get("APP_TSX");
|
|
@@ -872,11 +1222,11 @@ async function start() {
|
|
|
872
1222
|
if (fs.existsSync(appPath)) {
|
|
873
1223
|
try {
|
|
874
1224
|
const html = await renderPage(appPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
875
|
-
routes.set(`GET:${p}`, (
|
|
876
|
-
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");
|
|
877
1227
|
res.end(f);
|
|
878
1228
|
});
|
|
879
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
1229
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
880
1230
|
res.setHeader("Content-Type", "text/html");
|
|
881
1231
|
return res.end(html);
|
|
882
1232
|
}
|
|
@@ -891,11 +1241,11 @@ async function start() {
|
|
|
891
1241
|
if (fs.existsSync(notFoundPath)) {
|
|
892
1242
|
try {
|
|
893
1243
|
let html = await renderPage(notFoundPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
894
|
-
routes.set(`GET:${p}`, (
|
|
895
|
-
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");
|
|
896
1246
|
res.end(f);
|
|
897
1247
|
});
|
|
898
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
1248
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
899
1249
|
res.setHeader("Content-Type", "text/html");
|
|
900
1250
|
res.writeHead(404);
|
|
901
1251
|
html = html.replace("<script>", "<script>\nwindow.__status_code__ = 404;");
|
|
@@ -934,7 +1284,17 @@ async function start() {
|
|
|
934
1284
|
if (fs.existsSync(_404Built))
|
|
935
1285
|
watchSet.add(_404Built);
|
|
936
1286
|
for (const dir of watchSet) {
|
|
937
|
-
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
|
+
});
|
|
938
1298
|
}
|
|
939
1299
|
}
|
|
940
1300
|
return server;
|