@bananacool467/authtics-host 0.2.6 → 0.2.8

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.
@@ -1,15 +1,22 @@
1
1
  import path from "node:path";
2
2
  import fs from "node:fs";
3
- import { fileURLToPath } from "node:url";
3
+ import { fileURLToPath, pathToFileURL } from "node:url";
4
4
  import * as esbuild from "esbuild";
5
5
  import React from "react";
6
6
  import { renderToString } from "react-dom/server";
7
- import { tempFileForTsx, writeTempFile, readTempFile, ensureTempDirs, } from "./rendererTempFiles.js";
7
+ import { tempFileForTsx, clientBundleForTsx, serverBundleForTsx, ssgHtmlForTsx, getBuildStyle, getBuildTrigger, writeTempFile, readTempFile, ensureTempDirs, } from "./rendererTempFiles.js";
8
8
  import { createRequire } from "node:module";
9
9
  const customRequire = createRequire(import.meta.url);
10
10
  import { WebSocketServer } from "ws";
11
- export function handleSrtwsConnection(server, PageComponent) {
11
+ let srtwsServer = null;
12
+ export function deleteSrtwsServer() {
13
+ if (srtwsServer)
14
+ srtwsServer.close();
15
+ srtwsServer = null;
16
+ }
17
+ export function handleSrtwsConnection(server, tsxPath, config, reqUrl, reqDAT, rmvFileHandler, fileHandler) {
12
18
  const wss = new WebSocketServer({ noServer: true });
19
+ srtwsServer = wss;
13
20
  server.on("upgrade", (req, socket, head) => {
14
21
  if (req.url === "/__authtics_srtws__") {
15
22
  wss.handleUpgrade(req, socket, head, (ws) => {
@@ -18,8 +25,8 @@ export function handleSrtwsConnection(server, PageComponent) {
18
25
  }
19
26
  });
20
27
  wss.on("connection", (ws) => {
21
- const renderAndSend = () => {
22
- const pageHtml = renderToString(React.createElement(PageComponent));
28
+ const renderAndSend = async () => {
29
+ const pageHtml = await renderPage(tsxPath, reqUrl, config.devMode, config.compilerType, config.hotReload, config.autoPage, config.autoPageDir, config.appDir, fileHandler, rmvFileHandler, reqDAT, config.dat, 'csr', config.patchForSsr);
23
30
  ws.send(JSON.stringify({ type: "DOM", html: pageHtml }));
24
31
  };
25
32
  ws.on("message", () => {
@@ -27,7 +34,7 @@ export function handleSrtwsConnection(server, PageComponent) {
27
34
  renderAndSend();
28
35
  }
29
36
  catch (err) {
30
- console.error("[authtics] SRTWS error:", err);
37
+ console.error("[HostJS] SRTWS error:", err);
31
38
  }
32
39
  });
33
40
  });
@@ -55,12 +62,22 @@ const SRTWS_CLIENT_SCRIPT = `<script>
55
62
  const ws = new WebSocket(protocol + window.location.host + '/__authtics_srtws__');
56
63
 
57
64
  ws.onmessage = function(event) {
58
- const msg = JSON.parse(event.data);
59
- if (msg.type === 'DOM') {
60
- const root = document.getElementById('root');
61
- if (root) root.innerHTML = msg.html;
62
- }
63
- };
65
+ const msg = JSON.parse(event.data);
66
+ if (msg.type === 'DOM') {
67
+ document.documentElement.innerHTML = msg.html;
68
+ const scripts = document.documentElement.querySelectorAll('script');
69
+
70
+ scripts.forEach(oldScript => {
71
+ const newScript = document.createElement('script');
72
+ Array.from(oldScript.attributes).forEach(attr => {
73
+ newScript.setAttribute(attr.name, attr.value);
74
+ });
75
+ newScript.textContent = oldScript.textContent;
76
+ oldScript.parentNode.replaceChild(newScript, oldScript);
77
+ });
78
+ }
79
+ };
80
+
64
81
 
65
82
  ws.onopen = function() {
66
83
  ws.send(JSON.stringify({}));
@@ -78,7 +95,6 @@ const nodeStubPlugin = {
78
95
  }));
79
96
  build.onLoad({ filter: /.*/, namespace: 'browser-node-stub' }, () => ({
80
97
  contents: `
81
- // Node.js built-in stub for browser builds
82
98
  const stub = new Proxy({}, { get: () => () => undefined });
83
99
  export default stub;
84
100
  `,
@@ -86,9 +102,11 @@ const nodeStubPlugin = {
86
102
  }));
87
103
  },
88
104
  };
89
- async function buildWithEsbuild(virtualContent, resolveDir, platform, patchForSsr = false) {
105
+ async function buildWithEsbuild(virtualContent, resolveDir, platform, patchForServer = false, options = {}) {
90
106
  const toFwdSlash = (p) => p.split(path.sep).join('/');
91
107
  const isNode = platform === 'node';
108
+ const sourceMaps = options.sourceMaps === true;
109
+ const outfile = path.resolve(options.outfile ?? path.join(resolveDir, `virtual-entry-${platform}.js`));
92
110
  const result = await esbuild.build({
93
111
  stdin: {
94
112
  contents: virtualContent,
@@ -97,14 +115,16 @@ async function buildWithEsbuild(virtualContent, resolveDir, platform, patchForSs
97
115
  },
98
116
  bundle: true,
99
117
  write: false,
118
+ outfile,
100
119
  format: isNode ? 'cjs' : 'iife',
101
120
  platform,
102
121
  jsx: 'automatic',
103
- sourcemap: false,
104
122
  target: isNode ? ['node18'] : ['es2020'],
105
- minify: true,
123
+ sourcemap: sourceMaps ? "external" : false,
124
+ sourcesContent: sourceMaps,
125
+ minify: options.minify ?? !sourceMaps,
106
126
  treeShaking: true,
107
- banner: (isNode && patchForSsr) ? {
127
+ banner: (isNode && patchForServer) ? {
108
128
  js: `if (typeof globalThis.window === "undefined") {
109
129
  const noop = () => {};
110
130
 
@@ -195,7 +215,15 @@ async function buildWithEsbuild(virtualContent, resolveDir, platform, patchForSs
195
215
  toFwdSlash(path.join(__dirname, 'rendererTempFiles.js'))
196
216
  ],
197
217
  });
198
- return result.outputFiles[0].text;
218
+ const jsFile = result.outputFiles.find((file) => path.resolve(file.path) === outfile);
219
+ const mapFile = result.outputFiles.find((file) => path.resolve(file.path) === `${outfile}.map`);
220
+ if (!jsFile) {
221
+ throw new Error(`esbuild did not produce ${outfile}`);
222
+ }
223
+ const code = mapFile && options.sourceMapUrl
224
+ ? `${jsFile.text}\n//# sourceMappingURL=${options.sourceMapUrl}\n`
225
+ : jsFile.text;
226
+ return { code, map: mapFile?.text };
199
227
  }
200
228
  export function findFileUpwards(startDir, relativePath) {
201
229
  let dir = path.resolve(startDir);
@@ -227,6 +255,73 @@ function resolveTemplate(appDir, autoPageDir, autoPaging) {
227
255
  }
228
256
  return nextToCli;
229
257
  }
258
+ let htmlPanelBundleCache;
259
+ async function getHtmlPanelClientBundle() {
260
+ if (!devPanelPath) {
261
+ throw new Error("[HostJS] DevPanel is not available.");
262
+ }
263
+ const mtimeMs = fs.statSync(devPanelPath).mtimeMs;
264
+ if (htmlPanelBundleCache?.path === devPanelPath &&
265
+ htmlPanelBundleCache.mtimeMs === mtimeMs) {
266
+ return htmlPanelBundleCache.bundle;
267
+ }
268
+ const clientEntry = `
269
+ import React from "react";
270
+ import { hydrateRoot } from "react-dom/client";
271
+ import DevPanel from ${JSON.stringify(devPanelPath)};
272
+
273
+ const panelRoot = document.getElementById("authtics-dev-panel-root");
274
+ if (panelRoot) {
275
+ hydrateRoot(panelRoot, React.createElement(DevPanel));
276
+ }
277
+ `;
278
+ const bundle = buildWithEsbuild(clientEntry, path.dirname(devPanelPath), "browser", false, { sourceMaps: false, minify: true }).then(({ code }) => code);
279
+ htmlPanelBundleCache = {
280
+ path: devPanelPath,
281
+ mtimeMs,
282
+ bundle,
283
+ };
284
+ return bundle;
285
+ }
286
+ export async function handleHTML(rawHtml, actuallyShowPanel, configDAT, userDAT, hotReload = false) {
287
+ const datMatches = configDAT === undefined ||
288
+ configDAT === userDAT ||
289
+ (Array.isArray(configDAT) && configDAT.includes(userDAT ?? ""));
290
+ const usesDevPanel = actuallyShowPanel &&
291
+ devPanelPath !== null &&
292
+ datMatches;
293
+ const configScript = `<script>window.__authtics_config__=` +
294
+ JSON.stringify({
295
+ datRestricted: configDAT !== undefined,
296
+ devMode: usesDevPanel,
297
+ }) +
298
+ `;</script>`;
299
+ let panelContainer = "";
300
+ let panelClientScript = "";
301
+ if (usesDevPanel && devPanelPath) {
302
+ const panelModule = await import(pathToFileURL(devPanelPath).href);
303
+ const Panel = (panelModule.default ?? panelModule.DevPanel);
304
+ const panelHtml = renderToString(React.createElement(Panel));
305
+ panelContainer =
306
+ `<div id="authtics-dev-panel-root">${panelHtml}</div>`;
307
+ const clientCode = await getHtmlPanelClientBundle();
308
+ const safeClientCode = clientCode.replace(/<\/script/gi, "<\\/script");
309
+ panelClientScript = `<script>${safeClientCode}</script>`;
310
+ }
311
+ const injection = [
312
+ panelContainer,
313
+ configScript,
314
+ panelClientScript,
315
+ hotReload ? HMR_SCRIPT : "",
316
+ ]
317
+ .filter(Boolean)
318
+ .join("\n");
319
+ const bodyClose = /<\/body\s*>/i;
320
+ if (bodyClose.test(rawHtml)) {
321
+ return rawHtml.replace(bodyClose, `${injection}\n$&`);
322
+ }
323
+ return `${rawHtml}\n${injection}`;
324
+ }
230
325
  function buildHtml(opts) {
231
326
  const { pageHtml, clientCode, devPanelContainer, templatePath, hotReload, fileName, datRestricted, devMode, renderMode } = opts;
232
327
  const configScript = `<script>window.__authtics_config__={datRestricted:${datRestricted ? 'true' : 'false'},devMode:${devMode !== false ? 'true' : 'false'}};</script>`;
@@ -260,11 +355,18 @@ function buildHtml(opts) {
260
355
  </body>
261
356
  </html>`;
262
357
  }
263
- function isCacheValid(tsxPath, tempPath, serverCjsPath, templatePath, devPanelPath) {
264
- if (!fs.existsSync(tempPath) || !fs.existsSync(serverCjsPath))
358
+ function isCacheValid(tsxPath, tempPath, serverCjsPath, templatePath, devPanelPath, clientBundlePath, serverBundleRequired = true, requiredArtifacts = [], checkArtifactFreshness = true) {
359
+ if (!fs.existsSync(tempPath) || (serverBundleRequired && !fs.existsSync(serverCjsPath)))
360
+ return false;
361
+ if (clientBundlePath && !fs.existsSync(clientBundlePath))
362
+ return false;
363
+ if (requiredArtifacts.some((artifactPath) => !fs.existsSync(artifactPath)))
265
364
  return false;
266
365
  try {
267
366
  const cacheMtime = fs.statSync(tempPath).mtimeMs;
367
+ if (checkArtifactFreshness &&
368
+ requiredArtifacts.some((artifactPath) => fs.statSync(artifactPath).mtimeMs < cacheMtime))
369
+ return false;
268
370
  if (fs.statSync(tsxPath).mtimeMs > cacheMtime)
269
371
  return false;
270
372
  if (templatePath && fs.existsSync(templatePath) && fs.statSync(templatePath).mtimeMs > cacheMtime)
@@ -277,51 +379,89 @@ function isCacheValid(tsxPath, tempPath, serverCjsPath, templatePath, devPanelPa
277
379
  return false;
278
380
  }
279
381
  }
280
- const HMR_SCRIPT = `<script>(function(){
281
- var reloading=false;
282
- window.addEventListener('beforeunload', function() {
283
- reloading = true;
284
- });
382
+ const HMR_SCRIPT = `<script>
383
+ (function () {
384
+ var es = null;
385
+ var disposed = false;
386
+ var reloading = false;
285
387
 
286
- var es=new EventSource('/hmr');
287
- es.onmessage=function(e){
288
- if(e.data==='reload'&&!reloading){
289
- reloading=true;
388
+ function connect() {
389
+ if (disposed || reloading || es) return;
390
+
391
+ es = new EventSource('/hmr');
392
+
393
+ es.onopen = function () { };
394
+
395
+ es.onmessage = function (event) {
396
+ if (event.data !== 'reload' || disposed || reloading) return;
397
+
398
+ reloading = true;
399
+
400
+ es.onmessage = null;
401
+ es.onerror = null;
290
402
  es.close();
403
+ es = null;
404
+
291
405
  window.location.reload();
406
+ };
407
+
408
+ es.onerror = function () {
409
+ if (disposed || reloading) return;
410
+ };
411
+ }
412
+
413
+ function dispose() {
414
+ if (disposed) return;
415
+
416
+ disposed = true;
417
+
418
+ if (es) {
419
+ es.onmessage = null;
420
+ es.onerror = null;
421
+ es.close();
422
+ es = null;
292
423
  }
293
- };
294
- es.onerror=function(){
295
- if(!reloading){
296
- setTimeout(function(){
297
- if(!reloading){
298
- reloading=true;
299
- window.location.reload();
300
- }
301
- },1500);
302
- }
303
- };
304
- })();</script>`;
305
- export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = 'esbuild', hotReload = false, autoPaging = true, autoPageDir, appDir, fileHandler, rmvFileHandler, reqDAT, configDAT, renderMode = 'ssr', patchForSsr = false) {
424
+ }
425
+ window.addEventListener('pagehide', dispose, { once: true });
426
+ window.addEventListener('unload', dispose, { once: true });
427
+
428
+ connect();
429
+ })();
430
+ </script>`;
431
+ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = 'esbuild', hotReload = false, autoPaging = true, autoPageDir, appDir, fileHandler, rmvFileHandler, reqDAT, configDAT, renderMode = 'ssr', patchForServer = false, prebuildOnly = false) {
306
432
  ensureTempDirs();
307
433
  if (!fs.existsSync(tsxPath)) {
308
434
  throw new Error(`renderPage: source file not found: ${tsxPath}`);
309
435
  }
310
- let usesDevPanel = showPanel && devPanelPath !== null;
311
- if (usesDevPanel) {
312
- let datMatched = configDAT === undefined;
313
- if (configDAT && configDAT === reqDAT)
314
- datMatched = true;
315
- if (configDAT && Array.isArray(configDAT) && configDAT.includes(reqDAT ?? ""))
316
- datMatched = true;
317
- if (!datMatched)
318
- usesDevPanel = false;
319
- }
436
+ const datMatches = configDAT === undefined ||
437
+ configDAT === reqDAT ||
438
+ (Array.isArray(configDAT) && configDAT.includes(reqDAT ?? ""));
439
+ const usesDevPanel = showPanel &&
440
+ devPanelPath !== null &&
441
+ datMatches;
442
+ const enableDiagnostics = usesDevPanel;
320
443
  const templatePath = resolveTemplate(appDir, autoPageDir, autoPaging);
321
- const tempPath = tempFileForTsx(tsxPath);
322
- const serverCjsPath = tempPath.replace(/\.json$/, ".server.cjs");
323
- const ssgHtmlPath = tempPath.replace(/\.json$/, ".ssg.html");
324
- if (renderMode === 'ssg' && isCacheValid(tsxPath, ssgHtmlPath, serverCjsPath, templatePath, devPanelPath)) {
444
+ const cacheVariant = [
445
+ usesDevPanel ? "panel" : "normal",
446
+ enableDiagnostics ? "mapped" : "fast",
447
+ compiler,
448
+ ].join("-");
449
+ const tempPath = tempFileForTsx(tsxPath, cacheVariant);
450
+ const separateClientBundle = getBuildStyle() === "super";
451
+ const clientBundlePath = clientBundleForTsx(tsxPath, cacheVariant);
452
+ const serverCjsPath = serverBundleForTsx(tsxPath, cacheVariant, tempPath);
453
+ const ssgHtmlPath = ssgHtmlForTsx(tsxPath, cacheVariant, tempPath);
454
+ const requiredClientBundlePath = separateClientBundle ? clientBundlePath : undefined;
455
+ const serverCodeDisabled = renderMode === 'csr' || renderMode === 'srtws';
456
+ const clientJsUrl = `/${path.basename(tempPath).replace(".json", ".js").replace(".tsx_", ".").toLowerCase()}`;
457
+ const clientMapUrl = `${clientJsUrl}.map`;
458
+ const clientMapPath = `${clientBundlePath}.map`;
459
+ const serverMapPath = `${serverCjsPath}.map`;
460
+ const requiredMapArtifacts = enableDiagnostics && renderMode !== "srtws"
461
+ ? [clientMapPath, ...(!serverCodeDisabled ? [serverMapPath] : [])]
462
+ : [];
463
+ if (renderMode === 'ssg' &&
464
+ isCacheValid(tsxPath, ssgHtmlPath, serverCjsPath, templatePath, devPanelPath, requiredClientBundlePath, !serverCodeDisabled, requiredMapArtifacts, false)) {
325
465
  try {
326
466
  return fs.readFileSync(ssgHtmlPath, "utf-8");
327
467
  }
@@ -329,19 +469,38 @@ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = '
329
469
  }
330
470
  }
331
471
  let clientCode;
472
+ let clientMap;
332
473
  let serverCode;
474
+ let serverMap;
333
475
  let freshWrite = false;
334
- if (isCacheValid(tsxPath, tempPath, serverCjsPath, templatePath, devPanelPath) && autoPaging) {
476
+ if (isCacheValid(tsxPath, tempPath, serverCjsPath, templatePath, devPanelPath, requiredClientBundlePath, !serverCodeDisabled, requiredMapArtifacts) &&
477
+ (autoPaging || getBuildTrigger() === "prebuilt")) {
335
478
  try {
336
479
  const parsed = JSON.parse(readTempFile(tempPath));
337
- clientCode = parsed.clientCode;
338
- serverCode = fs.readFileSync(serverCjsPath, "utf-8");
480
+ if (separateClientBundle) {
481
+ const expectedBundleRef = path.relative(process.cwd(), clientBundlePath);
482
+ clientCode = parsed.clientBundle === expectedBundleRef
483
+ ? fs.readFileSync(clientBundlePath, "utf-8")
484
+ : undefined;
485
+ }
486
+ else {
487
+ clientCode = parsed.clientCode;
488
+ }
489
+ if (enableDiagnostics && renderMode !== "srtws") {
490
+ clientMap = fs.readFileSync(clientMapPath, "utf-8");
491
+ if (!serverCodeDisabled)
492
+ serverMap = fs.readFileSync(serverMapPath, "utf-8");
493
+ }
494
+ serverCode = serverCodeDisabled ? undefined : fs.readFileSync(serverCjsPath, "utf-8");
339
495
  }
340
496
  catch {
497
+ clientCode = undefined;
498
+ clientMap = undefined;
499
+ serverCode = undefined;
500
+ serverMap = undefined;
341
501
  }
342
502
  }
343
- const isCsr = renderMode === 'csr';
344
- if (!clientCode || (!serverCode && !isCsr)) {
503
+ if (clientCode === undefined || (clientCode === "" && renderMode !== "srtws") || (!serverCode && !serverCodeDisabled)) {
345
504
  const virtualServer = `
346
505
  ${usesDevPanel ? `import DevPanel from ${JSON.stringify(devPanelPath)};` : ""}
347
506
  import Page from ${JSON.stringify(tsxPath)};
@@ -366,26 +525,64 @@ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = '
366
525
  }
367
526
  `;
368
527
  if (compiler === 'esbuild') {
369
- if (!isCsr)
370
- serverCode = await buildWithEsbuild(virtualServer, process.cwd(), 'node', patchForSsr);
528
+ if (!serverCodeDisabled) {
529
+ const builtServer = await buildWithEsbuild(virtualServer, process.cwd(), 'node', patchForServer, {
530
+ sourceMaps: enableDiagnostics,
531
+ minify: !enableDiagnostics,
532
+ outfile: serverCjsPath,
533
+ sourceMapUrl: path.basename(serverMapPath),
534
+ });
535
+ serverCode = builtServer.code;
536
+ serverMap = builtServer.map;
537
+ }
371
538
  if (renderMode !== 'srtws') {
372
- clientCode = await buildWithEsbuild(virtualClient, process.cwd(), 'browser');
539
+ const built = await buildWithEsbuild(virtualClient, process.cwd(), 'browser', patchForServer, {
540
+ sourceMaps: enableDiagnostics,
541
+ minify: !enableDiagnostics,
542
+ outfile: clientBundlePath,
543
+ sourceMapUrl: clientMapUrl,
544
+ });
545
+ clientCode = built.code;
546
+ clientMap = built.map;
373
547
  }
374
548
  else {
375
549
  clientCode = "";
376
550
  }
377
551
  }
378
552
  if (serverCode)
379
- await fs.promises.writeFile(serverCjsPath, serverCode || "", "utf-8");
380
- writeTempFile(tempPath, JSON.stringify({ clientCode }));
553
+ await fs.promises.writeFile(serverCjsPath, serverCode, "utf-8");
554
+ if (separateClientBundle) {
555
+ await fs.promises.writeFile(clientBundlePath, clientCode ?? "", "utf-8");
556
+ writeTempFile(tempPath, JSON.stringify({
557
+ clientBundle: path.relative(process.cwd(), clientBundlePath),
558
+ }));
559
+ }
560
+ else {
561
+ writeTempFile(tempPath, JSON.stringify({ clientCode }));
562
+ }
563
+ if (serverMap)
564
+ await fs.promises.writeFile(serverMapPath, serverMap, "utf-8");
565
+ if (clientMap)
566
+ await fs.promises.writeFile(clientMapPath, clientMap, "utf-8");
567
+ if (requiredMapArtifacts.includes(clientMapPath) && !clientMap) {
568
+ throw new Error(`[HostJS] Source-map build did not produce ${clientMapPath}`);
569
+ }
570
+ if (requiredMapArtifacts.includes(serverMapPath) && !serverMap) {
571
+ throw new Error(`[HostJS] Source-map build did not produce ${serverMapPath}`);
572
+ }
381
573
  freshWrite = true;
382
574
  }
383
- fileHandler ? fileHandler(clientCode || "", `/${path.basename(tempPath).replace(".json", ".js").replace(".tsx_", ".").toLowerCase()}`) : null;
575
+ if (clientCode !== undefined)
576
+ fileHandler?.(clientCode, clientJsUrl);
577
+ if (clientMap !== undefined)
578
+ fileHandler?.(clientMap, clientMapUrl);
579
+ if (prebuildOnly && renderMode !== 'ssg')
580
+ return "";
384
581
  let pageHtml = "";
385
582
  let devPanelContainer = usesDevPanel
386
583
  ? `<div id="authtics-dev-panel-root"></div>`
387
584
  : "";
388
- if (!isCsr) {
585
+ if (!serverCodeDisabled) {
389
586
  const absoluteServerPath = path.resolve(serverCjsPath);
390
587
  delete customRequire.cache[absoluteServerPath];
391
588
  const serverModule = customRequire(absoluteServerPath);
@@ -1,5 +1,14 @@
1
+ import type { BuildStyle, BuildTrigger } from "./config.js";
2
+ export declare function setBuildStyle(style: BuildStyle, projectRoot?: string): void;
3
+ export declare function getBuildStyle(): BuildStyle;
4
+ export declare function setBuildTrigger(trigger: BuildTrigger): void;
5
+ export declare function getBuildTrigger(): BuildTrigger;
6
+ export declare function getBuildOutputRoot(): string;
1
7
  export declare function ensureTempDirs(): void;
2
- export declare function tempFileForTsx(tsxPath: string): string;
8
+ export declare function tempFileForTsx(tsxPath: string, variant?: string): string;
9
+ export declare function clientBundleForTsx(tsxPath: string, variant?: string): string;
10
+ export declare function serverBundleForTsx(tsxPath: string, variant?: string, tempPath?: string): string;
11
+ export declare function ssgHtmlForTsx(tsxPath: string, variant?: string, tempPath?: string): string;
3
12
  export declare function tempFileForDev(): string;
4
13
  export declare function isTempUpToDate(tsxPath: string, tempPath: string): boolean;
5
14
  export declare function writeTempFile(tempPath: string, content: string): void;
@@ -1,25 +1,100 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- const AUTHTICS_DIR = path.join(process.cwd(), ".authtics_host");
4
- const DEV_CACHE_DIR = path.join(AUTHTICS_DIR, "dev-cache");
5
- const CACHE_DIR = path.join(AUTHTICS_DIR, "cache");
3
+ let activeBuildStyle = "default";
4
+ let activeBuildTrigger = "default";
5
+ let activeProjectRoot = process.cwd();
6
+ function hostjsDir() {
7
+ return path.join(activeProjectRoot, ".hostjs");
8
+ }
9
+ function legacyDir() {
10
+ return path.join(activeProjectRoot, ".authtics_host");
11
+ }
12
+ function artifactName(tsxPath, variant = "normal") {
13
+ const base = path.basename(tsxPath);
14
+ const hash = simpleHash(`${tsxPath}:${variant}`);
15
+ return `${base}_${hash}`;
16
+ }
17
+ export function setBuildStyle(style, projectRoot = process.cwd()) {
18
+ if (style !== "default" && style !== "super") {
19
+ throw new Error('[HostJS] Invalid buildStyle. Expected "default" or "super".');
20
+ }
21
+ activeBuildStyle = style;
22
+ activeProjectRoot = path.resolve(projectRoot);
23
+ ensureTempDirs();
24
+ }
25
+ export function getBuildStyle() {
26
+ return activeBuildStyle;
27
+ }
28
+ export function setBuildTrigger(trigger) {
29
+ if (trigger !== "default" && trigger !== "prebuilt") {
30
+ throw new Error('[HostJS] Invalid buildTrigger. Expected "default" or "prebuilt".');
31
+ }
32
+ activeBuildTrigger = trigger;
33
+ }
34
+ export function getBuildTrigger() {
35
+ return activeBuildTrigger;
36
+ }
37
+ export function getBuildOutputRoot() {
38
+ return activeBuildStyle === "super" ? hostjsDir() : legacyDir();
39
+ }
6
40
  export function ensureTempDirs() {
7
- if (!fs.existsSync(AUTHTICS_DIR))
8
- fs.mkdirSync(AUTHTICS_DIR, { recursive: true });
9
- if (!fs.existsSync(CACHE_DIR))
10
- fs.mkdirSync(CACHE_DIR, { recursive: true });
11
- if (!fs.existsSync(DEV_CACHE_DIR))
12
- fs.mkdirSync(DEV_CACHE_DIR, { recursive: true });
13
- }
14
- export function tempFileForTsx(tsxPath) {
41
+ if (activeBuildStyle === "default") {
42
+ fs.mkdirSync(legacyDir(), { recursive: true });
43
+ fs.mkdirSync(path.join(legacyDir(), "cache"), { recursive: true });
44
+ fs.mkdirSync(path.join(legacyDir(), "dev-cache"), { recursive: true });
45
+ return;
46
+ }
47
+ const dirs = [
48
+ path.join(hostjsDir(), "cache", "bundler", "client-production"),
49
+ path.join(hostjsDir(), "cache", "bundler", "server-production"),
50
+ path.join(hostjsDir(), "cache", "bundler", "edge-server-production"),
51
+ path.join(hostjsDir(), "diagnostics"),
52
+ path.join(hostjsDir(), "server", "app"),
53
+ path.join(hostjsDir(), "server", "pages"),
54
+ path.join(hostjsDir(), "server", "chunks"),
55
+ path.join(hostjsDir(), "server", "route-cache"),
56
+ path.join(hostjsDir(), "types", "app"),
57
+ path.join(hostjsDir(), "dev"),
58
+ path.join(hostjsDir(), "trace"),
59
+ path.join(hostjsDir(), "trace-build"),
60
+ ];
61
+ for (const dir of dirs)
62
+ fs.mkdirSync(dir, { recursive: true });
63
+ }
64
+ export function tempFileForTsx(tsxPath, variant = "normal") {
15
65
  ensureTempDirs();
16
- const base = path.basename(tsxPath);
17
- const hash = simpleHash(tsxPath);
18
- return path.join(CACHE_DIR, `${base}_${hash}.json`);
66
+ const name = artifactName(tsxPath, variant);
67
+ const dir = activeBuildStyle === "super"
68
+ ? path.join(hostjsDir(), "server", "route-cache")
69
+ : path.join(legacyDir(), "cache");
70
+ return path.join(dir, `${name}.json`);
71
+ }
72
+ export function clientBundleForTsx(tsxPath, variant = "normal") {
73
+ ensureTempDirs();
74
+ if (activeBuildStyle === "default") {
75
+ return tempFileForTsx(tsxPath, variant).replace(/\.json$/, ".client.js");
76
+ }
77
+ return path.join(hostjsDir(), "cache", "bundler", "client-production", `${artifactName(tsxPath, variant)}.js`);
78
+ }
79
+ export function serverBundleForTsx(tsxPath, variant = "normal", tempPath) {
80
+ ensureTempDirs();
81
+ if (activeBuildStyle === "default") {
82
+ return (tempPath ?? tempFileForTsx(tsxPath, variant)).replace(/\.json$/, ".server.cjs");
83
+ }
84
+ return path.join(hostjsDir(), "cache", "bundler", "server-production", `${artifactName(tsxPath, variant)}.server.cjs`);
85
+ }
86
+ export function ssgHtmlForTsx(tsxPath, variant = "normal", tempPath) {
87
+ ensureTempDirs();
88
+ if (activeBuildStyle === "default") {
89
+ return (tempPath ?? tempFileForTsx(tsxPath, variant)).replace(/\.json$/, ".ssg.html");
90
+ }
91
+ return path.join(hostjsDir(), "server", "route-cache", `${artifactName(tsxPath, variant)}.ssg.html`);
19
92
  }
20
93
  export function tempFileForDev() {
21
94
  ensureTempDirs();
22
- return path.join(DEV_CACHE_DIR, "page.cache.json");
95
+ return activeBuildStyle === "super"
96
+ ? path.join(hostjsDir(), "dev", "page.cache.json")
97
+ : path.join(legacyDir(), "dev-cache", "page.cache.json");
23
98
  }
24
99
  export function isTempUpToDate(tsxPath, tempPath) {
25
100
  try {
@@ -32,14 +107,23 @@ export function isTempUpToDate(tsxPath, tempPath) {
32
107
  }
33
108
  }
34
109
  export function writeTempFile(tempPath, content) {
110
+ fs.mkdirSync(path.dirname(tempPath), { recursive: true });
35
111
  fs.writeFileSync(tempPath, content, "utf-8");
36
112
  }
37
113
  export function readTempFile(tempPath) {
38
114
  return fs.readFileSync(tempPath, "utf-8");
39
115
  }
40
116
  export function clearCache() {
41
- if (fs.existsSync(CACHE_DIR)) {
42
- fs.rmSync(CACHE_DIR, { recursive: true, force: true });
117
+ if (activeBuildStyle === "default") {
118
+ fs.rmSync(path.join(legacyDir(), "cache"), { recursive: true, force: true });
119
+ }
120
+ else {
121
+ for (const dir of [
122
+ path.join(hostjsDir(), "cache", "bundler"),
123
+ path.join(hostjsDir(), "server", "route-cache"),
124
+ ]) {
125
+ fs.rmSync(dir, { recursive: true, force: true });
126
+ }
43
127
  }
44
128
  ensureTempDirs();
45
129
  }
@@ -0,0 +1,9 @@
1
+ import type { AuthticsConfig } from "./config.js";
2
+ export type HostJSRouteKind = "page" | "api" | "static";
3
+ export interface HostJSRouteRecord {
4
+ pathname: string;
5
+ kind: HostJSRouteKind;
6
+ source: string;
7
+ }
8
+ export declare function refreshSuperRouteManifest(projectRoot: string, config: AuthticsConfig, pagesDir?: string, publicDir?: string): void;
9
+ export declare function initializeSuperBuildOutput(projectRoot: string, config: AuthticsConfig, pagesDir?: string, publicDir?: string): void;